Просмотр исходного кода

VLESS Reverse Proxy: Add "sniffing" to outbound's "reverse" (which is actually an inbound) (#5837)

Closes https://github.com/XTLS/Xray-core/issues/5662

---------

Co-authored-by: RPRX <[email protected]>
Copilot 2 месяцев назад
Родитель
Сommit
755f0a1d12
5 измененных файлов с 84 добавлено и 27 удалено
  1. 0 2
      go.sum
  2. 40 8
      infra/conf/vless.go
  3. 24 12
      proxy/vless/account.pb.go
  4. 3 0
      proxy/vless/account.proto
  5. 17 5
      proxy/vless/outbound/outbound.go

+ 0 - 2
go.sum

@@ -129,8 +129,6 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
 golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
-golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
-golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
 golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb h1:whnFRlWMcXI9d+ZbWg+4sHnLp52d5yiIPUxMBSt4X9A=
 golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb/go.mod h1:rpwXGsirqLqN2L0JDJQlwOboGHmptD5ZD6T2VmcqhTw=
 gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=

+ 40 - 8
infra/conf/vless.go

@@ -78,8 +78,13 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
 			return nil, errors.New(`VLESS clients: "encryption" should not be in inbound settings`)
 		}
 
-		if account.Reverse != nil && account.Reverse.Tag == "" {
-			return nil, errors.New(`VLESS clients: "tag" can't be empty for "reverse"`)
+		if account.Reverse != nil {
+			if account.Reverse.Tag == "" {
+				return nil, errors.New(`VLESS clients: "tag" can't be empty for "reverse"`)
+			}
+			if account.Reverse.Sniffing != nil { // may not be reached: error json unmarshal
+				return nil, errors.New(`VLESS clients: inbound's "reverse" can't have "sniffing"`)
+			}
 		}
 
 		user.Account = serial.ToTypedMessage(account)
@@ -197,6 +202,28 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
 	return config, nil
 }
 
+type VLessReverseConfig struct {
+	Tag      string          `json:"tag"`
+	Sniffing *SniffingConfig `json:"sniffing"`
+}
+
+func (c *VLessReverseConfig) Build() (*vless.Reverse, error) {
+	if c.Tag == "" {
+		return nil, errors.New(`VLESS reverse: "tag" can't be empty`)
+	}
+	r := &vless.Reverse{
+		Tag: c.Tag,
+	}
+	if c.Sniffing != nil {
+		sc, err := c.Sniffing.Build()
+		if err != nil {
+			return nil, errors.New(`VLESS reverse: invalid "sniffing" config`).Base(err)
+		}
+		r.Sniffing = sc
+	}
+	return r, nil
+}
+
 type VLessOutboundVnext struct {
 	Address *Address          `json:"address"`
 	Port    uint16            `json:"port"`
@@ -212,7 +239,7 @@ type VLessOutboundConfig struct {
 	Flow       string                `json:"flow"`
 	Seed       string                `json:"seed"`
 	Encryption string                `json:"encryption"`
-	Reverse    *vless.Reverse        `json:"reverse"`
+	Reverse    *VLessReverseConfig   `json:"reverse"`
 	Testpre    uint32                `json:"testpre"`
 	Testseed   []uint32              `json:"testseed"`
 	Vnext      []*VLessOutboundVnext `json:"vnext"`
@@ -260,13 +287,22 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) {
 				account.Flow = c.Flow
 				//account.Seed = c.Seed
 				account.Encryption = c.Encryption
-				account.Reverse = c.Reverse
+				if c.Reverse != nil {
+					rvs, err := c.Reverse.Build()
+					if err != nil {
+						return nil, err
+					}
+					account.Reverse = rvs
+				}
 				account.Testpre = c.Testpre
 				account.Testseed = c.Testseed
 			} else {
 				if err := json.Unmarshal(rawUser, account); err != nil {
 					return nil, errors.New(`VLESS users: invalid user`).Base(err)
 				}
+				if account.Reverse != nil { // may not be reached: error json unmarshal
+					return nil, errors.New(`VLESS users: please use simplified outbound's config style to use "reverse"`)
+				}
 			}
 
 			u, err := uuid.ParseString(account.Id)
@@ -326,10 +362,6 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) {
 				return nil, errors.New(`VLESS users: unsupported "encryption": ` + account.Encryption)
 			}
 
-			if account.Reverse != nil && account.Reverse.Tag == "" {
-				return nil, errors.New(`VLESS clients: "tag" can't be empty for "reverse"`)
-			}
-
 			user.Account = serial.ToTypedMessage(account)
 			spec.User = user
 			break

+ 24 - 12
proxy/vless/account.pb.go

@@ -7,6 +7,7 @@
 package vless
 
 import (
+	proxyman "github.com/xtls/xray-core/app/proxyman"
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
 	reflect "reflect"
@@ -22,8 +23,9 @@ const (
 )
 
 type Reverse struct {
-	state         protoimpl.MessageState `protogen:"open.v1"`
-	Tag           string                 `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
+	state         protoimpl.MessageState   `protogen:"open.v1"`
+	Tag           string                   `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
+	Sniffing      *proxyman.SniffingConfig `protobuf:"bytes,2,opt,name=sniffing,proto3" json:"sniffing,omitempty"`
 	unknownFields protoimpl.UnknownFields
 	sizeCache     protoimpl.SizeCache
 }
@@ -65,6 +67,13 @@ func (x *Reverse) GetTag() string {
 	return ""
 }
 
+func (x *Reverse) GetSniffing() *proxyman.SniffingConfig {
+	if x != nil {
+		return x.Sniffing
+	}
+	return nil
+}
+
 type Account struct {
 	state protoimpl.MessageState `protogen:"open.v1"`
 	// ID of the account, in the form of a UUID, e.g., "66ad4540-b58c-4ad2-9926-ea63445a9b57".
@@ -179,9 +188,10 @@ var File_proxy_vless_account_proto protoreflect.FileDescriptor
 
 const file_proxy_vless_account_proto_rawDesc = "" +
 	"\n" +
-	"\x19proxy/vless/account.proto\x12\x10xray.proxy.vless\"\x1b\n" +
+	"\x19proxy/vless/account.proto\x12\x10xray.proxy.vless\x1a\x19app/proxyman/config.proto\"Z\n" +
 	"\aReverse\x12\x10\n" +
-	"\x03tag\x18\x01 \x01(\tR\x03tag\"\x86\x02\n" +
+	"\x03tag\x18\x01 \x01(\tR\x03tag\x12=\n" +
+	"\bsniffing\x18\x02 \x01(\v2!.xray.app.proxyman.SniffingConfigR\bsniffing\"\x86\x02\n" +
 	"\aAccount\x12\x0e\n" +
 	"\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" +
 	"\x04flow\x18\x02 \x01(\tR\x04flow\x12\x1e\n" +
@@ -210,16 +220,18 @@ func file_proxy_vless_account_proto_rawDescGZIP() []byte {
 
 var file_proxy_vless_account_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
 var file_proxy_vless_account_proto_goTypes = []any{
-	(*Reverse)(nil), // 0: xray.proxy.vless.Reverse
-	(*Account)(nil), // 1: xray.proxy.vless.Account
+	(*Reverse)(nil),                 // 0: xray.proxy.vless.Reverse
+	(*Account)(nil),                 // 1: xray.proxy.vless.Account
+	(*proxyman.SniffingConfig)(nil), // 2: xray.app.proxyman.SniffingConfig
 }
 var file_proxy_vless_account_proto_depIdxs = []int32{
-	0, // 0: xray.proxy.vless.Account.reverse:type_name -> xray.proxy.vless.Reverse
-	1, // [1:1] is the sub-list for method output_type
-	1, // [1:1] is the sub-list for method input_type
-	1, // [1:1] is the sub-list for extension type_name
-	1, // [1:1] is the sub-list for extension extendee
-	0, // [0:1] is the sub-list for field type_name
+	2, // 0: xray.proxy.vless.Reverse.sniffing:type_name -> xray.app.proxyman.SniffingConfig
+	0, // 1: xray.proxy.vless.Account.reverse:type_name -> xray.proxy.vless.Reverse
+	2, // [2:2] is the sub-list for method output_type
+	2, // [2:2] is the sub-list for method input_type
+	2, // [2:2] is the sub-list for extension type_name
+	2, // [2:2] is the sub-list for extension extendee
+	0, // [0:2] is the sub-list for field type_name
 }
 
 func init() { file_proxy_vless_account_proto_init() }

+ 3 - 0
proxy/vless/account.proto

@@ -6,8 +6,11 @@ option go_package = "github.com/xtls/xray-core/proxy/vless";
 option java_package = "com.xray.proxy.vless";
 option java_multiple_files = true;
 
+import "app/proxyman/config.proto";
+
 message Reverse {
   string tag = 1;
+  xray.app.proxyman.SniffingConfig sniffing = 2;
 }
 
 message Account {

+ 17 - 5
proxy/vless/outbound/outbound.go

@@ -97,14 +97,26 @@ func New(ctx context.Context, config *Config) (*Handler, error) {
 	}
 
 	if a.Reverse != nil {
+		rvsCtx := session.ContextWithInbound(ctx, &session.Inbound{
+			Tag:  a.Reverse.Tag,
+			User: handler.server.User, // TODO: email
+		})
+		if sc := a.Reverse.Sniffing; sc != nil && sc.Enabled {
+			rvsCtx = session.ContextWithContent(rvsCtx, &session.Content{
+				SniffingRequest: session.SniffingRequest{
+					Enabled:                        sc.Enabled,
+					OverrideDestinationForProtocol: sc.DestinationOverride,
+					ExcludeForDomain:               sc.DomainsExcluded,
+					MetadataOnly:                   sc.MetadataOnly,
+					RouteOnly:                      sc.RouteOnly,
+				},
+			})
+		}
 		handler.reverse = &Reverse{
 			tag:        a.Reverse.Tag,
 			dispatcher: v.GetFeature(routing.DispatcherType()).(routing.Dispatcher),
-			ctx: session.ContextWithInbound(ctx, &session.Inbound{
-				Tag:  a.Reverse.Tag,
-				User: handler.server.User, // TODO: email
-			}),
-			handler: handler,
+			ctx:        rvsCtx,
+			handler:    handler,
 		}
 		handler.reverse.monitorTask = &task.Periodic{
 			Execute:  handler.reverse.monitor,