peer: avoid compactblock dos.

This commit is contained in:
Christopher Jeffrey 2017-01-05 03:59:51 -08:00
parent 5dc0202d56
commit cca763ca95
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
3 changed files with 60 additions and 33 deletions

View File

@ -111,6 +111,7 @@ function Peer(pool) {
this.compactMode = -1; this.compactMode = -1;
this.compactWitness = false; this.compactWitness = false;
this.compactBlocks = {}; this.compactBlocks = {};
this.compactAmount = 0;
this.lastMerkle = null; this.lastMerkle = null;
this.waitingTX = 0; this.waitingTX = 0;
this.syncSent = false; this.syncSent = false;
@ -866,7 +867,7 @@ Peer.prototype.sendFeeRate = function sendFeeRate(rate) {
Peer.prototype.destroy = function destroy() { Peer.prototype.destroy = function destroy() {
var connected = this.connected; var connected = this.connected;
var i, keys, cmd, entry, hash; var i, keys, cmd, entry;
if (this.destroyed) if (this.destroyed)
return; return;
@ -909,14 +910,8 @@ Peer.prototype.destroy = function destroy() {
entry.reject(new Error('Peer was destroyed.')); entry.reject(new Error('Peer was destroyed.'));
} }
keys = Object.keys(this.compactBlocks); this.compactBlocks = {};
this.compactAmount = 0;
for (i = 0; i < keys.length; i++) {
hash = keys[i];
entry = this.compactBlocks[hash];
delete this.compactBlocks[hash];
entry.destroy();
}
this.locker.destroy(); this.locker.destroy();
@ -1200,9 +1195,11 @@ Peer.prototype.blockType = function blockType() {
if (this.options.spv) if (this.options.spv)
return invTypes.FILTERED_BLOCK; return invTypes.FILTERED_BLOCK;
if (this.options.compact && this.compactMode !== -1) { if (this.outbound) {
if (!this.options.witness || this.compactWitness) if (this.options.compact && this.compactMode !== -1) {
return invTypes.CMPCT_BLOCK; if (!this.options.witness || this.compactWitness)
return invTypes.CMPCT_BLOCK;
}
} }
if (this.haveWitness) if (this.haveWitness)
@ -2584,7 +2581,14 @@ Peer.prototype.handleCmpctBlock = co(function* handleCmpctBlock(packet) {
return; return;
} }
if (this.compactAmount >= 10) {
this.logger.warning('Compact block DoS attempt (%s).', this.hostname);
this.destroy();
return;
}
this.compactBlocks[hash] = block; this.compactBlocks[hash] = block;
this.compactAmount++;
this.send(new packets.GetBlockTxnPacket(block.toRequest())); this.send(new packets.GetBlockTxnPacket(block.toRequest()));
@ -2652,10 +2656,13 @@ Peer.prototype.handleBlockTxn = co(function* handleBlockTxn(packet) {
if (!block) { if (!block) {
this.logger.debug('Peer sent unsolicited blocktxn (%s).', this.hostname); this.logger.debug('Peer sent unsolicited blocktxn (%s).', this.hostname);
this.compactBlocks = {};
this.compactAmount = 0;
return; return;
} }
delete this.compactBlocks[res.hash]; delete this.compactBlocks[res.hash];
this.compactAmount--;
if (!block.fillMissing(res)) { if (!block.fillMissing(res)) {
this.increaseBan(100); this.increaseBan(100);

View File

@ -364,7 +364,7 @@ Pool.prototype._close = co(function* close() {
for (i = 0; i < hashes.length; i++) { for (i = 0; i < hashes.length; i++) {
hash = hashes[i]; hash = hashes[i];
item = this.invMap[hash]; item = this.invMap[hash];
item.finish(); item.resolve();
} }
this.peers.destroy(); this.peers.destroy();
@ -455,7 +455,7 @@ Pool.prototype.listen = function listen() {
this.server = this.createServer(); this.server = this.createServer();
this.server.on('connection', function(socket) { this.server.on('connection', function(socket) {
self.handleInbound(socket); self.handleSocket(socket);
}); });
this.server.on('listening', function() { this.server.on('listening', function() {
@ -496,11 +496,11 @@ Pool.prototype.unlisten = function unlisten() {
* @param {net.Socket} socket * @param {net.Socket} socket
*/ */
Pool.prototype.handleInbound = function handleInbound(socket) { Pool.prototype.handleSocket = function handleSocket(socket) {
var host; var host;
if (!socket.remoteAddress) { if (!socket.remoteAddress) {
this.logger.debug('Ignoring disconnected leech.'); this.logger.debug('Ignoring disconnected peer.');
socket.destroy(); socket.destroy();
return; return;
} }
@ -508,13 +508,13 @@ Pool.prototype.handleInbound = function handleInbound(socket) {
host = IP.normalize(socket.remoteAddress); host = IP.normalize(socket.remoteAddress);
if (this.peers.inbound >= this.maxInbound) { if (this.peers.inbound >= this.maxInbound) {
this.logger.debug('Ignoring leech: too many inbound (%s).', host); this.logger.debug('Ignoring peer: too many inbound (%s).', host);
socket.destroy(); socket.destroy();
return; return;
} }
if (this.hosts.isBanned(host)) { if (this.hosts.isBanned(host)) {
this.logger.debug('Ignoring banned leech (%s).', host); this.logger.debug('Ignoring banned peer (%s).', host);
socket.destroy(); socket.destroy();
return; return;
} }
@ -585,8 +585,6 @@ Pool.prototype.setLoader = function setLoader(peer) {
peer.sync(); peer.sync();
this.fillOutbound();
this.emit('loader', peer); this.emit('loader', peer);
}; };
@ -1021,7 +1019,7 @@ Pool.prototype.handleBlock = co(function* handleBlock(peer, block) {
Pool.prototype.handleTX = co(function* handleTX(peer, tx) { Pool.prototype.handleTX = co(function* handleTX(peer, tx) {
var hash = tx.hash('hex'); var hash = tx.hash('hex');
var requested = this.fulfill(peer, hash); var requested = this.fulfill(peer, hash);
var i, missing; var missing;
if (!requested) { if (!requested) {
peer.invFilter.add(tx.hash()); peer.invFilter.add(tx.hash());
@ -2169,7 +2167,7 @@ BroadcastItem.prototype.refresh = function refresh() {
this.timeout = setTimeout(function() { this.timeout = setTimeout(function() {
self.emit('timeout'); self.emit('timeout');
self.finish(new Error('Timed out.')); self.reject(new Error('Timed out.'));
}, this.pool.invTimeout); }, this.pool.invTimeout);
}; };
@ -2192,28 +2190,49 @@ BroadcastItem.prototype.announce = function announce() {
}; };
/** /**
* Finish the broadcast, potentially with an error. * Finish the broadcast.
* @param {Error?} err
*/ */
BroadcastItem.prototype.finish = function finish(err) { BroadcastItem.prototype.cleanup = function cleanup() {
var i, job; assert(this.timeout != null, 'Already finished.');
assert(this.timeout, 'Already finished.');
assert(this.pool.invMap[this.hash], 'Already finished.'); assert(this.pool.invMap[this.hash], 'Already finished.');
clearTimeout(this.timeout); clearTimeout(this.timeout);
this.timeout = null; this.timeout = null;
delete this.pool.invMap[this.hash]; delete this.pool.invMap[this.hash];
};
/**
* Finish the broadcast, return with an error.
* @param {Error} err
*/
BroadcastItem.prototype.reject = function reject(err) {
var i, job;
this.cleanup();
for (i = 0; i < this.jobs.length; i++) { for (i = 0; i < this.jobs.length; i++) {
job = this.jobs[i]; job = this.jobs[i];
if (err) { job.reject(err);
job.reject(err); }
continue;
} this.jobs.length = 0;
job.resolve(); };
/**
* Finish the broadcast successfully.
*/
BroadcastItem.prototype.resolve = function resolve() {
var i, job;
this.cleanup();
for (i = 0; i < this.jobs.length; i++) {
job = this.jobs[i];
job.resolve(false);
} }
this.jobs.length = 0; this.jobs.length = 0;

View File

@ -10,6 +10,7 @@
var constants = require('../protocol/constants'); var constants = require('../protocol/constants');
var BufferReader = require('../utils/reader'); var BufferReader = require('../utils/reader');
var StaticWriter = require('../utils/staticwriter'); var StaticWriter = require('../utils/staticwriter');
var util = require('../utils/util');
/** /**
* Inv Item * Inv Item