From 1e836d26f4d3620ed73b4369a42793e0af9f5749 Mon Sep 17 00:00:00 2001 From: "Owain G. Ainsworth" Date: Thu, 28 Nov 2013 00:11:16 +0000 Subject: [PATCH] Handle tor and dns-name addresses. Perform the requisite processing on .onion addresses to turn them into the tor reserved ipv6 region (the same as bitcoind and onioncat). Furthermore, when printing an ip address, reverse the conversion so we print it nicely. base32 as standard is uppercase, but tor and bitcoind seem to use lowercase so we first must for we force .onion addrs to uppercase (and to lowercase on the reverse). As a side effect we now should handle dns names on the command line (via tor if required) and add them to the addressmanger as necessary. --- addrmanager.go | 54 +++++++++++++++++++++++++++++++++++++++++++++++++- peer.go | 10 ++++++++-- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/addrmanager.go b/addrmanager.go index 4febb107..bfc19fbb 100644 --- a/addrmanager.go +++ b/addrmanager.go @@ -8,6 +8,7 @@ import ( "bytes" "container/list" crand "crypto/rand" // for seeding + "encoding/base32" "encoding/binary" "encoding/json" "fmt" @@ -19,6 +20,7 @@ import ( "os" "path/filepath" "strconv" + "strings" "sync" "sync/atomic" "time" @@ -753,11 +755,61 @@ func NewAddrManager() *AddrManager { return &am } +// hostToNetAddress returns a netaddress given a host address. If the address is +// a tor .onion address this will be taken care of. else if the host is not an +// IP address it will be resolved (via tor if required). +func hostToNetAddress(host string, port uint16, services btcwire.ServiceFlag) (*btcwire.NetAddress, error) { + // tor address is 16 char base32 + ".onion" + var ip net.IP + if len(host) == 22 && host[16:] == ".onion" { + // go base32 encoding uses capitals (as does the rfc + // but tor and bitcoind tend to user lowercase, so we switch + // case here. + data, err := base32.StdEncoding.DecodeString( + strings.ToUpper(host[:16])) + if err != nil { + return nil, err + } + prefix := []byte{0xfd, 0x87, 0xd8, 0x7e, 0xeb, 0x43} + ip = net.IP(append(prefix, data...)) + } else if ip = net.ParseIP(host); ip == nil { + var err error + var ips []net.IP + if cfg.Proxy != "" { + ips, err = torLookupIP(host, cfg.Proxy) + } else { + ips, err = net.LookupIP(host) + } + if err != nil { + return nil, err + } + if len(ips) == 0 { + return nil, fmt.Errorf("No addresses found for %s", host) + } + ip = ips[0] + } + + return btcwire.NewNetAddressIPPort(ip, port, services), nil +} + +// ipString returns a string for the ip from the provided NetAddress. If the +// ip is in the range used for tor addresses then it will be transformed into +// the relavent .onion address. +func ipString(na *btcwire.NetAddress) string { + if Tor(na) { + // We know now that na.IP is long enogh. + base32 := base32.StdEncoding.EncodeToString(na.IP[6:]) + return strings.ToLower(base32) + ".onion" + } else { + return na.IP.String() + } +} + // NetAddressKey returns a string key in the form of ip:port for IPv4 addresses // or [ip]:port for IPv6 addresses. func NetAddressKey(na *btcwire.NetAddress) string { port := strconv.FormatUint(uint64(na.Port), 10) - addr := net.JoinHostPort(na.IP.String(), port) + addr := net.JoinHostPort(ipString(na), port) return addr } diff --git a/peer.go b/peer.go index 4e3b8bf6..45f072a0 100644 --- a/peer.go +++ b/peer.go @@ -1325,7 +1325,7 @@ func newOutboundPeer(s *server, addr string, persistent bool) *peer { // which case we return nil to be handled by the caller. This must be // done before we fork off the goroutine because as soon as this // function returns the peer must have a valid netaddress. - ip, portStr, err := net.SplitHostPort(addr) + host, portStr, err := net.SplitHostPort(addr) if err != nil { p.logError("Tried to create a new outbound peer with invalid "+ "address %s: %v", addr, err) @@ -1338,7 +1338,13 @@ func newOutboundPeer(s *server, addr string, persistent bool) *peer { "port %s: %v", portStr, err) return nil } - p.na = btcwire.NewNetAddressIPPort(net.ParseIP(ip), uint16(port), 0) + + p.na, err = hostToNetAddress(host, uint16(port), 0) + if err != nil { + p.logError("Can not turn host %s into netaddress: %v", + host, err) + return nil + } go func() { // Select which dial method to call depending on whether or