mirror of
https://github.com/go-gost/x.git
synced 2026-10-20 00:17:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1e6164a83 | ||
|
|
6f001a779a | ||
|
|
2e8a5d6f51 | ||
|
|
c2aedbabd9 | ||
|
|
79b6b9138e | ||
|
|
3db20563d2 | ||
|
|
a12870e766 | ||
|
|
edb9a992e2 | ||
|
|
f4d9a5f79b | ||
|
|
88ba01b3dd | ||
|
|
d8126e8e30 | ||
|
|
a97ac1f30a | ||
|
|
3b4fd643de | ||
|
|
3ca253537b | ||
|
|
41f527aa46 | ||
|
|
6b932e35bf | ||
|
|
a0cbee8817 |
@@ -271,3 +271,22 @@ func (p *localAdmission) Close() error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type admissionGroup struct {
|
||||
admissions []admission.Admission
|
||||
}
|
||||
|
||||
func AdmissionGroup(admissions ...admission.Admission) admission.Admission {
|
||||
return &admissionGroup{
|
||||
admissions: admissions,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *admissionGroup) Admit(ctx context.Context, addr string, opts ...admission.Option) bool {
|
||||
for _, admission := range p.admissions {
|
||||
if admission != nil && !admission.Admit(ctx, addr, opts...) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
+5
-2
@@ -53,8 +53,11 @@ type server struct {
|
||||
cclose chan struct{}
|
||||
}
|
||||
|
||||
func NewService(addr string, opts ...Option) (service.Service, error) {
|
||||
ln, err := net.Listen("tcp", addr)
|
||||
func NewService(network, addr string, opts ...Option) (service.Service, error) {
|
||||
if network == "" {
|
||||
network = "tcp"
|
||||
}
|
||||
ln, err := net.Listen(network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -252,3 +252,29 @@ func (p *authenticator) Close() error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type authenticatorGroup struct {
|
||||
authers []auth.Authenticator
|
||||
}
|
||||
|
||||
func AuthenticatorGroup(authers ...auth.Authenticator) auth.Authenticator {
|
||||
return &authenticatorGroup{
|
||||
authers: authers,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *authenticatorGroup) Authenticate(ctx context.Context, user, password string, opts ...auth.Option) (string, bool) {
|
||||
if len(p.authers) == 0 {
|
||||
return "", false
|
||||
}
|
||||
for _, auther := range p.authers {
|
||||
if auther == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if id, ok := auther.Authenticate(ctx, user, password, opts...); ok {
|
||||
return id, ok
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -288,3 +289,45 @@ func (bp *localBypass) Close() error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type bypassGroup struct {
|
||||
bypasses []bypass.Bypass
|
||||
}
|
||||
|
||||
func BypassGroup(bypasses ...bypass.Bypass) bypass.Bypass {
|
||||
return &bypassGroup{
|
||||
bypasses: bypasses,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *bypassGroup) Contains(ctx context.Context, network, addr string, opts ...bypass.Option) bool {
|
||||
var whitelist, blacklist []bool
|
||||
for _, bypass := range p.bypasses {
|
||||
result := bypass.Contains(ctx, network, addr, opts...)
|
||||
if bypass.IsWhitelist() {
|
||||
whitelist = append(whitelist, result)
|
||||
} else {
|
||||
blacklist = append(blacklist, result)
|
||||
}
|
||||
}
|
||||
status := false
|
||||
if len(whitelist) > 0 {
|
||||
if slices.Contains(whitelist, false) {
|
||||
status = false
|
||||
} else {
|
||||
status = true
|
||||
}
|
||||
}
|
||||
if !status && len(blacklist) > 0 {
|
||||
if slices.Contains(blacklist, true) {
|
||||
status = true
|
||||
} else {
|
||||
status = false
|
||||
}
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
func (p *bypassGroup) IsWhitelist() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
+47
-32
@@ -155,7 +155,7 @@ type SelectorConfig struct {
|
||||
|
||||
type AdmissionConfig struct {
|
||||
Name string `json:"name"`
|
||||
// DEPRECATED by whitelist since beta.4
|
||||
// Deprecated: use whitelist instead
|
||||
Reverse bool `yaml:",omitempty" json:"reverse,omitempty"`
|
||||
Whitelist bool `yaml:",omitempty" json:"whitelist,omitempty"`
|
||||
Matchers []string `yaml:",omitempty" json:"matchers,omitempty"`
|
||||
@@ -168,7 +168,7 @@ type AdmissionConfig struct {
|
||||
|
||||
type BypassConfig struct {
|
||||
Name string `json:"name"`
|
||||
// DEPRECATED by whitelist since beta.4
|
||||
// Deprecated: use whitelist instead
|
||||
Reverse bool `yaml:",omitempty" json:"reverse,omitempty"`
|
||||
Whitelist bool `yaml:",omitempty" json:"whitelist,omitempty"`
|
||||
Matchers []string `yaml:",omitempty" json:"matchers,omitempty"`
|
||||
@@ -347,7 +347,7 @@ type HandlerConfig struct {
|
||||
}
|
||||
|
||||
type ForwarderConfig struct {
|
||||
// DEPRECATED by hop field
|
||||
// Deprecated: use hop instead
|
||||
Name string `yaml:",omitempty" json:"name,omitempty"`
|
||||
// the referenced hop name
|
||||
Hop string `yaml:",omitempty" json:"hop,omitempty"`
|
||||
@@ -361,18 +361,20 @@ type ForwardNodeConfig struct {
|
||||
Network string `yaml:",omitempty" json:"network,omitempty"`
|
||||
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
|
||||
Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"`
|
||||
// DEPRECATED by filter.protocol
|
||||
// Deprecated: use matcher instead
|
||||
Protocol string `yaml:",omitempty" json:"protocol,omitempty"`
|
||||
// DEPRECATED by filter.host
|
||||
// Deprecated: use matcher instead
|
||||
Host string `yaml:",omitempty" json:"host,omitempty"`
|
||||
// DEPRECATED by filter.path
|
||||
// Deprecated: use matcher instead
|
||||
Path string `yaml:",omitempty" json:"path,omitempty"`
|
||||
// DEPRECATED by http.auth
|
||||
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
|
||||
Filter *NodeFilterConfig `yaml:",omitempty" json:"filter,omitempty"`
|
||||
HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"`
|
||||
TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"`
|
||||
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
|
||||
// Deprecated: use matcher instead
|
||||
Filter *NodeFilterConfig `yaml:",omitempty" json:"filter,omitempty"`
|
||||
Matcher *NodeMatcherConfig `yaml:",omitempty" json:"matcher,omitempty"`
|
||||
// Deprecated: use http.auth instead
|
||||
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
|
||||
HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"`
|
||||
TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"`
|
||||
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
type HTTPURLRewriteConfig struct {
|
||||
@@ -393,13 +395,24 @@ type NodeFilterConfig struct {
|
||||
Path string `yaml:",omitempty" json:"path,omitempty"`
|
||||
}
|
||||
|
||||
type NodeMatcherConfig struct {
|
||||
Rule string `yaml:",omitempty" json:"rule,omitempty"`
|
||||
Priority int `yaml:",omitempty" json:"priority,omitempty"`
|
||||
}
|
||||
|
||||
type HTTPNodeConfig struct {
|
||||
// rewrite host header
|
||||
Host string `yaml:",omitempty" json:"host,omitempty"`
|
||||
// additional request header
|
||||
// Deprecated: use requestHeader instead
|
||||
Header map[string]string `yaml:",omitempty" json:"header,omitempty"`
|
||||
// rewrite URL
|
||||
// additional request header
|
||||
RequestHeader map[string]string `yaml:"requestHeader,omitempty" json:"requestHeader,omitempty"`
|
||||
// additional response header
|
||||
ResponseHeader map[string]string `yaml:"responseHeader,omitempty" json:"responseHeader,omitempty"`
|
||||
// Deprecated: use rewriteURL instead
|
||||
Rewrite []HTTPURLRewriteConfig `yaml:",omitempty" json:"rewrite,omitempty"`
|
||||
// rewrite URL
|
||||
RewriteURL []HTTPURLRewriteConfig `yaml:"rewriteURL,omitempty" json:"rewriteURL,omitempty"`
|
||||
// rewrite response body
|
||||
RewriteBody []HTTPBodyRewriteConfig `yaml:"rewriteBody,omitempty" json:"rewriteBody,omitempty"`
|
||||
// HTTP basic auth
|
||||
@@ -433,9 +446,9 @@ type SockOptsConfig struct {
|
||||
type ServiceConfig struct {
|
||||
Name string `json:"name"`
|
||||
Addr string `yaml:",omitempty" json:"addr,omitempty"`
|
||||
// DEPRECATED by metadata.interface since beta.5
|
||||
// Deprecated: use metadata.interface instead
|
||||
Interface string `yaml:",omitempty" json:"interface,omitempty"`
|
||||
// DEPRECATED by metadata.so_mark since beta.5
|
||||
// Deprecated: use metadata.so_mark instead
|
||||
SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"`
|
||||
Admission string `yaml:",omitempty" json:"admission,omitempty"`
|
||||
Admissions []string `yaml:",omitempty" json:"admissions,omitempty"`
|
||||
@@ -508,22 +521,24 @@ type HopConfig struct {
|
||||
}
|
||||
|
||||
type NodeConfig struct {
|
||||
Name string `json:"name"`
|
||||
Addr string `yaml:",omitempty" json:"addr,omitempty"`
|
||||
Network string `yaml:",omitempty" json:"network,omitempty"`
|
||||
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
|
||||
Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"`
|
||||
Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
|
||||
Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
|
||||
Connector *ConnectorConfig `yaml:",omitempty" json:"connector,omitempty"`
|
||||
Dialer *DialerConfig `yaml:",omitempty" json:"dialer,omitempty"`
|
||||
Interface string `yaml:",omitempty" json:"interface,omitempty"`
|
||||
Netns string `yaml:",omitempty" json:"netns,omitempty"`
|
||||
SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"`
|
||||
Filter *NodeFilterConfig `yaml:",omitempty" json:"filter,omitempty"`
|
||||
HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"`
|
||||
TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"`
|
||||
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Addr string `yaml:",omitempty" json:"addr,omitempty"`
|
||||
Network string `yaml:",omitempty" json:"network,omitempty"`
|
||||
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
|
||||
Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"`
|
||||
Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
|
||||
Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
|
||||
Connector *ConnectorConfig `yaml:",omitempty" json:"connector,omitempty"`
|
||||
Dialer *DialerConfig `yaml:",omitempty" json:"dialer,omitempty"`
|
||||
Interface string `yaml:",omitempty" json:"interface,omitempty"`
|
||||
Netns string `yaml:",omitempty" json:"netns,omitempty"`
|
||||
SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"`
|
||||
// Deprecated: use matcher instead
|
||||
Filter *NodeFilterConfig `yaml:",omitempty" json:"filter,omitempty"`
|
||||
Matcher *NodeMatcherConfig `yaml:",omitempty" json:"matcher,omitempty"`
|
||||
HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"`
|
||||
TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"`
|
||||
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
|
||||
@@ -4,10 +4,10 @@ import (
|
||||
"crypto/tls"
|
||||
"strings"
|
||||
|
||||
"github.com/go-gost/core/bypass"
|
||||
"github.com/go-gost/core/chain"
|
||||
"github.com/go-gost/core/hop"
|
||||
"github.com/go-gost/core/logger"
|
||||
xbypass "github.com/go-gost/x/bypass"
|
||||
"github.com/go-gost/x/config"
|
||||
"github.com/go-gost/x/config/parsing"
|
||||
bypass_parser "github.com/go-gost/x/config/parsing/bypass"
|
||||
@@ -115,7 +115,7 @@ func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) {
|
||||
xhop.NameOption(cfg.Name),
|
||||
xhop.NodeOption(nodes...),
|
||||
xhop.SelectorOption(sel),
|
||||
xhop.BypassOption(bypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
|
||||
xhop.BypassOption(xbypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
|
||||
xhop.ReloadPeriodOption(cfg.Reload),
|
||||
xhop.LoggerOption(log.WithFields(map[string]any{
|
||||
"kind": "hop",
|
||||
|
||||
@@ -6,14 +6,13 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/go-gost/core/bypass"
|
||||
"github.com/go-gost/core/chain"
|
||||
"github.com/go-gost/core/connector"
|
||||
"github.com/go-gost/core/dialer"
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
xauth "github.com/go-gost/x/auth"
|
||||
xbypass "github.com/go-gost/x/bypass"
|
||||
xchain "github.com/go-gost/x/chain"
|
||||
"github.com/go-gost/x/config"
|
||||
"github.com/go-gost/x/config/parsing"
|
||||
@@ -21,7 +20,9 @@ import (
|
||||
bypass_parser "github.com/go-gost/x/config/parsing/bypass"
|
||||
tls_util "github.com/go-gost/x/internal/util/tls"
|
||||
mdx "github.com/go-gost/x/metadata"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
"github.com/go-gost/x/registry"
|
||||
"github.com/go-gost/x/routing"
|
||||
)
|
||||
|
||||
func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.Node, error) {
|
||||
@@ -149,7 +150,7 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
|
||||
|
||||
opts := []chain.NodeOption{
|
||||
chain.TransportNodeOption(tr),
|
||||
chain.BypassNodeOption(bypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
|
||||
chain.BypassNodeOption(xbypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
|
||||
chain.ResoloverNodeOption(registry.ResolverRegistry().Get(cfg.Resolver)),
|
||||
chain.HostMapperNodeOption(registry.HostsRegistry().Get(cfg.Hosts)),
|
||||
chain.MetadataNodeOption(nm),
|
||||
@@ -175,10 +176,33 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
|
||||
opts = append(opts, chain.NodeFilterOption(settings))
|
||||
}
|
||||
|
||||
if cfg.Matcher != nil {
|
||||
priority := cfg.Matcher.Priority
|
||||
|
||||
if rule := strings.TrimSpace(cfg.Matcher.Rule); rule != "" {
|
||||
if matcher, err := routing.NewMatcher(rule); err == nil {
|
||||
log.Debugf("new matcher for node %s with rule %s", cfg.Name, cfg.Matcher.Rule)
|
||||
if priority == 0 {
|
||||
priority = len(cfg.Matcher.Rule)
|
||||
}
|
||||
opts = append(opts, chain.MatcherNodeOption(matcher))
|
||||
} else {
|
||||
log.Error(err)
|
||||
priority = -1
|
||||
}
|
||||
}
|
||||
|
||||
opts = append(opts, chain.PriorityNodeOption(priority))
|
||||
}
|
||||
|
||||
if cfg.HTTP != nil {
|
||||
settings := &chain.HTTPNodeSettings{
|
||||
Host: cfg.HTTP.Host,
|
||||
Header: cfg.HTTP.Header,
|
||||
Host: cfg.HTTP.Host,
|
||||
RequestHeader: cfg.HTTP.RequestHeader,
|
||||
ResponseHeader: cfg.HTTP.ResponseHeader,
|
||||
}
|
||||
if settings.RequestHeader == nil {
|
||||
settings.RequestHeader = cfg.HTTP.Header
|
||||
}
|
||||
|
||||
if auth := cfg.HTTP.Auth; auth != nil && auth.Username != "" {
|
||||
@@ -191,7 +215,12 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
|
||||
})),
|
||||
)
|
||||
}
|
||||
for _, v := range cfg.HTTP.Rewrite {
|
||||
|
||||
rewriteURL := cfg.HTTP.RewriteURL
|
||||
if rewriteURL == nil {
|
||||
rewriteURL = cfg.HTTP.Rewrite
|
||||
}
|
||||
for _, v := range rewriteURL {
|
||||
if pattern, _ := regexp.Compile(v.Match); pattern != nil {
|
||||
settings.RewriteURL = append(settings.RewriteURL, chain.HTTPURLRewriteSetting{
|
||||
Pattern: pattern,
|
||||
@@ -201,7 +230,7 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
|
||||
}
|
||||
for _, v := range cfg.HTTP.RewriteBody {
|
||||
if pattern, _ := regexp.Compile(v.Match); pattern != nil {
|
||||
settings.RewriteBody = append(settings.RewriteBody, chain.HTTPBodyRewriteSettings{
|
||||
settings.RewriteResponseBody = append(settings.RewriteResponseBody, chain.HTTPBodyRewriteSettings{
|
||||
Type: v.Type,
|
||||
Pattern: pattern,
|
||||
Replacement: []byte(v.Replacement),
|
||||
|
||||
@@ -6,9 +6,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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/handler"
|
||||
"github.com/go-gost/core/hop"
|
||||
@@ -18,6 +16,9 @@ import (
|
||||
"github.com/go-gost/core/recorder"
|
||||
"github.com/go-gost/core/selector"
|
||||
"github.com/go-gost/core/service"
|
||||
xadmission "github.com/go-gost/x/admission"
|
||||
xauth "github.com/go-gost/x/auth"
|
||||
xbypass "github.com/go-gost/x/bypass"
|
||||
xchain "github.com/go-gost/x/chain"
|
||||
"github.com/go-gost/x/config"
|
||||
"github.com/go-gost/x/config/parsing"
|
||||
@@ -84,7 +85,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
||||
}
|
||||
var auther auth.Authenticator
|
||||
if len(authers) > 0 {
|
||||
auther = auth.AuthenticatorGroup(authers...)
|
||||
auther = xauth.AuthenticatorGroup(authers...)
|
||||
}
|
||||
|
||||
admissions := admission_parser.List(cfg.Admission, cfg.Admissions...)
|
||||
@@ -155,7 +156,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
||||
listener.AutherOption(auther),
|
||||
listener.AuthOption(auth_parser.Info(cfg.Listener.Auth)),
|
||||
listener.TLSConfigOption(tlsConfig),
|
||||
listener.AdmissionOption(admission.AdmissionGroup(admissions...)),
|
||||
listener.AdmissionOption(xadmission.AdmissionGroup(admissions...)),
|
||||
listener.TrafficLimiterOption(registry.TrafficLimiterRegistry().Get(cfg.Limiter)),
|
||||
listener.ConnLimiterOption(registry.ConnLimiterRegistry().Get(cfg.CLimiter)),
|
||||
listener.ServiceOption(cfg.Name),
|
||||
@@ -235,7 +236,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
||||
|
||||
auther = nil
|
||||
if len(authers) > 0 {
|
||||
auther = auth.AuthenticatorGroup(authers...)
|
||||
auther = xauth.AuthenticatorGroup(authers...)
|
||||
}
|
||||
|
||||
var recorders []recorder.RecorderObject
|
||||
@@ -277,7 +278,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
||||
handler.RouterOption(xchain.NewRouter(routerOpts...)),
|
||||
handler.AutherOption(auther),
|
||||
handler.AuthOption(auth_parser.Info(cfg.Handler.Auth)),
|
||||
handler.BypassOption(bypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
|
||||
handler.BypassOption(xbypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
|
||||
handler.TLSConfigOption(tlsConfig),
|
||||
handler.RateLimiterOption(registry.RateLimiterRegistry().Get(cfg.RLimiter)),
|
||||
handler.TrafficLimiterOption(registry.TrafficLimiterRegistry().Get(cfg.Handler.Limiter)),
|
||||
@@ -309,7 +310,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
||||
}
|
||||
|
||||
s := xservice.NewService(cfg.Name, ln, h,
|
||||
xservice.AdmissionOption(admission.AdmissionGroup(admissions...)),
|
||||
xservice.AdmissionOption(xadmission.AdmissionGroup(admissions...)),
|
||||
xservice.PreUpOption(preUp),
|
||||
xservice.PreDownOption(preDown),
|
||||
xservice.PostUpOption(postUp),
|
||||
@@ -374,6 +375,7 @@ func parseForwarder(cfg *config.ForwarderConfig, log logger.Logger) (hop.Hop, er
|
||||
Bypass: node.Bypass,
|
||||
Bypasses: node.Bypasses,
|
||||
Filter: filter,
|
||||
Matcher: node.Matcher,
|
||||
HTTP: httpCfg,
|
||||
TLS: node.TLS,
|
||||
Metadata: node.Metadata,
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package direct
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type conn struct{}
|
||||
|
||||
func (c *conn) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *conn) Read(b []byte) (n int, err error) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
func (c *conn) Write(b []byte) (n int, err error) {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
|
||||
func (c *conn) LocalAddr() net.Addr {
|
||||
return &net.TCPAddr{}
|
||||
}
|
||||
|
||||
func (c *conn) RemoteAddr() net.Addr {
|
||||
return &net.TCPAddr{}
|
||||
}
|
||||
|
||||
func (c *conn) SetDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *conn) SetReadDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *conn) SetWriteDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package forward
|
||||
package direct
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -16,6 +16,7 @@ func init() {
|
||||
}
|
||||
|
||||
type directConnector struct {
|
||||
md metadata
|
||||
options connector.Options
|
||||
}
|
||||
|
||||
@@ -31,7 +32,7 @@ func NewConnector(opts ...connector.Option) connector.Connector {
|
||||
}
|
||||
|
||||
func (c *directConnector) Init(md md.Metadata) (err error) {
|
||||
return nil
|
||||
return c.parseMetadata(md)
|
||||
}
|
||||
|
||||
func (c *directConnector) Connect(ctx context.Context, _ net.Conn, network, address string, opts ...connector.ConnectOption) (net.Conn, error) {
|
||||
@@ -40,6 +41,10 @@ func (c *directConnector) Connect(ctx context.Context, _ net.Conn, network, addr
|
||||
opt(&cOpts)
|
||||
}
|
||||
|
||||
if c.md.action == "reject" {
|
||||
return &conn{}, nil
|
||||
}
|
||||
|
||||
conn, err := cOpts.Dialer.Dial(ctx, network, address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package direct
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
action string
|
||||
}
|
||||
|
||||
func (c *directConnector) parseMetadata(md mdata.Metadata) (err error) {
|
||||
c.md.action = strings.ToLower(mdutil.GetString(md, "action"))
|
||||
return
|
||||
}
|
||||
@@ -90,8 +90,9 @@ func (d *http3Dialer) Dial(ctx context.Context, addr string, opts ...dialer.Dial
|
||||
KeepAlivePeriod: d.md.keepAlivePeriod,
|
||||
HandshakeIdleTimeout: d.md.handshakeTimeout,
|
||||
MaxIdleTimeout: d.md.maxIdleTimeout,
|
||||
Versions: []quic.VersionNumber{
|
||||
Versions: []quic.Version{
|
||||
quic.Version1,
|
||||
quic.Version2,
|
||||
},
|
||||
MaxIncomingStreams: int64(d.md.maxStreams),
|
||||
},
|
||||
|
||||
@@ -127,7 +127,7 @@ func (d *icmpDialer) initSession(ctx context.Context, addr net.Addr, conn net.Pa
|
||||
KeepAlivePeriod: d.md.keepAlivePeriod,
|
||||
HandshakeIdleTimeout: d.md.handshakeTimeout,
|
||||
MaxIdleTimeout: d.md.maxIdleTimeout,
|
||||
Versions: []quic.VersionNumber{
|
||||
Versions: []quic.Version{
|
||||
quic.Version1,
|
||||
quic.Version2,
|
||||
},
|
||||
|
||||
@@ -106,7 +106,7 @@ func (d *quicDialer) initSession(ctx context.Context, addr net.Addr, conn net.Pa
|
||||
KeepAlivePeriod: d.md.keepAlivePeriod,
|
||||
HandshakeIdleTimeout: d.md.handshakeTimeout,
|
||||
MaxIdleTimeout: d.md.maxIdleTimeout,
|
||||
Versions: []quic.VersionNumber{
|
||||
Versions: []quic.Version{
|
||||
quic.Version1,
|
||||
quic.Version2,
|
||||
},
|
||||
|
||||
@@ -7,9 +7,9 @@ toolchain go1.22.2
|
||||
require (
|
||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137
|
||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
|
||||
github.com/gin-contrib/cors v1.6.0
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/go-gost/core v0.1.7
|
||||
github.com/gin-contrib/cors v1.7.2
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/go-gost/core v0.1.11
|
||||
github.com/go-gost/gosocks4 v0.0.1
|
||||
github.com/go-gost/gosocks5 v0.4.2
|
||||
github.com/go-gost/plugin v0.1.1
|
||||
@@ -19,7 +19,7 @@ require (
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/golang/snappy v0.0.4
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/websocket v1.5.1
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/miekg/dns v1.1.61
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
@@ -33,21 +33,25 @@ require (
|
||||
github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8
|
||||
github.com/spf13/viper v1.18.2
|
||||
github.com/spf13/viper v1.19.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/vishvananda/netlink v1.1.0
|
||||
github.com/vishvananda/netns v0.0.4
|
||||
github.com/vulcand/predicate v1.2.0
|
||||
github.com/xtaci/kcp-go/v5 v5.6.5
|
||||
github.com/xtaci/smux v1.5.24
|
||||
github.com/xtaci/smux v1.5.31
|
||||
github.com/xtaci/tcpraw v1.2.25
|
||||
github.com/yl2chen/cidranger v1.0.2
|
||||
github.com/zalando/go-keyring v0.2.4
|
||||
golang.org/x/crypto v0.25.0
|
||||
golang.org/x/net v0.27.0
|
||||
golang.org/x/sys v0.22.0
|
||||
golang.org/x/crypto v0.27.0
|
||||
golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8
|
||||
golang.org/x/net v0.29.0
|
||||
golang.org/x/sys v0.25.0
|
||||
golang.org/x/text v0.18.0
|
||||
golang.org/x/time v0.5.0
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220703234212-c31a7b1ab478
|
||||
google.golang.org/grpc v1.65.0
|
||||
google.golang.org/protobuf v1.34.2
|
||||
google.golang.org/grpc v1.67.1
|
||||
google.golang.org/protobuf v1.35.1
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
@@ -56,25 +60,29 @@ require (
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
|
||||
github.com/alessio/shellescape v1.4.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bytedance/sonic v1.11.2 // indirect
|
||||
github.com/bytedance/sonic v1.11.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
||||
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/coreos/go-iptables v0.5.0 // indirect
|
||||
github.com/danieljoos/wincred v1.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.19.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||
github.com/google/gopacket v1.1.19 // indirect
|
||||
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba // indirect
|
||||
github.com/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/klauspost/reedsolomon v1.11.8 // indirect
|
||||
@@ -85,11 +93,12 @@ require (
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.19.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pion/logging v0.2.2 // indirect
|
||||
github.com/pion/transport/v2 v2.0.2 // indirect
|
||||
github.com/pion/udp/v2 v2.0.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/common v0.48.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
@@ -109,13 +118,12 @@ require (
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
go.uber.org/mock v0.4.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/arch v0.7.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/mod v0.19.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/term v0.24.0 // indirect
|
||||
golang.org/x/tools v0.23.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
)
|
||||
|
||||
@@ -10,21 +10,18 @@ github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl
|
||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
|
||||
github.com/bytedance/sonic v1.11.2 h1:ywfwo0a/3j9HR8wsYGWsIWl2mvRsI950HyoxiBERw5A=
|
||||
github.com/bytedance/sonic v1.11.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
|
||||
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
|
||||
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
||||
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
|
||||
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/coreos/go-iptables v0.5.0 h1:mw6SAibtHKZcNzAsOxjoHIG0gy5YFHhypWSSNc6EjbQ=
|
||||
github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
|
||||
@@ -37,6 +34,7 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
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=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
|
||||
@@ -47,14 +45,14 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/gin-contrib/cors v1.6.0 h1:0Z7D/bVhE6ja07lI8CTjTonp6SB07o8bNuFyRbsBUQg=
|
||||
github.com/gin-contrib/cors v1.6.0/go.mod h1:cI+h6iOAyxKRtUtC6iF/Si1KSFvGm/gK+kshxlCi8ro=
|
||||
github.com/gin-contrib/cors v1.7.2 h1:oLDHxdg8W/XDoN/8zamqk/Drgt4oVZDvaV0YmvVICQw=
|
||||
github.com/gin-contrib/cors v1.7.2/go.mod h1:SUJVARKgQ40dmrzgXEVxj2m7Ig1v1qIboQkPDTQ9t2E=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||
github.com/go-gost/core v0.1.7 h1:EP2Ux2SZR1omV6362MfTZG+oZTuaAODGqc0M9c/ZdEI=
|
||||
github.com/go-gost/core v0.1.7/go.mod h1:WGI43jOka7FAsSAwi/fSMaqxdR+E339ycb4NBGlFr6A=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/go-gost/core v0.1.11 h1:7zmRx0stWrClUJM/IT/ltRQJC9wUc8f6TAj1fbBiSPs=
|
||||
github.com/go-gost/core v0.1.11/go.mod h1:WGI43jOka7FAsSAwi/fSMaqxdR+E339ycb4NBGlFr6A=
|
||||
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.4.2 h1:IianxHTkACPqCwiOAT3MHoMdSUl+SEPSRu1ikawC1Pc=
|
||||
@@ -73,8 +71,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4=
|
||||
github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
|
||||
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
@@ -111,10 +109,14 @@ github.com/google/pprof v0.0.0-20240528025155-186aa0362fba h1:ql1qNgCyOB7iAEk8JT
|
||||
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf h1:C1GPyPJrOlJlIrcaBBiBpDsqZena2Ks8spa5xZqr1XQ=
|
||||
github.com/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf/go.mod h1:zXqxTI6jXDdKnlf8s+nT+3c8LrwUEy3yNpO4XJL90lA=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
|
||||
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
@@ -154,8 +156,8 @@ github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
|
||||
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
|
||||
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pion/dtls/v2 v2.2.6 h1:yXMxKr0Skd+Ub6A8UqXTRLSywskx93ooMRHsQUtd+Z4=
|
||||
github.com/pion/dtls/v2 v2.2.6/go.mod h1:t8fWJCIquY5rlQZwA2yWxUS1+OCrAdXrhVKXB5oD/wY=
|
||||
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
||||
@@ -200,6 +202,7 @@ github.com/shadowsocks/go-shadowsocks2 v0.1.5 h1:PDSQv9y2S85Fl7VBeOMF9StzeXZyK1H
|
||||
github.com/shadowsocks/go-shadowsocks2 v0.1.5/go.mod h1:AGGpIoek4HRno4xzyFiAtLHkOpcoznZEkAccaI/rplM=
|
||||
github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601 h1:XU9hik0exChEmY92ALW4l9WnDodxLVS9yOSNh2SizaQ=
|
||||
github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601/go.mod h1:mttDPaeLm87u74HMrP+n2tugXvIKWcwff/cqSX0lehY=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8=
|
||||
@@ -212,21 +215,24 @@ github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
|
||||
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
|
||||
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/templexxx/cpu v0.1.0 h1:wVM+WIJP2nYaxVxqgHPD4wGA2aJ9rvrQRV8CvFzNb40=
|
||||
@@ -244,12 +250,14 @@ github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYp
|
||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
||||
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
||||
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||
github.com/vulcand/predicate v1.2.0 h1:uFsW1gcnnR7R+QTID+FVcs0sSYlIGntoGOTb3rQJt50=
|
||||
github.com/vulcand/predicate v1.2.0/go.mod h1:VipoNYXny6c8N381zGUWkjuuNHiRbeAZhE7Qm9c+2GA=
|
||||
github.com/xtaci/kcp-go/v5 v5.6.5 h1:oxGZNobj3OddrLzwdJYnR/waNgwrL98u02u0DWNHE3k=
|
||||
github.com/xtaci/kcp-go/v5 v5.6.5/go.mod h1:Qy3Zf2tWTdFdEs0E8JvhrX+39r5UDZoYac8anvud7/Q=
|
||||
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
|
||||
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
|
||||
github.com/xtaci/smux v1.5.24 h1:77emW9dtnOxxOQ5ltR+8BbsX1kzcOxQ5gB+aaV9hXOY=
|
||||
github.com/xtaci/smux v1.5.24/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
|
||||
github.com/xtaci/smux v1.5.31 h1:3ha7sHtH46h85Iv7MfQogxasuRt1KPRhoFB3S4rmHgU=
|
||||
github.com/xtaci/smux v1.5.31/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
|
||||
github.com/xtaci/tcpraw v1.2.25 h1:VDlqo0op17JeXBM6e2G9ocCNLOJcw9mZbobMbJjo0vk=
|
||||
github.com/xtaci/tcpraw v1.2.25/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=
|
||||
github.com/yl2chen/cidranger v1.0.2 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+SevawU=
|
||||
@@ -262,17 +270,18 @@ go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
|
||||
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 h1:LoYXNGAShUG3m/ehNk4iFctuhGX/+R1ZpfJ4/ia80JM=
|
||||
golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
|
||||
@@ -291,19 +300,20 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -317,22 +327,22 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
|
||||
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
||||
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
|
||||
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -356,22 +366,23 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
||||
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
||||
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
|
||||
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
||||
@@ -122,7 +122,7 @@ func (h *fileHandler) handleFunc(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
log.WithFields(map[string]any{
|
||||
"duration": time.Since(start),
|
||||
}).Infof("%s %s %d %d", r.Method, r.RequestURI, rw.statusCode, rw.contentLength)
|
||||
}).Infof("%s %s %s %d %d", r.Method, r.RequestURI, r.Proto, rw.statusCode, rw.contentLength)
|
||||
}()
|
||||
|
||||
if auther := h.options.Auther; auther != nil {
|
||||
|
||||
@@ -92,7 +92,10 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
|
||||
ro.ClientIP = conn.RemoteAddr().String()
|
||||
if clientAddr := ctxvalue.ClientAddrFromContext(ctx); clientAddr != "" {
|
||||
ro.ClientIP = string(clientAddr)
|
||||
} else {
|
||||
ctx = ctxvalue.ContextWithClientAddr(ctx, ctxvalue.ClientAddr(conn.RemoteAddr().String()))
|
||||
}
|
||||
|
||||
if h, _, _ := net.SplitHostPort(ro.ClientIP); h != "" {
|
||||
ro.ClientIP = h
|
||||
}
|
||||
@@ -100,11 +103,17 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
|
||||
log := h.options.Logger.WithFields(map[string]any{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"sid": ctxvalue.SidFromContext(ctx),
|
||||
"sid": ro.SID,
|
||||
"client": ro.ClientIP,
|
||||
})
|
||||
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
|
||||
network := "tcp"
|
||||
if _, ok := conn.(net.PacketConn); ok {
|
||||
network = "udp"
|
||||
}
|
||||
ro.Network = network
|
||||
|
||||
pStats := stats.Stats{}
|
||||
conn = stats_wrapper.WrapConn(conn, &pStats)
|
||||
|
||||
@@ -130,12 +139,6 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
|
||||
return rate_limiter.ErrRateLimit
|
||||
}
|
||||
|
||||
network := "tcp"
|
||||
if _, ok := conn.(net.PacketConn); ok {
|
||||
network = "udp"
|
||||
}
|
||||
ro.Network = network
|
||||
|
||||
var proto string
|
||||
if network == "tcp" && h.md.sniffing {
|
||||
if h.md.sniffingTimeout > 0 {
|
||||
@@ -157,14 +160,16 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
|
||||
return cc, err
|
||||
}
|
||||
sniffer := &forwarder.Sniffer{
|
||||
Recorder: h.recorder.Recorder,
|
||||
RecorderOptions: h.recorder.Options,
|
||||
Certificate: h.md.certificate,
|
||||
PrivateKey: h.md.privateKey,
|
||||
NegotiatedProtocol: h.md.alpn,
|
||||
CertPool: h.certPool,
|
||||
MitmBypass: h.md.mitmBypass,
|
||||
ReadTimeout: h.md.readTimeout,
|
||||
Websocket: h.md.sniffingWebsocket,
|
||||
WebsocketSampleRate: h.md.sniffingWebsocketSampleRate,
|
||||
Recorder: h.recorder.Recorder,
|
||||
RecorderOptions: h.recorder.Options,
|
||||
Certificate: h.md.certificate,
|
||||
PrivateKey: h.md.privateKey,
|
||||
NegotiatedProtocol: h.md.alpn,
|
||||
CertPool: h.certPool,
|
||||
MitmBypass: h.md.mitmBypass,
|
||||
ReadTimeout: h.md.readTimeout,
|
||||
}
|
||||
|
||||
conn = xnet.NewReadWriteConn(br, conn, conn)
|
||||
|
||||
@@ -13,11 +13,13 @@ import (
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
readTimeout time.Duration
|
||||
readTimeout time.Duration
|
||||
httpKeepalive bool
|
||||
|
||||
sniffing bool
|
||||
sniffingTimeout time.Duration
|
||||
httpKeepalive bool
|
||||
sniffing bool
|
||||
sniffingTimeout time.Duration
|
||||
sniffingWebsocket bool
|
||||
sniffingWebsocketSampleRate float64
|
||||
|
||||
certificate *x509.Certificate
|
||||
privateKey crypto.PrivateKey
|
||||
@@ -31,10 +33,12 @@ func (h *forwardHandler) parseMetadata(md mdata.Metadata) (err error) {
|
||||
h.md.readTimeout = 15 * time.Second
|
||||
}
|
||||
|
||||
h.md.httpKeepalive = mdutil.GetBool(md, "http.keepalive")
|
||||
|
||||
h.md.sniffing = mdutil.GetBool(md, "sniffing")
|
||||
h.md.sniffingTimeout = mdutil.GetDuration(md, "sniffing.timeout")
|
||||
|
||||
h.md.httpKeepalive = mdutil.GetBool(md, "http.keepalive")
|
||||
h.md.sniffingWebsocket = mdutil.GetBool(md, "sniffing.websocket")
|
||||
h.md.sniffingWebsocketSampleRate = mdutil.GetFloat(md, "sniffing.websocket.sampleRate")
|
||||
|
||||
certFile := mdutil.GetString(md, "mitm.certFile", "mitm.caCertFile")
|
||||
keyFile := mdutil.GetString(md, "mitm.keyFile", "mitm.caKeyFile")
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/go-gost/x/internal/util/sniffing"
|
||||
tls_util "github.com/go-gost/x/internal/util/tls"
|
||||
rate_limiter "github.com/go-gost/x/limiter/rate"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
stats_wrapper "github.com/go-gost/x/observer/stats/wrapper"
|
||||
xrecorder "github.com/go-gost/x/recorder"
|
||||
"github.com/go-gost/x/registry"
|
||||
@@ -93,7 +94,10 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
|
||||
ro.ClientIP = conn.RemoteAddr().String()
|
||||
if clientAddr := ctxvalue.ClientAddrFromContext(ctx); clientAddr != "" {
|
||||
ro.ClientIP = string(clientAddr)
|
||||
} else {
|
||||
ctx = ctxvalue.ContextWithClientAddr(ctx, ctxvalue.ClientAddr(conn.RemoteAddr().String()))
|
||||
}
|
||||
|
||||
if h, _, _ := net.SplitHostPort(ro.ClientIP); h != "" {
|
||||
ro.ClientIP = h
|
||||
}
|
||||
@@ -101,11 +105,17 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
|
||||
log := h.options.Logger.WithFields(map[string]any{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"sid": ctxvalue.SidFromContext(ctx),
|
||||
"sid": ro.SID,
|
||||
"client": ro.ClientIP,
|
||||
})
|
||||
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
|
||||
network := "tcp"
|
||||
if _, ok := conn.(net.PacketConn); ok {
|
||||
network = "udp"
|
||||
}
|
||||
ro.Network = network
|
||||
|
||||
pStats := stats.Stats{}
|
||||
conn = stats_wrapper.WrapConn(conn, &pStats)
|
||||
|
||||
@@ -131,11 +141,12 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
|
||||
return rate_limiter.ErrRateLimit
|
||||
}
|
||||
|
||||
network := "tcp"
|
||||
if _, ok := conn.(net.PacketConn); ok {
|
||||
network = "udp"
|
||||
var host string
|
||||
if md, ok := conn.(mdata.Metadatable); ok {
|
||||
if v := mdutil.GetString(md.Metadata(), "host"); v != "" {
|
||||
host = v
|
||||
}
|
||||
}
|
||||
ro.Network = network
|
||||
|
||||
var proto string
|
||||
if network == "tcp" && h.md.sniffing {
|
||||
@@ -158,14 +169,16 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
|
||||
return cc, err
|
||||
}
|
||||
sniffer := &forwarder.Sniffer{
|
||||
Recorder: h.recorder.Recorder,
|
||||
RecorderOptions: h.recorder.Options,
|
||||
Certificate: h.md.certificate,
|
||||
PrivateKey: h.md.privateKey,
|
||||
NegotiatedProtocol: h.md.alpn,
|
||||
CertPool: h.certPool,
|
||||
MitmBypass: h.md.mitmBypass,
|
||||
ReadTimeout: h.md.readTimeout,
|
||||
Websocket: h.md.sniffingWebsocket,
|
||||
WebsocketSampleRate: h.md.sniffingWebsocketSampleRate,
|
||||
Recorder: h.recorder.Recorder,
|
||||
RecorderOptions: h.recorder.Options,
|
||||
Certificate: h.md.certificate,
|
||||
PrivateKey: h.md.privateKey,
|
||||
NegotiatedProtocol: h.md.alpn,
|
||||
CertPool: h.certPool,
|
||||
MitmBypass: h.md.mitmBypass,
|
||||
ReadTimeout: h.md.readTimeout,
|
||||
}
|
||||
|
||||
conn = xnet.NewReadWriteConn(br, conn, conn)
|
||||
@@ -191,6 +204,11 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
|
||||
}
|
||||
|
||||
var target *chain.Node
|
||||
if host != "" {
|
||||
target = &chain.Node{
|
||||
Addr: host,
|
||||
}
|
||||
}
|
||||
if h.hop != nil {
|
||||
target = h.hop.Select(ctx,
|
||||
hop.ProtocolSelectOption(proto),
|
||||
|
||||
@@ -15,10 +15,12 @@ import (
|
||||
type metadata struct {
|
||||
readTimeout time.Duration
|
||||
proxyProtocol int
|
||||
httpKeepalive bool
|
||||
|
||||
sniffing bool
|
||||
sniffingTimeout time.Duration
|
||||
httpKeepalive bool
|
||||
sniffing bool
|
||||
sniffingTimeout time.Duration
|
||||
sniffingWebsocket bool
|
||||
sniffingWebsocketSampleRate float64
|
||||
|
||||
certificate *x509.Certificate
|
||||
privateKey crypto.PrivateKey
|
||||
@@ -35,6 +37,8 @@ func (h *forwardHandler) parseMetadata(md mdata.Metadata) (err error) {
|
||||
|
||||
h.md.sniffing = mdutil.GetBool(md, "sniffing")
|
||||
h.md.sniffingTimeout = mdutil.GetDuration(md, "sniffing.timeout")
|
||||
h.md.sniffingWebsocket = mdutil.GetBool(md, "sniffing.websocket")
|
||||
h.md.sniffingWebsocketSampleRate = mdutil.GetFloat(md, "sniffing.websocket.sampleRate")
|
||||
|
||||
h.md.httpKeepalive = mdutil.GetBool(md, "http.keepalive")
|
||||
|
||||
|
||||
+340
-109
@@ -11,6 +11,7 @@ import (
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
@@ -29,17 +30,22 @@ import (
|
||||
"github.com/go-gost/core/recorder"
|
||||
xbypass "github.com/go-gost/x/bypass"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
ctx_internal "github.com/go-gost/x/internal/ctx"
|
||||
xio "github.com/go-gost/x/internal/io"
|
||||
xnet "github.com/go-gost/x/internal/net"
|
||||
xhttp "github.com/go-gost/x/internal/net/http"
|
||||
limiter_util "github.com/go-gost/x/internal/util/limiter"
|
||||
"github.com/go-gost/x/internal/util/sniffing"
|
||||
stats_util "github.com/go-gost/x/internal/util/stats"
|
||||
tls_util "github.com/go-gost/x/internal/util/tls"
|
||||
ws_util "github.com/go-gost/x/internal/util/ws"
|
||||
rate_limiter "github.com/go-gost/x/limiter/rate"
|
||||
traffic_wrapper "github.com/go-gost/x/limiter/traffic/wrapper"
|
||||
stats_wrapper "github.com/go-gost/x/observer/stats/wrapper"
|
||||
xrecorder "github.com/go-gost/x/recorder"
|
||||
"github.com/go-gost/x/registry"
|
||||
"golang.org/x/net/http/httpguts"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -47,13 +53,14 @@ func init() {
|
||||
}
|
||||
|
||||
type httpHandler struct {
|
||||
md metadata
|
||||
options handler.Options
|
||||
stats *stats_util.HandlerStats
|
||||
limiter traffic.TrafficLimiter
|
||||
cancel context.CancelFunc
|
||||
recorder recorder.RecorderObject
|
||||
certPool tls_util.CertPool
|
||||
md metadata
|
||||
options handler.Options
|
||||
stats *stats_util.HandlerStats
|
||||
limiter traffic.TrafficLimiter
|
||||
cancel context.CancelFunc
|
||||
recorder recorder.RecorderObject
|
||||
certPool tls_util.CertPool
|
||||
transport http.RoundTripper
|
||||
}
|
||||
|
||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||
@@ -81,7 +88,7 @@ func (h *httpHandler) Init(md md.Metadata) error {
|
||||
}
|
||||
|
||||
if limiter := h.options.Limiter; limiter != nil {
|
||||
h.limiter = limiter_util.NewCachedTrafficLimiter(limiter, 30*time.Second, 60*time.Second)
|
||||
h.limiter = limiter_util.NewCachedTrafficLimiter(limiter, h.md.limiterRefreshInterval, 60*time.Second)
|
||||
}
|
||||
|
||||
for _, ro := range h.options.Recorders {
|
||||
@@ -95,6 +102,14 @@ func (h *httpHandler) Init(md md.Metadata) error {
|
||||
h.certPool = tls_util.NewMemoryCertPool()
|
||||
}
|
||||
|
||||
h.transport = &http.Transport{
|
||||
DialContext: h.dial,
|
||||
IdleConnTimeout: 30 * time.Second,
|
||||
ResponseHeaderTimeout: h.md.readTimeout,
|
||||
DisableKeepAlives: !h.md.keepalive,
|
||||
DisableCompression: !h.md.compression,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -153,7 +168,8 @@ func (h *httpHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler
|
||||
return rate_limiter.ErrRateLimit
|
||||
}
|
||||
|
||||
req, err := http.ReadRequest(bufio.NewReader(conn))
|
||||
br := bufio.NewReader(conn)
|
||||
req, err := http.ReadRequest(br)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
@@ -164,6 +180,8 @@ func (h *httpHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler
|
||||
ro.ClientIP = clientIP.String()
|
||||
}
|
||||
|
||||
conn = xnet.NewReadWriteConn(br, conn, conn)
|
||||
|
||||
return h.handleRequest(ctx, conn, req, ro, log)
|
||||
}
|
||||
|
||||
@@ -205,7 +223,8 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
|
||||
ro.Host = addr
|
||||
|
||||
fields := map[string]any{
|
||||
"dst": addr,
|
||||
"dst": addr,
|
||||
"host": addr,
|
||||
}
|
||||
|
||||
if u, _, _ := h.basicProxyAuth(req.Header.Get("Proxy-Authorization")); u != "" {
|
||||
@@ -249,13 +268,26 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
|
||||
ro.HTTP.Response.Header = resp.Header
|
||||
}()
|
||||
|
||||
if req.Method == "PRI" ||
|
||||
(req.Method != http.MethodConnect && req.URL.Scheme != "http") {
|
||||
resp.StatusCode = http.StatusBadRequest
|
||||
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
dump, _ := httputil.DumpResponse(resp, false)
|
||||
log.Trace(string(dump))
|
||||
}
|
||||
|
||||
return resp.Write(conn)
|
||||
}
|
||||
|
||||
clientID, ok := h.authenticate(ctx, conn, req, resp, log)
|
||||
if !ok {
|
||||
return errors.New("authentication failed")
|
||||
}
|
||||
ctx = ctxvalue.ContextWithClientID(ctx, ctxvalue.ClientID(clientID))
|
||||
|
||||
if h.options.Bypass != nil && h.options.Bypass.Contains(ctx, network, addr) {
|
||||
if h.options.Bypass != nil &&
|
||||
h.options.Bypass.Contains(ctx, network, addr) {
|
||||
resp.StatusCode = http.StatusForbidden
|
||||
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
@@ -271,38 +303,6 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
|
||||
return h.handleUDP(ctx, conn, ro, log)
|
||||
}
|
||||
|
||||
if req.Method == "PRI" ||
|
||||
(req.Method != http.MethodConnect && req.URL.Scheme != "http") {
|
||||
resp.StatusCode = http.StatusBadRequest
|
||||
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
dump, _ := httputil.DumpResponse(resp, false)
|
||||
log.Trace(string(dump))
|
||||
}
|
||||
|
||||
return resp.Write(conn)
|
||||
}
|
||||
|
||||
switch h.md.hash {
|
||||
case "host":
|
||||
ctx = ctxvalue.ContextWithHash(ctx, &ctxvalue.Hash{Source: addr})
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
cc, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), network, addr)
|
||||
ro.Route = buf.String()
|
||||
if err != nil {
|
||||
resp.StatusCode = http.StatusServiceUnavailable
|
||||
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
dump, _ := httputil.DumpResponse(resp, false)
|
||||
log.Trace(string(dump))
|
||||
}
|
||||
resp.Write(conn)
|
||||
return err
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
{
|
||||
rw := traffic_wrapper.WrapReadWriter(
|
||||
h.limiter,
|
||||
@@ -327,12 +327,25 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
|
||||
}
|
||||
|
||||
if req.Method != http.MethodConnect {
|
||||
ro2 := &xrecorder.HandlerRecorderObject{}
|
||||
*ro2 = *ro
|
||||
ro.Time = time.Time{}
|
||||
return h.handleProxy(ctx, conn, cc, req, ro2, log)
|
||||
return h.handleProxy(ctx, conn, req, ro, log)
|
||||
}
|
||||
|
||||
ctx = ctx_internal.ContextWithRecorderObject(ctx, ro)
|
||||
ctx = ctxvalue.ContextWithLogger(ctx, log)
|
||||
cc, err := h.dial(ctx, "tcp", addr)
|
||||
|
||||
if err != nil {
|
||||
resp.StatusCode = http.StatusServiceUnavailable
|
||||
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
dump, _ := httputil.DumpResponse(resp, false)
|
||||
log.Trace(string(dump))
|
||||
}
|
||||
resp.Write(conn)
|
||||
return err
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
resp.StatusCode = http.StatusOK
|
||||
resp.Status = "200 Connection established"
|
||||
|
||||
@@ -365,14 +378,16 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
|
||||
return cc, nil
|
||||
}
|
||||
sniffer := &sniffing.Sniffer{
|
||||
Recorder: h.recorder.Recorder,
|
||||
RecorderOptions: h.recorder.Options,
|
||||
Certificate: h.md.certificate,
|
||||
PrivateKey: h.md.privateKey,
|
||||
NegotiatedProtocol: h.md.alpn,
|
||||
CertPool: h.certPool,
|
||||
MitmBypass: h.md.mitmBypass,
|
||||
ReadTimeout: h.md.readTimeout,
|
||||
Websocket: h.md.sniffingWebsocket,
|
||||
WebsocketSampleRate: h.md.sniffingWebsocketSampleRate,
|
||||
Recorder: h.recorder.Recorder,
|
||||
RecorderOptions: h.recorder.Options,
|
||||
Certificate: h.md.certificate,
|
||||
PrivateKey: h.md.privateKey,
|
||||
NegotiatedProtocol: h.md.alpn,
|
||||
CertPool: h.certPool,
|
||||
MitmBypass: h.md.mitmBypass,
|
||||
ReadTimeout: h.md.readTimeout,
|
||||
}
|
||||
|
||||
conn = xnet.NewReadWriteConn(br, conn, conn)
|
||||
@@ -404,13 +419,21 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *httpHandler) handleProxy(ctx context.Context, rw io.ReadWriter, cc net.Conn, req *http.Request, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
|
||||
if shouldClose, err := h.proxyRoundTrip(ctx, rw, cc, req, ro, log); err != nil || shouldClose {
|
||||
func (h *httpHandler) handleProxy(ctx context.Context, conn net.Conn, req *http.Request, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
|
||||
pStats := stats.Stats{}
|
||||
conn = stats_wrapper.WrapConn(conn, &pStats)
|
||||
|
||||
ro.Time = time.Time{}
|
||||
|
||||
if close, err := h.proxyRoundTrip(ctx, conn, req, ro, &pStats, log); err != nil || close {
|
||||
return err
|
||||
}
|
||||
|
||||
br := bufio.NewReader(conn)
|
||||
for {
|
||||
req, err := http.ReadRequest(bufio.NewReader(rw))
|
||||
pStats.Reset()
|
||||
|
||||
req, err := http.ReadRequest(br)
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) || errors.Is(err, net.ErrClosed) {
|
||||
return nil
|
||||
@@ -423,40 +446,49 @@ func (h *httpHandler) handleProxy(ctx context.Context, rw io.ReadWriter, cc net.
|
||||
log.Trace(string(dump))
|
||||
}
|
||||
|
||||
if shouldClose, err := h.proxyRoundTrip(ctx, rw, cc, req, ro, log); err != nil || shouldClose {
|
||||
if close, err := h.proxyRoundTrip(ctx, xio.NewReadWriter(br, conn), req, ro, &pStats, log); err != nil || close {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *httpHandler) proxyRoundTrip(ctx context.Context, rw io.ReadWriter, cc net.Conn, req *http.Request, ro *xrecorder.HandlerRecorderObject, log logger.Logger) (close bool, err error) {
|
||||
func (h *httpHandler) proxyRoundTrip(ctx context.Context, rw io.ReadWriter, req *http.Request, ro *xrecorder.HandlerRecorderObject, pStats *stats.Stats, log logger.Logger) (close bool, err error) {
|
||||
close = true
|
||||
|
||||
if req == nil {
|
||||
return
|
||||
}
|
||||
ro2 := &xrecorder.HandlerRecorderObject{}
|
||||
*ro2 = *ro
|
||||
ro = ro2
|
||||
|
||||
start := time.Now()
|
||||
ro.Time = start
|
||||
host := req.Host
|
||||
if _, port, _ := net.SplitHostPort(host); port == "" {
|
||||
host = net.JoinHostPort(strings.Trim(host, "[]"), "80")
|
||||
}
|
||||
ro.Host = host
|
||||
ro.Time = time.Now()
|
||||
|
||||
log = log.WithFields(map[string]any{
|
||||
"host": host,
|
||||
})
|
||||
|
||||
log.Infof("%s <-> %s", ro.RemoteAddr, req.Host)
|
||||
defer func() {
|
||||
ro.Duration = time.Since(start)
|
||||
if err != nil {
|
||||
ro.Err = err.Error()
|
||||
}
|
||||
ro.InputBytes = pStats.Get(stats.KindInputBytes)
|
||||
ro.OutputBytes = pStats.Get(stats.KindOutputBytes)
|
||||
ro.Duration = time.Since(ro.Time)
|
||||
if err := ro.Record(ctx, h.recorder.Recorder); err != nil {
|
||||
log.Errorf("record: %v", err)
|
||||
}
|
||||
|
||||
log.WithFields(map[string]any{
|
||||
"duration": time.Since(start),
|
||||
"duration": time.Since(ro.Time),
|
||||
"inputBytes": ro.InputBytes,
|
||||
"outputBytes": ro.OutputBytes,
|
||||
}).Infof("%s >-< %s", ro.RemoteAddr, req.Host)
|
||||
}()
|
||||
|
||||
if clientIP := xhttp.GetClientIP(req); clientIP != nil {
|
||||
ro.ClientIP = clientIP.String()
|
||||
}
|
||||
ro.HTTP = &xrecorder.HTTPRecorderObject{
|
||||
Host: req.Host,
|
||||
Proto: req.Proto,
|
||||
@@ -468,9 +500,20 @@ func (h *httpHandler) proxyRoundTrip(ctx context.Context, rw io.ReadWriter, cc n
|
||||
Header: req.Header.Clone(),
|
||||
},
|
||||
}
|
||||
if clientIP := xhttp.GetClientIP(req); clientIP != nil {
|
||||
ro.ClientIP = clientIP.String()
|
||||
}
|
||||
|
||||
clientAddr := ro.RemoteAddr
|
||||
if ro.ClientIP != "" {
|
||||
if _, port, _ := net.SplitHostPort(ro.RemoteAddr); port != "" {
|
||||
clientAddr = net.JoinHostPort(ro.ClientIP, port)
|
||||
}
|
||||
}
|
||||
|
||||
// HTTP/1.0
|
||||
if req.ProtoMajor == 1 && req.ProtoMinor == 0 {
|
||||
http10 := req.ProtoMajor == 1 && req.ProtoMinor == 0
|
||||
if http10 {
|
||||
if strings.ToLower(req.Header.Get("Connection")) == "keep-alive" {
|
||||
req.Header.Del("Connection")
|
||||
} else {
|
||||
@@ -478,27 +521,11 @@ func (h *httpHandler) proxyRoundTrip(ctx context.Context, rw io.ReadWriter, cc n
|
||||
}
|
||||
}
|
||||
|
||||
if !h.md.keepalive {
|
||||
req.Header.Set("Connection", "close")
|
||||
}
|
||||
|
||||
req.Header.Del("Proxy-Authorization")
|
||||
req.Header.Del("Proxy-Connection")
|
||||
req.Header.Del("Gost-Target")
|
||||
req.Header.Del("X-Gost-Target")
|
||||
|
||||
var reqBody *xhttp.Body
|
||||
if opts := h.recorder.Options; opts != nil && opts.HTTPBody {
|
||||
if req.Body != nil {
|
||||
maxSize := opts.MaxBodySize
|
||||
if maxSize <= 0 {
|
||||
maxSize = sniffing.DefaultBodySize
|
||||
}
|
||||
reqBody = xhttp.NewBody(req.Body, maxSize)
|
||||
req.Body = reqBody
|
||||
}
|
||||
}
|
||||
|
||||
res := &http.Response{
|
||||
ProtoMajor: req.ProtoMajor,
|
||||
ProtoMinor: req.ProtoMinor,
|
||||
@@ -507,25 +534,51 @@ func (h *httpHandler) proxyRoundTrip(ctx context.Context, rw io.ReadWriter, cc n
|
||||
}
|
||||
ro.HTTP.StatusCode = res.StatusCode
|
||||
|
||||
if err = req.Write(cc); err != nil {
|
||||
if h.options.Bypass != nil &&
|
||||
h.options.Bypass.Contains(ctx, "tcp", host) {
|
||||
res.StatusCode = http.StatusForbidden
|
||||
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
dump, _ := httputil.DumpResponse(res, false)
|
||||
log.Trace(string(dump))
|
||||
}
|
||||
log.Debug("bypass: ", host)
|
||||
res.Write(rw)
|
||||
err = xbypass.ErrBypass
|
||||
return
|
||||
}
|
||||
|
||||
var reqBody *xhttp.Body
|
||||
if opts := h.recorder.Options; opts != nil && opts.HTTPBody {
|
||||
if req.Body != nil {
|
||||
bodySize := opts.MaxBodySize
|
||||
if bodySize <= 0 {
|
||||
bodySize = sniffing.DefaultBodySize
|
||||
}
|
||||
if bodySize > sniffing.MaxBodySize {
|
||||
bodySize = sniffing.MaxBodySize
|
||||
}
|
||||
reqBody = xhttp.NewBody(req.Body, bodySize)
|
||||
req.Body = reqBody
|
||||
}
|
||||
}
|
||||
|
||||
ctx = ctxvalue.ContextWithClientAddr(ctx, ctxvalue.ClientAddr(clientAddr))
|
||||
ctx = ctx_internal.ContextWithRecorderObject(ctx, ro)
|
||||
ctx = ctxvalue.ContextWithLogger(ctx, log)
|
||||
|
||||
resp, err := h.transport.RoundTrip(req.WithContext(ctx))
|
||||
|
||||
if reqBody != nil {
|
||||
ro.HTTP.Request.Body = reqBody.Content()
|
||||
ro.HTTP.Request.ContentLength = reqBody.Length()
|
||||
}
|
||||
|
||||
cc.SetReadDeadline(time.Now().Add(h.md.readTimeout))
|
||||
resp, err := http.ReadResponse(bufio.NewReader(cc), req)
|
||||
if err != nil {
|
||||
log.Errorf("read response: %v", err)
|
||||
res.Write(rw)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
cc.SetReadDeadline(time.Time{})
|
||||
|
||||
ro.HTTP.StatusCode = resp.StatusCode
|
||||
ro.HTTP.Response.Header = resp.Header.Clone()
|
||||
@@ -537,35 +590,217 @@ func (h *httpHandler) proxyRoundTrip(ctx context.Context, rw io.ReadWriter, cc n
|
||||
}
|
||||
|
||||
// HTTP/1.0
|
||||
if req.ProtoMajor == 1 && req.ProtoMinor == 0 {
|
||||
if http10 {
|
||||
if !resp.Close {
|
||||
resp.Header.Set("Connection", "keep-alive")
|
||||
}
|
||||
resp.ProtoMajor = req.ProtoMajor
|
||||
resp.ProtoMinor = req.ProtoMinor
|
||||
resp.ProtoMajor = 1
|
||||
resp.ProtoMinor = 0
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusSwitchingProtocols {
|
||||
err = h.handleUpgradeResponse(ctx, rw, req, resp, ro, log)
|
||||
return
|
||||
}
|
||||
|
||||
var respBody *xhttp.Body
|
||||
if opts := h.recorder.Options; opts != nil && opts.HTTPBody {
|
||||
maxSize := opts.MaxBodySize
|
||||
if maxSize <= 0 {
|
||||
maxSize = sniffing.DefaultBodySize
|
||||
bodySize := opts.MaxBodySize
|
||||
if bodySize <= 0 {
|
||||
bodySize = sniffing.DefaultBodySize
|
||||
}
|
||||
respBody = xhttp.NewBody(resp.Body, maxSize)
|
||||
if bodySize > sniffing.MaxBodySize {
|
||||
bodySize = sniffing.MaxBodySize
|
||||
}
|
||||
respBody = xhttp.NewBody(resp.Body, bodySize)
|
||||
resp.Body = respBody
|
||||
}
|
||||
|
||||
if err = resp.Write(rw); err != nil {
|
||||
log.Errorf("write response: %v", err)
|
||||
return
|
||||
}
|
||||
err = resp.Write(rw)
|
||||
|
||||
if respBody != nil {
|
||||
ro.HTTP.Response.Body = respBody.Content()
|
||||
ro.HTTP.Response.ContentLength = respBody.Length()
|
||||
}
|
||||
|
||||
return resp.Close, nil
|
||||
if err != nil {
|
||||
err = fmt.Errorf("write response: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if resp.ContentLength >= 0 {
|
||||
close = resp.Close
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (h *httpHandler) dial(ctx context.Context, network, addr string) (conn net.Conn, err error) {
|
||||
switch h.md.hash {
|
||||
case "host":
|
||||
ctx = ctxvalue.ContextWithHash(ctx, &ctxvalue.Hash{Source: addr})
|
||||
}
|
||||
|
||||
if log := ctxvalue.LoggerFromContext(ctx); log != nil {
|
||||
log.Debugf("dial: new connection to host %s", addr)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
conn, err = h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), network, addr)
|
||||
if ro := ctx_internal.RecorderObjectFromContext(ctx); ro != nil {
|
||||
ro.Route = buf.String()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func upgradeType(h http.Header) string {
|
||||
if !httpguts.HeaderValuesContainsToken(h["Connection"], "Upgrade") {
|
||||
return ""
|
||||
}
|
||||
return h.Get("Upgrade")
|
||||
}
|
||||
|
||||
func (h *httpHandler) handleUpgradeResponse(ctx context.Context, rw io.ReadWriter, req *http.Request, res *http.Response, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
|
||||
reqUpType := upgradeType(req.Header)
|
||||
resUpType := upgradeType(res.Header)
|
||||
if !strings.EqualFold(reqUpType, resUpType) {
|
||||
return fmt.Errorf("backend tried to switch protocol %q when %q was requested", resUpType, reqUpType)
|
||||
}
|
||||
|
||||
backConn, ok := res.Body.(io.ReadWriteCloser)
|
||||
if !ok {
|
||||
return fmt.Errorf("internal error: 101 switching protocols response with non-writable body")
|
||||
}
|
||||
|
||||
res.Body = nil
|
||||
if err := res.Write(rw); err != nil {
|
||||
return fmt.Errorf("response write: %v", err)
|
||||
}
|
||||
|
||||
if reqUpType == "websocket" && h.md.sniffingWebsocket {
|
||||
return h.sniffingWebsocketFrame(ctx, rw, backConn, ro, log)
|
||||
}
|
||||
|
||||
return xnet.Transport(rw, backConn)
|
||||
}
|
||||
|
||||
func (h *httpHandler) sniffingWebsocketFrame(ctx context.Context, rw, cc io.ReadWriter, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
|
||||
errc := make(chan error, 1)
|
||||
|
||||
sampleRate := h.md.sniffingWebsocketSampleRate
|
||||
if sampleRate == 0 {
|
||||
sampleRate = sniffing.DefaultSampleRate
|
||||
}
|
||||
if sampleRate < 0 {
|
||||
sampleRate = math.MaxFloat64
|
||||
}
|
||||
|
||||
go func() {
|
||||
ro2 := &xrecorder.HandlerRecorderObject{}
|
||||
*ro2 = *ro
|
||||
ro := ro2
|
||||
|
||||
limiter := rate.NewLimiter(rate.Limit(sampleRate), int(sampleRate))
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
for {
|
||||
start := time.Now()
|
||||
|
||||
if err := h.copyWebsocketFrame(cc, rw, buf, "client", ro); err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
|
||||
if limiter.Allow() {
|
||||
ro.Duration = time.Since(start)
|
||||
ro.Time = time.Now()
|
||||
if err := ro.Record(ctx, h.recorder.Recorder); err != nil {
|
||||
log.Errorf("record: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
ro2 := &xrecorder.HandlerRecorderObject{}
|
||||
*ro2 = *ro
|
||||
ro := ro2
|
||||
|
||||
limiter := rate.NewLimiter(rate.Limit(sampleRate), int(sampleRate))
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
for {
|
||||
start := time.Now()
|
||||
|
||||
if err := h.copyWebsocketFrame(rw, cc, buf, "server", ro); err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
|
||||
if limiter.Allow() {
|
||||
ro.Duration = time.Since(start)
|
||||
ro.Time = time.Now()
|
||||
if err := ro.Record(ctx, h.recorder.Recorder); err != nil {
|
||||
log.Errorf("record: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
<-errc
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *httpHandler) copyWebsocketFrame(w io.Writer, r io.Reader, buf *bytes.Buffer, from string, ro *xrecorder.HandlerRecorderObject) (err error) {
|
||||
fr := ws_util.Frame{}
|
||||
if _, err = fr.ReadFrom(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ws := &xrecorder.WebsocketRecorderObject{
|
||||
From: from,
|
||||
Fin: fr.Header.Fin,
|
||||
Rsv1: fr.Header.Rsv1,
|
||||
Rsv2: fr.Header.Rsv2,
|
||||
Rsv3: fr.Header.Rsv3,
|
||||
OpCode: int(fr.Header.OpCode),
|
||||
Masked: fr.Header.Masked,
|
||||
MaskKey: fr.Header.MaskKey,
|
||||
Length: fr.Header.PayloadLength,
|
||||
}
|
||||
if opts := h.recorder.Options; opts != nil && opts.HTTPBody {
|
||||
bodySize := opts.MaxBodySize
|
||||
if bodySize <= 0 {
|
||||
bodySize = sniffing.DefaultBodySize
|
||||
}
|
||||
if bodySize > sniffing.MaxBodySize {
|
||||
bodySize = sniffing.MaxBodySize
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
if _, err := io.Copy(buf, io.LimitReader(fr.Data, int64(bodySize))); err != nil {
|
||||
return err
|
||||
}
|
||||
ws.Payload = buf.Bytes()
|
||||
}
|
||||
|
||||
ro.Websocket = ws
|
||||
length := uint64(fr.Header.Length()) + uint64(fr.Header.PayloadLength)
|
||||
if from == "client" {
|
||||
ro.InputBytes = length
|
||||
ro.OutputBytes = 0
|
||||
} else {
|
||||
ro.InputBytes = 0
|
||||
ro.OutputBytes = length
|
||||
}
|
||||
|
||||
fr.Data = io.MultiReader(bytes.NewReader(buf.Bytes()), fr.Data)
|
||||
if _, err := fr.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *httpHandler) decodeServerName(s string) (string, error) {
|
||||
@@ -714,11 +949,7 @@ func (h *httpHandler) observeStats(ctx context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
d := h.md.observePeriod
|
||||
if d < time.Millisecond {
|
||||
d = 5 * time.Second
|
||||
}
|
||||
ticker := time.NewTicker(d)
|
||||
ticker := time.NewTicker(h.md.observePeriod)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
|
||||
+33
-12
@@ -20,18 +20,22 @@ const (
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
readTimeout time.Duration
|
||||
keepalive bool
|
||||
probeResistance *probeResistance
|
||||
enableUDP bool
|
||||
header http.Header
|
||||
hash string
|
||||
authBasicRealm string
|
||||
observePeriod time.Duration
|
||||
proxyAgent string
|
||||
readTimeout time.Duration
|
||||
keepalive bool
|
||||
compression bool
|
||||
probeResistance *probeResistance
|
||||
enableUDP bool
|
||||
header http.Header
|
||||
hash string
|
||||
authBasicRealm string
|
||||
proxyAgent string
|
||||
observePeriod time.Duration
|
||||
limiterRefreshInterval time.Duration
|
||||
|
||||
sniffing bool
|
||||
sniffingTimeout time.Duration
|
||||
sniffing bool
|
||||
sniffingTimeout time.Duration
|
||||
sniffingWebsocket bool
|
||||
sniffingWebsocketSampleRate float64
|
||||
|
||||
certificate *x509.Certificate
|
||||
privateKey crypto.PrivateKey
|
||||
@@ -54,6 +58,7 @@ func (h *httpHandler) parseMetadata(md mdata.Metadata) error {
|
||||
}
|
||||
|
||||
h.md.keepalive = mdutil.GetBool(md, "http.keepalive", "keepalive")
|
||||
h.md.compression = mdutil.GetBool(md, "http.compression", "compression")
|
||||
|
||||
if pr := mdutil.GetString(md, "probeResist", "probe_resist"); pr != "" {
|
||||
if ss := strings.SplitN(pr, ":", 2); len(ss) == 2 {
|
||||
@@ -68,7 +73,21 @@ func (h *httpHandler) parseMetadata(md mdata.Metadata) error {
|
||||
h.md.hash = mdutil.GetString(md, "hash")
|
||||
h.md.authBasicRealm = mdutil.GetString(md, "authBasicRealm")
|
||||
|
||||
h.md.observePeriod = mdutil.GetDuration(md, "observePeriod")
|
||||
h.md.observePeriod = mdutil.GetDuration(md, "observePeriod", "observer.observePeriod")
|
||||
if h.md.observePeriod == 0 {
|
||||
h.md.observePeriod = 5 * time.Second
|
||||
}
|
||||
if h.md.observePeriod < time.Second {
|
||||
h.md.observePeriod = time.Second
|
||||
}
|
||||
|
||||
h.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if h.md.limiterRefreshInterval == 0 {
|
||||
h.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if h.md.limiterRefreshInterval < time.Second {
|
||||
h.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
|
||||
h.md.proxyAgent = mdutil.GetString(md, "http.proxyAgent", "proxyAgent")
|
||||
if h.md.proxyAgent == "" {
|
||||
@@ -77,6 +96,8 @@ func (h *httpHandler) parseMetadata(md mdata.Metadata) error {
|
||||
|
||||
h.md.sniffing = mdutil.GetBool(md, "sniffing")
|
||||
h.md.sniffingTimeout = mdutil.GetDuration(md, "sniffing.timeout")
|
||||
h.md.sniffingWebsocket = mdutil.GetBool(md, "sniffing.websocket")
|
||||
h.md.sniffingWebsocketSampleRate = mdutil.GetFloat(md, "sniffing.websocket.sampleRate")
|
||||
|
||||
certFile := mdutil.GetString(md, "mitm.certFile", "mitm.caCertFile")
|
||||
keyFile := mdutil.GetString(md, "mitm.keyFile", "mitm.caKeyFile")
|
||||
|
||||
@@ -77,7 +77,7 @@ func (h *http2Handler) Init(md md.Metadata) error {
|
||||
}
|
||||
|
||||
if limiter := h.options.Limiter; limiter != nil {
|
||||
h.limiter = limiter_util.NewCachedTrafficLimiter(limiter, 30*time.Second, 60*time.Second)
|
||||
h.limiter = limiter_util.NewCachedTrafficLimiter(limiter, h.md.limiterRefreshInterval, 60*time.Second)
|
||||
}
|
||||
|
||||
for _, ro := range h.options.Recorders {
|
||||
@@ -178,7 +178,8 @@ func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req
|
||||
ro.Host = host
|
||||
|
||||
fields := map[string]any{
|
||||
"dst": host,
|
||||
"dst": host,
|
||||
"host": host,
|
||||
}
|
||||
if u, _, _ := h.basicProxyAuth(req.Header.Get("Proxy-Authorization")); u != "" {
|
||||
fields["user"] = u
|
||||
@@ -486,11 +487,7 @@ func (h *http2Handler) observeStats(ctx context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
d := h.md.observePeriod
|
||||
if d < time.Millisecond {
|
||||
d = 5 * time.Second
|
||||
}
|
||||
ticker := time.NewTicker(d)
|
||||
ticker := time.NewTicker(h.md.observePeriod)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
|
||||
@@ -14,11 +14,12 @@ const (
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
probeResistance *probeResistance
|
||||
header http.Header
|
||||
hash string
|
||||
authBasicRealm string
|
||||
observePeriod time.Duration
|
||||
probeResistance *probeResistance
|
||||
header http.Header
|
||||
hash string
|
||||
authBasicRealm string
|
||||
observePeriod time.Duration
|
||||
limiterRefreshInterval time.Duration
|
||||
}
|
||||
|
||||
func (h *http2Handler) parseMetadata(md mdata.Metadata) error {
|
||||
@@ -42,7 +43,21 @@ func (h *http2Handler) parseMetadata(md mdata.Metadata) error {
|
||||
h.md.hash = mdutil.GetString(md, "hash")
|
||||
h.md.authBasicRealm = mdutil.GetString(md, "authBasicRealm")
|
||||
|
||||
h.md.observePeriod = mdutil.GetDuration(md, "observePeriod")
|
||||
h.md.observePeriod = mdutil.GetDuration(md, "observePeriod", "observer.observePeriod")
|
||||
if h.md.observePeriod == 0 {
|
||||
h.md.observePeriod = 5 * time.Second
|
||||
}
|
||||
if h.md.observePeriod < time.Second {
|
||||
h.md.observePeriod = time.Second
|
||||
}
|
||||
|
||||
h.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if h.md.limiterRefreshInterval == 0 {
|
||||
h.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if h.md.limiterRefreshInterval < time.Second {
|
||||
h.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -124,7 +124,8 @@ func (h *http3Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req
|
||||
}
|
||||
|
||||
log = log.WithFields(map[string]any{
|
||||
"dst": fmt.Sprintf("%s/%s", target.Addr, "tcp"),
|
||||
"dst": fmt.Sprintf("%s/%s", target.Addr, "tcp"),
|
||||
"host": target.Addr,
|
||||
})
|
||||
|
||||
log.Debugf("%s >> %s", req.RemoteAddr, addr)
|
||||
|
||||
@@ -128,9 +128,11 @@ func (h *redirectHandler) Handle(ctx context.Context, conn net.Conn, opts ...han
|
||||
}
|
||||
|
||||
ro.Host = dstAddr.String()
|
||||
ro.Dst = dstAddr.String()
|
||||
|
||||
log = log.WithFields(map[string]any{
|
||||
"dst": fmt.Sprintf("%s/%s", dstAddr, dstAddr.Network()),
|
||||
"dst": fmt.Sprintf("%s/%s", dstAddr, dstAddr.Network()),
|
||||
"host": dstAddr.String(),
|
||||
})
|
||||
|
||||
if h.md.sniffing {
|
||||
@@ -183,14 +185,16 @@ func (h *redirectHandler) Handle(ctx context.Context, conn net.Conn, opts ...han
|
||||
}
|
||||
|
||||
sniffer := &sniffing.Sniffer{
|
||||
Recorder: h.recorder.Recorder,
|
||||
RecorderOptions: h.recorder.Options,
|
||||
Certificate: h.md.certificate,
|
||||
PrivateKey: h.md.privateKey,
|
||||
NegotiatedProtocol: h.md.alpn,
|
||||
CertPool: h.certPool,
|
||||
MitmBypass: h.md.mitmBypass,
|
||||
ReadTimeout: h.md.readTimeout,
|
||||
Websocket: h.md.sniffingWebsocket,
|
||||
WebsocketSampleRate: h.md.sniffingWebsocketSampleRate,
|
||||
Recorder: h.recorder.Recorder,
|
||||
RecorderOptions: h.recorder.Options,
|
||||
Certificate: h.md.certificate,
|
||||
PrivateKey: h.md.privateKey,
|
||||
NegotiatedProtocol: h.md.alpn,
|
||||
CertPool: h.certPool,
|
||||
MitmBypass: h.md.mitmBypass,
|
||||
ReadTimeout: h.md.readTimeout,
|
||||
}
|
||||
|
||||
conn = xnet.NewReadWriteConn(br, conn, conn)
|
||||
|
||||
@@ -16,9 +16,11 @@ type metadata struct {
|
||||
readTimeout time.Duration
|
||||
tproxy bool
|
||||
|
||||
sniffing bool
|
||||
sniffingTimeout time.Duration
|
||||
sniffingFallback bool
|
||||
sniffing bool
|
||||
sniffingTimeout time.Duration
|
||||
sniffingFallback bool
|
||||
sniffingWebsocket bool
|
||||
sniffingWebsocketSampleRate float64
|
||||
|
||||
certificate *x509.Certificate
|
||||
privateKey crypto.PrivateKey
|
||||
@@ -36,6 +38,8 @@ func (h *redirectHandler) parseMetadata(md mdata.Metadata) (err error) {
|
||||
h.md.sniffing = mdutil.GetBool(md, "sniffing")
|
||||
h.md.sniffingTimeout = mdutil.GetDuration(md, "sniffing.timeout")
|
||||
h.md.sniffingFallback = mdutil.GetBool(md, "sniffing.fallback")
|
||||
h.md.sniffingWebsocket = mdutil.GetBool(md, "sniffing.websocket")
|
||||
h.md.sniffingWebsocketSampleRate = mdutil.GetFloat(md, "sniffing.websocket.sampleRate")
|
||||
|
||||
certFile := mdutil.GetString(md, "mitm.certFile", "mitm.caCertFile")
|
||||
keyFile := mdutil.GetString(md, "mitm.keyFile", "mitm.caKeyFile")
|
||||
|
||||
@@ -108,7 +108,8 @@ func (h *redirectHandler) Handle(ctx context.Context, conn net.Conn, opts ...han
|
||||
ro.Host = dstAddr.String()
|
||||
|
||||
log = log.WithFields(map[string]any{
|
||||
"dst": fmt.Sprintf("%s/%s", dstAddr, dstAddr.Network()),
|
||||
"dst": fmt.Sprintf("%s/%s", dstAddr, dstAddr.Network()),
|
||||
"host": dstAddr.String(),
|
||||
})
|
||||
|
||||
log.Debugf("%s >> %s", conn.RemoteAddr(), dstAddr)
|
||||
|
||||
+13
-10
@@ -33,8 +33,9 @@ func (h *relayHandler) handleConnect(ctx context.Context, conn net.Conn, network
|
||||
}
|
||||
|
||||
log = log.WithFields(map[string]any{
|
||||
"dst": fmt.Sprintf("%s/%s", address, network),
|
||||
"cmd": "connect",
|
||||
"dst": fmt.Sprintf("%s/%s", address, network),
|
||||
"cmd": "connect",
|
||||
"host": address,
|
||||
})
|
||||
|
||||
log.Debugf("%s >> %s/%s", conn.RemoteAddr(), address, network)
|
||||
@@ -165,14 +166,16 @@ func (h *relayHandler) handleConnect(ctx context.Context, conn net.Conn, network
|
||||
return cc, nil
|
||||
}
|
||||
sniffer := &sniffing.Sniffer{
|
||||
Recorder: h.recorder.Recorder,
|
||||
RecorderOptions: h.recorder.Options,
|
||||
Certificate: h.md.certificate,
|
||||
PrivateKey: h.md.privateKey,
|
||||
NegotiatedProtocol: h.md.alpn,
|
||||
CertPool: h.certPool,
|
||||
MitmBypass: h.md.mitmBypass,
|
||||
ReadTimeout: h.md.readTimeout,
|
||||
Websocket: h.md.sniffingWebsocket,
|
||||
WebsocketSampleRate: h.md.sniffingWebsocketSampleRate,
|
||||
Recorder: h.recorder.Recorder,
|
||||
RecorderOptions: h.recorder.Options,
|
||||
Certificate: h.md.certificate,
|
||||
PrivateKey: h.md.privateKey,
|
||||
NegotiatedProtocol: h.md.alpn,
|
||||
CertPool: h.certPool,
|
||||
MitmBypass: h.md.mitmBypass,
|
||||
ReadTimeout: h.md.readTimeout,
|
||||
}
|
||||
|
||||
conn = xnet.NewReadWriteConn(br, conn, conn)
|
||||
|
||||
@@ -70,7 +70,7 @@ func (h *relayHandler) Init(md md.Metadata) (err error) {
|
||||
}
|
||||
|
||||
if limiter := h.options.Limiter; limiter != nil {
|
||||
h.limiter = limiter_util.NewCachedTrafficLimiter(limiter, 30*time.Second, 60*time.Second)
|
||||
h.limiter = limiter_util.NewCachedTrafficLimiter(limiter, h.md.limiterRefreshInterval, 60*time.Second)
|
||||
}
|
||||
|
||||
for _, ro := range h.options.Recorders {
|
||||
@@ -253,11 +253,7 @@ func (h *relayHandler) observeStats(ctx context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
d := h.md.observePeriod
|
||||
if d < time.Millisecond {
|
||||
d = 5 * time.Second
|
||||
}
|
||||
ticker := time.NewTicker(d)
|
||||
ticker := time.NewTicker(h.md.observePeriod)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
|
||||
+30
-11
@@ -9,22 +9,25 @@ import (
|
||||
|
||||
"github.com/go-gost/core/bypass"
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
"github.com/go-gost/x/internal/util/mux"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
readTimeout time.Duration
|
||||
enableBind bool
|
||||
udpBufferSize int
|
||||
noDelay bool
|
||||
hash string
|
||||
muxCfg *mux.Config
|
||||
observePeriod time.Duration
|
||||
readTimeout time.Duration
|
||||
enableBind bool
|
||||
udpBufferSize int
|
||||
noDelay bool
|
||||
hash string
|
||||
muxCfg *mux.Config
|
||||
observePeriod time.Duration
|
||||
limiterRefreshInterval time.Duration
|
||||
|
||||
sniffing bool
|
||||
sniffingTimeout time.Duration
|
||||
sniffing bool
|
||||
sniffingTimeout time.Duration
|
||||
sniffingWebsocket bool
|
||||
sniffingWebsocketSampleRate float64
|
||||
|
||||
certificate *x509.Certificate
|
||||
privateKey crypto.PrivateKey
|
||||
@@ -59,10 +62,26 @@ func (h *relayHandler) parseMetadata(md mdata.Metadata) (err error) {
|
||||
MaxStreamBuffer: mdutil.GetInt(md, "mux.maxStreamBuffer"),
|
||||
}
|
||||
|
||||
h.md.observePeriod = mdutil.GetDuration(md, "observePeriod")
|
||||
h.md.observePeriod = mdutil.GetDuration(md, "observePeriod", "observer.observePeriod")
|
||||
if h.md.observePeriod == 0 {
|
||||
h.md.observePeriod = 5 * time.Second
|
||||
}
|
||||
if h.md.observePeriod < time.Second {
|
||||
h.md.observePeriod = time.Second
|
||||
}
|
||||
|
||||
h.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if h.md.limiterRefreshInterval == 0 {
|
||||
h.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if h.md.limiterRefreshInterval < time.Second {
|
||||
h.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
|
||||
h.md.sniffing = mdutil.GetBool(md, "sniffing")
|
||||
h.md.sniffingTimeout = mdutil.GetDuration(md, "sniffing.timeout")
|
||||
h.md.sniffingWebsocket = mdutil.GetBool(md, "sniffing.websocket")
|
||||
h.md.sniffingWebsocketSampleRate = mdutil.GetFloat(md, "sniffing.websocket.sampleRate")
|
||||
|
||||
certFile := mdutil.GetString(md, "mitm.certFile", "mitm.caCertFile")
|
||||
keyFile := mdutil.GetString(md, "mitm.keyFile", "mitm.caKeyFile")
|
||||
|
||||
+10
-8
@@ -143,14 +143,16 @@ func (h *sniHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.
|
||||
}
|
||||
|
||||
sniffer := &sniffing.Sniffer{
|
||||
Recorder: h.recorder.Recorder,
|
||||
RecorderOptions: h.recorder.Options,
|
||||
Certificate: h.md.certificate,
|
||||
PrivateKey: h.md.privateKey,
|
||||
NegotiatedProtocol: h.md.alpn,
|
||||
CertPool: h.certPool,
|
||||
MitmBypass: h.md.mitmBypass,
|
||||
ReadTimeout: h.md.readTimeout,
|
||||
Websocket: h.md.sniffingWebsocket,
|
||||
WebsocketSampleRate: h.md.sniffingWebsocketSampleRate,
|
||||
Recorder: h.recorder.Recorder,
|
||||
RecorderOptions: h.recorder.Options,
|
||||
Certificate: h.md.certificate,
|
||||
PrivateKey: h.md.privateKey,
|
||||
NegotiatedProtocol: h.md.alpn,
|
||||
CertPool: h.certPool,
|
||||
MitmBypass: h.md.mitmBypass,
|
||||
ReadTimeout: h.md.readTimeout,
|
||||
}
|
||||
conn = xnet.NewReadWriteConn(br, conn, conn)
|
||||
switch proto {
|
||||
|
||||
@@ -16,6 +16,9 @@ type metadata struct {
|
||||
readTimeout time.Duration
|
||||
hash string
|
||||
|
||||
sniffingWebsocket bool
|
||||
sniffingWebsocketSampleRate float64
|
||||
|
||||
certificate *x509.Certificate
|
||||
privateKey crypto.PrivateKey
|
||||
alpn string
|
||||
@@ -29,6 +32,9 @@ func (h *sniHandler) parseMetadata(md mdata.Metadata) (err error) {
|
||||
}
|
||||
h.md.hash = mdutil.GetString(md, "hash")
|
||||
|
||||
h.md.sniffingWebsocket = mdutil.GetBool(md, "sniffing.websocket")
|
||||
h.md.sniffingWebsocketSampleRate = mdutil.GetFloat(md, "sniffing.websocket.sampleRate")
|
||||
|
||||
certFile := mdutil.GetString(md, "mitm.certFile", "mitm.caCertFile")
|
||||
keyFile := mdutil.GetString(md, "mitm.keyFile", "mitm.caKeyFile")
|
||||
if certFile != "" && keyFile != "" {
|
||||
|
||||
+14
-15
@@ -75,7 +75,7 @@ func (h *socks4Handler) Init(md md.Metadata) (err error) {
|
||||
}
|
||||
|
||||
if limiter := h.options.Limiter; limiter != nil {
|
||||
h.limiter = limiter_util.NewCachedTrafficLimiter(limiter, 30*time.Second, 60*time.Second)
|
||||
h.limiter = limiter_util.NewCachedTrafficLimiter(limiter, h.md.limiterRefreshInterval, 60*time.Second)
|
||||
}
|
||||
|
||||
for _, ro := range h.options.Recorders {
|
||||
@@ -196,7 +196,8 @@ func (h *socks4Handler) handleConnect(ctx context.Context, conn net.Conn, req *g
|
||||
addr := req.Addr.String()
|
||||
|
||||
log = log.WithFields(map[string]any{
|
||||
"dst": addr,
|
||||
"dst": addr,
|
||||
"host": addr,
|
||||
})
|
||||
log.Debugf("%s >> %s", conn.RemoteAddr(), addr)
|
||||
|
||||
@@ -274,14 +275,16 @@ func (h *socks4Handler) handleConnect(ctx context.Context, conn net.Conn, req *g
|
||||
return cc, nil
|
||||
}
|
||||
sniffer := &sniffing.Sniffer{
|
||||
Recorder: h.recorder.Recorder,
|
||||
RecorderOptions: h.recorder.Options,
|
||||
Certificate: h.md.certificate,
|
||||
PrivateKey: h.md.privateKey,
|
||||
NegotiatedProtocol: h.md.alpn,
|
||||
CertPool: h.certPool,
|
||||
MitmBypass: h.md.mitmBypass,
|
||||
ReadTimeout: h.md.readTimeout,
|
||||
Websocket: h.md.sniffingWebsocket,
|
||||
WebsocketSampleRate: h.md.sniffingWebsocketSampleRate,
|
||||
Recorder: h.recorder.Recorder,
|
||||
RecorderOptions: h.recorder.Options,
|
||||
Certificate: h.md.certificate,
|
||||
PrivateKey: h.md.privateKey,
|
||||
NegotiatedProtocol: h.md.alpn,
|
||||
CertPool: h.certPool,
|
||||
MitmBypass: h.md.mitmBypass,
|
||||
ReadTimeout: h.md.readTimeout,
|
||||
}
|
||||
|
||||
conn = xnet.NewReadWriteConn(br, conn, conn)
|
||||
@@ -335,11 +338,7 @@ func (h *socks4Handler) observeStats(ctx context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
d := h.md.observePeriod
|
||||
if d < time.Millisecond {
|
||||
d = 5 * time.Second
|
||||
}
|
||||
ticker := time.NewTicker(d)
|
||||
ticker := time.NewTicker(h.md.observePeriod)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
|
||||
@@ -13,12 +13,15 @@ import (
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
readTimeout time.Duration
|
||||
hash string
|
||||
observePeriod time.Duration
|
||||
readTimeout time.Duration
|
||||
hash string
|
||||
observePeriod time.Duration
|
||||
limiterRefreshInterval time.Duration
|
||||
|
||||
sniffing bool
|
||||
sniffingTimeout time.Duration
|
||||
sniffing bool
|
||||
sniffingTimeout time.Duration
|
||||
sniffingWebsocket bool
|
||||
sniffingWebsocketSampleRate float64
|
||||
|
||||
certificate *x509.Certificate
|
||||
privateKey crypto.PrivateKey
|
||||
@@ -33,10 +36,27 @@ func (h *socks4Handler) parseMetadata(md mdata.Metadata) (err error) {
|
||||
}
|
||||
|
||||
h.md.hash = mdutil.GetString(md, "hash")
|
||||
h.md.observePeriod = mdutil.GetDuration(md, "observePeriod")
|
||||
|
||||
h.md.observePeriod = mdutil.GetDuration(md, "observePeriod", "observer.observePeriod")
|
||||
if h.md.observePeriod == 0 {
|
||||
h.md.observePeriod = 5 * time.Second
|
||||
}
|
||||
if h.md.observePeriod < time.Second {
|
||||
h.md.observePeriod = time.Second
|
||||
}
|
||||
|
||||
h.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if h.md.limiterRefreshInterval == 0 {
|
||||
h.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if h.md.limiterRefreshInterval < time.Second {
|
||||
h.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
|
||||
h.md.sniffing = mdutil.GetBool(md, "sniffing")
|
||||
h.md.sniffingTimeout = mdutil.GetDuration(md, "sniffing.timeout")
|
||||
h.md.sniffingWebsocket = mdutil.GetBool(md, "sniffing.websocket")
|
||||
h.md.sniffingWebsocketSampleRate = mdutil.GetFloat(md, "sniffing.websocket.sampleRate")
|
||||
|
||||
certFile := mdutil.GetString(md, "mitm.certFile", "mitm.caCertFile")
|
||||
keyFile := mdutil.GetString(md, "mitm.keyFile", "mitm.caKeyFile")
|
||||
|
||||
+13
-10
@@ -23,8 +23,9 @@ import (
|
||||
|
||||
func (h *socks5Handler) handleConnect(ctx context.Context, conn net.Conn, network, address string, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
|
||||
log = log.WithFields(map[string]any{
|
||||
"dst": fmt.Sprintf("%s/%s", address, network),
|
||||
"cmd": "connect",
|
||||
"dst": fmt.Sprintf("%s/%s", address, network),
|
||||
"cmd": "connect",
|
||||
"host": address,
|
||||
})
|
||||
log.Debugf("%s >> %s", conn.RemoteAddr(), address)
|
||||
|
||||
@@ -102,14 +103,16 @@ func (h *socks5Handler) handleConnect(ctx context.Context, conn net.Conn, networ
|
||||
return cc, nil
|
||||
}
|
||||
sniffer := &sniffing.Sniffer{
|
||||
Recorder: h.recorder.Recorder,
|
||||
RecorderOptions: h.recorder.Options,
|
||||
Certificate: h.md.certificate,
|
||||
PrivateKey: h.md.privateKey,
|
||||
NegotiatedProtocol: h.md.alpn,
|
||||
CertPool: h.certPool,
|
||||
MitmBypass: h.md.mitmBypass,
|
||||
ReadTimeout: h.md.readTimeout,
|
||||
Websocket: h.md.sniffingWebsocket,
|
||||
WebsocketSampleRate: h.md.sniffingWebsocketSampleRate,
|
||||
Recorder: h.recorder.Recorder,
|
||||
RecorderOptions: h.recorder.Options,
|
||||
Certificate: h.md.certificate,
|
||||
PrivateKey: h.md.privateKey,
|
||||
NegotiatedProtocol: h.md.alpn,
|
||||
CertPool: h.certPool,
|
||||
MitmBypass: h.md.mitmBypass,
|
||||
ReadTimeout: h.md.readTimeout,
|
||||
}
|
||||
|
||||
conn = xnet.NewReadWriteConn(br, conn, conn)
|
||||
|
||||
@@ -75,7 +75,7 @@ func (h *socks5Handler) Init(md md.Metadata) (err error) {
|
||||
}
|
||||
|
||||
if limiter := h.options.Limiter; limiter != nil {
|
||||
h.limiter = limiter_util.NewCachedTrafficLimiter(limiter, 30*time.Second, 60*time.Second)
|
||||
h.limiter = limiter_util.NewCachedTrafficLimiter(limiter, h.md.limiterRefreshInterval, 60*time.Second)
|
||||
}
|
||||
|
||||
for _, ro := range h.options.Recorders {
|
||||
@@ -216,11 +216,7 @@ func (h *socks5Handler) observeStats(ctx context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
d := h.md.observePeriod
|
||||
if d < time.Millisecond {
|
||||
d = 5 * time.Second
|
||||
}
|
||||
ticker := time.NewTicker(d)
|
||||
ticker := time.NewTicker(h.md.observePeriod)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
|
||||
@@ -9,24 +9,27 @@ import (
|
||||
|
||||
"github.com/go-gost/core/bypass"
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
"github.com/go-gost/x/internal/util/mux"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
readTimeout time.Duration
|
||||
noTLS bool
|
||||
enableBind bool
|
||||
enableUDP bool
|
||||
udpBufferSize int
|
||||
compatibilityMode bool
|
||||
hash string
|
||||
muxCfg *mux.Config
|
||||
observePeriod time.Duration
|
||||
readTimeout time.Duration
|
||||
noTLS bool
|
||||
enableBind bool
|
||||
enableUDP bool
|
||||
udpBufferSize int
|
||||
compatibilityMode bool
|
||||
hash string
|
||||
muxCfg *mux.Config
|
||||
observePeriod time.Duration
|
||||
limiterRefreshInterval time.Duration
|
||||
|
||||
sniffing bool
|
||||
sniffingTimeout time.Duration
|
||||
sniffing bool
|
||||
sniffingTimeout time.Duration
|
||||
sniffingWebsocket bool
|
||||
sniffingWebsocketSampleRate float64
|
||||
|
||||
certificate *x509.Certificate
|
||||
privateKey crypto.PrivateKey
|
||||
@@ -63,10 +66,26 @@ func (h *socks5Handler) parseMetadata(md mdata.Metadata) (err error) {
|
||||
MaxStreamBuffer: mdutil.GetInt(md, "mux.maxStreamBuffer"),
|
||||
}
|
||||
|
||||
h.md.observePeriod = mdutil.GetDuration(md, "observePeriod")
|
||||
h.md.observePeriod = mdutil.GetDuration(md, "observePeriod", "observer.observePeriod")
|
||||
if h.md.observePeriod == 0 {
|
||||
h.md.observePeriod = 5 * time.Second
|
||||
}
|
||||
if h.md.observePeriod < time.Second {
|
||||
h.md.observePeriod = time.Second
|
||||
}
|
||||
|
||||
h.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if h.md.limiterRefreshInterval == 0 {
|
||||
h.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if h.md.limiterRefreshInterval < time.Second {
|
||||
h.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
|
||||
h.md.sniffing = mdutil.GetBool(md, "sniffing")
|
||||
h.md.sniffingTimeout = mdutil.GetDuration(md, "sniffing.timeout")
|
||||
h.md.sniffingWebsocket = mdutil.GetBool(md, "sniffing.websocket")
|
||||
h.md.sniffingWebsocketSampleRate = mdutil.GetFloat(md, "sniffing.websocket.sampleRate")
|
||||
|
||||
certFile := mdutil.GetString(md, "mitm.certFile", "mitm.caCertFile")
|
||||
keyFile := mdutil.GetString(md, "mitm.keyFile", "mitm.caKeyFile")
|
||||
|
||||
+12
-9
@@ -148,7 +148,8 @@ func (h *ssHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.H
|
||||
conn.SetReadDeadline(time.Time{})
|
||||
|
||||
log = log.WithFields(map[string]any{
|
||||
"dst": addr.String(),
|
||||
"dst": addr.String(),
|
||||
"host": addr.String(),
|
||||
})
|
||||
|
||||
log.Debugf("%s >> %s", conn.RemoteAddr(), addr)
|
||||
@@ -191,14 +192,16 @@ func (h *ssHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.H
|
||||
return cc, nil
|
||||
}
|
||||
sniffer := &sniffing.Sniffer{
|
||||
Recorder: h.recorder.Recorder,
|
||||
RecorderOptions: h.recorder.Options,
|
||||
Certificate: h.md.certificate,
|
||||
PrivateKey: h.md.privateKey,
|
||||
NegotiatedProtocol: h.md.alpn,
|
||||
CertPool: h.certPool,
|
||||
MitmBypass: h.md.mitmBypass,
|
||||
ReadTimeout: h.md.readTimeout,
|
||||
Websocket: h.md.sniffingWebsocket,
|
||||
WebsocketSampleRate: h.md.sniffingWebsocketSampleRate,
|
||||
Recorder: h.recorder.Recorder,
|
||||
RecorderOptions: h.recorder.Options,
|
||||
Certificate: h.md.certificate,
|
||||
PrivateKey: h.md.privateKey,
|
||||
NegotiatedProtocol: h.md.alpn,
|
||||
CertPool: h.certPool,
|
||||
MitmBypass: h.md.mitmBypass,
|
||||
ReadTimeout: h.md.readTimeout,
|
||||
}
|
||||
|
||||
conn = xnet.NewReadWriteConn(br, conn, conn)
|
||||
|
||||
@@ -17,8 +17,10 @@ type metadata struct {
|
||||
hash string
|
||||
readTimeout time.Duration
|
||||
|
||||
sniffing bool
|
||||
sniffingTimeout time.Duration
|
||||
sniffing bool
|
||||
sniffingTimeout time.Duration
|
||||
sniffingWebsocket bool
|
||||
sniffingWebsocketSampleRate float64
|
||||
|
||||
certificate *x509.Certificate
|
||||
privateKey crypto.PrivateKey
|
||||
@@ -37,6 +39,8 @@ func (h *ssHandler) parseMetadata(md mdata.Metadata) (err error) {
|
||||
|
||||
h.md.sniffing = mdutil.GetBool(md, "sniffing")
|
||||
h.md.sniffingTimeout = mdutil.GetDuration(md, "sniffing.timeout")
|
||||
h.md.sniffingWebsocket = mdutil.GetBool(md, "sniffing.websocket")
|
||||
h.md.sniffingWebsocketSampleRate = mdutil.GetFloat(md, "sniffing.websocket.sampleRate")
|
||||
|
||||
certFile := mdutil.GetString(md, "mitm.certFile", "mitm.caCertFile")
|
||||
keyFile := mdutil.GetString(md, "mitm.keyFile", "mitm.caKeyFile")
|
||||
|
||||
+13
-10
@@ -140,8 +140,9 @@ func (h *forwardHandler) handleDirectForward(ctx context.Context, conn *sshd_uti
|
||||
|
||||
ro.Host = targetAddr
|
||||
log = log.WithFields(map[string]any{
|
||||
"dst": fmt.Sprintf("%s/%s", targetAddr, "tcp"),
|
||||
"cmd": "connect",
|
||||
"dst": fmt.Sprintf("%s/%s", targetAddr, "tcp"),
|
||||
"cmd": "connect",
|
||||
"host": targetAddr,
|
||||
})
|
||||
|
||||
log.Debugf("%s >> %s", conn.RemoteAddr(), targetAddr)
|
||||
@@ -179,14 +180,16 @@ func (h *forwardHandler) handleDirectForward(ctx context.Context, conn *sshd_uti
|
||||
return cc, nil
|
||||
}
|
||||
sniffer := &sniffing.Sniffer{
|
||||
Recorder: h.recorder.Recorder,
|
||||
RecorderOptions: h.recorder.Options,
|
||||
Certificate: h.md.certificate,
|
||||
PrivateKey: h.md.privateKey,
|
||||
NegotiatedProtocol: h.md.alpn,
|
||||
CertPool: h.certPool,
|
||||
MitmBypass: h.md.mitmBypass,
|
||||
ReadTimeout: h.md.readTimeout,
|
||||
Websocket: h.md.sniffingWebsocket,
|
||||
WebsocketSampleRate: h.md.sniffingWebsocketSampleRate,
|
||||
Recorder: h.recorder.Recorder,
|
||||
RecorderOptions: h.recorder.Options,
|
||||
Certificate: h.md.certificate,
|
||||
PrivateKey: h.md.privateKey,
|
||||
NegotiatedProtocol: h.md.alpn,
|
||||
CertPool: h.certPool,
|
||||
MitmBypass: h.md.mitmBypass,
|
||||
ReadTimeout: h.md.readTimeout,
|
||||
}
|
||||
|
||||
switch proto {
|
||||
|
||||
@@ -15,8 +15,10 @@ import (
|
||||
type metadata struct {
|
||||
readTimeout time.Duration
|
||||
|
||||
sniffing bool
|
||||
sniffingTimeout time.Duration
|
||||
sniffing bool
|
||||
sniffingTimeout time.Duration
|
||||
sniffingWebsocket bool
|
||||
sniffingWebsocketSampleRate float64
|
||||
|
||||
certificate *x509.Certificate
|
||||
privateKey crypto.PrivateKey
|
||||
@@ -32,6 +34,8 @@ func (h *forwardHandler) parseMetadata(md mdata.Metadata) (err error) {
|
||||
|
||||
h.md.sniffing = mdutil.GetBool(md, "sniffing")
|
||||
h.md.sniffingTimeout = mdutil.GetDuration(md, "sniffing.timeout")
|
||||
h.md.sniffingWebsocket = mdutil.GetBool(md, "sniffing.websocket")
|
||||
h.md.sniffingWebsocketSampleRate = mdutil.GetFloat(md, "sniffing.websocket.sampleRate")
|
||||
|
||||
certFile := mdutil.GetString(md, "mitm.certFile", "mitm.caCertFile")
|
||||
keyFile := mdutil.GetString(md, "mitm.keyFile", "mitm.caKeyFile")
|
||||
|
||||
@@ -37,11 +37,15 @@ func (h *tunnelHandler) handleBind(ctx context.Context, conn net.Conn, network,
|
||||
v := md5.Sum([]byte(tunnelID.String()))
|
||||
endpoint := hex.EncodeToString(v[:8])
|
||||
|
||||
addr := address
|
||||
host, port, _ := net.SplitHostPort(addr)
|
||||
if host == "" {
|
||||
addr = net.JoinHostPort(endpoint, port)
|
||||
host, port, _ := net.SplitHostPort(address)
|
||||
if host == "" || h.md.ingress == nil {
|
||||
host = endpoint
|
||||
} else if host != endpoint {
|
||||
if rule := h.md.ingress.GetRule(ctx, host); rule != nil && rule.Endpoint != tunnelID.String() {
|
||||
host = endpoint
|
||||
}
|
||||
}
|
||||
addr := net.JoinHostPort(host, port)
|
||||
|
||||
af := &relay.AddrFeature{}
|
||||
err = af.ParseFrom(addr)
|
||||
|
||||
+29
-12
@@ -16,6 +16,7 @@ func (h *tunnelHandler) handleConnect(ctx context.Context, req *relay.Request, c
|
||||
"dst": fmt.Sprintf("%s/%s", dstAddr, network),
|
||||
"cmd": "connect",
|
||||
"tunnel": tunnelID.String(),
|
||||
"host": dstAddr,
|
||||
})
|
||||
|
||||
resp := relay.Response{
|
||||
@@ -32,18 +33,34 @@ func (h *tunnelHandler) handleConnect(ctx context.Context, req *relay.Request, c
|
||||
|
||||
host, _, _ := net.SplitHostPort(dstAddr)
|
||||
|
||||
// client is a public entrypoint.
|
||||
if tunnelID.Equal(h.md.entryPointID) {
|
||||
resp.WriteTo(conn)
|
||||
return h.ep.Handle(ctx, conn)
|
||||
var tid relay.TunnelID
|
||||
if ingress := h.md.ingress; ingress != nil && host != "" {
|
||||
if rule := ingress.GetRule(ctx, host); rule != nil {
|
||||
tid = parseTunnelID(rule.Endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
if !h.md.directTunnel {
|
||||
var tid relay.TunnelID
|
||||
if ingress := h.md.ingress; ingress != nil && host != "" {
|
||||
if rule := ingress.GetRule(ctx, host); rule != nil {
|
||||
tid = parseTunnelID(rule.Endpoint)
|
||||
}
|
||||
// visitor is a public entrypoint.
|
||||
if tunnelID.Equal(h.md.entryPointID) {
|
||||
if tid.IsZero() {
|
||||
resp.Status = relay.StatusNetworkUnreachable
|
||||
resp.WriteTo(conn)
|
||||
err := fmt.Errorf("no route to host %s", host)
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
if tid.IsPrivate() {
|
||||
resp.Status = relay.StatusHostUnreachable
|
||||
resp.WriteTo(conn)
|
||||
err := fmt.Errorf("tunnel %s is private for host %s", tid, host)
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// direct routing
|
||||
if h.md.directTunnel {
|
||||
tid = tunnelID
|
||||
}
|
||||
if !tid.Equal(tunnelID) {
|
||||
resp.Status = relay.StatusHostUnreachable
|
||||
@@ -62,7 +79,7 @@ func (h *tunnelHandler) handleConnect(ctx context.Context, req *relay.Request, c
|
||||
timeout: 15 * time.Second,
|
||||
log: log,
|
||||
}
|
||||
cc, node, cid, err := d.Dial(ctx, network, tunnelID.String())
|
||||
cc, node, cid, err := d.Dial(ctx, network, tid.String())
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
resp.Status = relay.StatusServiceUnavailable
|
||||
@@ -71,7 +88,7 @@ func (h *tunnelHandler) handleConnect(ctx context.Context, req *relay.Request, c
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
log.Debugf("new connection to tunnel: %s, connector: %s", tunnelID, cid)
|
||||
log.Debugf("connect to node=%s tunnel=%s connector=%s OK", node, tid, cid)
|
||||
|
||||
if node == h.id {
|
||||
if _, err := resp.WriteTo(conn); err != nil {
|
||||
|
||||
@@ -68,11 +68,11 @@ func (d *Dialer) Dial(ctx context.Context, network string, tid string) (conn net
|
||||
}
|
||||
|
||||
node = service.Node
|
||||
cid = service.Name
|
||||
cid = service.ID
|
||||
|
||||
dialer := net.Dialer{
|
||||
Timeout: d.timeout,
|
||||
}
|
||||
conn, err = dialer.DialContext(ctx, network, service.Address)
|
||||
conn, err = dialer.DialContext(ctx, "tcp", service.Address)
|
||||
return
|
||||
}
|
||||
|
||||
+241
-94
@@ -2,10 +2,12 @@ package tunnel
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
@@ -24,35 +26,26 @@ import (
|
||||
"github.com/go-gost/relay"
|
||||
admission "github.com/go-gost/x/admission/wrapper"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
ctx_internal "github.com/go-gost/x/internal/ctx"
|
||||
xio "github.com/go-gost/x/internal/io"
|
||||
xnet "github.com/go-gost/x/internal/net"
|
||||
xhttp "github.com/go-gost/x/internal/net/http"
|
||||
"github.com/go-gost/x/internal/net/proxyproto"
|
||||
"github.com/go-gost/x/internal/util/sniffing"
|
||||
ws_util "github.com/go-gost/x/internal/util/ws"
|
||||
climiter "github.com/go-gost/x/limiter/conn/wrapper"
|
||||
metrics "github.com/go-gost/x/metrics/wrapper"
|
||||
stats_wrapper "github.com/go-gost/x/observer/stats/wrapper"
|
||||
xrecorder "github.com/go-gost/x/recorder"
|
||||
"golang.org/x/net/http/httpguts"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultBodySize = 1024 * 1024 // 1MB
|
||||
httpHeaderSID = "Gost-Sid"
|
||||
httpHeaderForwardedNode = "Gost-Forwarded-Node"
|
||||
)
|
||||
|
||||
type recorderObjectCtxKey struct{}
|
||||
|
||||
var (
|
||||
ctxKeyRecorderObject = &recorderObjectCtxKey{}
|
||||
)
|
||||
|
||||
func contextWithRecorderObject(ctx context.Context, ro *xrecorder.HandlerRecorderObject) context.Context {
|
||||
return context.WithValue(ctx, ctxKeyRecorderObject, ro)
|
||||
}
|
||||
|
||||
func recorderObjectFromContext(ctx context.Context) *xrecorder.HandlerRecorderObject {
|
||||
v, _ := ctx.Value(ctxKeyRecorderObject).(*xrecorder.HandlerRecorderObject)
|
||||
return v
|
||||
}
|
||||
|
||||
type entrypoint struct {
|
||||
node string
|
||||
service string
|
||||
@@ -62,6 +55,9 @@ type entrypoint struct {
|
||||
log logger.Logger
|
||||
recorder recorder.RecorderObject
|
||||
transport http.RoundTripper
|
||||
|
||||
sniffingWebsocket bool
|
||||
websocketSampleRate float64
|
||||
}
|
||||
|
||||
func (ep *entrypoint) Handle(ctx context.Context, conn net.Conn) (err error) {
|
||||
@@ -119,6 +115,77 @@ func (ep *entrypoint) Handle(ctx context.Context, conn net.Conn) (err error) {
|
||||
return ep.handleHTTP(ctx, conn, ro, log)
|
||||
}
|
||||
|
||||
func (ep *entrypoint) dial(ctx context.Context, network, addr string) (conn net.Conn, err error) {
|
||||
var tunnelID relay.TunnelID
|
||||
if ep.ingress != nil {
|
||||
if rule := ep.ingress.GetRule(ctx, addr); rule != nil {
|
||||
tunnelID = parseTunnelID(rule.Endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
log := ctxvalue.LoggerFromContext(ctx)
|
||||
if log == nil {
|
||||
log = ep.log
|
||||
}
|
||||
log.Debugf("dial: new connection to host %s", addr)
|
||||
|
||||
if tunnelID.IsZero() {
|
||||
return nil, fmt.Errorf("%w %s", ErrTunnelRoute, addr)
|
||||
}
|
||||
|
||||
ro := ctx_internal.RecorderObjectFromContext(ctx)
|
||||
ro.ClientID = tunnelID.String()
|
||||
|
||||
if tunnelID.IsPrivate() {
|
||||
return nil, fmt.Errorf("%w: tunnel %s is private for host %s", ErrPrivateTunnel, tunnelID, addr)
|
||||
}
|
||||
|
||||
log = log.WithFields(map[string]any{
|
||||
"tunnel": tunnelID.String(),
|
||||
})
|
||||
|
||||
d := &Dialer{
|
||||
node: ep.node,
|
||||
pool: ep.pool,
|
||||
sd: ep.sd,
|
||||
retry: 3,
|
||||
timeout: 15 * time.Second,
|
||||
log: log,
|
||||
}
|
||||
conn, node, cid, err := d.Dial(ctx, "tcp", tunnelID.String())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
log.Debugf("dial: connected to host %s, tunnel: %s, connector: %s", addr, tunnelID, cid)
|
||||
|
||||
if node == ep.node {
|
||||
ro.Redirect = ""
|
||||
|
||||
clientAddr := ctxvalue.ClientAddrFromContext(ctx)
|
||||
var features []relay.Feature
|
||||
af := &relay.AddrFeature{}
|
||||
af.ParseFrom(string(clientAddr))
|
||||
features = append(features, af) // src address
|
||||
|
||||
af = &relay.AddrFeature{}
|
||||
af.ParseFrom(addr)
|
||||
features = append(features, af) // dst address
|
||||
|
||||
if _, err = (&relay.Response{
|
||||
Version: relay.Version1,
|
||||
Status: relay.StatusOK,
|
||||
Features: features,
|
||||
}).WriteTo(conn); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
ro.Redirect = node
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (ep *entrypoint) handleHTTP(ctx context.Context, conn net.Conn, ro *xrecorder.HandlerRecorderObject, log logger.Logger) (err error) {
|
||||
pStats := stats.Stats{}
|
||||
conn = stats_wrapper.WrapConn(conn, &pStats)
|
||||
@@ -148,7 +215,7 @@ func (ep *entrypoint) handleHTTP(ctx context.Context, conn net.Conn, ro *xrecord
|
||||
|
||||
ro.Time = time.Time{}
|
||||
|
||||
if err := ep.httpRoundTrip(ctx, conn, req, ro, &pStats, log); err != nil {
|
||||
if err := ep.httpRoundTrip(ctx, xio.NewReadWriter(br, conn), req, ro, &pStats, log); err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
@@ -169,7 +236,7 @@ func (ep *entrypoint) handleHTTP(ctx context.Context, conn net.Conn, ro *xrecord
|
||||
log.Trace(string(dump))
|
||||
}
|
||||
|
||||
if err := ep.httpRoundTrip(ctx, conn, req, ro, &pStats, log); err != nil {
|
||||
if err := ep.httpRoundTrip(ctx, xio.NewReadWriter(br, conn), req, ro, &pStats, log); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -180,7 +247,23 @@ func (ep *entrypoint) httpRoundTrip(ctx context.Context, rw io.ReadWriter, req *
|
||||
*ro2 = *ro
|
||||
ro = ro2
|
||||
|
||||
if sid := req.Header.Get(httpHeaderSID); sid != "" {
|
||||
ro.SID = sid
|
||||
} else {
|
||||
req.Header.Set(httpHeaderSID, ro.SID)
|
||||
}
|
||||
req.Header.Set(httpHeaderForwardedNode, ro.Node)
|
||||
|
||||
host := req.Host
|
||||
if _, port, _ := net.SplitHostPort(host); port == "" {
|
||||
host = net.JoinHostPort(strings.Trim(host, "[]"), "80")
|
||||
}
|
||||
ro.Host = host
|
||||
ro.Time = time.Now()
|
||||
|
||||
log = log.WithFields(map[string]any{
|
||||
"host": host,
|
||||
})
|
||||
log.Infof("%s <-> %s", ro.RemoteAddr, req.Host)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
@@ -241,20 +324,29 @@ func (ep *entrypoint) httpRoundTrip(ctx context.Context, rw io.ReadWriter, req *
|
||||
var reqBody *xhttp.Body
|
||||
if opts := ep.recorder.Options; opts != nil && opts.HTTPBody {
|
||||
if req.Body != nil {
|
||||
maxSize := opts.MaxBodySize
|
||||
if maxSize <= 0 {
|
||||
maxSize = defaultBodySize
|
||||
bodySize := opts.MaxBodySize
|
||||
if bodySize <= 0 {
|
||||
bodySize = sniffing.DefaultBodySize
|
||||
}
|
||||
reqBody = xhttp.NewBody(req.Body, maxSize)
|
||||
if bodySize > sniffing.MaxBodySize {
|
||||
bodySize = sniffing.MaxBodySize
|
||||
}
|
||||
reqBody = xhttp.NewBody(req.Body, bodySize)
|
||||
req.Body = reqBody
|
||||
}
|
||||
}
|
||||
|
||||
ctx = ctxvalue.ContextWithClientAddr(ctx, ctxvalue.ClientAddr(clientAddr))
|
||||
ctx = contextWithRecorderObject(ctx, ro)
|
||||
ctx = ctx_internal.ContextWithRecorderObject(ctx, ro)
|
||||
ctx = ctxvalue.ContextWithLogger(ctx, log)
|
||||
|
||||
resp, err := ep.transport.RoundTrip(req.WithContext(ctx))
|
||||
|
||||
if reqBody != nil {
|
||||
ro.HTTP.Request.Body = reqBody.Content()
|
||||
ro.HTTP.Request.ContentLength = reqBody.Length()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrTunnelRoute) || errors.Is(err, ErrPrivateTunnel) {
|
||||
res.StatusCode = http.StatusBadGateway
|
||||
@@ -265,11 +357,6 @@ func (ep *entrypoint) httpRoundTrip(ctx context.Context, rw io.ReadWriter, req *
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if reqBody != nil {
|
||||
ro.HTTP.Request.Body = reqBody.Content()
|
||||
ro.HTTP.Request.ContentLength = reqBody.Length()
|
||||
}
|
||||
|
||||
ro.HTTP.StatusCode = resp.StatusCode
|
||||
ro.HTTP.Response.Header = resp.Header
|
||||
ro.HTTP.Response.ContentLength = resp.ContentLength
|
||||
@@ -280,28 +367,33 @@ func (ep *entrypoint) httpRoundTrip(ctx context.Context, rw io.ReadWriter, req *
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusSwitchingProtocols {
|
||||
return ep.handleUpgradeResponse(rw, req, resp)
|
||||
return ep.handleUpgradeResponse(ctx, rw, req, resp, ro, log)
|
||||
}
|
||||
|
||||
var respBody *xhttp.Body
|
||||
if opts := ep.recorder.Options; opts != nil && opts.HTTPBody {
|
||||
maxSize := opts.MaxBodySize
|
||||
if maxSize <= 0 {
|
||||
maxSize = defaultBodySize
|
||||
bodySize := opts.MaxBodySize
|
||||
if bodySize <= 0 {
|
||||
bodySize = sniffing.DefaultBodySize
|
||||
}
|
||||
respBody = xhttp.NewBody(resp.Body, maxSize)
|
||||
if bodySize > sniffing.MaxBodySize {
|
||||
bodySize = sniffing.MaxBodySize
|
||||
}
|
||||
respBody = xhttp.NewBody(resp.Body, bodySize)
|
||||
resp.Body = respBody
|
||||
}
|
||||
|
||||
if err = resp.Write(rw); err != nil {
|
||||
return fmt.Errorf("write response: %v", err)
|
||||
}
|
||||
err = resp.Write(rw)
|
||||
|
||||
if respBody != nil {
|
||||
ro.HTTP.Response.Body = respBody.Content()
|
||||
ro.HTTP.Response.ContentLength = respBody.Length()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("write response: %v", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -312,7 +404,7 @@ func upgradeType(h http.Header) string {
|
||||
return h.Get("Upgrade")
|
||||
}
|
||||
|
||||
func (ep *entrypoint) handleUpgradeResponse(rw io.ReadWriter, req *http.Request, res *http.Response) error {
|
||||
func (ep *entrypoint) handleUpgradeResponse(ctx context.Context, rw io.ReadWriter, req *http.Request, res *http.Response, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
|
||||
reqUpType := upgradeType(req.Header)
|
||||
resUpType := upgradeType(res.Header)
|
||||
if !strings.EqualFold(reqUpType, resUpType) {
|
||||
@@ -323,81 +415,136 @@ func (ep *entrypoint) handleUpgradeResponse(rw io.ReadWriter, req *http.Request,
|
||||
if !ok {
|
||||
return fmt.Errorf("internal error: 101 switching protocols response with non-writable body")
|
||||
}
|
||||
defer backConn.Close()
|
||||
|
||||
res.Body = nil
|
||||
if err := res.Write(rw); err != nil {
|
||||
return fmt.Errorf("response write: %v", err)
|
||||
}
|
||||
|
||||
if reqUpType == "websocket" && ep.sniffingWebsocket {
|
||||
return ep.sniffingWebsocketFrame(ctx, rw, backConn, ro, log)
|
||||
}
|
||||
|
||||
return xnet.Transport(rw, backConn)
|
||||
}
|
||||
|
||||
func (h *entrypoint) dial(ctx context.Context, network, addr string) (conn net.Conn, err error) {
|
||||
var tunnelID relay.TunnelID
|
||||
if h.ingress != nil {
|
||||
if rule := h.ingress.GetRule(ctx, addr); rule != nil {
|
||||
tunnelID = parseTunnelID(rule.Endpoint)
|
||||
func (ep *entrypoint) sniffingWebsocketFrame(ctx context.Context, rw, cc io.ReadWriter, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
|
||||
errc := make(chan error, 1)
|
||||
|
||||
sampleRate := ep.websocketSampleRate
|
||||
if sampleRate == 0 {
|
||||
sampleRate = sniffing.DefaultSampleRate
|
||||
}
|
||||
if sampleRate < 0 {
|
||||
sampleRate = math.MaxFloat64
|
||||
}
|
||||
|
||||
go func() {
|
||||
ro2 := &xrecorder.HandlerRecorderObject{}
|
||||
*ro2 = *ro
|
||||
ro := ro2
|
||||
|
||||
limiter := rate.NewLimiter(rate.Limit(sampleRate), int(sampleRate))
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
for {
|
||||
start := time.Now()
|
||||
|
||||
if err := ep.copyWebsocketFrame(cc, rw, buf, "client", ro); err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
|
||||
if limiter.Allow() {
|
||||
ro.Duration = time.Since(start)
|
||||
ro.Time = time.Now()
|
||||
if err := ro.Record(ctx, ep.recorder.Recorder); err != nil {
|
||||
log.Errorf("record: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
log := ctxvalue.LoggerFromContext(ctx)
|
||||
if log == nil {
|
||||
log = h.log
|
||||
}
|
||||
go func() {
|
||||
ro2 := &xrecorder.HandlerRecorderObject{}
|
||||
*ro2 = *ro
|
||||
ro := ro2
|
||||
|
||||
if tunnelID.IsZero() {
|
||||
return nil, fmt.Errorf("%w %s", ErrTunnelRoute, addr)
|
||||
}
|
||||
limiter := rate.NewLimiter(rate.Limit(sampleRate), int(sampleRate))
|
||||
|
||||
if ro := recorderObjectFromContext(ctx); ro != nil {
|
||||
ro.ClientID = tunnelID.String()
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
for {
|
||||
start := time.Now()
|
||||
|
||||
if tunnelID.IsPrivate() {
|
||||
return nil, fmt.Errorf("%w: tunnel %s is private for host %s", ErrPrivateTunnel, tunnelID, addr)
|
||||
}
|
||||
if err := ep.copyWebsocketFrame(rw, cc, buf, "server", ro); err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
|
||||
log = log.WithFields(map[string]any{
|
||||
"host": addr,
|
||||
"tunnel": tunnelID.String(),
|
||||
})
|
||||
|
||||
d := &Dialer{
|
||||
node: h.node,
|
||||
pool: h.pool,
|
||||
sd: h.sd,
|
||||
retry: 3,
|
||||
timeout: 15 * time.Second,
|
||||
log: log,
|
||||
}
|
||||
conn, node, cid, err := d.Dial(ctx, "tcp", tunnelID.String())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
log.Debugf("new connection to tunnel: %s, connector: %s", tunnelID, cid)
|
||||
|
||||
if node == h.node {
|
||||
clientAddr := ctxvalue.ClientAddrFromContext(ctx)
|
||||
var features []relay.Feature
|
||||
af := &relay.AddrFeature{}
|
||||
af.ParseFrom(string(clientAddr))
|
||||
features = append(features, af) // src address
|
||||
|
||||
af = &relay.AddrFeature{}
|
||||
af.ParseFrom(addr)
|
||||
features = append(features, af) // dst address
|
||||
|
||||
if _, err = (&relay.Response{
|
||||
Version: relay.Version1,
|
||||
Status: relay.StatusOK,
|
||||
Features: features,
|
||||
}).WriteTo(conn); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
if limiter.Allow() {
|
||||
ro.Duration = time.Since(start)
|
||||
ro.Time = time.Now()
|
||||
if err := ro.Record(ctx, ep.recorder.Recorder); err != nil {
|
||||
log.Errorf("record: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
<-errc
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ep *entrypoint) copyWebsocketFrame(w io.Writer, r io.Reader, buf *bytes.Buffer, from string, ro *xrecorder.HandlerRecorderObject) (err error) {
|
||||
fr := ws_util.Frame{}
|
||||
if _, err = fr.ReadFrom(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
ws := &xrecorder.WebsocketRecorderObject{
|
||||
From: from,
|
||||
Fin: fr.Header.Fin,
|
||||
Rsv1: fr.Header.Rsv1,
|
||||
Rsv2: fr.Header.Rsv2,
|
||||
Rsv3: fr.Header.Rsv3,
|
||||
OpCode: int(fr.Header.OpCode),
|
||||
Masked: fr.Header.Masked,
|
||||
MaskKey: fr.Header.MaskKey,
|
||||
Length: fr.Header.PayloadLength,
|
||||
}
|
||||
if opts := ep.recorder.Options; opts != nil && opts.HTTPBody {
|
||||
bodySize := opts.MaxBodySize
|
||||
if bodySize <= 0 {
|
||||
bodySize = sniffing.DefaultBodySize
|
||||
}
|
||||
if bodySize > sniffing.MaxBodySize {
|
||||
bodySize = sniffing.MaxBodySize
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
if _, err := io.Copy(buf, io.LimitReader(fr.Data, int64(bodySize))); err != nil {
|
||||
return err
|
||||
}
|
||||
ws.Payload = buf.Bytes()
|
||||
}
|
||||
|
||||
ro.Websocket = ws
|
||||
length := uint64(fr.Header.Length()) + uint64(fr.Header.PayloadLength)
|
||||
if from == "client" {
|
||||
ro.InputBytes = length
|
||||
ro.OutputBytes = 0
|
||||
} else {
|
||||
ro.InputBytes = 0
|
||||
ro.OutputBytes = length
|
||||
}
|
||||
|
||||
fr.Data = io.MultiReader(bytes.NewReader(buf.Bytes()), fr.Data)
|
||||
if _, err := fr.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ep *entrypoint) handleConnect(ctx context.Context, conn net.Conn, ro *xrecorder.HandlerRecorderObject, log logger.Logger) (err error) {
|
||||
|
||||
@@ -79,7 +79,7 @@ func (h *tunnelHandler) Init(md md.Metadata) (err error) {
|
||||
h.log = h.options.Logger.WithFields(map[string]any{
|
||||
"node": h.id,
|
||||
})
|
||||
h.pool = NewConnectorPool(h.id, h.md.sd)
|
||||
h.pool = NewConnectorPool(h.id)
|
||||
|
||||
h.ep = &entrypoint{
|
||||
node: h.id,
|
||||
@@ -90,12 +90,15 @@ func (h *tunnelHandler) Init(md md.Metadata) (err error) {
|
||||
log: h.log.WithFields(map[string]any{
|
||||
"kind": "entrypoint",
|
||||
}),
|
||||
sniffingWebsocket: h.md.sniffingWebsocket,
|
||||
websocketSampleRate: h.md.sniffingWebsocketSampleRate,
|
||||
}
|
||||
h.ep.transport = &http.Transport{
|
||||
DialContext: h.ep.dial,
|
||||
IdleConnTimeout: 30 * time.Second,
|
||||
ResponseHeaderTimeout: h.md.entryPointReadTimeout,
|
||||
DisableKeepAlives: !h.md.entryPointKeepalive,
|
||||
DisableCompression: !h.md.entryPointCompression,
|
||||
}
|
||||
|
||||
for _, ro := range h.options.Recorders {
|
||||
@@ -117,7 +120,7 @@ func (h *tunnelHandler) Init(md md.Metadata) (err error) {
|
||||
}
|
||||
|
||||
if limiter := h.options.Limiter; limiter != nil {
|
||||
h.limiter = limiter_util.NewCachedTrafficLimiter(limiter, 30*time.Second, 60*time.Second)
|
||||
h.limiter = limiter_util.NewCachedTrafficLimiter(limiter, h.md.limiterRefreshInterval, 60*time.Second)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -317,11 +320,7 @@ func (h *tunnelHandler) observeStats(ctx context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
d := h.md.observePeriod
|
||||
if d < time.Millisecond {
|
||||
d = 5 * time.Second
|
||||
}
|
||||
ticker := time.NewTicker(d)
|
||||
ticker := time.NewTicker(h.md.observePeriod)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
|
||||
+35
-16
@@ -22,18 +22,22 @@ const (
|
||||
type metadata struct {
|
||||
readTimeout time.Duration
|
||||
|
||||
entryPoint string
|
||||
entryPointID relay.TunnelID
|
||||
entryPointProxyProtocol int
|
||||
entryPointKeepalive bool
|
||||
entryPointReadTimeout time.Duration
|
||||
entryPoint string
|
||||
entryPointID relay.TunnelID
|
||||
entryPointProxyProtocol int
|
||||
entryPointKeepalive bool
|
||||
entryPointCompression bool
|
||||
entryPointReadTimeout time.Duration
|
||||
sniffingWebsocket bool
|
||||
sniffingWebsocketSampleRate float64
|
||||
|
||||
directTunnel bool
|
||||
tunnelTTL time.Duration
|
||||
ingress ingress.Ingress
|
||||
sd sd.SD
|
||||
muxCfg *mux.Config
|
||||
observePeriod time.Duration
|
||||
directTunnel bool
|
||||
tunnelTTL time.Duration
|
||||
ingress ingress.Ingress
|
||||
sd sd.SD
|
||||
muxCfg *mux.Config
|
||||
observePeriod time.Duration
|
||||
limiterRefreshInterval time.Duration
|
||||
}
|
||||
|
||||
func (h *tunnelHandler) parseMetadata(md mdata.Metadata) (err error) {
|
||||
@@ -43,16 +47,17 @@ func (h *tunnelHandler) parseMetadata(md mdata.Metadata) (err error) {
|
||||
h.md.entryPointID = parseTunnelID(mdutil.GetString(md, "entrypoint.id"))
|
||||
h.md.entryPointProxyProtocol = mdutil.GetInt(md, "entrypoint.ProxyProtocol")
|
||||
|
||||
h.md.entryPointKeepalive = true
|
||||
if mdutil.IsExists(md, "entrypoint.keepalive") {
|
||||
h.md.entryPointKeepalive = mdutil.GetBool(md, "entrypoint.keepalive")
|
||||
}
|
||||
h.md.entryPointKeepalive = mdutil.GetBool(md, "entrypoint.keepalive")
|
||||
h.md.entryPointCompression = mdutil.GetBool(md, "entrypoint.compression")
|
||||
|
||||
h.md.entryPointReadTimeout = mdutil.GetDuration(md, "entrypoint.readTimeout")
|
||||
if h.md.entryPointReadTimeout <= 0 {
|
||||
h.md.entryPointReadTimeout = 15 * time.Second
|
||||
}
|
||||
|
||||
h.md.sniffingWebsocket = mdutil.GetBool(md, "sniffing.websocket")
|
||||
h.md.sniffingWebsocketSampleRate = mdutil.GetFloat(md, "sniffing.websocket.sampleRate")
|
||||
|
||||
h.md.tunnelTTL = mdutil.GetDuration(md, "tunnel.ttl")
|
||||
if h.md.tunnelTTL <= 0 {
|
||||
h.md.tunnelTTL = defaultTTL
|
||||
@@ -100,7 +105,21 @@ func (h *tunnelHandler) parseMetadata(md mdata.Metadata) (err error) {
|
||||
h.md.muxCfg.MaxStreamBuffer = 1048576
|
||||
}
|
||||
|
||||
h.md.observePeriod = mdutil.GetDuration(md, "observePeriod")
|
||||
h.md.observePeriod = mdutil.GetDuration(md, "observePeriod", "observer.observePeriod")
|
||||
if h.md.observePeriod == 0 {
|
||||
h.md.observePeriod = 5 * time.Second
|
||||
}
|
||||
if h.md.observePeriod < time.Second {
|
||||
h.md.observePeriod = time.Second
|
||||
}
|
||||
|
||||
h.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if h.md.limiterRefreshInterval == 0 {
|
||||
h.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if h.md.limiterRefreshInterval < time.Second {
|
||||
h.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
+46
-34
@@ -38,6 +38,7 @@ type Connector struct {
|
||||
s *mux.Session
|
||||
t time.Time
|
||||
opts *ConnectorOptions
|
||||
log logger.Logger
|
||||
}
|
||||
|
||||
func NewConnector(id relay.ConnectorID, tid relay.TunnelID, node string, s *mux.Session, opts *ConnectorOptions) *Connector {
|
||||
@@ -52,23 +53,30 @@ func NewConnector(id relay.ConnectorID, tid relay.TunnelID, node string, s *mux.
|
||||
s: s,
|
||||
t: time.Now(),
|
||||
opts: opts,
|
||||
log: logger.Default().WithFields(map[string]any{
|
||||
"node": node,
|
||||
"tunnel": tid.String(),
|
||||
"connector": id.String(),
|
||||
}),
|
||||
}
|
||||
go c.accept()
|
||||
|
||||
go c.waitClose()
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Connector) accept() {
|
||||
func (c *Connector) waitClose() {
|
||||
for {
|
||||
conn, err := c.s.Accept()
|
||||
if err != nil {
|
||||
logger.Default().Errorf("connector %s: %v", c.id, err)
|
||||
c.s.Close()
|
||||
c.log.Errorf("connector %s: %v", c.id, err)
|
||||
c.Close()
|
||||
if c.opts.sd != nil {
|
||||
c.opts.sd.Deregister(context.Background(), &sd.Service{
|
||||
ID: c.id.String(),
|
||||
Name: c.tid.String(),
|
||||
Node: c.node,
|
||||
})
|
||||
c.log.Debugf("deregister connector %s from sd", c.id.String())
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -132,7 +140,6 @@ type Tunnel struct {
|
||||
t time.Time
|
||||
close chan struct{}
|
||||
mu sync.RWMutex
|
||||
sd sd.SD
|
||||
ttl time.Duration
|
||||
}
|
||||
|
||||
@@ -151,10 +158,6 @@ func NewTunnel(node string, tid relay.TunnelID, ttl time.Duration) *Tunnel {
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *Tunnel) WithSD(sd sd.SD) {
|
||||
t.sd = sd
|
||||
}
|
||||
|
||||
func (t *Tunnel) ID() relay.TunnelID {
|
||||
return t.id
|
||||
}
|
||||
@@ -175,6 +178,9 @@ func (t *Tunnel) GetConnector(network string) *Connector {
|
||||
defer t.mu.RUnlock()
|
||||
|
||||
if len(t.connectors) == 1 {
|
||||
if t.connectors[0].IsClosed() {
|
||||
return nil
|
||||
}
|
||||
return t.connectors[0]
|
||||
}
|
||||
|
||||
@@ -250,23 +256,17 @@ func (t *Tunnel) clean() {
|
||||
t.mu.Unlock()
|
||||
break
|
||||
}
|
||||
|
||||
var connectors []*Connector
|
||||
for _, c := range t.connectors {
|
||||
if c.IsClosed() {
|
||||
logger.Default().Debugf("remove tunnel: %s, connector: %s", t.id, c.id)
|
||||
if t.sd != nil {
|
||||
t.sd.Deregister(context.Background(), &sd.Service{
|
||||
ID: c.id.String(),
|
||||
Name: t.id.String(),
|
||||
Node: t.node,
|
||||
})
|
||||
}
|
||||
c.log.Debugf("remove connector: %s %s", t.id, c.id)
|
||||
continue
|
||||
}
|
||||
|
||||
connectors = append(connectors, c)
|
||||
if t.sd != nil {
|
||||
t.sd.Renew(context.Background(), &sd.Service{
|
||||
if c.opts.sd != nil {
|
||||
c.opts.sd.Renew(context.Background(), &sd.Service{
|
||||
ID: c.id.String(),
|
||||
Name: t.id.String(),
|
||||
Node: t.node,
|
||||
@@ -277,6 +277,7 @@ func (t *Tunnel) clean() {
|
||||
t.connectors = connectors
|
||||
}
|
||||
t.mu.Unlock()
|
||||
|
||||
case <-t.close:
|
||||
return
|
||||
}
|
||||
@@ -285,18 +286,21 @@ func (t *Tunnel) clean() {
|
||||
|
||||
type ConnectorPool struct {
|
||||
node string
|
||||
sd sd.SD
|
||||
tunnels map[string]*Tunnel
|
||||
mu sync.RWMutex
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
func NewConnectorPool(node string, sd sd.SD) *ConnectorPool {
|
||||
func NewConnectorPool(node string) *ConnectorPool {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
p := &ConnectorPool{
|
||||
node: node,
|
||||
sd: sd,
|
||||
tunnels: make(map[string]*Tunnel),
|
||||
cancel: cancel,
|
||||
}
|
||||
go p.closeIdles()
|
||||
|
||||
go p.closeIdles(ctx)
|
||||
return p
|
||||
}
|
||||
|
||||
@@ -309,8 +313,6 @@ func (p *ConnectorPool) Add(tid relay.TunnelID, c *Connector, ttl time.Duration)
|
||||
t := p.tunnels[s]
|
||||
if t == nil {
|
||||
t = NewTunnel(p.node, tid, ttl)
|
||||
t.WithSD(p.sd)
|
||||
|
||||
p.tunnels[s] = t
|
||||
}
|
||||
t.AddConnector(c)
|
||||
@@ -337,6 +339,10 @@ func (p *ConnectorPool) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if p.cancel != nil {
|
||||
p.cancel()
|
||||
}
|
||||
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
@@ -348,19 +354,25 @@ func (p *ConnectorPool) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ConnectorPool) closeIdles() {
|
||||
ticker := time.NewTicker(1 * time.Hour)
|
||||
func (p *ConnectorPool) closeIdles(ctx context.Context) {
|
||||
ticker := time.NewTicker(15 * time.Minute)
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
p.mu.Lock()
|
||||
for k, v := range p.tunnels {
|
||||
if v.CloseOnIdle() {
|
||||
delete(p.tunnels, k)
|
||||
logger.Default().Debugf("remove idle tunnel: %s", k)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
p.mu.Lock()
|
||||
for k, v := range p.tunnels {
|
||||
if v.CloseOnIdle() {
|
||||
delete(p.tunnels, k)
|
||||
logger.Default().Debugf("remove idle tunnel: %s", k)
|
||||
}
|
||||
}
|
||||
p.mu.Unlock()
|
||||
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
p.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -126,6 +126,7 @@ func (h *unixHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler
|
||||
log = log.WithFields(map[string]any{
|
||||
"node": target.Name,
|
||||
"dst": target.Addr,
|
||||
"host": target.Addr,
|
||||
})
|
||||
ro.Host = target.Addr
|
||||
|
||||
|
||||
+77
-107
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/go-gost/core/chain"
|
||||
"github.com/go-gost/core/hop"
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/core/routing"
|
||||
"github.com/go-gost/core/selector"
|
||||
"github.com/go-gost/x/config"
|
||||
node_parser "github.com/go-gost/x/config/parsing/node"
|
||||
@@ -132,18 +133,16 @@ func (p *chainHop) Select(ctx context.Context, opts ...hop.SelectOption) *chain.
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
log := p.options.logger
|
||||
|
||||
// hop level bypass
|
||||
if p.options.bypass != nil &&
|
||||
p.options.bypass.Contains(ctx, options.Network, options.Addr, bypass.WithHostOpton(options.Host)) {
|
||||
return nil
|
||||
}
|
||||
|
||||
filters := p.filterByHost(options.Host, p.Nodes()...)
|
||||
filters = p.filterByProtocol(options.Protocol, filters...)
|
||||
filters = p.filterByPath(options.Path, filters...)
|
||||
|
||||
var nodes []*chain.Node
|
||||
for _, node := range filters {
|
||||
for _, node := range p.Nodes() {
|
||||
if node == nil {
|
||||
continue
|
||||
}
|
||||
@@ -153,11 +152,42 @@ func (p *chainHop) Select(ctx context.Context, opts ...hop.SelectOption) *chain.
|
||||
continue
|
||||
}
|
||||
|
||||
if matcher := node.Options().Matcher; matcher != nil {
|
||||
req := routing.Request{
|
||||
ClientIP: options.ClientIP,
|
||||
Host: options.Host,
|
||||
Protocol: options.Protocol,
|
||||
Method: options.Method,
|
||||
Path: options.Path,
|
||||
Query: options.Query,
|
||||
Header: options.Header,
|
||||
}
|
||||
if !matcher.Match(&req) {
|
||||
continue
|
||||
}
|
||||
log.Debugf("node %s match request %s %s, priority %d", node.Name, req.Protocol, req.Host, node.Options().Priority)
|
||||
} else {
|
||||
if !p.isEligible(node, &options) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
nodes = append(nodes, node)
|
||||
}
|
||||
if len(nodes) == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(nodes) == 1 {
|
||||
return nodes[0]
|
||||
}
|
||||
|
||||
sort.Slice(nodes, func(i, j int) bool {
|
||||
return nodes[i].Options().Priority > nodes[j].Options().Priority
|
||||
})
|
||||
|
||||
if nodes[0].Options().Priority > 0 {
|
||||
return nodes[0]
|
||||
}
|
||||
|
||||
if s := p.options.selector; s != nil {
|
||||
return s.Select(ctx, nodes...)
|
||||
@@ -165,126 +195,66 @@ func (p *chainHop) Select(ctx context.Context, opts ...hop.SelectOption) *chain.
|
||||
return nodes[0]
|
||||
}
|
||||
|
||||
func (p *chainHop) filterByHost(host string, nodes ...*chain.Node) (filters []*chain.Node) {
|
||||
if host == "" || len(nodes) == 0 {
|
||||
return nodes
|
||||
func (p *chainHop) isEligible(node *chain.Node, opts *hop.SelectOptions) bool {
|
||||
if node == nil {
|
||||
return false
|
||||
}
|
||||
if node.Options().Filter == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if !p.checkHost(opts.Host, node) || !p.checkProtocol(opts.Protocol, node) || !p.checkPath(opts.Path, node) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *chainHop) checkHost(host string, node *chain.Node) bool {
|
||||
var vhost string
|
||||
if filter := node.Options().Filter; filter != nil {
|
||||
vhost = filter.Host
|
||||
}
|
||||
if vhost == "" { // backup node
|
||||
return true
|
||||
}
|
||||
|
||||
if host == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if v, _, _ := net.SplitHostPort(host); v != "" {
|
||||
host = v
|
||||
}
|
||||
p.options.logger.Debugf("filter by host: %s", host)
|
||||
|
||||
found := false
|
||||
for _, node := range nodes {
|
||||
if node == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var vhost string
|
||||
if filter := node.Options().Filter; filter != nil {
|
||||
vhost = filter.Host
|
||||
}
|
||||
if vhost == "" { // backup node
|
||||
if !found {
|
||||
filters = append(filters, node)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if vhost == host ||
|
||||
vhost[0] == '.' && strings.HasSuffix(host, vhost[1:]) {
|
||||
if !found { // clear all backup nodes when matched node found
|
||||
filters = nil
|
||||
}
|
||||
filters = append(filters, node)
|
||||
found = true
|
||||
continue
|
||||
}
|
||||
|
||||
if vhost == host || vhost[0] == '.' && strings.HasSuffix(host, vhost[1:]) {
|
||||
return true
|
||||
}
|
||||
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *chainHop) filterByProtocol(protocol string, nodes ...*chain.Node) (filters []*chain.Node) {
|
||||
if protocol == "" || len(nodes) == 0 {
|
||||
return nodes
|
||||
func (p *chainHop) checkProtocol(protocol string, node *chain.Node) bool {
|
||||
var prot string
|
||||
if filter := node.Options().Filter; filter != nil {
|
||||
prot = filter.Protocol
|
||||
}
|
||||
|
||||
p.options.logger.Debugf("filter by protocol: %s", protocol)
|
||||
found := false
|
||||
for _, node := range nodes {
|
||||
if node == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var prot string
|
||||
if filter := node.Options().Filter; filter != nil {
|
||||
prot = filter.Protocol
|
||||
}
|
||||
if prot == "" {
|
||||
if !found {
|
||||
filters = append(filters, node)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if prot == protocol {
|
||||
if !found {
|
||||
filters = nil
|
||||
}
|
||||
filters = append(filters, node)
|
||||
found = true
|
||||
continue
|
||||
}
|
||||
if prot == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
return
|
||||
return prot == protocol
|
||||
}
|
||||
|
||||
func (p *chainHop) filterByPath(path string, nodes ...*chain.Node) (filters []*chain.Node) {
|
||||
if path == "" || len(nodes) == 0 {
|
||||
return nodes
|
||||
func (p *chainHop) checkPath(path string, node *chain.Node) bool {
|
||||
var pathFilter string
|
||||
if filter := node.Options().Filter; filter != nil {
|
||||
pathFilter = filter.Path
|
||||
}
|
||||
|
||||
p.options.logger.Debugf("filter by path: %s", path)
|
||||
|
||||
sort.SliceStable(nodes, func(i, j int) bool {
|
||||
filter1 := nodes[i].Options().Filter
|
||||
if filter1 == nil {
|
||||
return false
|
||||
}
|
||||
filter2 := nodes[j].Options().Filter
|
||||
if filter2 == nil {
|
||||
return true
|
||||
}
|
||||
return len(filter1.Path) > len(filter2.Path)
|
||||
})
|
||||
|
||||
found := false
|
||||
for _, node := range nodes {
|
||||
var pathFilter string
|
||||
if filter := node.Options().Filter; filter != nil {
|
||||
pathFilter = filter.Path
|
||||
}
|
||||
if pathFilter == "" {
|
||||
if !found {
|
||||
filters = append(filters, node)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(path, pathFilter) {
|
||||
if !found {
|
||||
filters = nil
|
||||
}
|
||||
filters = append(filters, node)
|
||||
break
|
||||
}
|
||||
if pathFilter == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
return
|
||||
return strings.HasPrefix(path, pathFilter)
|
||||
}
|
||||
|
||||
func (p *chainHop) periodReload(ctx context.Context) error {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package ctx
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
xrecorder "github.com/go-gost/x/recorder"
|
||||
)
|
||||
|
||||
type recorderObjectCtxKey struct{}
|
||||
|
||||
var (
|
||||
ctxKeyRecorderObject = &recorderObjectCtxKey{}
|
||||
)
|
||||
|
||||
func ContextWithRecorderObject(ctx context.Context, ro *xrecorder.HandlerRecorderObject) context.Context {
|
||||
return context.WithValue(ctx, ctxKeyRecorderObject, ro)
|
||||
}
|
||||
|
||||
func RecorderObjectFromContext(ctx context.Context) *xrecorder.HandlerRecorderObject {
|
||||
v, _ := ctx.Value(ctxKeyRecorderObject).(*xrecorder.HandlerRecorderObject)
|
||||
return v
|
||||
}
|
||||
@@ -56,7 +56,6 @@ func NewGRPCConn(addr string, opts *Options) (*grpc.ClientConn, error) {
|
||||
grpc.WithConnectParams(grpc.ConnectParams{
|
||||
Backoff: backoff.DefaultConfig,
|
||||
}),
|
||||
grpc.FailOnNonTempDialError(true),
|
||||
}
|
||||
if opts.TLSConfig != nil {
|
||||
grpcOpts = append(grpcOpts,
|
||||
@@ -69,7 +68,7 @@ func NewGRPCConn(addr string, opts *Options) (*grpc.ClientConn, error) {
|
||||
if opts.Token != "" {
|
||||
grpcOpts = append(grpcOpts, grpc.WithPerRPCCredentials(&rpcCredentials{token: opts.Token}))
|
||||
}
|
||||
return grpc.Dial(addr, grpcOpts...)
|
||||
return grpc.NewClient(addr, grpcOpts...)
|
||||
}
|
||||
|
||||
type rpcCredentials struct {
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
@@ -32,14 +33,12 @@ import (
|
||||
xhttp "github.com/go-gost/x/internal/net/http"
|
||||
"github.com/go-gost/x/internal/util/sniffing"
|
||||
tls_util "github.com/go-gost/x/internal/util/tls"
|
||||
ws_util "github.com/go-gost/x/internal/util/ws"
|
||||
stats_wrapper "github.com/go-gost/x/observer/stats/wrapper"
|
||||
xrecorder "github.com/go-gost/x/recorder"
|
||||
"golang.org/x/net/http/httpguts"
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
|
||||
const (
|
||||
// Default max body size to record.
|
||||
DefaultBodySize = 1024 * 1024 // 1MB
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -102,6 +101,9 @@ func WithLog(log logger.Logger) HandleOption {
|
||||
}
|
||||
|
||||
type Sniffer struct {
|
||||
Websocket bool
|
||||
WebsocketSampleRate float64
|
||||
|
||||
Recorder recorder.Recorder
|
||||
RecorderOptions *recorder.Options
|
||||
|
||||
@@ -177,7 +179,7 @@ func (h *Sniffer) HandleHTTP(ctx context.Context, conn net.Conn, opts ...HandleO
|
||||
|
||||
ro.Time = time.Time{}
|
||||
|
||||
shouldClose, err := h.httpRoundTrip(ctx, conn, cc, node, req, &pStats, &ho)
|
||||
shouldClose, err := h.httpRoundTrip(ctx, xio.NewReadWriter(br, conn), cc, node, req, &pStats, &ho)
|
||||
if err != nil || shouldClose {
|
||||
return err
|
||||
}
|
||||
@@ -198,7 +200,7 @@ func (h *Sniffer) HandleHTTP(ctx context.Context, conn net.Conn, opts ...HandleO
|
||||
log.Trace(string(dump))
|
||||
}
|
||||
|
||||
if shouldClose, err := h.httpRoundTrip(ctx, conn, cc, node, req, &pStats, &ho); err != nil || shouldClose {
|
||||
if shouldClose, err := h.httpRoundTrip(ctx, xio.NewReadWriter(br, conn), cc, node, req, &pStats, &ho); err != nil || shouldClose {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -249,9 +251,13 @@ func (h *Sniffer) dial(ctx context.Context, conn net.Conn, req *http.Request, ho
|
||||
}
|
||||
if ho.Hop != nil {
|
||||
node = ho.Hop.Select(ctx,
|
||||
hop.HostSelectOption(host),
|
||||
hop.ClientIPSelectOption(net.ParseIP(ro.ClientIP)),
|
||||
hop.ProtocolSelectOption(sniffing.ProtoHTTP),
|
||||
hop.HostSelectOption(host),
|
||||
hop.MethodSelectOption(req.Method),
|
||||
hop.PathSelectOption(req.URL.Path),
|
||||
hop.QuerySelectOption(req.URL.Query()),
|
||||
hop.HeaderSelectOption(req.Header),
|
||||
)
|
||||
}
|
||||
if node == nil {
|
||||
@@ -398,11 +404,8 @@ func (h *Sniffer) httpRoundTrip(ctx context.Context, rw, cc io.ReadWriter, node
|
||||
}
|
||||
}
|
||||
|
||||
if !ho.HTTPKeepalive {
|
||||
req.Header.Set("Connection", "close")
|
||||
}
|
||||
|
||||
var bodyRewrites []chain.HTTPBodyRewriteSettings
|
||||
var responseHeader map[string]string
|
||||
var respBodyRewrites []chain.HTTPBodyRewriteSettings
|
||||
if httpSettings := node.Options().HTTP; httpSettings != nil {
|
||||
if auther := httpSettings.Auther; auther != nil {
|
||||
username, password, _ := req.BasicAuth()
|
||||
@@ -422,7 +425,7 @@ func (h *Sniffer) httpRoundTrip(ctx context.Context, rw, cc io.ReadWriter, node
|
||||
if httpSettings.Host != "" {
|
||||
req.Host = httpSettings.Host
|
||||
}
|
||||
for k, v := range httpSettings.Header {
|
||||
for k, v := range httpSettings.RequestHeader {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
|
||||
@@ -435,31 +438,37 @@ func (h *Sniffer) httpRoundTrip(ctx context.Context, rw, cc io.ReadWriter, node
|
||||
}
|
||||
}
|
||||
|
||||
bodyRewrites = httpSettings.RewriteBody
|
||||
responseHeader = httpSettings.ResponseHeader
|
||||
respBodyRewrites = httpSettings.RewriteResponseBody
|
||||
}
|
||||
|
||||
var reqBody *xhttp.Body
|
||||
if opts := h.RecorderOptions; opts != nil && opts.HTTPBody {
|
||||
if req.Body != nil {
|
||||
maxSize := opts.MaxBodySize
|
||||
if maxSize <= 0 {
|
||||
maxSize = DefaultBodySize
|
||||
bodySize := opts.MaxBodySize
|
||||
if bodySize <= 0 {
|
||||
bodySize = sniffing.DefaultBodySize
|
||||
}
|
||||
reqBody = xhttp.NewBody(req.Body, maxSize)
|
||||
if bodySize > sniffing.MaxBodySize {
|
||||
bodySize = sniffing.MaxBodySize
|
||||
}
|
||||
reqBody = xhttp.NewBody(req.Body, bodySize)
|
||||
req.Body = reqBody
|
||||
}
|
||||
}
|
||||
|
||||
if err = req.Write(cc); err != nil {
|
||||
res.Write(rw)
|
||||
return
|
||||
}
|
||||
err = req.Write(cc)
|
||||
|
||||
if reqBody != nil {
|
||||
ro.HTTP.Request.Body = reqBody.Content()
|
||||
ro.HTTP.Request.ContentLength = reqBody.Length()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
res.Write(rw)
|
||||
return
|
||||
}
|
||||
|
||||
xio.SetReadDeadline(cc, time.Now().Add(h.ReadTimeout))
|
||||
resp, err := http.ReadResponse(bufio.NewReader(cc), req)
|
||||
if err != nil {
|
||||
@@ -470,6 +479,15 @@ func (h *Sniffer) httpRoundTrip(ctx context.Context, rw, cc io.ReadWriter, node
|
||||
defer resp.Body.Close()
|
||||
xio.SetReadDeadline(cc, time.Time{})
|
||||
|
||||
if len(responseHeader) > 0 {
|
||||
if resp.Header == nil {
|
||||
resp.Header = http.Header{}
|
||||
}
|
||||
for k, v := range responseHeader {
|
||||
resp.Header.Set(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
ro.HTTP.StatusCode = resp.StatusCode
|
||||
ro.HTTP.Response.Header = resp.Header
|
||||
ro.HTTP.Response.ContentLength = resp.ContentLength
|
||||
@@ -479,6 +497,11 @@ func (h *Sniffer) httpRoundTrip(ctx context.Context, rw, cc io.ReadWriter, node
|
||||
log.Trace(string(dump))
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusSwitchingProtocols {
|
||||
h.handleUpgradeResponse(ctx, rw, cc, req, resp, ro, log)
|
||||
return
|
||||
}
|
||||
|
||||
// HTTP/1.0
|
||||
if req.ProtoMajor == 1 && req.ProtoMinor == 0 {
|
||||
if !resp.Close {
|
||||
@@ -488,39 +511,192 @@ func (h *Sniffer) httpRoundTrip(ctx context.Context, rw, cc io.ReadWriter, node
|
||||
resp.ProtoMinor = req.ProtoMinor
|
||||
}
|
||||
|
||||
if err = h.rewriteBody(resp, bodyRewrites...); err != nil {
|
||||
if !ho.HTTPKeepalive {
|
||||
resp.Header.Set("Connection", "close")
|
||||
}
|
||||
|
||||
if err = h.rewriteRespBody(resp, respBodyRewrites...); err != nil {
|
||||
log.Errorf("rewrite body: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
var respBody *xhttp.Body
|
||||
if opts := h.RecorderOptions; opts != nil && opts.HTTPBody {
|
||||
maxSize := opts.MaxBodySize
|
||||
if maxSize <= 0 {
|
||||
maxSize = DefaultBodySize
|
||||
bodySize := opts.MaxBodySize
|
||||
if bodySize <= 0 {
|
||||
bodySize = sniffing.DefaultBodySize
|
||||
}
|
||||
respBody = xhttp.NewBody(resp.Body, maxSize)
|
||||
if bodySize > sniffing.MaxBodySize {
|
||||
bodySize = sniffing.MaxBodySize
|
||||
}
|
||||
respBody = xhttp.NewBody(resp.Body, bodySize)
|
||||
resp.Body = respBody
|
||||
}
|
||||
|
||||
if err = resp.Write(rw); err != nil {
|
||||
log.Errorf("write response: %v", err)
|
||||
return
|
||||
}
|
||||
err = resp.Write(rw)
|
||||
|
||||
if respBody != nil {
|
||||
ro.HTTP.Response.Body = respBody.Content()
|
||||
ro.HTTP.Response.ContentLength = respBody.Length()
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusSwitchingProtocols {
|
||||
xnet.Transport(rw, cc)
|
||||
if err != nil {
|
||||
log.Errorf("write response: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
return resp.Close, nil
|
||||
if resp.ContentLength >= 0 {
|
||||
close = resp.Close
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (h *Sniffer) rewriteBody(resp *http.Response, rewrites ...chain.HTTPBodyRewriteSettings) error {
|
||||
func upgradeType(h http.Header) string {
|
||||
if !httpguts.HeaderValuesContainsToken(h["Connection"], "Upgrade") {
|
||||
return ""
|
||||
}
|
||||
return h.Get("Upgrade")
|
||||
}
|
||||
|
||||
func (h *Sniffer) handleUpgradeResponse(ctx context.Context, rw io.ReadWriter, cc io.ReadWriter, req *http.Request, res *http.Response, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
|
||||
reqUpType := upgradeType(req.Header)
|
||||
resUpType := upgradeType(res.Header)
|
||||
if !strings.EqualFold(reqUpType, resUpType) {
|
||||
return fmt.Errorf("backend tried to switch protocol %q when %q was requested", resUpType, reqUpType)
|
||||
}
|
||||
|
||||
res.Body = nil
|
||||
if err := res.Write(rw); err != nil {
|
||||
return fmt.Errorf("response write: %v", err)
|
||||
}
|
||||
|
||||
if reqUpType == "websocket" && h.Websocket {
|
||||
return h.sniffingWebsocketFrame(ctx, rw, cc, ro, log)
|
||||
}
|
||||
|
||||
return xnet.Transport(rw, cc)
|
||||
}
|
||||
|
||||
func (h *Sniffer) sniffingWebsocketFrame(ctx context.Context, rw, cc io.ReadWriter, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
|
||||
errc := make(chan error, 1)
|
||||
|
||||
sampleRate := h.WebsocketSampleRate
|
||||
if sampleRate == 0 {
|
||||
sampleRate = sniffing.DefaultSampleRate
|
||||
}
|
||||
if sampleRate < 0 {
|
||||
sampleRate = math.MaxFloat64
|
||||
}
|
||||
|
||||
go func() {
|
||||
ro2 := &xrecorder.HandlerRecorderObject{}
|
||||
*ro2 = *ro
|
||||
ro := ro2
|
||||
|
||||
limiter := rate.NewLimiter(rate.Limit(sampleRate), int(sampleRate))
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
for {
|
||||
start := time.Now()
|
||||
|
||||
if err := h.copyWebsocketFrame(cc, rw, buf, "client", ro); err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
|
||||
if limiter.Allow() {
|
||||
ro.Duration = time.Since(start)
|
||||
ro.Time = time.Now()
|
||||
if err := ro.Record(ctx, h.Recorder); err != nil {
|
||||
log.Errorf("record: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
ro2 := &xrecorder.HandlerRecorderObject{}
|
||||
*ro2 = *ro
|
||||
ro := ro2
|
||||
|
||||
limiter := rate.NewLimiter(rate.Limit(sampleRate), int(sampleRate))
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
for {
|
||||
start := time.Now()
|
||||
|
||||
if err := h.copyWebsocketFrame(rw, cc, buf, "server", ro); err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
|
||||
if limiter.Allow() {
|
||||
ro.Duration = time.Since(start)
|
||||
ro.Time = time.Now()
|
||||
if err := ro.Record(ctx, h.Recorder); err != nil {
|
||||
log.Errorf("record: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
<-errc
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Sniffer) copyWebsocketFrame(w io.Writer, r io.Reader, buf *bytes.Buffer, from string, ro *xrecorder.HandlerRecorderObject) (err error) {
|
||||
fr := ws_util.Frame{}
|
||||
if _, err = fr.ReadFrom(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ws := &xrecorder.WebsocketRecorderObject{
|
||||
From: from,
|
||||
Fin: fr.Header.Fin,
|
||||
Rsv1: fr.Header.Rsv1,
|
||||
Rsv2: fr.Header.Rsv2,
|
||||
Rsv3: fr.Header.Rsv3,
|
||||
OpCode: int(fr.Header.OpCode),
|
||||
Masked: fr.Header.Masked,
|
||||
MaskKey: fr.Header.MaskKey,
|
||||
Length: fr.Header.PayloadLength,
|
||||
}
|
||||
if opts := h.RecorderOptions; opts != nil && opts.HTTPBody {
|
||||
bodySize := opts.MaxBodySize
|
||||
if bodySize <= 0 {
|
||||
bodySize = sniffing.DefaultBodySize
|
||||
}
|
||||
if bodySize > sniffing.MaxBodySize {
|
||||
bodySize = sniffing.MaxBodySize
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
if _, err := io.Copy(buf, io.LimitReader(fr.Data, int64(bodySize))); err != nil {
|
||||
return err
|
||||
}
|
||||
ws.Payload = buf.Bytes()
|
||||
}
|
||||
|
||||
ro.Websocket = ws
|
||||
length := uint64(fr.Header.Length()) + uint64(fr.Header.PayloadLength)
|
||||
if from == "client" {
|
||||
ro.InputBytes = length
|
||||
ro.OutputBytes = 0
|
||||
} else {
|
||||
ro.InputBytes = 0
|
||||
ro.OutputBytes = length
|
||||
}
|
||||
|
||||
fr.Data = io.MultiReader(bytes.NewReader(buf.Bytes()), fr.Data)
|
||||
if _, err := fr.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Sniffer) rewriteRespBody(resp *http.Response, rewrites ...chain.HTTPBodyRewriteSettings) error {
|
||||
if resp == nil || len(rewrites) == 0 || resp.ContentLength <= 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -914,11 +1090,14 @@ func (h *h2Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
var reqBody *xhttp.Body
|
||||
if opts := h.recorderOptions; opts != nil && opts.HTTPBody {
|
||||
if req.Body != nil {
|
||||
maxSize := opts.MaxBodySize
|
||||
if maxSize <= 0 {
|
||||
maxSize = DefaultBodySize
|
||||
bodySize := opts.MaxBodySize
|
||||
if bodySize <= 0 {
|
||||
bodySize = sniffing.DefaultBodySize
|
||||
}
|
||||
reqBody = xhttp.NewBody(req.Body, maxSize)
|
||||
if bodySize > sniffing.MaxBodySize {
|
||||
bodySize = sniffing.MaxBodySize
|
||||
}
|
||||
reqBody = xhttp.NewBody(req.Body, bodySize)
|
||||
req.Body = reqBody
|
||||
}
|
||||
}
|
||||
@@ -949,11 +1128,14 @@ func (h *h2Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var respBody *xhttp.Body
|
||||
if opts := h.recorderOptions; opts != nil && opts.HTTPBody {
|
||||
maxSize := opts.MaxBodySize
|
||||
if maxSize <= 0 {
|
||||
maxSize = DefaultBodySize
|
||||
bodySize := opts.MaxBodySize
|
||||
if bodySize <= 0 {
|
||||
bodySize = sniffing.DefaultBodySize
|
||||
}
|
||||
respBody = xhttp.NewBody(resp.Body, maxSize)
|
||||
if bodySize > sniffing.MaxBodySize {
|
||||
bodySize = sniffing.MaxBodySize
|
||||
}
|
||||
respBody = xhttp.NewBody(resp.Body, bodySize)
|
||||
resp.Body = respBody
|
||||
}
|
||||
|
||||
|
||||
@@ -9,22 +9,22 @@ import (
|
||||
)
|
||||
|
||||
type cachedTrafficLimiter struct {
|
||||
inLimits *Cache
|
||||
outLimits *Cache
|
||||
limiter traffic.TrafficLimiter
|
||||
ttl time.Duration
|
||||
inLimits *Cache
|
||||
outLimits *Cache
|
||||
limiter traffic.TrafficLimiter
|
||||
refreshInterval time.Duration
|
||||
}
|
||||
|
||||
func NewCachedTrafficLimiter(limiter traffic.TrafficLimiter, ttl time.Duration, cleanupInterval time.Duration) traffic.TrafficLimiter {
|
||||
func NewCachedTrafficLimiter(limiter traffic.TrafficLimiter, refreshInterval time.Duration, cleanupInterval time.Duration) traffic.TrafficLimiter {
|
||||
if limiter == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
lim := &cachedTrafficLimiter{
|
||||
inLimits: NewCache(cleanupInterval),
|
||||
outLimits: NewCache(cleanupInterval),
|
||||
limiter: limiter,
|
||||
ttl: ttl,
|
||||
inLimits: NewCache(cleanupInterval),
|
||||
outLimits: NewCache(cleanupInterval),
|
||||
limiter: limiter,
|
||||
refreshInterval: refreshInterval,
|
||||
}
|
||||
return lim
|
||||
}
|
||||
@@ -45,11 +45,11 @@ func (p *cachedTrafficLimiter) In(ctx context.Context, key string, opts ...limit
|
||||
limNew = lim
|
||||
}
|
||||
if item == nil || !p.equal(lim, limNew) {
|
||||
p.inLimits.Set(key, NewItem(limNew, p.ttl))
|
||||
p.inLimits.Set(key, NewItem(limNew, p.refreshInterval))
|
||||
return limNew
|
||||
}
|
||||
|
||||
p.inLimits.Set(key, NewItem(lim, p.ttl))
|
||||
p.inLimits.Set(key, NewItem(lim, p.refreshInterval))
|
||||
|
||||
return lim
|
||||
}
|
||||
@@ -70,11 +70,11 @@ func (p *cachedTrafficLimiter) Out(ctx context.Context, key string, opts ...limi
|
||||
limNew = lim
|
||||
}
|
||||
if item == nil || !p.equal(lim, limNew) {
|
||||
p.outLimits.Set(key, NewItem(limNew, p.ttl))
|
||||
p.outLimits.Set(key, NewItem(limNew, p.refreshInterval))
|
||||
return limNew
|
||||
}
|
||||
|
||||
p.outLimits.Set(key, NewItem(lim, p.ttl))
|
||||
p.outLimits.Set(key, NewItem(lim, p.refreshInterval))
|
||||
|
||||
return lim
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
@@ -28,14 +29,21 @@ import (
|
||||
xnet "github.com/go-gost/x/internal/net"
|
||||
xhttp "github.com/go-gost/x/internal/net/http"
|
||||
tls_util "github.com/go-gost/x/internal/util/tls"
|
||||
ws_util "github.com/go-gost/x/internal/util/ws"
|
||||
stats_wrapper "github.com/go-gost/x/observer/stats/wrapper"
|
||||
xrecorder "github.com/go-gost/x/recorder"
|
||||
"golang.org/x/net/http/httpguts"
|
||||
"golang.org/x/net/http2"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
const (
|
||||
// Default max body size to record.
|
||||
DefaultBodySize = 1024 * 1024 // 1MB
|
||||
// DefaultBodySize is the default HTTP body or websocket frame size to record.
|
||||
DefaultBodySize = 64 * 1024 // 64KB
|
||||
// MaxBodySize is the maximum HTTP body or websocket frame size to record.
|
||||
MaxBodySize = 1024 * 1024 // 1MB
|
||||
// DeafultSampleRate is the default websocket sample rate (samples per second).
|
||||
DefaultSampleRate = 10.0
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -84,6 +92,9 @@ func WithLog(log logger.Logger) HandleOption {
|
||||
}
|
||||
|
||||
type Sniffer struct {
|
||||
Websocket bool
|
||||
WebsocketSampleRate float64
|
||||
|
||||
Recorder recorder.Recorder
|
||||
RecorderOptions *recorder.Options
|
||||
|
||||
@@ -176,7 +187,7 @@ func (h *Sniffer) HandleHTTP(ctx context.Context, conn net.Conn, opts ...HandleO
|
||||
|
||||
ro.Time = time.Time{}
|
||||
|
||||
shouldClose, err := h.httpRoundTrip(ctx, conn, cc, req, ro, &pStats, log)
|
||||
shouldClose, err := h.httpRoundTrip(ctx, xio.NewReadWriter(br, conn), cc, req, ro, &pStats, log)
|
||||
if err != nil || shouldClose {
|
||||
return err
|
||||
}
|
||||
@@ -197,7 +208,7 @@ func (h *Sniffer) HandleHTTP(ctx context.Context, conn net.Conn, opts ...HandleO
|
||||
log.Trace(string(dump))
|
||||
}
|
||||
|
||||
if shouldClose, err := h.httpRoundTrip(ctx, conn, cc, req, ro, &pStats, log); err != nil || shouldClose {
|
||||
if shouldClose, err := h.httpRoundTrip(ctx, xio.NewReadWriter(br, conn), cc, req, ro, &pStats, log); err != nil || shouldClose {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -302,28 +313,33 @@ func (h *Sniffer) httpRoundTrip(ctx context.Context, rw, cc io.ReadWriter, req *
|
||||
var reqBody *xhttp.Body
|
||||
if opts := h.RecorderOptions; opts != nil && opts.HTTPBody {
|
||||
if req.Body != nil {
|
||||
maxSize := opts.MaxBodySize
|
||||
if maxSize <= 0 {
|
||||
maxSize = DefaultBodySize
|
||||
bodySize := opts.MaxBodySize
|
||||
if bodySize <= 0 {
|
||||
bodySize = DefaultBodySize
|
||||
}
|
||||
reqBody = xhttp.NewBody(req.Body, maxSize)
|
||||
if bodySize > MaxBodySize {
|
||||
bodySize = MaxBodySize
|
||||
}
|
||||
reqBody = xhttp.NewBody(req.Body, bodySize)
|
||||
req.Body = reqBody
|
||||
}
|
||||
}
|
||||
|
||||
if err = req.Write(cc); err != nil {
|
||||
return
|
||||
}
|
||||
err = req.Write(cc)
|
||||
|
||||
if reqBody != nil {
|
||||
ro.HTTP.Request.Body = reqBody.Content()
|
||||
ro.HTTP.Request.ContentLength = reqBody.Length()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
xio.SetReadDeadline(cc, time.Now().Add(h.ReadTimeout))
|
||||
resp, err := http.ReadResponse(bufio.NewReader(cc), req)
|
||||
if err != nil {
|
||||
log.Errorf("read response: %v", err)
|
||||
err = fmt.Errorf("read response: %w", err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
@@ -338,6 +354,11 @@ func (h *Sniffer) httpRoundTrip(ctx context.Context, rw, cc io.ReadWriter, req *
|
||||
log.Trace(string(dump))
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusSwitchingProtocols {
|
||||
h.handleUpgradeResponse(ctx, rw, cc, req, resp, ro, log)
|
||||
return
|
||||
}
|
||||
|
||||
// HTTP/1.0
|
||||
if req.ProtoMajor == 1 && req.ProtoMinor == 0 {
|
||||
if !resp.Close {
|
||||
@@ -349,29 +370,178 @@ func (h *Sniffer) httpRoundTrip(ctx context.Context, rw, cc io.ReadWriter, req *
|
||||
|
||||
var respBody *xhttp.Body
|
||||
if opts := h.RecorderOptions; opts != nil && opts.HTTPBody {
|
||||
maxSize := opts.MaxBodySize
|
||||
if maxSize <= 0 {
|
||||
maxSize = DefaultBodySize
|
||||
bodySize := opts.MaxBodySize
|
||||
if bodySize <= 0 {
|
||||
bodySize = DefaultBodySize
|
||||
}
|
||||
respBody = xhttp.NewBody(resp.Body, maxSize)
|
||||
if bodySize > MaxBodySize {
|
||||
bodySize = MaxBodySize
|
||||
}
|
||||
respBody = xhttp.NewBody(resp.Body, bodySize)
|
||||
resp.Body = respBody
|
||||
}
|
||||
|
||||
if err = resp.Write(rw); err != nil {
|
||||
log.Errorf("write response: %v", err)
|
||||
return
|
||||
}
|
||||
err = resp.Write(rw)
|
||||
|
||||
if respBody != nil {
|
||||
ro.HTTP.Response.Body = respBody.Content()
|
||||
ro.HTTP.Response.ContentLength = respBody.Length()
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusSwitchingProtocols {
|
||||
xnet.Transport(rw, cc)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("write response: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
return resp.Close, nil
|
||||
if resp.ContentLength >= 0 {
|
||||
close = resp.Close
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func upgradeType(h http.Header) string {
|
||||
if !httpguts.HeaderValuesContainsToken(h["Connection"], "Upgrade") {
|
||||
return ""
|
||||
}
|
||||
return h.Get("Upgrade")
|
||||
}
|
||||
|
||||
func (h *Sniffer) handleUpgradeResponse(ctx context.Context, rw io.ReadWriter, cc io.ReadWriter, req *http.Request, res *http.Response, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
|
||||
reqUpType := upgradeType(req.Header)
|
||||
resUpType := upgradeType(res.Header)
|
||||
if !strings.EqualFold(reqUpType, resUpType) {
|
||||
return fmt.Errorf("backend tried to switch protocol %q when %q was requested", resUpType, reqUpType)
|
||||
}
|
||||
|
||||
res.Body = nil
|
||||
if err := res.Write(rw); err != nil {
|
||||
return fmt.Errorf("response write: %v", err)
|
||||
}
|
||||
|
||||
if reqUpType == "websocket" && h.Websocket {
|
||||
return h.sniffingWebsocketFrame(ctx, rw, cc, ro, log)
|
||||
}
|
||||
|
||||
return xnet.Transport(rw, cc)
|
||||
}
|
||||
|
||||
func (h *Sniffer) sniffingWebsocketFrame(ctx context.Context, rw, cc io.ReadWriter, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
|
||||
errc := make(chan error, 1)
|
||||
|
||||
sampleRate := h.WebsocketSampleRate
|
||||
if sampleRate == 0 {
|
||||
sampleRate = DefaultSampleRate
|
||||
}
|
||||
if sampleRate < 0 {
|
||||
sampleRate = math.MaxFloat64
|
||||
}
|
||||
|
||||
go func() {
|
||||
ro2 := &xrecorder.HandlerRecorderObject{}
|
||||
*ro2 = *ro
|
||||
ro := ro2
|
||||
|
||||
limiter := rate.NewLimiter(rate.Limit(sampleRate), int(sampleRate))
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
for {
|
||||
start := time.Now()
|
||||
|
||||
if err := h.copyWebsocketFrame(cc, rw, buf, "client", ro); err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
|
||||
if limiter.Allow() {
|
||||
ro.Duration = time.Since(start)
|
||||
ro.Time = time.Now()
|
||||
if err := ro.Record(ctx, h.Recorder); err != nil {
|
||||
log.Errorf("record: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
ro2 := &xrecorder.HandlerRecorderObject{}
|
||||
*ro2 = *ro
|
||||
ro := ro2
|
||||
|
||||
limiter := rate.NewLimiter(rate.Limit(sampleRate), int(sampleRate))
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
for {
|
||||
start := time.Now()
|
||||
|
||||
if err := h.copyWebsocketFrame(rw, cc, buf, "server", ro); err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
|
||||
if limiter.Allow() {
|
||||
ro.Duration = time.Since(start)
|
||||
ro.Time = time.Now()
|
||||
if err := ro.Record(ctx, h.Recorder); err != nil {
|
||||
log.Errorf("record: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
<-errc
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Sniffer) copyWebsocketFrame(w io.Writer, r io.Reader, buf *bytes.Buffer, from string, ro *xrecorder.HandlerRecorderObject) (err error) {
|
||||
fr := ws_util.Frame{}
|
||||
if _, err = fr.ReadFrom(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ws := &xrecorder.WebsocketRecorderObject{
|
||||
From: from,
|
||||
Fin: fr.Header.Fin,
|
||||
Rsv1: fr.Header.Rsv1,
|
||||
Rsv2: fr.Header.Rsv2,
|
||||
Rsv3: fr.Header.Rsv3,
|
||||
OpCode: int(fr.Header.OpCode),
|
||||
Masked: fr.Header.Masked,
|
||||
MaskKey: fr.Header.MaskKey,
|
||||
Length: fr.Header.PayloadLength,
|
||||
}
|
||||
if opts := h.RecorderOptions; opts != nil && opts.HTTPBody {
|
||||
bodySize := opts.MaxBodySize
|
||||
if bodySize <= 0 {
|
||||
bodySize = DefaultBodySize
|
||||
}
|
||||
if bodySize > MaxBodySize {
|
||||
bodySize = MaxBodySize
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
if _, err := io.Copy(buf, io.LimitReader(fr.Data, int64(bodySize))); err != nil {
|
||||
return err
|
||||
}
|
||||
ws.Payload = buf.Bytes()
|
||||
}
|
||||
|
||||
ro.Websocket = ws
|
||||
length := uint64(fr.Header.Length()) + uint64(fr.Header.PayloadLength)
|
||||
if from == "client" {
|
||||
ro.InputBytes = length
|
||||
ro.OutputBytes = 0
|
||||
} else {
|
||||
ro.InputBytes = 0
|
||||
ro.OutputBytes = length
|
||||
}
|
||||
|
||||
fr.Data = io.MultiReader(bytes.NewReader(buf.Bytes()), fr.Data)
|
||||
if _, err := fr.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Sniffer) HandleTLS(ctx context.Context, conn net.Conn, opts ...HandleOption) error {
|
||||
@@ -644,11 +814,15 @@ func (h *h2Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
var reqBody *xhttp.Body
|
||||
if opts := h.recorderOptions; opts != nil && opts.HTTPBody {
|
||||
if req.Body != nil {
|
||||
maxSize := opts.MaxBodySize
|
||||
if maxSize <= 0 {
|
||||
maxSize = DefaultBodySize
|
||||
bodySize := opts.MaxBodySize
|
||||
if bodySize <= 0 {
|
||||
bodySize = DefaultBodySize
|
||||
}
|
||||
reqBody = xhttp.NewBody(req.Body, maxSize)
|
||||
if bodySize > MaxBodySize {
|
||||
bodySize = MaxBodySize
|
||||
}
|
||||
|
||||
reqBody = xhttp.NewBody(req.Body, bodySize)
|
||||
req.Body = reqBody
|
||||
}
|
||||
}
|
||||
@@ -679,11 +853,14 @@ func (h *h2Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var respBody *xhttp.Body
|
||||
if opts := h.recorderOptions; opts != nil && opts.HTTPBody {
|
||||
maxSize := opts.MaxBodySize
|
||||
if maxSize <= 0 {
|
||||
maxSize = DefaultBodySize
|
||||
bodySize := opts.MaxBodySize
|
||||
if bodySize <= 0 {
|
||||
bodySize = DefaultBodySize
|
||||
}
|
||||
respBody = xhttp.NewBody(resp.Body, maxSize)
|
||||
if bodySize > MaxBodySize {
|
||||
bodySize = MaxBodySize
|
||||
}
|
||||
respBody = xhttp.NewBody(resp.Body, bodySize)
|
||||
resp.Body = respBody
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
package ws
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
)
|
||||
|
||||
// OpCode represents a WebSocket opcode.
|
||||
type OpCode int
|
||||
|
||||
// https://tools.ietf.org/html/rfc6455#section-11.8.
|
||||
const (
|
||||
OpContinuation OpCode = iota
|
||||
OpText
|
||||
OpBinary
|
||||
// 3 - 7 are reserved for further non-control frames.
|
||||
_
|
||||
_
|
||||
_
|
||||
_
|
||||
_
|
||||
OpClose
|
||||
OpPing
|
||||
OpPong
|
||||
// 11-16 are reserved for further control frames.
|
||||
)
|
||||
|
||||
// FrameHeader represents a WebSocket frame header.
|
||||
// See https://tools.ietf.org/html/rfc6455#section-5.2.
|
||||
type FrameHeader struct {
|
||||
Fin bool
|
||||
Rsv1 bool
|
||||
Rsv2 bool
|
||||
Rsv3 bool
|
||||
OpCode OpCode
|
||||
|
||||
PayloadLength int64
|
||||
|
||||
Masked bool
|
||||
MaskKey uint32
|
||||
}
|
||||
|
||||
// ReadFrom reads a header from the reader.
|
||||
// See https://tools.ietf.org/html/rfc6455#section-5.2.
|
||||
func (h *FrameHeader) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
var buf [8]byte
|
||||
|
||||
// First byte. FIN/RSV1/RSV2/RSV3/OpCode(4bits)
|
||||
nn, err := io.ReadFull(r, buf[:2])
|
||||
n += int64(n)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
b := buf[0]
|
||||
h.Fin = b&(1<<7) != 0
|
||||
h.Rsv1 = b&(1<<6) != 0
|
||||
h.Rsv2 = b&(1<<5) != 0
|
||||
h.Rsv3 = b&(1<<4) != 0
|
||||
|
||||
h.OpCode = OpCode(b & 0xf)
|
||||
|
||||
b = buf[1]
|
||||
h.Masked = b&(1<<7) != 0
|
||||
|
||||
payloadLength := b &^ (1 << 7)
|
||||
switch {
|
||||
case payloadLength < 126:
|
||||
h.PayloadLength = int64(payloadLength)
|
||||
case payloadLength == 126:
|
||||
nn, err = io.ReadFull(r, buf[:2])
|
||||
h.PayloadLength = int64(binary.BigEndian.Uint16(buf[:]))
|
||||
case payloadLength == 127:
|
||||
nn, err = io.ReadFull(r, buf[:])
|
||||
h.PayloadLength = int64(binary.BigEndian.Uint64(buf[:]))
|
||||
}
|
||||
n += int64(nn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if h.PayloadLength < 0 {
|
||||
err = fmt.Errorf("received negative payload length: %v", h.PayloadLength)
|
||||
return
|
||||
}
|
||||
|
||||
if h.Masked {
|
||||
nn, err = io.ReadFull(r, buf[:4])
|
||||
n += int64(nn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
h.MaskKey = binary.LittleEndian.Uint32(buf[:])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (h FrameHeader) Length() int {
|
||||
n := 2
|
||||
switch {
|
||||
case h.PayloadLength > math.MaxUint16:
|
||||
n += 8
|
||||
case h.PayloadLength > 125:
|
||||
n += 2
|
||||
}
|
||||
|
||||
if h.Masked {
|
||||
n += 4
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (h *FrameHeader) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var buf [14]byte
|
||||
pos := 0
|
||||
|
||||
var b byte
|
||||
if h.Fin {
|
||||
b |= 1 << 7
|
||||
}
|
||||
if h.Rsv1 {
|
||||
b |= 1 << 6
|
||||
}
|
||||
if h.Rsv2 {
|
||||
b |= 1 << 5
|
||||
}
|
||||
if h.Rsv3 {
|
||||
b |= 1 << 4
|
||||
}
|
||||
|
||||
b |= byte(h.OpCode)
|
||||
|
||||
buf[0] = b
|
||||
|
||||
lengthByte := byte(0)
|
||||
if h.Masked {
|
||||
lengthByte |= 1 << 7
|
||||
}
|
||||
|
||||
switch {
|
||||
case h.PayloadLength > math.MaxUint16:
|
||||
lengthByte |= 127
|
||||
case h.PayloadLength > 125:
|
||||
lengthByte |= 126
|
||||
case h.PayloadLength >= 0:
|
||||
lengthByte |= byte(h.PayloadLength)
|
||||
}
|
||||
buf[1] = lengthByte
|
||||
pos = 2
|
||||
|
||||
switch {
|
||||
case h.PayloadLength > math.MaxUint16:
|
||||
binary.BigEndian.PutUint64(buf[2:], uint64(h.PayloadLength))
|
||||
pos += 8
|
||||
case h.PayloadLength > 125:
|
||||
binary.BigEndian.PutUint16(buf[2:], uint16(h.PayloadLength))
|
||||
pos += 2
|
||||
}
|
||||
|
||||
if h.Masked {
|
||||
binary.LittleEndian.PutUint32(buf[pos:], h.MaskKey)
|
||||
pos += 4
|
||||
}
|
||||
|
||||
nn, err := w.Write(buf[:pos])
|
||||
n = int64(nn)
|
||||
return
|
||||
}
|
||||
|
||||
type Frame struct {
|
||||
Header FrameHeader
|
||||
Data io.Reader
|
||||
}
|
||||
|
||||
func (fr *Frame) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
if n, err = fr.Header.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fr.Data = io.LimitReader(r, fr.Header.PayloadLength)
|
||||
return
|
||||
}
|
||||
|
||||
func (fr *Frame) WriteTo(w io.Writer) (n int64, err error) {
|
||||
n, err = fr.Header.WriteTo(w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
nn, err := io.Copy(w, fr.Data)
|
||||
n += nn
|
||||
return
|
||||
}
|
||||
@@ -83,7 +83,7 @@ func (l *dnsListener) Init(md md.Metadata) (err error) {
|
||||
ln = limiter_wrapper.WrapListener(
|
||||
l.options.Service,
|
||||
ln,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
)
|
||||
|
||||
l.server = &dnsServer{
|
||||
@@ -124,7 +124,7 @@ func (l *dnsListener) Init(md md.Metadata) (err error) {
|
||||
ln = limiter_wrapper.WrapListener(
|
||||
l.options.Service,
|
||||
ln,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
)
|
||||
|
||||
l.server = &dnsServer{
|
||||
@@ -166,7 +166,7 @@ func (l *dnsListener) Init(md md.Metadata) (err error) {
|
||||
ln = limiter_wrapper.WrapListener(
|
||||
l.options.Service,
|
||||
ln,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
)
|
||||
|
||||
l.server = &dohServer{
|
||||
@@ -205,7 +205,7 @@ func (l *dnsListener) Init(md md.Metadata) (err error) {
|
||||
|
||||
pc = limiter_wrapper.WrapPacketConn(
|
||||
pc,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
"",
|
||||
limiter.ScopeOption(limiter.ScopeService),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
@@ -251,7 +251,7 @@ func (l *dnsListener) Accept() (conn net.Conn, err error) {
|
||||
conn = admission.WrapConn(l.options.Admission, conn)
|
||||
conn = limiter_wrapper.WrapConn(
|
||||
conn,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
conn.RemoteAddr().String(),
|
||||
limiter.ScopeOption(limiter.ScopeConn),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
|
||||
@@ -12,12 +12,13 @@ const (
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
mode string
|
||||
readBufferSize int
|
||||
readTimeout time.Duration
|
||||
writeTimeout time.Duration
|
||||
backlog int
|
||||
mptcp bool
|
||||
mode string
|
||||
readBufferSize int
|
||||
readTimeout time.Duration
|
||||
writeTimeout time.Duration
|
||||
backlog int
|
||||
mptcp bool
|
||||
limiterRefreshInterval time.Duration
|
||||
}
|
||||
|
||||
func (l *dnsListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
@@ -40,5 +41,13 @@ func (l *dnsListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
}
|
||||
l.md.mptcp = mdutil.GetBool(md, "mptcp")
|
||||
|
||||
l.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if l.md.limiterRefreshInterval == 0 {
|
||||
l.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if l.md.limiterRefreshInterval < time.Second {
|
||||
l.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ func (l *dtlsListener) Init(md md.Metadata) (err error) {
|
||||
ln = limiter_wrapper.WrapListener(
|
||||
l.options.Service,
|
||||
ln,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
)
|
||||
ln = climiter.WrapListener(l.options.ConnLimiter, ln)
|
||||
|
||||
@@ -104,7 +104,7 @@ func (l *dtlsListener) Accept() (conn net.Conn, err error) {
|
||||
conn = xdtls.Conn(c, l.md.bufferSize)
|
||||
conn = limiter_wrapper.WrapConn(
|
||||
conn,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
conn.RemoteAddr().String(),
|
||||
limiter.ScopeOption(limiter.ScopeConn),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
|
||||
@@ -12,9 +12,10 @@ const (
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
mtu int
|
||||
bufferSize int
|
||||
flightInterval time.Duration
|
||||
mtu int
|
||||
bufferSize int
|
||||
flightInterval time.Duration
|
||||
limiterRefreshInterval time.Duration
|
||||
}
|
||||
|
||||
func (l *dtlsListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
@@ -26,5 +27,13 @@ func (l *dtlsListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
|
||||
l.md.flightInterval = mdutil.GetDuration(md, "dtls.flightInterval", "flightInterval")
|
||||
|
||||
l.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if l.md.limiterRefreshInterval == 0 {
|
||||
l.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if l.md.limiterRefreshInterval < time.Second {
|
||||
l.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ func (l *ftcpListener) Init(md md.Metadata) (err error) {
|
||||
conn = admission.WrapPacketConn(l.options.Admission, conn)
|
||||
conn = limiter_wrapper.WrapPacketConn(
|
||||
conn,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
"",
|
||||
limiter.ScopeOption(limiter.ScopeService),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
@@ -88,7 +88,7 @@ func (l *ftcpListener) Accept() (conn net.Conn, err error) {
|
||||
|
||||
conn = limiter_wrapper.WrapConn(
|
||||
conn,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
conn.RemoteAddr().String(),
|
||||
limiter.ScopeOption(limiter.ScopeConn),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
|
||||
@@ -15,7 +15,8 @@ const (
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
ttl time.Duration
|
||||
ttl time.Duration
|
||||
limiterRefreshInterval time.Duration
|
||||
|
||||
readBufferSize int
|
||||
readQueueSize int
|
||||
@@ -49,5 +50,13 @@ func (l *ftcpListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
l.md.backlog = defaultBacklog
|
||||
}
|
||||
|
||||
l.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if l.md.limiterRefreshInterval == 0 {
|
||||
l.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if l.md.limiterRefreshInterval < time.Second {
|
||||
l.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ func (l *grpcListener) Init(md md.Metadata) (err error) {
|
||||
ln = limiter_wrapper.WrapListener(
|
||||
l.options.Service,
|
||||
ln,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
)
|
||||
ln = climiter.WrapListener(l.options.ConnLimiter, ln)
|
||||
|
||||
@@ -124,7 +124,7 @@ func (l *grpcListener) Accept() (conn net.Conn, err error) {
|
||||
case conn = <-l.cqueue:
|
||||
conn = limiter_wrapper.WrapConn(
|
||||
conn,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
conn.RemoteAddr().String(),
|
||||
limiter.ScopeOption(limiter.ScopeConn),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
|
||||
@@ -22,6 +22,7 @@ type metadata struct {
|
||||
keepalivePermitWithoutStream bool
|
||||
keepaliveMaxConnectionIdle time.Duration
|
||||
mptcp bool
|
||||
limiterRefreshInterval time.Duration
|
||||
}
|
||||
|
||||
func (l *grpcListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
@@ -53,5 +54,13 @@ func (l *grpcListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
l.md.mptcp = mdutil.GetBool(md, "mptcp")
|
||||
}
|
||||
|
||||
l.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if l.md.limiterRefreshInterval == 0 {
|
||||
l.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if l.md.limiterRefreshInterval < time.Second {
|
||||
l.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ func (l *h2Listener) Init(md md.Metadata) (err error) {
|
||||
ln = limiter_wrapper.WrapListener(
|
||||
l.options.Service,
|
||||
ln,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
)
|
||||
ln = climiter.WrapListener(l.options.ConnLimiter, ln)
|
||||
|
||||
@@ -130,7 +130,7 @@ func (l *h2Listener) Accept() (conn net.Conn, err error) {
|
||||
case conn = <-l.cqueue:
|
||||
conn = limiter_wrapper.WrapConn(
|
||||
conn,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
conn.RemoteAddr().String(),
|
||||
limiter.ScopeOption(limiter.ScopeConn),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package h2
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
)
|
||||
@@ -10,9 +12,10 @@ const (
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
path string
|
||||
backlog int
|
||||
mptcp bool
|
||||
path string
|
||||
backlog int
|
||||
mptcp bool
|
||||
limiterRefreshInterval time.Duration
|
||||
}
|
||||
|
||||
func (l *h2Listener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
@@ -29,5 +32,13 @@ func (l *h2Listener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
l.md.path = mdutil.GetString(md, path)
|
||||
l.md.mptcp = mdutil.GetBool(md, "mptcp")
|
||||
|
||||
l.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if l.md.limiterRefreshInterval == 0 {
|
||||
l.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if l.md.limiterRefreshInterval < time.Second {
|
||||
l.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ func (l *http2Listener) Init(md md.Metadata) (err error) {
|
||||
ln = limiter_wrapper.WrapListener(
|
||||
l.options.Service,
|
||||
ln,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
)
|
||||
ln = climiter.WrapListener(l.options.ConnLimiter, ln)
|
||||
|
||||
@@ -111,7 +111,7 @@ func (l *http2Listener) Accept() (conn net.Conn, err error) {
|
||||
case conn = <-l.cqueue:
|
||||
conn = limiter_wrapper.WrapConn(
|
||||
conn,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
conn.RemoteAddr().String(),
|
||||
limiter.ScopeOption(limiter.ScopeConn),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package http2
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
)
|
||||
@@ -10,8 +12,9 @@ const (
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
backlog int
|
||||
mptcp bool
|
||||
backlog int
|
||||
mptcp bool
|
||||
limiterRefreshInterval time.Duration
|
||||
}
|
||||
|
||||
func (l *http2Listener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
@@ -25,5 +28,13 @@ func (l *http2Listener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
}
|
||||
l.md.mptcp = mdutil.GetBool(md, "mptcp")
|
||||
|
||||
l.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if l.md.limiterRefreshInterval == 0 {
|
||||
l.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if l.md.limiterRefreshInterval < time.Second {
|
||||
l.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ func (l *http3Listener) Init(md md.Metadata) (err error) {
|
||||
KeepAlivePeriod: l.md.keepAlivePeriod,
|
||||
HandshakeIdleTimeout: l.md.handshakeTimeout,
|
||||
MaxIdleTimeout: l.md.maxIdleTimeout,
|
||||
Versions: []quic.VersionNumber{
|
||||
Versions: []quic.Version{
|
||||
quic.Version1,
|
||||
},
|
||||
MaxIncomingStreams: int64(l.md.maxStreams),
|
||||
@@ -93,7 +93,7 @@ func (l *http3Listener) Accept() (conn net.Conn, err error) {
|
||||
conn = admission.WrapConn(l.options.Admission, conn)
|
||||
conn = limiter_wrapper.WrapConn(
|
||||
conn,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
conn.RemoteAddr().String(),
|
||||
limiter.ScopeOption(limiter.ScopeConn),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
|
||||
@@ -26,6 +26,8 @@ type metadata struct {
|
||||
maxIdleTimeout time.Duration
|
||||
handshakeTimeout time.Duration
|
||||
maxStreams int
|
||||
|
||||
limiterRefreshInterval time.Duration
|
||||
}
|
||||
|
||||
func (l *http3Listener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
@@ -67,5 +69,13 @@ func (l *http3Listener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
l.md.maxIdleTimeout = mdutil.GetDuration(md, maxIdleTimeout)
|
||||
l.md.maxStreams = mdutil.GetInt(md, maxStreams)
|
||||
|
||||
l.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if l.md.limiterRefreshInterval == 0 {
|
||||
l.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if l.md.limiterRefreshInterval < time.Second {
|
||||
l.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ func (l *http3Listener) Init(md md.Metadata) (err error) {
|
||||
KeepAlivePeriod: l.md.keepAlivePeriod,
|
||||
HandshakeIdleTimeout: l.md.handshakeTimeout,
|
||||
MaxIdleTimeout: l.md.maxIdleTimeout,
|
||||
Versions: []quic.VersionNumber{
|
||||
Versions: []quic.Version{
|
||||
quic.Version1,
|
||||
},
|
||||
MaxIncomingStreams: int64(l.md.maxStreams),
|
||||
|
||||
@@ -79,7 +79,7 @@ func (l *wtListener) Init(md md.Metadata) (err error) {
|
||||
pc = admission.WrapPacketConn(l.options.Admission, pc)
|
||||
pc = limiter_wrapper.WrapPacketConn(
|
||||
pc,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
"",
|
||||
limiter.ScopeOption(limiter.ScopeService),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
@@ -130,7 +130,7 @@ func (l *wtListener) Accept() (conn net.Conn, err error) {
|
||||
case conn = <-l.cqueue:
|
||||
conn = limiter_wrapper.WrapConn(
|
||||
conn,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
conn.RemoteAddr().String(),
|
||||
limiter.ScopeOption(limiter.ScopeConn),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
|
||||
@@ -21,6 +21,8 @@ type metadata struct {
|
||||
maxIdleTimeout time.Duration
|
||||
handshakeTimeout time.Duration
|
||||
maxStreams int
|
||||
|
||||
limiterRefreshInterval time.Duration
|
||||
}
|
||||
|
||||
func (l *wtListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
@@ -54,5 +56,13 @@ func (l *wtListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
l.md.maxIdleTimeout = mdutil.GetDuration(md, maxIdleTimeout)
|
||||
l.md.maxStreams = mdutil.GetInt(md, maxStreams)
|
||||
|
||||
l.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if l.md.limiterRefreshInterval == 0 {
|
||||
l.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if l.md.limiterRefreshInterval < time.Second {
|
||||
l.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ func (l *icmpListener) Init(md md.Metadata) (err error) {
|
||||
conn = admission.WrapPacketConn(l.options.Admission, conn)
|
||||
conn = limiter_wrapper.WrapPacketConn(
|
||||
conn,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
"",
|
||||
limiter.ScopeOption(limiter.ScopeService),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
@@ -94,7 +94,7 @@ func (l *icmpListener) Init(md md.Metadata) (err error) {
|
||||
KeepAlivePeriod: l.md.keepAlivePeriod,
|
||||
HandshakeIdleTimeout: l.md.handshakeTimeout,
|
||||
MaxIdleTimeout: l.md.maxIdleTimeout,
|
||||
Versions: []quic.VersionNumber{
|
||||
Versions: []quic.Version{
|
||||
quic.Version1,
|
||||
quic.Version2,
|
||||
},
|
||||
@@ -123,7 +123,7 @@ func (l *icmpListener) Accept() (conn net.Conn, err error) {
|
||||
case conn = <-l.cqueue:
|
||||
conn = limiter_wrapper.WrapConn(
|
||||
conn,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
conn.RemoteAddr().String(),
|
||||
limiter.ScopeOption(limiter.ScopeConn),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
|
||||
@@ -16,7 +16,8 @@ type metadata struct {
|
||||
handshakeTimeout time.Duration
|
||||
maxIdleTimeout time.Duration
|
||||
|
||||
backlog int
|
||||
backlog int
|
||||
limiterRefreshInterval time.Duration
|
||||
}
|
||||
|
||||
func (l *icmpListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
@@ -34,5 +35,13 @@ func (l *icmpListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
l.md.handshakeTimeout = mdutil.GetDuration(md, "handshakeTimeout")
|
||||
l.md.maxIdleTimeout = mdutil.GetDuration(md, "maxIdleTimeout")
|
||||
|
||||
l.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if l.md.limiterRefreshInterval == 0 {
|
||||
l.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if l.md.limiterRefreshInterval < time.Second {
|
||||
l.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ func (l *kcpListener) Init(md md.Metadata) (err error) {
|
||||
conn = admission.WrapUDPConn(l.options.Admission, conn)
|
||||
conn = limiter_wrapper.WrapUDPConn(
|
||||
conn,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
"",
|
||||
limiter.ScopeOption(limiter.ScopeService),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
@@ -124,7 +124,7 @@ func (l *kcpListener) Accept() (conn net.Conn, err error) {
|
||||
case conn = <-l.cqueue:
|
||||
conn = limiter_wrapper.WrapConn(
|
||||
conn,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
conn.RemoteAddr().String(),
|
||||
limiter.ScopeOption(limiter.ScopeConn),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
|
||||
@@ -2,10 +2,11 @@ package kcp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
kcp_util "github.com/go-gost/x/internal/util/kcp"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -13,8 +14,9 @@ const (
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
config *kcp_util.Config
|
||||
backlog int
|
||||
config *kcp_util.Config
|
||||
backlog int
|
||||
limiterRefreshInterval time.Duration
|
||||
}
|
||||
|
||||
func (l *kcpListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
@@ -64,5 +66,13 @@ func (l *kcpListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
l.md.backlog = defaultBacklog
|
||||
}
|
||||
|
||||
l.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if l.md.limiterRefreshInterval == 0 {
|
||||
l.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if l.md.limiterRefreshInterval < time.Second {
|
||||
l.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ func (l *mtcpListener) Init(md md.Metadata) (err error) {
|
||||
ln = limiter_wrapper.WrapListener(
|
||||
l.options.Service,
|
||||
ln,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
)
|
||||
ln = climiter.WrapListener(l.options.ConnLimiter, ln)
|
||||
l.ln = ln
|
||||
@@ -101,7 +101,7 @@ func (l *mtcpListener) Accept() (conn net.Conn, err error) {
|
||||
case conn = <-l.cqueue:
|
||||
conn = limiter_wrapper.WrapConn(
|
||||
conn,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
conn.RemoteAddr().String(),
|
||||
limiter.ScopeOption(limiter.ScopeConn),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package mtcp
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
md "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
"github.com/go-gost/x/internal/util/mux"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -11,9 +13,10 @@ const (
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
mptcp bool
|
||||
muxCfg *mux.Config
|
||||
backlog int
|
||||
mptcp bool
|
||||
muxCfg *mux.Config
|
||||
backlog int
|
||||
limiterRefreshInterval time.Duration
|
||||
}
|
||||
|
||||
func (l *mtcpListener) parseMetadata(md md.Metadata) (err error) {
|
||||
@@ -36,5 +39,14 @@ func (l *mtcpListener) parseMetadata(md md.Metadata) (err error) {
|
||||
if l.md.backlog <= 0 {
|
||||
l.md.backlog = defaultBacklog
|
||||
}
|
||||
|
||||
l.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if l.md.limiterRefreshInterval == 0 {
|
||||
l.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if l.md.limiterRefreshInterval < time.Second {
|
||||
l.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ func (l *mtlsListener) Init(md md.Metadata) (err error) {
|
||||
ln = limiter_wrapper.WrapListener(
|
||||
l.options.Service,
|
||||
ln,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
)
|
||||
ln = climiter.WrapListener(l.options.ConnLimiter, ln)
|
||||
l.Listener = tls.NewListener(ln, l.options.TLSConfig)
|
||||
@@ -92,7 +92,7 @@ func (l *mtlsListener) Accept() (conn net.Conn, err error) {
|
||||
case conn = <-l.cqueue:
|
||||
conn = limiter_wrapper.WrapConn(
|
||||
conn,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
conn.RemoteAddr().String(),
|
||||
limiter.ScopeOption(limiter.ScopeConn),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package mtls
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
"github.com/go-gost/x/internal/util/mux"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -11,9 +13,10 @@ const (
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
muxCfg *mux.Config
|
||||
backlog int
|
||||
mptcp bool
|
||||
muxCfg *mux.Config
|
||||
backlog int
|
||||
mptcp bool
|
||||
limiterRefreshInterval time.Duration
|
||||
}
|
||||
|
||||
func (l *mtlsListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
@@ -33,5 +36,13 @@ func (l *mtlsListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
}
|
||||
l.md.mptcp = mdutil.GetBool(md, "mptcp")
|
||||
|
||||
l.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if l.md.limiterRefreshInterval == 0 {
|
||||
l.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if l.md.limiterRefreshInterval < time.Second {
|
||||
l.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ func (l *mwsListener) Init(md md.Metadata) (err error) {
|
||||
ln = limiter_wrapper.WrapListener(
|
||||
l.options.Service,
|
||||
ln,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
)
|
||||
ln = climiter.WrapListener(l.options.ConnLimiter, ln)
|
||||
|
||||
@@ -142,7 +142,7 @@ func (l *mwsListener) Accept() (conn net.Conn, err error) {
|
||||
case conn = <-l.cqueue:
|
||||
conn = limiter_wrapper.WrapConn(
|
||||
conn,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
conn.RemoteAddr().String(),
|
||||
limiter.ScopeOption(limiter.ScopeConn),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
"github.com/go-gost/x/internal/util/mux"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -27,7 +27,8 @@ type metadata struct {
|
||||
|
||||
muxCfg *mux.Config
|
||||
|
||||
mptcp bool
|
||||
mptcp bool
|
||||
limiterRefreshInterval time.Duration
|
||||
}
|
||||
|
||||
func (l *mwsListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
@@ -67,5 +68,13 @@ func (l *mwsListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
|
||||
l.md.mptcp = mdutil.GetBool(md, "mptcp")
|
||||
|
||||
l.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if l.md.limiterRefreshInterval == 0 {
|
||||
l.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if l.md.limiterRefreshInterval < time.Second {
|
||||
l.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ func (l *obfsListener) Init(md md.Metadata) (err error) {
|
||||
ln = limiter_wrapper.WrapListener(
|
||||
l.options.Service,
|
||||
ln,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
)
|
||||
ln = climiter.WrapListener(l.options.ConnLimiter, ln)
|
||||
|
||||
@@ -84,7 +84,7 @@ func (l *obfsListener) Accept() (net.Conn, error) {
|
||||
|
||||
conn = limiter_wrapper.WrapConn(
|
||||
conn,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
conn.RemoteAddr().String(),
|
||||
limiter.ScopeOption(limiter.ScopeConn),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
|
||||
@@ -2,14 +2,16 @@ package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
header http.Header
|
||||
mptcp bool
|
||||
header http.Header
|
||||
mptcp bool
|
||||
limiterRefreshInterval time.Duration
|
||||
}
|
||||
|
||||
func (l *obfsListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
@@ -26,5 +28,14 @@ func (l *obfsListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
}
|
||||
|
||||
l.md.mptcp = mdutil.GetBool(md, "mptcp")
|
||||
|
||||
l.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if l.md.limiterRefreshInterval == 0 {
|
||||
l.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if l.md.limiterRefreshInterval < time.Second {
|
||||
l.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ func (l *obfsListener) Init(md md.Metadata) (err error) {
|
||||
ln = limiter_wrapper.WrapListener(
|
||||
l.options.Service,
|
||||
ln,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
)
|
||||
ln = climiter.WrapListener(l.options.ConnLimiter, ln)
|
||||
|
||||
@@ -84,7 +84,7 @@ func (l *obfsListener) Accept() (net.Conn, error) {
|
||||
|
||||
conn = limiter_wrapper.WrapConn(
|
||||
conn,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
conn.RemoteAddr().String(),
|
||||
limiter.ScopeOption(limiter.ScopeConn),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
|
||||
@@ -1,15 +1,27 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
md "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
mptcp bool
|
||||
mptcp bool
|
||||
limiterRefreshInterval time.Duration
|
||||
}
|
||||
|
||||
func (l *obfsListener) parseMetadata(md md.Metadata) (err error) {
|
||||
l.md.mptcp = mdutil.GetBool(md, "mptcp")
|
||||
|
||||
l.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if l.md.limiterRefreshInterval == 0 {
|
||||
l.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if l.md.limiterRefreshInterval < time.Second {
|
||||
l.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ func (l *phtListener) Accept() (conn net.Conn, err error) {
|
||||
conn = admission.WrapConn(l.options.Admission, conn)
|
||||
conn = limiter_wrapper.WrapConn(
|
||||
conn,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
conn.RemoteAddr().String(),
|
||||
limiter.ScopeOption(limiter.ScopeConn),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
|
||||
@@ -2,6 +2,7 @@ package pht
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
@@ -15,11 +16,12 @@ const (
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
authorizePath string
|
||||
pushPath string
|
||||
pullPath string
|
||||
backlog int
|
||||
mptcp bool
|
||||
authorizePath string
|
||||
pushPath string
|
||||
pullPath string
|
||||
backlog int
|
||||
mptcp bool
|
||||
limiterRefreshInterval time.Duration
|
||||
}
|
||||
|
||||
func (l *phtListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
@@ -50,5 +52,14 @@ func (l *phtListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
}
|
||||
|
||||
l.md.mptcp = mdutil.GetBool(md, "mptcp")
|
||||
|
||||
l.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if l.md.limiterRefreshInterval == 0 {
|
||||
l.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if l.md.limiterRefreshInterval < time.Second {
|
||||
l.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ func (l *quicListener) Init(md md.Metadata) (err error) {
|
||||
conn = admission.WrapPacketConn(l.options.Admission, conn)
|
||||
conn = limiter_wrapper.WrapPacketConn(
|
||||
conn,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
"",
|
||||
limiter.ScopeOption(limiter.ScopeService),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
@@ -89,7 +89,7 @@ func (l *quicListener) Init(md md.Metadata) (err error) {
|
||||
KeepAlivePeriod: l.md.keepAlivePeriod,
|
||||
HandshakeIdleTimeout: l.md.handshakeTimeout,
|
||||
MaxIdleTimeout: l.md.maxIdleTimeout,
|
||||
Versions: []quic.VersionNumber{
|
||||
Versions: []quic.Version{
|
||||
quic.Version1,
|
||||
quic.Version2,
|
||||
},
|
||||
@@ -119,7 +119,7 @@ func (l *quicListener) Accept() (conn net.Conn, err error) {
|
||||
case conn = <-l.cqueue:
|
||||
conn = limiter_wrapper.WrapConn(
|
||||
conn,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
conn.RemoteAddr().String(),
|
||||
limiter.ScopeOption(limiter.ScopeConn),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
|
||||
@@ -18,8 +18,9 @@ type metadata struct {
|
||||
maxIdleTimeout time.Duration
|
||||
maxStreams int
|
||||
|
||||
cipherKey []byte
|
||||
backlog int
|
||||
cipherKey []byte
|
||||
backlog int
|
||||
limiterRefreshInterval time.Duration
|
||||
}
|
||||
|
||||
func (l *quicListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
@@ -53,5 +54,13 @@ func (l *quicListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
l.md.maxIdleTimeout = mdutil.GetDuration(md, maxIdleTimeout)
|
||||
l.md.maxStreams = mdutil.GetInt(md, maxStreams)
|
||||
|
||||
l.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if l.md.limiterRefreshInterval == 0 {
|
||||
l.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if l.md.limiterRefreshInterval < time.Second {
|
||||
l.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ func (l *redirectListener) Init(md md.Metadata) (err error) {
|
||||
ln = limiter_wrapper.WrapListener(
|
||||
l.options.Service,
|
||||
ln,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
)
|
||||
ln = climiter.WrapListener(l.options.ConnLimiter, ln)
|
||||
l.ln = ln
|
||||
@@ -88,7 +88,7 @@ func (l *redirectListener) Accept() (conn net.Conn, err error) {
|
||||
|
||||
conn = limiter_wrapper.WrapConn(
|
||||
conn,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
conn.RemoteAddr().String(),
|
||||
limiter.ScopeOption(limiter.ScopeConn),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
|
||||
@@ -1,17 +1,29 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
tproxy bool
|
||||
mptcp bool
|
||||
tproxy bool
|
||||
mptcp bool
|
||||
limiterRefreshInterval time.Duration
|
||||
}
|
||||
|
||||
func (l *redirectListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
l.md.tproxy = mdutil.GetBool(md, "tproxy")
|
||||
l.md.mptcp = mdutil.GetBool(md, "mptcp")
|
||||
|
||||
l.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if l.md.limiterRefreshInterval == 0 {
|
||||
l.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if l.md.limiterRefreshInterval < time.Second {
|
||||
l.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ func (l *redirectListener) Accept() (conn net.Conn, err error) {
|
||||
conn = admission.WrapConn(l.options.Admission, conn)
|
||||
conn = limiter_wrapper.WrapConn(
|
||||
conn,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
conn.RemoteAddr().String(),
|
||||
limiter.ScopeOption(limiter.ScopeConn),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
|
||||
@@ -13,8 +13,9 @@ const (
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
ttl time.Duration
|
||||
readBufferSize int
|
||||
ttl time.Duration
|
||||
readBufferSize int
|
||||
limiterRefreshInterval time.Duration
|
||||
}
|
||||
|
||||
func (l *redirectListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
@@ -33,5 +34,13 @@ func (l *redirectListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
l.md.readBufferSize = defaultReadBufferSize
|
||||
}
|
||||
|
||||
l.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if l.md.limiterRefreshInterval == 0 {
|
||||
l.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if l.md.limiterRefreshInterval < time.Second {
|
||||
l.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ type rtcpListener struct {
|
||||
ln net.Listener
|
||||
logger logger.Logger
|
||||
closed chan struct{}
|
||||
md metadata
|
||||
options listener.Options
|
||||
mu sync.Mutex
|
||||
}
|
||||
@@ -87,7 +88,7 @@ func (l *rtcpListener) Accept() (conn net.Conn, err error) {
|
||||
ln = limiter_wrapper.WrapListener(
|
||||
l.options.Service,
|
||||
ln,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
)
|
||||
ln = climiter.WrapListener(l.options.ConnLimiter, ln)
|
||||
l.setListener(ln)
|
||||
@@ -109,7 +110,7 @@ func (l *rtcpListener) Accept() (conn net.Conn, err error) {
|
||||
|
||||
conn = limiter_wrapper.WrapConn(
|
||||
conn,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
conn.RemoteAddr().String(),
|
||||
limiter.ScopeOption(limiter.ScopeConn),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
package rtcp
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
)
|
||||
|
||||
type metadata struct{}
|
||||
type metadata struct {
|
||||
limiterRefreshInterval time.Duration
|
||||
}
|
||||
|
||||
func (l *rtcpListener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
l.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
|
||||
if l.md.limiterRefreshInterval == 0 {
|
||||
l.md.limiterRefreshInterval = 30 * time.Second
|
||||
}
|
||||
if l.md.limiterRefreshInterval < time.Second {
|
||||
l.md.limiterRefreshInterval = time.Second
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ func (l *rudpListener) Accept() (conn net.Conn, err error) {
|
||||
ln = limiter_wrapper.WrapListener(
|
||||
l.options.Service,
|
||||
ln,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
)
|
||||
ln = climiter.WrapListener(l.options.ConnLimiter, ln)
|
||||
|
||||
@@ -116,7 +116,7 @@ func (l *rudpListener) Accept() (conn net.Conn, err error) {
|
||||
uc = admission.WrapUDPConn(l.options.Admission, uc)
|
||||
conn = limiter_wrapper.WrapUDPConn(
|
||||
uc,
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, 30*time.Second, 60*time.Second),
|
||||
limiter_util.NewCachedTrafficLimiter(l.options.TrafficLimiter, l.md.limiterRefreshInterval, 60*time.Second),
|
||||
"",
|
||||
limiter.ScopeOption(limiter.ScopeConn),
|
||||
limiter.ServiceOption(l.options.Service),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user