diff --git a/admission/wrapper/conn.go b/admission/wrapper/conn.go index d143cfe7..fee7f9f1 100644 --- a/admission/wrapper/conn.go +++ b/admission/wrapper/conn.go @@ -58,6 +58,20 @@ func (c *serverConn) Metadata() metadata.Metadata { return nil } +func (c *serverConn) SrcAddr() net.Addr { + if sc, ok := c.Conn.(xnet.SrcAddr); ok { + return sc.SrcAddr() + } + return nil +} + +func (c *serverConn) DstAddr() net.Addr { + if sc, ok := c.Conn.(xnet.DstAddr); ok { + return sc.DstAddr() + } + return nil +} + func (c *serverConn) CloseRead() error { if sc, ok := c.Conn.(xio.CloseRead); ok { return sc.CloseRead() diff --git a/auth/plugin/grpc.go b/auth/plugin/grpc.go index 144f1418..bda08717 100644 --- a/auth/plugin/grpc.go +++ b/auth/plugin/grpc.go @@ -51,11 +51,15 @@ func (p *grpcPlugin) Authenticate(ctx context.Context, user, password string, op return "", false } + var clientAddr string + if v := ctxvalue.SrcAddrFromContext(ctx); v != nil { + clientAddr = v.String() + } r, err := p.client.Authenticate(ctx, &proto.AuthenticateRequest{ Username: user, Password: password, - Client: string(ctxvalue.ClientAddrFromContext(ctx)), + Client: clientAddr, }) if err != nil { p.log.Error(err) diff --git a/auth/plugin/http.go b/auth/plugin/http.go index e867ad9e..b3e352a0 100644 --- a/auth/plugin/http.go +++ b/auth/plugin/http.go @@ -53,10 +53,15 @@ func (p *httpPlugin) Authenticate(ctx context.Context, user, password string, op return } + var clientAddr string + if v := ctxvalue.SrcAddrFromContext(ctx); v != nil { + clientAddr = v.String() + } + rb := httpPluginRequest{ Username: user, Password: password, - Client: string(ctxvalue.ClientAddrFromContext(ctx)), + Client: clientAddr, } v, err := json.Marshal(&rb) if err != nil { diff --git a/config/cmd/cmd.go b/config/cmd/cmd.go index baa724d9..cb5a27fd 100644 --- a/config/cmd/cmd.go +++ b/config/cmd/cmd.go @@ -115,15 +115,34 @@ func BuildConfigFromCmd(serviceList, nodeList []string) (*config.Config, error) } if v := mdutil.GetString(md, "interface"); v != "" { - hopConfig.Interface = v + m["hop.interface"] = v delete(m, "interface") } if v := mdutil.GetInt(md, "so_mark"); v > 0 { - hopConfig.SockOpts = &config.SockOptsConfig{ - Mark: v, - } + m["hop.so_mark"] = v delete(m, "so_mark") } + if v := mdutil.GetInt(md, "proxyProtocol"); v > 0 { + m["hop.proxyProtocol"] = v + delete(m, "proxyProtocol") + } + + hopMd := map[string]any{} + for k, v := range m { + if strings.HasPrefix(k, "node.") || + strings.HasPrefix(k, "connector.") || + strings.HasPrefix(k, "dialer.") { + continue + } + if s, found := strings.CutPrefix(k, "hop."); found { + hopMd[s] = v + delete(m, k) + continue + } + + hopMd[k] = v + } + hopConfig.Metadata = hopMd nodeConfig, err := buildNodeConfig(url, m) if err != nil { @@ -144,22 +163,6 @@ func BuildConfigFromCmd(serviceList, nodeList []string) (*config.Config, error) } hopConfig.Nodes = nodes - hopMd := map[string]any{} - for k, v := range m { - if strings.HasPrefix(k, "node.") || - strings.HasPrefix(k, "connector.") || - strings.HasPrefix(k, "dialer.") { - continue - } - if s, found := strings.CutPrefix(k, "hop."); found { - hopMd[s] = v - continue - } - - hopMd[k] = v - } - hopConfig.Metadata = hopMd - chain.Hops = append(chain.Hops, hopConfig) } @@ -501,6 +504,23 @@ func buildServiceConfig(url *url.URL) ([]*config.ServiceConfig, error) { m["service.interface"] = v delete(m, "interface") } + if v := mdutil.GetInt(md, "so_mark"); v > 0 { + m["service.so_mark"] = v + delete(m, "so_mark") + } + if v := mdutil.GetInt(md, "proxyProtocol"); v > 0 { + m["service.proxyProtocol"] = v + delete(m, "proxyProtocol") + } + if v := mdutil.GetString(md, "netns"); v != "" { + m["service.netns"] = v + delete(m, "netns") + } + if v := mdutil.GetString(md, "netns.out"); v != "" { + m["service.netns.out"] = v + delete(m, "netns.out") + } + selector := parseSelector(m) serviceMd := map[string]any{} diff --git a/config/config.go b/config/config.go index f9fd87fe..e066b141 100644 --- a/config/config.go +++ b/config/config.go @@ -507,21 +507,23 @@ type ChainGroupConfig struct { } type HopConfig struct { - Name string `json:"name"` - Interface string `yaml:",omitempty" json:"interface,omitempty"` - SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"` - Selector *SelectorConfig `yaml:",omitempty" json:"selector,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"` - Nodes []*NodeConfig `yaml:",omitempty" json:"nodes,omitempty"` - Reload time.Duration `yaml:",omitempty" json:"reload,omitempty"` - File *FileLoader `yaml:",omitempty" json:"file,omitempty"` - Redis *RedisLoader `yaml:",omitempty" json:"redis,omitempty"` - HTTP *HTTPLoader `yaml:"http,omitempty" json:"http,omitempty"` - Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"` - Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"` + Name string `json:"name"` + // Deprecated: use metadata.interface instead + Interface string `yaml:",omitempty" json:"interface,omitempty"` + // Deprecated: use metadata.so_mark instead + SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"` + Selector *SelectorConfig `yaml:",omitempty" json:"selector,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"` + Nodes []*NodeConfig `yaml:",omitempty" json:"nodes,omitempty"` + Reload time.Duration `yaml:",omitempty" json:"reload,omitempty"` + File *FileLoader `yaml:",omitempty" json:"file,omitempty"` + Redis *RedisLoader `yaml:",omitempty" json:"redis,omitempty"` + HTTP *HTTPLoader `yaml:"http,omitempty" json:"http,omitempty"` + Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"` + Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"` } type NodeConfig struct { @@ -534,9 +536,12 @@ type NodeConfig struct { 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 metadata.interface instead + Interface string `yaml:",omitempty" json:"interface,omitempty"` + // Deprecated: use metadata.netns instead + Netns string `yaml:",omitempty" json:"netns,omitempty"` + // Deprecated: use metadata.so_mark instead + 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"` diff --git a/config/parsing/hop/parse.go b/config/parsing/hop/parse.go index afe833f1..dc284118 100644 --- a/config/parsing/hop/parse.go +++ b/config/parsing/hop/parse.go @@ -50,6 +50,8 @@ func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) { } } + var ppv int + var soMark int ifce := cfg.Interface var netns string if cfg.Metadata != nil { @@ -57,7 +59,16 @@ func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) { if v := mdutil.GetString(md, parsing.MDKeyInterface); v != "" { ifce = v } - netns = mdutil.GetString(md, "netns") + + if cfg.SockOpts != nil { + soMark = cfg.SockOpts.Mark + } + if v := mdutil.GetInt(md, parsing.MDKeySoMark); v > 0 { + soMark = v + } + ppv = mdutil.GetInt(md, parsing.MDKeyProxyProtocol) + netns = mdutil.GetString(md, parsing.MDKeyNetns) + } var nodes []*chain.Node @@ -66,21 +77,53 @@ func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) { continue } + m := v.Metadata + if m == nil { + m = map[string]any{} + v.Metadata = m + } + md := metadata.NewMetadata(m) + if v.Resolver == "" { v.Resolver = cfg.Resolver } if v.Hosts == "" { v.Hosts = cfg.Hosts } - if v.Interface == "" { - v.Interface = ifce - } - if v.Netns == "" { - v.Netns = netns - } - if v.SockOpts == nil { - v.SockOpts = cfg.SockOpts + if !md.IsExists(parsing.MDKeyInterface) { + // inherit from hop + if ifce != "" { + m[parsing.MDKeyInterface] = ifce + } + // node level + if v.Interface != "" { + m[parsing.MDKeyInterface] = v.Interface + } + } + if !md.IsExists(parsing.MDKeySoMark) { + // inherit from hop + if soMark != 0 { + m[parsing.MDKeySoMark] = soMark + } + // node level + if v.SockOpts != nil && v.SockOpts.Mark != 0 { + m[parsing.MDKeySoMark] = v.SockOpts.Mark + } + } + if !md.IsExists(parsing.MDKeyProxyProtocol) && ppv > 0 { + // inherit from hop + m[parsing.MDKeyProxyProtocol] = ppv + } + if !md.IsExists(parsing.MDKeyNetns) { + // inherit from hop + if netns != "" { + m[parsing.MDKeyNetns] = netns + } + // node level + if v.Netns != "" { + m[parsing.MDKeyNetns] = v.Name + } } if v.Connector == nil { diff --git a/config/parsing/node/parse.go b/config/parsing/node/parse.go index bec92493..444e75ab 100644 --- a/config/parsing/node/parse.go +++ b/config/parsing/node/parse.go @@ -10,7 +10,6 @@ import ( "github.com/go-gost/core/connector" "github.com/go-gost/core/dialer" "github.com/go-gost/core/logger" - "github.com/go-gost/core/metadata" xauth "github.com/go-gost/x/auth" xbypass "github.com/go-gost/x/bypass" xchain "github.com/go-gost/x/chain" @@ -65,11 +64,6 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No return nil, err } - var nm metadata.Metadata - if cfg.Metadata != nil { - nm = mdx.NewMetadata(cfg.Metadata) - } - connectorLogger := nodeLogger.WithFields(map[string]any{ "kind": "connector", }) @@ -105,10 +99,7 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No return nil, err } - var ppv int - if nm != nil { - ppv = mdutil.GetInt(nm, parsing.MDKeyProxyProtocol) - } + md := mdx.NewMetadata(cfg.Metadata) dialerLogger := nodeLogger.WithFields(map[string]any{ "kind": "dialer", @@ -120,7 +111,7 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No dialer.AuthOption(auth_parser.Info(cfg.Dialer.Auth)), dialer.TLSConfigOption(tlsConfig), dialer.LoggerOption(dialerLogger), - dialer.ProxyProtocolOption(ppv), + dialer.ProxyProtocolOption(mdutil.GetInt(md, parsing.MDKeyProxyProtocol)), ) } else { return nil, fmt.Errorf("unregistered dialer: %s", cfg.Dialer.Type) @@ -135,16 +126,16 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No } var sockOpts *chain.SockOpts - if cfg.SockOpts != nil { + if v := mdutil.GetInt(md, parsing.MDKeySoMark); v != 0 { sockOpts = &chain.SockOpts{ - Mark: cfg.SockOpts.Mark, + Mark: v, } } tr := xchain.NewTransport(d, cr, chain.AddrTransportOption(cfg.Addr), - chain.InterfaceTransportOption(cfg.Interface), - chain.NetnsTransportOption(cfg.Netns), + chain.InterfaceTransportOption(mdutil.GetString(md, parsing.MDKeyInterface)), + chain.NetnsTransportOption(mdutil.GetString(md, parsing.MDKeyNetns)), chain.SockOptsTransportOption(sockOpts), ) @@ -153,7 +144,7 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No 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), + chain.MetadataNodeOption(md), chain.NetworkNodeOption(cfg.Network), } @@ -219,7 +210,7 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No 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{ diff --git a/connector/router/connector.go b/connector/router/connector.go index e5b20523..65796223 100644 --- a/connector/router/connector.go +++ b/connector/router/connector.go @@ -77,9 +77,9 @@ func (c *routerConnector) Connect(ctx context.Context, conn net.Conn, network, a }) } - srcAddr := conn.LocalAddr().String() - if v := ctxvalue.ClientAddrFromContext(ctx); v != "" { - srcAddr = string(v) + srcAddr := conn.RemoteAddr().String() + if v := ctxvalue.SrcAddrFromContext(ctx); v != nil { + srcAddr = v.String() } af := &relay.AddrFeature{} diff --git a/connector/tunnel/connector.go b/connector/tunnel/connector.go index 39afa5dd..76f9f6a6 100644 --- a/connector/tunnel/connector.go +++ b/connector/tunnel/connector.go @@ -73,9 +73,9 @@ func (c *tunnelConnector) Connect(ctx context.Context, conn net.Conn, network, a }) } - srcAddr := conn.LocalAddr().String() - if v := ctxvalue.ClientAddrFromContext(ctx); v != "" { - srcAddr = string(v) + srcAddr := conn.RemoteAddr().String() + if v := ctxvalue.SrcAddrFromContext(ctx); v != nil { + srcAddr = v.String() } af := &relay.AddrFeature{} diff --git a/ctx/value.go b/ctx/value.go index 349aa089..60dda25d 100644 --- a/ctx/value.go +++ b/ctx/value.go @@ -3,18 +3,21 @@ package ctx import ( "bytes" "context" + "net" "github.com/go-gost/core/logger" ) +type ClientAddr string + +func (a ClientAddr) String() string { + return string(a) +} + // clientAddrKey saves the client address. type clientAddrKey struct{} -type ClientAddr string - -var ( - keyClientAddr clientAddrKey -) +var keyClientAddr clientAddrKey func ContextWithClientAddr(ctx context.Context, addr ClientAddr) context.Context { return context.WithValue(ctx, keyClientAddr, addr) @@ -25,9 +28,43 @@ func ClientAddrFromContext(ctx context.Context) ClientAddr { return v } -// sidKey saves the session ID. -type sidKey struct{} -type Sid string +type ( + srcAddrKey struct{} + dstAddrKey struct{} +) + +var ( + keySrcAddr srcAddrKey + keyDstAddr dstAddrKey +) + +func ContextWithSrcAddr(ctx context.Context, addr net.Addr) context.Context { + return context.WithValue(ctx, keySrcAddr, addr) +} + +func SrcAddrFromContext(ctx context.Context) net.Addr { + v, _ := ctx.Value(keySrcAddr).(net.Addr) + return v +} + +func ContextWithDstAddr(ctx context.Context, addr net.Addr) context.Context { + return context.WithValue(ctx, keyDstAddr, addr) +} + +func DstAddrFromContext(ctx context.Context) net.Addr { + v, _ := ctx.Value(keyDstAddr).(net.Addr) + return v +} + +type ( + Sid string + // sidKey saves the session ID. + sidKey struct{} +) + +func (s Sid) String() string { + return string(s) +} var ( keySid sidKey @@ -42,12 +79,13 @@ func SidFromContext(ctx context.Context) Sid { return v } -// hashKey saves the hash source for Selector. -type hashKey struct{} - -type Hash struct { - Source string -} +type ( + // hashKey saves the hash source for Selector. + hashKey struct{} + Hash struct { + Source string + } +) var ( clientHashKey = &hashKey{} @@ -64,8 +102,14 @@ func HashFromContext(ctx context.Context) *Hash { return nil } -type clientIDKey struct{} -type ClientID string +type ( + ClientID string + clientIDKey struct{} +) + +func (s ClientID) String() string { + return string(s) +} var ( keyClientID = &clientIDKey{} diff --git a/go.mod b/go.mod index 391f970d..d4c7edd8 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/go-gost/x -go 1.23.1 +go 1.24 toolchain go1.24.5 @@ -25,7 +25,7 @@ require ( github.com/mitchellh/go-homedir v1.1.0 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pion/dtls/v2 v2.2.6 - github.com/pires/go-proxyproto v0.7.0 + github.com/pires/go-proxyproto v0.8.1 github.com/prometheus/client_golang v1.19.1 github.com/quic-go/quic-go v0.53.0 github.com/quic-go/webtransport-go v0.9.0 diff --git a/go.sum b/go.sum index e6885cea..3082513d 100644 --- a/go.sum +++ b/go.sum @@ -162,6 +162,8 @@ github.com/pion/udp/v2 v2.0.1 h1:xP0z6WNux1zWEjhC7onRA3EwwSliXqu1ElUZAQhUP54= github.com/pion/udp/v2 v2.0.1/go.mod h1:B7uvTMP00lzWdyMr/1PVZXtV3wpPIxBRd4Wl6AksXn8= github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs= github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4= +github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0= +github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= diff --git a/handler/dns/handler.go b/handler/dns/handler.go index c55d6c35..4df1891d 100644 --- a/handler/dns/handler.go +++ b/handler/dns/handler.go @@ -148,12 +148,8 @@ func (h *dnsHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler. SID: string(ctxvalue.SidFromContext(ctx)), } - ro.ClientIP = conn.RemoteAddr().String() - if clientAddr := ctxvalue.ClientAddrFromContext(ctx); clientAddr != "" { - ro.ClientIP = string(clientAddr) - } - if h, _, _ := net.SplitHostPort(ro.ClientIP); h != "" { - ro.ClientIP = h + if srcAddr := ctxvalue.SrcAddrFromContext(ctx); srcAddr != nil { + ro.ClientIP = srcAddr.String() } log := h.options.Logger.WithFields(map[string]any{ diff --git a/handler/file/handler.go b/handler/file/handler.go index 69462935..201453f7 100644 --- a/handler/file/handler.go +++ b/handler/file/handler.go @@ -10,6 +10,7 @@ import ( "github.com/go-gost/core/handler" md "github.com/go-gost/core/metadata" "github.com/go-gost/core/recorder" + ctxvalue "github.com/go-gost/x/ctx" xrecorder "github.com/go-gost/x/recorder" "github.com/go-gost/x/registry" ) @@ -65,7 +66,13 @@ func (h *fileHandler) Init(md md.Metadata) (err error) { } func (h *fileHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error { + var clientAddr string + if srcAddr := ctxvalue.SrcAddrFromContext(ctx); srcAddr != nil { + clientAddr = srcAddr.String() + } + h.options.Logger.WithFields(map[string]any{ + "client": clientAddr, "remote": conn.RemoteAddr().String(), "local": conn.LocalAddr().String(), }).Infof("%s - %s", conn.RemoteAddr(), conn.LocalAddr()) @@ -101,7 +108,6 @@ func (h *fileHandler) handleFunc(w http.ResponseWriter, r *http.Request) { }, Time: start, } - ro.ClientIP, _, _ = net.SplitHostPort(r.RemoteAddr) log := h.options.Logger.WithFields(map[string]any{ "remote": r.RemoteAddr, diff --git a/handler/forward/local/handler.go b/handler/forward/local/handler.go index 6ed5a0d3..9c933582 100644 --- a/handler/forward/local/handler.go +++ b/handler/forward/local/handler.go @@ -16,6 +16,7 @@ import ( "github.com/go-gost/core/recorder" ctxvalue "github.com/go-gost/x/ctx" xnet "github.com/go-gost/x/internal/net" + "github.com/go-gost/x/internal/net/proxyproto" "github.com/go-gost/x/internal/util/forwarder" "github.com/go-gost/x/internal/util/sniffing" tls_util "github.com/go-gost/x/internal/util/tls" @@ -89,22 +90,16 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand SID: string(ctxvalue.SidFromContext(ctx)), } - 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 + if srcAddr := ctxvalue.SrcAddrFromContext(ctx); srcAddr != nil { + ro.ClientIP = srcAddr.String() } log := h.options.Logger.WithFields(map[string]any{ - "remote": conn.RemoteAddr().String(), - "local": conn.LocalAddr().String(), - "sid": ro.SID, - "client": ro.ClientIP, + "network": ro.Network, + "remote": conn.RemoteAddr().String(), + "local": conn.LocalAddr().String(), + "sid": ro.SID, + "client": ro.ClientIP, }) log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr()) @@ -157,6 +152,13 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand var buf bytes.Buffer cc, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), "tcp", address) ro.Route = buf.String() + + cc = proxyproto.WrapClientConn( + h.md.proxyProtocol, + ctxvalue.SrcAddrFromContext(ctx), + ctxvalue.DstAddrFromContext(ctx), + cc) + return cc, err } sniffer := &forwarder.Sniffer{ @@ -246,6 +248,12 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand } defer cc.Close() + cc = proxyproto.WrapClientConn( + h.md.proxyProtocol, + ctxvalue.SrcAddrFromContext(ctx), + ctxvalue.DstAddrFromContext(ctx), + cc) + log = log.WithFields(map[string]any{"src": cc.LocalAddr().String(), "dst": cc.RemoteAddr().String()}) ro.Src = cc.LocalAddr().String() ro.Dst = cc.RemoteAddr().String() diff --git a/handler/forward/local/metadata.go b/handler/forward/local/metadata.go index 13ecb87a..d9f74280 100644 --- a/handler/forward/local/metadata.go +++ b/handler/forward/local/metadata.go @@ -15,6 +15,7 @@ import ( type metadata struct { readTimeout time.Duration httpKeepalive bool + proxyProtocol int sniffing bool sniffingTimeout time.Duration @@ -34,6 +35,7 @@ func (h *forwardHandler) parseMetadata(md mdata.Metadata) (err error) { } h.md.httpKeepalive = mdutil.GetBool(md, "http.keepalive") + h.md.proxyProtocol = mdutil.GetInt(md, "proxyProtocol") h.md.sniffing = mdutil.GetBool(md, "sniffing") h.md.sniffingTimeout = mdutil.GetDuration(md, "sniffing.timeout") diff --git a/handler/forward/remote/handler.go b/handler/forward/remote/handler.go index 48000e14..997d6da4 100644 --- a/handler/forward/remote/handler.go +++ b/handler/forward/remote/handler.go @@ -6,7 +6,6 @@ import ( "context" "errors" "net" - "strconv" "time" "github.com/go-gost/core/chain" @@ -91,15 +90,8 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand SID: string(ctxvalue.SidFromContext(ctx)), } - 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 + if srcAddr := ctxvalue.SrcAddrFromContext(ctx); srcAddr != nil { + ro.ClientIP = srcAddr.String() } log := h.options.Logger.WithFields(map[string]any{ @@ -166,6 +158,13 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand var buf bytes.Buffer cc, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), "tcp", address) ro.Route = buf.String() + + cc = proxyproto.WrapClientConn( + h.md.proxyProtocol, + ctxvalue.SrcAddrFromContext(ctx), + ctxvalue.DstAddrFromContext(ctx), + cc) + return cc, err } sniffer := &forwarder.Sniffer{ @@ -260,7 +259,11 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand ro.Src = cc.LocalAddr().String() ro.Dst = cc.RemoteAddr().String() - cc = proxyproto.WrapClientConn(h.md.proxyProtocol, conn.RemoteAddr(), convertAddr(conn.LocalAddr()), cc) + cc = proxyproto.WrapClientConn( + h.md.proxyProtocol, + ctxvalue.SrcAddrFromContext(ctx), + ctxvalue.DstAddrFromContext(ctx), + cc) t := time.Now() log.Infof("%s <-> %s", conn.RemoteAddr(), target.Addr) @@ -284,27 +287,3 @@ func (h *forwardHandler) checkRateLimit(addr net.Addr) bool { return true } - -func convertAddr(addr net.Addr) net.Addr { - host, sp, _ := net.SplitHostPort(addr.String()) - ip := net.ParseIP(host) - port, _ := strconv.Atoi(sp) - - if ip == nil || ip.Equal(net.IPv6zero) { - ip = net.IPv4zero - } - - switch addr.Network() { - case "tcp", "tcp4", "tcp6": - return &net.TCPAddr{ - IP: ip, - Port: port, - } - - default: - return &net.UDPAddr{ - IP: ip, - Port: port, - } - } -} diff --git a/handler/http/handler.go b/handler/http/handler.go index ddcd4519..0f3c5429 100644 --- a/handler/http/handler.go +++ b/handler/http/handler.go @@ -133,12 +133,8 @@ func (h *httpHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler SID: string(ctxvalue.SidFromContext(ctx)), } - ro.ClientIP = conn.RemoteAddr().String() - if clientAddr := ctxvalue.ClientAddrFromContext(ctx); clientAddr != "" { - ro.ClientIP = string(clientAddr) - } - if h, _, _ := net.SplitHostPort(ro.ClientIP); h != "" { - ro.ClientIP = h + if srcAddr := ctxvalue.SrcAddrFromContext(ctx); srcAddr != nil { + ro.ClientIP = srcAddr.String() } log := h.options.Logger.WithFields(map[string]any{ @@ -516,16 +512,6 @@ func (h *httpHandler) proxyRoundTrip(ctx context.Context, rw io.ReadWriteCloser, 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 http10 := req.ProtoMajor == 1 && req.ProtoMinor == 0 @@ -579,7 +565,6 @@ func (h *httpHandler) proxyRoundTrip(ctx context.Context, rw io.ReadWriteCloser, } } - ctx = ctxvalue.ContextWithClientAddr(ctx, ctxvalue.ClientAddr(clientAddr)) ctx = ctx_internal.ContextWithRecorderObject(ctx, ro) ctx = ctxvalue.ContextWithLogger(ctx, log) diff --git a/handler/http2/handler.go b/handler/http2/handler.go index a80c3e83..7d8e8b83 100644 --- a/handler/http2/handler.go +++ b/handler/http2/handler.go @@ -108,7 +108,9 @@ func (h *http2Handler) Handle(ctx context.Context, conn net.Conn, opts ...handle Time: start, SID: string(ctxvalue.SidFromContext(ctx)), } - ro.ClientIP, _, _ = net.SplitHostPort(conn.RemoteAddr().String()) + if srcAddr := ctxvalue.SrcAddrFromContext(ctx); srcAddr != nil { + ro.ClientIP = srcAddr.String() + } log := h.options.Logger.WithFields(map[string]any{ "remote": conn.RemoteAddr().String(), diff --git a/handler/http3/metadata.go b/handler/http3/metadata.go index 71275444..56c42089 100644 --- a/handler/http3/metadata.go +++ b/handler/http3/metadata.go @@ -15,15 +15,7 @@ type metadata struct { } func (h *http3Handler) parseMetadata(md mdata.Metadata) error { - const ( - header = "header" - probeResistKey = "probeResistance" - probeResistKeyX = "probe_resist" - knock = "knock" - hash = "hash" - ) - - if m := mdutil.GetStringMapString(md, header); len(m) > 0 { + if m := mdutil.GetStringMapString(md, "header"); len(m) > 0 { hd := http.Header{} for k, v := range m { hd.Add(k, v) @@ -31,20 +23,17 @@ func (h *http3Handler) parseMetadata(md mdata.Metadata) error { h.md.header = hd } - pr := mdutil.GetString(md, probeResistKey) - if pr == "" { - pr = mdutil.GetString(md, probeResistKeyX) - } + pr := mdutil.GetString(md, "probeResistance", "probe_resist") if pr != "" { if ss := strings.SplitN(pr, ":", 2); len(ss) == 2 { h.md.probeResistance = &probeResistance{ Type: ss[0], Value: ss[1], - Knock: mdutil.GetString(md, knock), + Knock: mdutil.GetString(md, "knock"), } } } - h.md.hash = mdutil.GetString(md, hash) + h.md.hash = mdutil.GetString(md, "hash") return nil } diff --git a/handler/redirect/tcp/handler.go b/handler/redirect/tcp/handler.go index 05e770b2..633efcf8 100644 --- a/handler/redirect/tcp/handler.go +++ b/handler/redirect/tcp/handler.go @@ -81,13 +81,17 @@ func (h *redirectHandler) Handle(ctx context.Context, conn net.Conn, opts ...han Time: start, SID: string(ctxvalue.SidFromContext(ctx)), } - ro.ClientIP, _, _ = net.SplitHostPort(conn.RemoteAddr().String()) + + if srcAddr := ctxvalue.SrcAddrFromContext(ctx); srcAddr != nil { + ro.ClientIP = srcAddr.String() + } log := h.options.Logger.WithFields(map[string]any{ + "network": ro.Network, "remote": conn.RemoteAddr().String(), "local": conn.LocalAddr().String(), "sid": ctxvalue.SidFromContext(ctx), - "network": ro.Network, + "client": ro.ClientIP, }) log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr()) diff --git a/handler/relay/handler.go b/handler/relay/handler.go index 73cc84b6..dc6925ab 100644 --- a/handler/relay/handler.go +++ b/handler/relay/handler.go @@ -112,12 +112,8 @@ func (h *relayHandler) Handle(ctx context.Context, conn net.Conn, opts ...handle SID: string(ctxvalue.SidFromContext(ctx)), } - ro.ClientIP = conn.RemoteAddr().String() - if clientAddr := ctxvalue.ClientAddrFromContext(ctx); clientAddr != "" { - ro.ClientIP = string(clientAddr) - } - if h, _, _ := net.SplitHostPort(ro.ClientIP); h != "" { - ro.ClientIP = h + if srcAddr := ctxvalue.SrcAddrFromContext(ctx); srcAddr != nil { + ro.ClientIP = srcAddr.String() } log := h.options.Logger.WithFields(map[string]any{ diff --git a/handler/sni/handler.go b/handler/sni/handler.go index 567b6d11..7c673d54 100644 --- a/handler/sni/handler.go +++ b/handler/sni/handler.go @@ -81,12 +81,8 @@ func (h *sniHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler. SID: string(ctxvalue.SidFromContext(ctx)), } - ro.ClientIP = conn.RemoteAddr().String() - if clientAddr := ctxvalue.ClientAddrFromContext(ctx); clientAddr != "" { - ro.ClientIP = string(clientAddr) - } - if h, _, _ := net.SplitHostPort(ro.ClientIP); h != "" { - ro.ClientIP = h + if srcAddr := ctxvalue.SrcAddrFromContext(ctx); srcAddr != nil { + ro.ClientIP = srcAddr.String() } log := h.options.Logger.WithFields(map[string]any{ @@ -131,6 +127,7 @@ func (h *sniHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler. var buf bytes.Buffer cc, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), "tcp", address) ro.Route = buf.String() + return cc, err } dialTLS := func(ctx context.Context, network, address string, cfg *tls.Config) (net.Conn, error) { diff --git a/internal/net/net.go b/internal/net/net.go index 41f882be..3be6eb45 100644 --- a/internal/net/net.go +++ b/internal/net/net.go @@ -26,8 +26,12 @@ type RemoteAddr interface { RemoteAddr() net.Addr } -type ClientAddr interface { - ClientAddr() net.Addr +type SrcAddr interface { + SrcAddr() net.Addr +} + +type DstAddr interface { + DstAddr() net.Addr } // tcpraw.TCPConn diff --git a/internal/net/pipe.go b/internal/net/pipe.go index fedd0006..b2c5fd00 100644 --- a/internal/net/pipe.go +++ b/internal/net/pipe.go @@ -69,13 +69,13 @@ func pipeBuffer(dst io.ReadWriteCloser, src io.ReadWriteCloser, bufferSize int) if cw, ok := dst.(xio.CloseWrite); ok { if e := cw.CloseWrite(); e == xio.ErrUnsupported { dst.Close() + } else { + // Set TCP half-close timeout. + xio.SetReadDeadline(dst, time.Now().Add(tcpWaitTimeout)) } } else { dst.Close() } - // Set TCP half-close timeout. - xio.SetReadDeadline(dst, time.Now().Add(tcpWaitTimeout)) - return err } diff --git a/internal/net/proxyproto/conn.go b/internal/net/proxyproto/conn.go index acb525a7..40fa17dc 100644 --- a/internal/net/proxyproto/conn.go +++ b/internal/net/proxyproto/conn.go @@ -2,6 +2,7 @@ package proxyproto import ( "net" + "strconv" xio "github.com/go-gost/x/internal/io" proxyproto "github.com/pires/go-proxyproto" @@ -11,6 +12,28 @@ type serverConn struct { net.Conn } +func (c *serverConn) RemoteAddr() net.Addr { + if conn, ok := c.Conn.(*proxyproto.Conn); ok { + return conn.Raw().RemoteAddr() + } + return c.Conn.RemoteAddr() +} + +func (c *serverConn) LocalAddr() net.Addr { + if conn, ok := c.Conn.(*proxyproto.Conn); ok { + return conn.Raw().LocalAddr() + } + return c.Conn.LocalAddr() +} + +func (c *serverConn) SrcAddr() net.Addr { + return c.Conn.RemoteAddr() +} + +func (c *serverConn) DstAddr() net.Addr { + return c.Conn.LocalAddr() +} + func (c *serverConn) CloseRead() error { if sc, ok := c.Conn.(xio.CloseRead); ok { return sc.CloseRead() @@ -36,7 +59,14 @@ func (c *serverConn) CloseWrite() error { } func WrapClientConn(ppv int, src, dst net.Addr, c net.Conn) net.Conn { - if ppv <= 0 { + if ppv <= 0 || c == nil { + return c + } + + if src = convertAddr(src); src == nil { + return c + } + if dst = convertAddr(dst); dst == nil { return c } @@ -44,3 +74,31 @@ func WrapClientConn(ppv int, src, dst net.Addr, c net.Conn) net.Conn { header.WriteTo(c) return c } + +func convertAddr(addr net.Addr) net.Addr { + if addr == nil { + return nil + } + + host, sp, _ := net.SplitHostPort(addr.String()) + ip := net.ParseIP(host) + port, _ := strconv.Atoi(sp) + + if ip == nil || ip.Equal(net.IPv6zero) { + ip = net.IPv4zero + } + + switch addr.Network() { + case "tcp", "tcp4", "tcp6": + return &net.TCPAddr{ + IP: ip, + Port: port, + } + + default: + return &net.UDPAddr{ + IP: ip, + Port: port, + } + } +} diff --git a/internal/util/ws/ws.go b/internal/util/ws/ws.go index d19e006a..ecfbfce2 100644 --- a/internal/util/ws/ws.go +++ b/internal/util/ws/ws.go @@ -5,6 +5,7 @@ import ( "sync" "time" + xio "github.com/go-gost/x/internal/io" xnet "github.com/go-gost/x/internal/net" "github.com/gorilla/websocket" ) @@ -13,14 +14,16 @@ type WebsocketConn interface { net.Conn WriteMessage(int, []byte) error ReadMessage() (int, []byte, error) - xnet.ClientAddr + xnet.SrcAddr + xio.CloseRead + xio.CloseWrite } type websocketConn struct { *websocket.Conn - rb []byte - clientAddr net.Addr - mux sync.Mutex + rb []byte + srcAddr net.Addr + mux sync.Mutex } func Conn(conn *websocket.Conn) WebsocketConn { @@ -29,10 +32,10 @@ func Conn(conn *websocket.Conn) WebsocketConn { } } -func ConnWithClientAddr(conn *websocket.Conn, clientAddr net.Addr) WebsocketConn { +func ConnWithSrcAddr(conn *websocket.Conn, srcAddr net.Addr) WebsocketConn { return &websocketConn{ - Conn: conn, - clientAddr: clientAddr, + Conn: conn, + srcAddr: srcAddr, } } @@ -77,6 +80,20 @@ func (c *websocketConn) SetWriteDeadline(t time.Time) error { return c.Conn.SetWriteDeadline(t) } -func (c *websocketConn) ClientAddr() net.Addr { - return c.clientAddr +func (c *websocketConn) SrcAddr() net.Addr { + return c.srcAddr +} + +func (c *websocketConn) CloseRead() error { + if sc, ok := c.Conn.NetConn().(xio.CloseRead); ok { + return sc.CloseRead() + } + return xio.ErrUnsupported +} + +func (c *websocketConn) CloseWrite() error { + if sc, ok := c.Conn.NetConn().(xio.CloseWrite); ok { + return sc.CloseWrite() + } + return xio.ErrUnsupported } diff --git a/internal/util/wt/conn.go b/internal/util/wt/conn.go index 5ac5a43a..5a8ce9e9 100644 --- a/internal/util/wt/conn.go +++ b/internal/util/wt/conn.go @@ -8,9 +8,9 @@ import ( ) type conn struct { - session *wt.Session - stream *wt.Stream - clientAddr net.Addr + session *wt.Session + stream *wt.Stream + srcAddr net.Addr } func Conn(session *wt.Session, stream *wt.Stream) net.Conn { @@ -20,11 +20,11 @@ func Conn(session *wt.Session, stream *wt.Stream) net.Conn { } } -func ConnWithClientAddr(session *wt.Session, stream *wt.Stream, clientAddr net.Addr) net.Conn { +func ConnWithSrcAddr(session *wt.Session, stream *wt.Stream, srcAddr net.Addr) net.Conn { return &conn{ - session: session, - stream: stream, - clientAddr: clientAddr, + session: session, + stream: stream, + srcAddr: srcAddr, } } @@ -48,8 +48,8 @@ func (c *conn) RemoteAddr() net.Addr { return c.session.RemoteAddr() } -func (c *conn) ClientAddr() net.Addr { - return c.clientAddr +func (c *conn) SrcAddr() net.Addr { + return c.srcAddr } func (c *conn) SetDeadline(t time.Time) error { diff --git a/limiter/traffic/wrapper/conn.go b/limiter/traffic/wrapper/conn.go index 30ce905e..5c71df89 100644 --- a/limiter/traffic/wrapper/conn.go +++ b/limiter/traffic/wrapper/conn.go @@ -107,9 +107,16 @@ func (c *limitConn) Metadata() metadata.Metadata { return nil } -func (c *limitConn) ClientAddr() net.Addr { - if sc, ok := c.Conn.(xnet.ClientAddr); ok { - return sc.ClientAddr() +func (c *limitConn) SrcAddr() net.Addr { + if sc, ok := c.Conn.(xnet.SrcAddr); ok { + return sc.SrcAddr() + } + return nil +} + +func (c *limitConn) DstAddr() net.Addr { + if sc, ok := c.Conn.(xnet.DstAddr); ok { + return sc.DstAddr() } return nil } diff --git a/listener/http2/conn.go b/listener/http2/conn.go index 6aa59b51..86549983 100644 --- a/listener/http2/conn.go +++ b/listener/http2/conn.go @@ -3,7 +3,6 @@ package http2 import ( "errors" "net" - "net/http" "time" mdata "github.com/go-gost/core/metadata" @@ -11,13 +10,11 @@ import ( // a dummy HTTP2 server conn used by HTTP2 handler type conn struct { - md mdata.Metadata - r *http.Request - w http.ResponseWriter - laddr net.Addr - raddr net.Addr - clientAddr net.Addr - closed chan struct{} + md mdata.Metadata + laddr net.Addr + raddr net.Addr + srcAddr net.Addr + closed chan struct{} } func (c *conn) Read(b []byte) (n int, err error) { @@ -45,8 +42,8 @@ func (c *conn) RemoteAddr() net.Addr { return c.raddr } -func (c *conn) ClientAddr() net.Addr { - return c.clientAddr +func (c *conn) SrcAddr() net.Addr { + return c.srcAddr } func (c *conn) SetDeadline(t time.Time) error { diff --git a/listener/http2/h2/conn.go b/listener/http2/h2/conn.go index f4b4ce03..30fdeb2e 100644 --- a/listener/http2/h2/conn.go +++ b/listener/http2/h2/conn.go @@ -14,7 +14,7 @@ type conn struct { w io.Writer remoteAddr net.Addr localAddr net.Addr - clientAddr net.Addr + srcAddr net.Addr closed chan struct{} } @@ -50,8 +50,8 @@ func (c *conn) RemoteAddr() net.Addr { return c.remoteAddr } -func (c *conn) ClientAddr() net.Addr { - return c.clientAddr +func (c *conn) SrcAddr() net.Addr { + return c.srcAddr } func (c *conn) SetDeadline(t time.Time) error { diff --git a/listener/http2/h2/listener.go b/listener/http2/h2/listener.go index 7c7f11f4..29d25604 100644 --- a/listener/http2/h2/listener.go +++ b/listener/http2/h2/listener.go @@ -157,7 +157,17 @@ func (l *h2Listener) Close() (err error) { } func (l *h2Listener) handleFunc(w http.ResponseWriter, r *http.Request) { - if l.logger.IsLevelEnabled(logger.TraceLevel) { + clientIP := xhttp.GetClientIP(r) + cip := "" + if clientIP != nil { + cip = clientIP.String() + } + log := l.logger.WithFields(map[string]any{ + "local": l.addr.String(), + "remote": r.RemoteAddr, + "client": cip, + }) + if log.IsLevelEnabled(logger.TraceLevel) { dump, _ := httputil.DumpRequest(r, false) l.logger.Trace(string(dump)) } @@ -195,14 +205,13 @@ func (l *h2Listener) upgrade(w http.ResponseWriter, r *http.Request) (*conn, err remoteAddr, _ := net.ResolveTCPAddr("tcp", r.RemoteAddr) if remoteAddr == nil { remoteAddr = &net.TCPAddr{ - IP: net.IPv4zero, - Port: 0, + IP: net.IPv4zero, } } - var clientAddr net.Addr + var srcAddr net.Addr if clientIP := xhttp.GetClientIP(r); clientIP != nil { - clientAddr = &net.IPAddr{IP: clientIP} + srcAddr = &net.TCPAddr{IP: clientIP} } return &conn{ @@ -210,7 +219,7 @@ func (l *h2Listener) upgrade(w http.ResponseWriter, r *http.Request) (*conn, err w: flushWriter{w}, localAddr: l.addr, remoteAddr: remoteAddr, - clientAddr: clientAddr, + srcAddr: srcAddr, closed: make(chan struct{}), }, nil } diff --git a/listener/http2/listener.go b/listener/http2/listener.go index e0d38840..1f7af845 100644 --- a/listener/http2/listener.go +++ b/listener/http2/listener.go @@ -139,21 +139,29 @@ func (l *http2Listener) Close() (err error) { } func (l *http2Listener) handleFunc(w http.ResponseWriter, r *http.Request) { - raddr, _ := net.ResolveTCPAddr("tcp", r.RemoteAddr) + remoteAddr, _ := net.ResolveTCPAddr("tcp", r.RemoteAddr) + if remoteAddr == nil { + remoteAddr = &net.TCPAddr{ + IP: net.IPv4zero, + } + } + + var srcAddr net.Addr + if clientIP := xhttp.GetClientIP(r); clientIP != nil { + srcAddr = &net.TCPAddr{IP: clientIP} + } + conn := &conn{ - laddr: l.addr, - raddr: raddr, - closed: make(chan struct{}), + laddr: l.addr, + raddr: remoteAddr, + srcAddr: srcAddr, + closed: make(chan struct{}), md: mdx.NewMetadata(map[string]any{ "r": r, "w": w, }), } - if clientIP := xhttp.GetClientIP(r); clientIP != nil { - conn.clientAddr = &net.IPAddr{IP: clientIP} - } - select { case l.cqueue <- conn: default: diff --git a/listener/http3/conn.go b/listener/http3/conn.go index 4a71b998..040e11e9 100644 --- a/listener/http3/conn.go +++ b/listener/http3/conn.go @@ -10,11 +10,11 @@ import ( // a dummy HTTP3 server conn used by HTTP3 handler type conn struct { - md mdata.Metadata - laddr net.Addr - raddr net.Addr - clientAddr net.Addr - closed chan struct{} + md mdata.Metadata + laddr net.Addr + raddr net.Addr + srcAddr net.Addr + closed chan struct{} } func (c *conn) Read(b []byte) (n int, err error) { @@ -42,8 +42,8 @@ func (c *conn) RemoteAddr() net.Addr { return c.raddr } -func (c *conn) ClientAddr() net.Addr { - return c.clientAddr +func (c *conn) SrcAddr() net.Addr { + return c.srcAddr } func (c *conn) SetDeadline(t time.Time) error { diff --git a/listener/http3/listener.go b/listener/http3/listener.go index 02d1fdce..d237d773 100644 --- a/listener/http3/listener.go +++ b/listener/http3/listener.go @@ -125,10 +125,15 @@ func (l *http3Listener) Close() (err error) { } func (l *http3Listener) handleFunc(w http.ResponseWriter, r *http.Request) { - raddr, _ := net.ResolveTCPAddr("tcp", r.RemoteAddr) + remoteAddr, _ := net.ResolveTCPAddr("tcp", r.RemoteAddr) + if remoteAddr == nil { + remoteAddr = &net.TCPAddr{ + IP: net.IPv4zero, + } + } conn := &conn{ laddr: l.addr, - raddr: raddr, + raddr: remoteAddr, closed: make(chan struct{}), md: mdx.NewMetadata(map[string]any{ "r": r, @@ -137,7 +142,7 @@ func (l *http3Listener) handleFunc(w http.ResponseWriter, r *http.Request) { } if clientIP := xhttp.GetClientIP(r); clientIP != nil { - conn.clientAddr = &net.IPAddr{IP: clientIP} + conn.srcAddr = &net.UDPAddr{IP: clientIP} } select { diff --git a/listener/http3/wt/listener.go b/listener/http3/wt/listener.go index 3cb144a1..e3e91a4a 100644 --- a/listener/http3/wt/listener.go +++ b/listener/http3/wt/listener.go @@ -176,15 +176,15 @@ func (l *wtListener) upgrade(w http.ResponseWriter, r *http.Request) { return } - var clientAddr net.Addr + var srcAddr net.Addr if clientIP != nil { - clientAddr = &net.IPAddr{IP: clientIP} + srcAddr = &net.UDPAddr{IP: clientIP} } - l.mux(s, clientAddr, log) + l.mux(s, srcAddr, log) } -func (l *wtListener) mux(s *wt.Session, clientAddr net.Addr, log logger.Logger) (err error) { +func (l *wtListener) mux(s *wt.Session, srcAddr net.Addr, log logger.Logger) (err error) { defer func() { if err != nil { s.CloseWithError(1, err.Error()) @@ -202,7 +202,7 @@ func (l *wtListener) mux(s *wt.Session, clientAddr net.Addr, log logger.Logger) } select { - case l.cqueue <- wt_util.ConnWithClientAddr(s, stream, clientAddr): + case l.cqueue <- wt_util.ConnWithSrcAddr(s, stream, srcAddr): default: stream.Close() l.logger.Warnf("connection queue is full, stream %v discarded", stream.StreamID()) diff --git a/listener/mws/listener.go b/listener/mws/listener.go index 54643544..ffc2814d 100644 --- a/listener/mws/listener.go +++ b/listener/mws/listener.go @@ -184,15 +184,15 @@ func (l *mwsListener) upgrade(w http.ResponseWriter, r *http.Request) { return } - var clientAddr net.Addr + var srcAddr net.Addr if clientIP != nil { - clientAddr = &net.IPAddr{IP: clientIP} + srcAddr = &net.TCPAddr{IP: clientIP} } - l.mux(ws_util.Conn(conn), clientAddr, log) + l.mux(ws_util.Conn(conn), srcAddr, log) } -func (l *mwsListener) mux(conn net.Conn, clientAddr net.Addr, log logger.Logger) { +func (l *mwsListener) mux(conn net.Conn, srcAddr net.Addr, log logger.Logger) { defer conn.Close() session, err := mux.ServerSession(conn, l.md.muxCfg) @@ -210,7 +210,7 @@ func (l *mwsListener) mux(conn net.Conn, clientAddr net.Addr, log logger.Logger) } select { - case l.cqueue <- &connWithClientAddr{Conn: stream, clientAddr: clientAddr}: + case l.cqueue <- &connWithSrcAddr{Conn: stream, srcAddr: srcAddr}: default: stream.Close() log.Warnf("connection queue is full, client %s discarded", stream.RemoteAddr()) @@ -218,11 +218,11 @@ func (l *mwsListener) mux(conn net.Conn, clientAddr net.Addr, log logger.Logger) } } -type connWithClientAddr struct { +type connWithSrcAddr struct { net.Conn - clientAddr net.Addr + srcAddr net.Addr } -func (c *connWithClientAddr) ClientAddr() net.Addr { - return c.clientAddr +func (c *connWithSrcAddr) SrcAddr() net.Addr { + return c.srcAddr } diff --git a/listener/ws/listener.go b/listener/ws/listener.go index 57d99ee7..639704b6 100644 --- a/listener/ws/listener.go +++ b/listener/ws/listener.go @@ -179,13 +179,13 @@ func (l *wsListener) upgrade(w http.ResponseWriter, r *http.Request) { return } - var clientAddr net.Addr + var srcAddr net.Addr if clientIP != nil { - clientAddr = &net.IPAddr{IP: clientIP} + srcAddr = &net.TCPAddr{IP: clientIP} } select { - case l.cqueue <- ws_util.ConnWithClientAddr(conn, clientAddr): + case l.cqueue <- ws_util.ConnWithSrcAddr(conn, srcAddr): default: conn.Close() log.Warnf("connection queue is full, client %s discarded", conn.RemoteAddr()) diff --git a/metadata/metadata.go b/metadata/metadata.go index 0ae3b671..e01bc923 100644 --- a/metadata/metadata.go +++ b/metadata/metadata.go @@ -9,7 +9,7 @@ import ( type mapMetadata map[string]any func NewMetadata(m map[string]any) metadata.Metadata { - if len(m) == 0 { + if m == nil { return nil } md := make(map[string]any) diff --git a/metrics/wrapper/conn.go b/metrics/wrapper/conn.go index 3df0e45d..547b6b14 100644 --- a/metrics/wrapper/conn.go +++ b/metrics/wrapper/conn.go @@ -81,9 +81,16 @@ func (c *serverConn) Metadata() metadata.Metadata { return nil } -func (c *serverConn) ClientAddr() net.Addr { - if sc, ok := c.Conn.(xnet.ClientAddr); ok { - return sc.ClientAddr() +func (c *serverConn) SrcAddr() net.Addr { + if sc, ok := c.Conn.(xnet.SrcAddr); ok { + return sc.SrcAddr() + } + return nil +} + +func (c *serverConn) DstAddr() net.Addr { + if sc, ok := c.Conn.(xnet.DstAddr); ok { + return sc.DstAddr() } return nil } diff --git a/observer/stats/wrapper/conn.go b/observer/stats/wrapper/conn.go index cd9a42b3..4df0ba2a 100644 --- a/observer/stats/wrapper/conn.go +++ b/observer/stats/wrapper/conn.go @@ -83,9 +83,16 @@ func (c *conn) Metadata() metadata.Metadata { return nil } -func (c *conn) ClientAddr() net.Addr { - if sc, ok := c.Conn.(xnet.ClientAddr); ok { - return sc.ClientAddr() +func (c *conn) SrcAddr() net.Addr { + if sc, ok := c.Conn.(xnet.SrcAddr); ok { + return sc.SrcAddr() + } + return nil +} + +func (c *conn) DstAddr() net.Addr { + if sc, ok := c.Conn.(xnet.DstAddr); ok { + return sc.DstAddr() } return nil } diff --git a/service/service.go b/service/service.go index 94fdb6ac..4ee7d35a 100644 --- a/service/service.go +++ b/service/service.go @@ -206,26 +206,38 @@ func (s *defaultService) Serve() error { s.setState(StateReady) } - clientAddr := conn.RemoteAddr().String() - if ca, ok := conn.(xnet.ClientAddr); ok { - if addr := ca.ClientAddr(); addr != nil { - clientAddr = addr.String() - } - } - clientIP := clientAddr - if h, _, _ := net.SplitHostPort(clientAddr); h != "" { - clientIP = h - } - sid := xid.New().String() ctx := ctxvalue.ContextWithSid(ctx, ctxvalue.Sid(sid)) - ctx = ctxvalue.ContextWithClientAddr(ctx, ctxvalue.ClientAddr(clientAddr)) - ctx = ctxvalue.ContextWithHash(ctx, &ctxvalue.Hash{Source: clientIP}) log := s.options.logger.WithFields(map[string]any{ "sid": sid, }) + srcAddr := conn.RemoteAddr() + if a, ok := conn.(xnet.SrcAddr); ok { + if addr := a.SrcAddr(); addr != nil { + srcAddr = addr + } + } + ctx = ctxvalue.ContextWithSrcAddr(ctx, srcAddr) + + clientAddr := srcAddr.String() + ctx = ctxvalue.ContextWithClientAddr(ctx, ctxvalue.ClientAddr(clientAddr)) + + dstAddr := conn.LocalAddr() + if a, ok := conn.(xnet.DstAddr); ok { + if addr := a.DstAddr(); addr != nil { + dstAddr = addr + } + } + ctx = ctxvalue.ContextWithDstAddr(ctx, dstAddr) + + clientIP := clientAddr + if h, _, _ := net.SplitHostPort(clientIP); h != "" { + clientIP = h + } + ctx = ctxvalue.ContextWithHash(ctx, &ctxvalue.Hash{Source: clientIP}) + for _, rec := range s.options.recorders { if rec.Record == recorder.RecorderServiceClientAddress { if err := rec.Recorder.Record(ctx, []byte(clientIP)); err != nil { @@ -235,9 +247,9 @@ func (s *defaultService) Serve() error { } } if s.options.admission != nil && - !s.options.admission.Admit(ctx, clientAddr) { + !s.options.admission.Admit(ctx, srcAddr.String()) { conn.Close() - log.Debugf("admission: %s is denied", clientAddr) + log.Debugf("admission: %s is denied", srcAddr) continue }