2017-01-30 23:30:08 +00:00
|
|
|
(function (exports) {
|
|
|
|
'use strict';
|
|
|
|
|
2017-02-16 21:16:21 +00:00
|
|
|
var classes = exports.DNS_CLASSES || require('./dns.classes.js').DNS_CLASSES;
|
|
|
|
var types = exports.DNS_TYPES || require('./dns.types.js').DNS_TYPES;
|
|
|
|
|
2017-02-11 22:41:01 +00:00
|
|
|
var dnspack = exports.DNS_PACKER = {
|
|
|
|
pack: function (packet) {
|
2017-02-16 21:16:21 +00:00
|
|
|
|
|
|
|
// TODO update for edns payload size
|
|
|
|
var ab = new ArrayBuffer(576); // http://serverfault.com/a/587626/93930
|
|
|
|
var dv = new DataView(ab);
|
|
|
|
var labelsMap = {};
|
2017-02-11 22:41:01 +00:00
|
|
|
var total = 0;
|
|
|
|
var id = packet.header.id; // 2 bytes
|
|
|
|
var header = dnspack.packHeader(packet.header); // 2 bytes
|
2017-02-11 23:01:02 +00:00
|
|
|
total = 12; // 2+2+2+2 bytes ({qd,an,ns,ar}count)
|
2017-02-11 22:41:01 +00:00
|
|
|
|
|
|
|
dv.setUint16(0, id, false);
|
|
|
|
dv.setUint16(2, header, false);
|
2017-02-11 23:01:02 +00:00
|
|
|
dv.setUint16(4, packet.question.length, false);
|
|
|
|
dv.setUint16(6, packet.answer.length, false);
|
|
|
|
dv.setUint16(8, packet.authority.length, false);
|
|
|
|
// EDNS is added as an additional with TYPE 41 (OPT, 0x29)
|
|
|
|
dv.setUint16(10, packet.additional.length + (packet.payload ? 1 : 0), false);
|
2017-02-11 22:41:01 +00:00
|
|
|
|
2017-02-16 21:16:21 +00:00
|
|
|
function lint(r) {
|
|
|
|
if (!r.name) {
|
|
|
|
throw new Error("missing name");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!r.class) {
|
|
|
|
if (!r.className) {
|
|
|
|
throw new Error("no className");
|
|
|
|
}
|
|
|
|
r.class = classes[r.className];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!r.type) {
|
|
|
|
if (!r.typeName) {
|
|
|
|
throw new Error("no typeName");
|
|
|
|
}
|
|
|
|
r.type = types[r.typeName];
|
|
|
|
if (!r.type) {
|
|
|
|
console.warn("ignoring invalid type '" + r.type + "' for '" + r.name + "', ignoring");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-16 23:29:23 +00:00
|
|
|
function packLabelSequence(sequence, terminates) {
|
|
|
|
console.log('sequence:', sequence);
|
|
|
|
if (labelsMap[sequence]) {
|
|
|
|
console.log('cached sequence:', sequence);
|
|
|
|
// minimal compression pointer 0xc0 (192)
|
|
|
|
dv.setUint8(total, 0xc0, false);
|
|
|
|
total += 1;
|
|
|
|
dv.setUint8(total, labelsMap[sequence].total, false);
|
|
|
|
total += 1;
|
|
|
|
return;
|
|
|
|
}
|
2017-02-16 21:16:21 +00:00
|
|
|
if (terminates) {
|
|
|
|
// we don't want non-terminating rdata cached, just terminating names
|
|
|
|
labelsMap[total] = { total: total, name: sequence };
|
|
|
|
labelsMap[sequence] = labelsMap[total];
|
|
|
|
}
|
|
|
|
sequence.split('.').forEach(function (label) {
|
|
|
|
dv.setUint8(total, label.length, false);
|
|
|
|
total += 1;
|
|
|
|
|
|
|
|
label.split('').forEach(function (ch) {
|
|
|
|
dv.setUint8(total, ch.charCodeAt(0), false);
|
|
|
|
total += 1;
|
|
|
|
});
|
|
|
|
});
|
2017-02-16 23:29:23 +00:00
|
|
|
if (terminates) {
|
|
|
|
// trailing 0 (null) as label sequence terminator
|
|
|
|
dv.setUint8(total, 0, false);
|
|
|
|
total += 1;
|
|
|
|
}
|
2017-02-16 21:16:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function packQuestion(q) {
|
|
|
|
lint(q);
|
|
|
|
|
2017-02-16 23:29:23 +00:00
|
|
|
packLabelSequence(q.name, true);
|
2017-02-16 21:16:21 +00:00
|
|
|
|
|
|
|
dv.setUint16(total, q.type, false);
|
2017-02-16 23:29:23 +00:00
|
|
|
total += 2;
|
|
|
|
dv.setUint16(total, q.class, false);
|
|
|
|
total += 2;
|
2017-02-16 21:16:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function packAnswer(a) {
|
|
|
|
packQuestion(a);
|
|
|
|
|
|
|
|
if ('undefined' === typeof a.ttl) {
|
|
|
|
throw new Error("missing ttl");
|
|
|
|
}
|
|
|
|
|
|
|
|
// 32-bit ttl
|
|
|
|
dv.setUint32(total, a.ttl, false);
|
|
|
|
total += 4;
|
|
|
|
|
|
|
|
if (a.rdata) {
|
|
|
|
// 16-bit
|
|
|
|
dv.setUint16(total, a.rdata.byteLength, false);
|
|
|
|
total += 2;
|
|
|
|
(new Uint8Array(a.rdata)).forEach(function (b) {
|
|
|
|
dv.setUint8(total, b, false);
|
|
|
|
total += 1;
|
|
|
|
});
|
|
|
|
} else {
|
2017-02-16 22:57:15 +00:00
|
|
|
total = dnspack.packRdata(ab, dv, total, a);
|
2017-02-16 21:16:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//console.log(packet);
|
|
|
|
// fixed size, other than labels
|
|
|
|
|
|
|
|
packet.question.forEach(packQuestion);
|
|
|
|
packet.answer.forEach(packAnswer);
|
|
|
|
packet.authority.forEach(packAnswer);
|
|
|
|
packet.additional.forEach(packAnswer);
|
|
|
|
|
|
|
|
// TODO handle compression pointers
|
|
|
|
// TODO handle edns
|
|
|
|
|
|
|
|
return ab.slice(0, total);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
, packLabels: function(map, labels) {
|
2017-02-11 22:41:01 +00:00
|
|
|
}
|
2017-02-16 21:16:21 +00:00
|
|
|
*/
|
|
|
|
|
2017-02-11 22:41:01 +00:00
|
|
|
, packHeader: function(h) {
|
|
|
|
var val = 0;
|
|
|
|
|
|
|
|
val += (h.qr << 15) & 0x8000;
|
|
|
|
val += (h.opcode << 11) & 0x7800;
|
|
|
|
val += (h.aa << 10) & 0x400;
|
|
|
|
val += (h.tc << 9) & 0x200;
|
|
|
|
val += (h.rd << 8) & 0x100;
|
|
|
|
val += (h.ra << 7) & 0x80;
|
|
|
|
val += (h.res1 << 6) & 0x40;
|
|
|
|
val += (h.res2 << 5) & 0x20;
|
|
|
|
val += (h.res3 << 4) & 0x10;
|
|
|
|
val += h.rcode & 0xF;
|
|
|
|
|
|
|
|
return val;
|
2017-01-30 23:30:08 +00:00
|
|
|
}
|
2017-02-16 21:16:21 +00:00
|
|
|
|
2017-01-30 23:30:08 +00:00
|
|
|
};
|
2017-02-16 22:52:07 +00:00
|
|
|
dnspack.packRdata = exports.DNS_RDATA_PACK || require('./dns.rdata.pack.js').DNS_RDATA_PACK;
|
2017-01-30 23:30:08 +00:00
|
|
|
|
|
|
|
}('undefined' !== typeof window ? window : exports));
|