remove pkgs to github.com/go-gost/x
This commit is contained in:
parent
6340d5198f
commit
04f6ed4708
@ -1,89 +1,5 @@
|
||||
package admission
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-gost/core/common/matcher"
|
||||
"github.com/go-gost/core/logger"
|
||||
)
|
||||
|
||||
type Admission interface {
|
||||
Admit(addr string) bool
|
||||
}
|
||||
|
||||
type options struct {
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
type Option func(opts *options)
|
||||
|
||||
func LoggerOption(logger logger.Logger) Option {
|
||||
return func(opts *options) {
|
||||
opts.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
type admission struct {
|
||||
matchers []matcher.Matcher
|
||||
reversed bool
|
||||
options options
|
||||
}
|
||||
|
||||
// NewAdmission creates and initializes a new Admission using matchers as its match rules.
|
||||
// The rules will be reversed if the reversed is true.
|
||||
func NewAdmission(reversed bool, matchers []matcher.Matcher, opts ...Option) Admission {
|
||||
options := options{}
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
return &admission{
|
||||
matchers: matchers,
|
||||
reversed: reversed,
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
// NewAdmissionPatterns creates and initializes a new Admission using matcher patterns as its match rules.
|
||||
// The rules will be reversed if the reverse is true.
|
||||
func NewAdmissionPatterns(reversed bool, patterns []string, opts ...Option) Admission {
|
||||
var matchers []matcher.Matcher
|
||||
for _, pattern := range patterns {
|
||||
if m := matcher.NewMatcher(pattern); m != nil {
|
||||
matchers = append(matchers, m)
|
||||
}
|
||||
}
|
||||
return NewAdmission(reversed, matchers, opts...)
|
||||
}
|
||||
|
||||
func (p *admission) Admit(addr string) bool {
|
||||
if addr == "" || p == nil || len(p.matchers) == 0 {
|
||||
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
|
||||
}
|
||||
|
22
auth/auth.go
22
auth/auth.go
@ -4,25 +4,3 @@ package auth
|
||||
type Authenticator interface {
|
||||
Authenticate(user, password string) bool
|
||||
}
|
||||
|
||||
// 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) 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)
|
||||
}
|
||||
|
@ -1,90 +1,7 @@
|
||||
package bypass
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-gost/core/common/matcher"
|
||||
"github.com/go-gost/core/logger"
|
||||
)
|
||||
|
||||
// Bypass is a filter of address (IP or domain).
|
||||
type Bypass interface {
|
||||
// Contains reports whether the bypass includes addr.
|
||||
Contains(addr string) bool
|
||||
}
|
||||
|
||||
type options struct {
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
type Option func(opts *options)
|
||||
|
||||
func LoggerOption(logger logger.Logger) Option {
|
||||
return func(opts *options) {
|
||||
opts.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
type 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 {
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
@ -1,99 +0,0 @@
|
||||
package matcher
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/gobwas/glob"
|
||||
)
|
||||
|
||||
// Matcher is a generic pattern matcher,
|
||||
// it gives the match result of the given pattern for specific v.
|
||||
type Matcher interface {
|
||||
Match(v string) bool
|
||||
}
|
||||
|
||||
// NewMatcher creates a Matcher for the given pattern.
|
||||
// The acutal Matcher depends on the pattern:
|
||||
// IP Matcher if pattern is a valid IP address.
|
||||
// CIDR Matcher if pattern is a valid CIDR address.
|
||||
// Domain Matcher if both of the above are not.
|
||||
func NewMatcher(pattern string) Matcher {
|
||||
if pattern == "" {
|
||||
return nil
|
||||
}
|
||||
if ip := net.ParseIP(pattern); ip != nil {
|
||||
return IPMatcher(ip)
|
||||
}
|
||||
if _, inet, err := net.ParseCIDR(pattern); err == nil {
|
||||
return CIDRMatcher(inet)
|
||||
}
|
||||
return DomainMatcher(pattern)
|
||||
}
|
||||
|
||||
type ipMatcher struct {
|
||||
ip net.IP
|
||||
}
|
||||
|
||||
// IPMatcher creates a Matcher for a specific IP address.
|
||||
func IPMatcher(ip net.IP) Matcher {
|
||||
return &ipMatcher{
|
||||
ip: ip,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *ipMatcher) Match(ip string) bool {
|
||||
if m == nil {
|
||||
return false
|
||||
}
|
||||
return m.ip.Equal(net.ParseIP(ip))
|
||||
}
|
||||
|
||||
type cidrMatcher struct {
|
||||
ipNet *net.IPNet
|
||||
}
|
||||
|
||||
// CIDRMatcher creates a Matcher for a specific CIDR notation IP address.
|
||||
func CIDRMatcher(inet *net.IPNet) Matcher {
|
||||
return &cidrMatcher{
|
||||
ipNet: inet,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *cidrMatcher) Match(ip string) bool {
|
||||
if m == nil || m.ipNet == nil {
|
||||
return false
|
||||
}
|
||||
return m.ipNet.Contains(net.ParseIP(ip))
|
||||
}
|
||||
|
||||
type domainMatcher struct {
|
||||
pattern string
|
||||
glob glob.Glob
|
||||
}
|
||||
|
||||
// DomainMatcher creates a Matcher for a specific domain pattern,
|
||||
// the pattern can be a plain domain such as 'example.com',
|
||||
// a wildcard such as '*.exmaple.com' or a special wildcard '.example.com'.
|
||||
func DomainMatcher(pattern string) Matcher {
|
||||
p := pattern
|
||||
if strings.HasPrefix(pattern, ".") {
|
||||
p = pattern[1:] // trim the prefix '.'
|
||||
pattern = "*" + p
|
||||
}
|
||||
return &domainMatcher{
|
||||
pattern: p,
|
||||
glob: glob.MustCompile(pattern),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *domainMatcher) Match(domain string) bool {
|
||||
if m == nil || m.glob == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if domain == m.pattern {
|
||||
return true
|
||||
}
|
||||
return m.glob.Match(domain)
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
package resolver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type CacheKey string
|
||||
|
||||
// NewCacheKey generates resolver cache key from question of dns query.
|
||||
func NewCacheKey(q *dns.Question) CacheKey {
|
||||
if q == nil {
|
||||
return ""
|
||||
}
|
||||
key := fmt.Sprintf("%s%s.%s", q.Name, dns.Class(q.Qclass).String(), dns.Type(q.Qtype).String())
|
||||
return CacheKey(key)
|
||||
}
|
||||
|
||||
type cacheItem struct {
|
||||
msg *dns.Msg
|
||||
ts time.Time
|
||||
ttl time.Duration
|
||||
}
|
||||
|
||||
type Cache struct {
|
||||
m sync.Map
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
func NewCache() *Cache {
|
||||
return &Cache{}
|
||||
}
|
||||
|
||||
func (c *Cache) WithLogger(logger logger.Logger) *Cache {
|
||||
c.logger = logger
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Cache) Load(key CacheKey) *dns.Msg {
|
||||
v, ok := c.m.Load(key)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
item, ok := v.(*cacheItem)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
if time.Since(item.ts) > item.ttl {
|
||||
c.m.Delete(key)
|
||||
return nil
|
||||
}
|
||||
|
||||
c.logger.Debugf("hit resolver cache: %s", key)
|
||||
|
||||
return item.msg.Copy()
|
||||
}
|
||||
|
||||
func (c *Cache) Store(key CacheKey, mr *dns.Msg, ttl time.Duration) {
|
||||
if key == "" || mr == nil || ttl < 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if ttl == 0 {
|
||||
for _, answer := range mr.Answer {
|
||||
v := time.Duration(answer.Header().Ttl) * time.Second
|
||||
if ttl == 0 || ttl > v {
|
||||
ttl = v
|
||||
}
|
||||
}
|
||||
}
|
||||
if ttl == 0 {
|
||||
ttl = 30 * time.Second
|
||||
}
|
||||
|
||||
c.m.Store(key, &cacheItem{
|
||||
msg: mr.Copy(),
|
||||
ts: time.Now(),
|
||||
ttl: ttl,
|
||||
})
|
||||
|
||||
c.logger.Debugf("resolver cache store: %s, ttl: %v", key, ttl)
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
package resolver
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func AddSubnetOpt(m *dns.Msg, ip net.IP) {
|
||||
if m == nil || ip == nil {
|
||||
return
|
||||
}
|
||||
|
||||
opt := new(dns.OPT)
|
||||
opt.Hdr.Name = "."
|
||||
opt.Hdr.Rrtype = dns.TypeOPT
|
||||
e := new(dns.EDNS0_SUBNET)
|
||||
e.Code = dns.EDNS0SUBNET
|
||||
if ip := ip.To4(); ip != nil {
|
||||
e.Family = 1
|
||||
e.SourceNetmask = 24
|
||||
e.Address = ip
|
||||
} else {
|
||||
e.Family = 2
|
||||
e.SourceNetmask = 128
|
||||
e.Address = ip.To16()
|
||||
}
|
||||
opt.Option = append(opt.Option, e)
|
||||
m.Extra = append(m.Extra, opt)
|
||||
}
|
@ -1,178 +0,0 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultConfig is a default TLS config for global use.
|
||||
DefaultConfig *tls.Config
|
||||
)
|
||||
|
||||
// LoadServerConfig loads the certificate from cert & key files and optional client CA file.
|
||||
func LoadServerConfig(certFile, keyFile, caFile string) (*tls.Config, error) {
|
||||
if certFile == "" && keyFile == "" {
|
||||
return DefaultConfig.Clone(), nil
|
||||
}
|
||||
|
||||
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg := &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||
|
||||
pool, err := loadCA(caFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if pool != nil {
|
||||
cfg.ClientCAs = pool
|
||||
cfg.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// LoadClientConfig loads the certificate from cert & key files and optional CA file.
|
||||
func LoadClientConfig(certFile, keyFile, caFile string, verify bool, serverName string) (*tls.Config, error) {
|
||||
var cfg *tls.Config
|
||||
|
||||
if certFile == "" && keyFile == "" {
|
||||
cfg = &tls.Config{}
|
||||
} else {
|
||||
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg = &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
}
|
||||
}
|
||||
|
||||
rootCAs, err := loadCA(caFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg.RootCAs = rootCAs
|
||||
cfg.ServerName = serverName
|
||||
cfg.InsecureSkipVerify = !verify
|
||||
|
||||
// If the root ca is given, but skip verify, we verify the certificate manually.
|
||||
if cfg.RootCAs != nil && !verify {
|
||||
cfg.VerifyConnection = func(state tls.ConnectionState) error {
|
||||
opts := x509.VerifyOptions{
|
||||
Roots: cfg.RootCAs,
|
||||
CurrentTime: time.Now(),
|
||||
DNSName: "",
|
||||
Intermediates: x509.NewCertPool(),
|
||||
}
|
||||
|
||||
certs := state.PeerCertificates
|
||||
for i, cert := range certs {
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
opts.Intermediates.AddCert(cert)
|
||||
}
|
||||
|
||||
_, err := certs[0].Verify(opts)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func loadCA(caFile string) (cp *x509.CertPool, err error) {
|
||||
if caFile == "" {
|
||||
return
|
||||
}
|
||||
cp = x509.NewCertPool()
|
||||
data, err := ioutil.ReadFile(caFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !cp.AppendCertsFromPEM(data) {
|
||||
return nil, errors.New("AppendCertsFromPEM failed")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Wrap a net.Conn into a client tls connection, performing any
|
||||
// additional verification as needed.
|
||||
//
|
||||
// As of go 1.3, crypto/tls only supports either doing no certificate
|
||||
// verification, or doing full verification including of the peer's
|
||||
// DNS name. For consul, we want to validate that the certificate is
|
||||
// signed by a known CA, but because consul doesn't use DNS names for
|
||||
// node names, we don't verify the certificate DNS names. Since go 1.3
|
||||
// no longer supports this mode of operation, we have to do it
|
||||
// manually.
|
||||
//
|
||||
// This code is taken from consul:
|
||||
// https://github.com/hashicorp/consul/blob/master/tlsutil/config.go
|
||||
func WrapTLSClient(conn net.Conn, tlsConfig *tls.Config, timeout time.Duration) (net.Conn, error) {
|
||||
var err error
|
||||
var tlsConn *tls.Conn
|
||||
|
||||
if timeout > 0 {
|
||||
conn.SetDeadline(time.Now().Add(timeout))
|
||||
defer conn.SetDeadline(time.Time{})
|
||||
}
|
||||
|
||||
tlsConn = tls.Client(conn, tlsConfig)
|
||||
|
||||
// Otherwise perform handshake, but don't verify the domain
|
||||
//
|
||||
// The following is lightly-modified from the doFullHandshake
|
||||
// method in https://golang.org/src/crypto/tls/handshake_client.go
|
||||
if err = tlsConn.Handshake(); err != nil {
|
||||
tlsConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We can do this in `tls.Config.VerifyConnection`, which effective for
|
||||
// other TLS protocols such as WebSocket. See `route.go:parseChainNode`
|
||||
/*
|
||||
// If crypto/tls is doing verification, there's no need to do our own.
|
||||
if tlsConfig.InsecureSkipVerify == false {
|
||||
return tlsConn, nil
|
||||
}
|
||||
|
||||
// Similarly if we use host's CA, we can do full handshake
|
||||
if tlsConfig.RootCAs == nil {
|
||||
return tlsConn, nil
|
||||
}
|
||||
|
||||
opts := x509.VerifyOptions{
|
||||
Roots: tlsConfig.RootCAs,
|
||||
CurrentTime: time.Now(),
|
||||
DNSName: "",
|
||||
Intermediates: x509.NewCertPool(),
|
||||
}
|
||||
|
||||
certs := tlsConn.ConnectionState().PeerCertificates
|
||||
for i, cert := range certs {
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
opts.Intermediates.AddCert(cert)
|
||||
}
|
||||
|
||||
_, err = certs[0].Verify(opts)
|
||||
if err != nil {
|
||||
tlsConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
*/
|
||||
|
||||
return tlsConn, err
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
package forward
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/go-gost/core/connector"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/core/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
|
||||
}
|
@ -1,129 +0,0 @@
|
||||
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/internal/util/socks"
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/core/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
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/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 = mdata.GetDuration(md, connectTimeout)
|
||||
|
||||
if mm := mdata.GetStringMapString(md, header); len(mm) > 0 {
|
||||
hd := http.Header{}
|
||||
for k, v := range mm {
|
||||
hd.Add(k, v)
|
||||
}
|
||||
c.md.header = hd
|
||||
}
|
||||
|
||||
return
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
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/core/registry"
|
||||
"github.com/go-gost/gosocks4"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
package v4
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/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 = mdata.GetDuration(md, connectTimeout)
|
||||
c.md.disable4a = mdata.GetBool(md, disable4a)
|
||||
|
||||
return
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
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/internal/util/mux"
|
||||
"github.com/go-gost/core/internal/util/socks"
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/gosocks5"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,173 +0,0 @@
|
||||
package v5
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/go-gost/core/connector"
|
||||
"github.com/go-gost/core/internal/util/socks"
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/core/registry"
|
||||
"github.com/go-gost/gosocks5"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
package v5
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/go-gost/core/internal/util/mux"
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/gosocks5"
|
||||
)
|
||||
|
||||
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()
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
package v5
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/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 = mdata.GetDuration(md, connectTimeout)
|
||||
c.md.noTLS = mdata.GetBool(md, noTLS)
|
||||
|
||||
return
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
package v5
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/url"
|
||||
|
||||
"github.com/go-gost/core/internal/util/socks"
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/gosocks5"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
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/core/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
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
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/core/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
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
handshakeTimeout time.Duration
|
||||
}
|
||||
|
||||
func (d *tlsDialer) parseMetadata(md mdata.Metadata) (err error) {
|
||||
const (
|
||||
handshakeTimeout = "handshakeTimeout"
|
||||
)
|
||||
|
||||
d.md.handshakeTimeout = mdata.GetDuration(md, handshakeTimeout)
|
||||
|
||||
return
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
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/core/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
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
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
|
||||
}
|
15
go.mod
15
go.mod
@ -3,15 +3,8 @@ module github.com/go-gost/core
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
|
||||
github.com/go-gost/gosocks4 v0.0.1
|
||||
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/miekg/dns v1.1.47
|
||||
github.com/prometheus/client_golang v1.12.1
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/xtaci/smux v1.5.16
|
||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
|
||||
)
|
||||
|
||||
require (
|
||||
@ -23,11 +16,5 @@ require (
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.32.1 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/stretchr/testify v1.7.0 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
|
||||
golang.org/x/tools v0.1.10 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
)
|
||||
|
41
go.sum
41
go.sum
@ -38,8 +38,6 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ=
|
||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
@ -54,7 +52,6 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
@ -63,10 +60,6 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gost/gosocks4 v0.0.1 h1:+k1sec8HlELuQV7rWftIkmy8UijzUt2I6t+iMPlGB2s=
|
||||
github.com/go-gost/gosocks4 v0.0.1/go.mod h1:3B6L47HbU/qugDg4JnoFPHgJXE43Inz8Bah1QaN9qCc=
|
||||
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-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
@ -74,8 +67,6 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@ -152,8 +143,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.1.47 h1:J9bWiXbqMbnZPcY8Qi2E3EWIBsIm6MZzzJB9VRg5gL8=
|
||||
github.com/miekg/dns v1.1.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
@ -164,7 +153,6 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
@ -192,21 +180,14 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/xtaci/smux v1.5.16 h1:FBPYOkW8ZTjLKUM4LI4xnnuuDC8CQ/dB04HD519WoEk=
|
||||
github.com/xtaci/smux v1.5.16/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
@ -248,9 +229,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@ -279,11 +257,7 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@ -299,8 +273,6 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -314,7 +286,6 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -335,14 +306,10 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 h1:A9i04dxx7Cribqbs8jf3FQLogkL/CV2YN7hj9KWJCkc=
|
||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@ -393,9 +360,6 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
|
||||
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@ -489,9 +453,6 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
@ -1,115 +0,0 @@
|
||||
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/core/registry"
|
||||
"github.com/go-gost/gosocks4"
|
||||
"github.com/go-gost/gosocks5"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
@ -1,117 +0,0 @@
|
||||
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/core/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
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package local
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
readTimeout time.Duration
|
||||
}
|
||||
|
||||
func (h *forwardHandler) parseMetadata(md mdata.Metadata) (err error) {
|
||||
const (
|
||||
readTimeout = "readTimeout"
|
||||
)
|
||||
|
||||
h.md.readTimeout = mdata.GetDuration(md, readTimeout)
|
||||
return
|
||||
}
|
@ -1,111 +0,0 @@
|
||||
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/core/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
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package remote
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
readTimeout time.Duration
|
||||
}
|
||||
|
||||
func (h *forwardHandler) parseMetadata(md mdata.Metadata) (err error) {
|
||||
const (
|
||||
readTimeout = "readTimeout"
|
||||
)
|
||||
|
||||
h.md.readTimeout = mdata.GetDuration(md, readTimeout)
|
||||
return
|
||||
}
|
@ -1,337 +0,0 @@
|
||||
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/core/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
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
mdata "github.com/go-gost/core/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 := mdata.GetStringMapString(md, header); len(m) > 0 {
|
||||
hd := http.Header{}
|
||||
for k, v := range m {
|
||||
hd.Add(k, v)
|
||||
}
|
||||
h.md.header = hd
|
||||
}
|
||||
|
||||
if v := mdata.GetString(md, probeResistKey); v != "" {
|
||||
if ss := strings.SplitN(v, ":", 2); len(ss) == 2 {
|
||||
h.md.probeResistance = &probeResistance{
|
||||
Type: ss[0],
|
||||
Value: ss[1],
|
||||
Knock: mdata.GetString(md, knock),
|
||||
}
|
||||
}
|
||||
}
|
||||
h.md.sni = mdata.GetBool(md, sni)
|
||||
h.md.enableUDP = mdata.GetBool(md, enableUDP)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type probeResistance struct {
|
||||
Type string
|
||||
Value string
|
||||
Knock string
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
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/internal/util/socks"
|
||||
"github.com/go-gost/core/logger"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
@ -1,152 +0,0 @@
|
||||
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/core/registry"
|
||||
"github.com/go-gost/gosocks4"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package v4
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
readTimeout time.Duration
|
||||
}
|
||||
|
||||
func (h *socks4Handler) parseMetadata(md mdata.Metadata) (err error) {
|
||||
const (
|
||||
readTimeout = "readTimeout"
|
||||
)
|
||||
|
||||
h.md.readTimeout = mdata.GetDuration(md, readTimeout)
|
||||
return
|
||||
}
|
@ -1,149 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
package v5
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/go-gost/core/chain"
|
||||
"github.com/go-gost/core/handler"
|
||||
"github.com/go-gost/core/internal/util/socks"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/core/registry"
|
||||
"github.com/go-gost/gosocks5"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
package v5
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
netpkg "github.com/go-gost/core/common/net"
|
||||
"github.com/go-gost/core/internal/util/mux"
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/gosocks5"
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package v5
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
readTimeout time.Duration
|
||||
noTLS bool
|
||||
enableBind bool
|
||||
enableUDP bool
|
||||
udpBufferSize int
|
||||
compatibilityMode bool
|
||||
}
|
||||
|
||||
func (h *socks5Handler) parseMetadata(md mdata.Metadata) (err error) {
|
||||
const (
|
||||
readTimeout = "readTimeout"
|
||||
noTLS = "notls"
|
||||
enableBind = "bind"
|
||||
enableUDP = "udp"
|
||||
udpBufferSize = "udpBufferSize"
|
||||
compatibilityMode = "comp"
|
||||
)
|
||||
|
||||
h.md.readTimeout = mdata.GetDuration(md, readTimeout)
|
||||
h.md.noTLS = mdata.GetBool(md, noTLS)
|
||||
h.md.enableBind = mdata.GetBool(md, enableBind)
|
||||
h.md.enableUDP = mdata.GetBool(md, enableUDP)
|
||||
|
||||
if bs := mdata.GetInt(md, udpBufferSize); bs > 0 {
|
||||
h.md.udpBufferSize = int(math.Min(math.Max(float64(bs), 512), 64*1024))
|
||||
} else {
|
||||
h.md.udpBufferSize = 1500
|
||||
}
|
||||
|
||||
h.md.compatibilityMode = mdata.GetBool(md, compatibilityMode)
|
||||
|
||||
return nil
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
package v5
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
|
||||
"github.com/go-gost/core/auth"
|
||||
"github.com/go-gost/core/internal/util/socks"
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/gosocks5"
|
||||
)
|
||||
|
||||
type serverSelector struct {
|
||||
methods []uint8
|
||||
Authenticator auth.Authenticator
|
||||
TLSConfig *tls.Config
|
||||
logger logger.Logger
|
||||
noTLS bool
|
||||
}
|
||||
|
||||
func (selector *serverSelector) Methods() []uint8 {
|
||||
return selector.methods
|
||||
}
|
||||
|
||||
func (s *serverSelector) Select(methods ...uint8) (method uint8) {
|
||||
s.logger.Debugf("%d %d %v", gosocks5.Ver5, len(methods), methods)
|
||||
method = gosocks5.MethodNoAuth
|
||||
for _, m := range methods {
|
||||
if m == socks.MethodTLS && !s.noTLS {
|
||||
method = m
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// when Authenticator is set, auth is mandatory
|
||||
if s.Authenticator != nil {
|
||||
if method == gosocks5.MethodNoAuth {
|
||||
method = gosocks5.MethodUserPass
|
||||
}
|
||||
if method == socks.MethodTLS && !s.noTLS {
|
||||
method = socks.MethodTLSAuth
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) {
|
||||
s.logger.Debugf("%d %d", gosocks5.Ver5, method)
|
||||
switch method {
|
||||
case socks.MethodTLS:
|
||||
conn = tls.Server(conn, s.TLSConfig)
|
||||
|
||||
case gosocks5.MethodUserPass, socks.MethodTLSAuth:
|
||||
if method == socks.MethodTLSAuth {
|
||||
conn = tls.Server(conn, s.TLSConfig)
|
||||
}
|
||||
|
||||
req, err := gosocks5.ReadUserPassRequest(conn)
|
||||
if err != nil {
|
||||
s.logger.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
s.logger.Debug(req)
|
||||
|
||||
if s.Authenticator != nil &&
|
||||
!s.Authenticator.Authenticate(req.Username, req.Password) {
|
||||
resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Failure)
|
||||
if err := resp.Write(conn); err != nil {
|
||||
s.logger.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
s.logger.Info(resp)
|
||||
|
||||
return nil, gosocks5.ErrAuthFailure
|
||||
}
|
||||
|
||||
resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Succeeded)
|
||||
if err := resp.Write(conn); err != nil {
|
||||
s.logger.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
s.logger.Debug(resp)
|
||||
|
||||
case gosocks5.MethodNoAcceptable:
|
||||
return nil, gosocks5.ErrBadMethod
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
package v5
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/go-gost/core/common/net/relay"
|
||||
"github.com/go-gost/core/internal/util/socks"
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/gosocks5"
|
||||
)
|
||||
|
||||
func (h *socks5Handler) handleUDP(ctx context.Context, conn net.Conn, log logger.Logger) error {
|
||||
log = log.WithFields(map[string]any{
|
||||
"cmd": "udp",
|
||||
})
|
||||
|
||||
if !h.md.enableUDP {
|
||||
reply := gosocks5.NewReply(gosocks5.NotAllowed, nil)
|
||||
log.Debug(reply)
|
||||
log.Error("socks5: UDP relay is disabled")
|
||||
return reply.Write(conn)
|
||||
}
|
||||
|
||||
cc, err := net.ListenUDP("udp", nil)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
reply := gosocks5.NewReply(gosocks5.Failure, nil)
|
||||
reply.Write(conn)
|
||||
log.Debug(reply)
|
||||
return err
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
saddr := gosocks5.Addr{}
|
||||
saddr.ParseFrom(cc.LocalAddr().String())
|
||||
saddr.Type = 0
|
||||
saddr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) // replace the IP to the out-going interface's
|
||||
reply := gosocks5.NewReply(gosocks5.Succeeded, &saddr)
|
||||
if err := reply.Write(conn); err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
log.Debug(reply)
|
||||
|
||||
log = log.WithFields(map[string]any{
|
||||
"bind": fmt.Sprintf("%s/%s", cc.LocalAddr(), cc.LocalAddr().Network()),
|
||||
})
|
||||
log.Debugf("bind on %s OK", cc.LocalAddr())
|
||||
|
||||
// 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("socks5: wrong connection type")
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
r := relay.NewUDPRelay(socks.UDPConn(cc, h.md.udpBufferSize), pc).
|
||||
WithBypass(h.options.Bypass).
|
||||
WithLogger(log)
|
||||
r.SetBufferSize(h.md.udpBufferSize)
|
||||
|
||||
go r.Run()
|
||||
|
||||
t := time.Now()
|
||||
log.Infof("%s <-> %s", conn.RemoteAddr(), cc.LocalAddr())
|
||||
io.Copy(ioutil.Discard, conn)
|
||||
log.WithFields(map[string]any{"duration": time.Since(t)}).
|
||||
Infof("%s >-< %s", conn.RemoteAddr(), cc.LocalAddr())
|
||||
|
||||
return nil
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
package v5
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/go-gost/core/common/net/relay"
|
||||
"github.com/go-gost/core/internal/util/socks"
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/gosocks5"
|
||||
)
|
||||
|
||||
func (h *socks5Handler) handleUDPTun(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) error {
|
||||
log = log.WithFields(map[string]any{
|
||||
"cmd": "udp-tun",
|
||||
})
|
||||
|
||||
bindAddr, _ := net.ResolveUDPAddr(network, address)
|
||||
if bindAddr == nil {
|
||||
bindAddr = &net.UDPAddr{}
|
||||
}
|
||||
|
||||
if bindAddr.Port == 0 {
|
||||
// relay mode
|
||||
if !h.md.enableUDP {
|
||||
reply := gosocks5.NewReply(gosocks5.NotAllowed, nil)
|
||||
log.Debug(reply)
|
||||
log.Error("socks5: UDP relay is disabled")
|
||||
return reply.Write(conn)
|
||||
}
|
||||
} else {
|
||||
// BIND mode
|
||||
if !h.md.enableBind {
|
||||
reply := gosocks5.NewReply(gosocks5.NotAllowed, nil)
|
||||
log.Debug(reply)
|
||||
log.Error("socks5: BIND is disabled")
|
||||
return reply.Write(conn)
|
||||
}
|
||||
}
|
||||
|
||||
pc, err := net.ListenUDP(network, bindAddr)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
defer pc.Close()
|
||||
|
||||
saddr := gosocks5.Addr{}
|
||||
saddr.ParseFrom(pc.LocalAddr().String())
|
||||
reply := gosocks5.NewReply(gosocks5.Succeeded, &saddr)
|
||||
if err := reply.Write(conn); err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
log.Debug(reply)
|
||||
log.Debugf("bind on %s OK", pc.LocalAddr())
|
||||
|
||||
r := relay.NewUDPRelay(socks.UDPTunServerConn(conn), pc).
|
||||
WithBypass(h.options.Bypass).
|
||||
WithLogger(log)
|
||||
r.SetBufferSize(h.md.udpBufferSize)
|
||||
|
||||
t := time.Now()
|
||||
log.Infof("%s <-> %s", conn.RemoteAddr(), pc.LocalAddr())
|
||||
r.Run()
|
||||
log.WithFields(map[string]any{
|
||||
"duration": time.Since(t),
|
||||
}).Infof("%s >-< %s", conn.RemoteAddr(), pc.LocalAddr())
|
||||
|
||||
return nil
|
||||
}
|
122
hosts/hosts.go
122
hosts/hosts.go
@ -2,131 +2,9 @@ package hosts
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/go-gost/core/logger"
|
||||
)
|
||||
|
||||
// HostMapper is a mapping from hostname to IP.
|
||||
type HostMapper interface {
|
||||
Lookup(network, host string) ([]net.IP, bool)
|
||||
}
|
||||
|
||||
type hostMapping struct {
|
||||
IPs []net.IP
|
||||
Hostname string
|
||||
}
|
||||
|
||||
// Hosts is a static table lookup for hostnames.
|
||||
// For each host a single line should be present with the following information:
|
||||
// IP_address canonical_hostname [aliases...]
|
||||
// Fields of the entry are separated by any number of blanks and/or tab characters.
|
||||
// Text from a "#" character until the end of the line is a comment, and is ignored.
|
||||
type Hosts struct {
|
||||
mappings sync.Map
|
||||
Logger logger.Logger
|
||||
}
|
||||
|
||||
func NewHosts() *Hosts {
|
||||
return &Hosts{}
|
||||
}
|
||||
|
||||
// Map maps ip to hostname or aliases.
|
||||
func (h *Hosts) Map(ip net.IP, hostname string, aliases ...string) {
|
||||
if hostname == "" {
|
||||
return
|
||||
}
|
||||
|
||||
v, _ := h.mappings.Load(hostname)
|
||||
m, _ := v.(*hostMapping)
|
||||
if m == nil {
|
||||
m = &hostMapping{
|
||||
IPs: []net.IP{ip},
|
||||
Hostname: hostname,
|
||||
}
|
||||
} else {
|
||||
m.IPs = append(m.IPs, ip)
|
||||
}
|
||||
h.mappings.Store(hostname, m)
|
||||
|
||||
for _, alias := range aliases {
|
||||
// indirect mapping from alias to hostname
|
||||
if alias != "" {
|
||||
h.mappings.Store(alias, &hostMapping{
|
||||
Hostname: hostname,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Lookup searches the IP address corresponds to the given network and host from the host table.
|
||||
// The network should be 'ip', 'ip4' or 'ip6', default network is 'ip'.
|
||||
// the host should be a hostname (example.org) or a hostname with dot prefix (.example.org).
|
||||
func (h *Hosts) Lookup(network, host string) (ips []net.IP, ok bool) {
|
||||
m := h.lookup(host)
|
||||
if m == nil {
|
||||
m = h.lookup("." + host)
|
||||
}
|
||||
if m == nil {
|
||||
s := host
|
||||
for {
|
||||
if index := strings.IndexByte(s, '.'); index > 0 {
|
||||
m = h.lookup(s[index:])
|
||||
s = s[index+1:]
|
||||
if m == nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// hostname alias
|
||||
if !strings.HasPrefix(m.Hostname, ".") && host != m.Hostname {
|
||||
m = h.lookup(m.Hostname)
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
switch network {
|
||||
case "ip4":
|
||||
for _, ip := range m.IPs {
|
||||
if ip = ip.To4(); ip != nil {
|
||||
ips = append(ips, ip)
|
||||
}
|
||||
}
|
||||
case "ip6":
|
||||
for _, ip := range m.IPs {
|
||||
if ip.To4() == nil {
|
||||
ips = append(ips, ip)
|
||||
}
|
||||
}
|
||||
default:
|
||||
ips = m.IPs
|
||||
}
|
||||
|
||||
if len(ips) > 0 {
|
||||
h.Logger.Debugf("host mapper: %s -> %s", host, ips)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (h *Hosts) lookup(host string) *hostMapping {
|
||||
if h == nil || host == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
v, ok := h.mappings.Load(host)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
m, _ := v.(*hostMapping)
|
||||
return m
|
||||
}
|
||||
|
@ -1,85 +0,0 @@
|
||||
package mux
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
smux "github.com/xtaci/smux"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
conn net.Conn
|
||||
session *smux.Session
|
||||
}
|
||||
|
||||
func ClientSession(conn net.Conn) (*Session, error) {
|
||||
s, err := smux.Client(conn, smux.DefaultConfig())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Session{
|
||||
conn: conn,
|
||||
session: s,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ServerSession(conn net.Conn) (*Session, error) {
|
||||
s, err := smux.Server(conn, smux.DefaultConfig())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Session{
|
||||
conn: conn,
|
||||
session: s,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (session *Session) GetConn() (net.Conn, error) {
|
||||
stream, err := session.session.OpenStream()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &streamConn{Conn: session.conn, stream: stream}, nil
|
||||
}
|
||||
|
||||
func (session *Session) Accept() (net.Conn, error) {
|
||||
stream, err := session.session.AcceptStream()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &streamConn{Conn: session.conn, stream: stream}, nil
|
||||
}
|
||||
|
||||
func (session *Session) Close() error {
|
||||
if session.session == nil {
|
||||
return nil
|
||||
}
|
||||
return session.session.Close()
|
||||
}
|
||||
|
||||
func (session *Session) IsClosed() bool {
|
||||
if session.session == nil {
|
||||
return true
|
||||
}
|
||||
return session.session.IsClosed()
|
||||
}
|
||||
|
||||
func (session *Session) NumStreams() int {
|
||||
return session.session.NumStreams()
|
||||
}
|
||||
|
||||
type streamConn struct {
|
||||
net.Conn
|
||||
stream *smux.Stream
|
||||
}
|
||||
|
||||
func (c *streamConn) Read(b []byte) (n int, err error) {
|
||||
return c.stream.Read(b)
|
||||
}
|
||||
|
||||
func (c *streamConn) Write(b []byte) (n int, err error) {
|
||||
return c.stream.Write(b)
|
||||
}
|
||||
|
||||
func (c *streamConn) Close() error {
|
||||
return c.stream.Close()
|
||||
}
|
@ -1,172 +0,0 @@
|
||||
package socks
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
|
||||
"github.com/go-gost/core/common/bufpool"
|
||||
"github.com/go-gost/gosocks5"
|
||||
)
|
||||
|
||||
type udpTunConn struct {
|
||||
net.Conn
|
||||
taddr net.Addr
|
||||
}
|
||||
|
||||
func UDPTunClientConn(c net.Conn, targetAddr net.Addr) net.Conn {
|
||||
return &udpTunConn{
|
||||
Conn: c,
|
||||
taddr: targetAddr,
|
||||
}
|
||||
}
|
||||
|
||||
func UDPTunClientPacketConn(c net.Conn) net.PacketConn {
|
||||
return &udpTunConn{
|
||||
Conn: c,
|
||||
}
|
||||
}
|
||||
|
||||
func UDPTunServerConn(c net.Conn) net.PacketConn {
|
||||
return &udpTunConn{
|
||||
Conn: c,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *udpTunConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
|
||||
socksAddr := gosocks5.Addr{}
|
||||
header := gosocks5.UDPHeader{
|
||||
Addr: &socksAddr,
|
||||
}
|
||||
dgram := gosocks5.UDPDatagram{
|
||||
Header: &header,
|
||||
Data: b,
|
||||
}
|
||||
_, err = dgram.ReadFrom(c.Conn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
n = len(dgram.Data)
|
||||
if n > len(b) {
|
||||
n = copy(b, dgram.Data)
|
||||
}
|
||||
addr, err = net.ResolveUDPAddr("udp", socksAddr.String())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *udpTunConn) Read(b []byte) (n int, err error) {
|
||||
n, _, err = c.ReadFrom(b)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *udpTunConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
|
||||
socksAddr := gosocks5.Addr{}
|
||||
if err = socksAddr.ParseFrom(addr.String()); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
header := gosocks5.UDPHeader{
|
||||
Addr: &socksAddr,
|
||||
}
|
||||
dgram := gosocks5.UDPDatagram{
|
||||
Header: &header,
|
||||
Data: b,
|
||||
}
|
||||
dgram.Header.Rsv = uint16(len(dgram.Data))
|
||||
dgram.Header.Frag = 0xff // UDP tun relay flag, used by shadowsocks
|
||||
_, err = dgram.WriteTo(c.Conn)
|
||||
n = len(b)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *udpTunConn) Write(b []byte) (n int, err error) {
|
||||
return c.WriteTo(b, c.taddr)
|
||||
}
|
||||
|
||||
var (
|
||||
DefaultBufferSize = 4096
|
||||
)
|
||||
|
||||
type udpConn struct {
|
||||
net.PacketConn
|
||||
raddr net.Addr
|
||||
taddr net.Addr
|
||||
bufferSize int
|
||||
}
|
||||
|
||||
func UDPConn(c net.PacketConn, bufferSize int) net.PacketConn {
|
||||
return &udpConn{
|
||||
PacketConn: c,
|
||||
bufferSize: bufferSize,
|
||||
}
|
||||
}
|
||||
|
||||
// ReadFrom reads an UDP datagram.
|
||||
// NOTE: for server side,
|
||||
// the returned addr is the target address the client want to relay to.
|
||||
func (c *udpConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
|
||||
rbuf := bufpool.Get(c.bufferSize)
|
||||
defer bufpool.Put(rbuf)
|
||||
|
||||
n, c.raddr, err = c.PacketConn.ReadFrom(*rbuf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
socksAddr := gosocks5.Addr{}
|
||||
header := gosocks5.UDPHeader{
|
||||
Addr: &socksAddr,
|
||||
}
|
||||
hlen, err := header.ReadFrom(bytes.NewReader((*rbuf)[:n]))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n = copy(b, (*rbuf)[hlen:n])
|
||||
|
||||
addr, err = net.ResolveUDPAddr("udp", socksAddr.String())
|
||||
return
|
||||
}
|
||||
|
||||
func (c *udpConn) Read(b []byte) (n int, err error) {
|
||||
n, _, err = c.ReadFrom(b)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *udpConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
|
||||
wbuf := bufpool.Get(c.bufferSize)
|
||||
defer bufpool.Put(wbuf)
|
||||
|
||||
socksAddr := gosocks5.Addr{}
|
||||
if err = socksAddr.ParseFrom(addr.String()); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
header := gosocks5.UDPHeader{
|
||||
Addr: &socksAddr,
|
||||
}
|
||||
dgram := gosocks5.UDPDatagram{
|
||||
Header: &header,
|
||||
Data: b,
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer((*wbuf)[:0])
|
||||
_, err = dgram.WriteTo(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = c.PacketConn.WriteTo(buf.Bytes(), c.raddr)
|
||||
n = len(b)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *udpConn) Write(b []byte) (n int, err error) {
|
||||
return c.WriteTo(b, c.taddr)
|
||||
}
|
||||
|
||||
func (c *udpConn) RemoteAddr() net.Addr {
|
||||
return c.raddr
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package socks
|
||||
|
||||
const (
|
||||
// MethodTLS is an extended SOCKS5 method with tls encryption support.
|
||||
MethodTLS uint8 = 0x80
|
||||
// MethodTLSAuth is an extended SOCKS5 method with tls encryption and authentication support.
|
||||
MethodTLSAuth uint8 = 0x82
|
||||
// MethodMux is an extended SOCKS5 method for stream multiplexing.
|
||||
MethodMux = 0x88
|
||||
)
|
||||
|
||||
const (
|
||||
// CmdMuxBind is an extended SOCKS5 request CMD for
|
||||
// multiplexing transport with the binding server.
|
||||
CmdMuxBind uint8 = 0xF2
|
||||
// CmdUDPTun is an extended SOCKS5 request CMD for UDP over TCP.
|
||||
CmdUDPTun uint8 = 0xF3
|
||||
)
|
@ -1,32 +0,0 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultKeepAlivePeriod = 180 * time.Second
|
||||
)
|
||||
|
||||
// TCPKeepAliveListener is a TCP listener with keep alive enabled.
|
||||
type TCPKeepAliveListener struct {
|
||||
KeepAlivePeriod time.Duration
|
||||
*net.TCPListener
|
||||
}
|
||||
|
||||
func (l *TCPKeepAliveListener) Accept() (c net.Conn, err error) {
|
||||
tc, err := l.AcceptTCP()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
tc.SetKeepAlive(true)
|
||||
period := l.KeepAlivePeriod
|
||||
if period <= 0 {
|
||||
period = defaultKeepAlivePeriod
|
||||
}
|
||||
tc.SetKeepAlivePeriod(period)
|
||||
|
||||
return tc, nil
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
package rtcp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/go-gost/core/chain"
|
||||
"github.com/go-gost/core/connector"
|
||||
"github.com/go-gost/core/listener"
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
metrics "github.com/go-gost/core/metrics/wrapper"
|
||||
"github.com/go-gost/core/registry"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.ListenerRegistry().Register("rtcp", NewListener)
|
||||
}
|
||||
|
||||
type rtcpListener struct {
|
||||
laddr net.Addr
|
||||
ln net.Listener
|
||||
md metadata
|
||||
router *chain.Router
|
||||
logger logger.Logger
|
||||
closed chan struct{}
|
||||
options listener.Options
|
||||
}
|
||||
|
||||
func NewListener(opts ...listener.Option) listener.Listener {
|
||||
options := listener.Options{}
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
return &rtcpListener{
|
||||
closed: make(chan struct{}),
|
||||
logger: options.Logger,
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *rtcpListener) Init(md md.Metadata) (err error) {
|
||||
if err = l.parseMetadata(md); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
laddr, err := net.ResolveTCPAddr("tcp", l.options.Addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
l.laddr = laddr
|
||||
l.router = (&chain.Router{}).
|
||||
WithChain(l.options.Chain).
|
||||
WithLogger(l.logger)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (l *rtcpListener) Accept() (conn net.Conn, err error) {
|
||||
select {
|
||||
case <-l.closed:
|
||||
return nil, net.ErrClosed
|
||||
default:
|
||||
}
|
||||
|
||||
if l.ln == nil {
|
||||
l.ln, err = l.router.Bind(
|
||||
context.Background(), "tcp", l.laddr.String(),
|
||||
connector.MuxBindOption(true),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, listener.NewAcceptError(err)
|
||||
}
|
||||
l.ln = metrics.WrapListener(l.options.Service, l.ln)
|
||||
}
|
||||
conn, err = l.ln.Accept()
|
||||
if err != nil {
|
||||
l.ln.Close()
|
||||
l.ln = nil
|
||||
return nil, listener.NewAcceptError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (l *rtcpListener) Addr() net.Addr {
|
||||
return l.laddr
|
||||
}
|
||||
|
||||
func (l *rtcpListener) Close() error {
|
||||
select {
|
||||
case <-l.closed:
|
||||
default:
|
||||
close(l.closed)
|
||||
if l.ln != nil {
|
||||
l.ln.Close()
|
||||
l.ln = nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
package rtcp
|
||||
|
||||
import (
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
)
|
||||
|
||||
type metadata struct{}
|
||||
|
||||
func (l *rtcpListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
return
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
package rudp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/go-gost/core/chain"
|
||||
"github.com/go-gost/core/connector"
|
||||
"github.com/go-gost/core/listener"
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
metrics "github.com/go-gost/core/metrics/wrapper"
|
||||
"github.com/go-gost/core/registry"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.ListenerRegistry().Register("rudp", NewListener)
|
||||
}
|
||||
|
||||
type rudpListener struct {
|
||||
laddr net.Addr
|
||||
ln net.Listener
|
||||
router *chain.Router
|
||||
closed chan struct{}
|
||||
logger logger.Logger
|
||||
md metadata
|
||||
options listener.Options
|
||||
}
|
||||
|
||||
func NewListener(opts ...listener.Option) listener.Listener {
|
||||
options := listener.Options{}
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
return &rudpListener{
|
||||
closed: make(chan struct{}),
|
||||
logger: options.Logger,
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *rudpListener) Init(md md.Metadata) (err error) {
|
||||
if err = l.parseMetadata(md); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
laddr, err := net.ResolveUDPAddr("udp", l.options.Addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
l.laddr = laddr
|
||||
l.router = (&chain.Router{}).
|
||||
WithChain(l.options.Chain).
|
||||
WithLogger(l.logger)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (l *rudpListener) Accept() (conn net.Conn, err error) {
|
||||
select {
|
||||
case <-l.closed:
|
||||
return nil, net.ErrClosed
|
||||
default:
|
||||
}
|
||||
|
||||
if l.ln == nil {
|
||||
l.ln, err = l.router.Bind(
|
||||
context.Background(), "udp", l.laddr.String(),
|
||||
connector.BacklogBindOption(l.md.backlog),
|
||||
connector.UDPConnTTLBindOption(l.md.ttl),
|
||||
connector.UDPDataBufferSizeBindOption(l.md.readBufferSize),
|
||||
connector.UDPDataQueueSizeBindOption(l.md.readQueueSize),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, listener.NewAcceptError(err)
|
||||
}
|
||||
}
|
||||
conn, err = l.ln.Accept()
|
||||
if err != nil {
|
||||
l.ln.Close()
|
||||
l.ln = nil
|
||||
return nil, listener.NewAcceptError(err)
|
||||
}
|
||||
|
||||
if pc, ok := conn.(net.PacketConn); ok {
|
||||
conn = metrics.WrapUDPConn(l.options.Service, pc)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (l *rudpListener) Addr() net.Addr {
|
||||
return l.laddr
|
||||
}
|
||||
|
||||
func (l *rudpListener) Close() error {
|
||||
select {
|
||||
case <-l.closed:
|
||||
default:
|
||||
close(l.closed)
|
||||
if l.ln != nil {
|
||||
l.ln.Close()
|
||||
l.ln = nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
package rudp
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTTL = 5 * time.Second
|
||||
defaultReadBufferSize = 1500
|
||||
defaultReadQueueSize = 128
|
||||
defaultBacklog = 128
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
ttl time.Duration
|
||||
readBufferSize int
|
||||
readQueueSize int
|
||||
backlog int
|
||||
}
|
||||
|
||||
func (l *rudpListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
const (
|
||||
ttl = "ttl"
|
||||
readBufferSize = "readBufferSize"
|
||||
readQueueSize = "readQueueSize"
|
||||
backlog = "backlog"
|
||||
)
|
||||
|
||||
l.md.ttl = mdata.GetDuration(md, ttl)
|
||||
if l.md.ttl <= 0 {
|
||||
l.md.ttl = defaultTTL
|
||||
}
|
||||
l.md.readBufferSize = mdata.GetInt(md, readBufferSize)
|
||||
if l.md.readBufferSize <= 0 {
|
||||
l.md.readBufferSize = defaultReadBufferSize
|
||||
}
|
||||
|
||||
l.md.readQueueSize = mdata.GetInt(md, readQueueSize)
|
||||
if l.md.readQueueSize <= 0 {
|
||||
l.md.readQueueSize = defaultReadQueueSize
|
||||
}
|
||||
|
||||
l.md.backlog = mdata.GetInt(md, backlog)
|
||||
if l.md.backlog <= 0 {
|
||||
l.md.backlog = defaultBacklog
|
||||
}
|
||||
|
||||
return
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/go-gost/core/listener"
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
metrics "github.com/go-gost/core/metrics/wrapper"
|
||||
"github.com/go-gost/core/registry"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.ListenerRegistry().Register("tcp", NewListener)
|
||||
}
|
||||
|
||||
type tcpListener struct {
|
||||
ln net.Listener
|
||||
logger logger.Logger
|
||||
md metadata
|
||||
options listener.Options
|
||||
}
|
||||
|
||||
func NewListener(opts ...listener.Option) listener.Listener {
|
||||
options := listener.Options{}
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
return &tcpListener{
|
||||
logger: options.Logger,
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *tcpListener) Init(md md.Metadata) (err error) {
|
||||
if err = l.parseMetadata(md); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ln, err := net.Listen("tcp", l.options.Addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
l.ln = metrics.WrapListener(l.options.Service, ln)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (l *tcpListener) Accept() (conn net.Conn, err error) {
|
||||
return l.ln.Accept()
|
||||
}
|
||||
|
||||
func (l *tcpListener) Addr() net.Addr {
|
||||
return l.ln.Addr()
|
||||
}
|
||||
|
||||
func (l *tcpListener) Close() error {
|
||||
return l.ln.Close()
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
md "github.com/go-gost/core/metadata"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
}
|
||||
|
||||
func (l *tcpListener) parseMetadata(md md.Metadata) (err error) {
|
||||
return
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
|
||||
admission "github.com/go-gost/core/admission/wrapper"
|
||||
"github.com/go-gost/core/listener"
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
metrics "github.com/go-gost/core/metrics/wrapper"
|
||||
"github.com/go-gost/core/registry"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.ListenerRegistry().Register("tls", NewListener)
|
||||
}
|
||||
|
||||
type tlsListener struct {
|
||||
ln net.Listener
|
||||
logger logger.Logger
|
||||
md metadata
|
||||
options listener.Options
|
||||
}
|
||||
|
||||
func NewListener(opts ...listener.Option) listener.Listener {
|
||||
options := listener.Options{}
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
return &tlsListener{
|
||||
logger: options.Logger,
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *tlsListener) Init(md md.Metadata) (err error) {
|
||||
if err = l.parseMetadata(md); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ln, err := net.Listen("tcp", l.options.Addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ln = metrics.WrapListener(l.options.Service, ln)
|
||||
ln = admission.WrapListener(l.options.Admission, ln)
|
||||
|
||||
l.ln = tls.NewListener(ln, l.options.TLSConfig)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (l *tlsListener) Accept() (conn net.Conn, err error) {
|
||||
return l.ln.Accept()
|
||||
}
|
||||
|
||||
func (l *tlsListener) Addr() net.Addr {
|
||||
return l.ln.Addr()
|
||||
}
|
||||
|
||||
func (l *tlsListener) Close() error {
|
||||
return l.ln.Close()
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
}
|
||||
|
||||
func (l *tlsListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
return
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
package udp
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/go-gost/core/common/net/udp"
|
||||
"github.com/go-gost/core/listener"
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
metrics "github.com/go-gost/core/metrics/wrapper"
|
||||
"github.com/go-gost/core/registry"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.ListenerRegistry().Register("udp", NewListener)
|
||||
}
|
||||
|
||||
type udpListener struct {
|
||||
ln net.Listener
|
||||
logger logger.Logger
|
||||
md metadata
|
||||
options listener.Options
|
||||
}
|
||||
|
||||
func NewListener(opts ...listener.Option) listener.Listener {
|
||||
options := listener.Options{}
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
return &udpListener{
|
||||
logger: options.Logger,
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *udpListener) Init(md md.Metadata) (err error) {
|
||||
if err = l.parseMetadata(md); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
laddr, err := net.ResolveUDPAddr("udp", l.options.Addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var conn net.PacketConn
|
||||
conn, err = net.ListenUDP("udp", laddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
conn = metrics.WrapPacketConn(l.options.Service, conn)
|
||||
|
||||
l.ln = udp.NewListener(conn, &udp.ListenConfig{
|
||||
Backlog: l.md.backlog,
|
||||
ReadQueueSize: l.md.readQueueSize,
|
||||
ReadBufferSize: l.md.readBufferSize,
|
||||
KeepAlive: l.md.keepalive,
|
||||
TTL: l.md.ttl,
|
||||
Logger: l.logger,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (l *udpListener) Accept() (conn net.Conn, err error) {
|
||||
return l.ln.Accept()
|
||||
}
|
||||
|
||||
func (l *udpListener) Addr() net.Addr {
|
||||
return l.ln.Addr()
|
||||
}
|
||||
|
||||
func (l *udpListener) Close() error {
|
||||
return l.ln.Close()
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
package udp
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTTL = 5 * time.Second
|
||||
defaultReadBufferSize = 1500
|
||||
defaultReadQueueSize = 128
|
||||
defaultBacklog = 128
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
readBufferSize int
|
||||
readQueueSize int
|
||||
backlog int
|
||||
keepalive bool
|
||||
ttl time.Duration
|
||||
}
|
||||
|
||||
func (l *udpListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
const (
|
||||
readBufferSize = "readBufferSize"
|
||||
readQueueSize = "readQueueSize"
|
||||
backlog = "backlog"
|
||||
keepAlive = "keepAlive"
|
||||
ttl = "ttl"
|
||||
)
|
||||
|
||||
l.md.ttl = mdata.GetDuration(md, ttl)
|
||||
if l.md.ttl <= 0 {
|
||||
l.md.ttl = defaultTTL
|
||||
}
|
||||
l.md.readBufferSize = mdata.GetInt(md, readBufferSize)
|
||||
if l.md.readBufferSize <= 0 {
|
||||
l.md.readBufferSize = defaultReadBufferSize
|
||||
}
|
||||
|
||||
l.md.readQueueSize = mdata.GetInt(md, readQueueSize)
|
||||
if l.md.readQueueSize <= 0 {
|
||||
l.md.readQueueSize = defaultReadQueueSize
|
||||
}
|
||||
|
||||
l.md.backlog = mdata.GetInt(md, backlog)
|
||||
if l.md.backlog <= 0 {
|
||||
l.md.backlog = defaultBacklog
|
||||
}
|
||||
l.md.keepalive = mdata.GetBool(md, keepAlive)
|
||||
|
||||
return
|
||||
}
|
@ -1,155 +0,0 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultLogger = NewLogger()
|
||||
)
|
||||
|
||||
func Default() Logger {
|
||||
return defaultLogger
|
||||
}
|
||||
|
||||
func SetDefault(logger Logger) {
|
||||
defaultLogger = logger
|
||||
}
|
||||
|
||||
type logger struct {
|
||||
logger *logrus.Entry
|
||||
}
|
||||
|
||||
func NewLogger(opts ...LoggerOption) Logger {
|
||||
var options LoggerOptions
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
log := logrus.New()
|
||||
if options.Output != nil {
|
||||
log.SetOutput(options.Output)
|
||||
}
|
||||
|
||||
switch options.Format {
|
||||
case TextFormat:
|
||||
log.SetFormatter(&logrus.TextFormatter{
|
||||
FullTimestamp: true,
|
||||
})
|
||||
default:
|
||||
log.SetFormatter(&logrus.JSONFormatter{
|
||||
DisableHTMLEscape: true,
|
||||
// PrettyPrint: true,
|
||||
})
|
||||
}
|
||||
|
||||
switch options.Level {
|
||||
case DebugLevel, InfoLevel, WarnLevel, ErrorLevel, FatalLevel:
|
||||
lvl, _ := logrus.ParseLevel(string(options.Level))
|
||||
log.SetLevel(lvl)
|
||||
default:
|
||||
log.SetLevel(logrus.InfoLevel)
|
||||
}
|
||||
|
||||
return &logger{
|
||||
logger: logrus.NewEntry(log),
|
||||
}
|
||||
}
|
||||
|
||||
// WithFields adds new fields to log.
|
||||
func (l *logger) WithFields(fields map[string]any) Logger {
|
||||
return &logger{
|
||||
logger: l.logger.WithFields(logrus.Fields(fields)),
|
||||
}
|
||||
}
|
||||
|
||||
// Debug logs a message at level Debug.
|
||||
func (l *logger) Debug(args ...any) {
|
||||
l.log(logrus.DebugLevel, args...)
|
||||
}
|
||||
|
||||
// Debugf logs a message at level Debug.
|
||||
func (l *logger) Debugf(format string, args ...any) {
|
||||
l.logf(logrus.DebugLevel, format, args...)
|
||||
}
|
||||
|
||||
// Info logs a message at level Info.
|
||||
func (l *logger) Info(args ...any) {
|
||||
l.log(logrus.InfoLevel, args...)
|
||||
}
|
||||
|
||||
// Infof logs a message at level Info.
|
||||
func (l *logger) Infof(format string, args ...any) {
|
||||
l.logf(logrus.InfoLevel, format, args...)
|
||||
}
|
||||
|
||||
// Warn logs a message at level Warn.
|
||||
func (l *logger) Warn(args ...any) {
|
||||
l.log(logrus.WarnLevel, args...)
|
||||
}
|
||||
|
||||
// Warnf logs a message at level Warn.
|
||||
func (l *logger) Warnf(format string, args ...any) {
|
||||
l.logf(logrus.WarnLevel, format, args...)
|
||||
}
|
||||
|
||||
// Error logs a message at level Error.
|
||||
func (l *logger) Error(args ...any) {
|
||||
l.log(logrus.ErrorLevel, args...)
|
||||
}
|
||||
|
||||
// Errorf logs a message at level Error.
|
||||
func (l *logger) Errorf(format string, args ...any) {
|
||||
l.logf(logrus.ErrorLevel, format, args...)
|
||||
}
|
||||
|
||||
// Fatal logs a message at level Fatal then the process will exit with status set to 1.
|
||||
func (l *logger) Fatal(args ...any) {
|
||||
l.log(logrus.FatalLevel, args...)
|
||||
l.logger.Logger.Exit(1)
|
||||
}
|
||||
|
||||
// Fatalf logs a message at level Fatal then the process will exit with status set to 1.
|
||||
func (l *logger) Fatalf(format string, args ...any) {
|
||||
l.logf(logrus.FatalLevel, format, args...)
|
||||
l.logger.Logger.Exit(1)
|
||||
}
|
||||
|
||||
func (l *logger) GetLevel() LogLevel {
|
||||
return LogLevel(l.logger.Logger.GetLevel().String())
|
||||
}
|
||||
|
||||
func (l *logger) IsLevelEnabled(level LogLevel) bool {
|
||||
lvl, _ := logrus.ParseLevel(string(level))
|
||||
return l.logger.Logger.IsLevelEnabled(lvl)
|
||||
}
|
||||
|
||||
func (l *logger) log(level logrus.Level, args ...any) {
|
||||
lg := l.logger
|
||||
if l.logger.Logger.IsLevelEnabled(logrus.DebugLevel) {
|
||||
lg = lg.WithField("caller", l.caller(3))
|
||||
}
|
||||
lg.Log(level, args...)
|
||||
}
|
||||
|
||||
func (l *logger) logf(level logrus.Level, format string, args ...any) {
|
||||
lg := l.logger
|
||||
if l.logger.Logger.IsLevelEnabled(logrus.DebugLevel) {
|
||||
lg = lg.WithField("caller", l.caller(3))
|
||||
}
|
||||
lg.Logf(level, format, args...)
|
||||
}
|
||||
|
||||
func (l *logger) caller(skip int) string {
|
||||
_, file, line, ok := runtime.Caller(skip)
|
||||
if !ok {
|
||||
file = "<???>"
|
||||
} else {
|
||||
file = filepath.Join(filepath.Base(filepath.Dir(file)), filepath.Base(file))
|
||||
}
|
||||
return fmt.Sprintf("%s:%d", file, line)
|
||||
}
|
@ -1,9 +1,5 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// LogFormat is format type
|
||||
type LogFormat string
|
||||
|
||||
@ -44,28 +40,14 @@ type Logger interface {
|
||||
IsLevelEnabled(level LogLevel) bool
|
||||
}
|
||||
|
||||
type LoggerOptions struct {
|
||||
Output io.Writer
|
||||
Format LogFormat
|
||||
Level LogLevel
|
||||
var (
|
||||
defaultLogger Logger
|
||||
)
|
||||
|
||||
func Default() Logger {
|
||||
return defaultLogger
|
||||
}
|
||||
|
||||
type LoggerOption func(opts *LoggerOptions)
|
||||
|
||||
func OutputLoggerOption(out io.Writer) LoggerOption {
|
||||
return func(opts *LoggerOptions) {
|
||||
opts.Output = out
|
||||
}
|
||||
}
|
||||
|
||||
func FormatLoggerOption(format LogFormat) LoggerOption {
|
||||
return func(opts *LoggerOptions) {
|
||||
opts.Format = format
|
||||
}
|
||||
}
|
||||
|
||||
func LevelLoggerOption(level LogLevel) LoggerOption {
|
||||
return func(opts *LoggerOptions) {
|
||||
opts.Level = level
|
||||
}
|
||||
func SetDefault(logger Logger) {
|
||||
defaultLogger = logger
|
||||
}
|
||||
|
@ -1,53 +0,0 @@
|
||||
package logger
|
||||
|
||||
var (
|
||||
nop = &nopLogger{}
|
||||
)
|
||||
|
||||
func Nop() Logger {
|
||||
return nop
|
||||
}
|
||||
|
||||
type nopLogger struct{}
|
||||
|
||||
func (l *nopLogger) WithFields(fields map[string]any) Logger {
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *nopLogger) Debug(args ...any) {
|
||||
}
|
||||
|
||||
func (l *nopLogger) Debugf(format string, args ...any) {
|
||||
}
|
||||
|
||||
func (l *nopLogger) Info(args ...any) {
|
||||
}
|
||||
|
||||
func (l *nopLogger) Infof(format string, args ...any) {
|
||||
}
|
||||
|
||||
func (l *nopLogger) Warn(args ...any) {
|
||||
}
|
||||
|
||||
func (l *nopLogger) Warnf(format string, args ...any) {
|
||||
}
|
||||
|
||||
func (l *nopLogger) Error(args ...any) {
|
||||
}
|
||||
|
||||
func (l *nopLogger) Errorf(format string, args ...any) {
|
||||
}
|
||||
|
||||
func (l *nopLogger) Fatal(args ...any) {
|
||||
}
|
||||
|
||||
func (l *nopLogger) Fatalf(format string, args ...any) {
|
||||
}
|
||||
|
||||
func (l *nopLogger) GetLevel() LogLevel {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (l *nopLogger) IsLevelEnabled(level LogLevel) bool {
|
||||
return false
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"github.com/go-gost/core/admission"
|
||||
)
|
||||
|
||||
type admissionRegistry struct {
|
||||
registry
|
||||
}
|
||||
|
||||
func (r *admissionRegistry) Register(name string, v admission.Admission) error {
|
||||
return r.registry.Register(name, v)
|
||||
}
|
||||
|
||||
func (r *admissionRegistry) Get(name string) admission.Admission {
|
||||
if name != "" {
|
||||
return &admissionWrapper{name: name, r: r}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *admissionRegistry) get(name string) admission.Admission {
|
||||
if v := r.registry.Get(name); v != nil {
|
||||
return v.(admission.Admission)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type admissionWrapper struct {
|
||||
name string
|
||||
r *admissionRegistry
|
||||
}
|
||||
|
||||
func (w *admissionWrapper) Admit(addr string) bool {
|
||||
p := w.r.get(w.name)
|
||||
if p == nil {
|
||||
return false
|
||||
}
|
||||
return p.Admit(addr)
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"github.com/go-gost/core/auth"
|
||||
)
|
||||
|
||||
type autherRegistry struct {
|
||||
registry
|
||||
}
|
||||
|
||||
func (r *autherRegistry) Register(name string, v auth.Authenticator) error {
|
||||
return r.registry.Register(name, v)
|
||||
}
|
||||
|
||||
func (r *autherRegistry) Get(name string) auth.Authenticator {
|
||||
if name != "" {
|
||||
return &autherWrapper{name: name, r: r}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *autherRegistry) get(name string) auth.Authenticator {
|
||||
if v := r.registry.Get(name); v != nil {
|
||||
return v.(auth.Authenticator)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type autherWrapper struct {
|
||||
name string
|
||||
r *autherRegistry
|
||||
}
|
||||
|
||||
func (w *autherWrapper) Authenticate(user, password string) bool {
|
||||
v := w.r.get(w.name)
|
||||
if v == nil {
|
||||
return true
|
||||
}
|
||||
return v.Authenticate(user, password)
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"github.com/go-gost/core/bypass"
|
||||
)
|
||||
|
||||
type bypassRegistry struct {
|
||||
registry
|
||||
}
|
||||
|
||||
func (r *bypassRegistry) Register(name string, v bypass.Bypass) error {
|
||||
return r.registry.Register(name, v)
|
||||
}
|
||||
|
||||
func (r *bypassRegistry) Get(name string) bypass.Bypass {
|
||||
if name != "" {
|
||||
return &bypassWrapper{name: name, r: r}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *bypassRegistry) get(name string) bypass.Bypass {
|
||||
if v := r.registry.Get(name); v != nil {
|
||||
return v.(bypass.Bypass)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type bypassWrapper struct {
|
||||
name string
|
||||
r *bypassRegistry
|
||||
}
|
||||
|
||||
func (w *bypassWrapper) Contains(addr string) bool {
|
||||
bp := w.r.get(w.name)
|
||||
if bp == nil {
|
||||
return false
|
||||
}
|
||||
return bp.Contains(addr)
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"github.com/go-gost/core/chain"
|
||||
)
|
||||
|
||||
type chainRegistry struct {
|
||||
registry
|
||||
}
|
||||
|
||||
func (r *chainRegistry) Register(name string, v chain.Chainer) error {
|
||||
return r.registry.Register(name, v)
|
||||
}
|
||||
|
||||
func (r *chainRegistry) Get(name string) chain.Chainer {
|
||||
if name != "" {
|
||||
return &chainWrapper{name: name, r: r}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *chainRegistry) get(name string) chain.Chainer {
|
||||
if v := r.registry.Get(name); v != nil {
|
||||
return v.(chain.Chainer)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type chainWrapper struct {
|
||||
name string
|
||||
r *chainRegistry
|
||||
}
|
||||
|
||||
func (w *chainWrapper) Route(network, address string) *chain.Route {
|
||||
v := w.r.get(w.name)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
return v.Route(network, address)
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"github.com/go-gost/core/connector"
|
||||
"github.com/go-gost/core/logger"
|
||||
)
|
||||
|
||||
type NewConnector func(opts ...connector.Option) connector.Connector
|
||||
|
||||
type connectorRegistry struct {
|
||||
registry
|
||||
}
|
||||
|
||||
func (r *connectorRegistry) Register(name string, v NewConnector) error {
|
||||
if err := r.registry.Register(name, v); err != nil {
|
||||
logger.Default().Fatal(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *connectorRegistry) Get(name string) NewConnector {
|
||||
if v := r.registry.Get(name); v != nil {
|
||||
return v.(NewConnector)
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"github.com/go-gost/core/dialer"
|
||||
"github.com/go-gost/core/logger"
|
||||
)
|
||||
|
||||
type NewDialer func(opts ...dialer.Option) dialer.Dialer
|
||||
|
||||
type dialerRegistry struct {
|
||||
registry
|
||||
}
|
||||
|
||||
func (r *dialerRegistry) Register(name string, v NewDialer) error {
|
||||
if err := r.registry.Register(name, v); err != nil {
|
||||
logger.Default().Fatal(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *dialerRegistry) Get(name string) NewDialer {
|
||||
if v := r.registry.Get(name); v != nil {
|
||||
return v.(NewDialer)
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"github.com/go-gost/core/handler"
|
||||
"github.com/go-gost/core/logger"
|
||||
)
|
||||
|
||||
type NewHandler func(opts ...handler.Option) handler.Handler
|
||||
|
||||
type handlerRegistry struct {
|
||||
registry
|
||||
}
|
||||
|
||||
func (r *handlerRegistry) Register(name string, v NewHandler) error {
|
||||
if err := r.registry.Register(name, v); err != nil {
|
||||
logger.Default().Fatal(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *handlerRegistry) Get(name string) NewHandler {
|
||||
if v := r.registry.Get(name); v != nil {
|
||||
return v.(NewHandler)
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/go-gost/core/hosts"
|
||||
)
|
||||
|
||||
type hostsRegistry struct {
|
||||
registry
|
||||
}
|
||||
|
||||
func (r *hostsRegistry) Register(name string, v hosts.HostMapper) error {
|
||||
return r.registry.Register(name, v)
|
||||
}
|
||||
|
||||
func (r *hostsRegistry) Get(name string) hosts.HostMapper {
|
||||
if name != "" {
|
||||
return &hostsWrapper{name: name, r: r}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *hostsRegistry) get(name string) hosts.HostMapper {
|
||||
if v := r.registry.Get(name); v != nil {
|
||||
return v.(hosts.HostMapper)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type hostsWrapper struct {
|
||||
name string
|
||||
r *hostsRegistry
|
||||
}
|
||||
|
||||
func (w *hostsWrapper) Lookup(network, host string) ([]net.IP, bool) {
|
||||
v := w.r.get(w.name)
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
return v.Lookup(network, host)
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"github.com/go-gost/core/listener"
|
||||
"github.com/go-gost/core/logger"
|
||||
)
|
||||
|
||||
type NewListener func(opts ...listener.Option) listener.Listener
|
||||
|
||||
type listenerRegistry struct {
|
||||
registry
|
||||
}
|
||||
|
||||
func (r *listenerRegistry) Register(name string, v NewListener) error {
|
||||
if err := r.registry.Register(name, v); err != nil {
|
||||
logger.Default().Fatal(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *listenerRegistry) Get(name string) NewListener {
|
||||
if v := r.registry.Get(name); v != nil {
|
||||
return v.(NewListener)
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/go-gost/core/admission"
|
||||
"github.com/go-gost/core/auth"
|
||||
"github.com/go-gost/core/bypass"
|
||||
"github.com/go-gost/core/chain"
|
||||
"github.com/go-gost/core/hosts"
|
||||
"github.com/go-gost/core/resolver"
|
||||
"github.com/go-gost/core/service"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrDup = errors.New("registry: duplicate object")
|
||||
)
|
||||
|
||||
var (
|
||||
listenerReg Registry[NewListener] = &listenerRegistry{}
|
||||
handlerReg Registry[NewHandler] = &handlerRegistry{}
|
||||
dialerReg Registry[NewDialer] = &dialerRegistry{}
|
||||
connectorReg Registry[NewConnector] = &connectorRegistry{}
|
||||
|
||||
serviceReg Registry[service.Service] = &serviceRegistry{}
|
||||
chainReg Registry[chain.Chainer] = &chainRegistry{}
|
||||
autherReg Registry[auth.Authenticator] = &autherRegistry{}
|
||||
admissionReg Registry[admission.Admission] = &admissionRegistry{}
|
||||
bypassReg Registry[bypass.Bypass] = &bypassRegistry{}
|
||||
resolverReg Registry[resolver.Resolver] = &resolverRegistry{}
|
||||
hostsReg Registry[hosts.HostMapper] = &hostsRegistry{}
|
||||
)
|
||||
|
||||
type Registry[T any] interface {
|
||||
Register(name string, v T) error
|
||||
Unregister(name string)
|
||||
IsRegistered(name string) bool
|
||||
Get(name string) T
|
||||
}
|
||||
|
||||
type registry struct {
|
||||
m sync.Map
|
||||
}
|
||||
|
||||
func (r *registry) Register(name string, v any) error {
|
||||
if name == "" || v == nil {
|
||||
return nil
|
||||
}
|
||||
if _, loaded := r.m.LoadOrStore(name, v); loaded {
|
||||
return ErrDup
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *registry) Unregister(name string) {
|
||||
r.m.Delete(name)
|
||||
}
|
||||
|
||||
func (r *registry) IsRegistered(name string) bool {
|
||||
_, ok := r.m.Load(name)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (r *registry) Get(name string) any {
|
||||
if name == "" {
|
||||
return nil
|
||||
}
|
||||
v, _ := r.m.Load(name)
|
||||
return v
|
||||
}
|
||||
|
||||
func ListenerRegistry() Registry[NewListener] {
|
||||
return listenerReg
|
||||
}
|
||||
|
||||
func HandlerRegistry() Registry[NewHandler] {
|
||||
return handlerReg
|
||||
}
|
||||
|
||||
func DialerRegistry() Registry[NewDialer] {
|
||||
return dialerReg
|
||||
}
|
||||
|
||||
func ConnectorRegistry() Registry[NewConnector] {
|
||||
return connectorReg
|
||||
}
|
||||
|
||||
func ServiceRegistry() Registry[service.Service] {
|
||||
return serviceReg
|
||||
}
|
||||
|
||||
func ChainRegistry() Registry[chain.Chainer] {
|
||||
return chainReg
|
||||
}
|
||||
|
||||
func AutherRegistry() Registry[auth.Authenticator] {
|
||||
return autherReg
|
||||
}
|
||||
|
||||
func AdmissionRegistry() Registry[admission.Admission] {
|
||||
return admissionReg
|
||||
}
|
||||
|
||||
func BypassRegistry() Registry[bypass.Bypass] {
|
||||
return bypassReg
|
||||
}
|
||||
|
||||
func ResolverRegistry() Registry[resolver.Resolver] {
|
||||
return resolverReg
|
||||
}
|
||||
|
||||
func HostsRegistry() Registry[hosts.HostMapper] {
|
||||
return hostsReg
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/go-gost/core/resolver"
|
||||
)
|
||||
|
||||
type resolverRegistry struct {
|
||||
registry
|
||||
}
|
||||
|
||||
func (r *resolverRegistry) Register(name string, v resolver.Resolver) error {
|
||||
return r.registry.Register(name, v)
|
||||
}
|
||||
|
||||
func (r *resolverRegistry) Get(name string) resolver.Resolver {
|
||||
if name != "" {
|
||||
return &resolverWrapper{name: name, r: r}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *resolverRegistry) get(name string) resolver.Resolver {
|
||||
if v := r.registry.Get(name); v != nil {
|
||||
return v.(resolver.Resolver)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type resolverWrapper struct {
|
||||
name string
|
||||
r *resolverRegistry
|
||||
}
|
||||
|
||||
func (w *resolverWrapper) Resolve(ctx context.Context, network, host string) ([]net.IP, error) {
|
||||
r := w.r.get(w.name)
|
||||
if r == nil {
|
||||
return nil, resolver.ErrInvalid
|
||||
}
|
||||
return r.Resolve(ctx, network, host)
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"github.com/go-gost/core/service"
|
||||
)
|
||||
|
||||
type serviceRegistry struct {
|
||||
registry
|
||||
}
|
||||
|
||||
func (r *serviceRegistry) Register(name string, v service.Service) error {
|
||||
return r.registry.Register(name, v)
|
||||
}
|
||||
|
||||
func (r *serviceRegistry) Get(name string) service.Service {
|
||||
if v := r.registry.Get(name); v != nil {
|
||||
return v.(service.Service)
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,223 +0,0 @@
|
||||
package exchanger
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-gost/core/chain"
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
router *chain.Router
|
||||
tlsConfig *tls.Config
|
||||
timeout time.Duration
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
// Option allows a common way to set Exchanger options.
|
||||
type Option func(opts *Options)
|
||||
|
||||
// RouterOption sets the router for Exchanger.
|
||||
func RouterOption(router *chain.Router) Option {
|
||||
return func(opts *Options) {
|
||||
opts.router = router
|
||||
}
|
||||
}
|
||||
|
||||
// TLSConfigOption sets the TLS config for Exchanger.
|
||||
func TLSConfigOption(cfg *tls.Config) Option {
|
||||
return func(opts *Options) {
|
||||
opts.tlsConfig = cfg
|
||||
}
|
||||
}
|
||||
|
||||
// LoggerOption sets the logger for Exchanger.
|
||||
func LoggerOption(logger logger.Logger) Option {
|
||||
return func(opts *Options) {
|
||||
opts.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
// TimeoutOption sets the timeout for Exchanger.
|
||||
func TimeoutOption(timeout time.Duration) Option {
|
||||
return func(opts *Options) {
|
||||
opts.timeout = timeout
|
||||
}
|
||||
}
|
||||
|
||||
// Exchanger is an interface for DNS synchronous query.
|
||||
type Exchanger interface {
|
||||
Exchange(ctx context.Context, msg []byte) ([]byte, error)
|
||||
String() string
|
||||
}
|
||||
|
||||
type exchanger struct {
|
||||
network string
|
||||
addr string
|
||||
rawAddr string
|
||||
router *chain.Router
|
||||
client *http.Client
|
||||
options Options
|
||||
}
|
||||
|
||||
// NewExchanger create an Exchanger.
|
||||
// The addr should be URL-like format,
|
||||
// e.g. udp://1.1.1.1:53, tls://1.1.1.1:853, https://1.0.0.1/dns-query
|
||||
func NewExchanger(addr string, opts ...Option) (Exchanger, error) {
|
||||
var options Options
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
if !strings.Contains(addr, "://") {
|
||||
addr = "udp://" + addr
|
||||
}
|
||||
u, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if options.timeout <= 0 {
|
||||
options.timeout = 5 * time.Second
|
||||
}
|
||||
|
||||
ex := &exchanger{
|
||||
network: u.Scheme,
|
||||
addr: u.Host,
|
||||
rawAddr: addr,
|
||||
router: options.router,
|
||||
options: options,
|
||||
}
|
||||
if _, port, _ := net.SplitHostPort(ex.addr); port == "" {
|
||||
ex.addr = net.JoinHostPort(ex.addr, "53")
|
||||
}
|
||||
if ex.router == nil {
|
||||
ex.router = (&chain.Router{}).WithLogger(options.logger)
|
||||
}
|
||||
|
||||
switch ex.network {
|
||||
case "tcp":
|
||||
case "dot", "tls":
|
||||
if ex.options.tlsConfig == nil {
|
||||
ex.options.tlsConfig = &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
}
|
||||
ex.network = "tcp"
|
||||
case "https":
|
||||
ex.addr = addr
|
||||
if ex.options.tlsConfig == nil {
|
||||
ex.options.tlsConfig = &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
}
|
||||
ex.client = &http.Client{
|
||||
Timeout: options.timeout,
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: options.tlsConfig,
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: options.timeout,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
DialContext: ex.dial,
|
||||
},
|
||||
}
|
||||
default:
|
||||
ex.network = "udp"
|
||||
}
|
||||
|
||||
return ex, nil
|
||||
}
|
||||
|
||||
func (ex *exchanger) Exchange(ctx context.Context, msg []byte) ([]byte, error) {
|
||||
if ex.network == "https" {
|
||||
return ex.dohExchange(ctx, msg)
|
||||
}
|
||||
return ex.exchange(ctx, msg)
|
||||
}
|
||||
|
||||
func (ex *exchanger) dohExchange(ctx context.Context, msg []byte) ([]byte, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", ex.addr, bytes.NewBuffer(msg))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create an HTTPS request: %w", err)
|
||||
}
|
||||
|
||||
// req.Header.Add("Content-Type", "application/dns-udpwireformat")
|
||||
req.Header.Add("Content-Type", "application/dns-message")
|
||||
|
||||
client := ex.client
|
||||
if client == nil {
|
||||
client = http.DefaultClient
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to perform an HTTPS request: %w", err)
|
||||
}
|
||||
|
||||
// Check response status code
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("returned status code %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
// Read wireformat response from the body
|
||||
buf, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read the response body: %w", err)
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (ex *exchanger) exchange(ctx context.Context, msg []byte) ([]byte, error) {
|
||||
if ex.options.timeout > 0 {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, ex.options.timeout)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
c, err := ex.dial(ctx, ex.network, ex.addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
if ex.options.tlsConfig != nil {
|
||||
c = tls.Client(c, ex.options.tlsConfig)
|
||||
}
|
||||
|
||||
conn := &dns.Conn{
|
||||
UDPSize: 1024,
|
||||
Conn: c,
|
||||
}
|
||||
|
||||
if _, err = conn.Write(msg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mr, err := conn.ReadMsg()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mr.Pack()
|
||||
}
|
||||
|
||||
func (ex *exchanger) dial(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
return ex.router.Dial(ctx, network, address)
|
||||
}
|
||||
|
||||
func (ex *exchanger) String() string {
|
||||
return ex.rawAddr
|
||||
}
|
@ -1,178 +0,0 @@
|
||||
package impl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-gost/core/chain"
|
||||
resolver_util "github.com/go-gost/core/common/util/resolver"
|
||||
"github.com/go-gost/core/logger"
|
||||
resolverpkg "github.com/go-gost/core/resolver"
|
||||
"github.com/go-gost/core/resolver/exchanger"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type NameServer struct {
|
||||
Addr string
|
||||
Chain chain.Chainer
|
||||
TTL time.Duration
|
||||
Timeout time.Duration
|
||||
ClientIP net.IP
|
||||
Prefer string
|
||||
Hostname string // for TLS handshake verification
|
||||
exchanger exchanger.Exchanger
|
||||
}
|
||||
|
||||
type resolverOptions struct {
|
||||
domain string
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
type ResolverOption func(opts *resolverOptions)
|
||||
|
||||
func DomainResolverOption(domain string) ResolverOption {
|
||||
return func(opts *resolverOptions) {
|
||||
opts.domain = domain
|
||||
}
|
||||
}
|
||||
|
||||
func LoggerResolverOption(logger logger.Logger) ResolverOption {
|
||||
return func(opts *resolverOptions) {
|
||||
opts.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
type resolver struct {
|
||||
servers []NameServer
|
||||
cache *resolver_util.Cache
|
||||
options resolverOptions
|
||||
}
|
||||
|
||||
func NewResolver(nameservers []NameServer, opts ...ResolverOption) (resolverpkg.Resolver, error) {
|
||||
options := resolverOptions{}
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
var servers []NameServer
|
||||
for _, server := range nameservers {
|
||||
addr := strings.TrimSpace(server.Addr)
|
||||
if addr == "" {
|
||||
continue
|
||||
}
|
||||
ex, err := exchanger.NewExchanger(
|
||||
addr,
|
||||
exchanger.RouterOption(
|
||||
(&chain.Router{}).
|
||||
WithChain(server.Chain).
|
||||
WithLogger(options.logger),
|
||||
),
|
||||
exchanger.TimeoutOption(server.Timeout),
|
||||
exchanger.LoggerOption(options.logger),
|
||||
)
|
||||
if err != nil {
|
||||
options.logger.Warnf("parse %s: %v", server, err)
|
||||
continue
|
||||
}
|
||||
|
||||
server.exchanger = ex
|
||||
servers = append(servers, server)
|
||||
}
|
||||
cache := resolver_util.NewCache().
|
||||
WithLogger(options.logger)
|
||||
|
||||
return &resolver{
|
||||
servers: servers,
|
||||
cache: cache,
|
||||
options: options,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *resolver) Resolve(ctx context.Context, network, host string) (ips []net.IP, err error) {
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
return []net.IP{ip}, nil
|
||||
}
|
||||
|
||||
if r.options.domain != "" &&
|
||||
!strings.Contains(host, ".") {
|
||||
host = host + "." + r.options.domain
|
||||
}
|
||||
|
||||
for _, server := range r.servers {
|
||||
ips, err = r.resolve(ctx, &server, host)
|
||||
if err != nil {
|
||||
r.options.logger.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
r.options.logger.Debugf("resolve %s via %s: %v", host, server.exchanger.String(), ips)
|
||||
|
||||
if len(ips) > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (r *resolver) resolve(ctx context.Context, server *NameServer, host string) (ips []net.IP, err error) {
|
||||
if server == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if server.Prefer == "ipv6" { // prefer ipv6
|
||||
mq := dns.Msg{}
|
||||
mq.SetQuestion(dns.Fqdn(host), dns.TypeAAAA)
|
||||
ips, err = r.resolveIPs(ctx, server, &mq)
|
||||
if err != nil || len(ips) > 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// fallback to ipv4
|
||||
mq := dns.Msg{}
|
||||
mq.SetQuestion(dns.Fqdn(host), dns.TypeA)
|
||||
return r.resolveIPs(ctx, server, &mq)
|
||||
}
|
||||
|
||||
func (r *resolver) resolveIPs(ctx context.Context, server *NameServer, mq *dns.Msg) (ips []net.IP, err error) {
|
||||
key := resolver_util.NewCacheKey(&mq.Question[0])
|
||||
mr := r.cache.Load(key)
|
||||
if mr == nil {
|
||||
resolver_util.AddSubnetOpt(mq, server.ClientIP)
|
||||
mr, err = r.exchange(ctx, server.exchanger, mq)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r.cache.Store(key, mr, server.TTL)
|
||||
}
|
||||
|
||||
for _, ans := range mr.Answer {
|
||||
if ar, _ := ans.(*dns.AAAA); ar != nil {
|
||||
ips = append(ips, ar.AAAA)
|
||||
}
|
||||
if ar, _ := ans.(*dns.A); ar != nil {
|
||||
ips = append(ips, ar.A)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (r *resolver) exchange(ctx context.Context, ex exchanger.Exchanger, mq *dns.Msg) (mr *dns.Msg, err error) {
|
||||
query, err := mq.Pack()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
reply, err := ex.Exchange(ctx, query)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
mr = &dns.Msg{}
|
||||
err = mr.Unpack(reply)
|
||||
|
||||
return
|
||||
}
|
@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalid = errors.New("resolver is invalid")
|
||||
ErrInvalid = errors.New("invalid resolver")
|
||||
)
|
||||
|
||||
type Resolver interface {
|
||||
|
Loading…
Reference in New Issue
Block a user