2017-01-14 02:04:27 +00:00
|
|
|
/* global Buffer */
|
|
|
|
var debug = require('debug')('mdns-packet:lib:dns:bufferconsumer');
|
|
|
|
var util = require('util');
|
|
|
|
|
|
|
|
var LABEL_POINTER = 0xc0;
|
|
|
|
|
|
|
|
var BufferConsumer = module.exports = function BufferConsumer(arg) {
|
|
|
|
if (!(arg instanceof Buffer)) {
|
|
|
|
debug('arg', arg);
|
|
|
|
throw new Error('Expected instance of Buffer');
|
|
|
|
}
|
|
|
|
this.buffer = arg;
|
|
|
|
this.length = this.buffer.length;
|
|
|
|
debug('new consumer of %d bytes', this.length);
|
|
|
|
this._offset = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
BufferConsumer.prototype.tell = function () {
|
|
|
|
return this._offset;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
BufferConsumer.prototype.seek = function (pos) {
|
|
|
|
debug('seek(%d)', pos);
|
|
|
|
if (pos < 0) {
|
|
|
|
throw new Error('Negative pos not allowed');
|
|
|
|
}
|
|
|
|
if (pos > this.length) {
|
|
|
|
debug('bad packet', this.buffer.toString('hex'));
|
|
|
|
throw new Error(util.format('Cannot seek after EOF. %d > %d',
|
|
|
|
pos, this.length));
|
|
|
|
}
|
|
|
|
this._offset = pos;
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
|
|
|
BufferConsumer.prototype.slice = function (length) {
|
|
|
|
var v;
|
|
|
|
if (typeof length === 'undefined') {
|
|
|
|
v = this.buffer.slice(this._offset);
|
|
|
|
this._offset = this.length - 1;
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
else {
|
2017-01-14 19:58:35 +00:00
|
|
|
if ((this._offset + length) > this.length) {
|
|
|
|
debug('Buffer owerflow. Slice beyond buffer.', {
|
|
|
|
offset: this._offset,
|
|
|
|
length: length,
|
|
|
|
bufferLength: this.length
|
|
|
|
});
|
|
|
|
debug('so far', this);
|
|
|
|
throw new Error('Buffer overflow');
|
|
|
|
}
|
2017-01-14 02:04:27 +00:00
|
|
|
v = this.buffer.slice(this._offset, this._offset + length);
|
|
|
|
this._offset += length;
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
BufferConsumer.prototype.isEOF = function () {
|
|
|
|
return this._offset >= this.length;
|
|
|
|
};
|
|
|
|
|
|
|
|
BufferConsumer.prototype.byte = function () {
|
|
|
|
this._offset += 1;
|
|
|
|
return this.buffer.readUInt8(this._offset - 1);
|
|
|
|
};
|
|
|
|
|
|
|
|
BufferConsumer.prototype.short = function () {
|
|
|
|
debug('reading short at %d of %d', this._offset, this.length);
|
|
|
|
this._offset += 2;
|
|
|
|
return this.buffer.readUInt16BE(this._offset - 2);
|
|
|
|
};
|
|
|
|
|
|
|
|
BufferConsumer.prototype.long = function () {
|
|
|
|
this._offset += 4;
|
|
|
|
return this.buffer.readUInt32BE(this._offset - 4);
|
|
|
|
};
|
|
|
|
|
|
|
|
BufferConsumer.prototype.string = function (encoding, length) {
|
|
|
|
var end;
|
|
|
|
var ret;
|
|
|
|
|
|
|
|
if (length === undefined) {
|
|
|
|
end = this.buffer.length;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
end = this.tell() + length;
|
|
|
|
// if (end > this.length) {
|
|
|
|
// throw new errors.MalformedPacket(
|
|
|
|
// 'Trying to read past eof. Start=%d, End=%s, Length=%s',
|
|
|
|
// this.tell(), end, this.length);
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!encoding) {
|
|
|
|
encoding = 'utf8';
|
|
|
|
}
|
|
|
|
ret = this.buffer.toString(encoding, this._offset, end);
|
|
|
|
debug('got a %s character string:', length, ret);
|
|
|
|
this.seek(end);
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Consumes a DNS name, which will either finish with a NULL byte or a suffix
|
|
|
|
* reference (i.e., 0xc0 <ref>).
|
|
|
|
*/
|
|
|
|
BufferConsumer.prototype.name = function (join, endAt) {
|
|
|
|
debug('.name(join:%s, endAt:%s)', join, endAt);
|
|
|
|
if (typeof join === 'undefined') { join = true; }
|
|
|
|
var parts = [];
|
|
|
|
var ret;
|
|
|
|
var len;
|
|
|
|
var pos;
|
|
|
|
var end;
|
|
|
|
var comp = false;
|
|
|
|
len = this.byte();
|
|
|
|
debug('name initial len', len);
|
|
|
|
if (len === 0) {
|
|
|
|
parts.push('');
|
|
|
|
}
|
|
|
|
while (len !== 0) {
|
|
|
|
if ((len & LABEL_POINTER) === LABEL_POINTER) {
|
|
|
|
debug('has label');
|
|
|
|
len -= LABEL_POINTER;
|
|
|
|
len = len << 8;
|
|
|
|
pos = len + this.byte();
|
|
|
|
if (!comp) {
|
|
|
|
end = this.tell();
|
|
|
|
}
|
|
|
|
this.seek(pos);
|
|
|
|
len = this.byte();
|
|
|
|
comp = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
debug('no label');
|
|
|
|
|
|
|
|
// Otherwise, consume a string!
|
|
|
|
var v = this.string('utf8', len);
|
|
|
|
if (v.length > 0) {
|
|
|
|
parts.push(v);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (endAt && this.tell() >= endAt) {
|
|
|
|
debug('leaving at', endAt);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
len = this.byte();
|
|
|
|
debug('got len', len);
|
|
|
|
}
|
|
|
|
if (!comp) {
|
|
|
|
end = this.tell();
|
|
|
|
}
|
|
|
|
debug('ended with %d parts at %d', parts.length, end);
|
|
|
|
this.seek(end);
|
|
|
|
if (join) {
|
|
|
|
ret = parts.join('.');
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ret = parts;
|
|
|
|
}
|
|
|
|
debug('ret', ret);
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
|
|
|
|