Add SOCKS4 support

pull/27/head
V2Ray 9 years ago
parent 2b7fdef203
commit 8e8dbbcfd0

@ -3,19 +3,29 @@ package socks
import (
"encoding/binary"
"errors"
"fmt"
"io"
"github.com/v2ray/v2ray-core/log"
v2net "github.com/v2ray/v2ray-core/net"
)
const (
socksVersion = uint8(5)
socksVersion = byte(0x05)
socks4Version = byte(0x04)
AuthNotRequired = byte(0x00)
AuthGssApi = byte(0x01)
AuthUserPass = byte(0x02)
AuthNoMatchingMethod = byte(0xFF)
Socks4RequestGranted = byte(90)
Socks4RequestRejected = byte(91)
)
var (
ErrorSocksVersion4 = errors.New("Using SOCKS version 4.")
)
// Authentication request header of Socks5 protocol
@ -25,6 +35,13 @@ type Socks5AuthenticationRequest struct {
authMethods [256]byte
}
type Socks4AuthenticationRequest struct {
Version byte
Command byte
Port uint16
IP [4]byte
}
func (request *Socks5AuthenticationRequest) HasAuthMethod(method byte) bool {
for i := 0; i < int(request.nMethods); i++ {
if request.authMethods[i] == method {
@ -34,10 +51,11 @@ func (request *Socks5AuthenticationRequest) HasAuthMethod(method byte) bool {
return false
}
func ReadAuthentication(reader io.Reader) (auth Socks5AuthenticationRequest, err error) {
func ReadAuthentication(reader io.Reader) (auth Socks5AuthenticationRequest, auth4 Socks4AuthenticationRequest, err error) {
buffer := make([]byte, 256)
nBytes, err := reader.Read(buffer)
if err != nil {
log.Error("Failed to read socks authentication: %v", err)
return
}
if nBytes < 2 {
@ -45,6 +63,15 @@ func ReadAuthentication(reader io.Reader) (auth Socks5AuthenticationRequest, err
return
}
if buffer[0] == socks4Version {
auth4.Version = buffer[0]
auth4.Command = buffer[1]
auth4.Port = binary.BigEndian.Uint16(buffer[2:4])
copy(auth4.IP[:], buffer[4:8])
err = ErrorSocksVersion4
return
}
auth.version = buffer[0]
if auth.version != socksVersion {
err = fmt.Errorf("Unknown SOCKS version %d", auth.version)
@ -70,6 +97,12 @@ type Socks5AuthenticationResponse struct {
authMethod byte
}
type Socks4AuthenticationResponse struct {
result byte
port uint16
ip []byte
}
func NewAuthenticationResponse(authMethod byte) *Socks5AuthenticationResponse {
return &Socks5AuthenticationResponse{
version: socksVersion,
@ -77,11 +110,29 @@ func NewAuthenticationResponse(authMethod byte) *Socks5AuthenticationResponse {
}
}
func NewSocks4AuthenticationResponse(result byte, port uint16, ip []byte) *Socks4AuthenticationResponse {
return &Socks4AuthenticationResponse{
result: result,
port: port,
ip: ip,
}
}
func WriteAuthentication(writer io.Writer, r *Socks5AuthenticationResponse) error {
_, err := writer.Write([]byte{r.version, r.authMethod})
return err
}
func WriteSocks4AuthenticationResponse(writer io.Writer, r *Socks4AuthenticationResponse) error {
buffer := make([]byte, 8)
// buffer[0] is always 0
buffer[1] = r.result
binary.BigEndian.PutUint16(buffer[2:4], r.port)
copy(buffer[4:], r.ip)
_, err := writer.Write(buffer)
return err
}
type Socks5UserPassRequest struct {
version byte
username string

@ -30,13 +30,30 @@ func TestAuthenticationRequestRead(t *testing.T) {
0x01, // nMethods
0x02, // methods
}
request, err := ReadAuthentication(bytes.NewReader(rawRequest))
request, _, err := ReadAuthentication(bytes.NewReader(rawRequest))
assert.Error(err).IsNil()
assert.Byte(request.version).Named("Version").Equals(0x05)
assert.Byte(request.nMethods).Named("#Methods").Equals(0x01)
assert.Byte(request.authMethods[0]).Named("Auth Method").Equals(0x02)
}
func TestAuthentication4RequestRead(t *testing.T) {
assert := unit.Assert(t)
rawRequest := []byte{
0x04, // version
0x01, // command
0x00, 0x35,
0x72, 0x72, 0x72, 0x72,
}
_, request4, err := ReadAuthentication(bytes.NewReader(rawRequest))
assert.Error(err).Equals(ErrorSocksVersion4)
assert.Byte(request4.Version).Named("Version").Equals(0x04)
assert.Byte(request4.Command).Named("Command").Equals(0x01)
assert.Uint16(request4.Port).Named("Port").Equals(53)
assert.Bytes(request4.IP[:]).Named("IP").Equals([]byte{0x72, 0x72, 0x72, 0x72})
}
func TestAuthenticationResponseWrite(t *testing.T) {
assert := unit.Assert(t)

@ -18,10 +18,12 @@ type Address struct {
}
func IPAddress(ip []byte, port uint16) Address {
ipCopy := make([]byte, 4)
copy(ipCopy, ip)
// TODO: check IP length
return Address{
Type: AddrTypeIP,
IP: net.IP(ip),
IP: net.IP(ipCopy),
Domain: "",
Port: port,
}

@ -23,7 +23,7 @@ func (vconn *FreedomConnection) Start(ray core.OutboundRay) error {
output := ray.OutboundOutput()
conn, err := net.Dial("tcp", vconn.dest.String())
if err != nil {
return log.Error("Failed to open tcp: %s", vconn.dest.String())
return log.Error("Failed to open tcp: %s : %v", vconn.dest.String(), err)
}
log.Debug("Sending outbound tcp: %s", vconn.dest.String())

@ -64,12 +64,29 @@ func (server *SocksServer) HandleConnection(connection net.Conn) error {
reader := bufio.NewReader(connection)
auth, err := socksio.ReadAuthentication(reader)
if err != nil {
auth, auth4, err := socksio.ReadAuthentication(reader)
if err != nil && err != socksio.ErrorSocksVersion4 {
log.Error("Error on reading authentication: %v", err)
return err
}
var dest v2net.Address
// TODO refactor this part
if err == socksio.ErrorSocksVersion4 {
result := socksio.Socks4RequestGranted
if auth4.Command == socksio.CmdBind {
result = socksio.Socks4RequestRejected
}
socks4Response := socksio.NewSocks4AuthenticationResponse(result, auth4.Port, auth4.IP[:])
socksio.WriteSocks4AuthenticationResponse(connection, socks4Response)
if result == socksio.Socks4RequestRejected {
return ErrorCommandNotSupported
}
dest = v2net.IPAddress(auth4.IP[:], auth4.Port)
} else {
expectedAuthMethod := socksio.AuthNotRequired
if server.config.AuthMethod == JsonAuthMethodUserPass {
expectedAuthMethod = socksio.AuthUserPass
@ -149,7 +166,11 @@ func (server *SocksServer) HandleConnection(connection net.Conn) error {
log.Error("Error on socksio write response: %v", err)
return err
}
ray := server.vPoint.NewInboundConnectionAccepted(request.Destination())
dest = request.Destination()
}
ray := server.vPoint.NewInboundConnectionAccepted(dest)
input := ray.InboundInput()
output := ray.InboundOutput()
readFinish := make(chan bool)

Loading…
Cancel
Save