forked from reusee/socks5hs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
socks5hs.go
116 lines (102 loc) · 2.62 KB
/
socks5hs.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package socks5hs
import (
"bytes"
"encoding/binary"
"net"
"strconv"
)
const (
VERSION = byte(5)
METHOD_NOT_REQUIRED = byte(0)
METHOD_NO_ACCEPTABLE = byte(0xff)
RESERVED = byte(0)
ADDR_TYPE_IP = byte(1)
ADDR_TYPE_IPV6 = byte(4)
ADDR_TYPE_DOMAIN = byte(3)
CMD_CONNECT = byte(1)
CMD_BIND = byte(2)
CMD_UDP_ASSOCIATE = byte(3)
REP_SUCCEED = byte(0)
REP_SERVER_FAILURE = byte(1)
REP_CONNECTION_NOT_ALLOW = byte(2)
REP_NETWORK_UNREACHABLE = byte(3)
REP_HOST_UNREACHABLE = byte(4)
REP_CONNECTION_REFUSED = byte(5)
REP_TTL_EXPIRED = byte(6)
REP_COMMAND_NOT_SUPPORTED = byte(7)
REP_ADDRESS_TYPE_NOT_SUPPORTED = byte(8)
)
func Handshake(conn net.Conn) (hostPort string, err error) {
defer ct(&err)
read := func(v interface{}) {
ce(binary.Read(conn, binary.BigEndian, v), "read")
}
write := func(v interface{}) {
ce(binary.Write(conn, binary.BigEndian, v), "read")
}
writeAck := func(reply byte) {
write(VERSION)
write(reply)
write(RESERVED)
write(ADDR_TYPE_IP)
write([4]byte{0, 0, 0, 0})
write(uint16(0))
}
// handshake
var ver, nMethods byte
read(&ver)
read(&nMethods)
methods := make([]byte, nMethods)
read(methods)
write(VERSION)
if ver != VERSION || nMethods < byte(1) {
write(METHOD_NO_ACCEPTABLE)
} else {
if bytes.IndexByte(methods, METHOD_NOT_REQUIRED) == -1 {
write(METHOD_NO_ACCEPTABLE)
} else {
write(METHOD_NOT_REQUIRED)
}
}
// request
var cmd, reserved, addrType byte
read(&ver)
read(&cmd)
read(&reserved)
read(&addrType)
if ver != VERSION {
return "", me(nil, "invalid version")
}
if reserved != RESERVED {
return "", me(nil, "invalid reserved byte")
}
if addrType != ADDR_TYPE_IP && addrType != ADDR_TYPE_DOMAIN && addrType != ADDR_TYPE_IPV6 {
writeAck(REP_ADDRESS_TYPE_NOT_SUPPORTED)
return "", me(nil, "address type not supported")
}
var address []byte
if addrType == ADDR_TYPE_IP {
address = make([]byte, 4)
} else if addrType == ADDR_TYPE_DOMAIN {
var domainLength byte
read(&domainLength)
address = make([]byte, domainLength)
} else if addrType == ADDR_TYPE_IPV6 {
address = make([]byte, 16)
}
read(address)
var port uint16
read(&port)
if addrType == ADDR_TYPE_IP || addrType == ADDR_TYPE_IPV6 {
ip := net.IP(address)
hostPort = net.JoinHostPort(ip.String(), strconv.Itoa(int(port)))
} else if addrType == ADDR_TYPE_DOMAIN {
hostPort = net.JoinHostPort(string(address), strconv.Itoa(int(port)))
}
if cmd != CMD_CONNECT {
writeAck(REP_COMMAND_NOT_SUPPORTED)
return "", me(nil, "command not supported")
}
writeAck(REP_SUCCEED)
return
}