diff --git a/README.md b/README.md index 7b3b2c7..1a34ef2 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ Similar API to `dns.js` and `native-dns-packet`. , "answer": [] , "authority": [] , "additional": [] +, "edns_options": [] , "byteLength": 44 } ``` diff --git a/dns.packer.js b/dns.packer.js index 069b6c8..210d076 100644 --- a/dns.packer.js +++ b/dns.packer.js @@ -1,15 +1,21 @@ (function (exports) { 'use strict'; +var classes = exports.DNS_CLASSES || require('./dns.classes.js').DNS_CLASSES; +var types = exports.DNS_TYPES || require('./dns.types.js').DNS_TYPES; + var dnspack = exports.DNS_PACKER = { pack: function (packet) { + + // TODO update for edns payload size + var ab = new ArrayBuffer(576); // http://serverfault.com/a/587626/93930 + var dv = new DataView(ab); + var labelsMap = {}; var total = 0; var id = packet.header.id; // 2 bytes var header = dnspack.packHeader(packet.header); // 2 bytes total = 12; // 2+2+2+2 bytes ({qd,an,ns,ar}count) - var ab = new ArrayBuffer(total); - var dv = new DataView(ab); dv.setUint16(0, id, false); dv.setUint16(2, header, false); dv.setUint16(4, packet.question.length, false); @@ -18,8 +24,112 @@ var dnspack = exports.DNS_PACKER = { // EDNS is added as an additional with TYPE 41 (OPT, 0x29) dv.setUint16(10, packet.additional.length + (packet.payload ? 1 : 0), false); - return ab; + 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"); + } + } + } + + function packLabel(sequence, terminates) { + 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; + }); + }); + } + + function packQuestion(q) { + lint(q); + + packLabel(q.name); + // trailing 0 (null) as label sequence terminator + dv.setUint8(total, 0, false); + total += 1; + + dv.setUint16(total, q.type, false); + dv.setUint16(total + 2, q.class, false); + total += 4; // two bytes type two bytes class + } + + function packRdata(a) { + dv.setUint16(total, 8, false); + total += 2; + dv.setUint32(total, 0xFFFFFFFF, false) + total += 4; + dv.setUint32(total, 0xFFFFFFFF, false) + total += 4; + //throw new Error('not implemented'); + } + + 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 { + packRdata(a); + } + } + + //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) { + } + */ + , packHeader: function(h) { var val = 0; @@ -36,6 +146,7 @@ var dnspack = exports.DNS_PACKER = { return val; } + }; }('undefined' !== typeof window ? window : exports)); diff --git a/dns.parser.js b/dns.parser.js index 237b389..4773bbf 100644 --- a/dns.parser.js +++ b/dns.parser.js @@ -232,6 +232,7 @@ pdns.unpack = function (ab) { packet.authority.push(rec); } + packet.edns_options = []; packet.additional = []; for (i = 0; i < packet.arcount; i += 1) { rec = unpackAnswer(ab, dv, ui8, total); diff --git a/dns.types.js b/dns.types.js index 3a690cc..015f438 100644 --- a/dns.types.js +++ b/dns.types.js @@ -14,6 +14,7 @@ var types = exports.DNS_TYPES = { , OPT: 0x29 // 41 , ANY: 0xff // 255 }; + // and in reverse Object.keys(types).forEach(function (key) { types[types[key]] = key; diff --git a/test/packer.js b/test/packer.js index c380ce4..fb3457c 100644 --- a/test/packer.js +++ b/test/packer.js @@ -32,9 +32,9 @@ h = '0' + h; } return h; - }).join('').match(/.{1,2}/g).join(' ').match(/.{1,47}/g).map(function (str) { + }).join('').match(/.{1,2}/g).join(' ').match(/.{1,48}/g).map(function (str) { var lead = bytecount.toString(16); - bytecount += 10; + bytecount += 16; while (lead.length < 7) { lead = '0' + lead; @@ -51,10 +51,15 @@ console.log('DEBUG with hexdump: '); console.log('hexdump ' + onefile.replace(/\.[^\.]*$/, '.bin')); console.log(''); + console.log(' 0 1 2 3 4 5 6 7 8 9 A B C D E F'); console.log(str + '\n' + trail); console.log(''); - console.error('test implementation not complete'); + console.error(''); + console.error('!!!'); + console.error('Test implementation not complete.'); + console.error('!!!'); + console.error(''); process.exit(1); }());