Sniff: Routing “attrs” support non http inbound (#3808)

* 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
This commit is contained in:
风扇滑翔翼 2024-09-14 00:26:36 +08:00 committed by GitHub
parent c259e4e4a6
commit 88ae774cce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 28 additions and 4 deletions

View File

@ -35,7 +35,7 @@ type Sniffer struct {
func NewSniffer(ctx context.Context) *Sniffer { func NewSniffer(ctx context.Context) *Sniffer {
ret := &Sniffer{ ret := &Sniffer{
sniffer: []protocolSnifferWithMetadata{ 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 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 bittorrent.SniffBittorrent(b) }, false, net.Network_TCP},
{func(c context.Context, b []byte) (SniffResult, error) { return quic.SniffQUIC(b) }, false, net.Network_UDP}, {func(c context.Context, b []byte) (SniffResult, error) { return quic.SniffQUIC(b) }, false, net.Network_UDP},

View File

@ -2,11 +2,13 @@ package http
import ( import (
"bytes" "bytes"
"context"
"errors" "errors"
"strings" "strings"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/session"
) )
type version byte type version byte
@ -56,7 +58,14 @@ func beginWithHTTPMethod(b []byte) error {
return errNotHTTPMethod 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 { if err := beginWithHTTPMethod(b); err != nil {
return nil, err return nil, err
} }
@ -76,8 +85,12 @@ func SniffHTTP(b []byte) (*SniffHeader, error) {
continue continue
} }
key := strings.ToLower(string(parts[0])) 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" { if key == "host" {
rawHost := strings.ToLower(string(bytes.TrimSpace(parts[1]))) rawHost := strings.ToLower(value)
dest, err := ParseHost(rawHost, net.Port(80)) dest, err := ParseHost(rawHost, net.Port(80))
if err != nil { if err != nil {
return nil, err return nil, err
@ -85,6 +98,16 @@ func SniffHTTP(b []byte) (*SniffHeader, error) {
sh.host = dest.Address.String() 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 { if len(sh.host) > 0 {
return sh, nil return sh, nil

View File

@ -1,6 +1,7 @@
package http_test package http_test
import ( import (
"context"
"testing" "testing"
. "github.com/xtls/xray-core/common/protocol/http" . "github.com/xtls/xray-core/common/protocol/http"
@ -88,7 +89,7 @@ first_name=John&last_name=Doe&action=Submit`,
} }
for _, test := range cases { for _, test := range cases {
header, err := SniffHTTP([]byte(test.input)) header, err := SniffHTTP([]byte(test.input), context.TODO())
if test.err { if test.err {
if err == nil { if err == nil {
t.Errorf("Expect error but nil, in test: %v", test) t.Errorf("Expect error but nil, in test: %v", test)