From 850cddff29f0ffeedff7c0e19aa0fef79e5dc408 Mon Sep 17 00:00:00 2001 From: jmaxxz Date: Sun, 12 Apr 2015 18:18:44 -0500 Subject: [PATCH 01/12] Begin refactoring of dns resource record decoder --- decode/dns.js | 299 +++++++++++++++++++++----------------------------- 1 file changed, 124 insertions(+), 175 deletions(-) diff --git a/decode/dns.js b/decode/dns.js index 6267da0..0368824 100644 --- a/decode/dns.js +++ b/decode/dns.js @@ -79,14 +79,6 @@ function DNS(emitter) { this._error = undefined; } -function DNSRRSet(count) { - this.rrs = new Array(count); -} - -DNSRRSet.prototype.toString = function () { - return this.rrs.join(", "); -}; - DNS.prototype.decoderName = "dns"; DNS.prototype.eventsOnDecode = true; @@ -94,142 +86,155 @@ DNS.prototype.eventsOnDecode = true; DNS.prototype.decode = function (raw_packet, offset) { //these 2 fields will be deleted soon. this.raw_packet = raw_packet; - this.offset = offset; + var offsetOriginal = offset; this.id = raw_packet.readUInt16BE(offset); // 0, 1 - this.header = new DnsFlags().decode(raw_packet.readUInt16BE(this.offset+2)); - this.qdcount = raw_packet.readUInt16BE(offset + 4); // 4, 5 - this.ancount = raw_packet.readUInt16BE(offset + 6); // 6, 7 - this.nscount = raw_packet.readUInt16BE(offset + 8); // 8, 9 - this.arcount = raw_packet.readUInt16BE(offset + 10); // 10, 11 - this.offset += 12; - - this.question = this.decode_RRs(this.qdcount, true); - this.answer = this.decode_RRs(this.ancount, false); - this.authority = this.decode_RRs(this.nscount, false); - this.additional = this.decode_RRs(this.arcount, false); + this.header = new DnsFlags().decode(raw_packet.readUInt16BE(offset+2)); + + // the number of question asked by this packet + var qcount = raw_packet.readUInt16BE(offset + 4); // 4, 5 + + // the number of answers provided by this packet + var acount = raw_packet.readUInt16BE(offset + 6); // 6, 7 + + // the number of authority records provided by this packet + var ncount = raw_packet.readUInt16BE(offset + 8); // 8, 9 + + // the number of addtional records provided by this packet + var arcount = raw_packet.readUInt16BE(offset + 10); // 10, 11 + offset += 12; + + this.questions = this.decode_RRs(qdcount, true); + + var offsetClosure = { offset: offset }; + this.answers = DecodeResourceRecords(raw_packet, offsetClosure, ancount, false); + this.authorities = DecodeResourceRecords(raw_packet, offsetClosure, nscount, false); + this.additionals = DecodeResourceRecords(raw_packet, offsetClosure, arcount, false); if(this.emitter) { this.emitter.emit("dns", this); } return this; }; -DNS.prototype.decode_RRs = function (count, is_question) { - if (count > 100) { - this._error = "Malformed DNS packet: too many RRs at offset " + this.offset; - return; - } - - var ret = new DNSRRSet(count); - for (var i = 0; i < count; i++) { - ret.rrs[i] = this.decode_RR(is_question); +function DecodeResourceRecords(raw_packet, offsetClosure, count) { + var ret = new Array(count); + for (var i = ret.length - 1; i >= 0; i--) { + ret[i] = new DNSRR().decode(raw_packet, offsetClosure); + offsetClosure.offset = ret[i].length; } return ret; }; -function DNSRR(is_question) { - this.name = ""; - this.type = null; - this.class = null; - this.ttl = null; - this.rdlength = null; - this.rdata = null; - this.is_question = is_question; +function DNSRR() { + this.name = undefined; + +/* + 1 = A, a host address + 2 = NS, an authoritative name server + 3 = MD, a mail destination (Obsolete - use MX) + 4 = MF, a mail forwarder (Obsolete - use MX) + 5 = CNAME, the canonical name for an alias + 6 = SOA, marks the start of a zone of authority + 7 = MB, a mailbox domain name (EXPERIMENTAL) + 8 = MG, a mail group member (EXPERIMENTAL) + 9 = MR, a mail rename domain name (EXPERIMENTAL) + 10 = NULL, a null RR (EXPERIMENTAL) + 11 = WKS, a well known service description + 12 = PTR, a domain name pointer + 13 = HINFO, host information + 14 = MINFO, mailbox or mail list information + 15 = MX, mail exchange + 16 = TXT, text strings +*/ + this.type = undefined; + +/* + 1 = IN, the Internet + 2 = CS, the CSNET class (Obsolete - used only for examples in some obsolete RFCs) + 3 = CH, the CHAOS class + 4 = HS, Hesiod [Dyer 87] +*/ + this.class = undefined; + this.ttl = undefined; + this.rdlength = undefined; + this.rdata = undefined; } -DNSRR.prototype.toString = function () { - var ret = this.name + " "; - if (this.is_question) { - ret += qtype_to_string(this.type) + " " + qclass_to_string(this.class); - } else { - ret += type_to_string(this.type) + " " + class_to_string(this.class) + " " + this.ttl + " " + this.rdata; +DNSRR.prototype.decode = function (raw_packet, offset) { + var initialOffset = offset; + this.name = []; + var currentChar; + while((currentChar = raw_packet[offset++]) != 0) { + this.name.push = currentChar; } - return ret; -}; -DNS.prototype.read_name = function () { - var result = ""; - var len_or_ptr; - var pointer_follows = 0; - var pos = this.offset; - - while ((len_or_ptr = this.raw_packet[pos]) !== 0x00) { - if ((len_or_ptr & 0xC0) === 0xC0) { - // pointer is bottom 6 bits of current byte, plus all 8 bits of next byte - pos = ((len_or_ptr & ~0xC0) << 8) | this.raw_packet[pos + 1]; - pointer_follows++; - if (pointer_follows === 1) { - this.offset += 2; - } - if (pointer_follows > 5) { - throw new Error("invalid DNS RR: too many compression pointers found at offset " + pos); - } - } else { - if (result.length > 0) { - result += "."; - } - if (len_or_ptr > 63) { - throw new Error("invalid DNS RR: length is too large at offset " + pos); - } - pos++; - for (var i = pos; i < (pos + len_or_ptr) && i < this.raw_packet.length; i++) { - if (i > this.raw_packet.length) { - throw new Error("invalid DNS RR: read beyond end of packet at offset " + i); - } - var ch = this.raw_packet[i]; - result += String.fromCharCode(ch); - } - pos += len_or_ptr; - - if (pointer_follows === 0) { - this.offset = pos; - } - } - } + this.type = this.raw_packet.readUInt16BE(offset); + offset += 2; + this.class = this.raw_packet.readUInt16BE(offset); + offset += 2; + this.ttl = this.raw_packet.readUInt32BE(offset); + offset += 4; + this.rdlength = this.raw_packet.readUInt16BE(offset); + offset += 2; - if (pointer_follows === 0) { - this.offset++; - } - return result; + return this; }; -DNS.prototype.decode_RR = function (is_question) { - if (this.offset > this.raw_packet.length) { - throw new Error("Malformed DNS RR. Offset is beyond packet len (decode_RR) :" + this.offset + " packet_len:" + this.raw_packet.length); - } - - var rr = new DNSRR(is_question); - - rr.name = this.read_name(); - - rr.type = this.raw_packet.readUInt16BE(this.offset); - this.offset += 2; - rr.class = this.raw_packet.readUInt16BE(this.offset); - this.offset += 2; - if (is_question) { - return rr; - } +function DnsQuery() { + this.name = undefined; + +/* + 1 = A, a host address + 2 = NS, an authoritative name server + 3 = MD, a mail destination (Obsolete - use MX) + 4 = MF, a mail forwarder (Obsolete - use MX) + 5 = CNAME, the canonical name for an alias + 6 = SOA, marks the start of a zone of authority + 7 = MB, a mailbox domain name (EXPERIMENTAL) + 8 = MG, a mail group member (EXPERIMENTAL) + 9 = MR, a mail rename domain name (EXPERIMENTAL) + 10 = NULL, a null RR (EXPERIMENTAL) + 11 = WKS, a well known service description + 12 = PTR, a domain name pointer + 13 = HINFO, host information + 14 = MINFO, mailbox or mail list information + 15 = MX, mail exchange + 16 = TXT, text strings + + # The following are only used by queries. + 252 = AXFR, a request for a transfer of an entire zone + 253 = MAILB, a request for mailbox-related records (MB, MG or MR) + 254 = MAILA, a request for mail agent RRs (Obsolete - see MX) + 255 = *, a request for all records +*/ + this.type = undefined; + +/* + 1 = IN, the Internet + 2 = CS, the CSNET class (Obsolete - used only for examples in some obsolete RFCs) + 3 = CH, the CHAOS class + 4 = HS, Hesiod [Dyer 87] +*/ + this.class = undefined; +} - rr.ttl = this.raw_packet.readUInt32BE(this.offset); - this.offset += 4; - rr.rdlength = this.raw_packet.readUInt16BE(this.offset); - this.offset += 2; - - if (rr.type === 1 && rr.class === 1 && rr.rdlength) { // A, IN - rr.rdata = new IPv4Addr().decode(this.raw_packet, this.offset); - } else if (rr.type === 2 && rr.class === 1) { // NS, IN - rr.rdata = this.read_name(); - this.offset -= rr.rdlength; // read_name moves offset - } else if (rr.type === 28 && rr.class === 1 && rr.rdlength === 16) { - rr.data = new IPv6Addr(this.raw_packet, this.offset); +DnsQuery.prototype.decode = function (raw_packet, offset) { + var initialOffset = offset; + this.name = []; + var currentChar; + while((currentChar = raw_packet[offset++]) != 0) { + this.name.push = currentChar; } - // TODO - decode other rr types - this.offset += rr.rdlength; + this.type = this.raw_packet.readUInt16BE(offset); + offset += 2; + this.class = this.raw_packet.readUInt16BE(offset); + offset += 2; - return rr; + return this; }; + DNS.prototype.toString = function () { var ret = " DNS "; @@ -250,62 +255,6 @@ DNS.prototype.toString = function () { return ret; }; -function type_to_string(type_num) { - switch (type_num) { - case 1: - return "A"; - case 2: - return "NS"; - case 3: - return "MD"; - case 4: - return "MF"; - case 5: - return "CNAME"; - case 6: - return "SOA"; - case 7: - return "MB"; - case 8: - return "MG"; - case 9: - return "MR"; - case 10: - return "NULL"; - case 11: - return "WKS"; - case 12: - return "PTR"; - case 13: - return "HINFO"; - case 14: - return "MINFO"; - case 15: - return "MX"; - case 16: - return "TXT"; - case 28: - return "AAAA"; - default: - return ("Unknown (" + type_num + ")"); - } -} - -function qtype_to_string(qtype_num) { - switch (qtype_num) { - case 252: - return "AXFR"; - case 253: - return "MAILB"; - case 254: - return "MAILA"; - case 255: - return "*"; - default: - return type_to_string(qtype_num); - } -} - function class_to_string(class_num) { switch (class_num) { case 1: From 50e094d28ae393a9dd02da1886032cf4405e7e00 Mon Sep 17 00:00:00 2001 From: jmaxxz Date: Sun, 12 Apr 2015 23:19:58 -0500 Subject: [PATCH 02/12] Spit dns decoder into multiple files --- decode/dns.js | 281 ---------------------------------- decode/dns/flags.js | 67 ++++++++ decode/dns/index.js | 82 ++++++++++ decode/dns/query.js | 56 +++++++ decode/dns/resource_record.js | 55 +++++++ 5 files changed, 260 insertions(+), 281 deletions(-) delete mode 100644 decode/dns.js create mode 100644 decode/dns/flags.js create mode 100644 decode/dns/index.js create mode 100644 decode/dns/query.js create mode 100644 decode/dns/resource_record.js diff --git a/decode/dns.js b/decode/dns.js deleted file mode 100644 index 0368824..0000000 --- a/decode/dns.js +++ /dev/null @@ -1,281 +0,0 @@ -var IPv4Addr = require("./ipv4_addr"); -var IPv6Addr = require("./ipv6_addr"); - -function DnsFlags() { - // is this a response? - this.isResponse = undefined; - - // 0 == Query - // 1 == Inverse query - // 2 == Status - // 3-15 Reserved for future use - this.opcode = undefined; - - // is the server the authority for the domain? - this.isAuthority = undefined; - - // is this message truncated? - this.isTruncated = undefined; - - // should name server recursively - // resolve domain? - this.isRecursionDesired = undefined; - - // Can the server even do recursion? - this.isRecursionAvailible = undefined; - - // Reserved for future use, unless the present is the future - // then assume the past is the present and the present is the - // past...or just update to support whatever this became. - // - // currently "should" always be zero. - this.z = undefined; - - // 0 == no error - // 1 == format error (query could not be interpeted) - // 2 == server error - // 3 == name error (domain requested by query does not exist) - // 4 == unsupported request - // 5 == refused - // a 4bit reply status code - this.responseCode = undefined; -} - -DnsFlags.prototype.decode = function (raw_packet, offset) { - var byte1 = raw_packet[offset]; - var byte2 = raw_packet[offset + 1]; - - this.isResponse = Boolean(byte1 & 0x80); - this.opcode = (byte1 & 0x78) >> 3; - - this.isAuthority = Boolean(byte1 & 0x04); - this.isTruncated = Boolean(byte1 & 0x02); - this.isRecursionDesired = Boolean(byte1 & 0x01); - this.isRecursionAvailible = Boolean(byte2 & 0x80); - this.z = byte2 & 0x70 >> 4; - this.responseCode = byte2 & 0x0F; - return this; -}; - -DnsFlags.prototype.toString = function () { - return "{ isResponse:" + this.isResponse + - " opcode:" + this.opcode + - " isAuthority:" + this.isAuthority + - " isTruncated:" + this.isTruncated + - " isRecursionDesired:" + this.isRecursionDesired + - " isRecursionAvailible:" + this.isRecursionAvailible + - " z:" + this.z + - " responseCode:" + this.responseCode + - " }"; -}; - -function DNS(emitter) { - this.emitter = emitter; - this.header = undefined; - this.question = undefined; - this.answer = undefined; - this.authority = undefined; - this.additional = undefined; - this._error = undefined; -} - -DNS.prototype.decoderName = "dns"; -DNS.prototype.eventsOnDecode = true; - -// http://tools.ietf.org/html/rfc1035 -DNS.prototype.decode = function (raw_packet, offset) { - //these 2 fields will be deleted soon. - this.raw_packet = raw_packet; - var offsetOriginal = offset; - - this.id = raw_packet.readUInt16BE(offset); // 0, 1 - this.header = new DnsFlags().decode(raw_packet.readUInt16BE(offset+2)); - - // the number of question asked by this packet - var qcount = raw_packet.readUInt16BE(offset + 4); // 4, 5 - - // the number of answers provided by this packet - var acount = raw_packet.readUInt16BE(offset + 6); // 6, 7 - - // the number of authority records provided by this packet - var ncount = raw_packet.readUInt16BE(offset + 8); // 8, 9 - - // the number of addtional records provided by this packet - var arcount = raw_packet.readUInt16BE(offset + 10); // 10, 11 - offset += 12; - - this.questions = this.decode_RRs(qdcount, true); - - var offsetClosure = { offset: offset }; - this.answers = DecodeResourceRecords(raw_packet, offsetClosure, ancount, false); - this.authorities = DecodeResourceRecords(raw_packet, offsetClosure, nscount, false); - this.additionals = DecodeResourceRecords(raw_packet, offsetClosure, arcount, false); - - if(this.emitter) { this.emitter.emit("dns", this); } - return this; -}; - -function DecodeResourceRecords(raw_packet, offsetClosure, count) { - var ret = new Array(count); - for (var i = ret.length - 1; i >= 0; i--) { - ret[i] = new DNSRR().decode(raw_packet, offsetClosure); - offsetClosure.offset = ret[i].length; - } - return ret; -}; - -function DNSRR() { - this.name = undefined; - -/* - 1 = A, a host address - 2 = NS, an authoritative name server - 3 = MD, a mail destination (Obsolete - use MX) - 4 = MF, a mail forwarder (Obsolete - use MX) - 5 = CNAME, the canonical name for an alias - 6 = SOA, marks the start of a zone of authority - 7 = MB, a mailbox domain name (EXPERIMENTAL) - 8 = MG, a mail group member (EXPERIMENTAL) - 9 = MR, a mail rename domain name (EXPERIMENTAL) - 10 = NULL, a null RR (EXPERIMENTAL) - 11 = WKS, a well known service description - 12 = PTR, a domain name pointer - 13 = HINFO, host information - 14 = MINFO, mailbox or mail list information - 15 = MX, mail exchange - 16 = TXT, text strings -*/ - this.type = undefined; - -/* - 1 = IN, the Internet - 2 = CS, the CSNET class (Obsolete - used only for examples in some obsolete RFCs) - 3 = CH, the CHAOS class - 4 = HS, Hesiod [Dyer 87] -*/ - this.class = undefined; - this.ttl = undefined; - this.rdlength = undefined; - this.rdata = undefined; -} - -DNSRR.prototype.decode = function (raw_packet, offset) { - var initialOffset = offset; - this.name = []; - var currentChar; - while((currentChar = raw_packet[offset++]) != 0) { - this.name.push = currentChar; - } - - this.type = this.raw_packet.readUInt16BE(offset); - offset += 2; - this.class = this.raw_packet.readUInt16BE(offset); - offset += 2; - this.ttl = this.raw_packet.readUInt32BE(offset); - offset += 4; - this.rdlength = this.raw_packet.readUInt16BE(offset); - offset += 2; - - - return this; -}; - -function DnsQuery() { - this.name = undefined; - -/* - 1 = A, a host address - 2 = NS, an authoritative name server - 3 = MD, a mail destination (Obsolete - use MX) - 4 = MF, a mail forwarder (Obsolete - use MX) - 5 = CNAME, the canonical name for an alias - 6 = SOA, marks the start of a zone of authority - 7 = MB, a mailbox domain name (EXPERIMENTAL) - 8 = MG, a mail group member (EXPERIMENTAL) - 9 = MR, a mail rename domain name (EXPERIMENTAL) - 10 = NULL, a null RR (EXPERIMENTAL) - 11 = WKS, a well known service description - 12 = PTR, a domain name pointer - 13 = HINFO, host information - 14 = MINFO, mailbox or mail list information - 15 = MX, mail exchange - 16 = TXT, text strings - - # The following are only used by queries. - 252 = AXFR, a request for a transfer of an entire zone - 253 = MAILB, a request for mailbox-related records (MB, MG or MR) - 254 = MAILA, a request for mail agent RRs (Obsolete - see MX) - 255 = *, a request for all records -*/ - this.type = undefined; - -/* - 1 = IN, the Internet - 2 = CS, the CSNET class (Obsolete - used only for examples in some obsolete RFCs) - 3 = CH, the CHAOS class - 4 = HS, Hesiod [Dyer 87] -*/ - this.class = undefined; -} - -DnsQuery.prototype.decode = function (raw_packet, offset) { - var initialOffset = offset; - this.name = []; - var currentChar; - while((currentChar = raw_packet[offset++]) != 0) { - this.name.push = currentChar; - } - - this.type = this.raw_packet.readUInt16BE(offset); - offset += 2; - this.class = this.raw_packet.readUInt16BE(offset); - offset += 2; - - return this; -}; - - -DNS.prototype.toString = function () { - var ret = " DNS "; - - ret += this.header.toString(); - if (this.qdcount > 0) { - ret += "\n question:" + this.question.rrs[0]; - } - if (this.ancount > 0) { - ret += "\n answer:" + this.answer; - } - if (this.nscount > 0) { - ret += "\n authority:" + this.authority; - } - if (this.arcount > 0) { - ret += "\n additional:" + this.additional; - } - - return ret; -}; - -function class_to_string(class_num) { - switch (class_num) { - case 1: - return "IN"; - case 2: - return "CS"; - case 3: - return "CH"; - case 4: - return "HS"; - default: - return "Unknown (" + class_num + ")"; - } -} - -function qclass_to_string(qclass_num) { - if (qclass_num === 255) { - return "*"; - } else { - return class_to_string(qclass_num); - } -} - -module.exports = DNS; diff --git a/decode/dns/flags.js b/decode/dns/flags.js new file mode 100644 index 0000000..5ae5b5e --- /dev/null +++ b/decode/dns/flags.js @@ -0,0 +1,67 @@ +function DnsFlags() { + // is this a response? + this.isResponse = undefined; + + // 0 == Query + // 1 == Inverse query + // 2 == Status + // 3-15 Reserved for future use + this.opcode = undefined; + + // is the server the authority for the domain? + this.isAuthority = undefined; + + // is this message truncated? + this.isTruncated = undefined; + + // should name server recursively + // resolve domain? + this.isRecursionDesired = undefined; + + // Can the server even do recursion? + this.isRecursionAvailible = undefined; + + // Reserved for future use, unless the present is the future + // then assume the past is the present and the present is the + // past...or just update to support whatever this became. + // + // currently "should" always be zero. + this.z = undefined; + + // 0 == no error + // 1 == format error (query could not be interpeted) + // 2 == server error + // 3 == name error (domain requested by query does not exist) + // 4 == unsupported request + // 5 == refused + // a 4bit reply status code + this.responseCode = undefined; +} + +DnsFlags.prototype.decode = function (raw_packet, offset) { + var byte1 = raw_packet[offset]; + var byte2 = raw_packet[offset + 1]; + + this.isResponse = Boolean(byte1 & 0x80); + this.opcode = (byte1 & 0x78) >> 3; + + this.isAuthority = Boolean(byte1 & 0x04); + this.isTruncated = Boolean(byte1 & 0x02); + this.isRecursionDesired = Boolean(byte1 & 0x01); + this.isRecursionAvailible = Boolean(byte2 & 0x80); + this.z = byte2 & 0x70 >> 4; + this.responseCode = byte2 & 0x0F; + return this; +}; + +DnsFlags.prototype.toString = function () { + return "{ isResponse:" + this.isResponse + + " opcode:" + this.opcode + + " isAuthority:" + this.isAuthority + + " isTruncated:" + this.isTruncated + + " isRecursionDesired:" + this.isRecursionDesired + + " isRecursionAvailible:" + this.isRecursionAvailible + + " z:" + this.z + + " responseCode:" + this.responseCode + + " }"; +}; \ No newline at end of file diff --git a/decode/dns/index.js b/decode/dns/index.js new file mode 100644 index 0000000..29f965f --- /dev/null +++ b/decode/dns/index.js @@ -0,0 +1,82 @@ +var IPv4Addr = require("../ipv4_addr"); +var IPv6Addr = require("../ipv6_addr"); +var ResourceRecord = require("../resource_record"); +var QueryRequest = require("../query"); +var Flags = require("../flags"); + +function DNS(emitter) { + this.emitter = emitter; + this.header = undefined; + this.question = undefined; + this.answer = undefined; + this.authority = undefined; + this.additional = undefined; + this._error = undefined; +} + +DNS.prototype.decoderName = "dns"; +DNS.prototype.eventsOnDecode = true; + +// http://tools.ietf.org/html/rfc1035 +DNS.prototype.decode = function (raw_packet, offset) { + //these 2 fields will be deleted soon. + this.raw_packet = raw_packet; + var offsetOriginal = offset; + + this.id = raw_packet.readUInt16BE(offset); // 0, 1 + this.header = new DnsFlags().decode(raw_packet.readUInt16BE(offset+2)); + + // the number of question asked by this packet + var qcount = raw_packet.readUInt16BE(offset + 4); // 4, 5 + + // the number of answers provided by this packet + var acount = raw_packet.readUInt16BE(offset + 6); // 6, 7 + + // the number of authority records provided by this packet + var ncount = raw_packet.readUInt16BE(offset + 8); // 8, 9 + + // the number of addtional records provided by this packet + var arcount = raw_packet.readUInt16BE(offset + 10); // 10, 11 + offset += 12; + + this.questions = this.decode_RRs(qdcount, true); + + var offsetClosure = { offset: offset }; + this.answers = DecodeResourceRecords(raw_packet, offsetClosure, ancount, false); + this.authorities = DecodeResourceRecords(raw_packet, offsetClosure, nscount, false); + this.additionals = DecodeResourceRecords(raw_packet, offsetClosure, arcount, false); + + if(this.emitter) { this.emitter.emit("dns", this); } + return this; +}; + +function DecodeResourceRecords(raw_packet, offsetClosure, count) { + var ret = new Array(count); + for (var i = ret.length - 1; i >= 0; i--) { + ret[i] = new ResourceRecord().decode(raw_packet, offsetClosure); + offsetClosure.offset = ret[i].length; + } + return ret; +}; + +DNS.prototype.toString = function () { + var ret = " DNS "; + + ret += this.header.toString(); + if (this.qdcount > 0) { + ret += "\n question:" + this.question.rrs[0]; + } + if (this.ancount > 0) { + ret += "\n answer:" + this.answer; + } + if (this.nscount > 0) { + ret += "\n authority:" + this.authority; + } + if (this.arcount > 0) { + ret += "\n additional:" + this.additional; + } + + return ret; +}; + +module.exports = DNS; diff --git a/decode/dns/query.js b/decode/dns/query.js new file mode 100644 index 0000000..99005b6 --- /dev/null +++ b/decode/dns/query.js @@ -0,0 +1,56 @@ +function DnsQuery() { + this.name = undefined; + +/* + 1 = A, a host address + 2 = NS, an authoritative name server + 3 = MD, a mail destination (Obsolete - use MX) + 4 = MF, a mail forwarder (Obsolete - use MX) + 5 = CNAME, the canonical name for an alias + 6 = SOA, marks the start of a zone of authority + 7 = MB, a mailbox domain name (EXPERIMENTAL) + 8 = MG, a mail group member (EXPERIMENTAL) + 9 = MR, a mail rename domain name (EXPERIMENTAL) + 10 = NULL, a null RR (EXPERIMENTAL) + 11 = WKS, a well known service description + 12 = PTR, a domain name pointer + 13 = HINFO, host information + 14 = MINFO, mailbox or mail list information + 15 = MX, mail exchange + 16 = TXT, text strings + + # The following are only used by queries. + 252 = AXFR, a request for a transfer of an entire zone + 253 = MAILB, a request for mailbox-related records (MB, MG or MR) + 254 = MAILA, a request for mail agent RRs (Obsolete - see MX) + 255 = *, a request for all records +*/ + this.type = undefined; + +/* + 1 = IN, the Internet + 2 = CS, the CSNET class (Obsolete - used only for examples in some obsolete RFCs) + 3 = CH, the CHAOS class + 4 = HS, Hesiod [Dyer 87] +*/ + this.class = undefined; + + // The total number of bytes read from the packet + this.length = undefined; +} + +DnsQuery.prototype.decode = function (raw_packet, offset) { + var initialOffset = offset; + this.name = []; + var currentChar; + while((currentChar = raw_packet[offset++]) != 0) { + this.name.push = currentChar; + } + + this.type = this.raw_packet.readUInt16BE(offset); + offset += 2; + this.class = this.raw_packet.readUInt16BE(offset); + offset += 2; + + return this; +}; diff --git a/decode/dns/resource_record.js b/decode/dns/resource_record.js new file mode 100644 index 0000000..b5394e9 --- /dev/null +++ b/decode/dns/resource_record.js @@ -0,0 +1,55 @@ +function DnsResourceRecord() { + this.name = undefined; +/* + 1 = A, a host address + 2 = NS, an authoritative name server + 3 = MD, a mail destination (Obsolete - use MX) + 4 = MF, a mail forwarder (Obsolete - use MX) + 5 = CNAME, the canonical name for an alias + 6 = SOA, marks the start of a zone of authority + 7 = MB, a mailbox domain name (EXPERIMENTAL) + 8 = MG, a mail group member (EXPERIMENTAL) + 9 = MR, a mail rename domain name (EXPERIMENTAL) + 10 = NULL, a null RR (EXPERIMENTAL) + 11 = WKS, a well known service description + 12 = PTR, a domain name pointer + 13 = HINFO, host information + 14 = MINFO, mailbox or mail list information + 15 = MX, mail exchange + 16 = TXT, text strings +*/ + this.type = undefined; + +/* + 1 = IN, the Internet + 2 = CS, the CSNET class (Obsolete - used only for examples in some obsolete RFCs) + 3 = CH, the CHAOS class + 4 = HS, Hesiod [Dyer 87] +*/ + this.class = undefined; + this.ttl = undefined; + this.rdlength = undefined; + this.rdata = undefined; + this.length = undefined; +} + +DnsResourceRecord.prototype.decode = function (raw_packet, offset) { + var initialOffset = offset; + this.name = []; + var currentChar; + while((currentChar = raw_packet[offset++]) != 0) { + this.name.push = currentChar; + } + + this.type = this.raw_packet.readUInt16BE(offset); + offset += 2; + this.class = this.raw_packet.readUInt16BE(offset); + offset += 2; + this.ttl = this.raw_packet.readUInt32BE(offset); + offset += 4; + this.rdlength = this.raw_packet.readUInt16BE(offset); + offset += 2; + + + return this; +}; \ No newline at end of file From a6804f1016dae014ceb8fd6afa8e0f6e8809756e Mon Sep 17 00:00:00 2001 From: jmaxxz Date: Mon, 13 Apr 2015 22:08:59 -0500 Subject: [PATCH 03/12] Fix code changed in refactoring to actually work --- decode/dns/flags.js | 4 ++- decode/dns/index.js | 39 +++++++++++++++------------ decode/dns/query.js | 12 +++++---- decode/dns/resource_record.js | 50 +++++++++++++++++++---------------- 4 files changed, 59 insertions(+), 46 deletions(-) diff --git a/decode/dns/flags.js b/decode/dns/flags.js index 5ae5b5e..ecc71c3 100644 --- a/decode/dns/flags.js +++ b/decode/dns/flags.js @@ -64,4 +64,6 @@ DnsFlags.prototype.toString = function () { " z:" + this.z + " responseCode:" + this.responseCode + " }"; -}; \ No newline at end of file +}; + +module.exports = DnsFlags; \ No newline at end of file diff --git a/decode/dns/index.js b/decode/dns/index.js index 29f965f..48c24a0 100644 --- a/decode/dns/index.js +++ b/decode/dns/index.js @@ -1,8 +1,6 @@ -var IPv4Addr = require("../ipv4_addr"); -var IPv6Addr = require("../ipv6_addr"); -var ResourceRecord = require("../resource_record"); -var QueryRequest = require("../query"); -var Flags = require("../flags"); +var ResourceRecord = require("./resource_record"); +var QueryRequest = require("./query"); +var Flags = require("./flags"); function DNS(emitter) { this.emitter = emitter; @@ -21,10 +19,9 @@ DNS.prototype.eventsOnDecode = true; DNS.prototype.decode = function (raw_packet, offset) { //these 2 fields will be deleted soon. this.raw_packet = raw_packet; - var offsetOriginal = offset; this.id = raw_packet.readUInt16BE(offset); // 0, 1 - this.header = new DnsFlags().decode(raw_packet.readUInt16BE(offset+2)); + this.header = new Flags().decode(raw_packet.readUInt16BE(offset+2)); // the number of question asked by this packet var qcount = raw_packet.readUInt16BE(offset + 4); // 4, 5 @@ -33,31 +30,39 @@ DNS.prototype.decode = function (raw_packet, offset) { var acount = raw_packet.readUInt16BE(offset + 6); // 6, 7 // the number of authority records provided by this packet - var ncount = raw_packet.readUInt16BE(offset + 8); // 8, 9 + var nscount = raw_packet.readUInt16BE(offset + 8); // 8, 9 // the number of addtional records provided by this packet var arcount = raw_packet.readUInt16BE(offset + 10); // 10, 11 offset += 12; - this.questions = this.decode_RRs(qdcount, true); + this.questions = decodeQueries(raw_packet, offset, qcount); - var offsetClosure = { offset: offset }; - this.answers = DecodeResourceRecords(raw_packet, offsetClosure, ancount, false); - this.authorities = DecodeResourceRecords(raw_packet, offsetClosure, nscount, false); - this.additionals = DecodeResourceRecords(raw_packet, offsetClosure, arcount, false); + this.answers = decodeResourceRecords(raw_packet, offset, acount); + this.authorities = decodeResourceRecords(raw_packet, offset, nscount); + this.additionals = decodeResourceRecords(raw_packet, offset, arcount); if(this.emitter) { this.emitter.emit("dns", this); } return this; }; -function DecodeResourceRecords(raw_packet, offsetClosure, count) { +function decodeResourceRecords(raw_packet, offset, count) { var ret = new Array(count); for (var i = ret.length - 1; i >= 0; i--) { - ret[i] = new ResourceRecord().decode(raw_packet, offsetClosure); - offsetClosure.offset = ret[i].length; + ret[i] = new ResourceRecord().decode(raw_packet, offset); + offset += ret[i].bytesDecoded; } return ret; -}; +} + +function decodeQueries(raw_packet, offset, count) { + var ret = new Array(count); + for (var i = ret.length - 1; i >= 0; i--) { + ret[i] = new QueryRequest().decode(raw_packet, offset); + offset += ret[i].bytesDecoded; + } + return ret; +} DNS.prototype.toString = function () { var ret = " DNS "; diff --git a/decode/dns/query.js b/decode/dns/query.js index 99005b6..13e880f 100644 --- a/decode/dns/query.js +++ b/decode/dns/query.js @@ -36,21 +36,23 @@ function DnsQuery() { this.class = undefined; // The total number of bytes read from the packet - this.length = undefined; + this.bytesDecoded = undefined; } DnsQuery.prototype.decode = function (raw_packet, offset) { var initialOffset = offset; this.name = []; var currentChar; - while((currentChar = raw_packet[offset++]) != 0) { - this.name.push = currentChar; + while((currentChar = raw_packet[offset++]) !== 0) { + this.name.push = currentChar; } - this.type = this.raw_packet.readUInt16BE(offset); + this.type = raw_packet.readUInt16BE(offset); offset += 2; - this.class = this.raw_packet.readUInt16BE(offset); + this.class = raw_packet.readUInt16BE(offset); offset += 2; + this.bytesDecoded = offset - initialOffset; return this; }; +module.exports = DnsQuery; diff --git a/decode/dns/resource_record.js b/decode/dns/resource_record.js index b5394e9..09dc98e 100644 --- a/decode/dns/resource_record.js +++ b/decode/dns/resource_record.js @@ -1,5 +1,5 @@ function DnsResourceRecord() { - this.name = undefined; + this.name = undefined; /* 1 = A, a host address 2 = NS, an authoritative name server @@ -18,7 +18,7 @@ function DnsResourceRecord() { 15 = MX, mail exchange 16 = TXT, text strings */ - this.type = undefined; + this.type = undefined; /* 1 = IN, the Internet @@ -26,30 +26,34 @@ function DnsResourceRecord() { 3 = CH, the CHAOS class 4 = HS, Hesiod [Dyer 87] */ - this.class = undefined; - this.ttl = undefined; - this.rdlength = undefined; - this.rdata = undefined; - this.length = undefined; + this.class = undefined; + this.ttl = undefined; + this.rdlength = undefined; + this.rdata = undefined; + + // the number of bytes decoded by this instance. + this.bytesDecoded = undefined; } DnsResourceRecord.prototype.decode = function (raw_packet, offset) { - var initialOffset = offset; - this.name = []; - var currentChar; - while((currentChar = raw_packet[offset++]) != 0) { - this.name.push = currentChar; - } + var initialOffset = offset; + this.name = []; + var currentChar; + while((currentChar = raw_packet[offset++]) !== 0) { + this.name.push = currentChar; + } - this.type = this.raw_packet.readUInt16BE(offset); - offset += 2; - this.class = this.raw_packet.readUInt16BE(offset); - offset += 2; - this.ttl = this.raw_packet.readUInt32BE(offset); - offset += 4; - this.rdlength = this.raw_packet.readUInt16BE(offset); - offset += 2; + this.type = raw_packet.readUInt16BE(offset); + offset += 2; + this.class = raw_packet.readUInt16BE(offset); + offset += 2; + this.ttl = raw_packet.readUInt32BE(offset); + offset += 4; + this.rdlength = raw_packet.readUInt16BE(offset); + offset += 2; + this.bytesDecoded = offset - initialOffset; + return this; +}; - return this; -}; \ No newline at end of file +module.exports = DnsResourceRecord; From 8425bae02326911aa063a9f70ee5877921c1baf4 Mon Sep 17 00:00:00 2001 From: jmaxxz Date: Tue, 14 Apr 2015 21:47:05 -0500 Subject: [PATCH 04/12] Refactor toString function for dns queries --- decode/dns/index.js | 16 ++++++++-------- decode/dns/query.js | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/decode/dns/index.js b/decode/dns/index.js index 48c24a0..97228b8 100644 --- a/decode/dns/index.js +++ b/decode/dns/index.js @@ -68,17 +68,17 @@ DNS.prototype.toString = function () { var ret = " DNS "; ret += this.header.toString(); - if (this.qdcount > 0) { - ret += "\n question:" + this.question.rrs[0]; + if (this.questions.length > 0) { + ret += "\n question:" + this.questions.join("\n\t"); } - if (this.ancount > 0) { - ret += "\n answer:" + this.answer; + if (this.answers.length > 0) { + ret += "\n answer:" + this.answers.join("\n\t"); } - if (this.nscount > 0) { - ret += "\n authority:" + this.authority; + if (this.authorities.length > 0) { + ret += "\n authority:" + this.authorities.join("\n\t"); } - if (this.arcount > 0) { - ret += "\n additional:" + this.additional; + if (this.additionals.length > 0) { + ret += "\n additional:" + this.additionals.join("\n\t"); } return ret; diff --git a/decode/dns/query.js b/decode/dns/query.js index 13e880f..e608440 100644 --- a/decode/dns/query.js +++ b/decode/dns/query.js @@ -55,4 +55,38 @@ DnsQuery.prototype.decode = function (raw_packet, offset) { return this; }; + +var questionTypes = []; +questionTypes[1] = "A"; +questionTypes[2] = "NS"; +questionTypes[3] = "MD"; +questionTypes[4] = "MF"; +questionTypes[5] = "CNAME"; +questionTypes[6] = "SOA"; +questionTypes[7] = "MB"; +questionTypes[8] = "MG"; +questionTypes[9] = "MR"; +questionTypes[10] = "NULL"; +questionTypes[11] = "WKS"; +questionTypes[12] = "PTR"; +questionTypes[13] = "MINFO"; +questionTypes[14] = "MX"; +questionTypes[15] = "TXT"; +questionTypes[252] = "AXFR"; +questionTypes[253] = "MAILB"; +questionTypes[254] = "MAILA"; +questionTypes[255] = "*"; + +var questionClasses = []; +questionClasses[1] = "IN"; +questionClasses[2] = "CS"; +questionClasses[3] = "CH"; +questionClasses[4] = "HS"; + +DnsQuery.prototype.toString = function () { + return "name: " + this.name + + " type: " + questionTypes[this.type] + + " class: " + questionClasses[this.class]; +}; + module.exports = DnsQuery; From 984cd832dffbd3b0abdd4f21e9bfdf5761acfe96 Mon Sep 17 00:00:00 2001 From: jmaxxz Date: Wed, 15 Apr 2015 23:39:24 -0500 Subject: [PATCH 05/12] Fix bug in query name decoding --- decode/dns/query.js | 17 +++++++++++++---- decode/dns/resource_record.js | 4 ++-- spec/decode/dns.spec.js | 17 +++++++++++++++++ 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/decode/dns/query.js b/decode/dns/query.js index e608440..ee131d5 100644 --- a/decode/dns/query.js +++ b/decode/dns/query.js @@ -41,10 +41,19 @@ function DnsQuery() { DnsQuery.prototype.decode = function (raw_packet, offset) { var initialOffset = offset; - this.name = []; - var currentChar; - while((currentChar = raw_packet[offset++]) !== 0) { - this.name.push = currentChar; + this.name = ""; + var segLength; + var firstSegment = true; + while((segLength = raw_packet[offset++]) !== 0) { + if(firstSegment) { + firstSegment = false; + } else { + this.name += "."; + } + + for (var i = 0; i < segLength; i++) { + this.name += String.fromCharCode(raw_packet[offset++]); + } } this.type = raw_packet.readUInt16BE(offset); diff --git a/decode/dns/resource_record.js b/decode/dns/resource_record.js index 09dc98e..b158108 100644 --- a/decode/dns/resource_record.js +++ b/decode/dns/resource_record.js @@ -37,10 +37,10 @@ function DnsResourceRecord() { DnsResourceRecord.prototype.decode = function (raw_packet, offset) { var initialOffset = offset; - this.name = []; + this.name = ""; var currentChar; while((currentChar = raw_packet[offset++]) !== 0) { - this.name.push = currentChar; + this.name += String.fromCharCode(currentChar); } this.type = raw_packet.readUInt16BE(offset); diff --git a/spec/decode/dns.spec.js b/spec/decode/dns.spec.js index 379e438..478292a 100644 --- a/spec/decode/dns.spec.js +++ b/spec/decode/dns.spec.js @@ -1,4 +1,5 @@ var Dns = require("../../decode/dns"); +var DnsQuery = require("../../decode/dns/query"); var events = require("events"); var shouldBehaveLikeADecoder = require("./decode").shouldBehaveLikeADecoder; require("should"); @@ -27,6 +28,14 @@ describe("Dns", function(){ this.instance.should.have.property("id", 0x311f); }); + it("sets #questions to the list of queries in the packet", function() { + var exampleQuery = new DnsQuery(); + exampleQuery.decode(new Buffer("01320131033136380331393207696e2d61646472046172706100000c0001", "hex"), 0); + + this.instance.decode(this.example, 0); + this.instance.should.have.property("questions", [exampleQuery]); + }); + it("sets #header.isResponse to true if the packet was a response", function() { this.instance.decode(this.example, 0); this.instance.header.should.have.property("isResponse", false); @@ -64,8 +73,16 @@ describe("Dns", function(){ }); describe("#toString()", function(){ + var exampleToString = " DNS { isResponse:false opcode:0 isAuthority:false isTruncated:false isRecursionDesired:false isRecursionAvailible:false z:0 responseCode:0 }" + + "\n question:name: 2.1.168.192.in-addr.arpa type: PTR class: IN"; it("is a function", function(){ this.instance.toString.should.be.type("function"); }); + + it("returns a value like\"" + exampleToString + "\"", function() { + this.instance.decode(this.example, 0); + var result = this.instance.toString(); + result.should.be.exactly(exampleToString); + }); }); }); \ No newline at end of file From d58eb51acfab8697814a4cfe707d82f7a2247663 Mon Sep 17 00:00:00 2001 From: jmaxxz Date: Thu, 16 Apr 2015 22:33:32 -0500 Subject: [PATCH 06/12] Create scaffold for spec of dns flags --- spec/decode/dns/flags.spec.js | 14 ++++++++++++++ spec/decode/{dns.spec.js => dns/index.spec.js} | 6 +++--- 2 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 spec/decode/dns/flags.spec.js rename spec/decode/{dns.spec.js => dns/index.spec.js} (95%) diff --git a/spec/decode/dns/flags.spec.js b/spec/decode/dns/flags.spec.js new file mode 100644 index 0000000..f0ab8e4 --- /dev/null +++ b/spec/decode/dns/flags.spec.js @@ -0,0 +1,14 @@ +var DnsFlags = require("../../../decode/dns/flags"); +var shouldBehaveLikeADecoder = require("../decode").shouldBehaveLikeADecoder; +require("should"); + +describe("DnsFlags", function(){ + beforeEach(function () { + this.instance = new DnsFlags(); + this.example = new Buffer("0100", "hex"); + }); + + describe("#decode()", function(){ + shouldBehaveLikeADecoder(); + }); +}); diff --git a/spec/decode/dns.spec.js b/spec/decode/dns/index.spec.js similarity index 95% rename from spec/decode/dns.spec.js rename to spec/decode/dns/index.spec.js index 478292a..17be48d 100644 --- a/spec/decode/dns.spec.js +++ b/spec/decode/dns/index.spec.js @@ -1,7 +1,7 @@ -var Dns = require("../../decode/dns"); -var DnsQuery = require("../../decode/dns/query"); +var Dns = require("../../../decode/dns"); +var DnsQuery = require("../../../decode/dns/query"); var events = require("events"); -var shouldBehaveLikeADecoder = require("./decode").shouldBehaveLikeADecoder; +var shouldBehaveLikeADecoder = require("../decode").shouldBehaveLikeADecoder; require("should"); describe("Dns", function(){ From 0d084fbd9d39d7b780db95dcf25d2ae54a0e6e9d Mon Sep 17 00:00:00 2001 From: jmaxxz Date: Fri, 17 Apr 2015 23:30:28 -0500 Subject: [PATCH 07/12] Add test for dns flags --- decode/dns/flags.js | 2 +- spec/decode/dns/flags.spec.js | 64 +++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/decode/dns/flags.js b/decode/dns/flags.js index ecc71c3..2599cd3 100644 --- a/decode/dns/flags.js +++ b/decode/dns/flags.js @@ -49,7 +49,7 @@ DnsFlags.prototype.decode = function (raw_packet, offset) { this.isTruncated = Boolean(byte1 & 0x02); this.isRecursionDesired = Boolean(byte1 & 0x01); this.isRecursionAvailible = Boolean(byte2 & 0x80); - this.z = byte2 & 0x70 >> 4; + this.z = (byte2 & 0x70) >> 4; this.responseCode = byte2 & 0x0F; return this; }; diff --git a/spec/decode/dns/flags.spec.js b/spec/decode/dns/flags.spec.js index f0ab8e4..651a1c6 100644 --- a/spec/decode/dns/flags.spec.js +++ b/spec/decode/dns/flags.spec.js @@ -10,5 +10,69 @@ describe("DnsFlags", function(){ describe("#decode()", function(){ shouldBehaveLikeADecoder(); + + it("sets #isResponse", function(){ + this.instance.decode(new Buffer([0x80, 0x00], "hex"), 0); + this.instance.should.have.property("isResponse", true); + + this.instance.decode(new Buffer([0x00, 0x00], "hex"), 0); + this.instance.should.have.property("isResponse", false); + }); + + it("sets #opcode", function(){ + this.instance.decode(new Buffer([0x78, 0x00], "hex"), 0); + this.instance.should.have.property("opcode", 15); + + this.instance.decode(new Buffer([0x00, 0xff], "hex"), 0); + this.instance.should.have.property("opcode", 0); + }); + + it("sets #isAuthority", function(){ + this.instance.decode(new Buffer([0x04, 0x00], "hex"), 0); + this.instance.should.have.property("isAuthority", true); + + this.instance.decode(new Buffer([0x00, 0xff], "hex"), 0); + this.instance.should.have.property("isAuthority", false); + }); + + it("sets #isTruncated", function(){ + this.instance.decode(new Buffer([0x02, 0x00], "hex"), 0); + this.instance.should.have.property("isTruncated", true); + + this.instance.decode(new Buffer([0x00, 0xff], "hex"), 0); + this.instance.should.have.property("isTruncated", false); + }); + + it("sets #isRecursionDesired", function(){ + this.instance.decode(new Buffer([0x01, 0x00], "hex"), 0); + this.instance.should.have.property("isRecursionDesired", true); + + this.instance.decode(new Buffer([0x00, 0xff], "hex"), 0); + this.instance.should.have.property("isRecursionDesired", false); + }); + + it("sets #isRecursionAvailible", function(){ + this.instance.decode(new Buffer([0x00, 0x80], "hex"), 0); + this.instance.should.have.property("isRecursionAvailible", true); + + this.instance.decode(new Buffer([0xff, 0x0f], "hex"), 0); + this.instance.should.have.property("isRecursionAvailible", false); + }); + + it("sets #z", function(){ + this.instance.decode(new Buffer([0x00, 0x70], "hex"), 0); + this.instance.should.have.property("z", 7); + + this.instance.decode(new Buffer([0xff, 0x0f], "hex"), 0); + this.instance.should.have.property("z", 0); + }); + + it("sets #responseCode", function(){ + this.instance.decode(new Buffer([0x00, 0x0f], "hex"), 0); + this.instance.should.have.property("responseCode", 15); + + this.instance.decode(new Buffer([0xff, 0x00], "hex"), 0); + this.instance.should.have.property("responseCode", 0); + }); }); }); From 1f3360e7d945f948bb9ff43ebe2e3d444d59d244 Mon Sep 17 00:00:00 2001 From: jmaxxz Date: Sat, 25 Apr 2015 21:19:40 -0500 Subject: [PATCH 08/12] Add test for DnsQuery --- spec/decode/dns/query.spec.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 spec/decode/dns/query.spec.js diff --git a/spec/decode/dns/query.spec.js b/spec/decode/dns/query.spec.js new file mode 100644 index 0000000..78af456 --- /dev/null +++ b/spec/decode/dns/query.spec.js @@ -0,0 +1,32 @@ +var DnsQuery = require("../../../decode/dns/query"); +var shouldBehaveLikeADecoder = require("../decode").shouldBehaveLikeADecoder; +require("should"); + +describe("DnsQuery", function() { + beforeEach(function () { + this.instance = new DnsQuery(); + this.example = new Buffer("01320131033136380331393207696e2d61646472046172706100" + //name:2.1.168.192.in-addr.arpa + "000c" + + "0001", + "hex"); + }); + + describe("#decode", function(){ + shouldBehaveLikeADecoder(); + + it("sets #name to the domain the client wants the IP of", function() { + this.instance.decode(this.example, 0); + this.instance.should.have.property("name", "2.1.168.192.in-addr.arpa"); + }); + + it("sets #type to the type of record being requested", function() { + this.instance.decode(this.example, 0); + this.instance.should.have.property("type", 0x000c); + }); + + it("sets #class to the class of record being requested", function() { + this.instance.decode(this.example, 0); + this.instance.should.have.property("class", 0x0001); + }); + }); +}); From dc1a3b9a4908515796dcf3cce94bf9016534395c Mon Sep 17 00:00:00 2001 From: jmaxxz Date: Sat, 25 Apr 2015 22:19:11 -0500 Subject: [PATCH 09/12] Add tests for DnsResourceRecord --- decode/dns/resource_record.js | 16 ++++++++-- spec/decode/dns/resource_record.spec.js | 40 +++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 spec/decode/dns/resource_record.spec.js diff --git a/decode/dns/resource_record.js b/decode/dns/resource_record.js index b158108..a670b75 100644 --- a/decode/dns/resource_record.js +++ b/decode/dns/resource_record.js @@ -38,9 +38,18 @@ function DnsResourceRecord() { DnsResourceRecord.prototype.decode = function (raw_packet, offset) { var initialOffset = offset; this.name = ""; - var currentChar; - while((currentChar = raw_packet[offset++]) !== 0) { - this.name += String.fromCharCode(currentChar); + var segLength; + var firstSegment = true; + while((segLength = raw_packet[offset++]) !== 0) { + if(firstSegment) { + firstSegment = false; + } else { + this.name += "."; + } + + for (var i = 0; i < segLength; i++) { + this.name += String.fromCharCode(raw_packet[offset++]); + } } this.type = raw_packet.readUInt16BE(offset); @@ -51,6 +60,7 @@ DnsResourceRecord.prototype.decode = function (raw_packet, offset) { offset += 4; this.rdlength = raw_packet.readUInt16BE(offset); offset += 2; + offset += this.rdlength; this.bytesDecoded = offset - initialOffset; return this; diff --git a/spec/decode/dns/resource_record.spec.js b/spec/decode/dns/resource_record.spec.js new file mode 100644 index 0000000..9999a61 --- /dev/null +++ b/spec/decode/dns/resource_record.spec.js @@ -0,0 +1,40 @@ +var DnsResourceRecord = require("../../../decode/dns/resource_record"); +var shouldBehaveLikeADecoder = require("../decode").shouldBehaveLikeADecoder; +require("should"); + +describe("DnsResourceRecord", function() { + beforeEach(function () { + this.instance = new DnsResourceRecord(); + this.example = new Buffer("01320131033136380331393207696e2d61646472046172706100" + //name:2.1.168.192.in-addr.arpa + "000c" + // type + "0001" + // class + "00000009" + //ttl + "0003" + // resource length + "010203", // resource + "hex"); + }); + + describe("#decode", function(){ + shouldBehaveLikeADecoder(); + + it("sets #name to the domain the client wants the IP of", function() { + this.instance.decode(this.example, 0); + this.instance.should.have.property("name", "2.1.168.192.in-addr.arpa"); + }); + + it("sets #type to the type of record being requested", function() { + this.instance.decode(this.example, 0); + this.instance.should.have.property("type", 0x000c); + }); + + it("sets #ttl to the time to live", function() { + this.instance.decode(this.example, 0); + this.instance.should.have.property("ttl", 9); + }); + + it("sets #rdlength to be the length of the resource", function() { + this.instance.decode(this.example, 0); + this.instance.should.have.property("rdlength", 3); + }); + }); +}); From 89335e95dfc986111462aae9df48a8aa46996bae Mon Sep 17 00:00:00 2001 From: jmaxxz Date: Wed, 29 Apr 2015 23:03:43 -0500 Subject: [PATCH 10/12] Add decoding of ipv4 dns resource responses --- decode/dns/resource_record.js | 30 +++++++++++++------------ decode/dns/util.js | 20 +++++++++++++++++ spec/decode/dns/resource_record.spec.js | 22 +++++++++++++----- 3 files changed, 53 insertions(+), 19 deletions(-) create mode 100644 decode/dns/util.js diff --git a/decode/dns/resource_record.js b/decode/dns/resource_record.js index a670b75..080409c 100644 --- a/decode/dns/resource_record.js +++ b/decode/dns/resource_record.js @@ -1,3 +1,7 @@ +var decodeName = require("./util").decodeName; +var IPv4Addr = require("../ipv4_addr"); +var IPv6Addr = require("../ipv6_addr"); + function DnsResourceRecord() { this.name = undefined; /* @@ -37,20 +41,9 @@ function DnsResourceRecord() { DnsResourceRecord.prototype.decode = function (raw_packet, offset) { var initialOffset = offset; - this.name = ""; - var segLength; - var firstSegment = true; - while((segLength = raw_packet[offset++]) !== 0) { - if(firstSegment) { - firstSegment = false; - } else { - this.name += "."; - } - - for (var i = 0; i < segLength; i++) { - this.name += String.fromCharCode(raw_packet[offset++]); - } - } + var decodedName = decodeName(raw_packet, offset); + this.name = decodedName.name; + offset += decodedName.bytesDecoded; this.type = raw_packet.readUInt16BE(offset); offset += 2; @@ -60,6 +53,15 @@ DnsResourceRecord.prototype.decode = function (raw_packet, offset) { offset += 4; this.rdlength = raw_packet.readUInt16BE(offset); offset += 2; + + if (this.type === 1 && this.class === 1 && this.rdlength === 4) { // A, IN + this.rdata = new IPv4Addr().decode(raw_packet, offset); + } else if (this.type === 2 && this.class === 1) { // NS, IN + this.rdata = decodeName(raw_packet, offset).name; + } else if (this.type === 28 && this.class === 1 && this.rdlength === 16) { + this.data = new IPv6Addr(raw_packet, offset); + } + offset += this.rdlength; this.bytesDecoded = offset - initialOffset; diff --git a/decode/dns/util.js b/decode/dns/util.js new file mode 100644 index 0000000..2c90f88 --- /dev/null +++ b/decode/dns/util.js @@ -0,0 +1,20 @@ +exports.decodeName = function(raw_packet, offset) { + var segLength; + var firstSegment = true; + var initialOffset = offset; + var result = { bytesDecoded:undefined, name:"" }; + + while((segLength = raw_packet[offset++]) !== 0) { + if(firstSegment) { + firstSegment = false; + } else { + result.name += "."; + } + + for (var i = 0; i < segLength; i++) { + result.name += String.fromCharCode(raw_packet[offset++]); + } + } + result.bytesDecoded = offset - initialOffset; + return result; +}; diff --git a/spec/decode/dns/resource_record.spec.js b/spec/decode/dns/resource_record.spec.js index 9999a61..bc79473 100644 --- a/spec/decode/dns/resource_record.spec.js +++ b/spec/decode/dns/resource_record.spec.js @@ -1,4 +1,5 @@ var DnsResourceRecord = require("../../../decode/dns/resource_record"); +var IPv4Addr = require("../../../decode/ipv4_addr"); var shouldBehaveLikeADecoder = require("../decode").shouldBehaveLikeADecoder; require("should"); @@ -6,11 +7,11 @@ describe("DnsResourceRecord", function() { beforeEach(function () { this.instance = new DnsResourceRecord(); this.example = new Buffer("01320131033136380331393207696e2d61646472046172706100" + //name:2.1.168.192.in-addr.arpa - "000c" + // type + "0001" + // type "0001" + // class "00000009" + //ttl - "0003" + // resource length - "010203", // resource + "0004" + // resource length + "01020304", // resource 1.2.3.4 (ipv4) "hex"); }); @@ -24,7 +25,12 @@ describe("DnsResourceRecord", function() { it("sets #type to the type of record being requested", function() { this.instance.decode(this.example, 0); - this.instance.should.have.property("type", 0x000c); + this.instance.should.have.property("type", 1); + }); + + it("sets #class to the class of record being requested", function() { + this.instance.decode(this.example, 0); + this.instance.should.have.property("class", 1); }); it("sets #ttl to the time to live", function() { @@ -34,7 +40,13 @@ describe("DnsResourceRecord", function() { it("sets #rdlength to be the length of the resource", function() { this.instance.decode(this.example, 0); - this.instance.should.have.property("rdlength", 3); + this.instance.should.have.property("rdlength", 4); + }); + + it("sets #rdata to be the record in the resource", function() { + this.instance.decode(this.example, 0); + var ipAddr = new IPv4Addr().decode(new Buffer("01020304", "hex"), 0); + this.instance.should.have.property("rdata", ipAddr); }); }); }); From f1796ea6f5a30cdca94d0a829c31800d50d7b82f Mon Sep 17 00:00:00 2001 From: jmaxxz Date: Fri, 1 May 2015 23:56:13 -0500 Subject: [PATCH 11/12] Add support basic support for PTR IN resource records --- decode/dns/resource_record.js | 2 +- decode/dns/util.js | 9 ++++++++- spec/decode/dns/resource_record.spec.js | 11 +++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/decode/dns/resource_record.js b/decode/dns/resource_record.js index 080409c..38c5f9b 100644 --- a/decode/dns/resource_record.js +++ b/decode/dns/resource_record.js @@ -56,7 +56,7 @@ DnsResourceRecord.prototype.decode = function (raw_packet, offset) { if (this.type === 1 && this.class === 1 && this.rdlength === 4) { // A, IN this.rdata = new IPv4Addr().decode(raw_packet, offset); - } else if (this.type === 2 && this.class === 1) { // NS, IN + } else if ((this.type === 2 || this.type === 12) && this.class === 1) { // (PTR | NS) && IN this.rdata = decodeName(raw_packet, offset).name; } else if (this.type === 28 && this.class === 1 && this.rdlength === 16) { this.data = new IPv6Addr(raw_packet, offset); diff --git a/decode/dns/util.js b/decode/dns/util.js index 2c90f88..20fe0ac 100644 --- a/decode/dns/util.js +++ b/decode/dns/util.js @@ -4,7 +4,14 @@ exports.decodeName = function(raw_packet, offset) { var initialOffset = offset; var result = { bytesDecoded:undefined, name:"" }; - while((segLength = raw_packet[offset++]) !== 0) { + if(raw_packet[offset] > 63) { + //Name is in pointer format which is currently not supported + result.bytesDecoded = 2; + result.name = ""; + return result; + } + + while((segLength = raw_packet[offset++]) !== 0 && segLength < 63) { if(firstSegment) { firstSegment = false; } else { diff --git a/spec/decode/dns/resource_record.spec.js b/spec/decode/dns/resource_record.spec.js index bc79473..d02e68c 100644 --- a/spec/decode/dns/resource_record.spec.js +++ b/spec/decode/dns/resource_record.spec.js @@ -48,5 +48,16 @@ describe("DnsResourceRecord", function() { var ipAddr = new IPv4Addr().decode(new Buffer("01020304", "hex"), 0); this.instance.should.have.property("rdata", ipAddr); }); + + it("sets #rdata to be the record in the resource for PTR IN", function() { + this.instance.decode(new Buffer("c00c" + + "000c" + //type PTR + "0001" + //class IN + "00000e10" + //ttl + "0017" + //data length + "08737465726c696e6708667265656e6f6465036e657400", //sterling.freenode.net + "hex"), 0); + this.instance.should.have.property("rdata", "sterling.freenode.net"); + }); }); }); From 802e02d79677a11541dbbb5cc647073fab5b0988 Mon Sep 17 00:00:00 2001 From: jmaxxz Date: Sat, 2 May 2015 10:01:01 -0500 Subject: [PATCH 12/12] Don't crash on domain names ending in a point --- decode/dns/util.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/decode/dns/util.js b/decode/dns/util.js index 20fe0ac..3547725 100644 --- a/decode/dns/util.js +++ b/decode/dns/util.js @@ -4,14 +4,7 @@ exports.decodeName = function(raw_packet, offset) { var initialOffset = offset; var result = { bytesDecoded:undefined, name:"" }; - if(raw_packet[offset] > 63) { - //Name is in pointer format which is currently not supported - result.bytesDecoded = 2; - result.name = ""; - return result; - } - - while((segLength = raw_packet[offset++]) !== 0 && segLength < 63) { + while((segLength = raw_packet[offset++]) !== 0 && segLength <= 63) { if(firstSegment) { firstSegment = false; } else { @@ -22,6 +15,15 @@ exports.decodeName = function(raw_packet, offset) { result.name += String.fromCharCode(raw_packet[offset++]); } } + + if(raw_packet[offset-1] > 63) { + // Detected a pointer, pointers + // are 2 bytes long so inc the offset + // At this time we have very poor support + // for pointers + offset++; + } + result.bytesDecoded = offset - initialOffset; return result; };