tor.go raw
1 package connmgr
2
3 import (
4 "encoding/binary"
5 "errors"
6 "net"
7 )
8
9 const (
10 torSucceeded = 0x00
11 torGeneralError = 0x01
12 torNotAllowed = 0x02
13 torNetUnreachable = 0x03
14 torHostUnreachable = 0x04
15 torConnectionRefused = 0x05
16 torTTLExpired = 0x06
17 torCmdNotSupported = 0x07
18 torAddrNotSupported = 0x08
19 )
20
21 var (
22 // ErrTorInvalidAddressResponse indicates an invalid address was returned by the Tor DNS resolver.
23 ErrTorInvalidAddressResponse = errors.New("invalid address response")
24 // ErrTorInvalidProxyResponse indicates the Tor proxy returned a response in an unexpected format.
25 ErrTorInvalidProxyResponse = errors.New("invalid proxy response")
26 // ErrTorUnrecognizedAuthMethod indicates the authentication method provided is not recognized.
27 ErrTorUnrecognizedAuthMethod = errors.New("invalid proxy authentication method")
28 torStatusErrors = map[byte]error{
29 torSucceeded: errors.New("tor succeeded"),
30 torGeneralError: errors.New("tor general error"),
31 torNotAllowed: errors.New("tor not allowed"),
32 torNetUnreachable: errors.New("tor network is unreachable"),
33 torHostUnreachable: errors.New("tor host is unreachable"),
34 torConnectionRefused: errors.New("tor connection refused"),
35 torTTLExpired: errors.New("tor TTL expired"),
36 torCmdNotSupported: errors.New("tor command not supported"),
37 torAddrNotSupported: errors.New("tor address type not supported"),
38 }
39 )
40
41 // TorLookupIP uses Tor to resolve DNS via the SOCKS extension they provide for resolution over the Tor network. Tor
42 // itself doesn't support ipv6 so this doesn't either.
43 func TorLookupIP(host, proxy string) ([]net.IP, error) {
44 conn, e := net.Dial("tcp", proxy)
45 if e != nil {
46 return nil, e
47 }
48 defer func() {
49 if e = conn.Close(); E.Chk(e) {
50 }
51 }()
52 buf := []byte{'\x05', '\x01', '\x00'}
53 _, e = conn.Write(buf)
54 if e != nil {
55 return nil, e
56 }
57 buf = make([]byte, 2)
58 _, e = conn.Read(buf)
59 if e != nil {
60 return nil, e
61 }
62 if buf[0] != '\x05' {
63 return nil, ErrTorInvalidProxyResponse
64 }
65 if buf[1] != '\x00' {
66 return nil, ErrTorUnrecognizedAuthMethod
67 }
68 buf = make([]byte, 7+len(host))
69 buf[0] = 5 // protocol version
70 buf[1] = '\xF0' // Tor Resolve
71 buf[2] = 0 // reserved
72 buf[3] = 3 // Tor Resolve
73 buf[4] = byte(len(host))
74 copy(buf[5:], host)
75 buf[5+len(host)] = 0 // Port 0
76 _, e = conn.Write(buf)
77 if e != nil {
78 return nil, e
79 }
80 buf = make([]byte, 4)
81 _, e = conn.Read(buf)
82 if e != nil {
83 return nil, e
84 }
85 if buf[0] != 5 {
86 return nil, ErrTorInvalidProxyResponse
87 }
88 if buf[1] != 0 {
89 if int(buf[1]) >= len(torStatusErrors) {
90 return nil, ErrTorInvalidProxyResponse
91 } else if e = torStatusErrors[buf[1]]; E.Chk(e) {
92 return nil, e
93 }
94 return nil, ErrTorInvalidProxyResponse
95 }
96 if buf[3] != 1 {
97 e = torStatusErrors[torGeneralError]
98 return nil, e
99 }
100 buf = make([]byte, 4)
101 bytes, e := conn.Read(buf)
102 if e != nil {
103 return nil, e
104 }
105 if bytes != 4 {
106 return nil, ErrTorInvalidAddressResponse
107 }
108 r := binary.BigEndian.Uint32(buf)
109 addr := make([]net.IP, 1)
110 addr[0] = net.IPv4(byte(r>>24), byte(r>>16), byte(r>>8), byte(r))
111 return addr, nil
112 }
113