From 88ae774cced77a5698d2ba6c1ac2bb0e740ae968 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E6=89=87=E6=BB=91=E7=BF=94=E7=BF=BC?= Date: Sat, 14 Sep 2024 00:26:36 +0800 Subject: [PATCH] =?UTF-8?q?Sniff:=20Routing=20=E2=80=9Cattrs=E2=80=9D=20su?= =?UTF-8?q?pport=20non=20http=20inbound=20(#3808)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Sniff: Support attrs route in non http inbound * Add capability to sniff method and path * Fix test * Skip HTTP inbound PlainHTTP mode * Fix test again --- app/dispatcher/sniffer.go | 2 +- common/protocol/http/sniff.go | 27 +++++++++++++++++++++++++-- common/protocol/http/sniff_test.go | 3 ++- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/app/dispatcher/sniffer.go b/app/dispatcher/sniffer.go index 4d62f1b3..c138447d 100644 --- a/app/dispatcher/sniffer.go +++ b/app/dispatcher/sniffer.go @@ -35,7 +35,7 @@ type Sniffer struct { func NewSniffer(ctx context.Context) *Sniffer { ret := &Sniffer{ sniffer: []protocolSnifferWithMetadata{ - {func(c context.Context, b []byte) (SniffResult, error) { return http.SniffHTTP(b) }, false, net.Network_TCP}, + {func(c context.Context, b []byte) (SniffResult, error) { return http.SniffHTTP(b, c) }, false, net.Network_TCP}, {func(c context.Context, b []byte) (SniffResult, error) { return tls.SniffTLS(b) }, false, net.Network_TCP}, {func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffBittorrent(b) }, false, net.Network_TCP}, {func(c context.Context, b []byte) (SniffResult, error) { return quic.SniffQUIC(b) }, false, net.Network_UDP}, diff --git a/common/protocol/http/sniff.go b/common/protocol/http/sniff.go index ceedede2..e85a0792 100644 --- a/common/protocol/http/sniff.go +++ b/common/protocol/http/sniff.go @@ -2,11 +2,13 @@ package http import ( "bytes" + "context" "errors" "strings" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/session" ) type version byte @@ -56,7 +58,14 @@ func beginWithHTTPMethod(b []byte) error { return errNotHTTPMethod } -func SniffHTTP(b []byte) (*SniffHeader, error) { +func SniffHTTP(b []byte, c context.Context) (*SniffHeader, error) { + content := session.ContentFromContext(c) + ShouldSniffAttr := true + // If content.Attributes have information, that means it comes from HTTP inbound PlainHTTP mode. + // It will set attributes, so skip it. + if content == nil || len(content.Attributes) != 0 { + ShouldSniffAttr = false + } if err := beginWithHTTPMethod(b); err != nil { return nil, err } @@ -76,8 +85,12 @@ func SniffHTTP(b []byte) (*SniffHeader, error) { continue } key := strings.ToLower(string(parts[0])) + value := string(bytes.TrimSpace(parts[1])) + if ShouldSniffAttr { + content.SetAttribute(key, value) // Put header in attribute + } if key == "host" { - rawHost := strings.ToLower(string(bytes.TrimSpace(parts[1]))) + rawHost := strings.ToLower(value) dest, err := ParseHost(rawHost, net.Port(80)) if err != nil { return nil, err @@ -85,6 +98,16 @@ func SniffHTTP(b []byte) (*SniffHeader, error) { sh.host = dest.Address.String() } } + // Parse request line + // Request line is like this + // "GET /homo/114514 HTTP/1.1" + if len(headers) > 0 && ShouldSniffAttr { + RequestLineParts := bytes.Split(headers[0], []byte{' '}) + if len(RequestLineParts) == 3 { + content.SetAttribute(":method", string(RequestLineParts[0])) + content.SetAttribute(":path", string(RequestLineParts[1])) + } + } if len(sh.host) > 0 { return sh, nil diff --git a/common/protocol/http/sniff_test.go b/common/protocol/http/sniff_test.go index fff66415..09ce7d6c 100644 --- a/common/protocol/http/sniff_test.go +++ b/common/protocol/http/sniff_test.go @@ -1,6 +1,7 @@ package http_test import ( + "context" "testing" . "github.com/xtls/xray-core/common/protocol/http" @@ -88,7 +89,7 @@ first_name=John&last_name=Doe&action=Submit`, } for _, test := range cases { - header, err := SniffHTTP([]byte(test.input)) + header, err := SniffHTTP([]byte(test.input), context.TODO()) if test.err { if err == nil { t.Errorf("Expect error but nil, in test: %v", test)