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`.
|
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
|
||||||
====
|
====
|
||||||
|
|
||||||
|
|
|
@ -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