From e764275c58042393da32cf562c17513d18c24ca1 Mon Sep 17 00:00:00 2001 From: V2Ray Date: Wed, 9 Sep 2015 12:13:52 +0200 Subject: [PATCH] socks5 server implementation --- io/socks/socks.go | 83 +++++++++++++++++++------ io/socks/socks_test.go | 20 +++--- net/socks/socks.go | 36 +++++++++++ net/{vmesshandler.go => vmess/vmess.go} | 2 +- vsegment.go | 9 +++ 5 files changed, 120 insertions(+), 30 deletions(-) rename net/{vmesshandler.go => vmess/vmess.go} (93%) create mode 100644 vsegment.go diff --git a/io/socks/socks.go b/io/socks/socks.go index 5137cc87a..6dba6ca6c 100644 --- a/io/socks/socks.go +++ b/io/socks/socks.go @@ -9,6 +9,10 @@ import ( const ( socksVersion = uint8(5) + + AuthNotRequired = byte(0x00) + AuthGssApi = byte(0x01) + AuthUserPass = byte(0x02) ) // Authentication request header of Socks5 protocol @@ -18,6 +22,15 @@ type Socks5AuthenticationRequest struct { authMethods [256]byte } +func (request *Socks5AuthenticationRequest) HasAuthMethod(method byte) bool { + for i := byte(0); i < request.nMethods; i++ { + if request.authMethods[i] == method { + return true + } + } + return false +} + func ReadAuthentication(reader io.Reader) (auth Socks5AuthenticationRequest, err error) { buffer := make([]byte, 2) nBytes, err := reader.Read(buffer) @@ -59,6 +72,13 @@ type Socks5AuthenticationResponse struct { authMethod byte } +func NewAuthenticationResponse(authMethod byte) *Socks5AuthenticationResponse { + response := new(Socks5AuthenticationResponse) + response.version = socksVersion + response.authMethod = authMethod + return response +} + func (r *Socks5AuthenticationResponse) ToBytes() []byte { buffer := make([]byte, 2 /* size of Socks5AuthenticationResponse */) buffer[0] = r.version @@ -66,7 +86,7 @@ func (r *Socks5AuthenticationResponse) ToBytes() []byte { return buffer } -func WriteAuthentication(writer io.Writer, response Socks5AuthenticationResponse) error { +func WriteAuthentication(writer io.Writer, response *Socks5AuthenticationResponse) error { _, err := writer.Write(response.ToBytes()) return err } @@ -75,16 +95,20 @@ const ( AddrTypeIPv4 = byte(0x01) AddrTypeIPv6 = byte(0x04) AddrTypeDomain = byte(0x03) + + CmdConnect = byte(0x01) + CmdBind = byte(0x02) + CmdUdpAssociate = byte(0x03) ) type Socks5Request struct { - version byte - command byte - addrType byte - ipv4 [4]byte - domain string - ipv6 [16]byte - port uint16 + Version byte + Command byte + AddrType byte + IPv4 [4]byte + Domain string + IPv6 [16]byte + Port uint16 } func ReadRequest(reader io.Reader) (request *Socks5Request, err error) { @@ -99,13 +123,13 @@ func ReadRequest(reader io.Reader) (request *Socks5Request, err error) { return } - request.version = buffer[0] - request.command = buffer[1] + request.Version = buffer[0] + request.Command = buffer[1] // buffer[2] is a reserved field - request.addrType = buffer[3] - switch request.addrType { + request.AddrType = buffer[3] + switch request.AddrType { case 0x01: - nBytes, err = reader.Read(request.ipv4[:]) + nBytes, err = reader.Read(request.IPv4[:]) if err != nil { return } @@ -124,9 +148,9 @@ func ReadRequest(reader io.Reader) (request *Socks5Request, err error) { err = fmt.Errorf("Unable to read domain") return } - request.domain = string(buffer[1 : domainLength+1]) + request.Domain = string(buffer[1 : domainLength+1]) case 0x04: - nBytes, err = reader.Read(request.ipv6[:]) + nBytes, err = reader.Read(request.IPv6[:]) if err != nil { return } @@ -135,7 +159,7 @@ func ReadRequest(reader io.Reader) (request *Socks5Request, err error) { return } default: - err = fmt.Errorf("Unexpected address type %d", request.addrType) + err = fmt.Errorf("Unexpected address type %d", request.AddrType) return } @@ -149,7 +173,7 @@ func ReadRequest(reader io.Reader) (request *Socks5Request, err error) { return } - request.port = binary.BigEndian.Uint16(buffer) + request.Port = binary.BigEndian.Uint16(buffer) return } @@ -175,7 +199,28 @@ type Socks5Response struct { Port uint16 } -func (r Socks5Response) toBytes() []byte { +func NewSocks5Response() *Socks5Response { + response := new(Socks5Response) + response.Version = socksVersion + return response +} + +func (r *Socks5Response) SetIPv4(ipv4 []byte) { + r.AddrType = AddrTypeIPv4 + copy(r.IPv4[:], ipv4) +} + +func (r *Socks5Response) SetIPv6(ipv6 []byte) { + r.AddrType = AddrTypeIPv6 + copy(r.IPv6[:], ipv6) +} + +func (r *Socks5Response) SetDomain(domain string) { + r.AddrType = AddrTypeDomain + r.Domain = domain +} + +func (r *Socks5Response) toBytes() []byte { buffer := make([]byte, 0, 300) buffer = append(buffer, r.Version) buffer = append(buffer, r.Error) @@ -196,7 +241,7 @@ func (r Socks5Response) toBytes() []byte { return buffer } -func WriteResponse(writer io.Writer, response Socks5Response) error { +func WriteResponse(writer io.Writer, response *Socks5Response) error { _, err := writer.Write(response.toBytes()) return err } diff --git a/io/socks/socks_test.go b/io/socks/socks_test.go index c6eee2bb5..f1b48c5c5 100644 --- a/io/socks/socks_test.go +++ b/io/socks/socks_test.go @@ -52,20 +52,20 @@ func TestRequestRead(t *testing.T) { if err != nil { t.Errorf("Unexpected error %v", err) } - if request.version != 0x05 { - t.Errorf("Expected version 5, but got %d", request.version) + if request.Version != 0x05 { + t.Errorf("Expected version 5, but got %d", request.Version) } - if request.command != 0x01 { - t.Errorf("Expected command 1, but got %d", request.command) + if request.Command != 0x01 { + t.Errorf("Expected command 1, but got %d", request.Command) } - if request.addrType != 0x01 { - t.Errorf("Expected addresstype 1, but got %d", request.addrType) + if request.AddrType != 0x01 { + t.Errorf("Expected addresstype 1, but got %d", request.AddrType) } - if !bytes.Equal([]byte{0x72, 0x72, 0x72, 0x72}, request.ipv4[:]) { - t.Errorf("Expected IPv4 address 114.114.114.114, but got %v", request.ipv4[:]) + if !bytes.Equal([]byte{0x72, 0x72, 0x72, 0x72}, request.IPv4[:]) { + t.Errorf("Expected IPv4 address 114.114.114.114, but got %v", request.IPv4[:]) } - if request.port != 53 { - t.Errorf("Expected port 53, but got %d", request.port) + if request.Port != 53 { + t.Errorf("Expected port 53, but got %d", request.Port) } } diff --git a/net/socks/socks.go b/net/socks/socks.go index 728d7d1ef..b4560d14a 100644 --- a/net/socks/socks.go +++ b/net/socks/socks.go @@ -1,7 +1,15 @@ package socks import ( + "errors" "net" + + socksio "github.com/v2ray/v2ray-core/io/socks" +) + +var ( + ErrorAuthenticationFailed = errors.New("None of the authentication methods is allowed.") + ErrorCommandNotSupported = errors.New("Client requested an unsupported command.") ) // SocksServer is a SOCKS 5 proxy server @@ -31,5 +39,33 @@ func (server *SocksServer) AcceptConnections(listener net.Listener) error { } func (server *SocksServer) HandleConnection(connection net.Conn) error { + defer connection.Close() + + auth, err := socksio.ReadAuthentication(connection) + if err != nil { + return err + } + + if auth.HasAuthMethod(socksio.AuthNotRequired) { + return ErrorAuthenticationFailed + } + + authResponse := socksio.NewAuthenticationResponse(socksio.AuthNotRequired) + socksio.WriteAuthentication(connection, authResponse) + + request, err := socksio.ReadRequest(connection) + if err != nil { + return err + } + + if request.Command == socksio.CmdBind || request.Command == socksio.CmdUdpAssociate { + response := socksio.NewSocks5Response() + response.Error = socksio.ErrorCommandNotSupported + socksio.WriteResponse(connection, response) + return ErrorCommandNotSupported + } + + // TODO: establish connection with VNext + return nil } diff --git a/net/vmesshandler.go b/net/vmess/vmess.go similarity index 93% rename from net/vmesshandler.go rename to net/vmess/vmess.go index 16b57eace..691e3c835 100644 --- a/net/vmesshandler.go +++ b/net/vmess/vmess.go @@ -1,4 +1,4 @@ -package net +package vemss import ( "net" diff --git a/vsegment.go b/vsegment.go new file mode 100644 index 000000000..9caa478e0 --- /dev/null +++ b/vsegment.go @@ -0,0 +1,9 @@ +package core + +// VSegment is a connection between 2 VPoints +type VSegment struct { +} + +func NewVSegment() *VSegment { + return new(VSegment) +}