add udp relay support for http handler

This commit is contained in:
ginuerzh
2021-12-01 21:23:19 +08:00
parent f3411832a8
commit 15f9aa091b
13 changed files with 386 additions and 250 deletions

View File

@ -90,6 +90,11 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
req.URL.Scheme = "http"
}
network := req.Header.Get("X-Gost-Protocol")
if network != "udp" {
network = "tcp"
}
// Try to get the actual host.
// Compatible with GOST 2.x.
if v := req.Header.Get("Gost-Target"); v != "" {
@ -168,6 +173,11 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
return
}
if network == "udp" {
h.handleUDP(ctx, conn, network, req.Host)
return
}
if req.Method == "PRI" ||
(req.Method != http.MethodConnect && req.URL.Scheme != "http") {
resp.StatusCode = http.StatusBadRequest
@ -187,7 +197,7 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
WithChain(h.chain).
WithRetry(h.md.retryCount).
WithLogger(h.logger)
cc, err := r.Dial(ctx, "tcp", addr)
cc, err := r.Dial(ctx, network, addr)
if err != nil {
resp.StatusCode = http.StatusServiceUnavailable
resp.Write(conn)
@ -209,13 +219,13 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
h.logger.Debug(string(dump))
}
if err = resp.Write(conn); err != nil {
h.logger.Warn(err)
h.logger.Error(err)
return
}
} else {
req.Header.Del("Proxy-Connection")
if err = req.Write(cc); err != nil {
h.logger.Warn(err)
h.logger.Error(err)
return
}
}

View File

@ -13,6 +13,7 @@ type metadata struct {
retryCount int
probeResist *probeResist
sni bool
enableUDP bool
}
func (h *httpHandler) parseMetadata(md md.Metadata) error {
@ -23,6 +24,7 @@ func (h *httpHandler) parseMetadata(md md.Metadata) error {
knock = "knock"
retryCount = "retry"
sni = "sni"
enableUDP = "udp"
)
h.md.proxyAgent = md.GetString(proxyAgent)
@ -53,6 +55,7 @@ func (h *httpHandler) parseMetadata(md md.Metadata) error {
}
h.md.retryCount = md.GetInt(retryCount)
h.md.sni = md.GetBool(sni)
h.md.enableUDP = md.GetBool(enableUDP)
return nil
}

83
pkg/handler/http/udp.go Normal file
View File

@ -0,0 +1,83 @@
package http
import (
"context"
"net"
"net/http"
"net/http/httputil"
"time"
"github.com/go-gost/gost/pkg/chain"
"github.com/go-gost/gost/pkg/common/util/socks"
"github.com/go-gost/gost/pkg/handler"
"github.com/go-gost/gost/pkg/logger"
)
func (h *httpHandler) handleUDP(ctx context.Context, conn net.Conn, network, address string) {
h.logger = h.logger.WithFields(map[string]interface{}{
"cmd": "udp",
})
resp := &http.Response{
ProtoMajor: 1,
ProtoMinor: 1,
Header: http.Header{},
}
if h.md.proxyAgent != "" {
resp.Header.Add("Proxy-Agent", h.md.proxyAgent)
}
if !h.md.enableUDP {
resp.StatusCode = http.StatusForbidden
resp.Write(conn)
if h.logger.IsLevelEnabled(logger.DebugLevel) {
dump, _ := httputil.DumpResponse(resp, false)
h.logger.Debug(string(dump))
}
h.logger.Error("UDP relay is diabled")
return
}
resp.StatusCode = http.StatusOK
if h.logger.IsLevelEnabled(logger.DebugLevel) {
dump, _ := httputil.DumpResponse(resp, false)
h.logger.Debug(string(dump))
}
if err := resp.Write(conn); err != nil {
h.logger.Error(err)
return
}
// obtain a udp connection
r := (&chain.Router{}).
WithChain(h.chain).
WithRetry(h.md.retryCount).
WithLogger(h.logger)
c, err := r.Dial(ctx, "udp", "") // UDP association
if err != nil {
h.logger.Error(err)
return
}
defer c.Close()
pc, ok := c.(net.PacketConn)
if !ok {
h.logger.Errorf("wrong connection type")
return
}
relay := handler.NewUDPRelay(socks.UDPTunServerConn(conn), pc).
WithBypass(h.bypass).
WithLogger(h.logger)
t := time.Now()
h.logger.Infof("%s <-> %s", conn.RemoteAddr(), pc.LocalAddr())
relay.Run()
h.logger.
WithFields(map[string]interface{}{
"duration": time.Since(t),
}).
Infof("%s >-< %s", conn.RemoteAddr(), pc.LocalAddr())
}