-
Notifications
You must be signed in to change notification settings - Fork 123
/
socks5.go
378 lines (335 loc) · 10.5 KB
/
socks5.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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
package socks5
import (
"context"
"fmt"
"io"
mrand "math/rand"
"net"
"strconv"
"sync"
"github.com/enfein/mieru/pkg/cipher"
"github.com/enfein/mieru/pkg/log"
"github.com/enfein/mieru/pkg/metrics"
"github.com/enfein/mieru/pkg/protocolv2"
"github.com/enfein/mieru/pkg/stderror"
"github.com/enfein/mieru/pkg/util"
)
const (
// socks5 version number.
Socks5Version byte = 5
// No authentication required.
NoAuth byte = 0
)
var (
HandshakeErrors = metrics.RegisterMetric("socks5", "HandshakeErrors")
DNSResolveErrors = metrics.RegisterMetric("socks5", "DNSResolveErrors")
UnsupportedCommandErrors = metrics.RegisterMetric("socks5", "UnsupportedCommandErrors")
NetworkUnreachableErrors = metrics.RegisterMetric("socks5", "NetworkUnreachableErrors")
HostUnreachableErrors = metrics.RegisterMetric("socks5", "HostUnreachableErrors")
ConnectionRefusedErrors = metrics.RegisterMetric("socks5", "ConnectionRefusedErrors")
UDPAssociateErrors = metrics.RegisterMetric("socks5", "UDPAssociateErrors")
// Incoming UDP association bytes.
UDPAssociateInBytes = metrics.RegisterMetric("socks5 UDP associate", "InBytes")
// Outgoing UDP association bytes.
UDPAssociateOutBytes = metrics.RegisterMetric("socks5 UDP associate", "OutBytes")
// Incoming UDP association packets.
UDPAssociateInPkts = metrics.RegisterMetric("socks5 UDP associate", "InPkts")
// Outgoing UDP association packets.
UDPAssociateOutPkts = metrics.RegisterMetric("socks5 UDP associate", "OutPkts")
)
// ProxyConfig is used to configure mieru proxy options.
type ProxyConfig struct {
// NetworkType ("tcp", "udp", etc.) used when dial to the proxy.
NetworkType string
// Address is proxy server listening address, in host:port format.
Address string
// Password is used to derive the cipher block used for encryption.
Password []byte
// Dial is the function to dial to the proxy server.
Dial func(ctx context.Context, proxyNetwork, localAddr, proxyAddr string, block cipher.BlockCipher) (net.Conn, error)
}
// Config is used to setup and configure a socks5 server.
type Config struct {
// Resolver can be provided to do custom name resolution.
Resolver *util.DNSResolver
// BindIP is used for bind or udp associate
BindIP net.IP
// Allow using socks5 to access resources served in localhost.
AllowLocalDestination bool
// Use mieru proxy to carry socks5 traffic.
UseProxy bool
// Mieru proxy configuration.
ProxyConf []ProxyConfig
// Mieru proxy multiplexer.
ProxyMux *protocolv2.Mux
}
// Server is responsible for accepting connections and handling
// the details of the SOCKS5 protocol
type Server struct {
config *Config
listener net.Listener
chAccept chan net.Conn
chAcceptErr chan error
die chan struct{}
}
var (
_ util.ConnHandler = &Server{}
)
// New creates a new Server and potentially returns an error.
func New(conf *Config) (*Server, error) {
// Ensure we have a DNS resolver.
if conf.Resolver == nil {
conf.Resolver = &util.DNSResolver{}
}
// Provide a default bind IP.
if conf.BindIP == nil {
conf.BindIP = net.ParseIP(util.AllIPAddr())
if conf.BindIP == nil {
return nil, fmt.Errorf("set socks5 bind IP failed")
}
}
return &Server{
config: conf,
chAccept: make(chan net.Conn, 256),
chAcceptErr: make(chan error, 1), // non-blocking
die: make(chan struct{}),
}, nil
}
// ListenAndServe is used to create a listener and serve on it.
func (s *Server) ListenAndServe(network, addr string) error {
l, err := net.Listen(network, addr)
if err != nil {
return err
}
return s.Serve(l)
}
// Serve is used to serve connections from a listener.
func (s *Server) Serve(l net.Listener) error {
s.listener = l
go s.acceptLoop()
for {
select {
case conn := <-s.chAccept:
go func() {
err := s.ServeConn(conn)
if err != nil && !stderror.IsEOF(err) && !stderror.IsClosed(err) {
log.Debugf("socks5 server listener %v ServeConn() failed: %v", s.listener.Addr(), err)
}
}()
case err := <-s.chAcceptErr:
log.Errorf("encountered error when socks5 server accept new connection: %v", err)
log.Infof("closing socks5 server listener")
if err := s.listener.Close(); err != nil {
log.Warnf("socks5 server listener %v Close() failed: %v", s.listener.Addr(), err)
}
return err // the err from chAcceptErr
case <-s.die:
log.Infof("closing socks5 server listener")
if err := s.listener.Close(); err != nil {
log.Warnf("socks5 server listener %v Close() failed: %v", s.listener.Addr(), err)
}
return nil
}
}
}
// Take implements util.ConnHandler interface.
func (s *Server) Take(conn net.Conn) (closed bool, err error) {
err = s.ServeConn(conn)
return true, err
}
// ServeConn is used to serve a single connection.
func (s *Server) ServeConn(conn net.Conn) error {
conn = util.WrapHierarchyConn(conn)
defer conn.Close()
if log.IsLevelEnabled(log.TraceLevel) {
log.Tracef("socks5 server starts to serve connection [%v - %v]", conn.LocalAddr(), conn.RemoteAddr())
}
if s.config.UseProxy {
return s.clientServeConn(conn)
} else {
return s.serverServeConn(conn)
}
}
// Close closes the network listener used by the server.
func (s *Server) Close() error {
close(s.die)
return nil
}
func (s *Server) acceptLoop() {
for {
conn, err := s.listener.Accept()
if err != nil {
s.chAcceptErr <- err
return
}
s.chAccept <- conn
if log.IsLevelEnabled(log.TraceLevel) {
log.Tracef("socks5 server accepted connection [%v - %v]", conn.LocalAddr(), conn.RemoteAddr())
}
}
}
func (s *Server) clientServeConn(conn net.Conn) error {
// Proxy is enabled, forward all the traffic to proxy.
// If there are multiple proxy endpoints, randomly select one of them.
n := len(s.config.ProxyConf)
if n == 0 {
log.Fatalf("No proxy configuration is available in socks5 server config")
}
i := mrand.Intn(n)
proxyConf := s.config.ProxyConf[i]
if proxyConf.NetworkType == "" {
log.Fatalf("Proxy network type is not set in socks5 server config")
}
if proxyConf.Address == "" {
log.Fatalf("Proxy address is not set in socks5 server config")
}
if len(proxyConf.Password) == 0 {
log.Fatalf("Proxy password is not set in socks5 server config")
}
if proxyConf.Dial == nil {
log.Fatalf("Proxy dial function is not set in socks5 server config")
}
ctx := context.Background()
var block cipher.BlockCipher
var err error
if proxyConf.NetworkType == "tcp" {
block, err = cipher.BlockCipherFromPassword(proxyConf.Password, false)
} else if proxyConf.NetworkType == "udp" {
block, err = cipher.BlockCipherFromPassword(proxyConf.Password, true)
} else {
return fmt.Errorf("proxy network type %q is not supported", proxyConf.NetworkType)
}
if err != nil {
return fmt.Errorf("cipher.BlockCipherFromPassword() failed: %w", err)
}
proxyConn, err := proxyConf.Dial(ctx, proxyConf.NetworkType, "", proxyConf.Address, block)
if err != nil {
return fmt.Errorf("proxy Dial(%q, %q) failed: %w", proxyConf.NetworkType, proxyConf.Address, err)
}
if err := s.proxySocks5AuthReq(conn, proxyConn); err != nil {
HandshakeErrors.Add(1)
proxyConn.Close()
return err
}
udpAssociateConn, err := s.proxySocks5ConnReq(conn, proxyConn)
if err != nil {
HandshakeErrors.Add(1)
proxyConn.Close()
return err
}
if udpAssociateConn != nil {
log.Debugf("UDP association is listening on %v", udpAssociateConn.LocalAddr())
conn.(util.HierarchyConn).AddSubConnection(udpAssociateConn)
go func() {
util.WaitForClose(conn)
conn.Close()
}()
return BidiCopyUDP(udpAssociateConn, WrapUDPAssociateTunnel(proxyConn))
}
return util.BidiCopy(conn, proxyConn, true)
}
func (s *Server) serverServeConn(conn net.Conn) error {
// Read the version byte and ensure we are compatible.
version := []byte{0}
if _, err := io.ReadFull(conn, version); err != nil {
HandshakeErrors.Add(1)
return fmt.Errorf("get socks version failed: %w", err)
}
if version[0] != Socks5Version {
HandshakeErrors.Add(1)
return fmt.Errorf("unsupported socks version: %v", version)
}
// Authenticate the connection.
nAuthMethods := []byte{0}
if _, err := io.ReadFull(conn, nAuthMethods); err != nil {
HandshakeErrors.Add(1)
return fmt.Errorf("get number of authentication method failed: %w", err)
}
if nAuthMethods[0] == 0 {
HandshakeErrors.Add(1)
return fmt.Errorf("number of authentication method is 0")
}
allowNoAuth := false
authMethods := make([]byte, nAuthMethods[0])
if _, err := io.ReadFull(conn, authMethods); err != nil {
HandshakeErrors.Add(1)
return fmt.Errorf("get authentication method failed: %w", err)
}
for _, method := range authMethods {
if method == NoAuth {
allowNoAuth = true
break
}
}
if !allowNoAuth {
HandshakeErrors.Add(1)
return fmt.Errorf("no authentication is not supported by socks5 client")
}
if _, err := conn.Write([]byte{Socks5Version, NoAuth}); err != nil {
HandshakeErrors.Add(1)
return fmt.Errorf("write authentication response failed: %w", err)
}
request, err := NewRequest(conn)
if err != nil {
HandshakeErrors.Add(1)
if err == errUnrecognizedAddrType {
if err := sendReply(conn, addrTypeNotSupported, nil); err != nil {
return fmt.Errorf("failed to send reply: %w", err)
}
}
return fmt.Errorf("failed to read destination address: %w", err)
}
if client, ok := conn.RemoteAddr().(*net.TCPAddr); ok {
request.RemoteAddr = &AddrSpec{IP: client.IP, Port: client.Port}
}
// Process the client request.
if err := s.handleRequest(request, conn); err != nil {
return fmt.Errorf("handleRequest() failed: %w", err)
}
return nil
}
// ServerGroup is a collection of socks5 servers that share the same lifecycle.
type ServerGroup struct {
servers map[string]*Server
mu sync.Mutex
}
// NewGroup creates a new ServerGroup.
func NewGroup() *ServerGroup {
return &ServerGroup{
servers: make(map[string]*Server),
}
}
// Add adds a socks5 server into the ServerGroup.
func (g *ServerGroup) Add(underlayProtocol string, port int, s *Server) error {
g.mu.Lock()
defer g.mu.Unlock()
key := underlayProtocol + "-" + strconv.Itoa(port)
if _, found := g.servers[key]; found {
return stderror.ErrAlreadyExist
}
if s == nil {
return stderror.ErrInvalidArgument
}
g.servers[key] = s
return nil
}
// CloseAndRemoveAll closes all the socks5 servers and clear the group.
func (g *ServerGroup) CloseAndRemoveAll() error {
g.mu.Lock()
defer g.mu.Unlock()
var lastErr error
for _, l := range g.servers {
err := l.Close()
if err != nil {
lastErr = err
}
}
g.servers = make(map[string]*Server)
return lastErr
}
// IsEmpty returns true if the group has no socks5 server.
func (g *ServerGroup) IsEmpty() bool {
g.mu.Lock()
defer g.mu.Unlock()
return len(g.servers) == 0
}