diff --git a/addrmanager.go b/addrmanager.go index b8dcdb2d..71ce79df 100644 --- a/addrmanager.go +++ b/addrmanager.go @@ -774,13 +774,7 @@ func hostToNetAddress(host string, port uint16, services btcwire.ServiceFlag) (* 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) - } + ips, err := BtcdLookup(host) if err != nil { return nil, err } diff --git a/config.go b/config.go index bbd85051..3f1c2da3 100644 --- a/config.go +++ b/config.go @@ -5,12 +5,14 @@ package main import ( + "errors" "fmt" "github.com/conformal/btcdb" _ "github.com/conformal/btcdb/ldb" "github.com/conformal/btcutil" "github.com/conformal/btcwire" "github.com/conformal/go-flags" + "github.com/conformal/go-socks" "net" "os" "path/filepath" @@ -71,7 +73,10 @@ type config 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"` - UseTor bool `long:"tor" description:"Specifies the proxy server used is a Tor node"` + OnionProxy string `long:"onion" description:"Connect to tor hidden services via SOCKS5 proxy (eg. 127.0.0.1:9050)"` + OnionProxyUser string `long:"onionuser" description:"Username for onion proxy server"` + OnionProxyPass string `long:"onionpass" default-mask:"-" description:"Password for onion proxy server"` + NoOnion bool `long:"noonion" description:"Disable connecting to tor hidden services"` TestNet3 bool `long:"testnet" description:"Use the test network"` RegressionTest bool `long:"regtest" description:"Use the regression test network"` DisableCheckpoints bool `long:"nocheckpoints" description:"Disable built-in checkpoints. Don't do this unless you know what you're doing."` @@ -80,6 +85,10 @@ type config struct { CpuProfile string `long:"cpuprofile" description:"Write CPU profile to the specified file"` DebugLevel string `short:"d" long:"debuglevel" description:"Logging level for all subsystems {trace, debug, info, warn, error, critical} -- You may also specify =,=,... to set the log level for individual subsystems -- Use show to list available subsystems"` Upnp bool `long:"upnp" description:"Use UPnP to map our listening port outside of NAT"` + onionlookup func(string) ([]net.IP, error) + lookup func(string) ([]net.IP, error) + oniondial func(string, string) (net.Conn, error) + dial func(string, string) (net.Conn, error) } // serviceOptions defines the configuration options for btcd as a service on @@ -422,15 +431,6 @@ func loadConfig() (*config, []string, error) { return nil, nil, err } - // --tor requires --proxy to be set. - if cfg.UseTor && cfg.Proxy == "" { - str := "%s: the --tor option requires --proxy to be set" - err := fmt.Errorf(str, "loadConfig") - fmt.Fprintln(os.Stderr, err) - parser.WriteHelp(os.Stderr) - return nil, nil, err - } - // --proxy or --connect without --listen disables listening. if (cfg.Proxy != "" || len(cfg.ConnectPeers) > 0) && len(cfg.Listeners) == 0 { @@ -494,5 +494,60 @@ func loadConfig() (*config, []string, error) { btcdLog.Warnf("%v", configFileError) } + cfg.dial = net.Dial + cfg.lookup = net.LookupIP + if cfg.Proxy != "" { + proxy := &socks.Proxy{ + Addr: cfg.Proxy, + Username: cfg.ProxyUser, + Password: cfg.ProxyPass, + } + cfg.dial = proxy.Dial + if !cfg.NoOnion { + cfg.lookup = func(host string) ([]net.IP, error) { + return torLookupIP(host, cfg.Proxy) + } + } + } + if cfg.OnionProxy != "" { + cfg.oniondial = func(a, b string) (net.Conn, error) { + proxy := &socks.Proxy{ + Addr: cfg.OnionProxy, + Username: cfg.OnionProxyUser, + Password: cfg.OnionProxyPass, + } + return proxy.Dial(a, b) + } + cfg.onionlookup = func(host string) ([]net.IP, error) { + return torLookupIP(host, cfg.OnionProxy) + } + } else { + cfg.oniondial = cfg.dial + cfg.onionlookup = cfg.lookup + } + + if cfg.NoOnion { + cfg.oniondial = func(a, b string) (net.Conn, error) { + return nil, errors.New("tor has been disabled") + } + cfg.onionlookup = func(a string) ([]net.IP, error) { + return nil, errors.New("tor has been disabled") + } + } + return &cfg, remainingArgs, nil } + +func BtcdDial(network, address string) (net.Conn, error) { + if strings.HasSuffix(address, ".onion") { + return cfg.oniondial(network, address) + } + return cfg.dial(network, address) +} + +func BtcdLookup(host string) ([]net.IP, error) { + if strings.HasSuffix(host, ".onion") { + return cfg.onionlookup(host) + } + return cfg.lookup(host) +} diff --git a/discovery.go b/discovery.go index b343e5f8..487926a9 100644 --- a/discovery.go +++ b/discovery.go @@ -7,7 +7,6 @@ package main import ( "encoding/binary" "errors" - "fmt" "net" ) @@ -41,29 +40,9 @@ var ( } ) -// try individual DNS server return list of strings for responses. -func doDNSLookup(host, proxy string) ([]net.IP, error) { - var err error - var addrs []net.IP - - if proxy != "" { - addrs, err = torLookupIP(host, proxy) - } else { - addrs, err = net.LookupIP(host) - } - if err != nil { - return nil, err - } - - return addrs, nil -} - -// Use Tor to resolve DNS. -/* - TODO: - * this function must be documented internally - * this function does not handle IPv6 -*/ +// torLookupIP uses Tor to resolve DNS via the SOCKS extension they provide for +// resolution over the Tor network. Tor itself doesnt support ipv6 so this +// doesn't either. func torLookupIP(host, proxy string) ([]net.IP, error) { conn, err := net.Dial("tcp", proxy) if err != nil { @@ -149,17 +128,12 @@ func torLookupIP(host, proxy string) ([]net.IP, error) { // resolution. If any errors occur then the seeder that errored will not have // any hosts in the list. Therefore if all hosts failed an empty slice of // strings will be returned. -func dnsDiscover(seeder string, proxy string) []net.IP { +func dnsDiscover(seeder string) []net.IP { discLog.Debugf("Fetching list of seeds from %v", seeder) - peers, err := doDNSLookup(seeder, proxy) + peers, err := BtcdLookup(seeder) if err != nil { - seederPlusProxy := seeder - if proxy != "" { - seederPlusProxy = fmt.Sprintf("%s (proxy %s)", - seeder, proxy) - } discLog.Debugf("Unable to fetch dns seeds from %s: %v", - seederPlusProxy, err) + seeder, err) return []net.IP{} } diff --git a/doc.go b/doc.go index 1154d80e..8a547bf3 100644 --- a/doc.go +++ b/doc.go @@ -20,8 +20,7 @@ this location. Usage: btcd [OPTIONS] -The flags are: - -h, --help Show this help message +Application Options: -V, --version Display version information and exit -C, --configfile= Path to configuration file -b, --datadir= Directory to store data @@ -33,35 +32,45 @@ The flags are: interfaces via --listen --listen= Add an interface/port to listen for connections (default all interfaces port: 8333, testnet: 18333) - --maxpeers= Max number of inbound and outbound peers + --maxpeers= Max number of inbound and outbound peers (125) --banduration= How long to ban misbehaving peers. Valid time units are - {s, m, h}. Minimum 1 second + {s, m, h}. Minimum 1 second (24h0m0s) -u, --rpcuser= Username for RPC connections -P, --rpcpass= Password for RPC connections --rpclisten= Add an interface/port to listen for RPC connections - (default localhost port: 8334, testnet: 18334) + (default port: 8334, testnet: 18334) --rpccert= File containing the certificate file --rpckey= File containing the certificate key --norpc Disable built-in RPC server -- NOTE: The RPC server is disabled by default if no rpcuser/rpcpass is specified --nodnsseed Disable DNS seeding for peers + --externalip= --proxy= Connect via SOCKS5 proxy (eg. 127.0.0.1:9050) --proxyuser= Username for proxy server --proxypass= Password for proxy server + --onion= Connect to tor hidden services via SOCKS5 proxy (eg. + 127.0.0.1:9050) + --onionuser= Username for onion proxy server + --onionpass= Password for onion proxy server + --noonion Disable connecting to tor hidden services --tor Specifies the proxy server used is a Tor node --testnet Use the test network --regtest Use the regression test network --nocheckpoints Disable built-in checkpoints. Don't do this unless you know what you're doing. - --dbtype= Database backend to use for the Block Chain + --dbtype= Database backend to use for the Block Chain (leveldb) --profile= Enable HTTP profiling on given port -- NOTE port must be - between 1024 and 65536 + between 1024 and 65536 (6060) --cpuprofile= Write CPU profile to the specified file - -d, --debuglevel: Logging level for all subsystems {trace, debug, info, + -d, --debuglevel= Logging level for all subsystems {trace, debug, info, warn, error, critical} -- You may also specify =,=,... to set the log level for individual subsystems -- Use show to list - available subsystems + available subsystems (info) + --upnp Use UPnP to map our listening port outside of NAT + +Help Options: + -h, --help Show this help message */ package main diff --git a/peer.go b/peer.go index bf932b3c..8661d78f 100644 --- a/peer.go +++ b/peer.go @@ -1544,31 +1544,16 @@ func newOutboundPeer(s *server, addr string, persistent bool) *peer { } go func() { - // Select which dial method to call depending on whether or - // not a proxy is configured. Also, add proxy information to - // logged address if needed. - dial := net.Dial - faddr := addr - if cfg.Proxy != "" { - proxy := &socks.Proxy{ - Addr: cfg.Proxy, - Username: cfg.ProxyUser, - Password: cfg.ProxyPass, - } - dial = proxy.Dial - faddr = fmt.Sprintf("%s via proxy %s", addr, cfg.Proxy) - } - // Attempt to connect to the peer. If the connection fails and // this is a persistent connection, retry after the retry // interval. for atomic.LoadInt32(&p.disconnect) == 0 { - srvrLog.Debugf("Attempting to connect to %s", faddr) - conn, err := dial("tcp", addr) + srvrLog.Debugf("Attempting to connect to %s", addr) + conn, err := BtcdDial("tcp", addr) if err != nil { p.retryCount += 1 srvrLog.Debugf("Failed to connect to %s: %v", - faddr, err) + addr, err) if !persistent { p.server.donePeers <- p return @@ -1576,7 +1561,7 @@ func newOutboundPeer(s *server, addr string, persistent bool) *peer { scaledInterval := connectionRetryInterval.Nanoseconds() * p.retryCount / 2 scaledDuration := time.Duration(scaledInterval) srvrLog.Debugf("Retrying connection to %s in "+ - "%s", faddr, scaledDuration) + "%s", addr, scaledDuration) time.Sleep(scaledDuration) continue } diff --git a/sample-btcd.conf b/sample-btcd.conf index 7c217367..6b8abfb1 100644 --- a/sample-btcd.conf +++ b/sample-btcd.conf @@ -28,11 +28,16 @@ ; proxyuser= ; proxypass= -; The SOCKS5 proxy above is Tor (https://www.torproject.org). -; Although not required if the proxy set is indeed Tor, setting this option -; improves anonymity by sending DNS queries over the Tor network (during DNS -; seed lookup). This stops your IP from being leaked via DNS. -; tor=1 +; The SOCKS5 proxy above is assumed to be Tor (https://www.torproject.org). +; If the proxy is not tor the the following my 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 ; ****************************************************************************** ; Summary of 'addpeer' versus 'connect'. diff --git a/server.go b/server.go index 73930ceb..ade3cb09 100644 --- a/server.go +++ b/server.go @@ -407,12 +407,8 @@ func (s *server) seedFromDNS() { return } - proxy := "" - if cfg.Proxy != "" && cfg.UseTor { - proxy = cfg.Proxy - } for _, seeder := range activeNetParams.dnsSeeds { - seedpeers := dnsDiscover(seeder, proxy) + seedpeers := dnsDiscover(seeder) if len(seedpeers) == 0 { continue }