mirror of https://github.com/v2ray/v2ray-core
refine socks udp handling
parent
a150ceb3ea
commit
b00ee67369
|
@ -272,3 +272,72 @@ func writeSocks4Response(writer io.Writer, errCode byte, address v2net.Address,
|
||||||
_, err := writer.Write(buffer.Bytes())
|
_, err := writer.Write(buffer.Bytes())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DecodeUDPPacket(packet []byte) (*protocol.RequestHeader, []byte, error) {
|
||||||
|
if len(packet) < 5 {
|
||||||
|
return nil, nil, errors.New("Socks|UDP: Insufficient length of packet.")
|
||||||
|
}
|
||||||
|
request := &protocol.RequestHeader{
|
||||||
|
Version: socks5Version,
|
||||||
|
Command: protocol.RequestCommandUDP,
|
||||||
|
}
|
||||||
|
|
||||||
|
// packet[0] and packet[1] are reserved
|
||||||
|
if packet[2] != 0 /* fragments */ {
|
||||||
|
return nil, nil, errors.New("Socks|UDP: Fragmented payload.")
|
||||||
|
}
|
||||||
|
|
||||||
|
addrType := packet[3]
|
||||||
|
var dataBegin int
|
||||||
|
|
||||||
|
switch addrType {
|
||||||
|
case addrTypeIPv4:
|
||||||
|
if len(packet) < 10 {
|
||||||
|
return nil, nil, errors.New("Socks|UDP: Insufficient length of packet.")
|
||||||
|
}
|
||||||
|
ip := packet[4:8]
|
||||||
|
request.Port = v2net.PortFromBytes(packet[8:10])
|
||||||
|
request.Address = v2net.IPAddress(ip)
|
||||||
|
dataBegin = 10
|
||||||
|
case addrTypeIPv6:
|
||||||
|
if len(packet) < 22 {
|
||||||
|
return nil, nil, errors.New("Socks|UDP: Insufficient length of packet.")
|
||||||
|
}
|
||||||
|
ip := packet[4:20]
|
||||||
|
request.Port = v2net.PortFromBytes(packet[20:22])
|
||||||
|
request.Address = v2net.IPAddress(ip)
|
||||||
|
dataBegin = 22
|
||||||
|
case addrTypeDomain:
|
||||||
|
domainLength := int(packet[4])
|
||||||
|
if len(packet) < 5+domainLength+2 {
|
||||||
|
return nil, nil, errors.New("Socks|UDP: Insufficient length of packet.")
|
||||||
|
}
|
||||||
|
domain := string(packet[5 : 5+domainLength])
|
||||||
|
request.Port = v2net.PortFromBytes(packet[5+domainLength : 5+domainLength+2])
|
||||||
|
request.Address = v2net.ParseAddress(domain)
|
||||||
|
dataBegin = 5 + domainLength + 2
|
||||||
|
default:
|
||||||
|
return nil, nil, errors.New("Socks|UDP: Unknown address type ", addrType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return request, packet[dataBegin:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func EncodeUDPPacket(request *protocol.RequestHeader, data []byte) *buf.Buffer {
|
||||||
|
b := buf.NewSmall()
|
||||||
|
b.AppendBytes(0, 0, 0 /* Fragment */)
|
||||||
|
switch request.Address.Family() {
|
||||||
|
case v2net.AddressFamilyIPv4:
|
||||||
|
b.AppendBytes(addrTypeIPv4)
|
||||||
|
b.Append(request.Address.IP())
|
||||||
|
case v2net.AddressFamilyIPv6:
|
||||||
|
b.AppendBytes(addrTypeIPv6)
|
||||||
|
b.Append(request.Address.IP())
|
||||||
|
case v2net.AddressFamilyDomain:
|
||||||
|
b.AppendBytes(addrTypeDomain, byte(len(request.Address.Domain())))
|
||||||
|
b.AppendSupplier(serial.WriteString(request.Address.Domain()))
|
||||||
|
}
|
||||||
|
b.AppendSupplier(serial.WriteUint16(request.Port.Value()))
|
||||||
|
b.Append(data)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
|
@ -1,311 +0,0 @@
|
||||||
package protocol
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"v2ray.com/core/common/buf"
|
|
||||||
"v2ray.com/core/common/crypto"
|
|
||||||
"v2ray.com/core/common/errors"
|
|
||||||
"v2ray.com/core/common/log"
|
|
||||||
v2net "v2ray.com/core/common/net"
|
|
||||||
"v2ray.com/core/proxy"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
socksVersion = byte(0x05)
|
|
||||||
socks4Version = byte(0x04)
|
|
||||||
|
|
||||||
AuthNotRequired = byte(0x00)
|
|
||||||
AuthGssApi = byte(0x01)
|
|
||||||
AuthUserPass = byte(0x02)
|
|
||||||
AuthNoMatchingMethod = byte(0xFF)
|
|
||||||
|
|
||||||
Socks4RequestGranted = byte(90)
|
|
||||||
Socks4RequestRejected = byte(91)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Authentication request header of Socks5 protocol
|
|
||||||
type Socks5AuthenticationRequest struct {
|
|
||||||
version byte
|
|
||||||
nMethods byte
|
|
||||||
authMethods [256]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (request *Socks5AuthenticationRequest) HasAuthMethod(method byte) bool {
|
|
||||||
for i := 0; i < int(request.nMethods); i++ {
|
|
||||||
if request.authMethods[i] == method {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadAuthentication(reader io.Reader) (auth Socks5AuthenticationRequest, auth4 Socks4AuthenticationRequest, err error) {
|
|
||||||
buffer := make([]byte, 256)
|
|
||||||
|
|
||||||
nBytes, err := reader.Read(buffer)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if nBytes < 2 {
|
|
||||||
err = errors.New("Socks: Insufficient header.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if buffer[0] == socks4Version {
|
|
||||||
auth4.Version = buffer[0]
|
|
||||||
auth4.Command = buffer[1]
|
|
||||||
auth4.Port = v2net.PortFromBytes(buffer[2:4])
|
|
||||||
copy(auth4.IP[:], buffer[4:8])
|
|
||||||
err = Socks4Downgrade
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
auth.version = buffer[0]
|
|
||||||
if auth.version != socksVersion {
|
|
||||||
log.Warning("Socks: Unknown protocol version ", auth.version)
|
|
||||||
err = proxy.ErrInvalidProtocolVersion
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
auth.nMethods = buffer[1]
|
|
||||||
if auth.nMethods <= 0 {
|
|
||||||
log.Warning("Socks: Zero length of authentication methods")
|
|
||||||
err = crypto.ErrAuthenticationFailed
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if nBytes-2 != int(auth.nMethods) {
|
|
||||||
log.Warning("Socks: Unmatching number of auth methods, expecting ", auth.nMethods, ", but got ", nBytes)
|
|
||||||
err = crypto.ErrAuthenticationFailed
|
|
||||||
return
|
|
||||||
}
|
|
||||||
copy(auth.authMethods[:], buffer[2:nBytes])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type Socks5AuthenticationResponse struct {
|
|
||||||
version byte
|
|
||||||
authMethod byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewAuthenticationResponse(authMethod byte) *Socks5AuthenticationResponse {
|
|
||||||
return &Socks5AuthenticationResponse{
|
|
||||||
version: socksVersion,
|
|
||||||
authMethod: authMethod,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteAuthentication(writer io.Writer, r *Socks5AuthenticationResponse) error {
|
|
||||||
_, err := writer.Write([]byte{r.version, r.authMethod})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
type Socks5UserPassRequest struct {
|
|
||||||
version byte
|
|
||||||
username string
|
|
||||||
password string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (request Socks5UserPassRequest) Username() string {
|
|
||||||
return request.username
|
|
||||||
}
|
|
||||||
|
|
||||||
func (request Socks5UserPassRequest) Password() string {
|
|
||||||
return request.password
|
|
||||||
}
|
|
||||||
|
|
||||||
func (request Socks5UserPassRequest) AuthDetail() string {
|
|
||||||
return request.username + ":" + request.password
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadUserPassRequest(reader io.Reader) (request Socks5UserPassRequest, err error) {
|
|
||||||
buffer := buf.NewLocal(512)
|
|
||||||
defer buffer.Release()
|
|
||||||
|
|
||||||
err = buffer.AppendSupplier(buf.ReadFullFrom(reader, 2))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
request.version = buffer.Byte(0)
|
|
||||||
nUsername := int(buffer.Byte(1))
|
|
||||||
|
|
||||||
buffer.Clear()
|
|
||||||
err = buffer.AppendSupplier(buf.ReadFullFrom(reader, nUsername))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
request.username = buffer.String()
|
|
||||||
|
|
||||||
err = buffer.AppendSupplier(buf.ReadFullFrom(reader, 1))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
nPassword := int(buffer.Byte(0))
|
|
||||||
err = buffer.AppendSupplier(buf.ReadFullFrom(reader, nPassword))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
request.password = buffer.String()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type Socks5UserPassResponse struct {
|
|
||||||
version byte
|
|
||||||
status byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSocks5UserPassResponse(status byte) Socks5UserPassResponse {
|
|
||||||
return Socks5UserPassResponse{
|
|
||||||
version: socksVersion,
|
|
||||||
status: status,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteUserPassResponse(writer io.Writer, response Socks5UserPassResponse) error {
|
|
||||||
_, err := writer.Write([]byte{response.version, response.status})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
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 v2net.Port
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadRequest(reader io.Reader) (request *Socks5Request, err error) {
|
|
||||||
buffer := buf.NewLocal(512)
|
|
||||||
defer buffer.Release()
|
|
||||||
|
|
||||||
err = buffer.AppendSupplier(buf.ReadFullFrom(reader, 4))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
request = &Socks5Request{
|
|
||||||
Version: buffer.Byte(0),
|
|
||||||
Command: buffer.Byte(1),
|
|
||||||
// buffer[2] is a reserved field
|
|
||||||
AddrType: buffer.Byte(3),
|
|
||||||
}
|
|
||||||
switch request.AddrType {
|
|
||||||
case AddrTypeIPv4:
|
|
||||||
_, err = io.ReadFull(reader, request.IPv4[:])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case AddrTypeDomain:
|
|
||||||
buffer.Clear()
|
|
||||||
err = buffer.AppendSupplier(buf.ReadFullFrom(reader, 1))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
domainLength := int(buffer.Byte(0))
|
|
||||||
err = buffer.AppendSupplier(buf.ReadFullFrom(reader, domainLength))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
request.Domain = string(buffer.BytesFrom(-domainLength))
|
|
||||||
case AddrTypeIPv6:
|
|
||||||
_, err = io.ReadFull(reader, request.IPv6[:])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
err = errors.Format("Socks: Unexpected address type %d", request.AddrType)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = buffer.AppendSupplier(buf.ReadFullFrom(reader, 2))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
request.Port = v2net.PortFromBytes(buffer.BytesFrom(-2))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (request *Socks5Request) Destination() v2net.Destination {
|
|
||||||
switch request.AddrType {
|
|
||||||
case AddrTypeIPv4:
|
|
||||||
return v2net.TCPDestination(v2net.IPAddress(request.IPv4[:]), request.Port)
|
|
||||||
case AddrTypeIPv6:
|
|
||||||
return v2net.TCPDestination(v2net.IPAddress(request.IPv6[:]), request.Port)
|
|
||||||
case AddrTypeDomain:
|
|
||||||
return v2net.TCPDestination(v2net.ParseAddress(request.Domain), request.Port)
|
|
||||||
default:
|
|
||||||
panic("Unknown address type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
ErrorSuccess = byte(0x00)
|
|
||||||
ErrorGeneralFailure = byte(0x01)
|
|
||||||
ErrorConnectionNotAllowed = byte(0x02)
|
|
||||||
ErrorNetworkUnreachable = byte(0x03)
|
|
||||||
ErrorHostUnUnreachable = byte(0x04)
|
|
||||||
ErrorConnectionRefused = byte(0x05)
|
|
||||||
ErrorTTLExpired = byte(0x06)
|
|
||||||
ErrorCommandNotSupported = byte(0x07)
|
|
||||||
ErrorAddressTypeNotSupported = byte(0x08)
|
|
||||||
)
|
|
||||||
|
|
||||||
type Socks5Response struct {
|
|
||||||
Version byte
|
|
||||||
Error byte
|
|
||||||
AddrType byte
|
|
||||||
IPv4 [4]byte
|
|
||||||
Domain string
|
|
||||||
IPv6 [16]byte
|
|
||||||
Port v2net.Port
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSocks5Response() *Socks5Response {
|
|
||||||
return &Socks5Response{
|
|
||||||
Version: socksVersion,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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) Write(writer io.Writer) {
|
|
||||||
writer.Write([]byte{r.Version, r.Error, 0x00 /* reserved */, r.AddrType})
|
|
||||||
switch r.AddrType {
|
|
||||||
case 0x01:
|
|
||||||
writer.Write(r.IPv4[:])
|
|
||||||
case 0x03:
|
|
||||||
writer.Write([]byte{byte(len(r.Domain))})
|
|
||||||
writer.Write([]byte(r.Domain))
|
|
||||||
case 0x04:
|
|
||||||
writer.Write(r.IPv6[:])
|
|
||||||
}
|
|
||||||
writer.Write(r.Port.Bytes(nil))
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
package protocol
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"v2ray.com/core/common/errors"
|
|
||||||
v2net "v2ray.com/core/common/net"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
Socks4Downgrade = errors.New("Downgraded to Socks 4.")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Socks4AuthenticationRequest struct {
|
|
||||||
Version byte
|
|
||||||
Command byte
|
|
||||||
Port v2net.Port
|
|
||||||
IP [4]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
type Socks4AuthenticationResponse struct {
|
|
||||||
result byte
|
|
||||||
port uint16
|
|
||||||
ip []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSocks4AuthenticationResponse(result byte, port v2net.Port, ip []byte) *Socks4AuthenticationResponse {
|
|
||||||
return &Socks4AuthenticationResponse{
|
|
||||||
result: result,
|
|
||||||
port: port.Value(),
|
|
||||||
ip: ip,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Socks4AuthenticationResponse) Write(writer io.Writer) {
|
|
||||||
writer.Write([]byte{
|
|
||||||
byte(0x00), r.result, byte(r.port >> 8), byte(r.port),
|
|
||||||
r.ip[0], r.ip[1], r.ip[2], r.ip[3]})
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
package protocol
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"v2ray.com/core/common/buf"
|
|
||||||
v2net "v2ray.com/core/common/net"
|
|
||||||
"v2ray.com/core/testing/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSocks4AuthenticationRequestRead(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
rawRequest := []byte{
|
|
||||||
0x04, // version
|
|
||||||
0x01, // command
|
|
||||||
0x00, 0x35,
|
|
||||||
0x72, 0x72, 0x72, 0x72,
|
|
||||||
}
|
|
||||||
_, request4, err := ReadAuthentication(bytes.NewReader(rawRequest))
|
|
||||||
assert.Error(err).Equals(Socks4Downgrade)
|
|
||||||
assert.Byte(request4.Version).Equals(0x04)
|
|
||||||
assert.Byte(request4.Command).Equals(0x01)
|
|
||||||
assert.Port(request4.Port).Equals(v2net.Port(53))
|
|
||||||
assert.Bytes(request4.IP[:]).Equals([]byte{0x72, 0x72, 0x72, 0x72})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSocks4AuthenticationResponseToBytes(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
response := NewSocks4AuthenticationResponse(byte(0x10), 443, []byte{1, 2, 3, 4})
|
|
||||||
|
|
||||||
buffer := buf.NewLocal(2048)
|
|
||||||
defer buffer.Release()
|
|
||||||
|
|
||||||
response.Write(buffer)
|
|
||||||
assert.Bytes(buffer.Bytes()).Equals([]byte{0x00, 0x10, 0x01, 0xBB, 0x01, 0x02, 0x03, 0x04})
|
|
||||||
}
|
|
|
@ -1,177 +0,0 @@
|
||||||
package protocol
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"v2ray.com/core/common/buf"
|
|
||||||
"v2ray.com/core/common/crypto"
|
|
||||||
v2net "v2ray.com/core/common/net"
|
|
||||||
"v2ray.com/core/proxy"
|
|
||||||
"v2ray.com/core/testing/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestHasAuthenticationMethod(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
request := Socks5AuthenticationRequest{
|
|
||||||
version: socksVersion,
|
|
||||||
nMethods: byte(0x02),
|
|
||||||
authMethods: [256]byte{0x01, 0x02},
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Bool(request.HasAuthMethod(byte(0x01))).IsTrue()
|
|
||||||
|
|
||||||
request.authMethods[0] = byte(0x03)
|
|
||||||
assert.Bool(request.HasAuthMethod(byte(0x01))).IsFalse()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAuthenticationRequestRead(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
buffer := buf.New()
|
|
||||||
buffer.AppendBytes(
|
|
||||||
0x05, // version
|
|
||||||
0x01, // nMethods
|
|
||||||
0x02, // methods
|
|
||||||
)
|
|
||||||
request, _, err := ReadAuthentication(buffer)
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
assert.Byte(request.version).Equals(0x05)
|
|
||||||
assert.Byte(request.nMethods).Equals(0x01)
|
|
||||||
assert.Byte(request.authMethods[0]).Equals(0x02)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAuthenticationResponseWrite(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
response := NewAuthenticationResponse(byte(0x05))
|
|
||||||
|
|
||||||
buffer := bytes.NewBuffer(make([]byte, 0, 10))
|
|
||||||
WriteAuthentication(buffer, response)
|
|
||||||
assert.Bytes(buffer.Bytes()).Equals([]byte{socksVersion, byte(0x05)})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRequestRead(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
rawRequest := []byte{
|
|
||||||
0x05, // version
|
|
||||||
0x01, // cmd connect
|
|
||||||
0x00, // reserved
|
|
||||||
0x01, // ipv4 type
|
|
||||||
0x72, 0x72, 0x72, 0x72, // 114.114.114.114
|
|
||||||
0x00, 0x35, // port 53
|
|
||||||
}
|
|
||||||
request, err := ReadRequest(bytes.NewReader(rawRequest))
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
assert.Byte(request.Version).Equals(0x05)
|
|
||||||
assert.Byte(request.Command).Equals(0x01)
|
|
||||||
assert.Byte(request.AddrType).Equals(0x01)
|
|
||||||
assert.Bytes(request.IPv4[:]).Equals([]byte{0x72, 0x72, 0x72, 0x72})
|
|
||||||
assert.Port(request.Port).Equals(v2net.Port(53))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResponseWrite(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
response := Socks5Response{
|
|
||||||
socksVersion,
|
|
||||||
ErrorSuccess,
|
|
||||||
AddrTypeIPv4,
|
|
||||||
[4]byte{0x72, 0x72, 0x72, 0x72},
|
|
||||||
"",
|
|
||||||
[16]byte{},
|
|
||||||
v2net.Port(53),
|
|
||||||
}
|
|
||||||
buffer := buf.NewLocal(2048)
|
|
||||||
defer buffer.Release()
|
|
||||||
|
|
||||||
response.Write(buffer)
|
|
||||||
expectedBytes := []byte{
|
|
||||||
socksVersion,
|
|
||||||
ErrorSuccess,
|
|
||||||
byte(0x00),
|
|
||||||
AddrTypeIPv4,
|
|
||||||
0x72, 0x72, 0x72, 0x72,
|
|
||||||
byte(0x00), byte(0x035),
|
|
||||||
}
|
|
||||||
assert.Bytes(buffer.Bytes()).Equals(expectedBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetIPv6(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
response := NewSocks5Response()
|
|
||||||
response.SetIPv6([]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15})
|
|
||||||
|
|
||||||
buffer := buf.NewLocal(2048)
|
|
||||||
defer buffer.Release()
|
|
||||||
response.Write(buffer)
|
|
||||||
assert.Bytes(buffer.Bytes()).Equals([]byte{
|
|
||||||
socksVersion, 0, 0, AddrTypeIPv6, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetDomain(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
response := NewSocks5Response()
|
|
||||||
response.SetDomain("v2ray.com")
|
|
||||||
|
|
||||||
buffer := buf.NewLocal(2048)
|
|
||||||
defer buffer.Release()
|
|
||||||
response.Write(buffer)
|
|
||||||
assert.Bytes(buffer.Bytes()).Equals([]byte{
|
|
||||||
socksVersion, 0, 0, AddrTypeDomain, 9, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0, 0})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmptyAuthRequest(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
_, _, err := ReadAuthentication(buf.New())
|
|
||||||
assert.Error(err).Equals(io.EOF)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSingleByteAuthRequest(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
_, _, err := ReadAuthentication(bytes.NewReader(make([]byte, 1)))
|
|
||||||
assert.Error(err).IsNotNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestZeroAuthenticationMethod(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
buffer := buf.New()
|
|
||||||
buffer.AppendBytes(5, 0)
|
|
||||||
_, _, err := ReadAuthentication(buffer)
|
|
||||||
assert.Error(err).Equals(crypto.ErrAuthenticationFailed)
|
|
||||||
}
|
|
||||||
func TestWrongProtocolVersion(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
buffer := buf.New()
|
|
||||||
buffer.AppendBytes(6, 1, 0)
|
|
||||||
_, _, err := ReadAuthentication(buffer)
|
|
||||||
assert.Error(err).Equals(proxy.ErrInvalidProtocolVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmptyRequest(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
_, err := ReadRequest(buf.New())
|
|
||||||
assert.Error(err).Equals(io.EOF)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIPv6Request(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
b := buf.New()
|
|
||||||
b.AppendBytes(5, 1, 0, 4, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 0, 8)
|
|
||||||
request, err := ReadRequest(b)
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
assert.Byte(request.Command).Equals(1)
|
|
||||||
assert.Bytes(request.IPv6[:]).Equals([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6})
|
|
||||||
assert.Port(request.Port).Equals(8)
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
package protocol
|
|
||||||
|
|
||||||
import (
|
|
||||||
"v2ray.com/core/common/buf"
|
|
||||||
"v2ray.com/core/common/errors"
|
|
||||||
v2net "v2ray.com/core/common/net"
|
|
||||||
"v2ray.com/core/common/serial"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrorUnknownAddressType = errors.New("Unknown Address Type.")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Socks5UDPRequest struct {
|
|
||||||
Fragment byte
|
|
||||||
Address v2net.Address
|
|
||||||
Port v2net.Port
|
|
||||||
Data *buf.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (request *Socks5UDPRequest) Destination() v2net.Destination {
|
|
||||||
return v2net.UDPDestination(request.Address, request.Port)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (request *Socks5UDPRequest) Write(buffer *buf.Buffer) {
|
|
||||||
buffer.AppendBytes(0, 0, request.Fragment)
|
|
||||||
switch request.Address.Family() {
|
|
||||||
case v2net.AddressFamilyIPv4:
|
|
||||||
buffer.AppendBytes(AddrTypeIPv4)
|
|
||||||
buffer.Append(request.Address.IP())
|
|
||||||
case v2net.AddressFamilyIPv6:
|
|
||||||
buffer.AppendBytes(AddrTypeIPv6)
|
|
||||||
buffer.Append(request.Address.IP())
|
|
||||||
case v2net.AddressFamilyDomain:
|
|
||||||
buffer.AppendBytes(AddrTypeDomain, byte(len(request.Address.Domain())))
|
|
||||||
buffer.Append([]byte(request.Address.Domain()))
|
|
||||||
}
|
|
||||||
buffer.AppendSupplier(serial.WriteUint16(request.Port.Value()))
|
|
||||||
buffer.Append(request.Data.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadUDPRequest(packet []byte) (*Socks5UDPRequest, error) {
|
|
||||||
if len(packet) < 5 {
|
|
||||||
return nil, errors.New("Socks|UDP: Insufficient length of packet.")
|
|
||||||
}
|
|
||||||
request := new(Socks5UDPRequest)
|
|
||||||
|
|
||||||
// packet[0] and packet[1] are reserved
|
|
||||||
request.Fragment = packet[2]
|
|
||||||
|
|
||||||
addrType := packet[3]
|
|
||||||
var dataBegin int
|
|
||||||
|
|
||||||
switch addrType {
|
|
||||||
case AddrTypeIPv4:
|
|
||||||
if len(packet) < 10 {
|
|
||||||
return nil, errors.New("Socks|UDP: Insufficient length of packet.")
|
|
||||||
}
|
|
||||||
ip := packet[4:8]
|
|
||||||
request.Port = v2net.PortFromBytes(packet[8:10])
|
|
||||||
request.Address = v2net.IPAddress(ip)
|
|
||||||
dataBegin = 10
|
|
||||||
case AddrTypeIPv6:
|
|
||||||
if len(packet) < 22 {
|
|
||||||
return nil, errors.New("Socks|UDP: Insufficient length of packet.")
|
|
||||||
}
|
|
||||||
ip := packet[4:20]
|
|
||||||
request.Port = v2net.PortFromBytes(packet[20:22])
|
|
||||||
request.Address = v2net.IPAddress(ip)
|
|
||||||
dataBegin = 22
|
|
||||||
case AddrTypeDomain:
|
|
||||||
domainLength := int(packet[4])
|
|
||||||
if len(packet) < 5+domainLength+2 {
|
|
||||||
return nil, errors.New("Socks|UDP: Insufficient length of packet.")
|
|
||||||
}
|
|
||||||
domain := string(packet[5 : 5+domainLength])
|
|
||||||
request.Port = v2net.PortFromBytes(packet[5+domainLength : 5+domainLength+2])
|
|
||||||
request.Address = v2net.ParseAddress(domain)
|
|
||||||
dataBegin = 5 + domainLength + 2
|
|
||||||
default:
|
|
||||||
return nil, errors.Format("Socks|UDP: Unknown address type %d", addrType)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(packet) > dataBegin {
|
|
||||||
b := buf.NewSmall()
|
|
||||||
b.Append(packet[dataBegin:])
|
|
||||||
request.Data = b
|
|
||||||
}
|
|
||||||
|
|
||||||
return request, nil
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
package protocol
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
v2net "v2ray.com/core/common/net"
|
|
||||||
"v2ray.com/core/testing/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSingleByteUDPRequest(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
request, err := ReadUDPRequest(make([]byte, 1))
|
|
||||||
if request != nil {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
assert.Error(err).IsNotNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDomainAddressRequest(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
payload := make([]byte, 0, 1024)
|
|
||||||
payload = append(payload, 0, 0, 1, AddrTypeDomain, byte(len("v2ray.com")))
|
|
||||||
payload = append(payload, []byte("v2ray.com")...)
|
|
||||||
payload = append(payload, 0, 80)
|
|
||||||
payload = append(payload, []byte("Actual payload")...)
|
|
||||||
|
|
||||||
request, err := ReadUDPRequest(payload)
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
|
|
||||||
assert.Byte(request.Fragment).Equals(1)
|
|
||||||
assert.Address(request.Address).EqualsString("v2ray.com")
|
|
||||||
assert.Port(request.Port).Equals(v2net.Port(80))
|
|
||||||
assert.String(request.Data.String()).Equals("Actual payload")
|
|
||||||
}
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
"v2ray.com/core/common/errors"
|
"v2ray.com/core/common/errors"
|
||||||
"v2ray.com/core/common/log"
|
"v2ray.com/core/common/log"
|
||||||
v2net "v2ray.com/core/common/net"
|
v2net "v2ray.com/core/common/net"
|
||||||
proto "v2ray.com/core/common/protocol"
|
"v2ray.com/core/common/protocol"
|
||||||
"v2ray.com/core/common/serial"
|
"v2ray.com/core/common/serial"
|
||||||
"v2ray.com/core/common/signal"
|
"v2ray.com/core/common/signal"
|
||||||
"v2ray.com/core/proxy"
|
"v2ray.com/core/proxy"
|
||||||
|
@ -120,7 +120,7 @@ func (v *Server) handleConnection(connection internet.Connection) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if request.Command == proto.RequestCommandTCP {
|
if request.Command == protocol.RequestCommandTCP {
|
||||||
dest := request.Destination()
|
dest := request.Destination()
|
||||||
session := &proxy.SessionInfo{
|
session := &proxy.SessionInfo{
|
||||||
Source: clientAddr,
|
Source: clientAddr,
|
||||||
|
@ -134,7 +134,7 @@ func (v *Server) handleConnection(connection internet.Connection) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if request.Command == proto.RequestCommandUDP {
|
if request.Command == protocol.RequestCommandUDP {
|
||||||
v.handleUDP()
|
v.handleUDP()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"v2ray.com/core/common/log"
|
"v2ray.com/core/common/log"
|
||||||
v2net "v2ray.com/core/common/net"
|
v2net "v2ray.com/core/common/net"
|
||||||
"v2ray.com/core/proxy"
|
"v2ray.com/core/proxy"
|
||||||
"v2ray.com/core/proxy/socks/protocol"
|
|
||||||
"v2ray.com/core/transport/internet/udp"
|
"v2ray.com/core/transport/internet/udp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -24,39 +23,33 @@ func (v *Server) listenUDP() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Server) handleUDPPayload(payload *buf.Buffer, session *proxy.SessionInfo) {
|
func (v *Server) handleUDPPayload(payload *buf.Buffer, session *proxy.SessionInfo) {
|
||||||
|
defer payload.Release()
|
||||||
|
|
||||||
source := session.Source
|
source := session.Source
|
||||||
log.Info("Socks: Client UDP connection from ", source)
|
log.Info("Socks: Client UDP connection from ", source)
|
||||||
request, err := protocol.ReadUDPRequest(payload.Bytes())
|
request, data, err := DecodeUDPPacket(payload.Bytes())
|
||||||
payload.Release()
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Socks: Failed to parse UDP request: ", err)
|
log.Error("Socks|Server: Failed to parse UDP request: ", err)
|
||||||
return
|
|
||||||
}
|
|
||||||
if request.Data.Len() == 0 {
|
|
||||||
request.Data.Release()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if request.Fragment != 0 {
|
|
||||||
log.Warning("Socks: Dropping fragmented UDP packets.")
|
|
||||||
// TODO handle fragments
|
|
||||||
request.Data.Release()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Socks: Send packet to ", request.Destination(), " with ", request.Data.Len(), " bytes")
|
if len(data) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Socks: Send packet to ", request.Destination(), " with ", len(data), " bytes")
|
||||||
log.Access(source, request.Destination, log.AccessAccepted, "")
|
log.Access(source, request.Destination, log.AccessAccepted, "")
|
||||||
v.udpServer.Dispatch(&proxy.SessionInfo{Source: source, Destination: request.Destination(), Inbound: v.meta}, request.Data, func(destination v2net.Destination, payload *buf.Buffer) {
|
|
||||||
response := &protocol.Socks5UDPRequest{
|
dataBuf := buf.NewSmall()
|
||||||
Fragment: 0,
|
dataBuf.Append(data)
|
||||||
Address: request.Destination().Address,
|
v.udpServer.Dispatch(&proxy.SessionInfo{Source: source, Destination: request.Destination(), Inbound: v.meta}, dataBuf, func(destination v2net.Destination, payload *buf.Buffer) {
|
||||||
Port: request.Destination().Port,
|
defer payload.Release()
|
||||||
Data: payload,
|
|
||||||
}
|
|
||||||
log.Info("Socks: Writing back UDP response with ", payload.Len(), " bytes to ", destination)
|
log.Info("Socks: Writing back UDP response with ", payload.Len(), " bytes to ", destination)
|
||||||
|
|
||||||
udpMessage := buf.NewLocal(2048)
|
udpMessage := EncodeUDPPacket(request, payload.Bytes())
|
||||||
response.Write(udpMessage)
|
defer udpMessage.Release()
|
||||||
|
|
||||||
v.udpMutex.RLock()
|
v.udpMutex.RLock()
|
||||||
if !v.accepting {
|
if !v.accepting {
|
||||||
|
@ -65,10 +58,9 @@ func (v *Server) handleUDPPayload(payload *buf.Buffer, session *proxy.SessionInf
|
||||||
}
|
}
|
||||||
nBytes, err := v.udpHub.WriteTo(udpMessage.Bytes(), destination)
|
nBytes, err := v.udpHub.WriteTo(udpMessage.Bytes(), destination)
|
||||||
v.udpMutex.RUnlock()
|
v.udpMutex.RUnlock()
|
||||||
udpMessage.Release()
|
|
||||||
response.Data.Release()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Socks: failed to write UDP message (", nBytes, " bytes) to ", destination, ": ", err)
|
log.Warning("Socks: failed to write UDP message (", nBytes, " bytes) to ", destination, ": ", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,7 +200,7 @@ func TestUDPAssociate(t *testing.T) {
|
||||||
return buffer
|
return buffer
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
_, err := udpServer.Start()
|
udpServerAddr, err := udpServer.Start()
|
||||||
assert.Error(err).IsNil()
|
assert.Error(err).IsNil()
|
||||||
defer udpServer.Close()
|
defer udpServer.Close()
|
||||||
|
|
||||||
|
@ -223,7 +223,7 @@ func TestUDPAssociate(t *testing.T) {
|
||||||
assert.Error(err).IsNil()
|
assert.Error(err).IsNil()
|
||||||
assert.Bytes(authResponse[:nBytes]).Equals([]byte{socks5Version, 0})
|
assert.Bytes(authResponse[:nBytes]).Equals([]byte{socks5Version, 0})
|
||||||
|
|
||||||
connectRequest := socks5Request(byte(3), v2net.TCPDestination(v2net.IPAddress([]byte{127, 0, 0, 1}), udpServer.Port))
|
connectRequest := socks5Request(byte(3), v2net.TCPDestination(v2net.LocalHostIP, udpServer.Port))
|
||||||
nBytes, err = conn.Write(connectRequest)
|
nBytes, err = conn.Write(connectRequest)
|
||||||
assert.Int(nBytes).Equals(len(connectRequest))
|
assert.Int(nBytes).Equals(len(connectRequest))
|
||||||
assert.Error(err).IsNil()
|
assert.Error(err).IsNil()
|
||||||
|
@ -241,7 +241,7 @@ func TestUDPAssociate(t *testing.T) {
|
||||||
|
|
||||||
for i := 0; i < 100; i++ {
|
for i := 0; i < 100; i++ {
|
||||||
udpPayload := "UDP request to udp server."
|
udpPayload := "UDP request to udp server."
|
||||||
udpRequest := socks5UDPRequest(v2net.UDPDestination(v2net.LocalHostIP, udpServer.Port), []byte(udpPayload))
|
udpRequest := socks5UDPRequest(udpServerAddr, []byte(udpPayload))
|
||||||
|
|
||||||
nBytes, err = udpConn.Write(udpRequest)
|
nBytes, err = udpConn.Write(udpRequest)
|
||||||
assert.Int(nBytes).Equals(len(udpRequest))
|
assert.Int(nBytes).Equals(len(udpRequest))
|
||||||
|
|
Loading…
Reference in New Issue