UNPKG

@sintaxi/dnsd

Version:

Dynamic authoritative name server for Iris services

224 lines (179 loc) 9.6 kB
// Copyright 2012 Iris Couch, all rights reserved. // // Test DNS message parsing var fs = require('fs') var tap = require('tap') var test = tap.test var util = require('util') var API = require('../named') test('Parse all known messages', function(t) { var files = [ 'dynamic-update', 'oreilly.com-query', 'oreilly.com-response', 'www.company.example-query' , 'www.company.example-response', 'www.microsoft.com-query', 'www.microsoft.com-response' , 'edns-query' ] files.forEach(function(name) { var data = packet(name) , msg t.doesNotThrow(function() { msg = API.parse(data) }, 'No errors parsing ' + name) t.ok(msg, 'Parse packet: ' + name) t.ok(msg.id, 'Packet id: ' + name) //console.log('%s:\n%s\n\n', name, util.inspect(msg, 0, 10)) }) t.end() }) // test('Parse invalid messages', function(t) { // var data = new Buffer('My name is Jason and I am awesome.') // t.throws(function() { API.parse(data) }, 'Exception parsing random data') // t.end() // }) test('Optimized parsing', function(t) { var data = packet('oreilly.com-response') data.__parsed = 0 API.parse(data) t.equal(data.__parsed, 1, 'Parsing only runs once (after that, it is memoized)') t.end() }) test('Message attributes', function(t) { var msg msg = API.parse(packet('oreilly.com-response')) t.equal(msg.type, 'response', 'DNS response') t.equal(msg.opcode, 'query', 'DNS opcode') t.equal(msg.authoritative, true, 'Authoritative message') t.equal(msg.truncated, false, 'Non-truncated message') t.equal(msg.recursion_desired, true, 'Recursion-desired in response') t.equal(msg.responseCode, 0, 'Successful response') msg = API.parse(packet('oreilly.com-query')) t.equal(msg.type, 'request', 'DNS request') t.equal(msg.authoritative, false, 'Non-authoritative request') t.equal(msg.recursion_desired, true, 'Recursion-desired in request') msg = API.parse(packet('dynamic-update')) t.equal(msg.opcode, 'update', 'DNS update opcode') msg = API.parse(packet('www.microsoft.com-response')) t.equal(msg.recursion_desired, false, 'No recursion desired') t.equal(msg.recursion_available, false, 'No recursion available') t.end() }) test('Message sections', function(t) { var msg, rec msg = API.parse(packet('www.microsoft.com-query')) t.type(msg.question, 'Array', 'Parse question section') t.type(msg.answer, 'undefined', 'No "answer" section') t.type(msg.authority, 'undefined', 'No "authority" section') t.type(msg.additional, 'undefined', 'No "additional" section') msg = API.parse(packet('oreilly.com-response')) t.type(msg.whatever, 'undefined', 'No "whatever" section') t.type(msg.question, 'Array', 'Parse question section') t.type(msg.answer, 'Array', 'Parse answer section') t.type(msg.authority, 'Array', 'Parse authority section') t.type(msg.additional, 'Array', 'Parse additional section') t.end() }) test('Message records', function(t) { var msg, rec msg = API.parse(packet('oreilly.com-response')) t.equal(msg.question.length, 1, 'Enumerate question records') t.equal(msg.answer.length, 2, 'Enumerate answer records') t.equal(msg.authority.length, 3, 'Enumerate authority records') t.equal(msg.additional.length, 5, 'Enumerate additional records') msg.question.forEach(function(rec, i) { t.type(rec, 'object', 'Question record is object: '+i) }) msg.answer.forEach(function(rec, i) { t.type(rec, 'object', 'Answer record is object: '+i) }) msg.authority.forEach(function(rec, i) { t.type(rec, 'object', 'Authority record is object: '+i) }) msg.additional.forEach(function(rec, i) { t.type(rec, 'object', 'Additional record is object: '+i) }) msg = API.parse(packet('www.microsoft.com-response')) t.equal(msg.question [0].name, 'www.microsoft.com.nsatc.net', 'Question name') t.equal(msg.answer [0].name, 'www.microsoft.com.nsatc.net', 'Answer name') t.equal(msg.authority [0].name, 'nsatc.net' , '1st authority name') t.equal(msg.authority [1].name, 'nsatc.net' , '2nd authority name') t.equal(msg.authority [2].name, 'nsatc.net' , '3rd authority name') t.equal(msg.authority [3].name, 'nsatc.net' , '4th authority name') t.equal(msg.additional[0].name, 'j.ns.nsatc.net' , '1st additional name') t.equal(msg.additional[1].name, 'k.ns.nsatc.net' , '2nd additional name') t.equal(msg.additional[2].name, 'us-ca-6.ns.nsatc.net' , '3rd additional name') t.equal(msg.additional[3].name, 'l.ns.nsatc.net' , '4th additional name') msg = API.parse(packet('oreilly.com-response')) t.type(msg.question [0].ttl, 'undefined', 'No TTL for question record') t.equal(msg.answer [0].ttl, 3600 , '1st answer ttl') t.equal(msg.answer [1].ttl, 3600 , '2nd answer ttl') t.equal(msg.authority [0].ttl, 21600, '1st authority ttl') t.equal(msg.authority [1].ttl, 21600, '2nd authority ttl') t.equal(msg.authority [2].ttl, 21600, '3rd authority ttl') t.equal(msg.additional[0].ttl, 21600, '1st additional ttl') t.equal(msg.additional[1].ttl, 21600, '2nd additional ttl') t.equal(msg.additional[2].ttl, 21600, '3rd additional ttl') t.equal(msg.additional[3].ttl, 32537, '4th additional ttl') t.equal(msg.additional[4].ttl, 32537, '5th additional ttl') msg.question.forEach(function(rr, i) { t.equal(rr.class, 'IN', 'Question class is IN: '+i) }) msg.answer.forEach(function(rr, i) { t.equal(rr.class, 'IN', 'Answer class is IN: '+i) }) msg.authority.forEach(function(rr, i) { t.equal(rr.class, 'IN', 'Authority class is IN: '+i) }) msg.additional.forEach(function(rr, i) { t.equal(rr.class, 'IN', 'Additional class is IN: '+i) }) msg = API.parse(packet('dynamic-update')) t.equal(msg.question[0].type, 'SOA', 'SOA question class') t.equal(msg.answer[0].class, 'NONE', 'NONE answer class') msg = API.parse(packet('oreilly.com-response')) t.equal(msg.question [0].type, 'MX', 'Question type') t.equal(msg.answer [0].type, 'MX', '1st answer type') t.equal(msg.answer [1].type, 'MX', '2nd answer type') t.equal(msg.authority [0].type, 'NS', '1st authority type') t.equal(msg.authority [1].type, 'NS', '2nd authority type') t.equal(msg.authority [2].type, 'NS', '3rd authority type') t.equal(msg.additional[0].type, 'A' , '1st additional type') t.equal(msg.additional[1].type, 'A' , '2nd additional type') t.equal(msg.additional[1].type, 'A' , '3rd additional type') t.equal(msg.additional[2].type, 'A' , '4th additional type') t.equal(msg.additional[3].type, 'A' , '5th additional type') t.type (msg.question [0].data, 'undefined', 'No question data') t.same (msg.answer [0].data, [20, 'smtp1.oreilly.com'], '1st answer data') t.same (msg.answer [1].data, [20, 'smtp2.oreilly.com'], '2nd answer data') t.equal(msg.authority [0].data, 'ns1.sonic.net' , '1st authority data') t.equal(msg.authority [1].data, 'ns2.sonic.net' , '2nd authority data') t.equal(msg.authority [2].data, 'ns.oreilly.com', '3rd authority data') t.equal(msg.additional[0].data, '209.204.146.22', '1st additional data') t.equal(msg.additional[1].data, '209.58.173.22' , '2nd additional data') t.equal(msg.additional[2].data, '209.204.146.21', '3rd additional data') t.equal(msg.additional[3].data, '208.201.224.11', '4th additional data') t.equal(msg.additional[4].data, '208.201.224.33', '5th additional data') t.end() }) test('Convenient text records', function(t) { t.plan(9) var msg = API.parse(packet('txt-response')) msg.answer.forEach(function(rec, i) { t.equal(rec.type, 'TXT', 'Parse text record: '+i) t.type(rec.data, 'string', 'Single text records become a string: '+i) }) // Convert to an array and see if it persists correctly. var data = msg.answer[0].data msg.answer[0].data = [data, 'An extra string'] var body = msg.toBinary() , msg2 = API.parse(body) t.type(msg2.answer[1].data, 'string', 'Single text record still a string') t.type(msg2.answer[0].data, 'Array' , 'Multiple text records are an array') t.equal(msg2.answer[0].data.length, 2, 'All text data accounted for') t.end() }) test('Encoding messages', function(t) { var files = [ 'dynamic-update', 'oreilly.com-query', 'oreilly.com-response', 'www.company.example-query' , 'www.company.example-response', 'www.microsoft.com-query', 'www.microsoft.com-response' , 'iriscouch.com-query', 'iriscouch.com-response', 'foo.iriscouch.com-query', 'foo.iriscouch.com-response' , 'registry.npmjs.org-response', 'srv-query', 'srv-response', 'txt-query', 'txt-response' , 'ptr-query', 'ptr-response', 'aaaa-query', 'aaaa-response', 'ipv6_ptr-query', 'ds-query', 'ds-response' ] t.plan(3 * files.length) // 3 for each file files.forEach(function(file, i) { var original = packet(file) , message = API.parse(original) var encoded t.doesNotThrow(function() { encoded = API.binify(message) }, 'Encode: ' + file) // Strangely, the SOA response does not completely compress the ".com" if(file == 'iriscouch.com-response') t.same(encoded.length, original.length - 3, 'parse/stringify round-trip: ' + file) else t.same(encoded, original, 'parse/stringify round-trip: ' + file) var redecoded = API.parse(encoded) t.same(redecoded, message, 'parse/stringify/parse round-trip: ' + file) }) t.end() }) function packet(name) { return fs.readFileSync(__dirname + '/../_test_data/' + name) }