diff --git a/decode/dns.js b/decode/dns.js deleted file mode 100644 index 6267da0..0000000 --- a/decode/dns.js +++ /dev/null @@ -1,332 +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; -} - -function DNSRRSet(count) { - this.rrs = new Array(count); -} - -DNSRRSet.prototype.toString = function () { - return this.rrs.join(", "); -}; - -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; - this.offset = 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); - - 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); - } - 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; -} - -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; - } - 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; - } - } - } - - if (pointer_follows === 0) { - this.offset++; - } - - return result; -}; - -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; - } - - 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); - } - // TODO - decode other rr types - - this.offset += rr.rdlength; - - return rr; -}; - -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 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: - 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..2599cd3 --- /dev/null +++ b/decode/dns/flags.js @@ -0,0 +1,69 @@ +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 + + " }"; +}; + +module.exports = DnsFlags; \ No newline at end of file diff --git a/decode/dns/index.js b/decode/dns/index.js new file mode 100644 index 0000000..97228b8 --- /dev/null +++ b/decode/dns/index.js @@ -0,0 +1,87 @@ +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; + + this.id = raw_packet.readUInt16BE(offset); // 0, 1 + 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 + + // 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 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 = decodeQueries(raw_packet, offset, qcount); + + 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, offset, count) { + var ret = new Array(count); + for (var i = ret.length - 1; i >= 0; i--) { + 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 "; + + ret += this.header.toString(); + if (this.questions.length > 0) { + ret += "\n question:" + this.questions.join("\n\t"); + } + if (this.answers.length > 0) { + ret += "\n answer:" + this.answers.join("\n\t"); + } + if (this.authorities.length > 0) { + ret += "\n authority:" + this.authorities.join("\n\t"); + } + if (this.additionals.length > 0) { + ret += "\n additional:" + this.additionals.join("\n\t"); + } + + return ret; +}; + +module.exports = DNS; diff --git a/decode/dns/query.js b/decode/dns/query.js new file mode 100644 index 0000000..ee131d5 --- /dev/null +++ b/decode/dns/query.js @@ -0,0 +1,101 @@ +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.bytesDecoded = undefined; +} + +DnsQuery.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++]); + } + } + + this.type = raw_packet.readUInt16BE(offset); + offset += 2; + this.class = raw_packet.readUInt16BE(offset); + offset += 2; + this.bytesDecoded = offset - initialOffset; + + 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; diff --git a/decode/dns/resource_record.js b/decode/dns/resource_record.js new file mode 100644 index 0000000..38c5f9b --- /dev/null +++ b/decode/dns/resource_record.js @@ -0,0 +1,71 @@ +var decodeName = require("./util").decodeName; +var IPv4Addr = require("../ipv4_addr"); +var IPv6Addr = require("../ipv6_addr"); + +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; + + // the number of bytes decoded by this instance. + this.bytesDecoded = undefined; +} + +DnsResourceRecord.prototype.decode = function (raw_packet, offset) { + var initialOffset = offset; + var decodedName = decodeName(raw_packet, offset); + this.name = decodedName.name; + offset += decodedName.bytesDecoded; + + 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; + + 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.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); + } + + offset += this.rdlength; + this.bytesDecoded = offset - initialOffset; + + return this; +}; + +module.exports = DnsResourceRecord; diff --git a/decode/dns/util.js b/decode/dns/util.js new file mode 100644 index 0000000..3547725 --- /dev/null +++ b/decode/dns/util.js @@ -0,0 +1,29 @@ +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 && segLength <= 63) { + if(firstSegment) { + firstSegment = false; + } else { + result.name += "."; + } + + for (var i = 0; i < segLength; i++) { + 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; +}; diff --git a/spec/decode/dns/flags.spec.js b/spec/decode/dns/flags.spec.js new file mode 100644 index 0000000..651a1c6 --- /dev/null +++ b/spec/decode/dns/flags.spec.js @@ -0,0 +1,78 @@ +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(); + + 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); + }); + }); +}); diff --git a/spec/decode/dns.spec.js b/spec/decode/dns/index.spec.js similarity index 72% rename from spec/decode/dns.spec.js rename to spec/decode/dns/index.spec.js index 379e438..17be48d 100644 --- a/spec/decode/dns.spec.js +++ b/spec/decode/dns/index.spec.js @@ -1,6 +1,7 @@ -var Dns = require("../../decode/dns"); +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(){ @@ -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 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); + }); + }); +}); diff --git a/spec/decode/dns/resource_record.spec.js b/spec/decode/dns/resource_record.spec.js new file mode 100644 index 0000000..d02e68c --- /dev/null +++ b/spec/decode/dns/resource_record.spec.js @@ -0,0 +1,63 @@ +var DnsResourceRecord = require("../../../decode/dns/resource_record"); +var IPv4Addr = require("../../../decode/ipv4_addr"); +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 + "0001" + // type + "0001" + // class + "00000009" + //ttl + "0004" + // resource length + "01020304", // resource 1.2.3.4 (ipv4) + "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", 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() { + 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", 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); + }); + + 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"); + }); + }); +});