[NOD-503] Remove Tor functionality (#573)

This commit is contained in:
Ori Newman 2020-01-08 10:45:27 +02:00 committed by Svarog
parent cd719b1d5b
commit d46857677f
12 changed files with 26 additions and 395 deletions

View File

@ -7,7 +7,6 @@ package addrmgr
import (
"container/list"
crand "crypto/rand" // for seeding
"encoding/base32"
"encoding/binary"
"encoding/json"
"github.com/pkg/errors"
@ -17,7 +16,6 @@ import (
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
@ -894,24 +892,11 @@ func (a *AddrManager) reset() {
a.addrTrying = make(map[*KnownAddress]bool)
}
// 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).
// HostToNetAddress returns a netaddress given a host address. If
// the host is not an IP address it will be resolved.
func (a *AddrManager) HostToNetAddress(host string, port uint16, services wire.ServiceFlag) (*wire.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 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 {
ip := net.ParseIP(host)
if ip == nil {
ips, err := a.lookupFunc(host)
if err != nil {
return nil, err
@ -925,25 +910,12 @@ func (a *AddrManager) HostToNetAddress(host string, port uint16, services wire.S
return wire.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 relevant .onion address.
func ipString(na *wire.NetAddress) string {
if IsOnionCatTor(na) {
// We know now that na.IP is long enough.
base32 := base32.StdEncoding.EncodeToString(na.IP[6:])
return strings.ToLower(base32) + ".onion"
}
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 *wire.NetAddress) string {
port := strconv.FormatUint(uint64(na.Port), 10)
return net.JoinHostPort(ipString(na), port)
return net.JoinHostPort(na.IP.String(), port)
}
// GetAddress returns a single address that should be routable. It picks a
@ -1287,18 +1259,6 @@ func getReachabilityFrom(localAddr, remoteAddr *wire.NetAddress) int {
return Unreachable
}
if IsOnionCatTor(remoteAddr) {
if IsOnionCatTor(localAddr) {
return Private
}
if IsRoutable(localAddr) && IsIPv4(localAddr) {
return Ipv4
}
return Default
}
if IsRFC4380(remoteAddr) {
if !IsRoutable(localAddr) {
return Default
@ -1376,7 +1336,7 @@ func (a *AddrManager) GetBestLocalAddress(remoteAddr *wire.NetAddress) *wire.Net
// Send something unroutable if nothing suitable.
var ip net.IP
if !IsIPv4(remoteAddr) && !IsOnionCatTor(remoteAddr) {
if !IsIPv4(remoteAddr) {
ip = net.IPv6zero
} else {
ip = net.IPv4zero

View File

@ -24,11 +24,11 @@ generally helps provide greater peer diversity, and perhaps more importantly,
drastically reduces the chances an attacker is able to coerce your peer into
only connecting to nodes they control.
The address manager also understands routability and Tor addresses and tries
hard to only return routable addresses. In addition, it uses the information
provided by the caller about connected, known good, and attempted addresses to
periodically purge peers which no longer appear to be good peers as well as
bias the selection toward known good peers. The general idea is to make a best
effort at only providing usable addresses.
The address manager also understands routability and tries hard to only return
routable addresses. In addition, it uses the information provided by the caller
about connected, known good, and attempted addresses to periodically purge
peers which no longer appear to be good peers as well as bias the selection
toward known good peers. The general idea is to make a best effort at only
providing usable addresses.
*/
package addrmgr

View File

@ -5,7 +5,6 @@
package addrmgr
import (
"fmt"
"net"
"github.com/kaspanet/kaspad/config"
@ -73,19 +72,6 @@ var (
// rfc6598Net specifies the IPv4 block as defined by RFC6598 (100.64.0.0/10)
rfc6598Net = ipNet("100.64.0.0", 10, 32)
// onionCatNet defines the IPv6 address block used to support Tor.
// We encode a .onion address as a 16 byte number by decoding the
// address prior to the .onion (i.e. the key hash) base32 into a ten
// byte number. It then stores the first 6 bytes of the address as
// 0xfd, 0x87, 0xd8, 0x7e, 0xeb, 0x43.
//
// This is the same range used by OnionCat, which is part part of the
// RFC4193 unique local IPv6 range.
//
// In summary the format is:
// { magic 6 bytes, 10 bytes base32 decode of key hash }
onionCatNet = ipNet("fd87:d87e:eb43::", 48, 128)
// zero4Net defines the IPv4 address block for address staring with 0
// (0.0.0.0/8).
zero4Net = ipNet("0.0.0.0", 8, 32)
@ -111,14 +97,6 @@ func IsLocal(na *wire.NetAddress) bool {
return na.IP.IsLoopback() || zero4Net.Contains(na.IP)
}
// IsOnionCatTor returns whether or not the passed address is in the IPv6 range
// used by Kaspa to support Tor (fd87:d87e:eb43::/48). Note that this range
// is the same range used by OnionCat, which is part of the RFC4193 unique local
// IPv6 range.
func IsOnionCatTor(na *wire.NetAddress) bool {
return onionCatNet.Contains(na.IP)
}
// IsRFC1918 returns whether or not the passed address is part of the IPv4
// private network address space as defined by RFC1918 (10.0.0.0/8,
// 172.16.0.0/12, or 192.168.0.0/16).
@ -232,13 +210,12 @@ func IsRoutable(na *wire.NetAddress) bool {
return IsValid(na) && !(IsRFC1918(na) || IsRFC2544(na) ||
IsRFC3927(na) || IsRFC4862(na) || IsRFC3849(na) ||
IsRFC4843(na) || IsRFC5737(na) || IsRFC6598(na) ||
IsLocal(na) || (IsRFC4193(na) && !IsOnionCatTor(na)))
IsLocal(na) || (IsRFC4193(na)))
}
// GroupKey returns a string representing the network group an address is part
// of. This is the /16 for IPv4, the /32 (/36 for he.net) for IPv6, the string
// "local" for a local address, the string "tor:key" where key is the /4 of the
// onion address for Tor address, and the string "unroutable" for an unroutable
// "local" for a local address, and the string "unroutable" for an unroutable
// address.
func GroupKey(na *wire.NetAddress) string {
if IsLocal(na) {
@ -270,10 +247,6 @@ func GroupKey(na *wire.NetAddress) string {
}
return ip.Mask(net.CIDRMask(16, 32)).String()
}
if IsOnionCatTor(na) {
// group is keyed off the first 4 bits of the actual onion key.
return fmt.Sprintf("tor:%d", na.IP[6]&((1<<4)-1))
}
// OK, so now we know ourselves to be a IPv6 address.
// We use /32 for everything, except for Hurricane Electric's

View File

@ -202,9 +202,9 @@ func TestGroupKey(t *testing.T) {
{name: "ipv6 rfc6145 translated ipv4", ip: "::ffff:0:0c01:0203", expected: "12.1.0.0"},
// Tor.
{name: "ipv6 tor onioncat", ip: "fd87:d87e:eb43:1234::5678", expected: "tor:2"},
{name: "ipv6 tor onioncat 2", ip: "fd87:d87e:eb43:1245::6789", expected: "tor:2"},
{name: "ipv6 tor onioncat 3", ip: "fd87:d87e:eb43:1345::6789", expected: "tor:3"},
{name: "ipv6 tor onioncat", ip: "fd87:d87e:eb43:1234::5678", expected: "unroutable"},
{name: "ipv6 tor onioncat 2", ip: "fd87:d87e:eb43:1245::6789", expected: "unroutable"},
{name: "ipv6 tor onioncat 3", ip: "fd87:d87e:eb43:1345::6789", expected: "unroutable"},
// IPv6 normal.
{name: "ipv6 normal", ip: "2602:100::1", expected: "2602:100::"},

View File

@ -125,11 +125,6 @@ type Flags struct {
Proxy string `long:"proxy" description:"Connect via SOCKS5 proxy (eg. 127.0.0.1:9050)"`
ProxyUser string `long:"proxyuser" description:"Username for proxy server"`
ProxyPass string `long:"proxypass" default-mask:"-" description:"Password for proxy server"`
OnionProxy string // DISABLED UNTIL DECISION ABOUT TOR `long:"onion" description:"Connect to tor hidden services via SOCKS5 proxy (eg. 127.0.0.1:9050)"`
OnionProxyUser string // DISABLED UNTIL DECISION ABOUT TOR `long:"onionuser" description:"Username for onion proxy server"`
OnionProxyPass string // DISABLED UNTIL DECISION ABOUT TOR `long:"onionpass" default-mask:"-" description:"Password for onion proxy server"`
NoOnion bool // DISABLED UNTIL DECISION ABOUT TOR `long:"noonion" description:"Disable connecting to tor hidden services"`
TorIsolation bool // DISABLED UNTIL DECISION ABOUT TOR `long:"torisolation" description:"Enable Tor stream isolation by randomizing user credentials for each connection."`
DbType string `long:"dbtype" description:"Database backend to use for the Block DAG"`
Profile string `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65536"`
CPUProfile string `long:"cpuprofile" description:"Write CPU profile to the specified file"`
@ -163,7 +158,6 @@ type Flags struct {
type Config struct {
*Flags
Lookup func(string) ([]net.IP, error)
OnionDial func(string, string, time.Duration) (net.Conn, error)
Dial func(string, string, time.Duration) (net.Conn, error)
MiningAddrs []util.Address
MinRelayTxFee util.Amount
@ -779,31 +773,11 @@ func loadConfig() (*Config, []string, error) {
activeConfig.ConnectPeers = network.NormalizeAddresses(activeConfig.ConnectPeers,
activeConfig.NetParams().DefaultPort)
// --noonion and --onion do not mix.
if activeConfig.NoOnion && activeConfig.OnionProxy != "" {
err := errors.Errorf("%s: the --noonion and --onion options may "+
"not be activated at the same time", funcName)
fmt.Fprintln(os.Stderr, err)
fmt.Fprintln(os.Stderr, usageMessage)
return nil, nil, err
}
// Tor stream isolation requires either proxy or onion proxy to be set.
if activeConfig.TorIsolation && activeConfig.Proxy == "" && activeConfig.OnionProxy == "" {
str := "%s: Tor stream isolation requires either proxy or " +
"onionproxy to be set"
err := errors.Errorf(str, funcName)
fmt.Fprintln(os.Stderr, err)
fmt.Fprintln(os.Stderr, usageMessage)
return nil, nil, err
}
// Setup dial and DNS resolution (lookup) functions depending on the
// specified options. The default is to use the standard
// net.DialTimeout function as well as the system DNS resolver. When a
// proxy is specified, the dial function is set to the proxy specific
// dial function and the lookup is set to use tor (unless --noonion is
// specified in which case the system DNS resolver is used).
// dial function.
activeConfig.Dial = net.DialTimeout
activeConfig.Lookup = net.LookupIP
if activeConfig.Proxy != "" {
@ -816,90 +790,12 @@ func loadConfig() (*Config, []string, error) {
return nil, nil, err
}
// Tor isolation flag means proxy credentials will be overridden
// unless there is also an onion proxy configured in which case
// that one will be overridden.
torIsolation := false
if activeConfig.TorIsolation && activeConfig.OnionProxy == "" &&
(activeConfig.ProxyUser != "" || activeConfig.ProxyPass != "") {
torIsolation = true
fmt.Fprintln(os.Stderr, "Tor isolation set -- "+
"overriding specified proxy user credentials")
}
proxy := &socks.Proxy{
Addr: activeConfig.Proxy,
Username: activeConfig.ProxyUser,
Password: activeConfig.ProxyPass,
TorIsolation: torIsolation,
Addr: activeConfig.Proxy,
Username: activeConfig.ProxyUser,
Password: activeConfig.ProxyPass,
}
activeConfig.Dial = proxy.DialTimeout
// Treat the proxy as tor and perform DNS resolution through it
// unless the --noonion flag is set or there is an
// onion-specific proxy configured.
if !activeConfig.NoOnion && activeConfig.OnionProxy == "" {
activeConfig.Lookup = func(host string) ([]net.IP, error) {
return network.TorLookupIP(host, activeConfig.Proxy)
}
}
}
// Setup onion address dial function depending on the specified options.
// The default is to use the same dial function selected above. However,
// when an onion-specific proxy is specified, the onion address dial
// function is set to use the onion-specific proxy while leaving the
// normal dial function as selected above. This allows .onion address
// traffic to be routed through a different proxy than normal traffic.
if activeConfig.OnionProxy != "" {
_, _, err := net.SplitHostPort(activeConfig.OnionProxy)
if err != nil {
str := "%s: Onion proxy address '%s' is invalid: %s"
err := errors.Errorf(str, funcName, activeConfig.OnionProxy, err)
fmt.Fprintln(os.Stderr, err)
fmt.Fprintln(os.Stderr, usageMessage)
return nil, nil, err
}
// Tor isolation flag means onion proxy credentials will be
// overridden.
if activeConfig.TorIsolation &&
(activeConfig.OnionProxyUser != "" || activeConfig.OnionProxyPass != "") {
fmt.Fprintln(os.Stderr, "Tor isolation set -- "+
"overriding specified onionproxy user "+
"credentials ")
}
activeConfig.OnionDial = func(network, addr string, timeout time.Duration) (net.Conn, error) {
proxy := &socks.Proxy{
Addr: activeConfig.OnionProxy,
Username: activeConfig.OnionProxyUser,
Password: activeConfig.OnionProxyPass,
TorIsolation: activeConfig.TorIsolation,
}
return proxy.DialTimeout(network, addr, timeout)
}
// When configured in bridge mode (both --onion and --proxy are
// configured), it means that the proxy configured by --proxy is
// not a tor proxy, so override the DNS resolution to use the
// onion-specific proxy.
if activeConfig.Proxy != "" {
activeConfig.Lookup = func(host string) ([]net.IP, error) {
return network.TorLookupIP(host, activeConfig.OnionProxy)
}
}
} else {
activeConfig.OnionDial = activeConfig.Dial
}
// Specifying --noonion means the onion address dial function results in
// an error.
if activeConfig.NoOnion {
activeConfig.OnionDial = func(a, b string, t time.Duration) (net.Conn, error) {
return nil, errors.New("tor has been disabled")
}
}
// Warn about missing config file only after all other configuration is

View File

@ -10,7 +10,7 @@ Package connmgr implements a generic Kaspa network connection manager.
Connection Manager handles all the general connection concerns such as
maintaining a set number of outbound connections, sourcing peers, banning,
limiting max connections, tor lookup, etc.
limiting max connections, etc.
The package provides a generic connection manager which is able to accept
connection requests from a source or a set of given addresses, dial them and

View File

@ -5,7 +5,7 @@ Connection Manager Overview
Connection Manager handles all the general connection concerns such as
maintaining a set number of outbound connections, sourcing peers, banning,
limiting max connections, tor lookup, etc.
limiting max connections, etc.
The package provides a generic connection manager which is able to accept
connection requests from a source or a set of given addresses, dial them and

View File

@ -28,24 +28,6 @@
; proxyuser=
; proxypass=
; The SOCKS5 proxy above is assumed to be Tor (https://www.torproject.org).
; If the proxy is not tor the following may be used to prevent using tor
; specific SOCKS queries to lookup addresses (this increases anonymity when tor
; is used by preventing your IP being leaked via DNS).
; noonion=1
; Use an alternative proxy to connect to .onion addresses. The proxy is assumed
; to be a Tor node. Non .onion addresses will be contacted with the main proxy
; or without a proxy if none is set.
; onion=127.0.0.1:9051
; onionuser=
; onionpass=
; Enable Tor stream isolation by randomizing proxy user credentials resulting in
; Tor creating a new circuit for each connection. This makes it more difficult
; to correlate connections.
; torisolation=1
; Use Universal Plug and Play (UPnP) to automatically open the listen port
; and obtain the external IP address from supported devices. NOTE: This option
; will have no effect if exernal IP addresses are specified.

View File

@ -68,28 +68,6 @@ var (
userAgentVersion = version.Version()
)
// onionAddr implements the net.Addr interface and represents a tor address.
type onionAddr struct {
addr string
}
// String returns the onion address.
//
// This is part of the net.Addr interface.
func (oa *onionAddr) String() string {
return oa.addr
}
// Network returns "onion".
//
// This is part of the net.Addr interface.
func (oa *onionAddr) Network() string {
return "onion"
}
// Ensure onionAddr implements the net.Addr interface.
var _ net.Addr = (*onionAddr)(nil)
// simpleAddr implements the net.Addr interface with two struct fields
type simpleAddr struct {
net, addr string
@ -1899,16 +1877,6 @@ func addrStringToNetAddr(addr string) (net.Addr, error) {
}, nil
}
// Tor addresses cannot be resolved to an IP, so just return an onion
// address instead.
if strings.HasSuffix(host, ".onion") {
if config.ActiveConfig().NoOnion {
return nil, errors.New("tor has been disabled")
}
return &onionAddr{addr: addr}, nil
}
// Attempt to look up an IP address associated with the parsed host.
ips, err := serverutils.KaspadLookup(host)
if err != nil {

View File

@ -5,7 +5,6 @@ import (
"github.com/kaspanet/kaspad/rpcmodel"
"github.com/kaspanet/kaspad/server/serverutils"
"net"
"strings"
)
// handleGetManualNodeInfo handles getManualNodeInfo commands.
@ -77,7 +76,7 @@ func getManualNodesInfo(s *Server, detailsArg *bool, node string) (interface{},
var ipList []string
switch {
case net.ParseIP(host) != nil, strings.HasSuffix(host, ".onion"):
case net.ParseIP(host) != nil:
ipList = make([]string, 1)
ipList[0] = host
default:

View File

@ -1,11 +1,9 @@
package serverutils
import (
"github.com/pkg/errors"
"io/ioutil"
"net"
"os"
"strings"
"sync"
"time"
@ -34,17 +32,8 @@ type Peer struct {
}
// KaspadLookup resolves the IP of the given host using the correct DNS lookup
// function depending on the configuration options. For example, addresses will
// be resolved using tor when the --proxy flag was specified unless --noonion
// was also specified in which case the normal system DNS resolver will be used.
//
// Any attempt to resolve a tor address (.onion) will return an error since they
// are not intended to be resolved outside of the tor proxy.
// function depending on the configuration options.
func KaspadLookup(host string) ([]net.IP, error) {
if strings.HasSuffix(host, ".onion") {
return nil, errors.Errorf("attempt to resolve tor address %s", host)
}
return config.ActiveConfig().Lookup(host)
}
@ -73,14 +62,7 @@ func GenCertPair(certFile, keyFile string) error {
}
// KaspadDial connects to the address on the named network using the appropriate
// dial function depending on the address and configuration options. For
// example, .onion addresses will be dialed using the onion specific proxy if
// one was specified, but will otherwise use the normal dial function (which
// could itself use a proxy or not).
// dial function depending on the address and configuration options.
func KaspadDial(addr net.Addr) (net.Conn, error) {
if strings.Contains(addr.String(), ".onion:") {
return config.ActiveConfig().OnionDial(addr.Network(), addr.String(),
config.DefaultConnectTimeout)
}
return config.ActiveConfig().Dial(addr.Network(), addr.String(), config.DefaultConnectTimeout)
}

View File

@ -1,129 +0,0 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package network
import (
"encoding/binary"
"github.com/pkg/errors"
"net"
)
const (
torSucceeded = 0x00
torGeneralError = 0x01
torNotAllowed = 0x02
torNetUnreachable = 0x03
torHostUnreachable = 0x04
torConnectionRefused = 0x05
torTTLExpired = 0x06
torCmdNotSupported = 0x07
torAddrNotSupported = 0x08
)
var (
// ErrTorInvalidAddressResponse indicates an invalid address was
// returned by the Tor DNS resolver.
ErrTorInvalidAddressResponse = errors.New("invalid address response")
// ErrTorInvalidProxyResponse indicates the Tor proxy returned a
// response in an unexpected format.
ErrTorInvalidProxyResponse = errors.New("invalid proxy response")
// ErrTorUnrecognizedAuthMethod indicates the authentication method
// provided is not recognized.
ErrTorUnrecognizedAuthMethod = errors.New("invalid proxy authentication method")
torStatusErrors = map[byte]error{
torSucceeded: errors.New("tor succeeded"),
torGeneralError: errors.New("tor general error"),
torNotAllowed: errors.New("tor not allowed"),
torNetUnreachable: errors.New("tor network is unreachable"),
torHostUnreachable: errors.New("tor host is unreachable"),
torConnectionRefused: errors.New("tor connection refused"),
torTTLExpired: errors.New("tor TTL expired"),
torCmdNotSupported: errors.New("tor command not supported"),
torAddrNotSupported: errors.New("tor address type not supported"),
}
)
// TorLookupIP uses Tor to resolve DNS via the SOCKS extension they provide for
// resolution over the Tor network. Tor itself doesn't support ipv6 so this
// doesn't either.
func TorLookupIP(host, proxy string) ([]net.IP, error) {
conn, err := net.Dial("tcp", proxy)
if err != nil {
return nil, err
}
defer conn.Close()
buf := []byte{'\x05', '\x01', '\x00'}
_, err = conn.Write(buf)
if err != nil {
return nil, err
}
buf = make([]byte, 2)
_, err = conn.Read(buf)
if err != nil {
return nil, err
}
if buf[0] != '\x05' {
return nil, ErrTorInvalidProxyResponse
}
if buf[1] != '\x00' {
return nil, ErrTorUnrecognizedAuthMethod
}
buf = make([]byte, 7+len(host))
buf[0] = 5 // protocol version
buf[1] = '\xF0' // Tor Resolve
buf[2] = 0 // reserved
buf[3] = 3 // Tor Resolve
buf[4] = byte(len(host))
copy(buf[5:], host)
buf[5+len(host)] = 0 // Port 0
_, err = conn.Write(buf)
if err != nil {
return nil, err
}
buf = make([]byte, 4)
_, err = conn.Read(buf)
if err != nil {
return nil, err
}
if buf[0] != 5 {
return nil, ErrTorInvalidProxyResponse
}
if buf[1] != 0 {
if int(buf[1]) >= len(torStatusErrors) {
return nil, ErrTorInvalidProxyResponse
} else if err := torStatusErrors[buf[1]]; err != nil {
return nil, err
}
return nil, ErrTorInvalidProxyResponse
}
if buf[3] != 1 {
err := torStatusErrors[torGeneralError]
return nil, err
}
buf = make([]byte, 4)
bytes, err := conn.Read(buf)
if err != nil {
return nil, err
}
if bytes != 4 {
return nil, ErrTorInvalidAddressResponse
}
r := binary.BigEndian.Uint32(buf)
addr := make([]net.IP, 1)
addr[0] = net.IPv4(byte(r>>24), byte(r>>16), byte(r>>8), byte(r))
return addr, nil
}