From b5414847cbea30e106e49385eebbf5741eb5154e 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: Wed, 14 Aug 2024 13:35:07 +0000 Subject: [PATCH 1/9] SOCKS: Add mixed mode --- infra/conf/socks.go | 5 +++ proxy/socks/config.pb.go | 69 +++++++++++++++++++++++----------------- proxy/socks/config.proto | 1 + proxy/socks/server.go | 49 +++++++++++++++++++++++++++- 4 files changed, 93 insertions(+), 31 deletions(-) diff --git a/infra/conf/socks.go b/infra/conf/socks.go index 52fbe4c650ff..c6612ccf2277 100644 --- a/infra/conf/socks.go +++ b/infra/conf/socks.go @@ -35,6 +35,7 @@ type SocksServerConfig struct { Host *Address `json:"ip"` Timeout uint32 `json:"timeout"` UserLevel uint32 `json:"userLevel"` + Mixed *bool `json:"mixed"` } func (v *SocksServerConfig) Build() (proto.Message, error) { @@ -63,6 +64,10 @@ func (v *SocksServerConfig) Build() (proto.Message, error) { config.Timeout = v.Timeout config.UserLevel = v.UserLevel + config.Mixed = true + if v.Mixed != nil { + config.Mixed = *v.Mixed + } return config, nil } diff --git a/proxy/socks/config.pb.go b/proxy/socks/config.pb.go index d13ebd10fcd8..d30404d16fd6 100644 --- a/proxy/socks/config.pb.go +++ b/proxy/socks/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.34.1 +// protoc-gen-go v1.34.2 // protoc v5.27.0 // source: proxy/socks/config.proto @@ -189,6 +189,7 @@ type ServerConfig struct { // Deprecated: Marked as deprecated in proxy/socks/config.proto. Timeout uint32 `protobuf:"varint,5,opt,name=timeout,proto3" json:"timeout,omitempty"` UserLevel uint32 `protobuf:"varint,6,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"` + Mixed bool `protobuf:"varint,7,opt,name=mixed,proto3" json:"mixed,omitempty"` } func (x *ServerConfig) Reset() { @@ -266,6 +267,13 @@ func (x *ServerConfig) GetUserLevel() uint32 { return 0 } +func (x *ServerConfig) GetMixed() bool { + if x != nil { + return x.Mixed + } + return false +} + // ClientConfig is the protobuf config for Socks client. type ClientConfig struct { state protoimpl.MessageState @@ -336,7 +344,7 @@ var file_proxy_socks_config_proto_rawDesc = []byte{ 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0xe3, 0x02, 0x0a, + 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0xf9, 0x02, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x37, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x73, 0x6f, @@ -355,30 +363,31 @@ var file_proxy_socks_config_proto_rawDesc = []byte{ 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, - 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x1a, 0x3b, 0x0a, 0x0d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0x81, 0x01, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, - 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x12, 0x33, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, - 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2a, 0x25, 0x0a, 0x08, 0x41, 0x75, 0x74, 0x68, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x4e, 0x4f, 0x5f, 0x41, 0x55, 0x54, 0x48, 0x10, 0x00, 0x12, - 0x0c, 0x0a, 0x08, 0x50, 0x41, 0x53, 0x53, 0x57, 0x4f, 0x52, 0x44, 0x10, 0x01, 0x2a, 0x2e, 0x0a, - 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x4f, 0x43, 0x4b, - 0x53, 0x35, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x4f, 0x43, 0x4b, 0x53, 0x34, 0x10, 0x01, - 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x4f, 0x43, 0x4b, 0x53, 0x34, 0x41, 0x10, 0x02, 0x42, 0x52, 0x0a, - 0x14, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, - 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, - 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x73, 0x6f, 0x63, 0x6b, 0x73, 0xaa, 0x02, - 0x10, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x53, 0x6f, 0x63, 0x6b, - 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x1a, 0x3b, 0x0a, 0x0d, 0x41, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x81, 0x01, 0x0a, 0x0c, 0x43, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, + 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, + 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, + 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x2e, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2a, 0x25, 0x0a, 0x08, + 0x41, 0x75, 0x74, 0x68, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x4e, 0x4f, 0x5f, 0x41, + 0x55, 0x54, 0x48, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x41, 0x53, 0x53, 0x57, 0x4f, 0x52, + 0x44, 0x10, 0x01, 0x2a, 0x2e, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0a, + 0x0a, 0x06, 0x53, 0x4f, 0x43, 0x4b, 0x53, 0x35, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x4f, + 0x43, 0x4b, 0x53, 0x34, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x4f, 0x43, 0x4b, 0x53, 0x34, + 0x41, 0x10, 0x02, 0x42, 0x52, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, + 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x50, 0x01, 0x5a, 0x25, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, + 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x73, + 0x6f, 0x63, 0x6b, 0x73, 0xaa, 0x02, 0x10, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, + 0x79, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -395,7 +404,7 @@ func file_proxy_socks_config_proto_rawDescGZIP() []byte { var file_proxy_socks_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2) var file_proxy_socks_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4) -var file_proxy_socks_config_proto_goTypes = []interface{}{ +var file_proxy_socks_config_proto_goTypes = []any{ (AuthType)(0), // 0: xray.proxy.socks.AuthType (Version)(0), // 1: xray.proxy.socks.Version (*Account)(nil), // 2: xray.proxy.socks.Account @@ -424,7 +433,7 @@ func file_proxy_socks_config_proto_init() { return } if !protoimpl.UnsafeEnabled { - file_proxy_socks_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_proxy_socks_config_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*Account); i { case 0: return &v.state @@ -436,7 +445,7 @@ func file_proxy_socks_config_proto_init() { return nil } } - file_proxy_socks_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + file_proxy_socks_config_proto_msgTypes[1].Exporter = func(v any, i int) any { switch v := v.(*ServerConfig); i { case 0: return &v.state @@ -448,7 +457,7 @@ func file_proxy_socks_config_proto_init() { return nil } } - file_proxy_socks_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + file_proxy_socks_config_proto_msgTypes[2].Exporter = func(v any, i int) any { switch v := v.(*ClientConfig); i { case 0: return &v.state diff --git a/proxy/socks/config.proto b/proxy/socks/config.proto index 85dded0d2443..bf98fe383f82 100644 --- a/proxy/socks/config.proto +++ b/proxy/socks/config.proto @@ -37,6 +37,7 @@ message ServerConfig { bool udp_enabled = 4; uint32 timeout = 5 [deprecated = true]; uint32 user_level = 6; + bool mixed = 7; } // ClientConfig is the protobuf config for Socks client. diff --git a/proxy/socks/server.go b/proxy/socks/server.go index 19c6c906440a..d639fd1a93c4 100644 --- a/proxy/socks/server.go +++ b/proxy/socks/server.go @@ -1,8 +1,10 @@ package socks import ( + "bufio" "context" "io" + "strings" "time" "github.com/xtls/xray-core/common" @@ -19,6 +21,7 @@ import ( "github.com/xtls/xray-core/features" "github.com/xtls/xray-core/features/policy" "github.com/xtls/xray-core/features/routing" + "github.com/xtls/xray-core/proxy/http" "github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/internet/udp" ) @@ -29,6 +32,7 @@ type Server struct { policyManager policy.Manager cone bool udpFilter *UDPFilter + httpServer *http.Server } // NewServer creates a new Server object. @@ -42,6 +46,13 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { if config.AuthType == AuthType_PASSWORD { s.udpFilter = new(UDPFilter) // We only use this when auth is enabled } + if config.Mixed { + httpConfig := &http.ServerConfig{} + if config.AuthType == AuthType_PASSWORD { + httpConfig.Accounts = config.Accounts + } + s.httpServer, _ = http.NewServer(ctx, httpConfig) + } return s, nil } @@ -75,9 +86,31 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con Level: s.config.UserLevel, } + var newConn readConn + if network == net.Network_TCP { + reader := bufio.NewReader(conn) + newConn = conBuff{ + Connection: conn, + reader: reader, + } + firstbyte, _ := reader.Peek(1) + if firstbyte[0] != 5 && firstbyte[0] != 4 { // Check if socks5/4 + errors.LogDebug(ctx, "Not socks request, try to parse as HTTP request") + bytes, _ := reader.Peek(8) + str := string(bytes) + // Check if the request has a valid HTTP method. If not, back to SOCKS. + httpMethods := []string{"GET", "POST", "HEAD", "PUT", "DELETE", "OPTIONS", "CONNECT", "TRACE", "PATCH"} + for _, method := range httpMethods { + if strings.HasPrefix(str, method) { + return s.httpServer.Process(ctx, network, newConn.(stat.Connection), dispatcher) + } + } + } + } + switch network { case net.Network_TCP: - return s.processTCP(ctx, conn, dispatcher) + return s.processTCP(ctx, newConn.(stat.Connection), dispatcher) case net.Network_UDP: return s.handleUDPPayload(ctx, conn, dispatcher) default: @@ -285,6 +318,20 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dis } } +// Used for read bytes from connection without consuming data +type readConn interface { + Read(p []byte) (n int, err error) +} + +type conBuff struct { + stat.Connection + reader *bufio.Reader +} + +func (c conBuff) Read(p []byte) (n int, err error) { + return c.reader.Read(p) +} + func init() { common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewServer(ctx, config.(*ServerConfig)) From c991b66bc4f43346e77e7f02633166e1173129bf 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: Wed, 14 Aug 2024 14:17:49 +0000 Subject: [PATCH 2/9] Fix test --- infra/conf/socks_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/infra/conf/socks_test.go b/infra/conf/socks_test.go index ff1833b86917..33b537290d25 100644 --- a/infra/conf/socks_test.go +++ b/infra/conf/socks_test.go @@ -44,6 +44,7 @@ func TestSocksInboundConfig(t *testing.T) { }, Timeout: 5, UserLevel: 1, + Mixed: true, }, }, }) From a2bb53a751b49d887f381da80b10bca256c55ce7 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: Wed, 14 Aug 2024 14:45:40 +0000 Subject: [PATCH 3/9] Remove config & simplify logic --- infra/conf/socks.go | 5 --- infra/conf/socks_test.go | 1 - proxy/socks/config.pb.go | 69 +++++++++++++++++----------------------- proxy/socks/config.proto | 1 - proxy/socks/server.go | 22 ++++--------- 5 files changed, 36 insertions(+), 62 deletions(-) diff --git a/infra/conf/socks.go b/infra/conf/socks.go index c6612ccf2277..52fbe4c650ff 100644 --- a/infra/conf/socks.go +++ b/infra/conf/socks.go @@ -35,7 +35,6 @@ type SocksServerConfig struct { Host *Address `json:"ip"` Timeout uint32 `json:"timeout"` UserLevel uint32 `json:"userLevel"` - Mixed *bool `json:"mixed"` } func (v *SocksServerConfig) Build() (proto.Message, error) { @@ -64,10 +63,6 @@ func (v *SocksServerConfig) Build() (proto.Message, error) { config.Timeout = v.Timeout config.UserLevel = v.UserLevel - config.Mixed = true - if v.Mixed != nil { - config.Mixed = *v.Mixed - } return config, nil } diff --git a/infra/conf/socks_test.go b/infra/conf/socks_test.go index 33b537290d25..ff1833b86917 100644 --- a/infra/conf/socks_test.go +++ b/infra/conf/socks_test.go @@ -44,7 +44,6 @@ func TestSocksInboundConfig(t *testing.T) { }, Timeout: 5, UserLevel: 1, - Mixed: true, }, }, }) diff --git a/proxy/socks/config.pb.go b/proxy/socks/config.pb.go index d30404d16fd6..d13ebd10fcd8 100644 --- a/proxy/socks/config.pb.go +++ b/proxy/socks/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.34.2 +// protoc-gen-go v1.34.1 // protoc v5.27.0 // source: proxy/socks/config.proto @@ -189,7 +189,6 @@ type ServerConfig struct { // Deprecated: Marked as deprecated in proxy/socks/config.proto. Timeout uint32 `protobuf:"varint,5,opt,name=timeout,proto3" json:"timeout,omitempty"` UserLevel uint32 `protobuf:"varint,6,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"` - Mixed bool `protobuf:"varint,7,opt,name=mixed,proto3" json:"mixed,omitempty"` } func (x *ServerConfig) Reset() { @@ -267,13 +266,6 @@ func (x *ServerConfig) GetUserLevel() uint32 { return 0 } -func (x *ServerConfig) GetMixed() bool { - if x != nil { - return x.Mixed - } - return false -} - // ClientConfig is the protobuf config for Socks client. type ClientConfig struct { state protoimpl.MessageState @@ -344,7 +336,7 @@ var file_proxy_socks_config_proto_rawDesc = []byte{ 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0xf9, 0x02, 0x0a, + 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0xe3, 0x02, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x37, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x73, 0x6f, @@ -363,31 +355,30 @@ var file_proxy_socks_config_proto_rawDesc = []byte{ 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, - 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x1a, 0x3b, 0x0a, 0x0d, 0x41, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x81, 0x01, 0x0a, 0x0c, 0x43, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, - 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, - 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, - 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, - 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x2e, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2a, 0x25, 0x0a, 0x08, - 0x41, 0x75, 0x74, 0x68, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x4e, 0x4f, 0x5f, 0x41, - 0x55, 0x54, 0x48, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x41, 0x53, 0x53, 0x57, 0x4f, 0x52, - 0x44, 0x10, 0x01, 0x2a, 0x2e, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0a, - 0x0a, 0x06, 0x53, 0x4f, 0x43, 0x4b, 0x53, 0x35, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x4f, - 0x43, 0x4b, 0x53, 0x34, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x4f, 0x43, 0x4b, 0x53, 0x34, - 0x41, 0x10, 0x02, 0x42, 0x52, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, - 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x50, 0x01, 0x5a, 0x25, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, - 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x73, - 0x6f, 0x63, 0x6b, 0x73, 0xaa, 0x02, 0x10, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, - 0x79, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x1a, 0x3b, 0x0a, 0x0d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x22, 0x81, 0x01, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x12, 0x33, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, + 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2a, 0x25, 0x0a, 0x08, 0x41, 0x75, 0x74, 0x68, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x4e, 0x4f, 0x5f, 0x41, 0x55, 0x54, 0x48, 0x10, 0x00, 0x12, + 0x0c, 0x0a, 0x08, 0x50, 0x41, 0x53, 0x53, 0x57, 0x4f, 0x52, 0x44, 0x10, 0x01, 0x2a, 0x2e, 0x0a, + 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x4f, 0x43, 0x4b, + 0x53, 0x35, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x4f, 0x43, 0x4b, 0x53, 0x34, 0x10, 0x01, + 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x4f, 0x43, 0x4b, 0x53, 0x34, 0x41, 0x10, 0x02, 0x42, 0x52, 0x0a, + 0x14, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, + 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, + 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x73, 0x6f, 0x63, 0x6b, 0x73, 0xaa, 0x02, + 0x10, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x53, 0x6f, 0x63, 0x6b, + 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -404,7 +395,7 @@ func file_proxy_socks_config_proto_rawDescGZIP() []byte { var file_proxy_socks_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2) var file_proxy_socks_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4) -var file_proxy_socks_config_proto_goTypes = []any{ +var file_proxy_socks_config_proto_goTypes = []interface{}{ (AuthType)(0), // 0: xray.proxy.socks.AuthType (Version)(0), // 1: xray.proxy.socks.Version (*Account)(nil), // 2: xray.proxy.socks.Account @@ -433,7 +424,7 @@ func file_proxy_socks_config_proto_init() { return } if !protoimpl.UnsafeEnabled { - file_proxy_socks_config_proto_msgTypes[0].Exporter = func(v any, i int) any { + file_proxy_socks_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Account); i { case 0: return &v.state @@ -445,7 +436,7 @@ func file_proxy_socks_config_proto_init() { return nil } } - file_proxy_socks_config_proto_msgTypes[1].Exporter = func(v any, i int) any { + file_proxy_socks_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ServerConfig); i { case 0: return &v.state @@ -457,7 +448,7 @@ func file_proxy_socks_config_proto_init() { return nil } } - file_proxy_socks_config_proto_msgTypes[2].Exporter = func(v any, i int) any { + file_proxy_socks_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ClientConfig); i { case 0: return &v.state diff --git a/proxy/socks/config.proto b/proxy/socks/config.proto index bf98fe383f82..85dded0d2443 100644 --- a/proxy/socks/config.proto +++ b/proxy/socks/config.proto @@ -37,7 +37,6 @@ message ServerConfig { bool udp_enabled = 4; uint32 timeout = 5 [deprecated = true]; uint32 user_level = 6; - bool mixed = 7; } // ClientConfig is the protobuf config for Socks client. diff --git a/proxy/socks/server.go b/proxy/socks/server.go index d639fd1a93c4..d8d1253ec828 100644 --- a/proxy/socks/server.go +++ b/proxy/socks/server.go @@ -4,7 +4,6 @@ import ( "bufio" "context" "io" - "strings" "time" "github.com/xtls/xray-core/common" @@ -46,13 +45,12 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { if config.AuthType == AuthType_PASSWORD { s.udpFilter = new(UDPFilter) // We only use this when auth is enabled } - if config.Mixed { - httpConfig := &http.ServerConfig{} - if config.AuthType == AuthType_PASSWORD { - httpConfig.Accounts = config.Accounts - } - s.httpServer, _ = http.NewServer(ctx, httpConfig) + // Create http config and server + httpConfig := &http.ServerConfig{} + if config.AuthType == AuthType_PASSWORD { + httpConfig.Accounts = config.Accounts } + s.httpServer, _ = http.NewServer(ctx, httpConfig) return s, nil } @@ -96,15 +94,7 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con firstbyte, _ := reader.Peek(1) if firstbyte[0] != 5 && firstbyte[0] != 4 { // Check if socks5/4 errors.LogDebug(ctx, "Not socks request, try to parse as HTTP request") - bytes, _ := reader.Peek(8) - str := string(bytes) - // Check if the request has a valid HTTP method. If not, back to SOCKS. - httpMethods := []string{"GET", "POST", "HEAD", "PUT", "DELETE", "OPTIONS", "CONNECT", "TRACE", "PATCH"} - for _, method := range httpMethods { - if strings.HasPrefix(str, method) { - return s.httpServer.Process(ctx, network, newConn.(stat.Connection), dispatcher) - } - } + return s.httpServer.Process(ctx, network, newConn.(stat.Connection), dispatcher) } } From f1618314bc1d214956b62a96496268440b1d4835 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: Wed, 14 Aug 2024 16:19:18 +0000 Subject: [PATCH 4/9] Pass first byte to process func instead of making buffer --- proxy/http/server.go | 16 +++++++++++++--- proxy/socks/server.go | 44 ++++++++++++++----------------------------- 2 files changed, 27 insertions(+), 33 deletions(-) diff --git a/proxy/http/server.go b/proxy/http/server.go index 60a017479026..35854342b221 100644 --- a/proxy/http/server.go +++ b/proxy/http/server.go @@ -2,6 +2,7 @@ package http import ( "bufio" + "bytes" "context" "encoding/base64" "io" @@ -82,15 +83,24 @@ type readerOnly struct { io.Reader } -func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error { +func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher, firstbyte ...byte) error { inbound := session.InboundFromContext(ctx) inbound.Name = "http" inbound.CanSpliceCopy = 2 inbound.User = &protocol.MemoryUser{ Level: s.config.UserLevel, } - - reader := bufio.NewReaderSize(readerOnly{conn}, buf.Size) + var reader *bufio.Reader + // Firstbyte is for forwarded conn from SOCKS inbound + // Because it needs first byte to choose protocol + // We need to add it back + if len(firstbyte) > 0 { + readerWithoutFirstbyte := bufio.NewReaderSize(readerOnly{conn}, buf.Size) + multiReader := io.MultiReader(bytes.NewReader(firstbyte), readerWithoutFirstbyte) + reader = bufio.NewReaderSize(multiReader, buf.Size) + } else { + reader = bufio.NewReaderSize(readerOnly{conn}, buf.Size) + } Start: if err := conn.SetReadDeadline(time.Now().Add(s.policy().Timeouts.Handshake)); err != nil { diff --git a/proxy/socks/server.go b/proxy/socks/server.go index d8d1253ec828..b898179b12d8 100644 --- a/proxy/socks/server.go +++ b/proxy/socks/server.go @@ -2,6 +2,7 @@ package socks import ( "bufio" + "bytes" "context" "io" "time" @@ -84,23 +85,15 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con Level: s.config.UserLevel, } - var newConn readConn - if network == net.Network_TCP { - reader := bufio.NewReader(conn) - newConn = conBuff{ - Connection: conn, - reader: reader, - } - firstbyte, _ := reader.Peek(1) + switch network { + case net.Network_TCP: + firstbyte := make([]byte, 1) + conn.Read(firstbyte) if firstbyte[0] != 5 && firstbyte[0] != 4 { // Check if socks5/4 errors.LogDebug(ctx, "Not socks request, try to parse as HTTP request") - return s.httpServer.Process(ctx, network, newConn.(stat.Connection), dispatcher) + return s.httpServer.Process(ctx, network, conn, dispatcher, firstbyte...) } - } - - switch network { - case net.Network_TCP: - return s.processTCP(ctx, newConn.(stat.Connection), dispatcher) + return s.processTCP(ctx, conn, dispatcher, firstbyte) case net.Network_UDP: return s.handleUDPPayload(ctx, conn, dispatcher) default: @@ -108,7 +101,7 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con } } -func (s *Server) processTCP(ctx context.Context, conn stat.Connection, dispatcher routing.Dispatcher) error { +func (s *Server) processTCP(ctx context.Context, conn stat.Connection, dispatcher routing.Dispatcher, firstbyte []byte) error { plcy := s.policy() if err := conn.SetReadDeadline(time.Now().Add(plcy.Timeouts.Handshake)); err != nil { errors.LogInfoInner(ctx, err, "failed to set deadline") @@ -126,7 +119,12 @@ func (s *Server) processTCP(ctx context.Context, conn stat.Connection, dispatche localAddress: net.IPAddress(conn.LocalAddr().(*net.TCPAddr).IP), } - reader := &buf.BufferedReader{Reader: buf.NewReader(conn)} + // Firstbyte is for forwarded conn from SOCKS inbound + // Because it needs first byte to choose protocol + // We need to add it back + readerWithoutFirstbyte := &buf.BufferedReader{Reader: buf.NewReader(conn)} + multiReader := io.MultiReader(bytes.NewReader(firstbyte), readerWithoutFirstbyte) + reader := bufio.NewReaderSize(multiReader, buf.Size) request, err := svrSession.Handshake(reader, conn) if err != nil { if inbound.Source.IsValid() { @@ -308,20 +306,6 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dis } } -// Used for read bytes from connection without consuming data -type readConn interface { - Read(p []byte) (n int, err error) -} - -type conBuff struct { - stat.Connection - reader *bufio.Reader -} - -func (c conBuff) Read(p []byte) (n int, err error) { - return c.reader.Read(p) -} - func init() { common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewServer(ctx, config.(*ServerConfig)) From 0fd04bdd62e557a94bf04f13cb7239d0bba2840a 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: Wed, 14 Aug 2024 16:39:03 +0000 Subject: [PATCH 5/9] Fix test --- proxy/http/server.go | 13 +++++++++---- proxy/socks/server.go | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/proxy/http/server.go b/proxy/http/server.go index 35854342b221..42cf4299fef2 100644 --- a/proxy/http/server.go +++ b/proxy/http/server.go @@ -83,7 +83,15 @@ type readerOnly struct { io.Reader } -func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher, firstbyte ...byte) error { +func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error { + return s.ProcessWithFirstbyte(ctx, network, conn, dispatcher) +} + +// Firstbyte is for forwarded conn from SOCKS inbound +// Because it needs first byte to choose protocol +// We need to add it back +// Other parts are the same as the process function +func (s *Server) ProcessWithFirstbyte(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher, firstbyte ...byte) error { inbound := session.InboundFromContext(ctx) inbound.Name = "http" inbound.CanSpliceCopy = 2 @@ -91,9 +99,6 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con Level: s.config.UserLevel, } var reader *bufio.Reader - // Firstbyte is for forwarded conn from SOCKS inbound - // Because it needs first byte to choose protocol - // We need to add it back if len(firstbyte) > 0 { readerWithoutFirstbyte := bufio.NewReaderSize(readerOnly{conn}, buf.Size) multiReader := io.MultiReader(bytes.NewReader(firstbyte), readerWithoutFirstbyte) diff --git a/proxy/socks/server.go b/proxy/socks/server.go index b898179b12d8..021a160881ef 100644 --- a/proxy/socks/server.go +++ b/proxy/socks/server.go @@ -91,7 +91,7 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con conn.Read(firstbyte) if firstbyte[0] != 5 && firstbyte[0] != 4 { // Check if socks5/4 errors.LogDebug(ctx, "Not socks request, try to parse as HTTP request") - return s.httpServer.Process(ctx, network, conn, dispatcher, firstbyte...) + return s.httpServer.ProcessWithFirstbyte(ctx, network, conn, dispatcher, firstbyte...) } return s.processTCP(ctx, conn, dispatcher, firstbyte) case net.Network_UDP: From 688123544dc867bec5f290f87b015c27250e7760 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: Wed, 14 Aug 2024 16:50:57 +0000 Subject: [PATCH 6/9] Add test --- testing/scenarios/socks_test.go | 82 +++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/testing/scenarios/socks_test.go b/testing/scenarios/socks_test.go index cb158c5861d4..1df7f353e82c 100644 --- a/testing/scenarios/socks_test.go +++ b/testing/scenarios/socks_test.go @@ -14,6 +14,7 @@ import ( "github.com/xtls/xray-core/proxy/blackhole" "github.com/xtls/xray-core/proxy/dokodemo" "github.com/xtls/xray-core/proxy/freedom" + "github.com/xtls/xray-core/proxy/http" "github.com/xtls/xray-core/proxy/socks" "github.com/xtls/xray-core/testing/servers/tcp" "github.com/xtls/xray-core/testing/servers/udp" @@ -102,6 +103,87 @@ func TestSocksBridgeTCP(t *testing.T) { } } +func TestSocksWithHttpRequest(t *testing.T) { + tcpServer := tcp.Server{ + MsgProcessor: xor, + } + dest, err := tcpServer.Start() + common.Must(err) + defer tcpServer.Close() + + serverPort := tcp.PickPort() + serverConfig := &core.Config{ + Inbound: []*core.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}}, + Listen: net.NewIPOrDomain(net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&socks.ServerConfig{ + AuthType: socks.AuthType_PASSWORD, + Accounts: map[string]string{ + "Test Account": "Test Password", + }, + Address: net.NewIPOrDomain(net.LocalHostIP), + UdpEnabled: false, + }), + }, + }, + Outbound: []*core.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), + }, + }, + } + + clientPort := tcp.PickPort() + clientConfig := &core.Config{ + Inbound: []*core.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}}, + Listen: net.NewIPOrDomain(net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, + }), + }, + }, + Outbound: []*core.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&http.ClientConfig{ + Server: []*protocol.ServerEndpoint{ + { + Address: net.NewIPOrDomain(net.LocalHostIP), + Port: uint32(serverPort), + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&http.Account{ + Username: "Test Account", + Password: "Test Password", + }), + }, + }, + }, + }, + }), + }, + }, + } + + servers, err := InitializeServerConfigs(serverConfig, clientConfig) + common.Must(err) + defer CloseAllServers(servers) + + if err := testTCPConn(clientPort, 1024, time.Second*2)(); err != nil { + t.Error(err) + } +} + func TestSocksBridageUDP(t *testing.T) { udpServer := udp.Server{ MsgProcessor: xor, From d31124c3b16dca81e3898d25d48aefba5e2c2490 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: Thu, 15 Aug 2024 07:23:19 +0000 Subject: [PATCH 7/9] Use io.Reader --- proxy/socks/server.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/proxy/socks/server.go b/proxy/socks/server.go index 021a160881ef..2d35d9cf925c 100644 --- a/proxy/socks/server.go +++ b/proxy/socks/server.go @@ -1,7 +1,6 @@ package socks import ( - "bufio" "bytes" "context" "io" @@ -123,8 +122,7 @@ func (s *Server) processTCP(ctx context.Context, conn stat.Connection, dispatche // Because it needs first byte to choose protocol // We need to add it back readerWithoutFirstbyte := &buf.BufferedReader{Reader: buf.NewReader(conn)} - multiReader := io.MultiReader(bytes.NewReader(firstbyte), readerWithoutFirstbyte) - reader := bufio.NewReaderSize(multiReader, buf.Size) + reader := io.MultiReader(bytes.NewReader(firstbyte), readerWithoutFirstbyte) request, err := svrSession.Handshake(reader, conn) if err != nil { if inbound.Source.IsValid() { From 02a691a8611895bdf99e3faa106c6cb04b6edc9b Mon Sep 17 00:00:00 2001 From: RPRX <63339210+RPRX@users.noreply.github.com> Date: Thu, 15 Aug 2024 09:59:55 +0000 Subject: [PATCH 8/9] Update server.go --- proxy/socks/server.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/proxy/socks/server.go b/proxy/socks/server.go index 2d35d9cf925c..00894ca8612a 100644 --- a/proxy/socks/server.go +++ b/proxy/socks/server.go @@ -1,7 +1,6 @@ package socks import ( - "bytes" "context" "io" "time" @@ -88,8 +87,8 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con case net.Network_TCP: firstbyte := make([]byte, 1) conn.Read(firstbyte) - if firstbyte[0] != 5 && firstbyte[0] != 4 { // Check if socks5/4 - errors.LogDebug(ctx, "Not socks request, try to parse as HTTP request") + if firstbyte[0] != 5 && firstbyte[0] != 4 { // Check if it is Socks5/4/4a + errors.LogDebug(ctx, "Not Socks request, try to parse as HTTP request") return s.httpServer.ProcessWithFirstbyte(ctx, network, conn, dispatcher, firstbyte...) } return s.processTCP(ctx, conn, dispatcher, firstbyte) @@ -121,8 +120,10 @@ func (s *Server) processTCP(ctx context.Context, conn stat.Connection, dispatche // Firstbyte is for forwarded conn from SOCKS inbound // Because it needs first byte to choose protocol // We need to add it back - readerWithoutFirstbyte := &buf.BufferedReader{Reader: buf.NewReader(conn)} - reader := io.MultiReader(bytes.NewReader(firstbyte), readerWithoutFirstbyte) + reader := &buf.BufferedReader{ + Reader: buf.NewReader(conn), + Buffer: buf.MultiBuffer{buf.FromBytes(firstbyte)}, + } request, err := svrSession.Handshake(reader, conn) if err != nil { if inbound.Source.IsValid() { @@ -155,7 +156,7 @@ func (s *Server) processTCP(ctx context.Context, conn stat.Connection, dispatche }) } - return s.transport(ctx, reader, conn, dest, dispatcher, inbound) + return s.transport(ctx, conn, dest, dispatcher, inbound) } if request.Command == protocol.RequestCommandUDP { @@ -174,7 +175,7 @@ func (*Server) handleUDP(c io.Reader) error { return common.Error2(io.Copy(buf.DiscardBytes, c)) } -func (s *Server) transport(ctx context.Context, reader io.Reader, writer io.Writer, dest net.Destination, dispatcher routing.Dispatcher, inbound *session.Inbound) error { +func (s *Server) transport(ctx context.Context, conn stat.Connection, dest net.Destination, dispatcher routing.Dispatcher, inbound *session.Inbound) error { ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, s.policy().Timeouts.ConnectionIdle) @@ -191,7 +192,7 @@ func (s *Server) transport(ctx context.Context, reader io.Reader, writer io.Writ requestDone := func() error { defer timer.SetTimeout(plcy.Timeouts.DownlinkOnly) - if err := buf.Copy(buf.NewReader(reader), link.Writer, buf.UpdateActivity(timer)); err != nil { + if err := buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer)); err != nil { return errors.New("failed to transport all TCP request").Base(err) } @@ -201,7 +202,7 @@ func (s *Server) transport(ctx context.Context, reader io.Reader, writer io.Writ responseDone := func() error { defer timer.SetTimeout(plcy.Timeouts.UplinkOnly) - v2writer := buf.NewWriter(writer) + v2writer := buf.NewWriter(conn) if err := buf.Copy(link.Reader, v2writer, buf.UpdateActivity(timer)); err != nil { return errors.New("failed to transport all TCP response").Base(err) } From cc0bb83af09edd9e985a4f2fdcf9f217c5ef4b88 Mon Sep 17 00:00:00 2001 From: RPRX <63339210+RPRX@users.noreply.github.com> Date: Thu, 15 Aug 2024 10:12:05 +0000 Subject: [PATCH 9/9] Update server.go --- proxy/socks/server.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/proxy/socks/server.go b/proxy/socks/server.go index 00894ca8612a..7375d2873ccd 100644 --- a/proxy/socks/server.go +++ b/proxy/socks/server.go @@ -41,13 +41,12 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), cone: ctx.Value("cone").(bool), } - if config.AuthType == AuthType_PASSWORD { - s.udpFilter = new(UDPFilter) // We only use this when auth is enabled + httpConfig := &http.ServerConfig{ + UserLevel: config.UserLevel, } - // Create http config and server - httpConfig := &http.ServerConfig{} if config.AuthType == AuthType_PASSWORD { httpConfig.Accounts = config.Accounts + s.udpFilter = new(UDPFilter) // We only use this when auth is enabled } s.httpServer, _ = http.NewServer(ctx, httpConfig) return s, nil