From 77d0419aca2fe9b5b07db694be6b8a2e28a38252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 16 Sep 2021 14:42:12 +0800 Subject: [PATCH] Add socks4/4a support --- infra/conf/socks.go | 15 +++++ proxy/socks/client.go | 38 +++++++++++- proxy/socks/config.pb.go | 127 +++++++++++++++++++++++++++++---------- proxy/socks/config.proto | 7 +++ 4 files changed, 154 insertions(+), 33 deletions(-) diff --git a/infra/conf/socks.go b/infra/conf/socks.go index b86dfae1..fe9205da 100644 --- a/infra/conf/socks.go +++ b/infra/conf/socks.go @@ -2,6 +2,7 @@ package conf import ( "encoding/json" + "strings" "github.com/golang/protobuf/proto" @@ -73,11 +74,22 @@ type SocksRemoteConfig struct { type SocksClientConfig struct { Servers []*SocksRemoteConfig `json:"servers"` + Version string `json:"version"` } func (v *SocksClientConfig) Build() (proto.Message, error) { config := new(socks.ClientConfig) config.Server = make([]*protocol.ServerEndpoint, len(v.Servers)) + switch strings.ToLower(v.Version) { + case "4": + config.Version = socks.Version_SOCKS4 + case "4a": + config.Version = socks.Version_SOCKS4A + case "", "5": + config.Version = socks.Version_SOCKS5 + default: + return nil, newError("failed to parse socks server version: ", v.Version).AtError() + } for idx, serverConfig := range v.Servers { server := &protocol.ServerEndpoint{ Address: serverConfig.Address.Build(), @@ -92,6 +104,9 @@ func (v *SocksClientConfig) Build() (proto.Message, error) { if err := json.Unmarshal(rawUser, account); err != nil { return nil, newError("failed to parse socks account").Base(err).AtError() } + if config.Version != socks.Version_SOCKS5 && account.Password != "" { + return nil, newError("password is only supported in socks5").AtError() + } user.Account = serial.ToTypedMessage(account.Build()) server.User = append(server.User, user) } diff --git a/proxy/socks/client.go b/proxy/socks/client.go index 409113ac..e1274079 100644 --- a/proxy/socks/client.go +++ b/proxy/socks/client.go @@ -15,6 +15,7 @@ import ( "github.com/xtls/xray-core/common/signal" "github.com/xtls/xray-core/common/task" "github.com/xtls/xray-core/core" + "github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/policy" "github.com/xtls/xray-core/transport" "github.com/xtls/xray-core/transport/internet" @@ -24,6 +25,8 @@ import ( type Client struct { serverPicker protocol.ServerPicker policyManager policy.Manager + version Version + dns dns.Client } // NewClient create a new Socks5 client based on the given config. @@ -41,10 +44,16 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { } v := core.MustFromContext(ctx) - return &Client{ + c := &Client{ serverPicker: protocol.NewRoundRobinServerPicker(serverList), policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), - }, nil + version: config.Version, + } + if config.Version == Version_SOCKS4 { + c.dns = v.GetFeature(dns.ClientType()).(dns.Client) + } + + return c, nil } // Process implements proxy.Outbound.Process. @@ -91,6 +100,31 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter Address: destination.Address, Port: destination.Port, } + + switch c.version { + case Version_SOCKS4: + if request.Address.Family().IsDomain() { + ips, err := c.dns.LookupIP(request.Address.Domain(), dns.IPOption{ + IPv4Enable: true, + }) + if err != nil { + return err + } else if len(ips) == 0 { + return dns.ErrEmptyResponse + } + request.Address = net.IPAddress(ips[0]) + } + fallthrough + case Version_SOCKS4A: + request.Version = socks4Version + + if destination.Network == net.Network_UDP { + return newError("udp is not supported in socks4") + } else if destination.Address.Family().IsIPv6() { + return newError("ipv6 is not supported in socks4") + } + } + if destination.Network == net.Network_UDP { request.Command = protocol.RequestCommandUDP } diff --git a/proxy/socks/config.pb.go b/proxy/socks/config.pb.go index d61419b5..b3116fba 100644 --- a/proxy/socks/config.pb.go +++ b/proxy/socks/config.pb.go @@ -71,6 +71,55 @@ func (AuthType) EnumDescriptor() ([]byte, []int) { return file_proxy_socks_config_proto_rawDescGZIP(), []int{0} } +type Version int32 + +const ( + Version_SOCKS5 Version = 0 + Version_SOCKS4 Version = 1 + Version_SOCKS4A Version = 2 +) + +// Enum value maps for Version. +var ( + Version_name = map[int32]string{ + 0: "SOCKS5", + 1: "SOCKS4", + 2: "SOCKS4A", + } + Version_value = map[string]int32{ + "SOCKS5": 0, + "SOCKS4": 1, + "SOCKS4A": 2, + } +) + +func (x Version) Enum() *Version { + p := new(Version) + *p = x + return p +} + +func (x Version) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Version) Descriptor() protoreflect.EnumDescriptor { + return file_proxy_socks_config_proto_enumTypes[1].Descriptor() +} + +func (Version) Type() protoreflect.EnumType { + return &file_proxy_socks_config_proto_enumTypes[1] +} + +func (x Version) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Version.Descriptor instead. +func (Version) EnumDescriptor() ([]byte, []int) { + return file_proxy_socks_config_proto_rawDescGZIP(), []int{1} +} + // Account represents a Socks account. type Account struct { state protoimpl.MessageState @@ -224,7 +273,8 @@ type ClientConfig struct { unknownFields protoimpl.UnknownFields // Sever is a list of Socks server addresses. - Server []*protocol.ServerEndpoint `protobuf:"bytes,1,rep,name=server,proto3" json:"server,omitempty"` + Server []*protocol.ServerEndpoint `protobuf:"bytes,1,rep,name=server,proto3" json:"server,omitempty"` + Version Version `protobuf:"varint,2,opt,name=version,proto3,enum=xray.proxy.socks.Version" json:"version,omitempty"` } func (x *ClientConfig) Reset() { @@ -266,6 +316,13 @@ func (x *ClientConfig) GetServer() []*protocol.ServerEndpoint { return nil } +func (x *ClientConfig) GetVersion() Version { + if x != nil { + return x.Version + } + return Version_SOCKS5 +} + var File_proxy_socks_config_proto protoreflect.FileDescriptor var file_proxy_socks_config_proto_rawDesc = []byte{ @@ -302,20 +359,26 @@ var file_proxy_socks_config_proto_rawDesc = []byte{ 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, 0x4c, 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, - 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, 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, + 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 ( @@ -330,27 +393,29 @@ func file_proxy_socks_config_proto_rawDescGZIP() []byte { return file_proxy_socks_config_proto_rawDescData } -var file_proxy_socks_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +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{}{ (AuthType)(0), // 0: xray.proxy.socks.AuthType - (*Account)(nil), // 1: xray.proxy.socks.Account - (*ServerConfig)(nil), // 2: xray.proxy.socks.ServerConfig - (*ClientConfig)(nil), // 3: xray.proxy.socks.ClientConfig - nil, // 4: xray.proxy.socks.ServerConfig.AccountsEntry - (*net.IPOrDomain)(nil), // 5: xray.common.net.IPOrDomain - (*protocol.ServerEndpoint)(nil), // 6: xray.common.protocol.ServerEndpoint + (Version)(0), // 1: xray.proxy.socks.Version + (*Account)(nil), // 2: xray.proxy.socks.Account + (*ServerConfig)(nil), // 3: xray.proxy.socks.ServerConfig + (*ClientConfig)(nil), // 4: xray.proxy.socks.ClientConfig + nil, // 5: xray.proxy.socks.ServerConfig.AccountsEntry + (*net.IPOrDomain)(nil), // 6: xray.common.net.IPOrDomain + (*protocol.ServerEndpoint)(nil), // 7: xray.common.protocol.ServerEndpoint } var file_proxy_socks_config_proto_depIdxs = []int32{ 0, // 0: xray.proxy.socks.ServerConfig.auth_type:type_name -> xray.proxy.socks.AuthType - 4, // 1: xray.proxy.socks.ServerConfig.accounts:type_name -> xray.proxy.socks.ServerConfig.AccountsEntry - 5, // 2: xray.proxy.socks.ServerConfig.address:type_name -> xray.common.net.IPOrDomain - 6, // 3: xray.proxy.socks.ClientConfig.server:type_name -> xray.common.protocol.ServerEndpoint - 4, // [4:4] is the sub-list for method output_type - 4, // [4:4] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 5, // 1: xray.proxy.socks.ServerConfig.accounts:type_name -> xray.proxy.socks.ServerConfig.AccountsEntry + 6, // 2: xray.proxy.socks.ServerConfig.address:type_name -> xray.common.net.IPOrDomain + 7, // 3: xray.proxy.socks.ClientConfig.server:type_name -> xray.common.protocol.ServerEndpoint + 1, // 4: xray.proxy.socks.ClientConfig.version:type_name -> xray.proxy.socks.Version + 5, // [5:5] is the sub-list for method output_type + 5, // [5:5] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name } func init() { file_proxy_socks_config_proto_init() } @@ -401,7 +466,7 @@ func file_proxy_socks_config_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proxy_socks_config_proto_rawDesc, - NumEnums: 1, + NumEnums: 2, NumMessages: 4, NumExtensions: 0, NumServices: 0, diff --git a/proxy/socks/config.proto b/proxy/socks/config.proto index 994663bb..7e7dc09f 100644 --- a/proxy/socks/config.proto +++ b/proxy/socks/config.proto @@ -23,6 +23,12 @@ enum AuthType { PASSWORD = 1; } +enum Version { + SOCKS5 = 0; + SOCKS4 = 1; + SOCKS4A = 2; +} + // ServerConfig is the protobuf config for Socks server. message ServerConfig { AuthType auth_type = 1; @@ -37,4 +43,5 @@ message ServerConfig { message ClientConfig { // Sever is a list of Socks server addresses. repeated xray.common.protocol.ServerEndpoint server = 1; + Version version = 2; }