-
Notifications
You must be signed in to change notification settings - Fork 0
/
request.go
166 lines (139 loc) · 3.21 KB
/
request.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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package socks5
import (
"bufio"
"errors"
"io"
"net"
)
type Request struct {
Command CommandType
AddressType AddressType
Address net.IP
Fqdn string
Port uint16
stream io.Writer
bufIo *bufio.Reader
}
type Response struct {
Type ReplyType
AddressType AddressType
BindAddress net.IP
BindPort uint16
BindFqdn string
}
func checkMethodAvailable(buf []byte) bool {
for i := 0; i < len(buf); i++ {
if buf[i] == 0 {
return true
}
}
return false
}
func (req *Request) recvMethodSelection() ([]byte, error) {
buf := make([]byte, 2)
if _, err := io.ReadFull(req.bufIo, buf); err != nil {
return nil, err
}
if buf[0] != SocksVersion {
return nil, errors.New("socks version is not 5")
}
if buf[1] == 0 {
return nil, errors.New("socks auth method is not specified")
}
methodsLen := int(buf[1])
buf = make([]byte, methodsLen)
if _, err := io.ReadFull(req.bufIo, buf); err != nil {
return nil, err
}
return buf, nil
}
func (req *Request) sendMethodSelection(method byte) error {
msg := []byte{SocksVersion, method}
_, err := req.stream.Write(msg)
return err
}
func (req *Request) recvRequest() error {
buf := make([]byte, 4)
if _, err := io.ReadFull(req.bufIo, buf); err != nil {
return err
}
if buf[0] != SocksVersion {
return errors.New("socks version is not 5")
}
if buf[1] < 1 || buf[1] > 3 {
return errors.New("invalid command")
}
req.Command = CommandType(buf[1])
req.AddressType = AddressType(buf[3])
switch req.AddressType {
case AddrIPv4:
buf = make([]byte, 4+2)
if _, err := io.ReadFull(req.bufIo, buf); err != nil {
return err
}
req.Address = net.IPv4(buf[0], buf[1], buf[2], buf[3])
req.Port = uint16(buf[4])<<8 | uint16(buf[5])
case AddrDns:
len, err := req.bufIo.ReadByte()
if err != nil {
return err
}
buf = make([]byte, len+2)
if _, err := io.ReadFull(req.bufIo, buf); err != nil {
return err
}
req.Fqdn = string(buf[:len])
req.Port = uint16(buf[len])<<8 | uint16(buf[len+1])
default:
return errors.New("invalid or unsupported address type")
}
return nil
}
func NewRequest(stream io.ReadWriter) *Request {
return &Request{
stream: stream,
bufIo: bufio.NewReader(stream),
}
}
func (req *Request) Negotiate() error {
methods, err := req.recvMethodSelection()
if err != nil {
return err
}
if !checkMethodAvailable(methods) {
if err := req.sendMethodSelection(0xff); err != nil {
return err
}
return errors.New("socks auth method is not supported")
}
if err := req.sendMethodSelection(0); err != nil {
return err
}
if err := req.recvRequest(); err != nil {
return err
}
return nil
}
func (req *Request) SendResponse(res Response) error {
buf := []byte{
5,
byte(res.Type),
0,
byte(res.AddressType),
}
switch res.AddressType {
case AddrIPv4:
buf = append(buf, []byte(res.BindAddress.To4())...)
case AddrDns:
buf = append(buf, append([]byte{byte(len(res.BindFqdn))}, res.BindFqdn...)...)
case AddrIPv6:
buf = append(buf, []byte(res.BindAddress.To16())...)
default:
return errors.New("invalid or unsupported address type")
}
buf = append(buf, byte(res.BindPort>>8), byte(res.BindPort&0xff))
if _, err := req.stream.Write(buf); err != nil {
return err
}
return nil
}