lnd/torsvc/torsvc.go
nsa e132ad8433 torsvc: Added new module to house Tor functions
This commit adds a new module named 'torsvc' which houses all Tor
functionality in an attempt to isolate it and make it reusable in
other projecs. Some additional tweaks were made to config.go and
to the bootstrapper.
2018-02-05 17:37:46 -08:00

163 lines
4.3 KiB
Go

package torsvc
import (
"fmt"
"net"
"strconv"
"github.com/btcsuite/go-socks/socks"
"github.com/miekg/dns"
"github.com/roasbeef/btcd/connmgr"
"golang.org/x/net/proxy"
)
const (
localhost = "127.0.0.1"
)
var (
// DNS Message Response Codes, 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",
// Left out 16: "Bad OPT Version" because of duplicate keys and
// because miekg/dns does not use this message response code.
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",
}
)
// TorDial returns a go-socks proxy dialer function that can be used to make
// connections to peers through Tor's exposed socks proxy.
func TorDial(socksPort string) func(string, string) (net.Conn, error) {
p := &socks.Proxy{Addr: localhost + ":" + socksPort}
return p.Dial
}
// TorLookupHost performs DNS resolution on a given hostname via Tor's
// native resolver. Only IPv4 addresses are returned.
func TorLookupHost(host, socksPort string) ([]string, error) {
ip, err := connmgr.TorLookupIP(host, localhost+":"+socksPort)
if err != nil {
return nil, err
}
var addrs []string
// Only one IPv4 address is returned by the TorLookupIP function.
addrs = append(addrs, ip[0].String())
return addrs, nil
}
// TorLookupSRV 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
// Tor proxy and connect directly to a DNS server and query it.
// NOTE: TorLookupSRV uses golang's proxy package since go-socks will cause
// the SRV request to hang.
func TorLookupSRV(service, proto, name, socksPort, dnsServer string) (string,
[]*net.SRV, error) {
// _service._proto.name as described in RFC#2782.
host := "_" + service + "._" + proto + "." + name + "."
// Set up golang's proxy dialer - Tor's socks proxy doesn't support
// authentication.
dialer, err := proxy.SOCKS5(
"tcp",
localhost+":"+socksPort,
nil,
proxy.Direct,
)
if err != nil {
return "", nil, err
}
// Dial dnsServer via Tor. dnsServer must have TCP resolution enabled
// for the port we are dialing.
conn, err := dialer.Dial("tcp", dnsServer)
if err != nil {
return "", nil, err
}
// Construct the actual SRV request.
msg := new(dns.Msg)
msg.SetQuestion(host, dns.TypeSRV)
msg.RecursionDesired = true
dnsConn := &dns.Conn{Conn: conn}
defer dnsConn.Close()
// Write the SRV request.
dnsConn.WriteMsg(msg)
// Read the response.
resp, err := dnsConn.ReadMsg()
if err != nil {
return "", nil, err
}
// If the message response code was not the success code, fail.
if resp.Rcode != dns.RcodeSuccess {
return "", nil, fmt.Errorf("Unsuccessful SRV request, "+
"received: %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
}
// TorResolveTCP uses Tor's proxy to resolve TCP addresses instead of the
// system resolver that ResolveTCPAddr and related functions use. This resolver
// only queries DNS servers in the case that a hostname is passed in the
// address parameter. Only TCP resolution is supported.
func TorResolveTCP(address, socksPort 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
}
// Look up the host's IP address via Tor.
ip, err := TorLookupHost(host, socksPort)
if err != nil {
return nil, err
}
// Convert port to an int.
p, err := strconv.Atoi(port)
if err != nil {
return nil, err
}
// Return a *net.TCPAddr exactly like net.ResolveTCPAddr.
return &net.TCPAddr{
IP: net.ParseIP(ip[0]),
Port: p,
}, nil
}