optimize parsers.
This commit is contained in:
parent
0fa972dcb3
commit
20d69b7cbc
@ -80,7 +80,8 @@ function BIP151Stream(cipher, key) {
|
|||||||
|
|
||||||
this.pending = [];
|
this.pending = [];
|
||||||
this.total = 0;
|
this.total = 0;
|
||||||
this.waiting = 0;
|
this.waiting = 4;
|
||||||
|
this.hasSize = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.inherits(BIP151Stream, EventEmitter);
|
utils.inherits(BIP151Stream, EventEmitter);
|
||||||
@ -178,7 +179,7 @@ BIP151Stream.prototype.sequence = function sequence() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
BIP151Stream.prototype.update = function update() {
|
BIP151Stream.prototype.update = function update() {
|
||||||
this.iv.writeUInt32LE(this.seq, 4, true);
|
this.iv.writeUInt32LE(this.seq, 0, true);
|
||||||
return this.iv;
|
return this.iv;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -267,89 +268,101 @@ BIP151Stream.prototype.verify = function verify(tag) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a ciphertext payload chunk.
|
* Parse ciphertext data and split into chunks.
|
||||||
* Potentially emits a `packet` event.
|
* Potentially emits a `packet` event.
|
||||||
* @param {Buffer} data
|
* @param {Buffer} data
|
||||||
*/
|
*/
|
||||||
|
|
||||||
BIP151Stream.prototype.feed = function feed(data) {
|
BIP151Stream.prototype.feed = function feed(data) {
|
||||||
var chunk, size, payload, tag, p, cmd, body;
|
var chunk, off, len;
|
||||||
|
|
||||||
while (data.length) {
|
this.total += data.length;
|
||||||
if (!this.waiting) {
|
this.pending.push(data);
|
||||||
this.total += data.length;
|
|
||||||
this.pending.push(data);
|
|
||||||
|
|
||||||
if (this.total < 4)
|
while (this.total >= this.waiting) {
|
||||||
break;
|
chunk = new Buffer(this.waiting);
|
||||||
|
off = 0;
|
||||||
|
len = 0;
|
||||||
|
|
||||||
chunk = concat(this.pending);
|
while (off < chunk.length) {
|
||||||
|
len = this.pending[0].copy(chunk, off);
|
||||||
this.total = 0;
|
if (len === this.pending[0].length)
|
||||||
this.pending.length = 0;
|
this.pending.shift();
|
||||||
|
else
|
||||||
size = this.decryptSize(chunk);
|
this.pending[0] = this.pending[0].slice(len);
|
||||||
data = chunk.slice(4);
|
off += len;
|
||||||
|
|
||||||
// Allow 3 batched packets of max message size (12mb).
|
|
||||||
// Not technically standard, but this protects us
|
|
||||||
// from buffering tons of data due to either an
|
|
||||||
// potential dos'er or a cipher state mismatch.
|
|
||||||
// Note that 6 is the minimum size:
|
|
||||||
// cmd=varint(1) string(1) length(4) data(0)
|
|
||||||
if (size < 6 || size > constants.MAX_MESSAGE * 3) {
|
|
||||||
this.emit('error', new Error('Bad packet size.'));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.waiting = size + 16;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.total += data.length;
|
assert.equal(off, chunk.length);
|
||||||
this.pending.push(data);
|
|
||||||
|
|
||||||
if (this.total < this.waiting)
|
this.total -= chunk.length;
|
||||||
break;
|
this.parse(chunk);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
chunk = concat(this.pending);
|
/**
|
||||||
payload = chunk.slice(0, this.waiting - 16);
|
* Parse a ciphertext payload chunk.
|
||||||
tag = chunk.slice(this.waiting - 16, this.waiting);
|
* Potentially emits a `packet` event.
|
||||||
data = chunk.slice(this.waiting);
|
* @param {Buffer} data
|
||||||
|
*/
|
||||||
|
|
||||||
this.total = 0;
|
BIP151Stream.prototype.parse = function parse(data) {
|
||||||
this.pending.length = 0;
|
var size, payload, tag, p, cmd, body;
|
||||||
this.waiting = 0;
|
|
||||||
|
|
||||||
// Authenticate payload before decrypting.
|
if (!this.hasSize) {
|
||||||
// This ensures the cipher state isn't altered
|
size = this.decryptSize(data);
|
||||||
// if the payload integrity has been compromised.
|
|
||||||
this.auth(payload);
|
|
||||||
this.finish();
|
|
||||||
|
|
||||||
if (!this.verify(tag)) {
|
// Allow 3 batched packets of max message size (12mb).
|
||||||
this.sequence();
|
// Not technically standard, but this protects us
|
||||||
this.emit('error', new Error('Bad tag.'));
|
// from buffering tons of data due to either an
|
||||||
continue;
|
// potential dos'er or a cipher state mismatch.
|
||||||
|
// Note that 6 is the minimum size:
|
||||||
|
// cmd=varint(1) string(1) length(4) data(0)
|
||||||
|
if (size < 6 || size > constants.MAX_MESSAGE * 3) {
|
||||||
|
this.waiting = 4;
|
||||||
|
this.emit('error', new Error('Bad packet size.'));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.decrypt(payload);
|
this.hasSize = true;
|
||||||
|
this.waiting = size + 16;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
payload = data.slice(0, this.waiting - 16);
|
||||||
|
tag = data.slice(this.waiting - 16, this.waiting);
|
||||||
|
|
||||||
|
this.hasSize = false;
|
||||||
|
this.waiting = 4;
|
||||||
|
|
||||||
|
// Authenticate payload before decrypting.
|
||||||
|
// This ensures the cipher state isn't altered
|
||||||
|
// if the payload integrity has been compromised.
|
||||||
|
this.auth(payload);
|
||||||
|
this.finish();
|
||||||
|
|
||||||
|
if (!this.verify(tag)) {
|
||||||
this.sequence();
|
this.sequence();
|
||||||
|
this.emit('error', new Error('Bad tag.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
p = bcoin.reader(payload, true);
|
this.decrypt(payload);
|
||||||
|
this.sequence();
|
||||||
|
|
||||||
while (p.left()) {
|
p = bcoin.reader(payload, true);
|
||||||
try {
|
|
||||||
cmd = p.readVarString('ascii');
|
|
||||||
body = p.readBytes(p.readU32());
|
|
||||||
} catch (e) {
|
|
||||||
this.emit('error', e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.emit('packet', cmd, body);
|
while (p.left()) {
|
||||||
|
try {
|
||||||
|
cmd = p.readVarString('ascii');
|
||||||
|
body = p.readBytes(p.readU32());
|
||||||
|
} catch (e) {
|
||||||
|
this.emit('error', e);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.emit('packet', cmd, body);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -626,16 +639,6 @@ BIP151.prototype.packet = function packet(cmd, body) {
|
|||||||
return this.output.packet(cmd, body);
|
return this.output.packet(cmd, body);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* Helpers
|
|
||||||
*/
|
|
||||||
|
|
||||||
function concat(buffers) {
|
|
||||||
return buffers.length > 1
|
|
||||||
? Buffer.concat(buffers)
|
|
||||||
: buffers[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Expose
|
* Expose
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -93,9 +93,7 @@ Parser.prototype.feed = function feed(data) {
|
|||||||
this.pending.push(data);
|
this.pending.push(data);
|
||||||
|
|
||||||
while (this.total >= this.waiting) {
|
while (this.total >= this.waiting) {
|
||||||
// Concat chunks
|
|
||||||
chunk = new Buffer(this.waiting);
|
chunk = new Buffer(this.waiting);
|
||||||
|
|
||||||
off = 0;
|
off = 0;
|
||||||
len = 0;
|
len = 0;
|
||||||
|
|
||||||
@ -110,7 +108,6 @@ Parser.prototype.feed = function feed(data) {
|
|||||||
|
|
||||||
assert.equal(off, chunk.length);
|
assert.equal(off, chunk.length);
|
||||||
|
|
||||||
// Slice buffers
|
|
||||||
this.total -= chunk.length;
|
this.total -= chunk.length;
|
||||||
this.parse(chunk);
|
this.parse(chunk);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -951,57 +951,83 @@ function Parser() {
|
|||||||
utils.inherits(Parser, EventEmitter);
|
utils.inherits(Parser, EventEmitter);
|
||||||
|
|
||||||
Parser.prototype.feed = function feed(data) {
|
Parser.prototype.feed = function feed(data) {
|
||||||
var chunk, header, body;
|
var chunk, off, len;
|
||||||
|
|
||||||
while (data) {
|
this.total += data.length;
|
||||||
this.total += data.length;
|
this.pending.push(data);
|
||||||
this.pending.push(data);
|
|
||||||
data = null;
|
|
||||||
|
|
||||||
if (this.total < this.waiting)
|
|
||||||
break;
|
|
||||||
|
|
||||||
chunk = concat(this.pending);
|
|
||||||
|
|
||||||
if (chunk.length > this.waiting) {
|
|
||||||
data = chunk.slice(this.waiting);
|
|
||||||
chunk = chunk.slice(0, this.waiting);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.header) {
|
|
||||||
this.header = this.parseHeader(chunk);
|
|
||||||
this.waiting = this.header.size;
|
|
||||||
this.pending.length = 0;
|
|
||||||
this.total = 0;
|
|
||||||
|
|
||||||
if (this.header.magic !== 0xdeadbeef) {
|
|
||||||
this.header = null;
|
|
||||||
this.waiting = 12;
|
|
||||||
this.emit('error', new Error('Bad magic number.'));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
while (this.total >= this.waiting) {
|
||||||
|
if (this.waiting === 0) {
|
||||||
|
this.parse(new Buffer(0));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
header = this.header;
|
if (this.pending[0].length > this.waiting) {
|
||||||
|
chunk = this.pending[0].slice(0, this.waiting);
|
||||||
this.pending.length = 0;
|
this.pending[0] = this.pending[0].slice(this.waiting);
|
||||||
this.total = 0;
|
this.total -= chunk.length;
|
||||||
this.waiting = 12;
|
this.parse(chunk);
|
||||||
this.header = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
body = this.parseBody(chunk);
|
|
||||||
} catch (e) {
|
|
||||||
this.emit('error', e);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.emit('packet', header.job, body);
|
if (this.pending[0].length === this.waiting) {
|
||||||
|
chunk = this.pending.shift();
|
||||||
|
this.total -= chunk.length;
|
||||||
|
this.parse(chunk);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk = new Buffer(this.waiting);
|
||||||
|
off = 0;
|
||||||
|
len = 0;
|
||||||
|
|
||||||
|
while (off < chunk.length) {
|
||||||
|
len = this.pending[0].copy(chunk, off);
|
||||||
|
if (len === this.pending[0].length)
|
||||||
|
this.pending.shift();
|
||||||
|
else
|
||||||
|
this.pending[0] = this.pending[0].slice(len);
|
||||||
|
off += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.equal(off, chunk.length);
|
||||||
|
|
||||||
|
this.total -= chunk.length;
|
||||||
|
this.parse(chunk);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Parser.prototype.parse = function parse(data) {
|
||||||
|
var header, body;
|
||||||
|
|
||||||
|
if (!this.header) {
|
||||||
|
header = this.parseHeader(data);
|
||||||
|
|
||||||
|
if (header.magic !== 0xdeadbeef) {
|
||||||
|
this.emit('error', new Error('Bad magic number.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.header = header;
|
||||||
|
this.waiting = header.size;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
header = this.header;
|
||||||
|
this.waiting = 12;
|
||||||
|
this.header = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
body = this.parseBody(data);
|
||||||
|
} catch (e) {
|
||||||
|
this.emit('error', e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emit('packet', header.job, body);
|
||||||
|
};
|
||||||
|
|
||||||
Parser.prototype.parseHeader = function parseHeader(data) {
|
Parser.prototype.parseHeader = function parseHeader(data) {
|
||||||
return {
|
return {
|
||||||
magic: data.readUInt32LE(0, true),
|
magic: data.readUInt32LE(0, true),
|
||||||
@ -1107,12 +1133,6 @@ function fromError(err) {
|
|||||||
return [err.message, err.stack + '', err.type];
|
return [err.message, err.stack + '', err.type];
|
||||||
}
|
}
|
||||||
|
|
||||||
function concat(buffers) {
|
|
||||||
return buffers.length > 1
|
|
||||||
? Buffer.concat(buffers)
|
|
||||||
: buffers[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Expose
|
* Expose
|
||||||
*/
|
*/
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user