-
Notifications
You must be signed in to change notification settings - Fork 2.1k
/
tor.go
343 lines (296 loc) · 9.95 KB
/
tor.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
package tor
import (
"bytes"
"crypto/rand"
"encoding/hex"
"fmt"
"net"
"strconv"
"time"
"github.com/btcsuite/btcd/connmgr"
"github.com/miekg/dns"
"golang.org/x/net/proxy"
)
var (
// dnsCodes maps the DNS response codes to a friendly description. This
// does not include the BADVERS code because of duplicate keys and the
// underlying DNS (miekg/dns) package not using it. For more info, see
// https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml.
dnsCodes = map[int]string{
0: "no error",
1: "format error",
2: "server failure",
3: "non-existent domain",
4: "not implemented",
5: "query refused",
6: "name exists when it should not",
7: "RR set exists when it should not",
8: "RR set that should exist does not",
9: "server not authoritative for zone",
10: "name not contained in zone",
16: "TSIG signature failure",
17: "key not recognized",
18: "signature out of time window",
19: "bad TKEY mode",
20: "duplicate key name",
21: "algorithm not supported",
22: "bad truncation",
23: "bad/missing server cookie",
}
// onionPrefixBytes is a special purpose IPv6 prefix to encode Onion v2
// addresses with. Because Neutrino uses the address manager of btcd
// which only understands net.IP addresses instead of net.Addr, we need
// to convert any .onion addresses into fake IPv6 addresses if we want
// to use a Tor hidden service as a Neutrino backend. This is the same
// range used by OnionCat, which is part part of the RFC4193 unique
// local IPv6 unicast address range.
onionPrefixBytes = []byte{0xfd, 0x87, 0xd8, 0x7e, 0xeb, 0x43}
)
// proxyConn is a wrapper around net.Conn that allows us to expose the actual
// remote address we're dialing, rather than the proxy's address.
type proxyConn struct {
net.Conn
remoteAddr net.Addr
}
func (c *proxyConn) RemoteAddr() net.Addr {
return c.remoteAddr
}
// Dial is a wrapper over the non-exported dial function that returns a wrapper
// around net.Conn in order to expose the actual remote address we're dialing,
// rather than the proxy's address.
func Dial(address, socksAddr string, streamIsolation bool,
skipProxyForClearNetTargets bool,
timeout time.Duration) (net.Conn, error) {
conn, err := dialProxy(
address, socksAddr, streamIsolation,
skipProxyForClearNetTargets, timeout,
)
if err != nil {
return nil, fmt.Errorf("dial proxy failed: %w", err)
}
// Now that the connection is established, we'll create our internal
// proxyConn that will serve in populating the correct remote address
// of the connection, rather than using the proxy's address.
remoteAddr, err := ParseAddr(address, socksAddr)
if err != nil {
return nil, err
}
return &proxyConn{
Conn: conn,
remoteAddr: remoteAddr,
}, nil
}
// dialProxy establishes a connection to the address via the provided TOR SOCKS
// proxy. Only TCP traffic may be routed via Tor.
//
// streamIsolation determines if we should force stream isolation for this new
// connection. If enabled, new connections will use a fresh circuit, rather than
// possibly re-using an existing circuit.
//
// skipProxyForClearNetTargets argument allows the dialer to directly connect
// to the provided address if it does not represent an union service, skipping
// the SOCKS proxy.
func dialProxy(address, socksAddr string, streamIsolation bool,
skipProxyForClearNetTargets bool,
timeout time.Duration) (net.Conn, error) {
// If we were requested to force stream isolation for this connection,
// we'll populate the authentication credentials with random data as
// Tor will create a new circuit for each set of credentials.
var auth *proxy.Auth
if streamIsolation {
var b [16]byte
if _, err := rand.Read(b[:]); err != nil {
return nil, err
}
auth = &proxy.Auth{
User: hex.EncodeToString(b[:8]),
Password: hex.EncodeToString(b[8:]),
}
}
clearDialer := &net.Dialer{Timeout: timeout}
if skipProxyForClearNetTargets {
host, _, err := net.SplitHostPort(address)
if err != nil {
return nil, err
}
// The SOCKS proxy is skipped if the target
// is not an union address.
if !IsOnionHost(host) {
return clearDialer.Dial("tcp", address)
}
}
// Establish the connection through Tor's SOCKS proxy.
dialer, err := proxy.SOCKS5("tcp", socksAddr, auth, clearDialer)
if err != nil {
return nil, fmt.Errorf("establish sock proxy: %w", err)
}
return dialer.Dial("tcp", address)
}
// LookupHost performs DNS resolution on a given host via Tor's native resolver.
// Only IPv4 addresses are returned.
func LookupHost(host, socksAddr string) ([]string, error) {
ip, err := connmgr.TorLookupIP(host, socksAddr)
if err != nil {
return nil, err
}
// Only one IPv4 address is returned by the TorLookupIP function.
return []string{ip[0].String()}, nil
}
// LookupSRV uses Tor's SOCKS proxy to route DNS SRV queries. Tor does not
// natively support SRV queries so we must route all SRV queries through the
// proxy by connecting directly to a DNS server and querying it. The DNS server
// must have TCP resolution enabled for the given port.
func LookupSRV(service, proto, name, socksAddr,
dnsServer string, streamIsolation bool, skipProxyForClearNetTargets bool,
timeout time.Duration) (string, []*net.SRV, error) {
// Connect to the DNS server we'll be using to query SRV records.
conn, err := dialProxy(
dnsServer, socksAddr, streamIsolation,
skipProxyForClearNetTargets, timeout,
)
if err != nil {
return "", nil, err
}
dnsConn := &dns.Conn{Conn: conn}
defer dnsConn.Close()
// Once connected, we'll construct the SRV request for the host
// following the format _service._proto.name. as described in RFC #2782.
host := fmt.Sprintf("_%s._%s.%s.", service, proto, name)
msg := new(dns.Msg).SetQuestion(host, dns.TypeSRV)
// Send the request to the DNS server and read its response.
if err := dnsConn.WriteMsg(msg); err != nil {
return "", nil, err
}
resp, err := dnsConn.ReadMsg()
if err != nil {
return "", nil, err
}
// We'll fail if we were unable to query the DNS server for our record.
if resp.Rcode != dns.RcodeSuccess {
return "", nil, fmt.Errorf("unable to query for SRV records: "+
"%s", dnsCodes[resp.Rcode])
}
// Retrieve the RR(s) of the Answer section.
var rrs []*net.SRV
for _, rr := range resp.Answer {
srv := rr.(*dns.SRV)
rrs = append(rrs, &net.SRV{
Target: srv.Target,
Port: srv.Port,
Priority: srv.Priority,
Weight: srv.Weight,
})
}
return "", rrs, nil
}
// ResolveTCPAddr uses Tor's proxy to resolve TCP addresses instead of the
// standard system resolver provided in the `net` package.
func ResolveTCPAddr(address, socksAddr string) (*net.TCPAddr, error) {
// Split host:port since the lookup function does not take a port.
host, port, err := net.SplitHostPort(address)
if err != nil {
return nil, err
}
ip, err := LookupHost(host, socksAddr)
if err != nil {
return nil, err
}
p, err := strconv.Atoi(port)
if err != nil {
return nil, err
}
return &net.TCPAddr{
IP: net.ParseIP(ip[0]),
Port: p,
}, nil
}
// ParseAddr parses an address from its string format to a net.Addr.
func ParseAddr(address, socksAddr string) (net.Addr, error) {
host, portStr, err := net.SplitHostPort(address)
if err != nil {
return nil, err
}
port, err := strconv.Atoi(portStr)
if err != nil {
return nil, err
}
if IsOnionHost(host) {
return &OnionAddr{OnionService: host, Port: port}, nil
}
return ResolveTCPAddr(address, socksAddr)
}
// IsOnionHost determines whether a host is part of an onion address.
func IsOnionHost(host string) bool {
// Note the starting index of the onion suffix in the host depending
// on its length.
var suffixIndex int
switch len(host) {
case V2Len:
suffixIndex = V2Len - OnionSuffixLen
case V3Len:
suffixIndex = V3Len - OnionSuffixLen
default:
return false
}
// Make sure the host ends with the ".onion" suffix.
if host[suffixIndex:] != OnionSuffix {
return false
}
// We'll now attempt to decode the host without its suffix, as the
// suffix includes invalid characters. This will tell us if the host is
// actually valid if successful.
host = host[:suffixIndex]
if _, err := Base32Encoding.DecodeString(host); err != nil {
return false
}
return true
}
// IsOnionFakeIP checks whether a given net.Addr is a fake IPv6 address that
// encodes an Onion v2 address.
func IsOnionFakeIP(addr net.Addr) bool {
_, err := FakeIPToOnionHost(addr)
return err == nil
}
// OnionHostToFakeIP encodes an Onion v2 address into a fake IPv6 address that
// encodes the same information but can be used for libraries that operate on an
// IP address base only, like btcd's address manager. For example, this will
// turn the onion host ld47qlr6h2b7hrrf.onion into the ip6 address
// fd87:d87e:eb43:58f9:f82e:3e3e:83f3:c625.
func OnionHostToFakeIP(host string) (net.IP, error) {
if len(host) != V2Len {
return nil, fmt.Errorf("invalid onion v2 host: %v", host)
}
data, err := Base32Encoding.DecodeString(host[:V2Len-OnionSuffixLen])
if err != nil {
return nil, err
}
ip := make([]byte, len(onionPrefixBytes)+len(data))
copy(ip, onionPrefixBytes)
copy(ip[len(onionPrefixBytes):], data)
return ip, nil
}
// FakeIPToOnionHost turns a fake IPv6 address that encodes an Onion v2 address
// back into its onion host address representation. For example, this will turn
// the fake tcp6 address [fd87:d87e:eb43:58f9:f82e:3e3e:83f3:c625]:8333 back
// into ld47qlr6h2b7hrrf.onion:8333.
func FakeIPToOnionHost(fakeIP net.Addr) (net.Addr, error) {
tcpAddr, ok := fakeIP.(*net.TCPAddr)
if !ok {
return nil, fmt.Errorf("invalid fake onion IP address: %v",
fakeIP)
}
ip := tcpAddr.IP
if len(ip) != len(onionPrefixBytes)+V2DecodedLen {
return nil, fmt.Errorf("invalid fake onion IP address length: "+
"%v", fakeIP)
}
if !bytes.Equal(ip[:len(onionPrefixBytes)], onionPrefixBytes) {
return nil, fmt.Errorf("invalid fake onion IP address prefix: "+
"%v", fakeIP)
}
host := Base32Encoding.EncodeToString(ip[len(onionPrefixBytes):])
return &OnionAddr{
OnionService: host + ".onion",
Port: tcpAddr.Port,
}, nil
}