diff --git a/config/config.go b/config/config.go index c88d5ac..f8f6e32 100644 --- a/config/config.go +++ b/config/config.go @@ -280,12 +280,24 @@ type ForwarderConfig struct { } type ForwardNodeConfig struct { - Name string `yaml:",omitempty" json:"name,omitempty"` - Addr string `yaml:",omitempty" json:"addr,omitempty"` - Host string `yaml:",omitempty" json:"host,omitempty"` - Protocol string `yaml:",omitempty" json:"protocol,omitempty"` - Bypass string `yaml:",omitempty" json:"bypass,omitempty"` - Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"` + Name string `yaml:",omitempty" json:"name,omitempty"` + Addr string `yaml:",omitempty" json:"addr,omitempty"` + Host string `yaml:",omitempty" json:"host,omitempty"` + Protocol string `yaml:",omitempty" json:"protocol,omitempty"` + Bypass string `yaml:",omitempty" json:"bypass,omitempty"` + Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"` + HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"` + TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"` +} + +type HTTPNodeConfig struct { + Host string `yaml:",omitempty" json:"host,omitempty"` + Header map[string]string `yaml:",omitempty" json:"header,omitempty"` +} + +type TLSNodeConfig struct { + ServerName string `yaml:"serverName,omitempty" json:"serverName,omitempty"` + Secure bool `yaml:",omitempty" json:"secure,omitempty"` } type DialerConfig struct { @@ -368,6 +380,8 @@ type NodeConfig struct { Connector *ConnectorConfig `yaml:",omitempty" json:"connector,omitempty"` Dialer *DialerConfig `yaml:",omitempty" json:"dialer,omitempty"` Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"` + HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"` + TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"` } type Config struct { diff --git a/config/parsing/chain.go b/config/parsing/chain.go index beabd1d..41f4a04 100644 --- a/config/parsing/chain.go +++ b/config/parsing/chain.go @@ -209,7 +209,8 @@ func ParseHop(cfg *config.HopConfig) (chain.Hop, error) { host = "." + host } } - node := chain.NewNode(v.Name, v.Addr, + + opts := []chain.NodeOption{ chain.TransportNodeOption(tr), chain.BypassNodeOption(bypass.BypassGroup(bypassList(v.Bypass, v.Bypasses...)...)), chain.ResoloverNodeOption(registry.ResolverRegistry().Get(v.Resolver)), @@ -217,7 +218,20 @@ func ParseHop(cfg *config.HopConfig) (chain.Hop, error) { chain.MetadataNodeOption(nm), chain.HostNodeOption(host), chain.ProtocolNodeOption(v.Protocol), - ) + } + if v.HTTP != nil { + opts = append(opts, chain.HTTPNodeOption(&chain.HTTPNodeSettings{ + Host: v.HTTP.Host, + Header: v.HTTP.Header, + })) + } + if v.TLS != nil { + opts = append(opts, chain.TLSNodeOption(&chain.TLSNodeSettings{ + ServerName: v.TLS.ServerName, + Secure: v.TLS.Secure, + })) + } + node := chain.NewNode(v.Name, v.Addr, opts...) nodes = append(nodes, node) } diff --git a/config/parsing/service.go b/config/parsing/service.go index 1665765..2e77f80 100644 --- a/config/parsing/service.go +++ b/config/parsing/service.go @@ -257,6 +257,8 @@ func parseForwarder(cfg *config.ForwarderConfig) (chain.Hop, error) { Protocol: node.Protocol, Bypass: node.Bypass, Bypasses: node.Bypasses, + HTTP: node.HTTP, + TLS: node.TLS, }, ) } diff --git a/handler/forward/local/handler.go b/handler/forward/local/handler.go index 0c87273..786754d 100644 --- a/handler/forward/local/handler.go +++ b/handler/forward/local/handler.go @@ -1,15 +1,19 @@ package local import ( + "bufio" "context" "errors" "fmt" "io" "net" + "net/http" + "net/http/httputil" "time" "github.com/go-gost/core/chain" "github.com/go-gost/core/handler" + "github.com/go-gost/core/logger" md "github.com/go-gost/core/metadata" netpkg "github.com/go-gost/x/internal/net" "github.com/go-gost/x/internal/util/forward" @@ -132,6 +136,31 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand marker.Reset() } + if protocol == forward.ProtoHTTP && + target.Options().HTTP != nil { + req, err := http.ReadRequest(bufio.NewReader(rw)) + if err != nil { + return err + } + + httpSettings := target.Options().HTTP + if httpSettings.Host != "" { + req.Host = httpSettings.Host + } + for k, v := range httpSettings.Header { + req.Header.Set(k, v) + } + + if log.IsLevelEnabled(logger.TraceLevel) { + dump, _ := httputil.DumpRequest(req, false) + log.Trace(string(dump)) + } + + if err := req.Write(cc); err != nil { + return err + } + } + t := time.Now() log.Debugf("%s <-> %s", conn.RemoteAddr(), target.Addr) netpkg.Transport(rw, cc) diff --git a/handler/forward/remote/handler.go b/handler/forward/remote/handler.go index 3581509..8a1bdee 100644 --- a/handler/forward/remote/handler.go +++ b/handler/forward/remote/handler.go @@ -1,15 +1,19 @@ package remote import ( + "bufio" "context" "errors" "fmt" "io" "net" + "net/http" + "net/http/httputil" "time" "github.com/go-gost/core/chain" "github.com/go-gost/core/handler" + "github.com/go-gost/core/logger" md "github.com/go-gost/core/metadata" netpkg "github.com/go-gost/x/internal/net" "github.com/go-gost/x/internal/util/forward" @@ -124,6 +128,30 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand marker.Reset() } + if protocol == forward.ProtoHTTP && + target.Options().HTTP != nil { + req, err := http.ReadRequest(bufio.NewReader(rw)) + if err != nil { + return err + } + + httpSettings := target.Options().HTTP + if httpSettings.Host != "" { + req.Host = httpSettings.Host + } + for k, v := range httpSettings.Header { + req.Header.Set(k, v) + } + + if log.IsLevelEnabled(logger.TraceLevel) { + dump, _ := httputil.DumpRequest(req, false) + log.Trace(string(dump)) + } + if err := req.Write(cc); err != nil { + return err + } + } + t := time.Now() log.Debugf("%s <-> %s", conn.RemoteAddr(), target.Addr) netpkg.Transport(rw, cc) diff --git a/handler/relay/bind.go b/handler/relay/bind.go index bde6f9f..24effee 100644 --- a/handler/relay/bind.go +++ b/handler/relay/bind.go @@ -199,11 +199,13 @@ func (h *relayHandler) handleTunnel(ctx context.Context, conn net.Conn, tunnelID Status: relay.StatusOK, } - if h.ep == nil { - resp.Status = relay.StatusServiceUnavailable - resp.WriteTo(conn) - return - } + /* + if h.ep == nil { + resp.Status = relay.StatusServiceUnavailable + resp.WriteTo(conn) + return + } + */ uuid, err := uuid.NewRandom() if err != nil { @@ -214,8 +216,12 @@ func (h *relayHandler) handleTunnel(ctx context.Context, conn net.Conn, tunnelID connectorID := relay.NewTunnelID(uuid[:]) + addr := ":0" + if h.ep != nil { + addr = h.ep.Addr().String() + } af := &relay.AddrFeature{} - err = af.ParseFrom(h.ep.Addr().String()) + err = af.ParseFrom(addr) if err != nil { log.Warn(err) } diff --git a/handler/relay/handler.go b/handler/relay/handler.go index 7540ed9..a293bfc 100644 --- a/handler/relay/handler.go +++ b/handler/relay/handler.go @@ -123,9 +123,8 @@ func (h *relayHandler) Handle(ctx context.Context, conn net.Conn, opts ...handle log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr()) - var tunnelID relay.TunnelID defer func() { - if tunnelID.IsZero() || err != nil { + if err != nil { conn.Close() } log.WithFields(map[string]any{ @@ -161,6 +160,7 @@ func (h *relayHandler) Handle(ctx context.Context, conn net.Conn, opts ...handle var user, pass string var address string + var tunnelID relay.TunnelID for _, f := range req.Features { switch f.Type() { case relay.FeatureUserAuth: @@ -195,20 +195,15 @@ func (h *relayHandler) Handle(ctx context.Context, conn net.Conn, opts ...handle } if h.hop != nil { - /* - if address != "" { - resp.Status = relay.StatusForbidden - log.Error("forward mode, CONNECT method is forbidden") - _, err := resp.WriteTo(conn) - return err - } - */ + defer conn.Close() // forward mode return h.handleForward(ctx, conn, network, log) } switch req.Cmd & relay.CmdMask { case relay.CmdConnect: + defer conn.Close() + if !tunnelID.IsZero() { return h.handleConnectTunnel(ctx, conn, network, address, tunnelID, log) } @@ -217,6 +212,8 @@ func (h *relayHandler) Handle(ctx context.Context, conn net.Conn, opts ...handle if !tunnelID.IsZero() { return h.handleTunnel(ctx, conn, tunnelID, log) } + + defer conn.Close() return h.handleBind(ctx, conn, network, address, log) default: resp.Status = relay.StatusBadRequest diff --git a/internal/util/forward/forward.go b/internal/util/forward/forward.go index e08a945..57d4a9d 100644 --- a/internal/util/forward/forward.go +++ b/internal/util/forward/forward.go @@ -25,6 +25,7 @@ func Sniffing(ctx context.Context, rdw io.ReadWriter) (rw io.ReadWriter, host st hdr[0] == dissector.Handshake && binary.BigEndian.Uint16(hdr[1:3]) == tls.VersionTLS10 { rw, host, err = sniffSNI(ctx, rw) + protocol = ProtoTLS return } @@ -36,6 +37,7 @@ func Sniffing(ctx context.Context, rdw io.ReadWriter) (rw io.ReadWriter, host st rw = xio.NewReadWriter(io.MultiReader(buf, rw), rw) if err == nil { host = r.Host + protocol = ProtoHTTP return } } @@ -87,11 +89,13 @@ func isHTTP(s string) bool { } const ( - SSHv2 = "SSH-2" + ProtoHTTP = "http" + ProtoTLS = "tls" + ProtoSSHv2 = "SSH-2" ) func sniffProtocol(hdr []byte) string { - if string(hdr) == SSHv2 { + if string(hdr) == ProtoSSHv2 { return "ssh" } return ""