217 lines
6.3 KiB
JavaScript
217 lines
6.3 KiB
JavaScript
;(function (exports) {
|
|
'use strict';
|
|
|
|
var pdns = exports.DNS_PARSER = {};
|
|
|
|
var classes = exports.DNS_CLASSES || require('./dns.classes.js').DNS_CLASSES;
|
|
var types = exports.DNS_TYPES || require('./dns.types.js').DNS_TYPES;
|
|
|
|
// Order http://www.zytrax.com/books/dns/ch15/
|
|
var packet = {};
|
|
pdns.unpackHeader = function (i) {
|
|
// i is one element from a Uint16Array (as a 16-bit unsigned integer)
|
|
|
|
var header = {
|
|
id: 0 // added here to preserve console.log order
|
|
|
|
, qr: (i & 0x8000) >> 15 // Query Response (0 is question, 1 is response)
|
|
, opcode: (i & 0x7800) >> 11 // 0 is question
|
|
, aa: (i & 0x400) >> 10 // Authoritative Answer (response-only)
|
|
, tc: (i & 0x200) >> 9 // TrunCated - expect another packet with same (?) id
|
|
, rd: (i & 0x100) >> 8 // Recursion Desired
|
|
|
|
, ra: (i & 0x80) >> 7
|
|
, res1: (i & 0x40) >> 6 // z
|
|
, res2: (i & 0x20) >> 5 // ad
|
|
, res3: (i & 0x10) >> 4 // cd
|
|
, rcode: (i & 0xF) // Error Code (response-only)
|
|
};
|
|
|
|
return header;
|
|
};
|
|
|
|
pdns._unpackLabels = exports.DNS_UNPACK_LABELS || require('./dns.unpack-labels.js').DNS_UNPACK_LABELS;
|
|
|
|
pdns.unpack = function (ab) {
|
|
if (ab.buffer) {
|
|
ab = ab.buffer;
|
|
}
|
|
|
|
// SANITY Check
|
|
if (ab.byteLength < 12) {
|
|
throw new Error(
|
|
"A DNS header has a minimum length of 12 bytes but this packet has only " + ab.byteLength + "bytes."
|
|
);
|
|
}
|
|
|
|
// DO: new Uint8Array(arrayBuffer);
|
|
// DO NOT: Uint8Array.from(arrayBuffer); // WILL NOT WORK
|
|
|
|
// DO: new DataView(arrayBuffer).getUint16(7, false);
|
|
// DO NOT: arrayBuffer.slice();
|
|
//
|
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
|
|
// https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView
|
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array
|
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/slice
|
|
|
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView
|
|
var dv = new DataView(ab);
|
|
var id = dv.getUint16(0, false);
|
|
packet.header = pdns.unpackHeader(dv.getUint16(2, false));
|
|
var qdcount = dv.getUint16(4, false); // query count
|
|
var ancount = dv.getUint16(6, false); // answer count
|
|
var nscount = dv.getUint16(8, false); // authority count
|
|
var arcount = dv.getUint16(10, false); // additional count
|
|
var total = 12;
|
|
var i;
|
|
var rec;
|
|
var ui8 = new Uint8Array(ab);
|
|
|
|
// TODO move to pdns.unpackQuestion to make testable
|
|
function unpackQuestion(ab, dv, ui8, total) {
|
|
var ototal = total;
|
|
var q = pdns._unpackLabels(ui8, total, {
|
|
name: '' // ex: daplie.com
|
|
, type: 0 // ex: 1
|
|
, typeName: '' // ex: A
|
|
, class: 0 // ex: 1
|
|
, className: '' // ex: IN
|
|
, byteLength: 0 // the total byte length (including pointers, but not their labels)
|
|
, labels: [] // an array of the labels individually
|
|
, cpcount: 0 // the number of compression pointers traversed
|
|
});
|
|
//console.log('unpackQuestion QNAME:');
|
|
//console.log(q);
|
|
total += q.byteLength;
|
|
|
|
|
|
|
|
|
|
|
|
if (ab.byteLength - total < 4) {
|
|
// console.error(str.join(''));
|
|
throw new Error(
|
|
"Expected a 2-byte QTYPE and 2-byte QCLASS following " + total + "-byte QNAME"
|
|
+ " but packet itself has " + (ab.byteLength - total) + " bytes remaining"
|
|
);
|
|
}
|
|
|
|
q.type = dv.getUint16(total, false);
|
|
total += 2;
|
|
q.class = dv.getUint16(total, false);
|
|
total += 2;
|
|
q.byteLength = total - ototal;
|
|
|
|
q.className = classes[q.class];
|
|
q.typeName = types[q.type];
|
|
|
|
// console.log('*********************');
|
|
// console.log(q.typeName);
|
|
|
|
return q;
|
|
}
|
|
|
|
function unpackAnswer(ab, dv, ui8, total) {
|
|
var ototal = total;
|
|
var q = pdns._unpackLabels(ui8, total, {
|
|
name: ''
|
|
, type: 0
|
|
, typeName: ''
|
|
, class: 0
|
|
, className: ''
|
|
, byteLength: 0
|
|
, labels: []
|
|
, cpcount: 0
|
|
|
|
, rdstart: 0
|
|
//, rdata: 0
|
|
, rdlength: 0
|
|
});
|
|
//console.log('unpackAnswer QNAME:');
|
|
//console.log(q);
|
|
total += q.byteLength;
|
|
q.className = classes[q.class];
|
|
q.typeName = types[q.type];
|
|
|
|
if (ab.byteLength - total < 10) {
|
|
// console.error(str.join(''));
|
|
throw new Error(
|
|
"Expected a 2-byte QTYPE, 2-byte QCLASS, 4-byte TTL, and 2-byte RDLENGTH following " + total + "-byte QNAME"
|
|
+ " but packet itself has " + (ab.byteLength - total) + " bytes remaining"
|
|
);
|
|
}
|
|
|
|
q.type = dv.getUint16(total, false);
|
|
total += 2;
|
|
q.class = dv.getUint16(total, false);
|
|
total += 2;
|
|
q.ttl = dv.getUint32(total, false);
|
|
total += 4;
|
|
q.rdlength = dv.getUint16(total, false);
|
|
total += 2;
|
|
|
|
q.className = classes[q.class];
|
|
q.typeName = types[q.type];
|
|
|
|
|
|
// TODO actually parse RDATA
|
|
q.rdstart = total;
|
|
// console.log('q.rdata', q.rdlength, 'bytes:');
|
|
// console.log(new Uint8Array(ab).slice(total, total + q.rdlength));
|
|
//q.rdata = Array.prototype.slice.apply(q.rdata);
|
|
|
|
//q.rdend = q.rdstart + q.rdlength;
|
|
|
|
total += q.rdlength;
|
|
q.byteLength = total - ototal;
|
|
return q;
|
|
}
|
|
|
|
packet.header.id = id;
|
|
|
|
|
|
console.log('qdcount', qdcount);
|
|
packet.question = [];
|
|
for (i = 0; i < qdcount; i += 1) {
|
|
rec = unpackQuestion(ab, dv, ui8, total);
|
|
total += rec.byteLength;
|
|
packet.question.push(rec);
|
|
}
|
|
|
|
console.log('ancount', ancount);
|
|
packet.answer = [];
|
|
for (i = 0; i < ancount; i += 1) {
|
|
rec = unpackAnswer(ab, dv, ui8, total);
|
|
total += rec.byteLength;
|
|
packet.answer.push(rec);
|
|
}
|
|
|
|
console.log('nscount', nscount);
|
|
packet.authority = [];
|
|
for (i = 0; i < nscount; i += 1) {
|
|
rec = unpackAnswer(ab, dv, ui8, total);
|
|
total += rec.byteLength;
|
|
packet.authority.push(rec);
|
|
}
|
|
|
|
console.log('arcount', arcount);
|
|
packet.additional = [];
|
|
for (i = 0; i < arcount; i += 1) {
|
|
rec = unpackAnswer(ab, dv, ui8, total);
|
|
total += rec.byteLength;
|
|
packet.additional.push(rec);
|
|
}
|
|
|
|
if (ab.byteLength !== total) {
|
|
throw new Error(
|
|
"Parsed " + total + " bytes, but packet is " + ab.byteLength + " bytes."
|
|
);
|
|
}
|
|
packet.byteLength = total;
|
|
return packet;
|
|
};
|
|
pdns.unpackRdata = exports.DNS_RDATA_PARSE || require('./dns.rdata.parse.js').DNS_RDATA_PARSE;
|
|
|
|
}('undefined' !== typeof window ? window : exports));
|