From 5073c1508f2a95a2a02a8380a875676385cfc161 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Tue, 24 Jan 2017 18:25:44 -0800 Subject: [PATCH] net: tor support for outgoing conns. --- browser/wsproxy.js | 2 +- etc/sample.conf | 5 ++-- lib/net/bip150.js | 8 +----- lib/net/dns-browser.js | 6 ++-- lib/net/dns.js | 11 ++++++-- lib/net/hostlist.js | 8 +----- lib/net/peer.js | 9 +----- lib/net/pool.js | 53 +++++++++++++++++++++++++++++++----- lib/net/tcp.js | 3 ++ lib/node/config.js | 3 +- lib/node/fullnode.js | 3 +- lib/node/spvnode.js | 3 +- lib/primitives/netaddress.js | 4 +-- lib/utils/ip.js | 22 +++++++-------- 14 files changed, 87 insertions(+), 53 deletions(-) diff --git a/browser/wsproxy.js b/browser/wsproxy.js index c65d8ab7..fc2792d3 100644 --- a/browser/wsproxy.js +++ b/browser/wsproxy.js @@ -162,7 +162,7 @@ WSProxy.prototype._handleConnect = function _handleConnect(ws, port, host, nonce return; } - if (!IP.isRoutable(raw) || IP.isTor(raw)) { + if (!IP.isRoutable(raw) || IP.isOnion(raw)) { this.log( 'Client is trying to connect to a bad ip: %s (%s).', host, state.host); diff --git a/etc/sample.conf b/etc/sample.conf index 3ff5416b..5dbf4b64 100644 --- a/etc/sample.conf +++ b/etc/sample.conf @@ -59,8 +59,9 @@ listen: true max-outbound: 8 max-inbound: 30 -# Websocket Proxy Server (Browser Only) -proxy-server: localhost +# Proxy Server (browser=websockets, node=socks) +# proxy: foo:bar@127.0.0.1:9050 +# onion: true # Custom list of DNS seeds # seeds: seed.bitcoin.sipa.be diff --git a/lib/net/bip150.js b/lib/net/bip150.js index 206ada53..65636301 100644 --- a/lib/net/bip150.js +++ b/lib/net/bip150.js @@ -468,7 +468,6 @@ function AuthDB(options) { this.logger = null; this.resolve = dns.resolve; - this.proxyServer = null; this.dnsKnown = []; this.known = {}; @@ -496,11 +495,6 @@ AuthDB.prototype._init = function _init(options) { this.resolve = options.resolve; } - if (options.proxyServer != null) { - assert(typeof options.proxyServer === 'string'); - this.proxyServer = options.proxyServer; - } - if (options.knownPeers != null) { assert(typeof options.knownPeers === 'object'); this.setKnown(options.knownPeers); @@ -638,7 +632,7 @@ AuthDB.prototype.populate = co(function* populate(addr, key) { this.logger.info('Resolving authorized hosts from: %s.', addr.host); try { - hosts = yield this.resolve(addr.host, this.proxyServer); + hosts = yield this.resolve(addr.host); } catch (e) { if (this.logger) this.logger.error(e); diff --git a/lib/net/dns-browser.js b/lib/net/dns-browser.js index b0b9ee53..521bfd78 100644 --- a/lib/net/dns-browser.js +++ b/lib/net/dns-browser.js @@ -9,7 +9,7 @@ var ProxySocket = require('./proxysocket'); var socket; -exports.resolve = function resolve(host, proxy) { +exports.resolve = function resolve(host, proxy, onion) { return new Promise(function(resolve, reject) { if (!socket) socket = new ProxySocket(proxy); @@ -30,6 +30,6 @@ exports.resolve = function resolve(host, proxy) { }); }; -exports.lookup = function lookup(host, proxy) { - return exports.resolve(host, proxy); +exports.lookup = function lookup(host, proxy, onion) { + return exports.resolve(host, proxy, onion); }; diff --git a/lib/net/dns.js b/lib/net/dns.js index dd47a212..02740280 100644 --- a/lib/net/dns.js +++ b/lib/net/dns.js @@ -7,6 +7,7 @@ 'use strict'; var dns = require('dns'); +var socks = require('./socks'); var options = { family: 4, @@ -14,7 +15,10 @@ var options = { all: true }; -exports.resolve = function resolve(host, proxy) { +exports.resolve = function resolve(host, proxy, onion) { + if (proxy && onion) + return socks.resolve(host, proxy); + return new Promise(function(resolve, reject) { dns.resolve(host, 'A', function(err, result) { if (err) { @@ -32,7 +36,10 @@ exports.resolve = function resolve(host, proxy) { }); }; -exports.lookup = function lookup(host, proxy) { +exports.lookup = function lookup(host, proxy, onion) { + if (proxy && onion) + return socks.resolve(host, proxy); + return new Promise(function(resolve, reject) { var addrs = []; var i, addr; diff --git a/lib/net/hostlist.js b/lib/net/hostlist.js index 2894b862..25d9d960 100644 --- a/lib/net/hostlist.js +++ b/lib/net/hostlist.js @@ -32,7 +32,6 @@ function HostList(options) { this.network = Network.primary; this.logger = null; this.address = new NetAddress(); - this.proxyServer = null; this.resolve = dns.resolve; this.banTime = common.BAN_TIME; @@ -89,11 +88,6 @@ HostList.prototype._initOptions = function initOptions(options) { this.address = options.address; } - if (options.proxyServer != null) { - assert(typeof options.proxyServer === 'string'); - this.proxyServer = options.proxyServer; - } - if (options.resolve != null) { assert(typeof options.resolve === 'function'); this.resolve = options.resolve; @@ -804,7 +798,7 @@ HostList.prototype.populate = co(function* populate(target) { this.logger.info('Resolving host: %s.', target.host); try { - hosts = yield this.resolve(target.host, this.proxyServer); + hosts = yield this.resolve(target.host); } catch (e) { if (this.logger) this.logger.error(e); diff --git a/lib/net/peer.js b/lib/net/peer.js index fd65b86a..d628ac8a 100644 --- a/lib/net/peer.js +++ b/lib/net/peer.js @@ -417,12 +417,11 @@ Peer.prototype.accept = function accept(socket) { */ Peer.prototype.connect = function connect(addr) { - var proxy = this.options.proxyServer; var socket; assert(!this.socket); - socket = this.options.createSocket(addr.port, addr.host, proxy); + socket = this.options.createSocket(addr.port, addr.host); this.address = addr; this.ts = util.now(); @@ -2156,7 +2155,6 @@ function PeerOptions(options) { this.network = Network.primary; this.logger = Logger.global; - this.proxyServer = null; this.createSocket = tcp.createSocket; this.version = common.PROTOCOL_VERSION; this.services = common.LOCAL_SERVICES; @@ -2197,11 +2195,6 @@ PeerOptions.prototype.fromOptions = function fromOptions(options) { this.logger = options.logger; } - if (options.proxyServer != null) { - assert(typeof options.proxyServer === 'string'); - this.proxyServer = options.proxyServer; - } - if (options.createSocket != null) { assert(typeof options.createSocket === 'function'); this.createSocket = options.createSocket; diff --git a/lib/net/pool.js b/lib/net/pool.js index 7c5177c1..a85d2a7e 100644 --- a/lib/net/pool.js +++ b/lib/net/pool.js @@ -291,7 +291,7 @@ Pool.prototype._connect = co(function* connect() { yield this.listen(); - if (this.address.isNull()) { + if (this.address.isNull() && !this.options.proxy) { try { ip = yield this.getIP(); } catch (e) { @@ -1291,6 +1291,9 @@ Pool.prototype.handleAddr = co(function* handleAddr(peer, packet) { if (!addr.hasServices(services)) continue; + if (!this.options.onion && addr.isOnion()) + continue; + if (addr.ts <= 100000000 || addr.ts > now + 10 * 60) addr.ts = now - 5 * 24 * 60 * 60; @@ -2653,6 +2656,9 @@ Pool.prototype.getHost = function getHost() { if (!addr.hasServices(services)) continue; + if (!this.options.onion && addr.isOnion()) + continue; + if (i < 30 && now - entry.lastAttempt < 600) continue; @@ -3222,10 +3228,11 @@ function PoolOptions(options) { this.port = this.network.port; this.maxOutbound = 8; this.maxInbound = 8; - this.createSocket = tcp.createSocket; + this.createSocket = this._createSocket; this.createServer = tcp.createServer; - this.resolve = dns.resolve; - this.proxyServer = null; + this.resolve = this._resolve; + this.proxy = null; + this.onion = false; this.selfish = false; this.version = common.PROTOCOL_VERSION; this.agent = common.USER_AGENT; @@ -3362,9 +3369,14 @@ PoolOptions.prototype.fromOptions = function fromOptions(options) { this.resolve = options.resolve; } - if (options.proxyServer) { - assert(typeof options.proxyServer === 'string'); - this.proxyServer = options.proxyServer; + if (options.proxy) { + assert(typeof options.proxy === 'string'); + this.proxy = options.proxy; + } + + if (options.onion) { + assert(typeof options.onion === 'boolean'); + this.onion = options.onion; } if (options.selfish) { @@ -3454,6 +3466,7 @@ PoolOptions.prototype.fromOptions = function fromOptions(options) { this.checkpoints = true; this.compact = false; this.bip37 = false; + this.listen = false; } if (this.selfish) { @@ -3464,6 +3477,9 @@ PoolOptions.prototype.fromOptions = function fromOptions(options) { if (this.bip37) this.services |= common.services.BLOOM; + if (this.proxy) + this.listen = false; + if (options.services != null) { assert(util.isUInt32(options.services)); this.services = options.services; @@ -3576,6 +3592,29 @@ PoolOptions.prototype.getRate = function getRate(hash) { return entry.getRate(); }; +/** + * Default createSocket call. + * @private + * @param {Number} port + * @param {String} host + * @returns {net.Socket} + */ + +PoolOptions.prototype._createSocket = function createSocket(port, host) { + return tcp.createSocket(port, host, this.proxy); +}; + +/** + * Default resolve call. + * @private + * @param {String} name + * @returns {String[]} + */ + +PoolOptions.prototype._resolve = function resolve(name) { + return dns.resolve(name, this.proxy, this.onion); +}; + /** * Peer List * @constructor diff --git a/lib/net/tcp.js b/lib/net/tcp.js index d83518b4..8ded91f8 100644 --- a/lib/net/tcp.js +++ b/lib/net/tcp.js @@ -8,9 +8,12 @@ var EventEmitter = require('events').EventEmitter; var net = require('net'); +var socks = require('./socks'); var tcp = exports; tcp.createSocket = function createSocket(port, host, proxy) { + if (proxy) + return socks.connect(proxy, port, host); return net.connect(port, host); }; diff --git a/lib/node/config.js b/lib/node/config.js index 1e947662..157c8190 100644 --- a/lib/node/config.js +++ b/lib/node/config.js @@ -192,7 +192,8 @@ config.parseData = function parseData(data, prefix, dirname) { options.bip151 = bool(data.bip151); options.bip150 = bool(data.bip150); options.identityKey = key(data.identitykey); - options.proxyServer = str(data.proxyserver); + options.proxy = str(data.proxy); + options.onion = bool(data.onion); options.seeds = list(data.seeds); options.nodes = list(data.nodes); options.maxOutbound = num(data.maxoutbound); diff --git a/lib/node/fullnode.js b/lib/node/fullnode.js index 12530b55..acdedfd8 100644 --- a/lib/node/fullnode.js +++ b/lib/node/fullnode.js @@ -110,7 +110,8 @@ function FullNode(options) { identityKey: this.options.identityKey, maxOutbound: this.options.maxOutbound, maxInbound: this.options.maxInbound, - proxyServer: this.options.proxyServer, + proxy: this.options.proxy, + onion: this.options.onion, seeds: this.options.seeds, nodes: this.options.nodes, publicHost: this.options.publicHost, diff --git a/lib/node/spvnode.js b/lib/node/spvnode.js index d89a614d..03a2f2e9 100644 --- a/lib/node/spvnode.js +++ b/lib/node/spvnode.js @@ -60,7 +60,8 @@ function SPVNode(options) { network: this.network, logger: this.logger, chain: this.chain, - proxyServer: this.options.proxyServer, + proxy: this.options.proxy, + onion: this.options.onion, seeds: this.options.seeds, nodes: this.options.nodes, bip151: this.options.bip151, diff --git a/lib/primitives/netaddress.js b/lib/primitives/netaddress.js index fb2e0bf4..22639f25 100644 --- a/lib/primitives/netaddress.js +++ b/lib/primitives/netaddress.js @@ -164,8 +164,8 @@ NetAddress.prototype.isRoutable = function isRoutable() { * @returns {Boolean} */ -NetAddress.prototype.isTor = function isTor() { - return IP.isTor(this.raw); +NetAddress.prototype.isOnion = function isOnion() { + return IP.isOnion(this.raw); }; /** diff --git a/lib/utils/ip.js b/lib/utils/ip.js index 17695caa..d1639919 100644 --- a/lib/utils/ip.js +++ b/lib/utils/ip.js @@ -47,7 +47,7 @@ IP.types = { DNS: -1, IPV4: 4, IPV6: 6, - TOR: 10 + ONION: 10 }; /** @@ -178,8 +178,8 @@ IP.getStringType = function getStringType(str) { if (IP.isV6String(str)) return IP.types.IPV6; - if (IP.isTorString(str)) - return IP.types.TOR; + if (IP.isOnionString(str)) + return IP.types.ONION; return IP.types.DNS; }; @@ -226,7 +226,7 @@ IP.isV6String = function isV6String(str) { * @returns {Boolean} */ -IP.isTorString = function isTorString(str) { +IP.isOnionString = function isOnionString(str) { assert(typeof str === 'string'); if (str.length < 7) @@ -278,7 +278,7 @@ IP.toBuffer = function toBuffer(str) { return IP.parseV4(str, raw, 12); } - if (IP.isTorString(str)) { + if (IP.isOnionString(str)) { data = TOR_ONION; data.copy(raw, 0); data = base32.decode(str.slice(0, -6)); @@ -420,7 +420,7 @@ IP.toString = function toString(raw) { return host; } - if (IP.isTor(raw)) { + if (IP.isOnion(raw)) { host = base32.encode(raw.slice(6)); return host + '.onion'; } @@ -466,7 +466,7 @@ IP.isIPv4 = function isIPv4(raw) { */ IP.isIPv6 = function isIPv6(raw) { - return !IP.isMapped(raw) && !IP.isTor(raw); + return !IP.isMapped(raw) && !IP.isOnion(raw); }; /** @@ -482,8 +482,8 @@ IP.getType = function getType(raw) { if (IP.isIPv6(raw)) return IP.types.IPV6; - if (IP.isTor(raw)) - return IP.types.TOR; + if (IP.isOnion(raw)) + return IP.types.ONION; assert(false, 'Unknown type.'); }; @@ -727,7 +727,7 @@ IP.isRFC4843 = function isRFC4843(raw) { * @returns {Boolean} */ -IP.isTor = function isTor(raw) { +IP.isOnion = function isOnion(raw) { return IP.hasPrefix(raw, TOR_ONION); }; @@ -815,7 +815,7 @@ IP.isRoutable = function isRoutable(raw) { if (IP.isRFC5737(raw)) return false; - if (IP.isRFC4193(raw) && !IP.isTor(raw)) + if (IP.isRFC4193(raw) && !IP.isOnion(raw)) return false; if (IP.isRFC4843(raw))