peer: avoid compactblock dos.
This commit is contained in:
parent
5dc0202d56
commit
cca763ca95
@ -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);
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user