-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathsocks.go
769 lines (679 loc) · 22.3 KB
/
socks.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
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
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
package pt
import (
"bufio"
"fmt"
"io"
"net"
"time"
)
const (
socks4Version = 0x04
socks5Version = 0x05
socksAuthNoneRequired = 0x00
socksAuthUsernamePassword = 0x02
socksAuthNoAcceptableMethods = 0xff
socksCmdConnect = 0x01
socksReserved = 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
socks4ResponseVersion = 0x00
socks4RequestGranted = 0x5a
socks4RequestRejected = 0x5b
)
// 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
socksVersion byte
}
// Send a message to the proxy client that access to the given address is
// granted.
// For SOCKS5, Addr is ignored, and "0.0.0.0:0" is always sent back for
// BND.ADDR/BND.PORT in the SOCKS response.
// For SOCKS4a, if the IP field inside addr is not an IPv4 address, the IP
// portion of the response will be four zero bytes.
func (conn *SocksConn) Grant(addr *net.TCPAddr) error {
if conn.socksVersion == socks4Version {
return sendSocks4aResponseGranted(conn, addr)
}
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 {
if conn.socksVersion == socks4Version {
return sendSocks4aResponseRejected(conn)
}
return sendSocks5ResponseRejected(conn, SocksRepGeneralFailure)
}
// Send a message to the proxy client that access was rejected, with the
// specific error code indicating the reason behind the rejection.
// For SOCKS4a, the reason is ignored.
func (conn *SocksConn) RejectReason(reason byte) error {
if conn.socksVersion == socks4Version {
return sendSocks4aResponseRejected(conn)
}
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() {
// break
// }
// continue
// }
// 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) {
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()
err = newTemporaryNetError("AcceptSocks: conn.SetDeadline() #1 failed: %s", err.Error())
return nil, err
}
rw := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn))
// Determine which SOCKS version the client is using and branch on it.
if version, err := socksPeekByte(rw.Reader); err != nil {
conn.Close()
err = newTemporaryNetError("AcceptSocks: socksPeekByte() failed: %s", err.Error())
return nil, err
} else if version == socks4Version {
conn.socksVersion = socks4Version
conn.Req, err = readSocks4aConnect(rw.Reader)
if err != nil {
conn.Close()
return nil, err
}
} else if version == socks5Version {
conn.socksVersion = socks5Version
conn.Req, err = socks5Handshake(rw)
if err != nil {
conn.Close()
return nil, err
}
} else {
conn.Close()
err = newTemporaryNetError("AcceptSocks: Illegal SOCKS version: 0x%02x", version)
return nil, err
}
err = conn.SetDeadline(time.Time{})
if err != nil {
conn.Close()
err = newTemporaryNetError("AcceptSocks: conn.SetDeadline() #2 failed: %s", err.Error())
return nil, err
}
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(rw *bufio.ReadWriter) (req SocksRequest, err error) {
// Negotiate the authentication method.
var method byte
if method, err = socks5NegotiateAuth(rw); err != nil {
return
}
// Authenticate the client.
if err = socks5Authenticate(rw, method, &req); err != nil {
return
}
// Read the command.
err = socks5ReadCommand(rw, &req)
return
}
// socks5NegotiateAuth negotiates the authentication method and returns the
// selected method as a byte. On negotiation failures an error is returned.
func socks5NegotiateAuth(rw *bufio.ReadWriter) (method byte, err error) {
// Validate the version.
if err = socksReadByteVerify(rw.Reader, "version", socks5Version); err != nil {
err = newTemporaryNetError("socks5NegotiateAuth: %s", err.Error())
return
}
// Read the number of methods.
var nmethods byte
if nmethods, err = socksReadByte(rw.Reader); err != nil {
err = newTemporaryNetError("socks5NegotiateAuth: Failed to read nmethods byte: %s", err.Error())
return
}
// Read the methods.
var methods []byte
if methods, err = socksReadBytes(rw.Reader, int(nmethods)); err != nil {
err = newTemporaryNetError("socks5NegotiateAuth: Failed to read methods bytes: %s", err.Error())
return
}
// Pick the most "suitable" method.
method = socksAuthNoAcceptableMethods
for _, m := range methods {
// [Psiphon]
// Some SOCKS5 clients send both None and Username/Password when in fact they are only
// able to auth with None. Since we don't need pluggable transport parameters and prefer
// enabling clients to proxy, we prefer None, which allows those clients to connect.
/*
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
}
*/
switch m {
case socksAuthNoneRequired:
method = m
case socksAuthUsernamePassword:
if method == socksAuthNoAcceptableMethods {
method = m
}
}
// [Psiphon]
}
// Send the negotiated method.
var msg [2]byte
msg[0] = socks5Version
msg[1] = method
if _, err = rw.Writer.Write(msg[:]); err != nil {
err = newTemporaryNetError("socks5NegotiateAuth: Failed to write negotiated method: %s", err.Error())
return
}
if err = socksFlushBuffers(rw); err != nil {
err = newTemporaryNetError("socks5NegotiateAuth: Failed to flush buffers: %s", err.Error())
return
}
return
}
// socks5Authenticate authenticates the client via the chosen authentication
// mechanism.
func socks5Authenticate(rw *bufio.ReadWriter, method byte, req *SocksRequest) (err error) {
switch method {
case socksAuthNoneRequired:
// Straight into reading the connect.
case socksAuthUsernamePassword:
if err = socks5AuthRFC1929(rw, req); err != nil {
return
}
case socksAuthNoAcceptableMethods:
err = newTemporaryNetError("socks5Authenticate: SOCKS method select had no compatible methods")
return
default:
err = newTemporaryNetError("socks5Authenticate: SOCKS method select picked a unsupported method 0x%02x", method)
return
}
if err = socksFlushBuffers(rw); err != nil {
err = newTemporaryNetError("socks5Authenticate: Failed to flush buffers: %s", err)
return
}
return
}
// socks5AuthRFC1929 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 socks5AuthRFC1929(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.Reader, "auth version", socksAuthRFC1929Ver); err != nil {
sendErrResp()
err = newTemporaryNetError("socks5AuthRFC1929: %s", err)
return
}
// Read the username.
var ulen byte
if ulen, err = socksReadByte(rw.Reader); err != nil {
err = newTemporaryNetError("socks5AuthRFC1929: Failed to read username length: %s", err)
return
}
if ulen < 1 {
sendErrResp()
err = newTemporaryNetError("socks5AuthRFC1929: username with 0 length")
return
}
var uname []byte
if uname, err = socksReadBytes(rw.Reader, int(ulen)); err != nil {
err = newTemporaryNetError("socks5AuthRFC1929: Failed to read username: %s", err)
return
}
req.Username = string(uname)
// Read the password.
var plen byte
if plen, err = socksReadByte(rw.Reader); err != nil {
err = newTemporaryNetError("socks5AuthRFC1929: Failed to read password length: %s", err)
return
}
if plen < 1 {
sendErrResp()
err = newTemporaryNetError("socks5AuthRFC1929: password with 0 length")
return
}
var passwd []byte
if passwd, err = socksReadBytes(rw.Reader, int(plen)); err != nil {
err = newTemporaryNetError("socks5AuthRFC1929: Failed to read password: %s", err)
return
}
if !(plen == 1 && passwd[0] == 0x00) {
// tor will set the password to 'NUL' if there are no arguments.
req.Password = string(passwd)
}
// [Psiphon]
// Since we don't need pluggable transport parameters and prefer enabling clients to proxy,
// don't parse or validate username/password as PT args.
/*
// 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()
err = newTemporaryNetError("socks5AuthRFC1929: failed to parse client parameters: %s", err)
return
}
*/
// [Psiphon]
// Write success response
resp := []byte{socksAuthRFC1929Ver, socksAuthRFC1929Success}
if _, err = rw.Write(resp[:]); err != nil {
err = newTemporaryNetError("socks5AuthRFC1929: failed to write success response: %s", err)
return
}
return
}
// socks5ReadCommand reads a SOCKS5 client command and parses out the relevant
// fields into a SocksRequest. Only CMD_CONNECT is supported.
func socks5ReadCommand(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.Reader, "version", socks5Version); err != nil {
sendErrResp(SocksRepGeneralFailure)
err = newTemporaryNetError("socks5ReadCommand: %s", err)
return
}
if err = socksReadByteVerify(rw.Reader, "command", socksCmdConnect); err != nil {
sendErrResp(SocksRepCommandNotSupported)
err = newTemporaryNetError("socks5ReadCommand: %s", err)
return
}
if err = socksReadByteVerify(rw.Reader, "reserved", socksReserved); err != nil {
sendErrResp(SocksRepGeneralFailure)
err = newTemporaryNetError("socks5ReadCommand: %s", err)
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.Reader); err != nil {
err = newTemporaryNetError("socks5ReadCommand: Failed to read address type: %s", err)
return
}
var host string
switch atype {
case socksAtypeV4:
var addr []byte
if addr, err = socksReadBytes(rw.Reader, net.IPv4len); err != nil {
err = newTemporaryNetError("socks5ReadCommand: Failed to read IPv4 address: %s", err)
return
}
host = net.IPv4(addr[0], addr[1], addr[2], addr[3]).String()
case socksAtypeDomainName:
var alen byte
if alen, err = socksReadByte(rw.Reader); err != nil {
err = newTemporaryNetError("socks5ReadCommand: Failed to read domain name length: %s", err)
return
}
if alen == 0 {
err = newTemporaryNetError("socks5ReadCommand: SOCKS request had domain name with 0 length")
return
}
var addr []byte
if addr, err = socksReadBytes(rw.Reader, int(alen)); err != nil {
err = newTemporaryNetError("socks5ReadCommand: Failed to read domain name: %s", err)
return
}
host = string(addr)
case socksAtypeV6:
var rawAddr []byte
if rawAddr, err = socksReadBytes(rw.Reader, net.IPv6len); err != nil {
err = newTemporaryNetError("socks5ReadCommand: Failed to read IPv6 address: %s", err)
return
}
addr := make(net.IP, net.IPv6len)
copy(addr[:], rawAddr[:])
host = fmt.Sprintf("[%s]", addr.String())
default:
sendErrResp(SocksRepAddressNotSupported)
err = newTemporaryNetError("socks5ReadCommand: SOCKS request had unsupported address type 0x%02x", atype)
return
}
var rawPort []byte
if rawPort, err = socksReadBytes(rw.Reader, 2); err != nil {
err = newTemporaryNetError("socks5ReadCommand: Failed to read port number: %s", err)
return
}
port := int(rawPort[0])<<8 | int(rawPort[1])<<0
if err = socksFlushBuffers(rw); err != nil {
err = newTemporaryNetError("socks5ReadCommand: Failed to flush buffers: %s", err)
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] = socks5Version
resp[1] = code
resp[2] = socksReserved
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.
if _, err := w.Write(resp[:]); err != nil {
err = newTemporaryNetError("sendSocks5Response: Failed write response: %s", err)
return err
}
return nil
}
// 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)
}
/*
* Common helpers
*/
func socksFlushBuffers(rw *bufio.ReadWriter) error {
if err := rw.Writer.Flush(); err != nil {
return err
}
if err := socksFlushReadBuffer(rw.Reader); err != nil {
return err
}
return nil
}
func socksFlushReadBuffer(r *bufio.Reader) error {
if r.Buffered() > 0 {
return fmt.Errorf("%d bytes left after SOCKS message", r.Buffered())
}
return nil
}
func socksReadByte(r *bufio.Reader) (byte, error) {
return r.ReadByte()
}
func socksReadBytes(r *bufio.Reader, n int) ([]byte, error) {
ret := make([]byte, n)
if _, err := io.ReadFull(r, ret); err != nil {
return nil, err
}
return ret, nil
}
func socksReadByteVerify(r *bufio.Reader, descr string, expected byte) error {
val, err := socksReadByte(r)
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
}
func socksReadBytesUntil(r *bufio.Reader, end byte) ([]byte, error) {
val, err := r.ReadBytes(end)
if err != nil {
return nil, err
}
return val, nil
}
func socksPeekByte(r *bufio.Reader) (b byte, err error) {
var byteSlice []byte
if byteSlice, err = r.Peek(1); err != nil {
return
}
b = byteSlice[0]
return
}
// temporaryNetError is used for our custom errors. All such errors are "temporary";
// that is, the listener doesn't need to be torn down when they occur. They also
// need to implement the net.Error interface.
type temporaryNetError struct {
error
}
// Ensure temporaryNetError implements net.Error
var _ net.Error = temporaryNetError{}
func newTemporaryNetError(errMsg string, args ...interface{}) *temporaryNetError {
return &temporaryNetError{
error: fmt.Errorf(errMsg, args...),
}
}
func (tne temporaryNetError) Timeout() bool {
return false
}
func (tne temporaryNetError) Temporary() bool {
return true
}
/*
* SOCKS4a-specific code
*/
// Read a SOCKS4a connect request. Returns a SocksRequest.
func readSocks4aConnect(r *bufio.Reader) (req SocksRequest, err error) {
// Validate the version.
if err = socksReadByteVerify(r, "version", socks4Version); err != nil {
err = newTemporaryNetError("readSocks4aConnect: %s", err.Error())
return
}
var cmdConnect byte
if cmdConnect, err = socksReadByte(r); err != nil {
err = newTemporaryNetError("readSocks4aConnect: Failed to read connect command: %s", err.Error())
return
}
if cmdConnect != socksCmdConnect {
err = newTemporaryNetError("readSocks4aConnect: SOCKS header had command 0x%02x, not 0x%02x", cmdConnect, socksCmdConnect)
return
}
var rawPort []byte
if rawPort, err = socksReadBytes(r, 2); err != nil {
err = newTemporaryNetError("readSocks4aConnect: Failed to read port: %s", err.Error())
return
}
port := int(rawPort[0])<<8 | int(rawPort[1])<<0
var rawHostIP []byte
if rawHostIP, err = socksReadBytes(r, 4); err != nil {
err = newTemporaryNetError("readSocks4aConnect: Failed to read IP address: %s", err.Error())
return
}
// If there's a hostname, it comes after the username, so we'll wait a bit
// before we process the IP info.
var usernameBytes []byte
usernameBytes, err = socksReadBytesUntil(r, '\x00')
if err != nil {
err = newTemporaryNetError("readSocks4aConnect: Failed to read username: %s", err.Error())
return
}
req.Username = string(usernameBytes[:len(usernameBytes)-1])
// [Psiphon]
// Since we don't need pluggable transport parameters and prefer enabling clients to proxy,
// don't parse or validate username/password as PT args.
/*
req.Args, err = parseClientParameters(req.Username)
if err != nil {
err = newTemporaryNetError("readSocks4aConnect: Failed to parse client parameters: %s", err.Error())
return
}
*/
// [Psiphon]
var host string
if rawHostIP[0] == 0 && rawHostIP[1] == 0 && rawHostIP[2] == 0 && rawHostIP[3] != 0 {
// If the IP is of the form 0.0.0.x (with x nonzero), then a domain name is provided.
var hostBytes []byte
if hostBytes, err = socksReadBytesUntil(r, '\x00'); err != nil {
err = newTemporaryNetError("readSocks4aConnect: Failed to read domain name: %s", err.Error())
return
}
host = string(hostBytes[:len(hostBytes)-1])
} else {
host = net.IPv4(rawHostIP[0], rawHostIP[1], rawHostIP[2], rawHostIP[3]).String()
}
req.Target = fmt.Sprintf("%s:%d", host, port)
if err = socksFlushReadBuffer(r); err != nil {
err = newTemporaryNetError("readSocks4aConnect: Failed to flush buffers: %s", err.Error())
return
}
return
}
// Send a SOCKS4a response with the given code and address. If the IP field
// inside addr is not an IPv4 address, the IP portion of the response will be
// four zero bytes.
func sendSocks4aResponse(w io.Writer, code byte, addr *net.TCPAddr) error {
var resp [8]byte
resp[0] = socks4ResponseVersion
resp[1] = code
resp[2] = byte((addr.Port >> 8) & 0xff)
resp[3] = byte((addr.Port >> 0) & 0xff)
ipv4 := addr.IP.To4()
if ipv4 != nil {
resp[4] = ipv4[0]
resp[5] = ipv4[1]
resp[6] = ipv4[2]
resp[7] = ipv4[3]
}
if _, err := w.Write(resp[:]); err != nil {
err = newTemporaryNetError("sendSocks4aResponse: Failed to write response: %s", err.Error())
return err
}
return nil
}
// Send a SOCKS4a response code 0x5a.
func sendSocks4aResponseGranted(w io.Writer, addr *net.TCPAddr) error {
return sendSocks4aResponse(w, socks4RequestGranted, addr)
}
// Send a SOCKS4a response code 0x5b (with an all-zero address).
func sendSocks4aResponseRejected(w io.Writer) error {
emptyAddr := net.TCPAddr{IP: net.IPv4(0, 0, 0, 0), Port: 0}
return sendSocks4aResponse(w, socks4RequestRejected, &emptyAddr)
}