Enable use of a different proxy for .onion addresses.

This implements --onion (and --onionuser/--onionpass) that enable a
different proxy to be used to connect to .onion addresses. If no main
proxy is supplied then no proxy will be used for non-onion addresses.

Additionally we add --noonion that blocks connection attempts to .onion
addresses entirely (and avoids using tor for proxy dns lookups).

the --tor option has been supersceded and thus removed.

Closes #47
This commit is contained in:
Owain G. Ainsworth 2013-12-17 15:10:59 +00:00
parent 34657d43d9
commit dd7c910e86
7 changed files with 105 additions and 87 deletions

View file

@ -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
}

View file

@ -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 <subsystem>=<level>,<subsystem2>=<level>,... 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)
}

View file

@ -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{}
}

27
doc.go
View file

@ -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
<subsystem>=<level>,<subsystem2>=<level>,... 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

23
peer.go
View file

@ -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
}

View file

@ -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'.

View file

@ -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
}