Skip to content

Commit

Permalink
Enable use of a different proxy for .onion addresses.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Owain G. Ainsworth committed Jan 7, 2014
1 parent 34657d4 commit dd7c910
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 87 deletions.
8 changes: 1 addition & 7 deletions addrmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
75 changes: 65 additions & 10 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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."`
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}
38 changes: 6 additions & 32 deletions discovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ package main
import (
"encoding/binary"
"errors"
"fmt"
"net"
)

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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{}
}

Expand Down
27 changes: 18 additions & 9 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 changes: 4 additions & 19 deletions peer.go
Original file line number Diff line number Diff line change
Expand Up @@ -1544,39 +1544,24 @@ 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
}
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
}
Expand Down
15 changes: 10 additions & 5 deletions sample-btcd.conf
Original file line number Diff line number Diff line change
Expand Up @@ -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'.
Expand Down
6 changes: 1 addition & 5 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down

0 comments on commit dd7c910

Please sign in to comment.