From 9ee66f7efeb36e7c4e66db7ed19a7176527e86f5 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/v4/socks.go | 15 +++++ proxy/socks/client.go | 46 +++++++++++++- proxy/socks/config.pb.go | 132 +++++++++++++++++++++++++++++---------- proxy/socks/config.proto | 8 +++ 4 files changed, 166 insertions(+), 35 deletions(-) diff --git a/infra/conf/v4/socks.go b/infra/conf/v4/socks.go index 1998414db..fa8811216 100644 --- a/infra/conf/v4/socks.go +++ b/infra/conf/v4/socks.go @@ -2,6 +2,7 @@ package v4 import ( "encoding/json" + "strings" "github.com/golang/protobuf/proto" @@ -74,11 +75,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(), @@ -93,6 +105,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 a9b1f459c..6eafd612b 100644 --- a/proxy/socks/client.go +++ b/proxy/socks/client.go @@ -13,6 +13,7 @@ import ( "github.com/v2fly/v2ray-core/v4/common/session" "github.com/v2fly/v2ray-core/v4/common/signal" "github.com/v2fly/v2ray-core/v4/common/task" + "github.com/v2fly/v2ray-core/v4/features/dns" "github.com/v2fly/v2ray-core/v4/features/policy" "github.com/v2fly/v2ray-core/v4/transport" "github.com/v2fly/v2ray-core/v4/transport/internet" @@ -22,6 +23,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. @@ -39,10 +42,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. @@ -89,6 +98,39 @@ 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() { + if d, ok := c.dns.(dns.ClientWithIPOption); ok { + d.SetFakeDNSOption(false) // Skip FakeDNS + } else { + newError("DNS client doesn't implement ClientWithIPOption") + } + + lookupFunc := c.dns.LookupIP + if lookupIPv4, ok := c.dns.(dns.IPv4Lookup); ok { + lookupFunc = lookupIPv4.LookupIPv4 + } + ips, err := lookupFunc(request.Address.Domain()) + 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 4237a70ec..0edd0529c 100644 --- a/proxy/socks/config.pb.go +++ b/proxy/socks/config.pb.go @@ -71,6 +71,56 @@ func (AuthType) EnumDescriptor() ([]byte, []int) { return file_proxy_socks_config_proto_rawDescGZIP(), []int{0} } +// AuthType is the outbound server version of Socks proxy. +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 +274,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=v2ray.core.proxy.socks.Version" json:"version,omitempty"` } func (x *ClientConfig) Reset() { @@ -266,6 +317,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{ @@ -303,22 +361,28 @@ var file_proxy_socks_config_proto_rawDesc = []byte{ 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, 0x52, 0x0a, 0x0c, 0x43, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x42, 0x0a, 0x06, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x76, 0x32, 0x72, - 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 0x63, 0x0a, 0x1a, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, - 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x73, 0x6f, - 0x63, 0x6b, 0x73, 0x50, 0x01, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x76, 0x32, 0x66, 0x6c, 0x79, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, - 0x72, 0x65, 0x2f, 0x76, 0x34, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x73, 0x6f, 0x63, 0x6b, - 0x73, 0xaa, 0x02, 0x16, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x50, - 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x8d, 0x01, 0x0a, 0x0c, 0x43, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x42, 0x0a, 0x06, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x76, 0x32, + 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, + 0x39, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x1f, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 0x63, 0x0a, 0x1a, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x50, + 0x01, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x32, + 0x66, 0x6c, 0x79, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, + 0x34, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x73, 0x6f, 0x63, 0x6b, 0x73, 0xaa, 0x02, 0x16, + 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, + 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -333,27 +397,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: v2ray.core.proxy.socks.AuthType - (*Account)(nil), // 1: v2ray.core.proxy.socks.Account - (*ServerConfig)(nil), // 2: v2ray.core.proxy.socks.ServerConfig - (*ClientConfig)(nil), // 3: v2ray.core.proxy.socks.ClientConfig - nil, // 4: v2ray.core.proxy.socks.ServerConfig.AccountsEntry - (*net.IPOrDomain)(nil), // 5: v2ray.core.common.net.IPOrDomain - (*protocol.ServerEndpoint)(nil), // 6: v2ray.core.common.protocol.ServerEndpoint + (Version)(0), // 1: v2ray.core.proxy.socks.Version + (*Account)(nil), // 2: v2ray.core.proxy.socks.Account + (*ServerConfig)(nil), // 3: v2ray.core.proxy.socks.ServerConfig + (*ClientConfig)(nil), // 4: v2ray.core.proxy.socks.ClientConfig + nil, // 5: v2ray.core.proxy.socks.ServerConfig.AccountsEntry + (*net.IPOrDomain)(nil), // 6: v2ray.core.common.net.IPOrDomain + (*protocol.ServerEndpoint)(nil), // 7: v2ray.core.common.protocol.ServerEndpoint } var file_proxy_socks_config_proto_depIdxs = []int32{ 0, // 0: v2ray.core.proxy.socks.ServerConfig.auth_type:type_name -> v2ray.core.proxy.socks.AuthType - 4, // 1: v2ray.core.proxy.socks.ServerConfig.accounts:type_name -> v2ray.core.proxy.socks.ServerConfig.AccountsEntry - 5, // 2: v2ray.core.proxy.socks.ServerConfig.address:type_name -> v2ray.core.common.net.IPOrDomain - 6, // 3: v2ray.core.proxy.socks.ClientConfig.server:type_name -> v2ray.core.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: v2ray.core.proxy.socks.ServerConfig.accounts:type_name -> v2ray.core.proxy.socks.ServerConfig.AccountsEntry + 6, // 2: v2ray.core.proxy.socks.ServerConfig.address:type_name -> v2ray.core.common.net.IPOrDomain + 7, // 3: v2ray.core.proxy.socks.ClientConfig.server:type_name -> v2ray.core.common.protocol.ServerEndpoint + 1, // 4: v2ray.core.proxy.socks.ClientConfig.version:type_name -> v2ray.core.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() } @@ -404,7 +470,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 d9f1d5b1c..050a95401 100644 --- a/proxy/socks/config.proto +++ b/proxy/socks/config.proto @@ -23,6 +23,13 @@ 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 +44,5 @@ message ServerConfig { message ClientConfig { // Sever is a list of Socks server addresses. repeated v2ray.core.common.protocol.ServerEndpoint server = 1; + Version version = 2; }