Skip to content

Commit

Permalink
Add socks4/4a support
Browse files Browse the repository at this point in the history
  • Loading branch information
nekohasekai authored and xiaokangwang committed Sep 17, 2021
1 parent 8952eea commit 97ef239
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 35 deletions.
15 changes: 15 additions & 0 deletions infra/conf/socks.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package conf

import (
"encoding/json"
"strings"

"github.com/golang/protobuf/proto"

Expand Down Expand Up @@ -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(),
Expand All @@ -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)
}
Expand Down
46 changes: 44 additions & 2 deletions proxy/socks/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,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"
Expand All @@ -25,6 +26,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.
Expand All @@ -42,10 +45,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.
Expand Down Expand Up @@ -92,6 +101,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
}
Expand Down
132 changes: 99 additions & 33 deletions proxy/socks/config.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions proxy/socks/config.proto
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}

0 comments on commit 97ef239

Please sign in to comment.