Fast, lightweight, easy-to-extend, easy-to-test, pure JavaScript (ES5.1) implementation for DNS / mDNS.
Go to file
AJ ONeal fb55be240d started browser code 2017-02-01 21:57:40 -07:00
bin refactor as part of #8 compat with native-dns-packet / dns-js 2017-01-30 16:30:08 -07:00
examples cleanup 2017-01-21 14:44:37 -07:00
samples finished PTR parser 2017-02-01 20:37:28 -07:00
test/fixtures reorganzing 2017-01-28 13:53:54 -07:00
.gitignore add .gitignore and delete node_modules 2017-01-21 14:39:54 -07:00
.gitmodules reorganzing 2017-01-28 13:53:54 -07:00
README.md finished task #6. Moved the location where the class and type is being assigned. 2017-01-26 17:03:16 -07:00
browser.js started browser code 2017-02-01 21:57:40 -07:00
demo.html started browser code 2017-02-01 21:57:40 -07:00
dns.classes.js in reverse also 2017-01-21 13:35:06 -07:00
dns.js started working on the mx record type. Parses but does not have periods in exchange address. 2017-02-01 18:28:43 -07:00
dns.pack.js started browser code 2017-02-01 21:57:40 -07:00
dns.packer.js refactor as part of #8 compat with native-dns-packet / dns-js 2017-01-30 16:30:08 -07:00
dns.parser.js started browser code 2017-02-01 21:57:40 -07:00
dns.rdata.parse.js starting dns record type parsers 2017-01-26 19:18:46 -07:00
dns.type.a.js use ab.slice(start, end) instead of ui8 array for rdata 2017-01-30 11:28:12 -07:00
dns.type.aaaa.js simplify dns.type.aaaa.js 2017-01-30 11:21:06 -07:00
dns.type.any.js starting dns record type parsers 2017-01-26 19:18:46 -07:00
dns.type.cname.js added demo.html file, finished MX record type parser. 2017-02-01 19:45:03 -07:00
dns.type.mx.js cleanup 2017-02-01 21:41:33 -07:00
dns.type.ns.js NS record type parser complete. 2017-02-01 20:32:01 -07:00
dns.type.ptr.js finished PTR parser 2017-02-01 20:37:28 -07:00
dns.type.soa.js finished task #6. Moved the location where the class and type is being assigned. 2017-01-26 17:03:16 -07:00
dns.type.srv.js starting dns record type parsers 2017-01-26 19:18:46 -07:00
dns.types.js found bug. Changed the AAAA hex value. 2017-01-26 18:37:15 -07:00
dns.unpack-labels.js It's a Christmas miracle! refactored and got CNAME in one shot - W00T! 2017-01-28 15:15:27 -07:00
howto.md renamed some files and added objective statement and purpose of document 2017-01-14 13:49:40 -07:00
notes.md renamed some files and added objective statement and purpose of document 2017-01-14 13:49:40 -07:00
package.json refactor as part of #8 compat with native-dns-packet / dns-js 2017-01-30 16:30:08 -07:00
parse-binary-test.js added the test code that AJ wrote 2017-01-20 17:10:00 -07:00

README.md

dns-lint

Fast, lightweight, pure JavaScript (ES5.1) implementation for DNS / mDNS.

Works great in Web Browsers and in node.js!

Detailed error checking makes it great for

  • capture
  • packing (JSON to DNS)
  • parsing (DNS to JSON)
  • linting (finding errors in packets)
  • debugging

No external dependencies for modern browsers. Uses DataView, Uint8Array, Uint16Array, and ArrayBuffer

Similar API to dns.js and native-dns-packet.

{ "id": 54231
, "qr": 0
, "opcode": 0
, "aa": 0
, "tc": 0
, "rd": 1
, "ra": 0
, "res1": 0
, "res2": 0
, "res3": 0
, "rcode": 0
, "questions": [
    { "name": "bowie._sftp-ssh._tcp.local"
    , "type": 1
    , "class": 1
    , "byteLength": 32
    }
  ]
, "answers": []
, "authority": []
, "additional": []
, "byteLength": 44
}

Install

npm install git+https://git@git.daplie.com:Daplie/dns-lint

Usage

CLI

You can work directly from node_modules/dns-lint:

pushd node_modules/dns-lint/

Capture mDNS broadcast packets

# example
# node bin/mdns-capture.js <file-prefix>
node bin/mdns-capture.js mdns-test
# in another terminal
dig @224.0.0.251 -p 5353 -t PTR _services._dns-sd._udp.local

Parsing a saved packet

# example
# node bin/dns-parse.js </path/to/packet.dns.bin>
node bin/dns-parse.js samples/a-0.mdns.bin

You can also parse a saved packet from the native-dns-packet directory. these test packets have the binary for each record type and what it's parsed output should be.

Library

  • packet = dnsjs.unpack(arrayBuffer)
  • packet = dnsjs.unpackRdatas(arrayBuffer, packet)
  • packet.answers[0].data = dnsjs.unpackRdatas(arrayBuffer, packet, packet.answers[0])

node.js:

var nodeBuffer = fs.readFileSync('./samples/a-0.mdns.bin');
var arrayBuffer = nodeBuffer.buffer;

var dnsjs = require('dns-lint');
var packet = dnsjs.unpack(arrayBuffer);

console.log(packet);

Browser:

var arrayBuffer = new Uint8Array.from([ /* bytes */ ]).buffer;

var packet = pdns.unpack(arrayBuffer);

console.log(packet);

mDNS Documentation

This document is currently used and update for testing purposes of DNS packets with Daplie applications. Please make note of any errata, as the organization of this document is based on the step by step process of debugging current issues regarding DNS, and not necessarily a demonstration on how to fix those issues. This document is for learning purposes and meant to assist future developers avoid similar bugs.

Objective

Create a robust DNS library that checks all possible combinations of DNS flags and messages in order to debug current DNS state for Daplie system and potentially develope DNS library with built in linting for use of Daplie, inc and community.

How to duplicate DNS crash:

>> cd ~/dns_test
>> node listen.jss

Then in another terminal enter:

>> dig @224.0.0.251 -p 5353 -t PTR _cloud._tcp.local

The listener then crashes with an output of:

START DNS PACKET
/home/daplie/dns_test/node_modules/dns-js/lib/bufferconsumer.js:52
      throw new Error('Buffer overflow')
      ^

Error: Buffer overflow
    at BufferConsumer.slice (/home/daplie/dns_test/node_modules/dns-js/lib/bufferconsumer.js:52:13)
  s  at Function.DNSRecord.parse (/home/daplie/dns_test/node_modules/dns-js/lib/dnsrecord.js:237:46)
    at /home/daplie/dns_test/node_modules/dns-js/lib/dnspacket.js:164:30
    at Array.forEach (native)
    at Function.DNSPacket.parse (/home/daplie/dns_test/node_modules/dns-js/lib/dnspacket.js:159:17)
    at /home/daplie/dns_test/cloud-respond.js:86:31
    at Array.forEach (native)
    at /home/daplie/dns_test/cloud-respond.js:11:21
    at Array.forEach (native)
    at Object.module.exports.respond (/home/daplie/dns_test/cloud-respond.js:10:11)

After commenting out lines 45-53 in dns_test/node_modules/dns-js/lib/bufferconsumer.js and rerunning the previous commands, the result is a new error:

START DNS PACKET
buffer.js:829
    throw new RangeError('Index out of range');
    ^

RangeError: Index out of range
    at checkOffset (buffer.js:829:11)
    at Buffer.readUInt8 (buffer.js:867:5)
    at BufferConsumer.byte (/home/daplie/dns_test/node_modules/dns-js/lib/bufferconsumer.js:67:22)
    at BufferConsumer.name (/home/daplie/dns_test/node_modules/dns-js/lib/bufferconsumer.js:120:14)
    at Function.DNSRecord.parse (/home/daplie/dns_test/node_modules/dns-js/lib/dnsrecord.js:187:14)
    at /home/daplie/dns_test/node_modules/dns-js/lib/dnspacket.js:164:30
    at Array.forEach (native)
    at Function.DNSPacket.parse (/home/daplie/dns_test/node_modules/dns-js/lib/dnspacket.js:159:17)
    at /home/daplie/dns_test/cloud-respond.js:86:31
    at Array.forEach (native)

which is located in the node.js buffer module. The API is here.

However, the error we are working with will most likely be dealt with by parsing through the binary and putting it in a format that is acceptable to a custom buffer, since the current buffer.js does doesn't seem to do the trick.

Using

function pad(str, len, ch) {

  while (str.length < len) {
    str = ch + str;
  }

  return str;
}

the binary output comes out as:

11100001
10001000
00000001
00100000
00000000
00000001
00000000
00000000
00000000
00000000
00000000
00000001
00000110
01011111
01100011
01101100
01101111
01110101
01100100
00000100
01011111
01110100
01100011
01110000
00000101
01101100
01101111
01100011
01100001
01101100
00000000
00000000
00001100
00000000
00000001
00000000
00000000
00101001
00010000
00000000
00000000
00000000
00000000
00000000
00000000
00000000

In order to figure out where the dnspacket code is going wrong, We will have to write our own test code to see if the dns code is doing what it's supposed to be doing.

Looking at the code below:

// parses through the flags
// val can be 1 or 0
// (val & 0x8000) >> 15 does the following
// val = 0000 0000 0000 0001
//     & 1001 0000 0000 0000
//    _______________________
//       0000 0000 0000 0000
// same if val were 0
// but if val were 0x8000
// then
// (val & 0x8000) >> 15 does the following
// val = 1001 0000 0000 0001
//     & 1001 0000 0000 0000
//    _______________________
//       1001 0000 0000 0000
// >>15= 0000 0000 0000 0001

function parseFlags(val, packet) {
  packet.header.qr = (val & 0x8000) >> 15;
  packet.header.opcode = (val & 0x7800) >> 11;
  packet.header.aa = (val & 0x400) >> 10;
  packet.header.tc = (val & 0x200) >> 9;
  packet.header.rd = (val & 0x100) >> 8;
  packet.header.ra = (val & 0x80) >> 7;
  packet.header.res1 = (val & 0x40) >> 6;
  packet.header.res2 = (val & 0x20) >> 5;
  packet.header.res3 = (val & 0x10) >> 4;
  packet.header.rcode = (val & 0xF);
}

One effective way to check is to create a dns packet, pass it to a custom packer and parser function and compare the input and output:

'use strict';

// order one http://www.binarytides.com/dns-query-code-in-c-with-linux-sockets/
// order two http://www.zytrax.com/books/dns/ch15/

function parse(i) {
  var header = {
   // first byte appears to be reverse
    qr: (i & 0x8000) >> 15
  , opcode: (i & 0x7800) >> 11
  , aa: (i & 0x400) >> 10
  , tc: (i & 0x200) >> 9
  , rd: (i & 0x100) >> 8

  // second byte appears to be second byte, but also in reverse
  , ra: (i & 0x80) >> 7
  , res1: (i & 0x40) >> 6 // z
  , res2: (i & 0x20) >> 5 // ad
  , res3: (i & 0x10) >> 4 // cd
  , rcode: (i & 0x1F)
  };

  return header;
}

function pack(header) {
  var val = 0;

  val += (header.qr << 15) & 0x8000;
  val += (header.opcode << 11) & 0x7800;
  val += (header.aa << 10) & 0x400;
  val += (header.tc << 9) & 0x200;
  val += (header.rd << 8) & 0x100;
  val += (header.ra << 7) & 0x80;
  val += (header.res1 << 6) & 0x40;
  val += (header.res2 << 5) & 0x20;
  val += (header.res3 << 4) & 0x10;
  val += header.rcode & 0x1F;

  return val;
}

var start = {
  qr: 1
, opcode: 12
, aa: 0
, tc: 0
, rd: 1
, ra: 0
, res1: 0
, res2: 0
, res3: 0
, rcode: 0
};

var i = pack(start);

var obj = parse(i);

console.log(i);
console.log(start);
console.log(obj);
// does a recursive check to see if start and obj are equal
require('assert').deepEqual(start, obj);

How to print out hex values of the DNS message in node.js?

socket.on('message', function (message, rinfo) {
  console.log('Received %d bytes from %s:%d\n',
    message.length, rinfo.address, rinfo.port);
  //console.log(msg.toString('utf8'));

  console.log(message.toString('hex'));

DNS sec: security puts a signature on a DNS packet and imprints a signature so that the sender of the packet is confirmed