summaryrefslogtreecommitdiff
path: root/vendor/git.torproject.org/pluggable-transports/goptlib.git/socks.go
blob: 29827d9677c59d412546995523584ef2df35e208 (plain)
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
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
package pt

import (
	"bufio"
	"fmt"
	"io"
	"net"
	"time"
)

const (
	socksVersion = 0x05

	socksAuthNoneRequired        = 0x00
	socksAuthUsernamePassword    = 0x02
	socksAuthNoAcceptableMethods = 0xff

	socksCmdConnect = 0x01
	socksRsv        = 0x00

	socksAtypeV4         = 0x01
	socksAtypeDomainName = 0x03
	socksAtypeV6         = 0x04

	socksAuthRFC1929Ver     = 0x01
	socksAuthRFC1929Success = 0x00
	socksAuthRFC1929Fail    = 0x01

	socksRepSucceeded = 0x00
	// "general SOCKS server failure"
	SocksRepGeneralFailure = 0x01
	// "connection not allowed by ruleset"
	SocksRepConnectionNotAllowed = 0x02
	// "Network unreachable"
	SocksRepNetworkUnreachable = 0x03
	// "Host unreachable"
	SocksRepHostUnreachable = 0x04
	// "Connection refused"
	SocksRepConnectionRefused = 0x05
	// "TTL expired"
	SocksRepTTLExpired = 0x06
	// "Command not supported"
	SocksRepCommandNotSupported = 0x07
	// "Address type not supported"
	SocksRepAddressNotSupported = 0x08
)

// Put a sanity timeout on how long we wait for a SOCKS request.
const socksRequestTimeout = 5 * time.Second

// SocksRequest describes a SOCKS request.
type SocksRequest struct {
	// The endpoint requested by the client as a "host:port" string.
	Target string
	// The userid string sent by the client.
	Username string
	// The password string sent by the client.
	Password string
	// The parsed contents of Username as a key–value mapping.
	Args Args
}

// SocksConn encapsulates a net.Conn and information associated with a SOCKS request.
type SocksConn struct {
	net.Conn
	Req SocksRequest
}

// Send a message to the proxy client that access to the given address is
// granted. Addr is ignored, and "0.0.0.0:0" is always sent back for
// BND.ADDR/BND.PORT in the SOCKS response.
func (conn *SocksConn) Grant(addr *net.TCPAddr) error {
	return sendSocks5ResponseGranted(conn)
}

// Send a message to the proxy client that access was rejected or failed.  This
// sends back a "General Failure" error code.  RejectReason should be used if
// more specific error reporting is desired.
func (conn *SocksConn) Reject() error {
	return conn.RejectReason(SocksRepGeneralFailure)
}

// Send a message to the proxy client that access was rejected, with the
// specific error code indicating the reason behind the rejection.
func (conn *SocksConn) RejectReason(reason byte) error {
	return sendSocks5ResponseRejected(conn, reason)
}

// SocksListener wraps a net.Listener in order to read a SOCKS request on Accept.
//
// 	func handleConn(conn *pt.SocksConn) error {
// 		defer conn.Close()
// 		remote, err := net.Dial("tcp", conn.Req.Target)
// 		if err != nil {
// 			conn.Reject()
// 			return err
// 		}
// 		defer remote.Close()
// 		err = conn.Grant(remote.RemoteAddr().(*net.TCPAddr))
// 		if err != nil {
// 			return err
// 		}
// 		// do something with conn and remote
// 		return nil
// 	}
// 	...
// 	ln, err := pt.ListenSocks("tcp", "127.0.0.1:0")
// 	if err != nil {
// 		panic(err.Error())
// 	}
// 	for {
// 		conn, err := ln.AcceptSocks()
// 		if err != nil {
// 			log.Printf("accept error: %s", err)
// 			if e, ok := err.(net.Error); ok && e.Temporary() {
// 				continue
// 			}
// 			break
// 		}
// 		go handleConn(conn)
// 	}
type SocksListener struct {
	net.Listener
}

// Open a net.Listener according to network and laddr, and return it as a
// SocksListener.
func ListenSocks(network, laddr string) (*SocksListener, error) {
	ln, err := net.Listen(network, laddr)
	if err != nil {
		return nil, err
	}
	return NewSocksListener(ln), nil
}

// Create a new SocksListener wrapping the given net.Listener.
func NewSocksListener(ln net.Listener) *SocksListener {
	return &SocksListener{ln}
}

// Accept is the same as AcceptSocks, except that it returns a generic net.Conn.
// It is present for the sake of satisfying the net.Listener interface.
func (ln *SocksListener) Accept() (net.Conn, error) {
	return ln.AcceptSocks()
}

// Call Accept on the wrapped net.Listener, do SOCKS negotiation, and return a
// SocksConn. After accepting, you must call either conn.Grant or conn.Reject
// (presumably after trying to connect to conn.Req.Target).
//
// Errors returned by AcceptSocks may be temporary (for example, EOF while
// reading the request, or a badly formatted userid string), or permanent (e.g.,
// the underlying socket is closed). You can determine whether an error is
// temporary and take appropriate action with a type conversion to net.Error.
// For example:
//
// 	for {
// 		conn, err := ln.AcceptSocks()
// 		if err != nil {
// 			if e, ok := err.(net.Error); ok && e.Temporary() {
// 				log.Printf("temporary accept error; trying again: %s", err)
// 				continue
// 			}
// 			log.Printf("permanent accept error; giving up: %s", err)
// 			break
// 		}
// 		go handleConn(conn)
// 	}
func (ln *SocksListener) AcceptSocks() (*SocksConn, error) {
retry:
	c, err := ln.Listener.Accept()
	if err != nil {
		return nil, err
	}
	conn := new(SocksConn)
	conn.Conn = c
	err = conn.SetDeadline(time.Now().Add(socksRequestTimeout))
	if err != nil {
		conn.Close()
		goto retry
	}
	conn.Req, err = socks5Handshake(conn)
	if err != nil {
		conn.Close()
		goto retry
	}
	err = conn.SetDeadline(time.Time{})
	if err != nil {
		conn.Close()
		goto retry
	}
	return conn, nil
}

// Returns "socks5", suitable to be included in a call to Cmethod.
func (ln *SocksListener) Version() string {
	return "socks5"
}

// socks5handshake conducts the SOCKS5 handshake up to the point where the
// client command is read and the proxy must open the outgoing connection.
// Returns a SocksRequest.
func socks5Handshake(s io.ReadWriter) (req SocksRequest, err error) {
	rw := bufio.NewReadWriter(bufio.NewReader(s), bufio.NewWriter(s))

	// Negotiate the authentication method.
	var method byte
	if method, err = socksNegotiateAuth(rw); err != nil {
		return
	}

	// Authenticate the client.
	if err = socksAuthenticate(rw, method, &req); err != nil {
		return
	}

	// Read the command.
	err = socksReadCommand(rw, &req)
	return
}

// socksNegotiateAuth negotiates the authentication method and returns the
// selected method as a byte.  On negotiation failures an error is returned.
func socksNegotiateAuth(rw *bufio.ReadWriter) (method byte, err error) {
	// Validate the version.
	if err = socksReadByteVerify(rw, "version", socksVersion); err != nil {
		return
	}

	// Read the number of methods.
	var nmethods byte
	if nmethods, err = socksReadByte(rw); err != nil {
		return
	}

	// Read the methods.
	var methods []byte
	if methods, err = socksReadBytes(rw, int(nmethods)); err != nil {
		return
	}

	// Pick the most "suitable" method.
	method = socksAuthNoAcceptableMethods
	for _, m := range methods {
		switch m {
		case socksAuthNoneRequired:
			// Pick Username/Password over None if the client happens to
			// send both.
			if method == socksAuthNoAcceptableMethods {
				method = m
			}

		case socksAuthUsernamePassword:
			method = m
		}
	}

	// Send the negotiated method.
	var msg [2]byte
	msg[0] = socksVersion
	msg[1] = method
	if _, err = rw.Writer.Write(msg[:]); err != nil {
		return
	}

	if err = socksFlushBuffers(rw); err != nil {
		return
	}
	return
}

// socksAuthenticate authenticates the client via the chosen authentication
// mechanism.
func socksAuthenticate(rw *bufio.ReadWriter, method byte, req *SocksRequest) (err error) {
	switch method {
	case socksAuthNoneRequired:
		// Straight into reading the connect.

	case socksAuthUsernamePassword:
		if err = socksAuthRFC1929(rw, req); err != nil {
			return
		}

	case socksAuthNoAcceptableMethods:
		err = fmt.Errorf("SOCKS method select had no compatible methods")
		return

	default:
		err = fmt.Errorf("SOCKS method select picked a unsupported method 0x%02x", method)
		return
	}

	if err = socksFlushBuffers(rw); err != nil {
		return
	}
	return
}

// socksAuthRFC1929 authenticates the client via RFC 1929 username/password
// auth.  As a design decision any valid username/password is accepted as this
// field is primarily used as an out-of-band argument passing mechanism for
// pluggable transports.
func socksAuthRFC1929(rw *bufio.ReadWriter, req *SocksRequest) (err error) {
	sendErrResp := func() {
		// Swallow the write/flush error here, we are going to close the
		// connection and the original failure is more useful.
		resp := []byte{socksAuthRFC1929Ver, socksAuthRFC1929Fail}
		rw.Write(resp[:])
		socksFlushBuffers(rw)
	}

	// Validate the fixed parts of the command message.
	if err = socksReadByteVerify(rw, "auth version", socksAuthRFC1929Ver); err != nil {
		sendErrResp()
		return
	}

	// Read the username.
	var ulen byte
	if ulen, err = socksReadByte(rw); err != nil {
		return
	}
	if ulen < 1 {
		sendErrResp()
		err = fmt.Errorf("RFC1929 username with 0 length")
		return
	}
	var uname []byte
	if uname, err = socksReadBytes(rw, int(ulen)); err != nil {
		return
	}
	req.Username = string(uname)

	// Read the password.
	var plen byte
	if plen, err = socksReadByte(rw); err != nil {
		return
	}
	if plen < 1 {
		sendErrResp()
		err = fmt.Errorf("RFC1929 password with 0 length")
		return
	}
	var passwd []byte
	if passwd, err = socksReadBytes(rw, int(plen)); err != nil {
		return
	}
	if !(plen == 1 && passwd[0] == 0x00) {
		// tor will set the password to 'NUL' if there are no arguments.
		req.Password = string(passwd)
	}

	// Mash the username/password together and parse it as a pluggable
	// transport argument string.
	if req.Args, err = parseClientParameters(req.Username + req.Password); err != nil {
		sendErrResp()
	} else {
		resp := []byte{socksAuthRFC1929Ver, socksAuthRFC1929Success}
		_, err = rw.Write(resp[:])
	}
	return
}

// socksReadCommand reads a SOCKS5 client command and parses out the relevant
// fields into a SocksRequest.  Only CMD_CONNECT is supported.
func socksReadCommand(rw *bufio.ReadWriter, req *SocksRequest) (err error) {
	sendErrResp := func(reason byte) {
		// Swallow errors that occur when writing/flushing the response,
		// connection will be closed anyway.
		sendSocks5ResponseRejected(rw, reason)
		socksFlushBuffers(rw)
	}

	// Validate the fixed parts of the command message.
	if err = socksReadByteVerify(rw, "version", socksVersion); err != nil {
		sendErrResp(SocksRepGeneralFailure)
		return
	}
	if err = socksReadByteVerify(rw, "command", socksCmdConnect); err != nil {
		sendErrResp(SocksRepCommandNotSupported)
		return
	}
	if err = socksReadByteVerify(rw, "reserved", socksRsv); err != nil {
		sendErrResp(SocksRepGeneralFailure)
		return
	}

	// Read the destination address/port.
	// XXX: This should probably eventually send socks 5 error messages instead
	// of rudely closing connections on invalid addresses.
	var atype byte
	if atype, err = socksReadByte(rw); err != nil {
		return
	}
	var host string
	switch atype {
	case socksAtypeV4:
		var addr []byte
		if addr, err = socksReadBytes(rw, net.IPv4len); err != nil {
			return
		}
		host = net.IPv4(addr[0], addr[1], addr[2], addr[3]).String()

	case socksAtypeDomainName:
		var alen byte
		if alen, err = socksReadByte(rw); err != nil {
			return
		}
		if alen == 0 {
			err = fmt.Errorf("SOCKS request had domain name with 0 length")
			return
		}
		var addr []byte
		if addr, err = socksReadBytes(rw, int(alen)); err != nil {
			return
		}
		host = string(addr)

	case socksAtypeV6:
		var rawAddr []byte
		if rawAddr, err = socksReadBytes(rw, net.IPv6len); err != nil {
			return
		}
		addr := make(net.IP, net.IPv6len)
		copy(addr[:], rawAddr[:])
		host = fmt.Sprintf("[%s]", addr.String())

	default:
		sendErrResp(SocksRepAddressNotSupported)
		err = fmt.Errorf("SOCKS request had unsupported address type 0x%02x", atype)
		return
	}
	var rawPort []byte
	if rawPort, err = socksReadBytes(rw, 2); err != nil {
		return
	}
	port := int(rawPort[0])<<8 | int(rawPort[1])<<0

	if err = socksFlushBuffers(rw); err != nil {
		return
	}

	req.Target = fmt.Sprintf("%s:%d", host, port)
	return
}

// Send a SOCKS5 response with the given code. BND.ADDR/BND.PORT is always the
// IPv4 address/port "0.0.0.0:0".
func sendSocks5Response(w io.Writer, code byte) error {
	resp := make([]byte, 4+4+2)
	resp[0] = socksVersion
	resp[1] = code
	resp[2] = socksRsv
	resp[3] = socksAtypeV4

	// BND.ADDR/BND.PORT should be the address and port that the outgoing
	// connection is bound to on the proxy, but Tor does not use this
	// information, so all zeroes are sent.

	_, err := w.Write(resp[:])
	return err
}

// Send a SOCKS5 response code 0x00.
func sendSocks5ResponseGranted(w io.Writer) error {
	return sendSocks5Response(w, socksRepSucceeded)
}

// Send a SOCKS5 response with the provided failure reason.
func sendSocks5ResponseRejected(w io.Writer, reason byte) error {
	return sendSocks5Response(w, reason)
}

func socksFlushBuffers(rw *bufio.ReadWriter) error {
	if err := rw.Writer.Flush(); err != nil {
		return err
	}
	if rw.Reader.Buffered() > 0 {
		return fmt.Errorf("%d bytes left after SOCKS message", rw.Reader.Buffered())
	}
	return nil
}

func socksReadByte(rw *bufio.ReadWriter) (byte, error) {
	return rw.Reader.ReadByte()
}

func socksReadBytes(rw *bufio.ReadWriter, n int) ([]byte, error) {
	ret := make([]byte, n)
	if _, err := io.ReadFull(rw.Reader, ret); err != nil {
		return nil, err
	}
	return ret, nil
}

func socksReadByteVerify(rw *bufio.ReadWriter, descr string, expected byte) error {
	val, err := socksReadByte(rw)
	if err != nil {
		return err
	}
	if val != expected {
		return fmt.Errorf("SOCKS message field %s was 0x%02x, not 0x%02x", descr, val, expected)
	}
	return nil
}

var _ net.Listener = (*SocksListener)(nil)