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

View File

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

View File

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