how to contribute
This commit is contained in:
parent
8711c0b16e
commit
f71b9b5aff
119
README.md
119
README.md
|
@ -18,21 +18,25 @@ Detailed error checking makes it great for
|
|||
Similar API to `dns.js` and `native-dns-packet`.
|
||||
|
||||
```json
|
||||
{ "id": 54231
|
||||
, "qr": 0
|
||||
, "opcode": 0
|
||||
, "aa": 0
|
||||
, "tc": 0
|
||||
, "rd": 1
|
||||
, "ra": 0
|
||||
, "res1": 0
|
||||
, "res2": 0
|
||||
, "res3": 0
|
||||
, "rcode": 0
|
||||
{ "header": {
|
||||
"id": 54231
|
||||
, "qr": 0
|
||||
, "opcode": 0
|
||||
, "aa": 0
|
||||
, "tc": 0
|
||||
, "rd": 1
|
||||
, "ra": 0
|
||||
, "res1": 0
|
||||
, "res2": 0
|
||||
, "res3": 0
|
||||
, "rcode": 0
|
||||
}
|
||||
, "question": [
|
||||
{ "name": "bowie._sftp-ssh._tcp.local"
|
||||
, "type": 1
|
||||
, "typeName": "A"
|
||||
, "class": 1
|
||||
, "className": "IN"
|
||||
, "byteLength": 32
|
||||
}
|
||||
]
|
||||
|
@ -120,6 +124,99 @@ var packet = pdns.unpack(arrayBuffer);
|
|||
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
|
||||
====
|
||||
|
||||
|
|
|
@ -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));
|
Loading…
Reference in New Issue