dns-suite.js/dns.unpack-labels.js

100 lines
3.0 KiB
JavaScript
Raw Normal View History

(function (exports) {
'use strict';
// unpack labels with 0x20 compression pointer support
// ui8 is the ArrayBuffer of the entire packet
// ptr is the current index of the packet
// q = { byteLength: 0, cpcount: 0, labels: [], name: '' }
2017-02-03 02:43:56 +00:00
//
// NOTE:
// "NAME"s are terminated with 0x00
2017-10-09 20:04:06 +00:00
// "RDATA" is terminated by its length, but its labels may null-terminated
exports.DNS_UNPACK_LABELS = function (ui8, ptr, q) {
if (q.cpcount > 25) {
throw new Error("compression pointer loop detected (over 25 pointers seems like a loop)");
}
var total = ptr;
var i;
var len;
var label = [];
do {
label.length = 0;
len = ui8[total];
if (len === undefined){
2017-02-03 02:43:56 +00:00
// RDATA is terminated by undefined, not len === 0
2017-10-09 19:48:54 +00:00
q.trunc = true;
2017-02-03 02:43:56 +00:00
break;
}
2017-02-03 02:43:56 +00:00
// Handle message compression pointers. See 4.1.4 of RFC1035 for details.
2017-10-09 19:48:54 +00:00
// 0xc0 // 192 // parseInt('11000000', 2).toString(16)
if (len >= 0xc0) {
2017-10-09 19:48:54 +00:00
// Only the two highest bits are used to signify the pointer.
// The remaining bits may be used to specify the address of the pointer
// (it would seem that only 1 extra bit is actually used since the message size is 512 bytes)
var cpptr = ((ui8[total] & 0x3f) << 8) | ui8[total + 1];
2017-10-09 20:04:06 +00:00
if (cpptr >= total) {
throw new Error(
"Compression pointer at"
+ " 0x" + total.toString() + " (" + total + ")"
+ " points forward to"
+ " 0x" + cpptr.toString(16) + " (" + cpptr + ")"
);
}
// We're not coming back, so if this is the first time we're following one of
// these pointers we up the byteLength to mark the pointer as part of the label
if (!q.cpcount) {
q.byteLength += 2; // cp and len
2017-10-09 20:04:06 +00:00
//q.cps = [];
}
q.cpcount += 1;
2017-10-09 20:04:06 +00:00
//q.cps.push(cpptr);
2017-02-03 02:43:56 +00:00
// recursion
return exports.DNS_UNPACK_LABELS(ui8, cpptr, q);
}
2017-02-03 02:43:56 +00:00
//str.length = 0; // fast empty array
if (ui8.byteLength - total < len) {
//console.error('-1', ui8[total - 1]);
//console.error(' 0', ui8[total]);
//console.error(' 1', ui8[total + 1]);
//console.error(' 1', ui8[total + 2]);
throw new Error(
"Expected a string of length " + len
+ " but packet itself has " + (ui8.byteLength - total) + " bytes remaining"
);
}
2017-02-03 02:43:56 +00:00
// Advance the pointer to after the length indicator, then read the string in.
total += 1;
for (i = 0; i < len; i += 1) {
// TODO check url-allowable characters
label.push(String.fromCharCode(ui8[total]));
total += 1;
}
2017-02-03 02:43:56 +00:00
if (label.length) {
q.labels.push(label.join(''));
}
2017-02-03 02:43:56 +00:00
// It is important this be done every time, so that if we run into a message compression
// pointer later we still have a record of what was consumed before that.
if (0 === q.cpcount) {
q.byteLength = total - ptr;
}
2017-02-03 02:43:56 +00:00
} while (0 !== len && undefined !== len);
//str.pop(); // remove trailing '.'
q.name = q.labels.join('.');
return q;
};
}('undefined' !== typeof window ? window : exports));