add pkgs from core
This commit is contained in:
parent
7eb3687e0e
commit
a3346ad246
86
admission/admission.go
Normal file
86
admission/admission.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package admission
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
admission_pkg "github.com/go-gost/core/admission"
|
||||||
|
"github.com/go-gost/core/logger"
|
||||||
|
"github.com/go-gost/x/internal/util/matcher"
|
||||||
|
)
|
||||||
|
|
||||||
|
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_pkg.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_pkg.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 {
|
||||||
|
p.options.logger.Debugf("admission: %v is denied", addr)
|
||||||
|
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
|
||||||
|
if !b {
|
||||||
|
p.options.logger.Debugf("admission: %v is denied", addr)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
@ -4,9 +4,9 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
"github.com/go-gost/x/config/parsing"
|
"github.com/go-gost/x/config/parsing"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
// swagger:parameters createAdmissionRequest
|
// swagger:parameters createAdmissionRequest
|
||||||
|
@ -4,9 +4,9 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
"github.com/go-gost/x/config/parsing"
|
"github.com/go-gost/x/config/parsing"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
// swagger:parameters createAutherRequest
|
// swagger:parameters createAutherRequest
|
||||||
|
@ -4,9 +4,9 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
"github.com/go-gost/x/config/parsing"
|
"github.com/go-gost/x/config/parsing"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
// swagger:parameters createBypassRequest
|
// swagger:parameters createBypassRequest
|
||||||
|
@ -4,9 +4,9 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
"github.com/go-gost/x/config/parsing"
|
"github.com/go-gost/x/config/parsing"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
// swagger:parameters createChainRequest
|
// swagger:parameters createChainRequest
|
||||||
|
@ -4,9 +4,9 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
"github.com/go-gost/x/config/parsing"
|
"github.com/go-gost/x/config/parsing"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
// swagger:parameters createHostsRequest
|
// swagger:parameters createHostsRequest
|
||||||
|
@ -4,9 +4,9 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
"github.com/go-gost/x/config/parsing"
|
"github.com/go-gost/x/config/parsing"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
// swagger:parameters createResolverRequest
|
// swagger:parameters createResolverRequest
|
||||||
|
@ -4,9 +4,9 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
"github.com/go-gost/x/config/parsing"
|
"github.com/go-gost/x/config/parsing"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
// swagger:parameters createServiceRequest
|
// swagger:parameters createServiceRequest
|
||||||
|
27
auth/auth.go
Normal file
27
auth/auth.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/go-gost/core/auth"
|
||||||
|
)
|
||||||
|
|
||||||
|
// authenticator is an Authenticator that authenticates client by key-value pairs.
|
||||||
|
type authenticator struct {
|
||||||
|
kvs map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAuthenticator creates an Authenticator that authenticates client by pre-defined user mapping.
|
||||||
|
func NewAuthenticator(kvs map[string]string) auth.Authenticator {
|
||||||
|
return &authenticator{
|
||||||
|
kvs: kvs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authenticate checks the validity of the provided user-password pair.
|
||||||
|
func (au *authenticator) Authenticate(user, password string) bool {
|
||||||
|
if au == nil || len(au.kvs) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
v, ok := au.kvs[user]
|
||||||
|
return ok && (v == "" || password == v)
|
||||||
|
}
|
85
bypass/bypass.go
Normal file
85
bypass/bypass.go
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
package bypass
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
bypass_pkg "github.com/go-gost/core/bypass"
|
||||||
|
"github.com/go-gost/core/logger"
|
||||||
|
"github.com/go-gost/x/internal/util/matcher"
|
||||||
|
)
|
||||||
|
|
||||||
|
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 bypass struct {
|
||||||
|
matchers []matcher.Matcher
|
||||||
|
reversed bool
|
||||||
|
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.Matcher, opts ...Option) bypass_pkg.Bypass {
|
||||||
|
options := options{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&options)
|
||||||
|
}
|
||||||
|
return &bypass{
|
||||||
|
matchers: matchers,
|
||||||
|
reversed: reversed,
|
||||||
|
options: options,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 ...Option) bypass_pkg.Bypass {
|
||||||
|
var matchers []matcher.Matcher
|
||||||
|
for _, pattern := range patterns {
|
||||||
|
if m := matcher.NewMatcher(pattern); m != nil {
|
||||||
|
matchers = append(matchers, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NewBypass(reversed, matchers, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bp *bypass) Contains(addr string) bool {
|
||||||
|
if addr == "" || bp == nil || len(bp.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 bp.matchers {
|
||||||
|
if matcher == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if matcher.Match(addr) {
|
||||||
|
matched = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b := !bp.reversed && matched ||
|
||||||
|
bp.reversed && !matched
|
||||||
|
if b {
|
||||||
|
bp.options.logger.Debugf("bypass: %s", addr)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
@ -2,13 +2,13 @@ package parsing
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/go-gost/core/chain"
|
"github.com/go-gost/core/chain"
|
||||||
tls_util "github.com/go-gost/core/common/util/tls"
|
|
||||||
"github.com/go-gost/core/connector"
|
"github.com/go-gost/core/connector"
|
||||||
"github.com/go-gost/core/dialer"
|
"github.com/go-gost/core/dialer"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
"github.com/go-gost/core/metadata"
|
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
|
tls_util "github.com/go-gost/x/internal/util/tls"
|
||||||
|
"github.com/go-gost/x/metadata"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseChain(cfg *config.ChainConfig) (chain.Chainer, error) {
|
func ParseChain(cfg *config.ChainConfig) (chain.Chainer, error) {
|
||||||
@ -58,7 +58,7 @@ func ParseChain(cfg *config.ChainConfig) (chain.Chainer, error) {
|
|||||||
if v.Connector.Metadata == nil {
|
if v.Connector.Metadata == nil {
|
||||||
v.Connector.Metadata = make(map[string]any)
|
v.Connector.Metadata = make(map[string]any)
|
||||||
}
|
}
|
||||||
if err := cr.Init(metadata.MapMetadata(v.Connector.Metadata)); err != nil {
|
if err := cr.Init(metadata.NewMetadata(v.Connector.Metadata)); err != nil {
|
||||||
connectorLogger.Error("init: ", err)
|
connectorLogger.Error("init: ", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -88,7 +88,7 @@ func ParseChain(cfg *config.ChainConfig) (chain.Chainer, error) {
|
|||||||
if v.Dialer.Metadata == nil {
|
if v.Dialer.Metadata == nil {
|
||||||
v.Dialer.Metadata = make(map[string]any)
|
v.Dialer.Metadata = make(map[string]any)
|
||||||
}
|
}
|
||||||
if err := d.Init(metadata.MapMetadata(v.Dialer.Metadata)); err != nil {
|
if err := d.Init(metadata.NewMetadata(v.Dialer.Metadata)); err != nil {
|
||||||
dialerLogger.Error("init: ", err)
|
dialerLogger.Error("init: ", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,16 @@ import (
|
|||||||
"github.com/go-gost/core/auth"
|
"github.com/go-gost/core/auth"
|
||||||
"github.com/go-gost/core/bypass"
|
"github.com/go-gost/core/bypass"
|
||||||
"github.com/go-gost/core/chain"
|
"github.com/go-gost/core/chain"
|
||||||
hostspkg "github.com/go-gost/core/hosts"
|
"github.com/go-gost/core/hosts"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
"github.com/go-gost/core/resolver"
|
"github.com/go-gost/core/resolver"
|
||||||
resolver_impl "github.com/go-gost/core/resolver/impl"
|
admission_impl "github.com/go-gost/x/admission"
|
||||||
|
auth_impl "github.com/go-gost/x/auth"
|
||||||
|
bypass_impl "github.com/go-gost/x/bypass"
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
|
hosts_impl "github.com/go-gost/x/hosts"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
|
resolver_impl "github.com/go-gost/x/resolver"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseAuther(cfg *config.AutherConfig) auth.Authenticator {
|
func ParseAuther(cfg *config.AutherConfig) auth.Authenticator {
|
||||||
@ -33,14 +37,14 @@ func ParseAuther(cfg *config.AutherConfig) auth.Authenticator {
|
|||||||
if len(m) == 0 {
|
if len(m) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return auth.NewAuthenticator(m)
|
return auth_impl.NewAuthenticator(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseAutherFromAuth(au *config.AuthConfig) auth.Authenticator {
|
func ParseAutherFromAuth(au *config.AuthConfig) auth.Authenticator {
|
||||||
if au == nil || au.Username == "" {
|
if au == nil || au.Username == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return auth.NewAuthenticator(map[string]string{
|
return auth_impl.NewAuthenticator(map[string]string{
|
||||||
au.Username: au.Password,
|
au.Username: au.Password,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -84,10 +88,10 @@ func ParseAdmission(cfg *config.AdmissionConfig) admission.Admission {
|
|||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return admission.NewAdmissionPatterns(
|
return admission_impl.NewAdmissionPatterns(
|
||||||
cfg.Reverse,
|
cfg.Reverse,
|
||||||
cfg.Matchers,
|
cfg.Matchers,
|
||||||
admission.LoggerOption(logger.Default().WithFields(map[string]any{
|
admission_impl.LoggerOption(logger.Default().WithFields(map[string]any{
|
||||||
"kind": "admission",
|
"kind": "admission",
|
||||||
"admission": cfg.Name,
|
"admission": cfg.Name,
|
||||||
})),
|
})),
|
||||||
@ -98,10 +102,10 @@ func ParseBypass(cfg *config.BypassConfig) bypass.Bypass {
|
|||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return bypass.NewBypassPatterns(
|
return bypass_impl.NewBypassPatterns(
|
||||||
cfg.Reverse,
|
cfg.Reverse,
|
||||||
cfg.Matchers,
|
cfg.Matchers,
|
||||||
bypass.LoggerOption(logger.Default().WithFields(map[string]any{
|
bypass_impl.LoggerOption(logger.Default().WithFields(map[string]any{
|
||||||
"kind": "bypass",
|
"kind": "bypass",
|
||||||
"bypass": cfg.Name,
|
"bypass": cfg.Name,
|
||||||
})),
|
})),
|
||||||
@ -136,11 +140,11 @@ func ParseResolver(cfg *config.ResolverConfig) (resolver.Resolver, error) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseHosts(cfg *config.HostsConfig) hostspkg.HostMapper {
|
func ParseHosts(cfg *config.HostsConfig) hosts.HostMapper {
|
||||||
if cfg == nil || len(cfg.Mappings) == 0 {
|
if cfg == nil || len(cfg.Mappings) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
hosts := hostspkg.NewHosts()
|
hosts := hosts_impl.NewHosts()
|
||||||
hosts.Logger = logger.Default().WithFields(map[string]any{
|
hosts.Logger = logger.Default().WithFields(map[string]any{
|
||||||
"kind": "hosts",
|
"kind": "hosts",
|
||||||
"hosts": cfg.Name,
|
"hosts": cfg.Name,
|
||||||
|
@ -4,14 +4,14 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-gost/core/chain"
|
"github.com/go-gost/core/chain"
|
||||||
tls_util "github.com/go-gost/core/common/util/tls"
|
|
||||||
"github.com/go-gost/core/handler"
|
"github.com/go-gost/core/handler"
|
||||||
"github.com/go-gost/core/listener"
|
"github.com/go-gost/core/listener"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
"github.com/go-gost/core/metadata"
|
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
"github.com/go-gost/core/service"
|
"github.com/go-gost/core/service"
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
|
tls_util "github.com/go-gost/x/internal/util/tls"
|
||||||
|
"github.com/go-gost/x/metadata"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
||||||
@ -46,6 +46,9 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
|||||||
listenerLogger.Error(err)
|
listenerLogger.Error(err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if tlsConfig == nil {
|
||||||
|
tlsConfig = defaultTLSConfig.Clone()
|
||||||
|
}
|
||||||
|
|
||||||
auther := ParseAutherFromAuth(cfg.Listener.Auth)
|
auther := ParseAutherFromAuth(cfg.Listener.Auth)
|
||||||
if cfg.Listener.Auther != "" {
|
if cfg.Listener.Auther != "" {
|
||||||
@ -66,7 +69,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
|||||||
if cfg.Listener.Metadata == nil {
|
if cfg.Listener.Metadata == nil {
|
||||||
cfg.Listener.Metadata = make(map[string]any)
|
cfg.Listener.Metadata = make(map[string]any)
|
||||||
}
|
}
|
||||||
if err := ln.Init(metadata.MapMetadata(cfg.Listener.Metadata)); err != nil {
|
if err := ln.Init(metadata.NewMetadata(cfg.Listener.Metadata)); err != nil {
|
||||||
listenerLogger.Error("init: ", err)
|
listenerLogger.Error("init: ", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -85,6 +88,9 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
|||||||
handlerLogger.Error(err)
|
handlerLogger.Error(err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if tlsConfig == nil {
|
||||||
|
tlsConfig = defaultTLSConfig.Clone()
|
||||||
|
}
|
||||||
|
|
||||||
auther = ParseAutherFromAuth(cfg.Handler.Auth)
|
auther = ParseAutherFromAuth(cfg.Handler.Auth)
|
||||||
if cfg.Handler.Auther != "" {
|
if cfg.Handler.Auther != "" {
|
||||||
@ -124,7 +130,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
|||||||
if cfg.Handler.Metadata == nil {
|
if cfg.Handler.Metadata == nil {
|
||||||
cfg.Handler.Metadata = make(map[string]any)
|
cfg.Handler.Metadata = make(map[string]any)
|
||||||
}
|
}
|
||||||
if err := h.Init(metadata.MapMetadata(cfg.Handler.Metadata)); err != nil {
|
if err := h.Init(metadata.NewMetadata(cfg.Handler.Metadata)); err != nil {
|
||||||
handlerLogger.Error("init: ", err)
|
handlerLogger.Error("init: ", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
113
config/parsing/tls.go
Normal file
113
config/parsing/tls.go
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
package parsing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/pem"
|
||||||
|
"math/big"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-gost/core/logger"
|
||||||
|
"github.com/go-gost/x/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultTLSConfig *tls.Config
|
||||||
|
)
|
||||||
|
|
||||||
|
func BuildDefaultTLSConfig(cfg *config.TLSConfig) {
|
||||||
|
log := logger.Default()
|
||||||
|
|
||||||
|
if cfg == nil {
|
||||||
|
cfg = &config.TLSConfig{
|
||||||
|
CertFile: "cert.pem",
|
||||||
|
KeyFile: "key.pem",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsConfig, err := loadConfig(cfg.CertFile, cfg.KeyFile)
|
||||||
|
if err != nil {
|
||||||
|
// generate random self-signed certificate.
|
||||||
|
cert, err := genCertificate()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
tlsConfig = &tls.Config{
|
||||||
|
Certificates: []tls.Certificate{cert},
|
||||||
|
}
|
||||||
|
log.Warn("load TLS certificate files failed, use random generated certificate")
|
||||||
|
} else {
|
||||||
|
log.Info("load TLS certificate files OK")
|
||||||
|
}
|
||||||
|
defaultTLSConfig = tlsConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadConfig(certFile, keyFile string) (*tls.Config, error) {
|
||||||
|
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &tls.Config{
|
||||||
|
Certificates: []tls.Certificate{cert},
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func genCertificate() (cert tls.Certificate, err error) {
|
||||||
|
rawCert, rawKey, err := generateKeyPair()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return tls.X509KeyPair(rawCert, rawKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateKeyPair() (rawCert, rawKey []byte, err error) {
|
||||||
|
// Create private key and self-signed certificate
|
||||||
|
// Adapted from https://golang.org/src/crypto/tls/generate_cert.go
|
||||||
|
|
||||||
|
priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
validFor := time.Hour * 24 * 365 * 10 // ten years
|
||||||
|
notBefore := time.Now()
|
||||||
|
notAfter := notBefore.Add(validFor)
|
||||||
|
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||||
|
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
template := x509.Certificate{
|
||||||
|
SerialNumber: serialNumber,
|
||||||
|
Subject: pkix.Name{
|
||||||
|
Organization: []string{"gost"},
|
||||||
|
},
|
||||||
|
NotBefore: notBefore,
|
||||||
|
NotAfter: notAfter,
|
||||||
|
|
||||||
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||||
|
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})
|
||||||
|
privBytes, err := x509.MarshalPKCS8PrivateKey(priv)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rawKey = pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privBytes})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
45
connector/forward/connector.go
Normal file
45
connector/forward/connector.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package forward
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/go-gost/core/connector"
|
||||||
|
md "github.com/go-gost/core/metadata"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.ConnectorRegistry().Register("forward", NewConnector)
|
||||||
|
}
|
||||||
|
|
||||||
|
type forwardConnector struct {
|
||||||
|
options connector.Options
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnector(opts ...connector.Option) connector.Connector {
|
||||||
|
options := connector.Options{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &forwardConnector{
|
||||||
|
options: options,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *forwardConnector) Init(md md.Metadata) (err error) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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]any{
|
||||||
|
"remote": conn.RemoteAddr().String(),
|
||||||
|
"local": conn.LocalAddr().String(),
|
||||||
|
"network": network,
|
||||||
|
"address": address,
|
||||||
|
})
|
||||||
|
log.Infof("connect %s/%s", address, network)
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
129
connector/http/connector.go
Normal file
129
connector/http/connector.go
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-gost/core/connector"
|
||||||
|
"github.com/go-gost/core/logger"
|
||||||
|
md "github.com/go-gost/core/metadata"
|
||||||
|
"github.com/go-gost/x/internal/util/socks"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.ConnectorRegistry().Register("http", NewConnector)
|
||||||
|
}
|
||||||
|
|
||||||
|
type httpConnector struct {
|
||||||
|
md metadata
|
||||||
|
options connector.Options
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnector(opts ...connector.Option) connector.Connector {
|
||||||
|
options := connector.Options{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &httpConnector{
|
||||||
|
options: options,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *httpConnector) Init(md md.Metadata) (err error) {
|
||||||
|
return c.parseMetadata(md)
|
||||||
|
}
|
||||||
|
|
||||||
|
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]any{
|
||||||
|
"local": conn.LocalAddr().String(),
|
||||||
|
"remote": conn.RemoteAddr().String(),
|
||||||
|
"network": network,
|
||||||
|
"address": address,
|
||||||
|
})
|
||||||
|
log.Infof("connect %s/%s", address, network)
|
||||||
|
|
||||||
|
req := &http.Request{
|
||||||
|
Method: http.MethodConnect,
|
||||||
|
URL: &url.URL{Host: address},
|
||||||
|
Host: address,
|
||||||
|
ProtoMajor: 1,
|
||||||
|
ProtoMinor: 1,
|
||||||
|
Header: c.md.header,
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Header == nil {
|
||||||
|
req.Header = http.Header{}
|
||||||
|
}
|
||||||
|
req.Header.Set("Proxy-Connection", "keep-alive")
|
||||||
|
|
||||||
|
if user := c.options.Auth; user != nil {
|
||||||
|
u := user.Username()
|
||||||
|
p, _ := user.Password()
|
||||||
|
req.Header.Set("Proxy-Authorization",
|
||||||
|
"Basic "+base64.StdEncoding.EncodeToString([]byte(u+":"+p)))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch network {
|
||||||
|
case "tcp", "tcp4", "tcp6":
|
||||||
|
if _, ok := conn.(net.PacketConn); ok {
|
||||||
|
err := fmt.Errorf("tcp over udp is unsupported")
|
||||||
|
log.Error(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
case "udp", "udp4", "udp6":
|
||||||
|
req.Header.Set("X-Gost-Protocol", "udp")
|
||||||
|
default:
|
||||||
|
err := fmt.Errorf("network %s is unsupported", network)
|
||||||
|
log.Error(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if log.IsLevelEnabled(logger.DebugLevel) {
|
||||||
|
dump, _ := httputil.DumpRequest(req, false)
|
||||||
|
log.Debug(string(dump))
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.md.connectTimeout > 0 {
|
||||||
|
conn.SetDeadline(time.Now().Add(c.md.connectTimeout))
|
||||||
|
defer conn.SetDeadline(time.Time{})
|
||||||
|
}
|
||||||
|
|
||||||
|
req = req.WithContext(ctx)
|
||||||
|
if err := req.Write(conn); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.ReadResponse(bufio.NewReader(conn), req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// NOTE: the server may return `Transfer-Encoding: chunked` header,
|
||||||
|
// then the Content-Length of response will be unknown (-1),
|
||||||
|
// in this case, close body will be blocked, so we leave it untouched.
|
||||||
|
// defer resp.Body.Close()
|
||||||
|
|
||||||
|
if log.IsLevelEnabled(logger.DebugLevel) {
|
||||||
|
dump, _ := httputil.DumpResponse(resp, false)
|
||||||
|
log.Debug(string(dump))
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("%s", resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
if network == "udp" {
|
||||||
|
addr, _ := net.ResolveUDPAddr(network, address)
|
||||||
|
return socks.UDPTunClientConn(conn, addr), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
33
connector/http/metadata.go
Normal file
33
connector/http/metadata.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
type metadata struct {
|
||||||
|
connectTimeout time.Duration
|
||||||
|
header http.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *httpConnector) parseMetadata(md mdata.Metadata) (err error) {
|
||||||
|
const (
|
||||||
|
connectTimeout = "timeout"
|
||||||
|
header = "header"
|
||||||
|
)
|
||||||
|
|
||||||
|
c.md.connectTimeout = mdx.GetDuration(md, connectTimeout)
|
||||||
|
|
||||||
|
if mm := mdx.GetStringMapString(md, header); len(mm) > 0 {
|
||||||
|
hd := http.Header{}
|
||||||
|
for k, v := range mm {
|
||||||
|
hd.Add(k, v)
|
||||||
|
}
|
||||||
|
c.md.header = hd
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
@ -15,7 +15,7 @@ import (
|
|||||||
"github.com/go-gost/core/connector"
|
"github.com/go-gost/core/connector"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
type metadata struct {
|
type metadata struct {
|
||||||
@ -18,8 +19,8 @@ func (c *http2Connector) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
header = "header"
|
header = "header"
|
||||||
)
|
)
|
||||||
|
|
||||||
c.md.connectTimeout = mdata.GetDuration(md, connectTimeout)
|
c.md.connectTimeout = mdx.GetDuration(md, connectTimeout)
|
||||||
if mm := mdata.GetStringMapString(md, header); len(mm) > 0 {
|
if mm := mdx.GetStringMapString(md, header); len(mm) > 0 {
|
||||||
hd := http.Header{}
|
hd := http.Header{}
|
||||||
for k, v := range mm {
|
for k, v := range mm {
|
||||||
hd.Add(k, v)
|
hd.Add(k, v)
|
||||||
|
@ -8,9 +8,9 @@ import (
|
|||||||
|
|
||||||
"github.com/go-gost/core/connector"
|
"github.com/go-gost/core/connector"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
"github.com/go-gost/relay"
|
"github.com/go-gost/relay"
|
||||||
relay_util "github.com/go-gost/x/internal/util/relay"
|
relay_util "github.com/go-gost/x/internal/util/relay"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
type metadata struct {
|
type metadata struct {
|
||||||
@ -17,8 +18,8 @@ func (c *relayConnector) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
noDelay = "nodelay"
|
noDelay = "nodelay"
|
||||||
)
|
)
|
||||||
|
|
||||||
c.md.connectTimeout = mdata.GetDuration(md, connectTimeout)
|
c.md.connectTimeout = mdx.GetDuration(md, connectTimeout)
|
||||||
c.md.noDelay = mdata.GetBool(md, noDelay)
|
c.md.noDelay = mdx.GetBool(md, noDelay)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/go-gost/core/connector"
|
"github.com/go-gost/core/connector"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
type metadata struct {
|
type metadata struct {
|
||||||
@ -17,8 +18,8 @@ func (c *sniConnector) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
connectTimeout = "timeout"
|
connectTimeout = "timeout"
|
||||||
)
|
)
|
||||||
|
|
||||||
c.md.host = mdata.GetString(md, host)
|
c.md.host = mdx.GetString(md, host)
|
||||||
c.md.connectTimeout = mdata.GetDuration(md, connectTimeout)
|
c.md.connectTimeout = mdx.GetDuration(md, connectTimeout)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
123
connector/socks/v4/connector.go
Normal file
123
connector/socks/v4/connector.go
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
package v4
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-gost/core/connector"
|
||||||
|
md "github.com/go-gost/core/metadata"
|
||||||
|
"github.com/go-gost/gosocks4"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.ConnectorRegistry().Register("socks4", NewConnector)
|
||||||
|
registry.ConnectorRegistry().Register("socks4a", NewConnector)
|
||||||
|
}
|
||||||
|
|
||||||
|
type socks4Connector struct {
|
||||||
|
md metadata
|
||||||
|
options connector.Options
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnector(opts ...connector.Option) connector.Connector {
|
||||||
|
options := connector.Options{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &socks4Connector{
|
||||||
|
options: options,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *socks4Connector) Init(md md.Metadata) (err error) {
|
||||||
|
return c.parseMetadata(md)
|
||||||
|
}
|
||||||
|
|
||||||
|
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]any{
|
||||||
|
"remote": conn.RemoteAddr().String(),
|
||||||
|
"local": conn.LocalAddr().String(),
|
||||||
|
"network": network,
|
||||||
|
"address": address,
|
||||||
|
})
|
||||||
|
log.Infof("connect %s/%s", address, network)
|
||||||
|
|
||||||
|
switch network {
|
||||||
|
case "tcp", "tcp4", "tcp6":
|
||||||
|
if _, ok := conn.(net.PacketConn); ok {
|
||||||
|
err := fmt.Errorf("tcp over udp is unsupported")
|
||||||
|
log.Error(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
err := fmt.Errorf("network %s is unsupported", network)
|
||||||
|
log.Error(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var addr *gosocks4.Addr
|
||||||
|
|
||||||
|
if c.md.disable4a {
|
||||||
|
taddr, err := net.ResolveTCPAddr("tcp4", address)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("resolve: ", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(taddr.IP) == 0 {
|
||||||
|
taddr.IP = net.IPv4zero
|
||||||
|
}
|
||||||
|
addr = &gosocks4.Addr{
|
||||||
|
Type: gosocks4.AddrIPv4,
|
||||||
|
Host: taddr.IP.String(),
|
||||||
|
Port: uint16(taddr.Port),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
host, port, err := net.SplitHostPort(address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p, _ := strconv.Atoi(port)
|
||||||
|
addr = &gosocks4.Addr{
|
||||||
|
Type: gosocks4.AddrDomain,
|
||||||
|
Host: host,
|
||||||
|
Port: uint16(p),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.md.connectTimeout > 0 {
|
||||||
|
conn.SetDeadline(time.Now().Add(c.md.connectTimeout))
|
||||||
|
defer conn.SetDeadline(time.Time{})
|
||||||
|
}
|
||||||
|
|
||||||
|
var userid []byte
|
||||||
|
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 {
|
||||||
|
log.Error(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debug(req)
|
||||||
|
|
||||||
|
reply, err := gosocks4.ReadReply(conn)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debug(reply)
|
||||||
|
|
||||||
|
if reply.Code != gosocks4.Granted {
|
||||||
|
err = errors.New("host unreachable")
|
||||||
|
log.Error(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
25
connector/socks/v4/metadata.go
Normal file
25
connector/socks/v4/metadata.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package v4
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
type metadata struct {
|
||||||
|
connectTimeout time.Duration
|
||||||
|
disable4a bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *socks4Connector) parseMetadata(md mdata.Metadata) (err error) {
|
||||||
|
const (
|
||||||
|
connectTimeout = "timeout"
|
||||||
|
disable4a = "disable4a"
|
||||||
|
)
|
||||||
|
|
||||||
|
c.md.connectTimeout = mdx.GetDuration(md, connectTimeout)
|
||||||
|
c.md.disable4a = mdx.GetBool(md, disable4a)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
133
connector/socks/v5/bind.go
Normal file
133
connector/socks/v5/bind.go
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
package v5
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/go-gost/core/common/net/udp"
|
||||||
|
"github.com/go-gost/core/connector"
|
||||||
|
"github.com/go-gost/core/logger"
|
||||||
|
"github.com/go-gost/gosocks5"
|
||||||
|
"github.com/go-gost/x/internal/util/mux"
|
||||||
|
"github.com/go-gost/x/internal/util/socks"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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]any{
|
||||||
|
"remote": conn.RemoteAddr().String(),
|
||||||
|
"local": conn.LocalAddr().String(),
|
||||||
|
"network": network,
|
||||||
|
"address": address,
|
||||||
|
})
|
||||||
|
log.Infof("bind on %s/%s", address, network)
|
||||||
|
|
||||||
|
options := connector.BindOptions{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch network {
|
||||||
|
case "tcp", "tcp4", "tcp6":
|
||||||
|
if options.Mux {
|
||||||
|
return c.muxBindTCP(ctx, conn, network, address, log)
|
||||||
|
}
|
||||||
|
return c.bindTCP(ctx, conn, network, address, log)
|
||||||
|
case "udp", "udp4", "udp6":
|
||||||
|
return c.bindUDP(ctx, conn, network, address, &options, log)
|
||||||
|
default:
|
||||||
|
err := fmt.Errorf("network %s is unsupported", network)
|
||||||
|
log.Error(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *socks5Connector) bindTCP(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) (net.Listener, error) {
|
||||||
|
laddr, err := c.bind(conn, gosocks5.CmdBind, network, address, log)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &tcpListener{
|
||||||
|
addr: laddr,
|
||||||
|
conn: conn,
|
||||||
|
logger: log,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *socks5Connector) muxBindTCP(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) (net.Listener, error) {
|
||||||
|
laddr, err := c.bind(conn, socks.CmdMuxBind, network, address, log)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
session, err := mux.ServerSession(conn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &tcpMuxListener{
|
||||||
|
addr: laddr,
|
||||||
|
session: session,
|
||||||
|
logger: log,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *socks5Connector) bindUDP(ctx context.Context, conn net.Conn, network, address string, opts *connector.BindOptions, log logger.Logger) (net.Listener, error) {
|
||||||
|
laddr, err := c.bind(conn, socks.CmdUDPTun, network, address, log)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ln := udp.NewListener(socks.UDPTunClientPacketConn(conn),
|
||||||
|
&udp.ListenConfig{
|
||||||
|
Addr: laddr,
|
||||||
|
Backlog: opts.Backlog,
|
||||||
|
ReadQueueSize: opts.UDPDataQueueSize,
|
||||||
|
ReadBufferSize: opts.UDPDataBufferSize,
|
||||||
|
TTL: opts.UDPConnTTL,
|
||||||
|
KeepAlive: true,
|
||||||
|
Logger: log,
|
||||||
|
})
|
||||||
|
|
||||||
|
return ln, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *socks5Connector) bind(conn net.Conn, cmd uint8, network, address string, log logger.Logger) (net.Addr, error) {
|
||||||
|
addr := gosocks5.Addr{}
|
||||||
|
addr.ParseFrom(address)
|
||||||
|
req := gosocks5.NewRequest(cmd, &addr)
|
||||||
|
if err := req.Write(conn); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debug(req)
|
||||||
|
|
||||||
|
// first reply, bind status
|
||||||
|
reply, err := gosocks5.ReadReply(conn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug(reply)
|
||||||
|
|
||||||
|
if reply.Rep != gosocks5.Succeeded {
|
||||||
|
return nil, fmt.Errorf("bind on %s/%s failed", address, network)
|
||||||
|
}
|
||||||
|
|
||||||
|
var baddr net.Addr
|
||||||
|
switch network {
|
||||||
|
case "tcp", "tcp4", "tcp6":
|
||||||
|
baddr, err = net.ResolveTCPAddr(network, reply.Addr.String())
|
||||||
|
case "udp", "udp4", "udp6":
|
||||||
|
baddr, err = net.ResolveUDPAddr(network, reply.Addr.String())
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("unknown network %s", network)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debugf("bind on %s/%s OK", baddr, baddr.Network())
|
||||||
|
|
||||||
|
return baddr, nil
|
||||||
|
}
|
17
connector/socks/v5/conn.go
Normal file
17
connector/socks/v5/conn.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package v5
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
type bindConn struct {
|
||||||
|
net.Conn
|
||||||
|
localAddr net.Addr
|
||||||
|
remoteAddr net.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *bindConn) LocalAddr() net.Addr {
|
||||||
|
return c.localAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *bindConn) RemoteAddr() net.Addr {
|
||||||
|
return c.remoteAddr
|
||||||
|
}
|
173
connector/socks/v5/connector.go
Normal file
173
connector/socks/v5/connector.go
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
package v5
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-gost/core/connector"
|
||||||
|
"github.com/go-gost/core/logger"
|
||||||
|
md "github.com/go-gost/core/metadata"
|
||||||
|
"github.com/go-gost/gosocks5"
|
||||||
|
"github.com/go-gost/x/internal/util/socks"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.ConnectorRegistry().Register("socks5", NewConnector)
|
||||||
|
registry.ConnectorRegistry().Register("socks", NewConnector)
|
||||||
|
}
|
||||||
|
|
||||||
|
type socks5Connector struct {
|
||||||
|
selector gosocks5.Selector
|
||||||
|
md metadata
|
||||||
|
options connector.Options
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnector(opts ...connector.Option) connector.Connector {
|
||||||
|
options := connector.Options{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &socks5Connector{
|
||||||
|
options: options,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *socks5Connector) Init(md md.Metadata) (err error) {
|
||||||
|
if err = c.parseMetadata(md); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
selector := &clientSelector{
|
||||||
|
methods: []uint8{
|
||||||
|
gosocks5.MethodNoAuth,
|
||||||
|
gosocks5.MethodUserPass,
|
||||||
|
},
|
||||||
|
User: c.options.Auth,
|
||||||
|
TLSConfig: c.options.TLSConfig,
|
||||||
|
logger: c.options.Logger,
|
||||||
|
}
|
||||||
|
if !c.md.noTLS {
|
||||||
|
selector.methods = append(selector.methods, socks.MethodTLS)
|
||||||
|
if selector.TLSConfig == nil {
|
||||||
|
selector.TLSConfig = &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.selector = selector
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handshake implements connector.Handshaker.
|
||||||
|
func (c *socks5Connector) Handshake(ctx context.Context, conn net.Conn) (net.Conn, error) {
|
||||||
|
log := c.options.Logger.WithFields(map[string]any{
|
||||||
|
"remote": conn.RemoteAddr().String(),
|
||||||
|
"local": conn.LocalAddr().String(),
|
||||||
|
})
|
||||||
|
|
||||||
|
if c.md.connectTimeout > 0 {
|
||||||
|
conn.SetDeadline(time.Now().Add(c.md.connectTimeout))
|
||||||
|
defer conn.SetDeadline(time.Time{})
|
||||||
|
}
|
||||||
|
|
||||||
|
cc := gosocks5.ClientConn(conn, c.selector)
|
||||||
|
if err := cc.Handleshake(); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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]any{
|
||||||
|
"remote": conn.RemoteAddr().String(),
|
||||||
|
"local": conn.LocalAddr().String(),
|
||||||
|
"network": network,
|
||||||
|
"address": address,
|
||||||
|
})
|
||||||
|
log.Infof("connect %s/%s", address, network)
|
||||||
|
|
||||||
|
if c.md.connectTimeout > 0 {
|
||||||
|
conn.SetDeadline(time.Now().Add(c.md.connectTimeout))
|
||||||
|
defer conn.SetDeadline(time.Time{})
|
||||||
|
}
|
||||||
|
|
||||||
|
switch network {
|
||||||
|
case "udp", "udp4", "udp6":
|
||||||
|
return c.connectUDP(ctx, conn, network, address, log)
|
||||||
|
case "tcp", "tcp4", "tcp6":
|
||||||
|
if _, ok := conn.(net.PacketConn); ok {
|
||||||
|
err := fmt.Errorf("tcp over udp is unsupported")
|
||||||
|
log.Error(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
err := fmt.Errorf("network %s is unsupported", network)
|
||||||
|
log.Error(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
addr := gosocks5.Addr{}
|
||||||
|
if err := addr.ParseFrom(address); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req := gosocks5.NewRequest(gosocks5.CmdConnect, &addr)
|
||||||
|
if err := req.Write(conn); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debug(req)
|
||||||
|
|
||||||
|
reply, err := gosocks5.ReadReply(conn)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debug(reply)
|
||||||
|
|
||||||
|
if reply.Rep != gosocks5.Succeeded {
|
||||||
|
err = errors.New("host unreachable")
|
||||||
|
log.Error(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *socks5Connector) connectUDP(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) (net.Conn, error) {
|
||||||
|
addr, err := net.ResolveUDPAddr(network, address)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req := gosocks5.NewRequest(socks.CmdUDPTun, nil)
|
||||||
|
if err := req.Write(conn); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debug(req)
|
||||||
|
|
||||||
|
reply, err := gosocks5.ReadReply(conn)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debug(reply)
|
||||||
|
|
||||||
|
if reply.Rep != gosocks5.Succeeded {
|
||||||
|
return nil, errors.New("get socks5 UDP tunnel failure")
|
||||||
|
}
|
||||||
|
|
||||||
|
return socks.UDPTunClientConn(conn, addr), nil
|
||||||
|
}
|
102
connector/socks/v5/listener.go
Normal file
102
connector/socks/v5/listener.go
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
package v5
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/go-gost/core/logger"
|
||||||
|
"github.com/go-gost/gosocks5"
|
||||||
|
"github.com/go-gost/x/internal/util/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tcpListener struct {
|
||||||
|
addr net.Addr
|
||||||
|
conn net.Conn
|
||||||
|
logger logger.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tcpListener) Accept() (net.Conn, error) {
|
||||||
|
// second reply, peer connected
|
||||||
|
rep, err := gosocks5.ReadReply(p.conn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p.logger.Debug(rep)
|
||||||
|
|
||||||
|
if rep.Rep != gosocks5.Succeeded {
|
||||||
|
return nil, fmt.Errorf("peer connect failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
raddr, err := net.ResolveTCPAddr("tcp", rep.Addr.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &bindConn{
|
||||||
|
Conn: p.conn,
|
||||||
|
localAddr: p.addr,
|
||||||
|
remoteAddr: raddr,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tcpListener) Addr() net.Addr {
|
||||||
|
return p.addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tcpListener) Close() error {
|
||||||
|
return p.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
type tcpMuxListener struct {
|
||||||
|
addr net.Addr
|
||||||
|
session *mux.Session
|
||||||
|
logger logger.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tcpMuxListener) Accept() (net.Conn, error) {
|
||||||
|
cc, err := p.session.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := p.getPeerConn(cc)
|
||||||
|
if err != nil {
|
||||||
|
cc.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tcpMuxListener) getPeerConn(conn net.Conn) (net.Conn, error) {
|
||||||
|
// second reply, peer connected
|
||||||
|
rep, err := gosocks5.ReadReply(conn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p.logger.Debug(rep)
|
||||||
|
|
||||||
|
if rep.Rep != gosocks5.Succeeded {
|
||||||
|
err = fmt.Errorf("peer connect failed")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
raddr, err := net.ResolveTCPAddr("tcp", rep.Addr.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &bindConn{
|
||||||
|
Conn: conn,
|
||||||
|
localAddr: p.addr,
|
||||||
|
remoteAddr: raddr,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tcpMuxListener) Addr() net.Addr {
|
||||||
|
return p.addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tcpMuxListener) Close() error {
|
||||||
|
return p.session.Close()
|
||||||
|
}
|
25
connector/socks/v5/metadata.go
Normal file
25
connector/socks/v5/metadata.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package v5
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
type metadata struct {
|
||||||
|
connectTimeout time.Duration
|
||||||
|
noTLS bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *socks5Connector) parseMetadata(md mdata.Metadata) (err error) {
|
||||||
|
const (
|
||||||
|
connectTimeout = "timeout"
|
||||||
|
noTLS = "notls"
|
||||||
|
)
|
||||||
|
|
||||||
|
c.md.connectTimeout = mdx.GetDuration(md, connectTimeout)
|
||||||
|
c.md.noTLS = mdx.GetBool(md, noTLS)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
73
connector/socks/v5/selector.go
Normal file
73
connector/socks/v5/selector.go
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package v5
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/go-gost/core/logger"
|
||||||
|
"github.com/go-gost/gosocks5"
|
||||||
|
"github.com/go-gost/x/internal/util/socks"
|
||||||
|
)
|
||||||
|
|
||||||
|
type clientSelector struct {
|
||||||
|
methods []uint8
|
||||||
|
User *url.Userinfo
|
||||||
|
TLSConfig *tls.Config
|
||||||
|
logger logger.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *clientSelector) Methods() []uint8 {
|
||||||
|
s.logger.Debug("methods: ", s.methods)
|
||||||
|
return s.methods
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *clientSelector) AddMethod(methods ...uint8) {
|
||||||
|
s.methods = append(s.methods, methods...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *clientSelector) Select(methods ...uint8) (method uint8) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *clientSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) {
|
||||||
|
s.logger.Debug("method selected: ", method)
|
||||||
|
|
||||||
|
switch method {
|
||||||
|
case socks.MethodTLS:
|
||||||
|
conn = tls.Client(conn, s.TLSConfig)
|
||||||
|
|
||||||
|
case gosocks5.MethodUserPass, socks.MethodTLSAuth:
|
||||||
|
if method == socks.MethodTLSAuth {
|
||||||
|
conn = tls.Client(conn, s.TLSConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
var username, password string
|
||||||
|
if s.User != nil {
|
||||||
|
username = s.User.Username()
|
||||||
|
password, _ = s.User.Password()
|
||||||
|
}
|
||||||
|
|
||||||
|
req := gosocks5.NewUserPassRequest(gosocks5.UserPassVer, username, password)
|
||||||
|
if err := req.Write(conn); err != nil {
|
||||||
|
s.logger.Error(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s.logger.Debug(req)
|
||||||
|
|
||||||
|
resp, err := gosocks5.ReadUserPassResponse(conn)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s.logger.Debug(resp)
|
||||||
|
|
||||||
|
if resp.Status != gosocks5.Succeeded {
|
||||||
|
return nil, gosocks5.ErrAuthFailure
|
||||||
|
}
|
||||||
|
case gosocks5.MethodNoAcceptable:
|
||||||
|
return nil, gosocks5.ErrBadMethod
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
@ -9,9 +9,9 @@ import (
|
|||||||
"github.com/go-gost/core/common/bufpool"
|
"github.com/go-gost/core/common/bufpool"
|
||||||
"github.com/go-gost/core/connector"
|
"github.com/go-gost/core/connector"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
"github.com/go-gost/gosocks5"
|
"github.com/go-gost/gosocks5"
|
||||||
"github.com/go-gost/x/internal/util/ss"
|
"github.com/go-gost/x/internal/util/ss"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
"github.com/shadowsocks/go-shadowsocks2/core"
|
"github.com/shadowsocks/go-shadowsocks2/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
type metadata struct {
|
type metadata struct {
|
||||||
@ -19,9 +20,9 @@ func (c *ssConnector) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
noDelay = "nodelay"
|
noDelay = "nodelay"
|
||||||
)
|
)
|
||||||
|
|
||||||
c.md.key = mdata.GetString(md, key)
|
c.md.key = mdx.GetString(md, key)
|
||||||
c.md.connectTimeout = mdata.GetDuration(md, connectTimeout)
|
c.md.connectTimeout = mdx.GetDuration(md, connectTimeout)
|
||||||
c.md.noDelay = mdata.GetBool(md, noDelay)
|
c.md.noDelay = mdx.GetBool(md, noDelay)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,9 @@ import (
|
|||||||
|
|
||||||
"github.com/go-gost/core/connector"
|
"github.com/go-gost/core/connector"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
"github.com/go-gost/x/internal/util/relay"
|
"github.com/go-gost/x/internal/util/relay"
|
||||||
"github.com/go-gost/x/internal/util/ss"
|
"github.com/go-gost/x/internal/util/ss"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
"github.com/shadowsocks/go-shadowsocks2/core"
|
"github.com/shadowsocks/go-shadowsocks2/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
type metadata struct {
|
type metadata struct {
|
||||||
@ -20,10 +21,10 @@ func (c *ssuConnector) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
bufferSize = "bufferSize" // udp buffer size
|
bufferSize = "bufferSize" // udp buffer size
|
||||||
)
|
)
|
||||||
|
|
||||||
c.md.key = mdata.GetString(md, key)
|
c.md.key = mdx.GetString(md, key)
|
||||||
c.md.connectTimeout = mdata.GetDuration(md, connectTimeout)
|
c.md.connectTimeout = mdx.GetDuration(md, connectTimeout)
|
||||||
|
|
||||||
if bs := mdata.GetInt(md, bufferSize); bs > 0 {
|
if bs := mdx.GetInt(md, bufferSize); bs > 0 {
|
||||||
c.md.bufferSize = int(math.Min(math.Max(float64(bs), 512), 64*1024))
|
c.md.bufferSize = int(math.Min(math.Max(float64(bs), 512), 64*1024))
|
||||||
} else {
|
} else {
|
||||||
c.md.bufferSize = 1500
|
c.md.bufferSize = 1500
|
||||||
|
@ -7,8 +7,8 @@ import (
|
|||||||
|
|
||||||
"github.com/go-gost/core/connector"
|
"github.com/go-gost/core/connector"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
ssh_util "github.com/go-gost/x/internal/util/ssh"
|
ssh_util "github.com/go-gost/x/internal/util/ssh"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/go-gost/core/dialer"
|
"github.com/go-gost/core/dialer"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
"github.com/go-gost/x/registry"
|
||||||
"github.com/xtaci/tcpraw"
|
"github.com/xtaci/tcpraw"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -8,8 +8,8 @@ import (
|
|||||||
|
|
||||||
"github.com/go-gost/core/dialer"
|
"github.com/go-gost/core/dialer"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
pb "github.com/go-gost/x/internal/util/grpc/proto"
|
pb "github.com/go-gost/x/internal/util/grpc/proto"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/backoff"
|
"google.golang.org/grpc/backoff"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
|
@ -2,6 +2,7 @@ package grpc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
type metadata struct {
|
type metadata struct {
|
||||||
@ -15,8 +16,8 @@ func (d *grpcDialer) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
host = "host"
|
host = "host"
|
||||||
)
|
)
|
||||||
|
|
||||||
d.md.insecure = mdata.GetBool(md, insecure)
|
d.md.insecure = mdx.GetBool(md, insecure)
|
||||||
d.md.host = mdata.GetString(md, host)
|
d.md.host = mdx.GetString(md, host)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,8 @@ import (
|
|||||||
"github.com/go-gost/core/dialer"
|
"github.com/go-gost/core/dialer"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
mdx "github.com/go-gost/x/metadata"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -98,7 +99,7 @@ func (d *http2Dialer) Dial(ctx context.Context, address string, opts ...dialer.D
|
|||||||
defer d.clientMutex.Unlock()
|
defer d.clientMutex.Unlock()
|
||||||
delete(d.clients, address)
|
delete(d.clients, address)
|
||||||
},
|
},
|
||||||
md: md.MapMetadata{"client": client},
|
md: mdx.NewMetadata(map[string]any{"client": client}),
|
||||||
}
|
}
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
|
@ -15,7 +15,7 @@ import (
|
|||||||
"github.com/go-gost/core/dialer"
|
"github.com/go-gost/core/dialer"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
"github.com/go-gost/x/registry"
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package h2
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
type metadata struct {
|
type metadata struct {
|
||||||
@ -15,8 +16,8 @@ func (d *h2Dialer) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
path = "path"
|
path = "path"
|
||||||
)
|
)
|
||||||
|
|
||||||
d.md.host = mdata.GetString(md, host)
|
d.md.host = mdx.GetString(md, host)
|
||||||
d.md.path = mdata.GetString(md, path)
|
d.md.path = mdx.GetString(md, path)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,8 @@ import (
|
|||||||
|
|
||||||
"github.com/go-gost/core/dialer"
|
"github.com/go-gost/core/dialer"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
pht_util "github.com/go-gost/x/internal/util/pht"
|
pht_util "github.com/go-gost/x/internal/util/pht"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
"github.com/lucas-clemente/quic-go"
|
"github.com/lucas-clemente/quic-go"
|
||||||
"github.com/lucas-clemente/quic-go/http3"
|
"github.com/lucas-clemente/quic-go/http3"
|
||||||
)
|
)
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -34,19 +35,19 @@ func (d *http3Dialer) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
host = "host"
|
host = "host"
|
||||||
)
|
)
|
||||||
|
|
||||||
d.md.authorizePath = mdata.GetString(md, authorizePath)
|
d.md.authorizePath = mdx.GetString(md, authorizePath)
|
||||||
if !strings.HasPrefix(d.md.authorizePath, "/") {
|
if !strings.HasPrefix(d.md.authorizePath, "/") {
|
||||||
d.md.authorizePath = defaultAuthorizePath
|
d.md.authorizePath = defaultAuthorizePath
|
||||||
}
|
}
|
||||||
d.md.pushPath = mdata.GetString(md, pushPath)
|
d.md.pushPath = mdx.GetString(md, pushPath)
|
||||||
if !strings.HasPrefix(d.md.pushPath, "/") {
|
if !strings.HasPrefix(d.md.pushPath, "/") {
|
||||||
d.md.pushPath = defaultPushPath
|
d.md.pushPath = defaultPushPath
|
||||||
}
|
}
|
||||||
d.md.pullPath = mdata.GetString(md, pullPath)
|
d.md.pullPath = mdx.GetString(md, pullPath)
|
||||||
if !strings.HasPrefix(d.md.pullPath, "/") {
|
if !strings.HasPrefix(d.md.pullPath, "/") {
|
||||||
d.md.pullPath = defaultPullPath
|
d.md.pullPath = defaultPullPath
|
||||||
}
|
}
|
||||||
|
|
||||||
d.md.host = mdata.GetString(md, host)
|
d.md.host = mdx.GetString(md, host)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,8 @@ import (
|
|||||||
"github.com/go-gost/core/dialer"
|
"github.com/go-gost/core/dialer"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
icmp_pkg "github.com/go-gost/x/internal/util/icmp"
|
icmp_pkg "github.com/go-gost/x/internal/util/icmp"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
"github.com/lucas-clemente/quic-go"
|
"github.com/lucas-clemente/quic-go"
|
||||||
"golang.org/x/net/icmp"
|
"golang.org/x/net/icmp"
|
||||||
)
|
)
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
type metadata struct {
|
type metadata struct {
|
||||||
@ -19,11 +20,11 @@ func (d *icmpDialer) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
maxIdleTimeout = "maxIdleTimeout"
|
maxIdleTimeout = "maxIdleTimeout"
|
||||||
)
|
)
|
||||||
|
|
||||||
d.md.handshakeTimeout = mdata.GetDuration(md, handshakeTimeout)
|
d.md.handshakeTimeout = mdx.GetDuration(md, handshakeTimeout)
|
||||||
|
|
||||||
d.md.keepAlive = mdata.GetBool(md, keepAlive)
|
d.md.keepAlive = mdx.GetBool(md, keepAlive)
|
||||||
d.md.handshakeTimeout = mdata.GetDuration(md, handshakeTimeout)
|
d.md.handshakeTimeout = mdx.GetDuration(md, handshakeTimeout)
|
||||||
d.md.maxIdleTimeout = mdata.GetDuration(md, maxIdleTimeout)
|
d.md.maxIdleTimeout = mdx.GetDuration(md, maxIdleTimeout)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,8 @@ import (
|
|||||||
"github.com/go-gost/core/dialer"
|
"github.com/go-gost/core/dialer"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
kcp_util "github.com/go-gost/x/internal/util/kcp"
|
kcp_util "github.com/go-gost/x/internal/util/kcp"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
"github.com/xtaci/kcp-go/v5"
|
"github.com/xtaci/kcp-go/v5"
|
||||||
"github.com/xtaci/smux"
|
"github.com/xtaci/smux"
|
||||||
"github.com/xtaci/tcpraw"
|
"github.com/xtaci/tcpraw"
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
kcp_util "github.com/go-gost/x/internal/util/kcp"
|
kcp_util "github.com/go-gost/x/internal/util/kcp"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
type metadata struct {
|
type metadata struct {
|
||||||
@ -19,7 +20,7 @@ func (d *kcpDialer) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
handshakeTimeout = "handshakeTimeout"
|
handshakeTimeout = "handshakeTimeout"
|
||||||
)
|
)
|
||||||
|
|
||||||
if m := mdata.GetStringMap(md, config); len(m) > 0 {
|
if m := mdx.GetStringMap(md, config); len(m) > 0 {
|
||||||
b, err := json.Marshal(m)
|
b, err := json.Marshal(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -34,6 +35,6 @@ func (d *kcpDialer) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
d.md.config = kcp_util.DefaultConfig
|
d.md.config = kcp_util.DefaultConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
d.md.handshakeTimeout = mdata.GetDuration(md, handshakeTimeout)
|
d.md.handshakeTimeout = mdx.GetDuration(md, handshakeTimeout)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
"github.com/go-gost/core/dialer"
|
"github.com/go-gost/core/dialer"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
"github.com/go-gost/x/registry"
|
||||||
"github.com/xtaci/smux"
|
"github.com/xtaci/smux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
type metadata struct {
|
type metadata struct {
|
||||||
@ -29,14 +30,14 @@ func (d *mtlsDialer) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
muxMaxStreamBuffer = "muxMaxStreamBuffer"
|
muxMaxStreamBuffer = "muxMaxStreamBuffer"
|
||||||
)
|
)
|
||||||
|
|
||||||
d.md.handshakeTimeout = mdata.GetDuration(md, handshakeTimeout)
|
d.md.handshakeTimeout = mdx.GetDuration(md, handshakeTimeout)
|
||||||
|
|
||||||
d.md.muxKeepAliveDisabled = mdata.GetBool(md, muxKeepAliveDisabled)
|
d.md.muxKeepAliveDisabled = mdx.GetBool(md, muxKeepAliveDisabled)
|
||||||
d.md.muxKeepAliveInterval = mdata.GetDuration(md, muxKeepAliveInterval)
|
d.md.muxKeepAliveInterval = mdx.GetDuration(md, muxKeepAliveInterval)
|
||||||
d.md.muxKeepAliveTimeout = mdata.GetDuration(md, muxKeepAliveTimeout)
|
d.md.muxKeepAliveTimeout = mdx.GetDuration(md, muxKeepAliveTimeout)
|
||||||
d.md.muxMaxFrameSize = mdata.GetInt(md, muxMaxFrameSize)
|
d.md.muxMaxFrameSize = mdx.GetInt(md, muxMaxFrameSize)
|
||||||
d.md.muxMaxReceiveBuffer = mdata.GetInt(md, muxMaxReceiveBuffer)
|
d.md.muxMaxReceiveBuffer = mdx.GetInt(md, muxMaxReceiveBuffer)
|
||||||
d.md.muxMaxStreamBuffer = mdata.GetInt(md, muxMaxStreamBuffer)
|
d.md.muxMaxStreamBuffer = mdx.GetInt(md, muxMaxStreamBuffer)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,8 @@ import (
|
|||||||
|
|
||||||
"github.com/go-gost/core/dialer"
|
"github.com/go-gost/core/dialer"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
ws_util "github.com/go-gost/x/internal/util/ws"
|
ws_util "github.com/go-gost/x/internal/util/ws"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/xtaci/smux"
|
"github.com/xtaci/smux"
|
||||||
)
|
)
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -54,34 +55,34 @@ func (d *mwsDialer) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
muxMaxStreamBuffer = "muxMaxStreamBuffer"
|
muxMaxStreamBuffer = "muxMaxStreamBuffer"
|
||||||
)
|
)
|
||||||
|
|
||||||
d.md.host = mdata.GetString(md, host)
|
d.md.host = mdx.GetString(md, host)
|
||||||
|
|
||||||
d.md.path = mdata.GetString(md, path)
|
d.md.path = mdx.GetString(md, path)
|
||||||
if d.md.path == "" {
|
if d.md.path == "" {
|
||||||
d.md.path = defaultPath
|
d.md.path = defaultPath
|
||||||
}
|
}
|
||||||
|
|
||||||
d.md.muxKeepAliveDisabled = mdata.GetBool(md, muxKeepAliveDisabled)
|
d.md.muxKeepAliveDisabled = mdx.GetBool(md, muxKeepAliveDisabled)
|
||||||
d.md.muxKeepAliveInterval = mdata.GetDuration(md, muxKeepAliveInterval)
|
d.md.muxKeepAliveInterval = mdx.GetDuration(md, muxKeepAliveInterval)
|
||||||
d.md.muxKeepAliveTimeout = mdata.GetDuration(md, muxKeepAliveTimeout)
|
d.md.muxKeepAliveTimeout = mdx.GetDuration(md, muxKeepAliveTimeout)
|
||||||
d.md.muxMaxFrameSize = mdata.GetInt(md, muxMaxFrameSize)
|
d.md.muxMaxFrameSize = mdx.GetInt(md, muxMaxFrameSize)
|
||||||
d.md.muxMaxReceiveBuffer = mdata.GetInt(md, muxMaxReceiveBuffer)
|
d.md.muxMaxReceiveBuffer = mdx.GetInt(md, muxMaxReceiveBuffer)
|
||||||
d.md.muxMaxStreamBuffer = mdata.GetInt(md, muxMaxStreamBuffer)
|
d.md.muxMaxStreamBuffer = mdx.GetInt(md, muxMaxStreamBuffer)
|
||||||
|
|
||||||
d.md.handshakeTimeout = mdata.GetDuration(md, handshakeTimeout)
|
d.md.handshakeTimeout = mdx.GetDuration(md, handshakeTimeout)
|
||||||
d.md.readHeaderTimeout = mdata.GetDuration(md, readHeaderTimeout)
|
d.md.readHeaderTimeout = mdx.GetDuration(md, readHeaderTimeout)
|
||||||
d.md.readBufferSize = mdata.GetInt(md, readBufferSize)
|
d.md.readBufferSize = mdx.GetInt(md, readBufferSize)
|
||||||
d.md.writeBufferSize = mdata.GetInt(md, writeBufferSize)
|
d.md.writeBufferSize = mdx.GetInt(md, writeBufferSize)
|
||||||
d.md.enableCompression = mdata.GetBool(md, enableCompression)
|
d.md.enableCompression = mdx.GetBool(md, enableCompression)
|
||||||
|
|
||||||
if m := mdata.GetStringMapString(md, header); len(m) > 0 {
|
if m := mdx.GetStringMapString(md, header); len(m) > 0 {
|
||||||
h := http.Header{}
|
h := http.Header{}
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
h.Add(k, v)
|
h.Add(k, v)
|
||||||
}
|
}
|
||||||
d.md.header = h
|
d.md.header = h
|
||||||
}
|
}
|
||||||
d.md.keepAlive = mdata.GetDuration(md, keepAlive)
|
d.md.keepAlive = mdx.GetDuration(md, keepAlive)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/go-gost/core/dialer"
|
"github.com/go-gost/core/dialer"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
type metadata struct {
|
type metadata struct {
|
||||||
@ -17,13 +18,13 @@ func (d *obfsHTTPDialer) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
host = "host"
|
host = "host"
|
||||||
)
|
)
|
||||||
|
|
||||||
if m := mdata.GetStringMapString(md, header); len(m) > 0 {
|
if m := mdx.GetStringMapString(md, header); len(m) > 0 {
|
||||||
h := http.Header{}
|
h := http.Header{}
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
h.Add(k, v)
|
h.Add(k, v)
|
||||||
}
|
}
|
||||||
d.md.header = h
|
d.md.header = h
|
||||||
}
|
}
|
||||||
d.md.host = mdata.GetString(md, host)
|
d.md.host = mdx.GetString(md, host)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/go-gost/core/dialer"
|
"github.com/go-gost/core/dialer"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -2,6 +2,7 @@ package tls
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
type metadata struct {
|
type metadata struct {
|
||||||
@ -13,6 +14,6 @@ func (d *obfsTLSDialer) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
host = "host"
|
host = "host"
|
||||||
)
|
)
|
||||||
|
|
||||||
d.md.host = mdata.GetString(md, host)
|
d.md.host = mdx.GetString(md, host)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,8 @@ import (
|
|||||||
"github.com/go-gost/core/dialer"
|
"github.com/go-gost/core/dialer"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
pht_util "github.com/go-gost/x/internal/util/pht"
|
pht_util "github.com/go-gost/x/internal/util/pht"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -34,19 +35,19 @@ func (d *phtDialer) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
host = "host"
|
host = "host"
|
||||||
)
|
)
|
||||||
|
|
||||||
d.md.authorizePath = mdata.GetString(md, authorizePath)
|
d.md.authorizePath = mdx.GetString(md, authorizePath)
|
||||||
if !strings.HasPrefix(d.md.authorizePath, "/") {
|
if !strings.HasPrefix(d.md.authorizePath, "/") {
|
||||||
d.md.authorizePath = defaultAuthorizePath
|
d.md.authorizePath = defaultAuthorizePath
|
||||||
}
|
}
|
||||||
d.md.pushPath = mdata.GetString(md, pushPath)
|
d.md.pushPath = mdx.GetString(md, pushPath)
|
||||||
if !strings.HasPrefix(d.md.pushPath, "/") {
|
if !strings.HasPrefix(d.md.pushPath, "/") {
|
||||||
d.md.pushPath = defaultPushPath
|
d.md.pushPath = defaultPushPath
|
||||||
}
|
}
|
||||||
d.md.pullPath = mdata.GetString(md, pullPath)
|
d.md.pullPath = mdx.GetString(md, pullPath)
|
||||||
if !strings.HasPrefix(d.md.pullPath, "/") {
|
if !strings.HasPrefix(d.md.pullPath, "/") {
|
||||||
d.md.pullPath = defaultPullPath
|
d.md.pullPath = defaultPullPath
|
||||||
}
|
}
|
||||||
|
|
||||||
d.md.host = mdata.GetString(md, host)
|
d.md.host = mdx.GetString(md, host)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,8 @@ import (
|
|||||||
"github.com/go-gost/core/dialer"
|
"github.com/go-gost/core/dialer"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
quic_util "github.com/go-gost/x/internal/util/quic"
|
quic_util "github.com/go-gost/x/internal/util/quic"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
"github.com/lucas-clemente/quic-go"
|
"github.com/lucas-clemente/quic-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
type metadata struct {
|
type metadata struct {
|
||||||
@ -25,16 +26,16 @@ func (d *quicDialer) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
host = "host"
|
host = "host"
|
||||||
)
|
)
|
||||||
|
|
||||||
d.md.handshakeTimeout = mdata.GetDuration(md, handshakeTimeout)
|
d.md.handshakeTimeout = mdx.GetDuration(md, handshakeTimeout)
|
||||||
|
|
||||||
if key := mdata.GetString(md, cipherKey); key != "" {
|
if key := mdx.GetString(md, cipherKey); key != "" {
|
||||||
d.md.cipherKey = []byte(key)
|
d.md.cipherKey = []byte(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.md.keepAlive = mdata.GetBool(md, keepAlive)
|
d.md.keepAlive = mdx.GetBool(md, keepAlive)
|
||||||
d.md.handshakeTimeout = mdata.GetDuration(md, handshakeTimeout)
|
d.md.handshakeTimeout = mdx.GetDuration(md, handshakeTimeout)
|
||||||
d.md.maxIdleTimeout = mdata.GetDuration(md, maxIdleTimeout)
|
d.md.maxIdleTimeout = mdx.GetDuration(md, maxIdleTimeout)
|
||||||
|
|
||||||
d.md.host = mdata.GetString(md, host)
|
d.md.host = mdx.GetString(md, host)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,8 @@ import (
|
|||||||
"github.com/go-gost/core/dialer"
|
"github.com/go-gost/core/dialer"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
ssh_util "github.com/go-gost/x/internal/util/ssh"
|
ssh_util "github.com/go-gost/x/internal/util/ssh"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -24,7 +25,7 @@ func (d *sshDialer) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
passphrase = "passphrase"
|
passphrase = "passphrase"
|
||||||
)
|
)
|
||||||
|
|
||||||
if v := mdata.GetString(md, user); v != "" {
|
if v := mdx.GetString(md, user); v != "" {
|
||||||
ss := strings.SplitN(v, ":", 2)
|
ss := strings.SplitN(v, ":", 2)
|
||||||
if len(ss) == 1 {
|
if len(ss) == 1 {
|
||||||
d.md.user = url.User(ss[0])
|
d.md.user = url.User(ss[0])
|
||||||
@ -33,13 +34,13 @@ func (d *sshDialer) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if key := mdata.GetString(md, privateKeyFile); key != "" {
|
if key := mdx.GetString(md, privateKeyFile); key != "" {
|
||||||
data, err := ioutil.ReadFile(key)
|
data, err := ioutil.ReadFile(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
pp := mdata.GetString(md, passphrase)
|
pp := mdx.GetString(md, passphrase)
|
||||||
if pp == "" {
|
if pp == "" {
|
||||||
d.md.signer, err = ssh.ParsePrivateKey(data)
|
d.md.signer, err = ssh.ParsePrivateKey(data)
|
||||||
} else {
|
} else {
|
||||||
@ -50,7 +51,7 @@ func (d *sshDialer) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
d.md.handshakeTimeout = mdata.GetDuration(md, handshakeTimeout)
|
d.md.handshakeTimeout = mdx.GetDuration(md, handshakeTimeout)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,8 @@ import (
|
|||||||
|
|
||||||
"github.com/go-gost/core/dialer"
|
"github.com/go-gost/core/dialer"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
ssh_util "github.com/go-gost/x/internal/util/ssh"
|
ssh_util "github.com/go-gost/x/internal/util/ssh"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -20,13 +21,13 @@ func (d *sshdDialer) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
passphrase = "passphrase"
|
passphrase = "passphrase"
|
||||||
)
|
)
|
||||||
|
|
||||||
if key := mdata.GetString(md, privateKeyFile); key != "" {
|
if key := mdx.GetString(md, privateKeyFile); key != "" {
|
||||||
data, err := ioutil.ReadFile(key)
|
data, err := ioutil.ReadFile(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
pp := mdata.GetString(md, passphrase)
|
pp := mdx.GetString(md, passphrase)
|
||||||
if pp == "" {
|
if pp == "" {
|
||||||
d.md.signer, err = ssh.ParsePrivateKey(data)
|
d.md.signer, err = ssh.ParsePrivateKey(data)
|
||||||
} else {
|
} else {
|
||||||
@ -37,7 +38,7 @@ func (d *sshdDialer) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
d.md.handshakeTimeout = mdata.GetDuration(md, handshakeTimeout)
|
d.md.handshakeTimeout = mdx.GetDuration(md, handshakeTimeout)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
48
dialer/tcp/dialer.go
Normal file
48
dialer/tcp/dialer.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package tcp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/go-gost/core/dialer"
|
||||||
|
"github.com/go-gost/core/logger"
|
||||||
|
md "github.com/go-gost/core/metadata"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.DialerRegistry().Register("tcp", NewDialer)
|
||||||
|
}
|
||||||
|
|
||||||
|
type tcpDialer struct {
|
||||||
|
md metadata
|
||||||
|
logger logger.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDialer(opts ...dialer.Option) dialer.Dialer {
|
||||||
|
options := &dialer.Options{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &tcpDialer{
|
||||||
|
logger: options.Logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *tcpDialer) Init(md md.Metadata) (err error) {
|
||||||
|
return d.parseMetadata(md)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *tcpDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOption) (net.Conn, error) {
|
||||||
|
var options dialer.DialOptions
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := options.NetDialer.Dial(ctx, "tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
d.logger.Error(err)
|
||||||
|
}
|
||||||
|
return conn, err
|
||||||
|
}
|
23
dialer/tcp/metadata.go
Normal file
23
dialer/tcp/metadata.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package tcp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
md "github.com/go-gost/core/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
dialTimeout = "dialTimeout"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultDialTimeout = 5 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
type metadata struct {
|
||||||
|
dialTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *tcpDialer) parseMetadata(md md.Metadata) (err error) {
|
||||||
|
return
|
||||||
|
}
|
68
dialer/tls/dialer.go
Normal file
68
dialer/tls/dialer.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package tls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-gost/core/dialer"
|
||||||
|
"github.com/go-gost/core/logger"
|
||||||
|
md "github.com/go-gost/core/metadata"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.DialerRegistry().Register("tls", NewDialer)
|
||||||
|
}
|
||||||
|
|
||||||
|
type tlsDialer struct {
|
||||||
|
md metadata
|
||||||
|
logger logger.Logger
|
||||||
|
options dialer.Options
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDialer(opts ...dialer.Option) dialer.Dialer {
|
||||||
|
options := dialer.Options{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &tlsDialer{
|
||||||
|
logger: options.Logger,
|
||||||
|
options: options,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *tlsDialer) Init(md md.Metadata) (err error) {
|
||||||
|
return d.parseMetadata(md)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *tlsDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOption) (net.Conn, error) {
|
||||||
|
var options dialer.DialOptions
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := options.NetDialer.Dial(ctx, "tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
d.logger.Error(err)
|
||||||
|
}
|
||||||
|
return conn, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handshake implements dialer.Handshaker
|
||||||
|
func (d *tlsDialer) Handshake(ctx context.Context, conn net.Conn, options ...dialer.HandshakeOption) (net.Conn, error) {
|
||||||
|
if d.md.handshakeTimeout > 0 {
|
||||||
|
conn.SetDeadline(time.Now().Add(d.md.handshakeTimeout))
|
||||||
|
defer conn.SetDeadline(time.Time{})
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsConn := tls.Client(conn, d.options.TLSConfig)
|
||||||
|
if err := tlsConn.HandshakeContext(ctx); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tlsConn, nil
|
||||||
|
}
|
22
dialer/tls/metadata.go
Normal file
22
dialer/tls/metadata.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package tls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
type metadata struct {
|
||||||
|
handshakeTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *tlsDialer) parseMetadata(md mdata.Metadata) (err error) {
|
||||||
|
const (
|
||||||
|
handshakeTimeout = "handshakeTimeout"
|
||||||
|
)
|
||||||
|
|
||||||
|
d.md.handshakeTimeout = mdx.GetDuration(md, handshakeTimeout)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
17
dialer/udp/conn.go
Normal file
17
dialer/udp/conn.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package udp
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
type conn struct {
|
||||||
|
*net.UDPConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
||||||
|
return c.UDPConn.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
|
||||||
|
n, err = c.UDPConn.Read(b)
|
||||||
|
addr = c.RemoteAddr()
|
||||||
|
return
|
||||||
|
}
|
50
dialer/udp/dialer.go
Normal file
50
dialer/udp/dialer.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package udp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/go-gost/core/dialer"
|
||||||
|
"github.com/go-gost/core/logger"
|
||||||
|
md "github.com/go-gost/core/metadata"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.DialerRegistry().Register("udp", NewDialer)
|
||||||
|
}
|
||||||
|
|
||||||
|
type udpDialer struct {
|
||||||
|
md metadata
|
||||||
|
logger logger.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDialer(opts ...dialer.Option) dialer.Dialer {
|
||||||
|
options := &dialer.Options{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &udpDialer{
|
||||||
|
logger: options.Logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *udpDialer) Init(md md.Metadata) (err error) {
|
||||||
|
return d.parseMetadata(md)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *udpDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOption) (net.Conn, error) {
|
||||||
|
var options dialer.DialOptions
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := options.NetDialer.Dial(ctx, "udp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &conn{
|
||||||
|
UDPConn: c.(*net.UDPConn),
|
||||||
|
}, nil
|
||||||
|
}
|
23
dialer/udp/metadata.go
Normal file
23
dialer/udp/metadata.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package udp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
md "github.com/go-gost/core/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
dialTimeout = "dialTimeout"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultDialTimeout = 5 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
type metadata struct {
|
||||||
|
dialTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *udpDialer) parseMetadata(md md.Metadata) (err error) {
|
||||||
|
return
|
||||||
|
}
|
@ -8,8 +8,8 @@ import (
|
|||||||
|
|
||||||
"github.com/go-gost/core/dialer"
|
"github.com/go-gost/core/dialer"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
ws_util "github.com/go-gost/x/internal/util/ws"
|
ws_util "github.com/go-gost/x/internal/util/ws"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -40,27 +41,27 @@ func (d *wsDialer) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
keepAlive = "keepAlive"
|
keepAlive = "keepAlive"
|
||||||
)
|
)
|
||||||
|
|
||||||
d.md.host = mdata.GetString(md, host)
|
d.md.host = mdx.GetString(md, host)
|
||||||
|
|
||||||
d.md.path = mdata.GetString(md, path)
|
d.md.path = mdx.GetString(md, path)
|
||||||
if d.md.path == "" {
|
if d.md.path == "" {
|
||||||
d.md.path = defaultPath
|
d.md.path = defaultPath
|
||||||
}
|
}
|
||||||
|
|
||||||
d.md.handshakeTimeout = mdata.GetDuration(md, handshakeTimeout)
|
d.md.handshakeTimeout = mdx.GetDuration(md, handshakeTimeout)
|
||||||
d.md.readHeaderTimeout = mdata.GetDuration(md, readHeaderTimeout)
|
d.md.readHeaderTimeout = mdx.GetDuration(md, readHeaderTimeout)
|
||||||
d.md.readBufferSize = mdata.GetInt(md, readBufferSize)
|
d.md.readBufferSize = mdx.GetInt(md, readBufferSize)
|
||||||
d.md.writeBufferSize = mdata.GetInt(md, writeBufferSize)
|
d.md.writeBufferSize = mdx.GetInt(md, writeBufferSize)
|
||||||
d.md.enableCompression = mdata.GetBool(md, enableCompression)
|
d.md.enableCompression = mdx.GetBool(md, enableCompression)
|
||||||
|
|
||||||
if m := mdata.GetStringMapString(md, header); len(m) > 0 {
|
if m := mdx.GetStringMapString(md, header); len(m) > 0 {
|
||||||
h := http.Header{}
|
h := http.Header{}
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
h.Add(k, v)
|
h.Add(k, v)
|
||||||
}
|
}
|
||||||
d.md.header = h
|
d.md.header = h
|
||||||
}
|
}
|
||||||
d.md.keepAlive = mdata.GetDuration(md, keepAlive)
|
d.md.keepAlive = mdx.GetDuration(md, keepAlive)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
2
go.mod
2
go.mod
@ -6,7 +6,7 @@ require (
|
|||||||
github.com/docker/libcontainer v2.2.1+incompatible
|
github.com/docker/libcontainer v2.2.1+incompatible
|
||||||
github.com/gin-contrib/cors v1.3.1
|
github.com/gin-contrib/cors v1.3.1
|
||||||
github.com/gin-gonic/gin v1.7.7
|
github.com/gin-gonic/gin v1.7.7
|
||||||
github.com/go-gost/core v0.0.0-20220403142327-6340d5198f83
|
github.com/go-gost/core v0.0.0-20220404033031-04f6ed470873
|
||||||
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09
|
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/relay v0.1.1-0.20211123134818-8ef7fd81ffd7
|
||||||
github.com/go-gost/tls-dissector v0.0.2-0.20211125135007-2b5d5bd9c07e
|
github.com/go-gost/tls-dissector v0.0.2-0.20211125135007-2b5d5bd9c07e
|
||||||
|
2
go.sum
2
go.sum
@ -119,6 +119,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2
|
|||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-gost/core v0.0.0-20220403142327-6340d5198f83 h1:Tt11K5yA/qnxSY8SDH774PSE/VP4Jqes+ab79g/N4Uw=
|
github.com/go-gost/core v0.0.0-20220403142327-6340d5198f83 h1:Tt11K5yA/qnxSY8SDH774PSE/VP4Jqes+ab79g/N4Uw=
|
||||||
github.com/go-gost/core v0.0.0-20220403142327-6340d5198f83/go.mod h1:oga1T7DJPJM+DpiQaZvTES9P9jvybRSgR/V5j+sEDpg=
|
github.com/go-gost/core v0.0.0-20220403142327-6340d5198f83/go.mod h1:oga1T7DJPJM+DpiQaZvTES9P9jvybRSgR/V5j+sEDpg=
|
||||||
|
github.com/go-gost/core v0.0.0-20220404033031-04f6ed470873 h1:u+g28xvN00bW5ivbhb2GGo0R+JIBy5arxy5R+rKesqk=
|
||||||
|
github.com/go-gost/core v0.0.0-20220404033031-04f6ed470873/go.mod h1:/LzdiQ+0+3FMhyqw0phjFjXFdOa1fcQR5/bL/7ripCs=
|
||||||
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09 h1:A95M6UWcfZgOuJkQ7QLfG0Hs5peWIUSysCDNz4pfe04=
|
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09 h1:A95M6UWcfZgOuJkQ7QLfG0Hs5peWIUSysCDNz4pfe04=
|
||||||
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09/go.mod h1:1G6I7HP7VFVxveGkoK8mnprnJqSqJjdcASKsdUn4Pp4=
|
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09/go.mod h1:1G6I7HP7VFVxveGkoK8mnprnJqSqJjdcASKsdUn4Pp4=
|
||||||
github.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7 h1:itaaJhQJ19kUXEB4Igb0EbY8m+1Py2AaNNSBds/9gk4=
|
github.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7 h1:itaaJhQJ19kUXEB4Igb0EbY8m+1Py2AaNNSBds/9gk4=
|
||||||
|
115
handler/auto/handler.go
Normal file
115
handler/auto/handler.go
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
package auto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
netpkg "github.com/go-gost/core/common/net"
|
||||||
|
"github.com/go-gost/core/handler"
|
||||||
|
md "github.com/go-gost/core/metadata"
|
||||||
|
"github.com/go-gost/gosocks4"
|
||||||
|
"github.com/go-gost/gosocks5"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.HandlerRegistry().Register("auto", NewHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
type autoHandler struct {
|
||||||
|
httpHandler handler.Handler
|
||||||
|
socks4Handler handler.Handler
|
||||||
|
socks5Handler handler.Handler
|
||||||
|
options handler.Options
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||||
|
options := handler.Options{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
h := &autoHandler{
|
||||||
|
options: options,
|
||||||
|
}
|
||||||
|
|
||||||
|
if f := registry.HandlerRegistry().Get("http"); f != nil {
|
||||||
|
v := append(opts,
|
||||||
|
handler.LoggerOption(options.Logger.WithFields(map[string]any{"type": "http"})))
|
||||||
|
h.httpHandler = f(v...)
|
||||||
|
}
|
||||||
|
if f := registry.HandlerRegistry().Get("socks4"); f != nil {
|
||||||
|
v := append(opts,
|
||||||
|
handler.LoggerOption(options.Logger.WithFields(map[string]any{"type": "socks4"})))
|
||||||
|
h.socks4Handler = f(v...)
|
||||||
|
}
|
||||||
|
if f := registry.HandlerRegistry().Get("socks5"); f != nil {
|
||||||
|
v := append(opts,
|
||||||
|
handler.LoggerOption(options.Logger.WithFields(map[string]any{"type": "socks5"})))
|
||||||
|
h.socks5Handler = f(v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *autoHandler) Init(md md.Metadata) error {
|
||||||
|
if h.httpHandler != nil {
|
||||||
|
if err := h.httpHandler.Init(md); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if h.socks4Handler != nil {
|
||||||
|
if err := h.socks4Handler.Init(md); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if h.socks5Handler != nil {
|
||||||
|
if err := h.socks5Handler.Init(md); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *autoHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
|
||||||
|
log := h.options.Logger.WithFields(map[string]any{
|
||||||
|
"remote": conn.RemoteAddr().String(),
|
||||||
|
"local": conn.LocalAddr().String(),
|
||||||
|
})
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||||
|
defer func() {
|
||||||
|
log.WithFields(map[string]any{
|
||||||
|
"duration": time.Since(start),
|
||||||
|
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||||
|
}()
|
||||||
|
|
||||||
|
br := bufio.NewReader(conn)
|
||||||
|
b, err := br.Peek(1)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
conn.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
conn = netpkg.NewBufferReaderConn(conn, br)
|
||||||
|
switch b[0] {
|
||||||
|
case gosocks4.Ver4: // socks4
|
||||||
|
if h.socks4Handler != nil {
|
||||||
|
return h.socks4Handler.Handle(ctx, conn)
|
||||||
|
}
|
||||||
|
case gosocks5.Ver5: // socks5
|
||||||
|
if h.socks5Handler != nil {
|
||||||
|
return h.socks5Handler.Handle(ctx, conn)
|
||||||
|
}
|
||||||
|
default: // http
|
||||||
|
if h.httpHandler != nil {
|
||||||
|
return h.httpHandler.Handle(ctx, conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -12,13 +12,13 @@ import (
|
|||||||
|
|
||||||
"github.com/go-gost/core/chain"
|
"github.com/go-gost/core/chain"
|
||||||
"github.com/go-gost/core/common/bufpool"
|
"github.com/go-gost/core/common/bufpool"
|
||||||
resolver_util "github.com/go-gost/core/common/util/resolver"
|
|
||||||
"github.com/go-gost/core/handler"
|
"github.com/go-gost/core/handler"
|
||||||
"github.com/go-gost/core/hosts"
|
"github.com/go-gost/core/hosts"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
resolver_util "github.com/go-gost/x/internal/util/resolver"
|
||||||
"github.com/go-gost/core/resolver/exchanger"
|
"github.com/go-gost/x/registry"
|
||||||
|
"github.com/go-gost/x/resolver/exchanger"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -5,9 +5,11 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
defaultTimeout = 5 * time.Second
|
||||||
defaultBufferSize = 1024
|
defaultBufferSize = 1024
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -29,17 +31,17 @@ func (h *dnsHandler) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
dns = "dns"
|
dns = "dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
h.md.readTimeout = mdata.GetDuration(md, readTimeout)
|
h.md.readTimeout = mdx.GetDuration(md, readTimeout)
|
||||||
h.md.ttl = mdata.GetDuration(md, ttl)
|
h.md.ttl = mdx.GetDuration(md, ttl)
|
||||||
h.md.timeout = mdata.GetDuration(md, timeout)
|
h.md.timeout = mdx.GetDuration(md, timeout)
|
||||||
if h.md.timeout <= 0 {
|
if h.md.timeout <= 0 {
|
||||||
h.md.timeout = 5 * time.Second
|
h.md.timeout = defaultTimeout
|
||||||
}
|
}
|
||||||
sip := mdata.GetString(md, clientIP)
|
sip := mdx.GetString(md, clientIP)
|
||||||
if sip != "" {
|
if sip != "" {
|
||||||
h.md.clientIP = net.ParseIP(sip)
|
h.md.clientIP = net.ParseIP(sip)
|
||||||
}
|
}
|
||||||
h.md.dns = mdata.GetStrings(md, dns)
|
h.md.dns = mdx.GetStrings(md, dns)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
117
handler/forward/local/handler.go
Normal file
117
handler/forward/local/handler.go
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
package local
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-gost/core/chain"
|
||||||
|
netpkg "github.com/go-gost/core/common/net"
|
||||||
|
"github.com/go-gost/core/handler"
|
||||||
|
md "github.com/go-gost/core/metadata"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.HandlerRegistry().Register("tcp", NewHandler)
|
||||||
|
registry.HandlerRegistry().Register("udp", NewHandler)
|
||||||
|
registry.HandlerRegistry().Register("forward", NewHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
type forwardHandler struct {
|
||||||
|
group *chain.NodeGroup
|
||||||
|
router *chain.Router
|
||||||
|
md metadata
|
||||||
|
options handler.Options
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||||
|
options := handler.Options{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &forwardHandler{
|
||||||
|
options: options,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *forwardHandler) Init(md md.Metadata) (err error) {
|
||||||
|
if err = h.parseMetadata(md); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.group == nil {
|
||||||
|
// dummy node used by relay connector.
|
||||||
|
h.group = chain.NewNodeGroup(&chain.Node{Name: "dummy", Addr: ":0"})
|
||||||
|
}
|
||||||
|
|
||||||
|
h.router = h.options.Router
|
||||||
|
if h.router == nil {
|
||||||
|
h.router = (&chain.Router{}).WithLogger(h.options.Logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward implements handler.Forwarder.
|
||||||
|
func (h *forwardHandler) Forward(group *chain.NodeGroup) {
|
||||||
|
h.group = group
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
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]any{
|
||||||
|
"duration": time.Since(start),
|
||||||
|
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||||
|
}()
|
||||||
|
|
||||||
|
target := h.group.Next()
|
||||||
|
if target == nil {
|
||||||
|
err := errors.New("target not available")
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
network := "tcp"
|
||||||
|
if _, ok := conn.(net.PacketConn); ok {
|
||||||
|
network = "udp"
|
||||||
|
}
|
||||||
|
|
||||||
|
log = log.WithFields(map[string]any{
|
||||||
|
"dst": fmt.Sprintf("%s/%s", target.Addr, network),
|
||||||
|
})
|
||||||
|
|
||||||
|
log.Infof("%s >> %s", conn.RemoteAddr(), target.Addr)
|
||||||
|
|
||||||
|
cc, err := h.router.Dial(ctx, network, target.Addr)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
// 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 err
|
||||||
|
}
|
||||||
|
defer cc.Close()
|
||||||
|
target.Marker.Reset()
|
||||||
|
|
||||||
|
t := time.Now()
|
||||||
|
log.Infof("%s <-> %s", conn.RemoteAddr(), target.Addr)
|
||||||
|
netpkg.Transport(conn, cc)
|
||||||
|
log.WithFields(map[string]any{
|
||||||
|
"duration": time.Since(t),
|
||||||
|
}).Infof("%s >-< %s", conn.RemoteAddr(), target.Addr)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
21
handler/forward/local/metadata.go
Normal file
21
handler/forward/local/metadata.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package local
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
type metadata struct {
|
||||||
|
readTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *forwardHandler) parseMetadata(md mdata.Metadata) (err error) {
|
||||||
|
const (
|
||||||
|
readTimeout = "readTimeout"
|
||||||
|
)
|
||||||
|
|
||||||
|
h.md.readTimeout = mdx.GetDuration(md, readTimeout)
|
||||||
|
return
|
||||||
|
}
|
111
handler/forward/remote/handler.go
Normal file
111
handler/forward/remote/handler.go
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
package remote
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-gost/core/chain"
|
||||||
|
netpkg "github.com/go-gost/core/common/net"
|
||||||
|
"github.com/go-gost/core/handler"
|
||||||
|
md "github.com/go-gost/core/metadata"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.HandlerRegistry().Register("rtcp", NewHandler)
|
||||||
|
registry.HandlerRegistry().Register("rudp", NewHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
type forwardHandler struct {
|
||||||
|
group *chain.NodeGroup
|
||||||
|
router *chain.Router
|
||||||
|
md metadata
|
||||||
|
options handler.Options
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||||
|
options := handler.Options{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &forwardHandler{
|
||||||
|
options: options,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *forwardHandler) Init(md md.Metadata) (err error) {
|
||||||
|
if err = h.parseMetadata(md); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h.router = h.options.Router
|
||||||
|
if h.router == nil {
|
||||||
|
h.router = (&chain.Router{}).WithLogger(h.options.Logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward implements handler.Forwarder.
|
||||||
|
func (h *forwardHandler) Forward(group *chain.NodeGroup) {
|
||||||
|
h.group = group
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
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]any{
|
||||||
|
"duration": time.Since(start),
|
||||||
|
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||||
|
}()
|
||||||
|
|
||||||
|
target := h.group.Next()
|
||||||
|
if target == nil {
|
||||||
|
err := errors.New("target not available")
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
network := "tcp"
|
||||||
|
if _, ok := conn.(net.PacketConn); ok {
|
||||||
|
network = "udp"
|
||||||
|
}
|
||||||
|
|
||||||
|
log = log.WithFields(map[string]any{
|
||||||
|
"dst": fmt.Sprintf("%s/%s", target.Addr, network),
|
||||||
|
})
|
||||||
|
|
||||||
|
log.Infof("%s >> %s", conn.RemoteAddr(), target.Addr)
|
||||||
|
|
||||||
|
cc, err := h.router.Dial(ctx, network, target.Addr)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
// 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 err
|
||||||
|
}
|
||||||
|
defer cc.Close()
|
||||||
|
target.Marker.Reset()
|
||||||
|
|
||||||
|
t := time.Now()
|
||||||
|
log.Infof("%s <-> %s", conn.RemoteAddr(), target.Addr)
|
||||||
|
netpkg.Transport(conn, cc)
|
||||||
|
log.WithFields(map[string]any{
|
||||||
|
"duration": time.Since(t),
|
||||||
|
}).Infof("%s >-< %s", conn.RemoteAddr(), target.Addr)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
21
handler/forward/remote/metadata.go
Normal file
21
handler/forward/remote/metadata.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package remote
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
type metadata struct {
|
||||||
|
readTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *forwardHandler) parseMetadata(md mdata.Metadata) (err error) {
|
||||||
|
const (
|
||||||
|
readTimeout = "readTimeout"
|
||||||
|
)
|
||||||
|
|
||||||
|
h.md.readTimeout = mdx.GetDuration(md, readTimeout)
|
||||||
|
return
|
||||||
|
}
|
337
handler/http/handler.go
Normal file
337
handler/http/handler.go
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"hash/crc32"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/asaskevich/govalidator"
|
||||||
|
"github.com/go-gost/core/chain"
|
||||||
|
netpkg "github.com/go-gost/core/common/net"
|
||||||
|
"github.com/go-gost/core/handler"
|
||||||
|
"github.com/go-gost/core/logger"
|
||||||
|
md "github.com/go-gost/core/metadata"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.HandlerRegistry().Register("http", NewHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
type httpHandler struct {
|
||||||
|
router *chain.Router
|
||||||
|
md metadata
|
||||||
|
options handler.Options
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||||
|
options := handler.Options{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &httpHandler{
|
||||||
|
options: options,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *httpHandler) Init(md md.Metadata) error {
|
||||||
|
if err := h.parseMetadata(md); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
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, opts ...handler.HandleOption) error {
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
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]any{
|
||||||
|
"duration": time.Since(start),
|
||||||
|
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||||
|
}()
|
||||||
|
|
||||||
|
req, err := http.ReadRequest(bufio.NewReader(conn))
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer req.Body.Close()
|
||||||
|
|
||||||
|
return h.handleRequest(ctx, conn, req, log)
|
||||||
|
}
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
network := req.Header.Get("X-Gost-Protocol")
|
||||||
|
if network != "udp" {
|
||||||
|
network = "tcp"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to get the actual host.
|
||||||
|
// Compatible with GOST 2.x.
|
||||||
|
if v := req.Header.Get("Gost-Target"); v != "" {
|
||||||
|
if h, err := h.decodeServerName(v); err == nil {
|
||||||
|
req.Host = h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
req.Header.Del("Gost-Target")
|
||||||
|
|
||||||
|
if v := req.Header.Get("X-Gost-Target"); v != "" {
|
||||||
|
if h, err := h.decodeServerName(v); err == nil {
|
||||||
|
req.Host = h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
req.Header.Del("X-Gost-Target")
|
||||||
|
|
||||||
|
addr := req.Host
|
||||||
|
if _, port, _ := net.SplitHostPort(addr); port == "" {
|
||||||
|
addr = net.JoinHostPort(addr, "80")
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := map[string]any{
|
||||||
|
"dst": addr,
|
||||||
|
}
|
||||||
|
if u, _, _ := h.basicProxyAuth(req.Header.Get("Proxy-Authorization"), log); u != "" {
|
||||||
|
fields["user"] = u
|
||||||
|
}
|
||||||
|
log = log.WithFields(fields)
|
||||||
|
|
||||||
|
if log.IsLevelEnabled(logger.DebugLevel) {
|
||||||
|
dump, _ := httputil.DumpRequest(req, false)
|
||||||
|
log.Debug(string(dump))
|
||||||
|
}
|
||||||
|
log.Infof("%s >> %s", conn.RemoteAddr(), addr)
|
||||||
|
|
||||||
|
resp := &http.Response{
|
||||||
|
ProtoMajor: 1,
|
||||||
|
ProtoMinor: 1,
|
||||||
|
Header: h.md.header,
|
||||||
|
}
|
||||||
|
if resp.Header == nil {
|
||||||
|
resp.Header = http.Header{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.options.Bypass != nil && h.options.Bypass.Contains(addr) {
|
||||||
|
resp.StatusCode = http.StatusForbidden
|
||||||
|
|
||||||
|
if log.IsLevelEnabled(logger.DebugLevel) {
|
||||||
|
dump, _ := httputil.DumpResponse(resp, false)
|
||||||
|
log.Debug(string(dump))
|
||||||
|
}
|
||||||
|
log.Info("bypass: ", addr)
|
||||||
|
|
||||||
|
return resp.Write(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !h.authenticate(conn, req, resp, log) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if network == "udp" {
|
||||||
|
return h.handleUDP(ctx, conn, log)
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Method == "PRI" ||
|
||||||
|
(req.Method != http.MethodConnect && req.URL.Scheme != "http") {
|
||||||
|
resp.StatusCode = http.StatusBadRequest
|
||||||
|
|
||||||
|
if log.IsLevelEnabled(logger.DebugLevel) {
|
||||||
|
dump, _ := httputil.DumpResponse(resp, false)
|
||||||
|
log.Debug(string(dump))
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.Write(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Del("Proxy-Authorization")
|
||||||
|
|
||||||
|
cc, err := h.router.Dial(ctx, network, addr)
|
||||||
|
if err != nil {
|
||||||
|
resp.StatusCode = http.StatusServiceUnavailable
|
||||||
|
|
||||||
|
if log.IsLevelEnabled(logger.DebugLevel) {
|
||||||
|
dump, _ := httputil.DumpResponse(resp, false)
|
||||||
|
log.Debug(string(dump))
|
||||||
|
}
|
||||||
|
resp.Write(conn)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer cc.Close()
|
||||||
|
|
||||||
|
if req.Method == http.MethodConnect {
|
||||||
|
resp.StatusCode = http.StatusOK
|
||||||
|
resp.Status = "200 Connection established"
|
||||||
|
|
||||||
|
if log.IsLevelEnabled(logger.DebugLevel) {
|
||||||
|
dump, _ := httputil.DumpResponse(resp, false)
|
||||||
|
log.Debug(string(dump))
|
||||||
|
}
|
||||||
|
if err = resp.Write(conn); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
req.Header.Del("Proxy-Connection")
|
||||||
|
if err = req.Write(cc); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
log.Infof("%s <-> %s", conn.RemoteAddr(), addr)
|
||||||
|
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) {
|
||||||
|
b, err := base64.RawURLEncoding.DecodeString(s)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if len(b) < 4 {
|
||||||
|
return "", errors.New("invalid name")
|
||||||
|
}
|
||||||
|
v, err := base64.RawURLEncoding.DecodeString(string(b[4:]))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if crc32.ChecksumIEEE(v) != binary.BigEndian.Uint32(b[:4]) {
|
||||||
|
return "", errors.New("invalid name")
|
||||||
|
}
|
||||||
|
return string(v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *httpHandler) basicProxyAuth(proxyAuth string, log logger.Logger) (username, password string, ok bool) {
|
||||||
|
if proxyAuth == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(proxyAuth, "Basic ") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(proxyAuth, "Basic "))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cs := string(c)
|
||||||
|
s := strings.IndexByte(cs, ':')
|
||||||
|
if s < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return cs[:s], cs[s+1:], true
|
||||||
|
}
|
||||||
|
|
||||||
|
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.options.Auther == nil || h.options.Auther.Authenticate(u, p) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
pr := h.md.probeResistance
|
||||||
|
// probing resistance is enabled, and knocking host is mismatch.
|
||||||
|
if pr != nil && (pr.Knock == "" || !strings.EqualFold(req.URL.Hostname(), pr.Knock)) {
|
||||||
|
resp.StatusCode = http.StatusServiceUnavailable // default status code
|
||||||
|
|
||||||
|
switch pr.Type {
|
||||||
|
case "code":
|
||||||
|
resp.StatusCode, _ = strconv.Atoi(pr.Value)
|
||||||
|
case "web":
|
||||||
|
url := pr.Value
|
||||||
|
if !strings.HasPrefix(url, "http") {
|
||||||
|
url = "http://" + url
|
||||||
|
}
|
||||||
|
r, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
resp = r
|
||||||
|
defer resp.Body.Close()
|
||||||
|
case "host":
|
||||||
|
cc, err := net.Dial("tcp", pr.Value)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
defer cc.Close()
|
||||||
|
|
||||||
|
req.Write(cc)
|
||||||
|
netpkg.Transport(conn, cc)
|
||||||
|
return
|
||||||
|
case "file":
|
||||||
|
f, _ := os.Open(pr.Value)
|
||||||
|
if f != nil {
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
resp.StatusCode = http.StatusOK
|
||||||
|
if finfo, _ := f.Stat(); finfo != nil {
|
||||||
|
resp.ContentLength = finfo.Size()
|
||||||
|
}
|
||||||
|
resp.Header.Set("Content-Type", "text/html")
|
||||||
|
resp.Body = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.Header == nil {
|
||||||
|
resp.Header = http.Header{}
|
||||||
|
}
|
||||||
|
if resp.StatusCode == 0 {
|
||||||
|
resp.StatusCode = http.StatusProxyAuthRequired
|
||||||
|
resp.Header.Add("Proxy-Authenticate", "Basic realm=\"gost\"")
|
||||||
|
if strings.ToLower(req.Header.Get("Proxy-Connection")) == "keep-alive" {
|
||||||
|
// XXX libcurl will keep sending auth request in same conn
|
||||||
|
// which we don't supported yet.
|
||||||
|
resp.Header.Add("Connection", "close")
|
||||||
|
resp.Header.Add("Proxy-Connection", "close")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("proxy authentication required")
|
||||||
|
} else {
|
||||||
|
resp.Header.Set("Server", "nginx/1.20.1")
|
||||||
|
resp.Header.Set("Date", time.Now().Format(http.TimeFormat))
|
||||||
|
if resp.StatusCode == http.StatusOK {
|
||||||
|
resp.Header.Set("Connection", "keep-alive")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if log.IsLevelEnabled(logger.DebugLevel) {
|
||||||
|
dump, _ := httputil.DumpResponse(resp, false)
|
||||||
|
log.Debug(string(dump))
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Write(conn)
|
||||||
|
return
|
||||||
|
}
|
54
handler/http/metadata.go
Normal file
54
handler/http/metadata.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
type metadata struct {
|
||||||
|
probeResistance *probeResistance
|
||||||
|
sni bool
|
||||||
|
enableUDP bool
|
||||||
|
header http.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *httpHandler) parseMetadata(md mdata.Metadata) error {
|
||||||
|
const (
|
||||||
|
header = "header"
|
||||||
|
probeResistKey = "probeResistance"
|
||||||
|
knock = "knock"
|
||||||
|
sni = "sni"
|
||||||
|
enableUDP = "udp"
|
||||||
|
)
|
||||||
|
|
||||||
|
if m := mdx.GetStringMapString(md, header); len(m) > 0 {
|
||||||
|
hd := http.Header{}
|
||||||
|
for k, v := range m {
|
||||||
|
hd.Add(k, v)
|
||||||
|
}
|
||||||
|
h.md.header = hd
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := mdx.GetString(md, probeResistKey); v != "" {
|
||||||
|
if ss := strings.SplitN(v, ":", 2); len(ss) == 2 {
|
||||||
|
h.md.probeResistance = &probeResistance{
|
||||||
|
Type: ss[0],
|
||||||
|
Value: ss[1],
|
||||||
|
Knock: mdx.GetString(md, knock),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h.md.sni = mdx.GetBool(md, sni)
|
||||||
|
h.md.enableUDP = mdx.GetBool(md, enableUDP)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type probeResistance struct {
|
||||||
|
Type string
|
||||||
|
Value string
|
||||||
|
Knock string
|
||||||
|
}
|
80
handler/http/udp.go
Normal file
80
handler/http/udp.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-gost/core/common/net/relay"
|
||||||
|
"github.com/go-gost/core/logger"
|
||||||
|
"github.com/go-gost/x/internal/util/socks"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *httpHandler) handleUDP(ctx context.Context, conn net.Conn, log logger.Logger) error {
|
||||||
|
log = log.WithFields(map[string]any{
|
||||||
|
"cmd": "udp",
|
||||||
|
})
|
||||||
|
|
||||||
|
resp := &http.Response{
|
||||||
|
ProtoMajor: 1,
|
||||||
|
ProtoMinor: 1,
|
||||||
|
Header: h.md.header,
|
||||||
|
}
|
||||||
|
if resp.Header == nil {
|
||||||
|
resp.Header = http.Header{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !h.md.enableUDP {
|
||||||
|
resp.StatusCode = http.StatusForbidden
|
||||||
|
|
||||||
|
if log.IsLevelEnabled(logger.DebugLevel) {
|
||||||
|
dump, _ := httputil.DumpResponse(resp, false)
|
||||||
|
log.Debug(string(dump))
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Error("http: UDP relay is disabled")
|
||||||
|
|
||||||
|
return resp.Write(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.StatusCode = http.StatusOK
|
||||||
|
if log.IsLevelEnabled(logger.DebugLevel) {
|
||||||
|
dump, _ := httputil.DumpResponse(resp, false)
|
||||||
|
log.Debug(string(dump))
|
||||||
|
}
|
||||||
|
if err := resp.Write(conn); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// obtain a udp connection
|
||||||
|
c, err := h.router.Dial(ctx, "udp", "") // UDP association
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
pc, ok := c.(net.PacketConn)
|
||||||
|
if !ok {
|
||||||
|
err = errors.New("wrong connection type")
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
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]any{
|
||||||
|
"duration": time.Since(t),
|
||||||
|
}).Infof("%s >-< %s", conn.RemoteAddr(), pc.LocalAddr())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -23,7 +23,7 @@ import (
|
|||||||
"github.com/go-gost/core/handler"
|
"github.com/go-gost/core/handler"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
type metadata struct {
|
type metadata struct {
|
||||||
@ -19,7 +20,7 @@ func (h *http2Handler) parseMetadata(md mdata.Metadata) error {
|
|||||||
knock = "knock"
|
knock = "knock"
|
||||||
)
|
)
|
||||||
|
|
||||||
if m := mdata.GetStringMapString(md, header); len(m) > 0 {
|
if m := mdx.GetStringMapString(md, header); len(m) > 0 {
|
||||||
hd := http.Header{}
|
hd := http.Header{}
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
hd.Add(k, v)
|
hd.Add(k, v)
|
||||||
@ -27,12 +28,12 @@ func (h *http2Handler) parseMetadata(md mdata.Metadata) error {
|
|||||||
h.md.header = hd
|
h.md.header = hd
|
||||||
}
|
}
|
||||||
|
|
||||||
if v := mdata.GetString(md, probeResistKey); v != "" {
|
if v := mdx.GetString(md, probeResistKey); v != "" {
|
||||||
if ss := strings.SplitN(v, ":", 2); len(ss) == 2 {
|
if ss := strings.SplitN(v, ":", 2); len(ss) == 2 {
|
||||||
h.md.probeResistance = &probeResistance{
|
h.md.probeResistance = &probeResistance{
|
||||||
Type: ss[0],
|
Type: ss[0],
|
||||||
Value: ss[1],
|
Value: ss[1],
|
||||||
Knock: mdata.GetString(md, knock),
|
Knock: mdx.GetString(md, knock),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,8 @@ import (
|
|||||||
"github.com/go-gost/core/handler"
|
"github.com/go-gost/core/handler"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
dissector "github.com/go-gost/tls-dissector"
|
dissector "github.com/go-gost/tls-dissector"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -2,6 +2,7 @@ package redirect
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
type metadata struct {
|
type metadata struct {
|
||||||
@ -14,7 +15,7 @@ func (h *redirectHandler) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
sniffing = "sniffing"
|
sniffing = "sniffing"
|
||||||
tproxy = "tproxy"
|
tproxy = "tproxy"
|
||||||
)
|
)
|
||||||
h.md.sniffing = mdata.GetBool(md, sniffing)
|
h.md.sniffing = mdx.GetBool(md, sniffing)
|
||||||
h.md.tproxy = mdata.GetBool(md, tproxy)
|
h.md.tproxy = mdx.GetBool(md, tproxy)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
netpkg "github.com/go-gost/core/common/net"
|
netpkg "github.com/go-gost/core/common/net"
|
||||||
"github.com/go-gost/core/handler"
|
"github.com/go-gost/core/handler"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -10,8 +10,8 @@ import (
|
|||||||
"github.com/go-gost/core/chain"
|
"github.com/go-gost/core/chain"
|
||||||
"github.com/go-gost/core/handler"
|
"github.com/go-gost/core/handler"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
"github.com/go-gost/relay"
|
"github.com/go-gost/relay"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
type metadata struct {
|
type metadata struct {
|
||||||
@ -22,11 +23,11 @@ func (h *relayHandler) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
noDelay = "nodelay"
|
noDelay = "nodelay"
|
||||||
)
|
)
|
||||||
|
|
||||||
h.md.readTimeout = mdata.GetDuration(md, readTimeout)
|
h.md.readTimeout = mdx.GetDuration(md, readTimeout)
|
||||||
h.md.enableBind = mdata.GetBool(md, enableBind)
|
h.md.enableBind = mdx.GetBool(md, enableBind)
|
||||||
h.md.noDelay = mdata.GetBool(md, noDelay)
|
h.md.noDelay = mdx.GetBool(md, noDelay)
|
||||||
|
|
||||||
if bs := mdata.GetInt(md, udpBufferSize); bs > 0 {
|
if bs := mdx.GetInt(md, udpBufferSize); bs > 0 {
|
||||||
h.md.udpBufferSize = int(math.Min(math.Max(float64(bs), 512), 64*1024))
|
h.md.udpBufferSize = int(math.Min(math.Max(float64(bs), 512), 64*1024))
|
||||||
} else {
|
} else {
|
||||||
h.md.udpBufferSize = 1500
|
h.md.udpBufferSize = 1500
|
||||||
|
@ -16,8 +16,8 @@ import (
|
|||||||
netpkg "github.com/go-gost/core/common/net"
|
netpkg "github.com/go-gost/core/common/net"
|
||||||
"github.com/go-gost/core/handler"
|
"github.com/go-gost/core/handler"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/core/registry"
|
|
||||||
dissector "github.com/go-gost/tls-dissector"
|
dissector "github.com/go-gost/tls-dissector"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
type metadata struct {
|
type metadata struct {
|
||||||
@ -15,6 +16,6 @@ func (h *sniHandler) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
readTimeout = "readTimeout"
|
readTimeout = "readTimeout"
|
||||||
)
|
)
|
||||||
|
|
||||||
h.md.readTimeout = mdata.GetDuration(md, readTimeout)
|
h.md.readTimeout = mdx.GetDuration(md, readTimeout)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
152
handler/socks/v4/handler.go
Normal file
152
handler/socks/v4/handler.go
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
package v4
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-gost/core/chain"
|
||||||
|
netpkg "github.com/go-gost/core/common/net"
|
||||||
|
"github.com/go-gost/core/handler"
|
||||||
|
"github.com/go-gost/core/logger"
|
||||||
|
md "github.com/go-gost/core/metadata"
|
||||||
|
"github.com/go-gost/gosocks4"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrUnknownCmd = errors.New("socks4: unknown command")
|
||||||
|
ErrUnimplemented = errors.New("socks4: unimplemented")
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.HandlerRegistry().Register("socks4", NewHandler)
|
||||||
|
registry.HandlerRegistry().Register("socks4a", NewHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
type socks4Handler struct {
|
||||||
|
router *chain.Router
|
||||||
|
md metadata
|
||||||
|
options handler.Options
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||||
|
options := handler.Options{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &socks4Handler{
|
||||||
|
options: options,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *socks4Handler) Init(md md.Metadata) (err error) {
|
||||||
|
if err := h.parseMetadata(md); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
h.router = h.options.Router
|
||||||
|
if h.router == nil {
|
||||||
|
h.router = (&chain.Router{}).WithLogger(h.options.Logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *socks4Handler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
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]any{
|
||||||
|
"duration": time.Since(start),
|
||||||
|
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||||
|
}()
|
||||||
|
|
||||||
|
if h.md.readTimeout > 0 {
|
||||||
|
conn.SetReadDeadline(time.Now().Add(h.md.readTimeout))
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := gosocks4.ReadRequest(conn)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Debug(req)
|
||||||
|
|
||||||
|
conn.SetReadDeadline(time.Time{})
|
||||||
|
|
||||||
|
if h.options.Auther != nil &&
|
||||||
|
!h.options.Auther.Authenticate(string(req.Userid), "") {
|
||||||
|
resp := gosocks4.NewReply(gosocks4.RejectedUserid, nil)
|
||||||
|
log.Debug(resp)
|
||||||
|
return resp.Write(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch req.Cmd {
|
||||||
|
case gosocks4.CmdConnect:
|
||||||
|
return h.handleConnect(ctx, conn, req, log)
|
||||||
|
case gosocks4.CmdBind:
|
||||||
|
return h.handleBind(ctx, conn, req)
|
||||||
|
default:
|
||||||
|
err = ErrUnknownCmd
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *socks4Handler) handleConnect(ctx context.Context, conn net.Conn, req *gosocks4.Request, log logger.Logger) error {
|
||||||
|
addr := req.Addr.String()
|
||||||
|
|
||||||
|
log = log.WithFields(map[string]any{
|
||||||
|
"dst": addr,
|
||||||
|
})
|
||||||
|
log.Infof("%s >> %s", conn.RemoteAddr(), addr)
|
||||||
|
|
||||||
|
if h.options.Bypass != nil && h.options.Bypass.Contains(addr) {
|
||||||
|
resp := gosocks4.NewReply(gosocks4.Rejected, nil)
|
||||||
|
log.Debug(resp)
|
||||||
|
log.Info("bypass: ", addr)
|
||||||
|
return resp.Write(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
cc, err := h.router.Dial(ctx, "tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
resp := gosocks4.NewReply(gosocks4.Failed, nil)
|
||||||
|
resp.Write(conn)
|
||||||
|
log.Debug(resp)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer cc.Close()
|
||||||
|
|
||||||
|
resp := gosocks4.NewReply(gosocks4.Granted, nil)
|
||||||
|
if err := resp.Write(conn); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Debug(resp)
|
||||||
|
|
||||||
|
t := time.Now()
|
||||||
|
log.Infof("%s <-> %s", conn.RemoteAddr(), addr)
|
||||||
|
netpkg.Transport(conn, cc)
|
||||||
|
log.WithFields(map[string]any{
|
||||||
|
"duration": time.Since(t),
|
||||||
|
}).Infof("%s >-< %s", conn.RemoteAddr(), addr)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *socks4Handler) handleBind(ctx context.Context, conn net.Conn, req *gosocks4.Request) error {
|
||||||
|
// TODO: bind
|
||||||
|
return ErrUnimplemented
|
||||||
|
}
|
21
handler/socks/v4/metadata.go
Normal file
21
handler/socks/v4/metadata.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package v4
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdx "github.com/go-gost/x/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
type metadata struct {
|
||||||
|
readTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *socks4Handler) parseMetadata(md mdata.Metadata) (err error) {
|
||||||
|
const (
|
||||||
|
readTimeout = "readTimeout"
|
||||||
|
)
|
||||||
|
|
||||||
|
h.md.readTimeout = mdx.GetDuration(md, readTimeout)
|
||||||
|
return
|
||||||
|
}
|
149
handler/socks/v5/bind.go
Normal file
149
handler/socks/v5/bind.go
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
package v5
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
netpkg "github.com/go-gost/core/common/net"
|
||||||
|
"github.com/go-gost/core/logger"
|
||||||
|
"github.com/go-gost/gosocks5"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *socks5Handler) handleBind(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) error {
|
||||||
|
log = log.WithFields(map[string]any{
|
||||||
|
"dst": fmt.Sprintf("%s/%s", address, network),
|
||||||
|
"cmd": "bind",
|
||||||
|
})
|
||||||
|
|
||||||
|
log.Infof("%s >> %s", conn.RemoteAddr(), address)
|
||||||
|
|
||||||
|
if !h.md.enableBind {
|
||||||
|
reply := gosocks5.NewReply(gosocks5.NotAllowed, nil)
|
||||||
|
log.Debug(reply)
|
||||||
|
log.Error("socks5: BIND is disabled")
|
||||||
|
return reply.Write(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BIND does not support chain.
|
||||||
|
return h.bindLocal(ctx, conn, network, address, log)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *socks5Handler) bindLocal(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) error {
|
||||||
|
ln, err := net.Listen(network, address) // strict mode: if the port already in use, it will return error
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
reply := gosocks5.NewReply(gosocks5.Failure, nil)
|
||||||
|
if err := reply.Write(conn); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
log.Debug(reply)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
socksAddr := gosocks5.Addr{}
|
||||||
|
if err := socksAddr.ParseFrom(ln.Addr().String()); err != nil {
|
||||||
|
log.Warn(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issue: may not reachable when host has multi-interface
|
||||||
|
socksAddr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String())
|
||||||
|
socksAddr.Type = 0
|
||||||
|
reply := gosocks5.NewReply(gosocks5.Succeeded, &socksAddr)
|
||||||
|
if err := reply.Write(conn); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
ln.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Debug(reply)
|
||||||
|
|
||||||
|
log = log.WithFields(map[string]any{
|
||||||
|
"bind": fmt.Sprintf("%s/%s", ln.Addr(), ln.Addr().Network()),
|
||||||
|
})
|
||||||
|
|
||||||
|
log.Debugf("bind on %s OK", ln.Addr())
|
||||||
|
|
||||||
|
h.serveBind(ctx, conn, ln, log)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *socks5Handler) serveBind(ctx context.Context, conn net.Conn, ln net.Listener, log logger.Logger) {
|
||||||
|
var rc net.Conn
|
||||||
|
accept := func() <-chan error {
|
||||||
|
errc := make(chan error, 1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(errc)
|
||||||
|
defer ln.Close()
|
||||||
|
|
||||||
|
c, err := ln.Accept()
|
||||||
|
if err != nil {
|
||||||
|
errc <- err
|
||||||
|
}
|
||||||
|
rc = c
|
||||||
|
}()
|
||||||
|
|
||||||
|
return errc
|
||||||
|
}
|
||||||
|
|
||||||
|
pc1, pc2 := net.Pipe()
|
||||||
|
pipe := func() <-chan error {
|
||||||
|
errc := make(chan error, 1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(errc)
|
||||||
|
defer pc1.Close()
|
||||||
|
|
||||||
|
errc <- netpkg.Transport(conn, pc1)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return errc
|
||||||
|
}
|
||||||
|
|
||||||
|
defer pc2.Close()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-accept():
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
|
||||||
|
reply := gosocks5.NewReply(gosocks5.Failure, nil)
|
||||||
|
if err := reply.Write(pc2); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
log.Debug(reply)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rc.Close()
|
||||||
|
|
||||||
|
log.Debugf("peer %s accepted", rc.RemoteAddr())
|
||||||
|
|
||||||
|
log = log.WithFields(map[string]any{
|
||||||
|
"local": rc.LocalAddr().String(),
|
||||||
|
"remote": rc.RemoteAddr().String(),
|
||||||
|
})
|
||||||
|
|
||||||
|
raddr := gosocks5.Addr{}
|
||||||
|
raddr.ParseFrom(rc.RemoteAddr().String())
|
||||||
|
reply := gosocks5.NewReply(gosocks5.Succeeded, &raddr)
|
||||||
|
if err := reply.Write(pc2); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
log.Debug(reply)
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
log.Infof("%s <-> %s", rc.LocalAddr(), rc.RemoteAddr())
|
||||||
|
netpkg.Transport(pc2, rc)
|
||||||
|
log.WithFields(map[string]any{"duration": time.Since(start)}).
|
||||||
|
Infof("%s >-< %s", rc.LocalAddr(), rc.RemoteAddr())
|
||||||
|
|
||||||
|
case err := <-pipe():
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
ln.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
53
handler/socks/v5/connect.go
Normal file
53
handler/socks/v5/connect.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package v5
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
netpkg "github.com/go-gost/core/common/net"
|
||||||
|
"github.com/go-gost/core/logger"
|
||||||
|
"github.com/go-gost/gosocks5"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *socks5Handler) handleConnect(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) error {
|
||||||
|
log = log.WithFields(map[string]any{
|
||||||
|
"dst": fmt.Sprintf("%s/%s", address, network),
|
||||||
|
"cmd": "connect",
|
||||||
|
})
|
||||||
|
log.Infof("%s >> %s", conn.RemoteAddr(), address)
|
||||||
|
|
||||||
|
if h.options.Bypass != nil && h.options.Bypass.Contains(address) {
|
||||||
|
resp := gosocks5.NewReply(gosocks5.NotAllowed, nil)
|
||||||
|
log.Debug(resp)
|
||||||
|
log.Info("bypass: ", address)
|
||||||
|
return resp.Write(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
cc, err := h.router.Dial(ctx, network, address)
|
||||||
|
if err != nil {
|
||||||
|
resp := gosocks5.NewReply(gosocks5.NetUnreachable, nil)
|
||||||
|
log.Debug(resp)
|
||||||
|
resp.Write(conn)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer cc.Close()
|
||||||
|
|
||||||
|
resp := gosocks5.NewReply(gosocks5.Succeeded, nil)
|
||||||
|
if err := resp.Write(conn); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Debug(resp)
|
||||||
|
|
||||||
|
t := time.Now()
|
||||||
|
log.Infof("%s <-> %s", conn.RemoteAddr(), address)
|
||||||
|
netpkg.Transport(conn, cc)
|
||||||
|
log.WithFields(map[string]any{
|
||||||
|
"duration": time.Since(t),
|
||||||
|
}).Infof("%s >-< %s", conn.RemoteAddr(), address)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
115
handler/socks/v5/handler.go
Normal file
115
handler/socks/v5/handler.go
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
package v5
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-gost/core/chain"
|
||||||
|
"github.com/go-gost/core/handler"
|
||||||
|
md "github.com/go-gost/core/metadata"
|
||||||
|
"github.com/go-gost/gosocks5"
|
||||||
|
"github.com/go-gost/x/internal/util/socks"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrUnknownCmd = errors.New("socks5: unknown command")
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.HandlerRegistry().Register("socks5", NewHandler)
|
||||||
|
registry.HandlerRegistry().Register("socks", NewHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
type socks5Handler struct {
|
||||||
|
selector gosocks5.Selector
|
||||||
|
router *chain.Router
|
||||||
|
md metadata
|
||||||
|
options handler.Options
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||||
|
options := handler.Options{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &socks5Handler{
|
||||||
|
options: options,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *socks5Handler) Init(md md.Metadata) (err error) {
|
||||||
|
if err = h.parseMetadata(md); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h.router = h.options.Router
|
||||||
|
if h.router == nil {
|
||||||
|
h.router = (&chain.Router{}).WithLogger(h.options.Logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
h.selector = &serverSelector{
|
||||||
|
Authenticator: h.options.Auther,
|
||||||
|
TLSConfig: h.options.TLSConfig,
|
||||||
|
logger: h.options.Logger,
|
||||||
|
noTLS: h.md.noTLS,
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *socks5Handler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
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]any{
|
||||||
|
"duration": time.Since(start),
|
||||||
|
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||||
|
}()
|
||||||
|
|
||||||
|
if h.md.readTimeout > 0 {
|
||||||
|
conn.SetReadDeadline(time.Now().Add(h.md.readTimeout))
|
||||||
|
}
|
||||||
|
|
||||||
|
conn = gosocks5.ServerConn(conn, h.selector)
|
||||||
|
req, err := gosocks5.ReadRequest(conn)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Debug(req)
|
||||||
|
conn.SetReadDeadline(time.Time{})
|
||||||
|
|
||||||
|
address := req.Addr.String()
|
||||||
|
|
||||||
|
switch req.Cmd {
|
||||||
|
case gosocks5.CmdConnect:
|
||||||
|
return h.handleConnect(ctx, conn, "tcp", address, log)
|
||||||
|
case gosocks5.CmdBind:
|
||||||
|
return h.handleBind(ctx, conn, "tcp", address, log)
|
||||||
|
case socks.CmdMuxBind:
|
||||||
|
return h.handleMuxBind(ctx, conn, "tcp", address, log)
|
||||||
|
case gosocks5.CmdUdp:
|
||||||
|
return h.handleUDP(ctx, conn, log)
|
||||||
|
case socks.CmdUDPTun:
|
||||||
|
return h.handleUDPTun(ctx, conn, "udp", address, log)
|
||||||
|
default:
|
||||||
|
err = ErrUnknownCmd
|
||||||
|
log.Error(err)
|
||||||
|
resp := gosocks5.NewReply(gosocks5.CmdUnsupported, nil)
|
||||||
|
resp.Write(conn)
|
||||||
|
log.Debug(resp)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
133
handler/socks/v5/mbind.go
Normal file
133
handler/socks/v5/mbind.go
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
package v5
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
netpkg "github.com/go-gost/core/common/net"
|
||||||
|
"github.com/go-gost/core/logger"
|
||||||
|
"github.com/go-gost/gosocks5"
|
||||||
|
"github.com/go-gost/x/internal/util/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *socks5Handler) handleMuxBind(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) error {
|
||||||
|
log = log.WithFields(map[string]any{
|
||||||
|
"dst": fmt.Sprintf("%s/%s", address, network),
|
||||||
|
"cmd": "mbind",
|
||||||
|
})
|
||||||
|
|
||||||
|
log.Infof("%s >> %s", conn.RemoteAddr(), address)
|
||||||
|
|
||||||
|
if !h.md.enableBind {
|
||||||
|
reply := gosocks5.NewReply(gosocks5.NotAllowed, nil)
|
||||||
|
log.Debug(reply)
|
||||||
|
log.Error("socks5: BIND is disabled")
|
||||||
|
return reply.Write(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
return h.muxBindLocal(ctx, conn, network, address, log)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *socks5Handler) muxBindLocal(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) error {
|
||||||
|
ln, err := net.Listen(network, address) // strict mode: if the port already in use, it will return error
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
reply := gosocks5.NewReply(gosocks5.Failure, nil)
|
||||||
|
if err := reply.Write(conn); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
log.Debug(reply)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
socksAddr := gosocks5.Addr{}
|
||||||
|
err = socksAddr.ParseFrom(ln.Addr().String())
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issue: may not reachable when host has multi-interface
|
||||||
|
socksAddr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String())
|
||||||
|
socksAddr.Type = 0
|
||||||
|
reply := gosocks5.NewReply(gosocks5.Succeeded, &socksAddr)
|
||||||
|
if err := reply.Write(conn); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
ln.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Debug(reply)
|
||||||
|
|
||||||
|
log = log.WithFields(map[string]any{
|
||||||
|
"bind": fmt.Sprintf("%s/%s", ln.Addr(), ln.Addr().Network()),
|
||||||
|
})
|
||||||
|
|
||||||
|
log.Debugf("bind on %s OK", ln.Addr())
|
||||||
|
|
||||||
|
return h.serveMuxBind(ctx, conn, ln, log)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *socks5Handler) serveMuxBind(ctx context.Context, conn net.Conn, ln net.Listener, log logger.Logger) error {
|
||||||
|
// Upgrade connection to multiplex stream.
|
||||||
|
session, err := mux.ClientSession(conn)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer session.Close()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer ln.Close()
|
||||||
|
for {
|
||||||
|
conn, err := session.Accept()
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
conn.Close() // we do not handle incoming connections.
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
rc, err := ln.Accept()
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Debugf("peer %s accepted", rc.RemoteAddr())
|
||||||
|
|
||||||
|
go func(c net.Conn) {
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
log = log.WithFields(map[string]any{
|
||||||
|
"local": rc.LocalAddr().String(),
|
||||||
|
"remote": rc.RemoteAddr().String(),
|
||||||
|
})
|
||||||
|
sc, err := session.GetConn()
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer sc.Close()
|
||||||
|
|
||||||
|
// incompatible with GOST v2.x
|
||||||
|
if !h.md.compatibilityMode {
|
||||||
|
addr := gosocks5.Addr{}
|
||||||
|
addr.ParseFrom(c.RemoteAddr().String())
|
||||||
|
reply := gosocks5.NewReply(gosocks5.Succeeded, &addr)
|
||||||
|
if err := reply.Write(sc); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Debug(reply)
|
||||||
|
}
|
||||||
|
|
||||||
|
t := time.Now()
|
||||||
|
log.Infof("%s <-> %s", c.LocalAddr(), c.RemoteAddr())
|
||||||
|
netpkg.Transport(sc, c)
|
||||||
|
log.WithFields(map[string]any{"duration": time.Since(t)}).
|
||||||
|
Infof("%s >-< %s", c.LocalAddr(), c.RemoteAddr())
|
||||||
|
}(rc)
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user