diff --git a/packer/type.cname.js b/packer/type.cname.js index 3ca92fe..afa823e 100644 --- a/packer/type.cname.js +++ b/packer/type.cname.js @@ -15,14 +15,14 @@ exports.DNS_PACKER_TYPE_CNAME = function (ab, dv, total, record) { // RDATA // a sequence of labels record.data.split('.').forEach(function (label) { - cnameLen += 1 + label.length; - - dv.setUint8(total, label.length, false); - total += 1; + cnameLen += 1 + label.length; + + dv.setUint8(total, label.length, false); + total += 1; - label.split('').forEach(function (ch) { - dv.setUint8(total, ch.charCodeAt(0), false); - total += 1; + label.split('').forEach(function (ch) { + dv.setUint8(total, ch.charCodeAt(0), false); + total += 1; }); }); diff --git a/packer/type.ns.js b/packer/type.ns.js new file mode 100644 index 0000000..20a0ee1 --- /dev/null +++ b/packer/type.ns.js @@ -0,0 +1,38 @@ +(function (exports) { +'use strict'; + + +// NS name for the supplied domain. May be label, pointer or any combination + +exports.DNS_PACKER_TYPE_NS = function (ab, dv, total, record) { + if(!record.data){ + throw new Error("no data on NS record"); + } + + + var nsLen = 0; + var rdLenIndex = total; + total +=2; + + // RDATA + // a sequence of labels + record.data.split('.').forEach(function(label) { + nsLen += 1 + label.length; + + dv.setUint8(total, label.length, false); + total += 1; + + label.split('').forEach(function (ch){ + dv.setUint8(total, ch.charCodeAt(0), false); + total += 1; + }); + }); + + // RDLENGTH + dv.setUint16(rdLenIndex, record.data.length + 1, false); + + + return total; +}; + +}('undefined' !== typeof window ? window : exports)); diff --git a/packer/type.ptr.js b/packer/type.ptr.js new file mode 100644 index 0000000..51db54c --- /dev/null +++ b/packer/type.ptr.js @@ -0,0 +1,37 @@ +(function (exports) { +'use strict'; + +// The host name that represents the supplied UP address +// May be a label, pointer or any combination + +exports.DNS_PACKER_TYPE_PTR = function (ab, dv, total, record) { + if (!record.data) { + throw new Error("no data for PTR record"); + } + + var ptrLen = 0; + var rdLenIndex = total; + total += 2; + + // RDATA + // a sequence of labels + record.data.split('.').forEach(function (label){ + console.log("the labels are: " + label); + ptrLen += 1 + label.length; + + dv.setUint8(total, label.length, false); + total += 1; + + label.split('').forEach(function (ch){ + dv.setUint8(total, ch.charCodeAt(0), false); + total += 1; + }); + }); + + // RDLENGTH + dv.setUint16(rdLenIndex, record.data.length + 1, false); + + return total; +}; + +}('undefined' !== typeof window ? window : exports)); diff --git a/packer/type.soa.js b/packer/type.soa.js new file mode 100644 index 0000000..d79a9f5 --- /dev/null +++ b/packer/type.soa.js @@ -0,0 +1,87 @@ +(function (exports) { +'use strict'; + +// Value Meaning/Use +// Primary NS Variable length. The name of the Primary Master for the domain. May be a label, pointer, or any combination +// Admin MB Variable length. The administrator's mailbox. May be a label, pointer, or any combination +// Serial Number Unsigned 32-bit integer +// Refresh Interval Unsigned 32-bit integer +// Retry Interval Unsigned 32-bit integer +// Expiration Limit Unsigned 32-bit integer +// Minimum TTL Unsigned 32-bit integer + + + +exports.DNS_PACKER_TYPE_SOA = function (ab, dv, total, record) { + if(!record.name_server){ + throw new Error("no name server for SOA record"); + } + if(!record.email_addr){ + throw new Error("ne email address for SOA record"); + } + if(!record.sn){ + throw new Error("no serial number for SOA record"); + } + if(!record.ref){ + throw new Error("no serial number for SOA record"); + } + if(!record.ret){ + throw new Error("no serial number for SOA record"); + } + if(!record.ex){ + throw new Error("no serial number for SOA record"); + } + if(!record.nx){ + throw new Error("no serial number for SOA record"); + } + + var soaLen = 20; // take into account sn, ref, ret, ex, and nx + // (32-bits each. 4Bytes * 5 = 20) + var rdLenIndex = total; + total += 2; // Save space for RDLENGTH + + // pack name_server which is a sequence of labels + record.name_server.split('.').forEach(function(label){ + soaLen += 1 + label.length; + + dv.setUint8(total, label.length, false); + total += 1; + + label.split('').forEach(function(ch) { + dv.setUint8(total, ch.charCodeAt(0), false); + total += 1; + }); + }); + + // pack email address which is a sequence of labels + record.email_addr.split('.').forEach(function (label){ + soaLen += 1 + label.length; + + dv.setUint8(total, label.length, false); + total += 1; + + label.split('').forEach(function (ch){ + dv.setUint8(total, ch.charCodeAt(0), false); + total += 1; + }); + }); + + // pack all 32-bit values + dv.setUint32(total, parseInt(record.sn, 10), false); + total+=4; + dv.setUint32(total, parseInt(record.ref, 10), false); + total+=4; + dv.setUint32(total, parseInt(record.ret, 10), false); + total+=4; + dv.setUint32(total, parseInt(record.ex, 10), false); + total+=4; + dv.setUint32(total, parseInt(record.nx, 10), false); + total+=4; + + // RDLENGTH + dv.setUint16(rdLenIndex, soaLen, false); + + return total; +}; + +}('undefined' !== typeof window ? window : exports)); diff --git a/packer/type.srv.js b/packer/type.srv.js new file mode 100644 index 0000000..dee2731 --- /dev/null +++ b/packer/type.srv.js @@ -0,0 +1,69 @@ +(function (exports) { +'use strict'; + +// SRV RDATA contains: +// Priority: The relative priority of this service. 16-bit (range 0-65535) +// Weight: Used when more than one serivice has the same priority. 16-bit +// (range 0-65535) +// Port: Port number assigned to the symbolic service. 16-bit (range 0-65535) +// Target: The name of the host that will provide service. + +exports.DNS_PACKER_TYPE_SRV = function (ab, dv, total, record) { + + // maybe these should be changed to 'hasOwnProperty' for all of these + // TODO: Check that number is in range 1-64k + if (!record.priority){ + throw new Error("no priority for SRV record"); + } + if (!record.hasOwnProperty('weight')){ + throw new Error("no weight for SRV record"); + } + if (!record.port){ + throw new Error("no port for SRV record"); + } + if (!record.target) { + throw new Error("no target for SRV record"); + } + + // console.log("record length, priority, weight, port, then target"); + // console.log("record priority is: " + record.priority); + // console.log("record weight is: " + record.weight); + // console.log("record port is: " + record.port); + // console.log("record target is: " + record.target); + // console.log("total length currently is: " + total); + + + var srvLen = 6; // 16-bit priority, weight and port = 6 Bytes + var rdLenIndex = total; + + total+=2; // space for RDLENGTH + + dv.setUint16(total, parseInt(record.priority, 10), false); + total+=2; + + dv.setUint16(total,parseInt(record.weight, 10), false); + total+=2; + + dv.setUint16(total, parseInt(record.port, 10), false); + total+=2; + + record.target.split('.').forEach(function (label){ + srvLen += 1 + label.length; + + dv.setUint8(total, label.length, false); + total += 1; + + label.split('').forEach(function (ch) { + dv.setUint8(total, ch.charCodeAt(0), false); + total += 1; + }); + }); + + // RDLENGTH + + dv.setUint16(rdLenIndex, srvLen, false); + + return total; +}; + +}('undefined' !== typeof window ? window : exports)); diff --git a/packer/type.txt.js b/packer/type.txt.js new file mode 100644 index 0000000..dee1b66 --- /dev/null +++ b/packer/type.txt.js @@ -0,0 +1,43 @@ +(function (exports) { +'use strict'; + +// Record type is just any text. + +exports.DNS_PACKER_TYPE_TXT = function (ab, dv, total, record) { + if (!record.data){ + throw new Error("no data for TXT record"); + } + + var txtLen = 0; + var rdLenIndex = total; + total += 3; + + // RDATA + console.log("what is my record data: " + typeof record.data[0]); + console.log("what are my labels? "); + // var res = record.data[0].split(" "); + // console.log("Res: " + res); + + console.log("for each rdata"); + record.data.forEach(function(str){ + + str.split('').forEach(function(ch){ + + + txtLen += 1; + // console.log(chcim); + dv.setUint8(total, ch.charCodeAt(0), false); + total += 1; + + }); + }); + + console.log("txt rdata length is: " + txtLen); + dv.setUint16(rdLenIndex, txtLen+1, false); + dv.setUint8(rdLenIndex+2, txtLen, false); + // total +=1; + + return total; +}; + +}('undefined' !== typeof window ? window : exports)); diff --git a/parser/type.soa.js b/parser/type.soa.js index b468e8a..534197a 100644 --- a/parser/type.soa.js +++ b/parser/type.soa.js @@ -1,21 +1,66 @@ (function (exports) { 'use strict'; -// TODO. Not yet implemented - // Value Meaning/Use // Primary NS Variable length. The name of the Primary Master for the domain. May be a label, pointer, or any combination // Admin MB Variable length. The administrator's mailbox. May be a label, pointer, or any combination // Serial Number Unsigned 32-bit integer // Refresh Interval Unsigned 32-bit integer // Retry Interval Unsigned 32-bit integer -// Retry Interval Unsigned 32-bit integer // Expiration Limit Unsigned 32-bit integer // Minimum TTL Unsigned 32-bit integer +var unpackLabels = exports.DNS_UNPACK_LABELS || require('../dns.unpack-labels.js').DNS_UNPACK_LABELS; exports.DNS_PARSER_TYPE_SOA = function (ab, packet, record) { + var rdataAb = ab.slice(record.rdstart, record.rdstart + record.rdlength); + var dv = new DataView(rdataAb); + + // we need this information for this parser + var cpcount = unpackLabels(new Uint8Array(ab), record.rdstart, { byteLength: 0, cpcount: 0, labels: [], name: '' }).cpcount; + var offset = unpackLabels(new Uint8Array(ab), record.rdstart, { byteLength: 0, cpcount: 0, labels: [], name: '' }).byteLength; + var labels = unpackLabels(new Uint8Array(ab), record.rdstart, { byteLength: 0, cpcount: 0, labels: [], name: '' }).labels; + + // Primary NS + record.name_server = unpackLabels(new Uint8Array(ab), record.rdstart, { byteLength: 0, cpcount: 0, labels: [], name: '' }).name; + + // if there exists compression pointers in the rdata + if (cpcount > 0){ + // do something awesome with compression pointers to get the email address + // I need the length of all the data before the email address starts. + // if there are compression pointers then there will be a byte to indicate the length of each label, the label, + // then there will be a compression pointer to grab the longest label. + + var start = 2; // start or email_addr. take into account compression pointer and address length + for(var i = 0; i < labels.length; i++){ + + // increase start by the label length. the +1 is to take into account the next label size byte + start = start + labels[i].length + 1; + // check for cpcount. 2 counts behind + if(parseInt(dv.getUint8(start - 2), 10) === 192){ + record.email_addr = unpackLabels(new Uint8Array(ab), record.rdstart + start ,{ byteLength: 0, cpcount: 0, labels: [], name: '' }).name; + } + } + } // if there are no compression pointers, we can get the email address directly from the offset + else { + + record.email_addr = unpackLabels(new Uint8Array(ab), record.rdstart + offset, { byteLength: 0, cpcount: 0, labels: [], name: '' }).name; + } + // Serial Number + record.sn = dv.getUint32(dv.byteLength - 20, false); + // Refresh Interval + record.ref = dv.getUint32(dv.byteLength - 16, false); + // Retry Interval + record.ret = dv.getUint32(dv.byteLength - 12, false); + // Expiration Limit + record.ex = dv.getUint32(dv.byteLength - 8, false); + // Minimum TTL + record.nx = dv.getUint32(dv.byteLength - 4, false); + + + return record; + }; }('undefined' !== typeof window ? window : exports)); diff --git a/parser/type.srv.js b/parser/type.srv.js index 3628d82..63ebcde 100644 --- a/parser/type.srv.js +++ b/parser/type.srv.js @@ -1,6 +1,5 @@ (function (exports) { 'use strict'; -// TODO. Not yet implemented // SRV identifies the host(s) that will support a particular service. It // is a general purpose RR to discover any service. diff --git a/test/soa_test/google.com.soa.0.bin b/test/soa_test/google.com.soa.0.bin new file mode 100644 index 0000000..dd9cc9a Binary files /dev/null and b/test/soa_test/google.com.soa.0.bin differ diff --git a/test/soa_test/google.com.soa.0.json b/test/soa_test/google.com.soa.0.json new file mode 100644 index 0000000..cb84dc8 --- /dev/null +++ b/test/soa_test/google.com.soa.0.json @@ -0,0 +1,63 @@ +{ + "header": { + "id": 52080, + "qr": 1, + "opcode": 0, + "aa": 0, + "tc": 0, + "rd": 1, + "ra": 1, + "res1": 0, + "res2": 0, + "res3": 0, + "rcode": 0 + }, + "qdcount": 1, + "ancount": 1, + "nscount": 0, + "arcount": 0, + "question": [ + { + "name": "google.com", + "type": 6, + "typeName": "SOA", + "class": 1, + "className": "IN", + "byteLength": 16, + "labels": [ + "google", + "com" + ], + "cpcount": 0 + } + ], + "answer": [ + { + "name": "google.com", + "type": 6, + "typeName": "SOA", + "class": 1, + "className": "IN", + "byteLength": 50, + "labels": [ + "google", + "com" + ], + "cpcount": 1, + "rdstart": 40, + "rdlength": 38, + "ttl": 59, + "name_server": "ns3.google.com", + "email_addr": "dns-admin.google.com", + "sn": 150355194, + "ref": 900, + "ret": 900, + "ex": 1800, + "nx": 60 + } + ], + "authority": [], + "edns_options": [], + "additional": [], + "byteLength": 78 +} \ No newline at end of file diff --git a/test/soa_test/google.com.soa.query.bin b/test/soa_test/google.com.soa.query.bin new file mode 100644 index 0000000..ca6b44b Binary files /dev/null and b/test/soa_test/google.com.soa.query.bin differ diff --git a/test/soa_test/google.com.soa.query.json b/test/soa_test/google.com.soa.query.json new file mode 100644 index 0000000..ab5efda --- /dev/null +++ b/test/soa_test/google.com.soa.query.json @@ -0,0 +1,19 @@ +{ + "header": { + "id": 52080, + "qr": 0, + "opcode": 0, + "aa": 0, + "tc": 0, + "rd": 1, + "ra": 0, + "rcode": 0 + }, + "question": [ + { + "name": "google.com", + "typeName": "SOA", + "className": "IN" + } + ] +} \ No newline at end of file diff --git a/test/txt_test/google.com.txt.0.bin b/test/txt_test/google.com.txt.0.bin new file mode 100644 index 0000000..f29c4a8 Binary files /dev/null and b/test/txt_test/google.com.txt.0.bin differ diff --git a/test/txt_test/google.com.txt.0.json b/test/txt_test/google.com.txt.0.json new file mode 100644 index 0000000..e50d47a --- /dev/null +++ b/test/txt_test/google.com.txt.0.json @@ -0,0 +1,201 @@ +{ + "header": { + "id": 430, + "qr": 1, + "opcode": 0, + "aa": 0, + "tc": 0, + "rd": 1, + "ra": 1, + "res1": 0, + "res2": 0, + "res3": 0, + "rcode": 0 + }, + "qdcount": 1, + "ancount": 1, + "nscount": 4, + "arcount": 4, + "question": [ + { + "name": "google.com", + "type": 16, + "typeName": "TXT", + "class": 1, + "className": "IN", + "byteLength": 16, + "labels": [ + "google", + "com" + ], + "cpcount": 0 + } + ], + "answer": [ + { + "name": "google.com", + "type": 16, + "typeName": "TXT", + "class": 1, + "className": "IN", + "byteLength": 48, + "labels": [ + "google", + "com" + ], + "cpcount": 1, + "rdstart": 40, + "rdlength": 36, + "ttl": 2930, + "data": [ + "v=spf1 include:_spf.google.com ~all" + ] + } + ], + "authority": [ + { + "name": "google.com", + "type": 2, + "typeName": "NS", + "class": 1, + "className": "IN", + "byteLength": 18, + "labels": [ + "google", + "com" + ], + "cpcount": 1, + "rdstart": 88, + "rdlength": 6, + "ttl": 82790, + "data": "ns3.google.com" + }, + { + "name": "google.com", + "type": 2, + "typeName": "NS", + "class": 1, + "className": "IN", + "byteLength": 18, + "labels": [ + "google", + "com" + ], + "cpcount": 1, + "rdstart": 106, + "rdlength": 6, + "ttl": 82790, + "data": "ns4.google.com" + }, + { + "name": "google.com", + "type": 2, + "typeName": "NS", + "class": 1, + "className": "IN", + "byteLength": 18, + "labels": [ + "google", + "com" + ], + "cpcount": 1, + "rdstart": 124, + "rdlength": 6, + "ttl": 82790, + "data": "ns1.google.com" + }, + { + "name": "google.com", + "type": 2, + "typeName": "NS", + "class": 1, + "className": "IN", + "byteLength": 18, + "labels": [ + "google", + "com" + ], + "cpcount": 1, + "rdstart": 142, + "rdlength": 6, + "ttl": 82790, + "data": "ns2.google.com" + } + ], + "edns_options": [], + "additional": [ + { + "name": "ns1.google.com", + "type": 1, + "typeName": "A", + "class": 1, + "className": "IN", + "byteLength": 16, + "labels": [ + "ns1", + "google", + "com" + ], + "cpcount": 2, + "rdstart": 160, + "rdlength": 4, + "ttl": 255588, + "address": "216.239.32.10" + }, + { + "name": "ns2.google.com", + "type": 1, + "typeName": "A", + "class": 1, + "className": "IN", + "byteLength": 16, + "labels": [ + "ns2", + "google", + "com" + ], + "cpcount": 2, + "rdstart": 176, + "rdlength": 4, + "ttl": 255577, + "address": "216.239.34.10" + }, + { + "name": "ns3.google.com", + "type": 1, + "typeName": "A", + "class": 1, + "className": "IN", + "byteLength": 16, + "labels": [ + "ns3", + "google", + "com" + ], + "cpcount": 2, + "rdstart": 192, + "rdlength": 4, + "ttl": 255599, + "address": "216.239.36.10" + }, + { + "name": "ns4.google.com", + "type": 1, + "typeName": "A", + "class": 1, + "className": "IN", + "byteLength": 16, + "labels": [ + "ns4", + "google", + "com" + ], + "cpcount": 2, + "rdstart": 208, + "rdlength": 4, + "ttl": 255595, + "address": "216.239.38.10" + } + ], + "byteLength": 212 +} \ No newline at end of file diff --git a/test/txt_test/google.com.txt.query.bin b/test/txt_test/google.com.txt.query.bin new file mode 100644 index 0000000..393b330 Binary files /dev/null and b/test/txt_test/google.com.txt.query.bin differ diff --git a/test/txt_test/google.com.txt.query.json b/test/txt_test/google.com.txt.query.json new file mode 100644 index 0000000..9361cf8 --- /dev/null +++ b/test/txt_test/google.com.txt.query.json @@ -0,0 +1,19 @@ +{ + "header": { + "id": 430, + "qr": 0, + "opcode": 0, + "aa": 0, + "tc": 0, + "rd": 1, + "ra": 0, + "rcode": 0 + }, + "question": [ + { + "name": "google.com", + "typeName": "TXT", + "className": "IN" + } + ] +} \ No newline at end of file