net: refactor host management.

This commit is contained in:
Christopher Jeffrey 2016-12-16 13:43:23 -08:00
parent dc58c99ba2
commit 761b6d6636
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
8 changed files with 472 additions and 378 deletions

View File

@ -150,7 +150,7 @@ WSProxy.prototype._handleConnect = function _handleConnect(ws, port, host, nonce
}
socket.on('connect', function() {
ws.emit('tcp connect');
ws.emit('tcp connect', socket.remoteAddress, socket.remotePort);
});
socket.on('data', function(data) {

View File

@ -9,4 +9,5 @@ exports.Parser = require('./parser');
exports.Peer = require('./peer');
exports.Pool = require('./pool');
exports.tcp = require('./tcp');
exports.dns = require('./dns');
exports.time = require('./time');

View File

@ -29,6 +29,7 @@ var errors = require('../btc/errors');
var List = require('../utils/list');
var packetTypes = packets.types;
var VerifyResult = errors.VerifyResult;
var IP = require('../utils/ip');
/**
* Represents a remote peer.
@ -72,9 +73,9 @@ var VerifyResult = errors.VerifyResult;
* @emits Peer#ack
*/
function Peer(pool, addr, socket) {
function Peer(pool) {
if (!(this instanceof Peer))
return new Peer(pool, addr, socket);
return new Peer(pool);
EventEmitter.call(this);
@ -142,16 +143,13 @@ function Peer(pool, addr, socket) {
this.queueBlock = new List();
this.queueTX = new List();
this.uid = 0;
this.id = Peer.uid++;
this.setMaxListeners(10000);
assert(addr, 'Host required.');
this.host = addr.host;
this.port = addr.port;
this.hostname = addr.hostname;
this.host = '0.0.0.0';
this.port = 0;
this.hostname = '0.0.0.0:0';
if (this.options.bip151) {
this.bip151 = new BIP151();
@ -169,16 +167,6 @@ function Peer(pool, addr, socket) {
this.parser = new Parser(this);
this.framer = new Framer(this);
if (!socket) {
this.socket = this.connect(this.port, this.host);
this.outbound = true;
} else {
this.socket = socket;
this.ts = util.now();
this.connected = true;
this.pending = false;
}
this._init();
}
@ -200,7 +188,59 @@ Peer.uid = 0;
Peer.prototype._init = function init() {
var self = this;
this.parser.on('packet', co(function* (packet) {
try {
yield self._onPacket(packet);
} catch (e) {
self.destroy();
self.error(e);
}
}));
this.parser.on('error', function(err) {
self.error(err);
self.reject(null, 'malformed', 'error parsing message', 10);
});
if (this.bip151) {
this.bip151.on('error', function(err) {
self.reject(null, 'malformed', 'error parsing message', 10);
self.error(err);
});
this.bip151.on('rekey', function() {
self.logger.debug('Rekeying with peer (%s).', self.hostname);
self.send(self.bip151.toRekey());
});
}
};
/**
* Set peer host, port, and hostname.
* @param {String} host
* @param {Number} port
*/
Peer.prototype.setHost = function setHost(host, port) {
assert(typeof host === 'string');
assert(typeof port === 'number');
this.host = host;
this.port = port;
this.hostname = IP.hostname(host, port);
};
/**
* Bind to socket.
* @param {net.Socket} socket
*/
Peer.prototype.bind = function bind(socket) {
var self = this;
assert(!this.socket);
this.socket = socket;
this.socket.once('error', function(err) {
self.destroy();
self.error(err);
switch (err.code) {
@ -219,6 +259,7 @@ Peer.prototype._init = function init() {
});
this.socket.once('close', function() {
self.destroy();
self.error('socket hangup');
});
@ -232,39 +273,32 @@ Peer.prototype._init = function init() {
self.parser.feed(chunk);
});
};
this.parser.on('packet', co(function* (packet) {
try {
yield self._onPacket(packet);
} catch (e) {
self.error(e);
}
}));
/**
* Accept an inbound socket.
* @param {net.Socket} socket
*/
this.parser.on('error', function(err) {
self.error(err, true);
self.reject(null, 'malformed', 'error parsing message', 10);
});
Peer.prototype.accept = function accept(socket) {
var host = IP.normalize(socket.remoteAddress);
var port = socket.remotePort;
if (this.bip151) {
this.bip151.on('error', function(err) {
self.reject(null, 'malformed', 'error parsing message', 10);
self.error(err, true);
});
this.bip151.on('rekey', function() {
self.logger.debug('Rekeying with peer (%s).', self.hostname);
self.send(self.bip151.toRekey());
});
}
assert(!this.socket);
this.open();
this.bind(socket);
this.setHost(host, port);
this.ts = util.now();
this.outbound = false;
this.connected = true;
this.pending = false;
};
/**
* Create the socket and begin connecting. This method
* will use `options.createSocket` if provided.
* @param {String} host
* @param {Number} port
* @param {String} host
* @returns {net.Socket}
*/
@ -280,45 +314,61 @@ Peer.prototype.connect = function connect(port, host) {
else
socket = tcp.connect(port, host, proxy);
this.bind(socket);
this.setHost(host, port);
this.outbound = true;
this.logger.debug('Connecting to %s.', this.hostname);
socket.once('connect', function() {
self.logger.info('Connected to %s.', self.hostname);
});
return socket;
};
/**
* Open and initialize the peer.
* Open and perform initial handshake.
* @returns {Promise}
*/
Peer.prototype.open = co(function* open() {
try {
yield this._connect();
yield this._stallify();
yield this._bip151();
yield this._bip150();
yield this._handshake();
yield this._finalize();
} catch (e) {
this.error(e);
return;
}
yield this._wait();
yield this._stallify();
yield this._bip151();
yield this._bip150();
yield this._handshake();
yield this._finalize();
assert(!this.destroyed);
if (this.destroyed)
throw new Error('Peer was destroyed.');
clearTimeout(this.connectTimeout);
this.connectTimeout = null;
// Finally we can let the pool know
// that this peer is ready to go.
this.emit('open');
});
/**
* Open and perform initial handshake (without rejection).
* @returns {Promise}
*/
Peer.prototype.tryOpen = co(function* tryOpen() {
try {
yield this.open();
} catch (e) {
this.destroy();
this.error(e);
}
});
/**
* Wait for connection.
* @private
*/
Peer.prototype._connect = function _connect() {
Peer.prototype._wait = function _wait() {
var self = this;
if (this.connected) {
@ -332,9 +382,6 @@ Peer.prototype._connect = function _connect() {
self.connected = true;
self.emit('connect');
clearTimeout(self.connectTimeout);
self.connectTimeout = null;
resolve();
});
@ -380,7 +427,7 @@ Peer.prototype._bip151 = co(function* _bip151() {
try {
yield this.bip151.wait(3000);
} catch (err) {
this.error(err, true);
this.error(err);
}
assert(this.bip151.completed);
@ -463,7 +510,7 @@ Peer.prototype._handshake = co(function* _handshake() {
* @private
*/
Peer.prototype._finalize = function _finalize() {
Peer.prototype._finalize = co(function* _finalize() {
var self = this;
// Setup the ping interval.
@ -506,9 +553,7 @@ Peer.prototype._finalize = function _finalize() {
// Start syncing the chain.
this.sync();
return Promise.resolve();
};
});
/**
* Test whether the peer is the loader peer.
@ -857,6 +902,7 @@ Peer.prototype.needsDrain = function needsDrain(size) {
var self = this;
if (this.maybeStall()) {
this.destroy();
this.error('Peer stalled (drain).');
return;
}
@ -869,6 +915,7 @@ Peer.prototype.needsDrain = function needsDrain(size) {
'Peer is not reading: %s buffered (%s).',
util.mb(this.drainSize),
this.hostname);
this.destroy();
this.error('Peer stalled (drain).');
return;
}
@ -887,6 +934,7 @@ Peer.prototype.maybeStall = function maybeStall() {
return false;
this.drainSize = 0;
this.destroy();
this.error('Peer stalled.');
return true;
@ -933,7 +981,7 @@ Peer.prototype.sendRaw = function sendRaw(cmd, body, checksum) {
* @param {...String|Error} err
*/
Peer.prototype.error = function error(err, keep) {
Peer.prototype.error = function error(err) {
var i, args, msg;
if (this.destroyed)
@ -954,9 +1002,6 @@ Peer.prototype.error = function error(err, keep) {
err.message += ' (' + this.hostname + ')';
if (!keep)
this.destroy();
this.emit('error', err);
};
@ -1021,18 +1066,16 @@ Peer.prototype.response = function response(cmd, payload) {
/**
* Send `getdata` to peer.
* @param {InvItem[]} items
* @param {LoadRequest[]} items
*/
Peer.prototype.getData = function getData(items) {
var data = new Array(items.length);
var inv = [];
var i, item;
for (i = 0; i < items.length; i++) {
item = items[i];
if (item.toInv)
item = item.toInv();
item = item.toInv();
if (this.options.compact
&& this.compactMode
@ -1041,10 +1084,10 @@ Peer.prototype.getData = function getData(items) {
item.type = constants.inv.CMPCT_BLOCK;
}
data[i] = item;
inv.push(item);
}
this.send(new packets.GetDataPacket(data));
this.send(new packets.GetDataPacket(inv));
};
/**
@ -1878,13 +1921,13 @@ Peer.prototype._handleAddr = co(function* _handleAddr(packet) {
if (addr.ts <= 100000000 || addr.ts > now + 10 * 60)
addr.ts = now - 5 * 24 * 60 * 60;
this.addrFilter.add(addr.host, 'ascii');
this.addrFilter.add(addr.hostname, 'ascii');
}
this.logger.info(
'Received %d addrs (hosts=%d, peers=%d) (%s).',
addrs.length,
this.pool.hosts.items.length,
this.pool.hosts.size(),
this.pool.peers.size(),
this.hostname);
@ -1962,13 +2005,8 @@ Peer.prototype._handleGetAddr = co(function* _handleGetAddr(packet) {
this.sentAddr = true;
for (i = 0; i < this.pool.hosts.items.length; i++) {
addr = this.pool.hosts.items[i];
if (!addr.isIP())
continue;
if (!this.addrFilter.added(addr.host, 'ascii'))
for (addr = this.pool.hosts.head(); addr; addr = addr.next) {
if (!this.addrFilter.added(addr.hostname, 'ascii'))
continue;
items.push(addr);
@ -2721,11 +2759,13 @@ function RequestEntry(peer, cmd, resolve, reject) {
this.cmd = cmd;
this.resolve = resolve;
this.reject = reject;
this.id = peer.uid++;
this.id = RequestEntry.uid++;
this.onTimeout = this._onTimeout.bind(this);
this.timeout = setTimeout(this.onTimeout, this.peer.requestTimeout);
}
RequestEntry.uid = 0;
RequestEntry.prototype._onTimeout = function _onTimeout() {
var queue = this.peer.requestMap[this.cmd];

View File

@ -30,6 +30,7 @@ var request = require('../http/request');
var VerifyError = errors.VerifyError;
var VerifyResult = errors.VerifyResult;
var List = require('../utils/list');
var dns = require('./dns');
/**
* A pool of peers for handling all network activity.
@ -288,6 +289,26 @@ Pool.prototype._init = function _init() {
self.emit('full');
self.logger.info('Chain is fully synced (height=%d).', self.chain.height);
});
if (!this.options.selfish && !this.options.spv) {
if (this.mempool) {
this.mempool.on('tx', function(tx) {
self.announceTX(tx);
});
}
// Normally we would also broadcast
// competing chains, but we want to
// avoid getting banned if an evil
// miner sends us an invalid competing
// chain that we can't connect and
// verify yet.
this.chain.on('block', function(block) {
if (!self.chain.synced)
return;
self.announceBlock(block);
});
}
};
/**
@ -297,18 +318,7 @@ Pool.prototype._init = function _init() {
*/
Pool.prototype._open = co(function* _open() {
var ip, key;
try {
ip = yield this.getIP();
} catch (e) {
this.logger.error(e);
}
if (ip) {
this.address.setHost(ip);
this.logger.info('External IP found: %s.', ip);
}
var key;
if (this.mempool)
yield this.mempool.open();
@ -362,42 +372,53 @@ Pool.prototype._close = co(function* close() {
/**
* Connect to the network.
* @returns {Promise}
*/
Pool.prototype.connect = function connect() {
var self = this;
Pool.prototype.connect = co(function* connect() {
var unlock = yield this.locker.lock();
try {
return yield this._connect();
} finally {
unlock();
}
});
/**
* Connect to the network (no lock).
* @returns {Promise}
*/
Pool.prototype._connect = co(function* connect() {
var ip;
assert(this.loaded, 'Pool is not loaded.');
if (this.connected)
return;
if (!this.options.selfish && !this.options.spv) {
if (this.mempool) {
this.mempool.on('tx', function(tx) {
self.announceTX(tx);
});
}
// Normally we would also broadcast
// competing chains, but we want to
// avoid getting banned if an evil
// miner sends us an invalid competing
// chain that we can't connect and
// verify yet.
this.chain.on('block', function(block) {
if (!self.chain.synced)
return;
self.announceBlock(block);
});
try {
ip = yield this.getIP();
} catch (e) {
this.logger.error(e);
}
assert(this.hosts.seeds.length !== 0, 'No seeds available.');
if (ip) {
this.address.setHost(ip);
this.logger.info('External IP found: %s.', ip);
}
yield this.hosts.discover();
if (this.hosts.size() === 0)
throw new Error('No hosts available. Do you have an internet connection?');
this.logger.info('Resolved %d hosts from DNS seeds.', this.hosts.size());
this.addLoader();
this.connected = true;
};
});
/**
* Start listening on a server socket.
@ -483,12 +504,6 @@ Pool.prototype._handleLeech = function _handleLeech(socket) {
return;
}
if (this.hosts.isIgnored(addr)) {
this.logger.debug('Ignoring leech (%s).', addr.hostname);
socket.destroy();
return;
}
// Some kind of weird port collision
// between inbound ports and outbound ports.
if (this.peers.get(addr)) {
@ -497,7 +512,7 @@ Pool.prototype._handleLeech = function _handleLeech(socket) {
return;
}
this.addLeech(addr, socket);
this.addInbound(socket);
};
/**
@ -604,7 +619,11 @@ Pool.prototype.addLoader = function addLoader() {
return;
}
addr = this.getLoaderHost();
addr = this.hosts.getHost();
if (this.peers.get(addr))
return;
peer = this.peers.get(addr);
if (peer) {
@ -617,7 +636,7 @@ Pool.prototype.addLoader = function addLoader() {
this.startInterval();
}
peer = this.createPeer(addr);
peer = this.createPeer(addr.port, addr.host);
this.logger.info('Added loader peer (%s).', peer.hostname);
@ -663,13 +682,13 @@ Pool.prototype.setLoader = function setLoader(peer) {
* Start the blockchain sync.
*/
Pool.prototype.startSync = function startSync() {
Pool.prototype.startSync = co(function* startSync() {
this.syncing = true;
this.startInterval();
this.startTimeout();
this.connect();
yield this.connect();
if (!this.peers.load) {
this.addLoader();
@ -677,7 +696,7 @@ Pool.prototype.startSync = function startSync() {
}
this.sync();
};
});
/**
* Send a sync to each peer.
@ -714,7 +733,7 @@ Pool.prototype.forceSync = function forceSync() {
* Stop the blockchain sync.
*/
Pool.prototype.stopSync = function stopSync() {
Pool.prototype.stopSync = co(function* stopSync() {
var peer;
if (!this.syncing)
@ -733,7 +752,7 @@ Pool.prototype.stopSync = function stopSync() {
continue;
peer.syncSent = false;
}
};
});
/**
* Handle `headers` packet from a given peer.
@ -940,10 +959,7 @@ Pool.prototype.__handleInv = co(function* _handleInv(hashes, peer) {
*/
Pool.prototype._handleBlock = co(function* _handleBlock(block, peer) {
var requested;
// Fulfill the load request.
requested = this.fulfill(block);
var requested = this.fulfill(block);
// Someone is sending us blocks without
// us requesting them.
@ -1034,20 +1050,59 @@ Pool.prototype.sendMempool = function sendMempool() {
Pool.prototype.sendAlert = function sendAlert(alert) {
var peer;
for (peer = this.peers.head(); peer; peer = peer.next)
for (peer = this.peers.head(); peer; peer = peer.next) {
if (peer.pending)
continue;
peer.sendAlert(alert);
}
};
/**
* Create a base peer with no special purpose.
* @private
* @param {Object} options
* @param {Number} port
* @param {String} host
* @returns {Peer}
*/
Pool.prototype.createPeer = function createPeer(addr, socket) {
Pool.prototype.createPeer = function createPeer(port, host) {
var self = this;
var peer = new Peer(this);
this.bindPeer(peer);
peer.connect(port, host);
peer.tryOpen();
return peer;
};
/**
* Accept an inbound socket.
* @private
* @param {net.Socket} socket
* @returns {Peer}
*/
Pool.prototype.acceptPeer = function acceptPeer(socket) {
var self = this;
var peer = new Peer(this);
this.bindPeer(peer);
peer.accept(socket);
peer.tryOpen();
return peer;
};
/**
* Bind to peer events.
* @private
*/
Pool.prototype.bindPeer = function bindPeer(peer) {
var self = this;
var peer = new Peer(this, addr, socket);
peer.once('open', function() {
if (!peer.outbound)
@ -1263,8 +1318,6 @@ Pool.prototype.createPeer = function createPeer(addr, socket) {
self.emit('error', e);
}
}));
return peer;
};
/**
@ -1349,10 +1402,8 @@ Pool.prototype.hasReject = function hasReject(hash) {
*/
Pool.prototype._handleTX = co(function* _handleTX(tx, peer) {
var i, requested, missing;
// Fulfill the load request.
requested = this.fulfill(tx);
var requested = this.fulfill(tx);
var i, missing;
if (!requested) {
peer.invFilter.add(tx.hash());
@ -1379,14 +1430,16 @@ Pool.prototype._handleTX = co(function* _handleTX(tx, peer) {
try {
missing = yield this.mempool.addTX(tx);
} catch (err) {
if (err.type === 'VerifyError') {
if (err.score !== -1)
peer.reject(tx, err.code, err.reason, err.score);
}
if (err.type === 'VerifyError')
peer.reject(tx, err.code, err.reason, err.score);
throw err;
}
if (this.options.requestMissing && missing) {
if (missing) {
this.logger.debug(
'Requesting %d missing transactions (%s).',
missing.length, peer.hostname);
try {
for (i = 0; i < missing.length; i++)
this.getTX(peer, missing[i]);
@ -1399,19 +1452,19 @@ Pool.prototype._handleTX = co(function* _handleTX(tx, peer) {
});
/**
* Create a leech peer from an existing socket.
* Create an inbound peer from an existing socket.
* @private
* @param {net.Socket} socket
*/
Pool.prototype.addLeech = function addLeech(addr, socket) {
Pool.prototype.addInbound = function addInbound(socket) {
var self = this;
var peer;
if (!this.loaded)
return socket.destroy();
peer = this.createPeer(addr, socket);
peer = this.acceptPeer(socket);
this.logger.info('Added leech peer (%s).', peer.hostname);
@ -1423,12 +1476,12 @@ Pool.prototype.addLeech = function addLeech(addr, socket) {
};
/**
* Create a outbound non-loader peer. These primarily
* Create an outbound non-loader peer. These primarily
* exist for transaction relaying.
* @private
*/
Pool.prototype.addPeer = function addPeer() {
Pool.prototype.addOutbound = function addOutbound() {
var self = this;
var peer, addr;
@ -1444,10 +1497,10 @@ Pool.prototype.addPeer = function addPeer() {
addr = this.hosts.getHost();
if (!addr)
if (this.peers.get(addr))
return;
peer = this.createPeer(addr);
peer = this.createPeer(addr.port, addr.host);
this.peers.add(peer);
@ -1469,7 +1522,7 @@ Pool.prototype.fillPeers = function fillPeers() {
this.maxOutbound);
for (i = 0; i < this.maxOutbound - 1; i++)
this.addPeer();
this.addOutbound();
};
/**
@ -1829,18 +1882,6 @@ Pool.prototype.setFeeRate = function setFeeRate(rate) {
}
};
/**
* Allocate a new loader host.
* @returns {NetworkAddress}
*/
Pool.prototype.getLoaderHost = function getLoaderHost() {
if (!this.connected && this.options.preferredSeed)
return this.hosts.seeds[0];
return this.hosts.getLoaderHost();
};
/**
* Increase peer's ban score.
* @param {Peer} peer
@ -1909,16 +1950,6 @@ Pool.prototype.ignore = function ignore(addr) {
peer.destroy();
};
/**
* Test whether the host is ignored.
* @param {NetworkAddress} addr
* @returns {Boolean}
*/
Pool.prototype.isIgnored = function isIgnored(addr) {
return this.hosts.isIgnored(addr);
};
/**
* Attempt to retrieve external IP from icanhazip.com.
* @returns {Promise}
@ -1981,7 +2012,7 @@ Pool.prototype.getIP2 = co(function* getIP2() {
*/
function PeerList(pool) {
this.pool = pool;
this.logger = pool.logger;
this.map = {};
this.list = new List();
this.load = null;
@ -2028,7 +2059,7 @@ PeerList.prototype.remove = function remove(peer) {
delete this.map[peer.hostname];
if (peer.isLoader()) {
this.pool.logger.info('Removed loader peer (%s).', peer.hostname);
this.logger.info('Removed loader peer (%s).', peer.hostname);
this.load = null;
}
@ -2071,26 +2102,57 @@ PeerList.prototype.destroy = function destroy() {
*/
function HostList(pool) {
this.pool = pool;
this.network = pool.network;
this.logger = pool.logger;
this.maxOutbound = pool.maxOutbound;
this.list = new List();
this.seeds = [];
this.items = [];
this.map = {};
// Ignored hosts
this.ignored = {};
// Misbehaving hosts
this.misbehaving = {};
this.setSeeds(this.pool.network.seeds);
this.setSeeds(this.network.seeds);
}
/**
* Get head element.
* @returns {NetworkAddress}
*/
HostList.prototype.head = function head() {
return this.list.head;
};
/**
* Get tail element.
* @returns {NetworkAddress}
*/
HostList.prototype.tail = function tail() {
return this.list.tail;
};
/**
* Get list size.
* @returns {Number}
*/
HostList.prototype.size = function size() {
return this.list.size;
};
/**
* Reset host list.
*/
HostList.prototype.reset = function reset() {
this.map = {};
this.list.reset();
};
/**
* Clear misbehaving and ignored.
*/
HostList.prototype.clear = function clear() {
this.ignored = {};
this.misbehaving = {};
};
@ -2099,71 +2161,11 @@ HostList.prototype.clear = function clear() {
* @returns {NetworkAddress}
*/
HostList.prototype.getLoaderHost = function getLoaderHost() {
var addr = this.getRandom(this.seeds);
if (addr)
return addr;
addr = this.getRandom(this.items);
if (addr)
return addr;
this.pool.logger.warning('All seeds banned or ignored. Clearing...');
this.clear();
return this.getRandom(this.seeds);
};
/**
* Allocate a new host which is not currently being used.
* @returns {NetworkAddress}
*/
HostList.prototype.getHost = function getHost() {
var addr = this.getRandom(this.seeds, true);
if (addr)
return addr;
return this.getRandom(this.items, true);
};
/**
* Get a random host from collection of hosts.
* @param {NetworkAddress[]} hosts
* @param {Boolean} unique
* @returns {NetworkAddress}
*/
HostList.prototype.getRandom = function getRandom(hosts, unique) {
var index = Math.random() * hosts.length | 0;
var last = -1;
var i, addr;
for (i = 0; i < hosts.length; i++) {
addr = hosts[i];
if (this.isMisbehaving(addr))
continue;
if (this.isIgnored(addr))
continue;
if (unique && this.pool.peers.get(addr))
continue;
if (i >= index)
return addr;
last = i;
}
if (last === -1)
return;
return hosts[last];
var addr = this.list.shift();
assert(addr, 'No address available.');
this.list.push(addr);
return addr;
};
/**
@ -2173,14 +2175,13 @@ HostList.prototype.getRandom = function getRandom(hosts, unique) {
*/
HostList.prototype.add = function add(addr) {
if (this.items.length > 500)
if (this.list.size > 500)
return;
if (this.map[addr.hostname])
return;
util.binaryInsert(this.items, addr, compare);
assert(this.list.unshift(addr));
this.map[addr.hostname] = addr;
return addr;
@ -2198,15 +2199,14 @@ HostList.prototype.remove = function remove(addr) {
if (!item)
return;
util.binaryRemove(this.items, item, compare);
assert(this.list.remove(item));
delete this.map[item.hostname];
return item;
};
/**
* Increase peer's ban score.
* Mark a peer as misbehaving.
* @param {NetworkAddress} addr
*/
@ -2222,7 +2222,6 @@ HostList.prototype.ban = function ban(addr) {
HostList.prototype.unban = function unban(addr) {
delete this.misbehaving[addr.host];
delete this.ignored[addr.host];
};
/**
@ -2251,18 +2250,12 @@ HostList.prototype.isMisbehaving = function isMisbehaving(addr) {
*/
HostList.prototype.ignore = function ignore(addr) {
if (!this.remove(addr))
this.ignored[addr.host] = true;
};
var item = this.map[addr.hostname];
/**
* Test whether the host/peer is ignored.
* @param {NetworkAddress} addr
* @returns {Boolean}
*/
if (!item)
return;
HostList.prototype.isIgnored = function isIgnored(addr) {
return this.ignored[addr.host] === true;
this.list.remove(item);
};
/**
@ -2271,14 +2264,13 @@ HostList.prototype.isIgnored = function isIgnored(addr) {
*/
HostList.prototype.setSeeds = function setSeeds(seeds) {
var i, hostname, seed;
var i, seed;
this.seeds.length = 0;
for (i = 0; i < seeds.length; i++) {
hostname = seeds[i];
seed = NetworkAddress.fromHostname(hostname, this.pool.network);
this.seeds.push(seed);
seed = seeds[i];
this.addSeed(seed);
}
};
@ -2288,10 +2280,58 @@ HostList.prototype.setSeeds = function setSeeds(seeds) {
*/
HostList.prototype.addSeed = function addSeed(hostname) {
var seed = NetworkAddress.fromHostname(hostname, this.pool.network);
this.seeds.unshift(seed);
var addr = IP.parseHost(hostname, this.network.port);
this.seeds.push(addr);
};
/**
* Populate from seed.
* @param {String} seed
*/
HostList.prototype.discover = co(function* discover() {
var i, seed;
for (i = 0; i < this.seeds.length; i++) {
seed = this.seeds[i];
yield this.populate(seed);
if (this.list.size >= this.maxOutbound)
break;
}
});
/**
* Populate from seed.
* @param {String} seed
*/
HostList.prototype.populate = co(function* populate(seed) {
var i, addr, hosts, host;
if (seed.version !== -1) {
addr = NetworkAddress.fromHost(seed.host, seed.port, this.network);
this.add(addr);
return;
}
this.logger.info('Resolving hosts from seed: %s.', seed.host);
try {
hosts = yield dns.resolve(seed.host);
} catch (e) {
this.logger.error(e);
return;
}
for (i = 0; i < hosts.length; i++) {
host = hosts[i];
addr = NetworkAddress.fromHost(host, seed.port, this.network);
this.add(addr);
}
});
/**
* Represents an in-flight block or transaction.
* @exports LoadRequest
@ -2438,10 +2478,9 @@ function BroadcastItem(pool, msg) {
item = msg.toInv();
this.pool = pool;
this.msg = msg;
this.hash = item.hash;
this.type = item.type;
this.msg = msg;
this.callback = [];
this.prev = null;

View File

@ -6,6 +6,7 @@ var BufferWriter = require('../utils/writer');
var assert = require('assert');
var EventEmitter = require('events').EventEmitter;
var IOClient = require('socket.io-client');
var IP = require('../utils/ip');
function ProxySocket(uri) {
if (!(this instanceof ProxySocket))
@ -53,9 +54,11 @@ ProxySocket.prototype._init = function _init() {
console.error(err);
});
this.socket.on('tcp connect', function() {
this.socket.on('tcp connect', function(addr, port) {
if (self.closed)
return;
self.remoteAddress = addr;
self.remotePort = port;
self.emit('connect');
});

View File

@ -23,7 +23,6 @@ var BufferReader = require('../utils/reader');
* @param {Number?} options.services - Service bits.
* @param {String?} options.host - IP address (IPv6 or IPv4).
* @param {Number?} options.port - Port.
* @property {Number} id
* @property {Host} host
* @property {Number} port
* @property {Number} services
@ -34,25 +33,19 @@ function NetworkAddress(options) {
if (!(this instanceof NetworkAddress))
return new NetworkAddress(options);
this.id = NetworkAddress.uid++;
this.host = '0.0.0.0';
this.port = 0;
this.services = 0;
this.ts = 0;
this.hostname = '0.0.0.0:0';
this.prev = null;
this.next = null;
if (options)
this.fromOptions(options);
}
/**
* Globally incremented unique id.
* @private
* @type {Number}
*/
NetworkAddress.uid = 0;
/**
* Inject properties from options object.
* @private
@ -60,15 +53,12 @@ NetworkAddress.uid = 0;
*/
NetworkAddress.prototype.fromOptions = function fromOptions(options) {
var host = options.host;
assert(typeof options.host === 'string');
assert(typeof options.port === 'number');
if (IP.version(host) !== -1)
host = IP.normalize(host);
assert(IP.version(options.host) !== -1);
this.host = host;
this.host = IP.normalize(options.host);
this.port = options.port;
if (options.services) {
@ -96,15 +86,6 @@ NetworkAddress.fromOptions = function fromOptions(options) {
return new NetworkAddress().fromOptions(options);
};
/**
* Test whether the `host` field is an ip address.
* @returns {Boolean}
*/
NetworkAddress.prototype.isIP = function isIP() {
return IP.version(this.host) !== -1;
};
/**
* Test whether the NETWORK service bit is set.
* @returns {Boolean}
@ -162,17 +143,41 @@ NetworkAddress.prototype.setPort = function setPort(port) {
};
/**
* Inspect the network address.
* @returns {Object}
* Inject properties from host, port, and network.
* @private
* @param {String} host
* @param {Number} port
* @param {(Network|NetworkType)?} network
*/
NetworkAddress.prototype.inspect = function inspect() {
return '<NetworkAddress:'
+ ' id=' + this.id
+ ' hostname=' + IP.hostname(this.host, this.port)
+ ' services=' + this.services.toString(2)
+ ' date=' + util.date(this.ts)
+ '>';
NetworkAddress.prototype.fromHost = function fromHost(host, port, network) {
network = Network.get(network);
assert(IP.version(host) !== -1);
this.host = host;
this.port = port || network.port;
this.services = constants.services.NETWORK
| constants.services.BLOOM
| constants.services.WITNESS;
this.ts = network.now();
this.hostname = IP.hostname(this.host, this.port);
return this;
};
/**
* Instantiate a network address
* from a host and port.
* @param {String} host
* @param {Number} port
* @param {(Network|NetworkType)?} network
* @returns {NetworkAddress}
*/
NetworkAddress.fromHost = function fromHost(host, port, network) {
return new NetworkAddress().fromHost(host, port, network);
};
/**
@ -183,20 +188,10 @@ NetworkAddress.prototype.inspect = function inspect() {
*/
NetworkAddress.prototype.fromHostname = function fromHostname(hostname, network) {
var address = IP.parseHost(hostname);
var addr;
network = Network.get(network);
this.host = address.host;
this.port = address.port || network.port;
this.services = constants.services.NETWORK
| constants.services.BLOOM
| constants.services.WITNESS;
this.ts = network.now();
this.hostname = IP.hostname(this.host, this.port);
return this;
addr = IP.parseHost(hostname, network.port);
return this.fromHost(addr.host, addr.port, network);
};
/**
@ -218,21 +213,11 @@ NetworkAddress.fromHostname = function fromHostname(hostname, network) {
*/
NetworkAddress.prototype.fromSocket = function fromSocket(socket, network) {
assert(typeof socket.remoteAddress === 'string');
assert(typeof socket.remotePort === 'number');
network = Network.get(network);
this.host = IP.normalize(socket.remoteAddress);
this.port = socket.remotePort;
this.services = constants.services.NETWORK
| constants.services.BLOOM
| constants.services.WITNESS;
this.ts = network.now();
this.hostname = IP.hostname(this.host, this.port);
return this;
var host = socket.remoteAddress;
var port = socket.remotePort;
assert(typeof host === 'string');
assert(typeof port === 'number');
return this.fromHost(IP.normalize(host), port, network);
};
/**
@ -336,6 +321,20 @@ NetworkAddress.prototype.toRaw = function toRaw(full) {
return this.toWriter(new StaticWriter(size), full).render();
};
/**
* Inspect the network address.
* @returns {Object}
*/
NetworkAddress.prototype.inspect = function inspect() {
return '<NetworkAddress:'
+ ' id=' + this.id
+ ' hostname=' + IP.hostname(this.host, this.port)
+ ' services=' + this.services.toString(2)
+ ' date=' + util.date(this.ts)
+ '>';
};
/*
* Expose
*/

View File

@ -15,16 +15,17 @@ var assert = require('assert');
* @example
* IP.parseHost('127.0.0.1:3000');
* @param {String} addr
* @param {Number?} fallback - Fallback port.
* @returns {Object} Contains `host` and `port`.
*/
exports.parseHost = function parseHost(addr) {
var parts, host, port;
exports.parseHost = function parseHost(addr, fallback) {
var port = fallback || 0;
var parts, host, version;
assert(addr);
if (typeof addr === 'object')
return addr;
assert(typeof addr === 'string');
assert(addr.length > 0);
assert(typeof port === 'number');
if (addr[0] === '[') {
addr = addr.substring(1);
@ -35,15 +36,20 @@ exports.parseHost = function parseHost(addr) {
}
host = parts[0];
port = +parts[1] || 0;
assert(host.length > 0, 'Bad host.');
if (exports.version(host) !== -1)
if (parts.length === 2) {
port = parts[1];
assert(/^\d+$/.test(port), 'Bad port.');
port = parseInt(port, 10);
}
version = exports.version(host);
if (version !== -1)
host = exports.normalize(host);
return {
host: host,
port: port
};
return new Address(host, port, version);
};
/**
@ -54,11 +60,22 @@ exports.parseHost = function parseHost(addr) {
*/
exports.hostname = function hostname(host, port) {
var version = exports.version(host);
var version
assert(typeof host === 'string');
assert(host.length > 0);
assert(typeof port === 'number');
assert(!/[\[\]]/.test(host), 'Bad host.');
version = exports.version(host);
if (version !== -1)
host = exports.normalize(host);
if (version === 6)
host = '[' + host + ']';
return host + ':' + port;
};
@ -69,8 +86,7 @@ exports.hostname = function hostname(host, port) {
*/
exports.version = function version(ip) {
if (typeof ip !== 'string')
return -1;
assert(typeof ip === 'string');
if (IP.isV4Format(ip))
return 4;
@ -113,14 +129,6 @@ exports.isMapped = function isMapped(ip) {
exports.toBuffer = function toBuffer(ip) {
var out;
if (Buffer.isBuffer(ip)) {
assert(ip.length === 16);
return ip;
}
if (!ip)
return toBuffer('0.0.0.0');
assert(typeof ip === 'string');
assert(exports.version(ip) !== -1);
@ -148,14 +156,6 @@ exports.toBuffer = function toBuffer(ip) {
*/
exports.toString = function toString(ip) {
if (typeof ip === 'string') {
assert(exports.version(ip) !== -1);
return ip;
}
if (!ip)
return '0.0.0.0';
assert(Buffer.isBuffer(ip));
assert(ip.length === 16);
@ -183,6 +183,17 @@ exports.normalize = function normalize(ip) {
return exports.toString(exports.toBuffer(ip));
};
/*
* Helpers
*/
function Address(host, port, version) {
this.host = host;
this.port = port;
this.version = version;
}
/*
* Expose IP functions.
*/

View File

@ -78,6 +78,7 @@
"./lib/db/backends": "./lib/db/backends-browser.js",
"./lib/hd/wordlist": "./lib/hd/wordlist-browser.js",
"./lib/net/tcp": "./lib/net/tcp-browser.js",
"./lib/net/dns": "./lib/net/dns-browser.js",
"./lib/blockchain/layout": "./lib/blockchain/layout-browser.js",
"./lib/wallet/layout": "./lib/wallet/layout-browser.js",
"fs": "./browser/empty.js",