how to contribute

This commit is contained in:
AJ ONeal 2017-02-11 08:51:47 -07:00
parent 8711c0b16e
commit f71b9b5aff
2 changed files with 224 additions and 11 deletions

119
README.md
View File

@ -18,21 +18,25 @@ Detailed error checking makes it great for
Similar API to `dns.js` and `native-dns-packet`. Similar API to `dns.js` and `native-dns-packet`.
```json ```json
{ "id": 54231 { "header": {
, "qr": 0 "id": 54231
, "opcode": 0 , "qr": 0
, "aa": 0 , "opcode": 0
, "tc": 0 , "aa": 0
, "rd": 1 , "tc": 0
, "ra": 0 , "rd": 1
, "res1": 0 , "ra": 0
, "res2": 0 , "res1": 0
, "res3": 0 , "res2": 0
, "rcode": 0 , "res3": 0
, "rcode": 0
}
, "question": [ , "question": [
{ "name": "bowie._sftp-ssh._tcp.local" { "name": "bowie._sftp-ssh._tcp.local"
, "type": 1 , "type": 1
, "typeName": "A"
, "class": 1 , "class": 1
, "className": "IN"
, "byteLength": 32 , "byteLength": 32
} }
] ]
@ -120,6 +124,99 @@ var packet = pdns.unpack(arrayBuffer);
console.log(packet); console.log(packet);
``` ```
Contributing and Development
============================
How to add a new parser
-----------------------
Each RR (aka Resource Record or RData) parser is individual. Examples include:
* A (`dns.type.a.js`)
* AAAA (`dns.type.aaaa.js`)
* CNAME (`dns.type.cname.js`)
* TXT (`dns.type.txt.js`)
* SRV (`dns.type.srv.js`)
Let's say that To create a parser for a type which we don't currently support,
just add the appropriate information to `dns.types.js` and create a file for
the name of the type in the format `dns.type.<typename>.js`.
For example, if `CNAME` wasn't already supported and I wanted to add support for
it I would follow these steps:
1) Update `dns.types.js`
```
A: 0x01 // 1
, NS: 0x02 // 2
, CNAME: 0x05 // 5 // I would simply add this line
, SOA: 0x06 // 6
```
2) Capture a packet to `test/fixtures/<domain>.<tld>.<type>.bin`
This will construct and send a DNS query and save the first result
that comes back.
In some cases (such as CNAME), the typical (or required) way to illicit
the desired response is to make a request of a different type.
If that's the case, manually rename the the file afterwards.
Ideally you should have some idea of what the result file should look
like and should place that in `test/fixtures/<domain>.<tld>.<type>.json`
```bash
node bin/capture-query.js --name www.google.com --type CNAME
```
3) Create `dns.type.cname.js`
Copy `dns.type.TEMPLATE.js` to the type for which you wish to create support
(`dns.type.cname.js` in this example) and fill in the blanks.
```
var unpackLabels = exports.DNS_UNPACK_LABELS || require('./dns.unpack-labels.js').DNS_UNPACK_LABELS;
exports.DNS_TYPE_CNAME = function (ab, packet, record) {
// record = { rdstart, rdlength, type, class }
// example of not parsing and just leaving as binary data
record.data = new Uint8Array(ab.slice(record.rdstart, record.rdstart + record.rdlength));
return record;
};
}('undefined' !== typeof window ? window : exports));
```
4) Document what you've learned in `doc/<type>.txt`
You may be right or you might be wrong, but you might be right.
In any case, take a minute to document some of the gritty details of what you learned about this
record type - tips, tricks, little-known facts, etc.
This may help (or wildly mislead) others if there's a bug in your parser that they need to track down.
At the very least someone can follow a few links you followed and your thought process.
5) Check that my changes include these files
```
├── README.md
├── demo.html (add the appropriate script tag)
├── doc
| └── cname.txt
├── dns.classes.js (not necessarily, but potentially)
├── dns.type.cname.js
├── dns.types.js
├── package.json (bump the minor version)
└── test
└── fixtures
   ├── www.google.com.cname.bin
   └── www.google.com.cname.js
```
mDNS Documentation mDNS Documentation
==== ====

116
dns.type.TEMPLATE.js Normal file
View File

@ -0,0 +1,116 @@
(function (exports) {
'use strict';
// Put some documentation here in these comments.
// See examples of documentation in dns.type.a.js
// and dns.type.mx.js
// If the data type you're wanting to unpack contains labels
// (meaning text that will be represented as a period-separated
// domain name, i.e. www.google.com) then you will need to use
// `unpackLabels`, which has compression pointer support
var unpackLabels = exports.DNS_UNPACK_LABELS || require('./dns.unpack-labels.js').DNS_UNPACK_LABELS;
// The parser receives exactly 3 parameters as follows:
//
// ab - an ArrayBuffer containing the whole of the packet as binary
// you will use Uint8Array (for which endianness doesn't matter)
// and DataView (with wich you MUST always specify 'false' for
// Big Endian, which is "network byte order")
//
// packet - a plain javascript value object (i.e. to/from JSON) that
// has all of the currently parsed fields (generally not used),
// containing meta data in `header`, and arrays of potentially
// parsed (or not yet parsed) records in `question`, `answer`,
// `authority`, `additional`
//
// record - an element of an array in packet (meaning one of `question`,
// `answer`, `authority`, `additional`) which has potentially
// helpful data about the record such as `rdstart` and `rdlength`,
// signifying the type at which the binary segment for this record
// begins and its length
//
exports.DNS_TYPE_MX = function (ab, packet, record) {
//
// Slicing RData
//
// For various reasons it may be easier to work with a slice of the
// ArrayBuffer container the binary packet that just represents the
// RData you want to work with starting at 0 and ending at the end of
// the Resource Record (aka RData) rather than always adding `rdstart`
// to some offset and checking that you don't read past the record's
// end (`rdstart + rdlength`)
var rdataAb = ab.slice(record.rdstart, record.rdstart + record.rdlength);
//
// Using Uint8Array
//
// it's very likely that you'll want to process individual bytes,
// for which you would use Uint8Array - for example, if some significant
// portion of the record is to be read as a non-label string
var ui8 = new Uint8Array(rdataAb);
// Example: reading a string whose length is defined by the first byte
var len = ui8[0];
var i;
record.value = '';
for (i = 0; i < len; i += 1) {
record.value += String.fromCharCode(ui8[i]);
}
// Example: reading a string whose length is terminated with 0
var i;
record.value = '';
for (i = 0; i < len; i += 1) {
if (0 === ui8[i]) {
break;
}
record.value += String.fromCharCode(ui8[i]);
}
//
// Using DataView
//
// it's also very likely that you'll want to interpret some variable
// byte-width data, such as an id or type number, something of that nature
var dv = new DataView(rdataAb);
// Example: reading a single-octet type, a sexdectet id, and quad-octet date
record.rtype = dv.getUint8(0, false); // start at 0
record.rid = dv.getUint16(1, false); // next is at 1
record.date = new Date(dv.getUint32(3, false) * 1000); // advance 2 more bytes to 3
// the next read, if any, would be at 7
//
// Unpacking Labels
//
// if your data type contains labels (i.e. www.google.com would be represented
// as 3 labels and would have the byte sequence 0x03"www"0x06"google"0x03"com),
// they may contain compression pointers (represented as 0xc0, meaning 192 in
// decimal - outside the ascii range) and they may be terminated either by 0x00
// or by the end of the record, so you should use unpackLabels and provide an
// ArrayBuffer that is sliced to the end of your record (otherwise record-length
// terminated strings would be misinterpretted overflow)
// Example: assuming some label started at the 7th byte
var truncatedAb = new Uint8Array(ab.slice(0, record.rdstart + record.rdlength));
var labelData = unpackLabels(truncatedAb, record.rdstart+7, { byteLength: 0, cpcount: 0, labels: [], name: '' });
record.deviceName = labelData.name;
// finally, return the record
return record;
};
}('undefined' !== typeof window ? window : exports));