dns-suite.js/README.md

498 lines
10 KiB
Markdown
Raw Permalink Normal View History

2019-10-20 05:52:59 +00:00
# [dns-suite.js](https://git.rootprojects.org/root/dns-suite.js)
2023-01-20 07:10:41 +00:00
| Built by [Root](https://rootprojects.org) for [Telebit](https://telebit.io) and [Hub](https://rootprojects.org/hub)
2017-01-21 22:07:10 +00:00
2017-10-28 06:02:40 +00:00
| **dns-suite.js**
| [dig.js](https://git.coolaj86.com/coolaj86/dig.js)
2017-11-03 05:48:55 +00:00
| [mdig.js](https://git.coolaj86.com/coolaj86/mdig.js)
2017-10-28 06:02:40 +00:00
| [digd.js](https://git.coolaj86.com/coolaj86/digd.js)
2017-01-21 22:07:10 +00:00
2019-10-20 05:52:59 +00:00
Fast, lightweight, and easy-to-extend Vanilla JS (ES5.1) implementation of DNS / mDNS for Node.js and Browsers.
2017-01-21 22:07:10 +00:00
2019-10-20 05:52:59 +00:00
- [x] Full DNS Support
- [x] Queries
- [x] Answers
- [x] Authority
- [x] Additional
- [x] Built for Debugging
- [x] capture
- [x] packing (JSON to DNS/mDNS)
- [x] parsing (DNS/mDNS to JSON)
- [x] linting (finding errors in packets)
2017-01-21 22:07:10 +00:00
2019-10-20 05:52:59 +00:00
Uses `DataView`, `Uint8Array`, `Uint16Array`, and `ArrayBuffer`
2017-01-21 22:07:10 +00:00
Similar API to `dns.js` and `native-dns-packet`.
2019-10-20 05:52:59 +00:00
# Example Query
2017-01-21 22:07:10 +00:00
2019-10-20 05:52:59 +00:00
```js
var DNSPacket = require('dns-suite').DNSPacket;
2019-10-20 05:52:59 +00:00
var query = {
header: {
id: rnd,
qr: 0,
opcode: 0,
aa: 0,
rd: 1,
ra: 0,
rcode: 0
},
question: [
{
name: 'google.com',
typeName: 'A',
className: 'IN'
}
]
};
var buffer = DNSPacket.pack(query);
```
2019-10-20 05:52:59 +00:00
# Example Response
2017-02-23 23:40:20 +00:00
2019-10-20 05:52:59 +00:00
```js
var DNSPacket = require('dns-suite').DNSPacket;
DNSPacket.parse(buffer);
2017-02-23 23:40:20 +00:00
```
2019-10-20 05:52:59 +00:00
```json
{
"header": {
"id": 5423,
"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
}
],
"answer": [],
"authority": [],
"additional": [],
"edns_options": [],
"byteLength": 44
}
2017-02-23 23:40:20 +00:00
```
2019-10-20 05:52:59 +00:00
## Install
```
npm install --save dns-suite
2017-01-21 22:07:10 +00:00
```
2017-02-03 04:26:05 +00:00
**Test**:
```bash
2019-10-20 05:52:59 +00:00
node ./node_modules/dns-suite/examples/dns-pack.js
2017-02-03 04:26:05 +00:00
```
2019-10-20 05:52:59 +00:00
## Usage
2017-01-21 22:07:10 +00:00
2019-10-20 05:52:59 +00:00
- CLI
- API
2017-02-24 01:09:21 +00:00
2017-09-28 19:30:03 +00:00
### CLI Usage
2017-01-21 22:07:10 +00:00
2017-09-28 19:30:03 +00:00
When installed globally you can use these commands:
2017-01-21 22:07:10 +00:00
2017-09-28 19:30:03 +00:00
```
dns-parse.js </path/to/packet.dns.bin> [out.json] # parses a saved DNS packet to JSON
dns-pack.js </path/to/packet.dns.json> [out.bin] # packs a JSON DNS packet to binary
dns-test.js </path/to/packet.dns(.json|.bin)> # convert a packet back and forth to test reciprocity of the packer and parser
2017-01-21 22:07:10 +00:00
```
2017-10-28 06:02:40 +00:00
For **capturing packets** you should use [`dig.js`](https://git.coolaj86.com/coolaj86/dig.js#options) with the `--output` option.
It can capture mDNS as well. See <https://git.coolaj86.com/coolaj86/dig.js#options>.
2017-09-30 00:23:03 +00:00
2017-09-28 19:30:03 +00:00
You can also access them directly from `node_modules/dns-suite` in a project:
2017-01-21 22:07:10 +00:00
2017-01-21 22:11:39 +00:00
```bash
2017-09-28 19:30:03 +00:00
node node_modules/dns-suite/bin/dns-parse.js node_modules/dns-suite/samples/a-0.mdns.bin
2017-01-21 22:07:10 +00:00
```
2017-09-28 19:30:03 +00:00
### Library API
2017-01-21 22:07:10 +00:00
2019-10-20 05:52:59 +00:00
- `DNSPacket.parse(nodeOrArrayBuffer)` returns json (as shown above)
- `DNSPacket.pack(packet)` returns ArrayBuffer (browser and node)
- `DNSPacket.write(packet)` returns NodeBuffer (node only)
2017-01-21 22:07:10 +00:00
node.js:
2019-10-20 05:52:59 +00:00
2017-01-21 22:11:39 +00:00
```js
2017-01-21 22:07:10 +00:00
var nodeBuffer = fs.readFileSync('./samples/a-0.mdns.bin');
var arrayBuffer = nodeBuffer.buffer;
2017-02-24 01:09:21 +00:00
var DNSPacket = require('dns-suite').DNSPacket;
var packet = DNSPacket.parse(arrayBuffer);
var ab = DNSPacket.pack(packet);
2017-01-21 22:07:10 +00:00
console.log(packet);
2017-02-24 01:09:21 +00:00
console.log(new Uint8Array(ab));
2017-01-21 22:07:10 +00:00
```
Browser:
2019-10-20 05:52:59 +00:00
2017-01-21 22:11:39 +00:00
```js
2019-10-20 05:52:59 +00:00
var arrayBuffer = new Uint8Array.from([
/* bytes */
]).buffer;
2017-01-21 22:07:10 +00:00
2017-02-24 01:09:21 +00:00
var packet = DNSPacket.parse(arrayBuffer);
var ab = DNSPacket.pack(packet);
2017-01-21 22:07:10 +00:00
console.log(packet);
2017-02-24 01:09:21 +00:00
console.log(new Uint8Array(ab));
```
2019-10-20 05:52:59 +00:00
## Capturing Packets
2017-02-24 01:09:21 +00:00
2017-10-28 06:02:40 +00:00
We have a command line tool for that! See [dig.js](https://git.coolaj86.com/coolaj86/dig.js).
2017-02-24 01:09:21 +00:00
```bash
2017-02-24 01:09:21 +00:00
# Install
2017-10-28 06:02:40 +00:00
npm install -g 'git+https://git.coolaj86.com/coolaj86/dig.js.git'
2017-02-24 01:09:21 +00:00
# Use with DNS
2018-03-21 02:03:47 +00:00
dig.js A coolaj86.com --output .
2017-02-24 01:09:21 +00:00
# Use with mDNS
dig.js --mdns PTR _services._dns-sd._udp.local --output .
2017-01-21 22:07:10 +00:00
```
2019-10-20 05:52:59 +00:00
# Resource Record Examples
2019-10-20 05:52:59 +00:00
- SOA
- NS
- A
- AAAA
- CNAME
- MX
- TXT
- SRV
- PTR
2019-10-20 05:52:59 +00:00
## SOA
I'm pretty sure that the SOA only goes in the `authority` section
(except when SOA is queried explicitly)
and that it's only given as a response to any empty set
(where `RCODE == NXDOMAIN`)
to affirm "yes, I am responsible for this domain but, no, I don't have a record for it".
If another nameserver has been delegated authority for a particular subdomain
a set of `NS` records should be returned instead.
```json
{
2019-10-20 05:52:59 +00:00
"name": "yahoo.com",
"type": 6,
"typeName": "SOA",
"class": 1,
"className": "IN",
"ttl": 599,
"primary": "ns1.yahoo.com",
"admin": "hostmaster.yahoo-inc.com",
"serial": 2017092539,
"refresh": 3600,
"retry": 300,
"expiration": 1814400,
"minimum": 600
}
```
2019-10-20 05:52:59 +00:00
## NS
I'm also pretty sure that the NS only goes in the `authority` section
(except when NS is queried explicitly)
and that it's given as a successful response
(`RCODE == SUCCESS`)
to any query type
(`A` or `AAAA`, `MX`, `TXT`, or `SRV`)
where the answer sections is an empty set because the records in
question have been delegated to another nameserver.
```json
{
2019-10-20 05:52:59 +00:00
"name": "google.com",
"type": 2,
"typeName": "NS",
"class": 1,
"className": "IN",
"ttl": 82790,
"data": "ns3.google.com"
}
```
2019-10-20 05:52:59 +00:00
## A
The most common type of record. Returns the IPv4 address for a given domain.
```json
{
2019-10-20 05:52:59 +00:00
"name": "www.linode.com",
"type": 1,
"typeName": "A",
"class": 1,
"className": "IN",
"ttl": 291,
"address": "72.14.191.202"
}
```
2019-10-20 05:52:59 +00:00
## AAAA
Returns the IPv6 address for a given domain.
```json
{
2019-10-20 05:52:59 +00:00
"name": "irc6.geo.oftc.net",
"type": 28,
"typeName": "AAAA",
"class": 1,
"className": "IN",
"ttl": 59,
"address": "2607:f8f0:610:4000:211:11ff:fe1c:7bec"
}
```
2019-10-20 05:52:59 +00:00
## CNAME
The CNAME is used to look up the IP address for the given alias.
(the alias is often referred to incorrectly as a CNAME but it is, in fact, the alias)
```json
{
2019-10-20 05:52:59 +00:00
"name": "www.nodejs.org",
"type": 5,
"typeName": "CNAME",
"class": 1,
"className": "IN",
"ttl": 3600,
"data": "nodejs.org"
}
```
2019-10-20 05:52:59 +00:00
## MX
Mail Exchange Records show the alias that should be looked up to know where incoming mail should
be sent.
```json
{
2019-10-20 05:52:59 +00:00
"name": "microsoft.com",
"type": 15,
"typeName": "MX",
"class": 1,
"className": "IN",
"ttl": 197,
"priority": 10,
"exchange": "microsoft-com.mail.protection.outlook.com"
}
```
2019-10-20 05:52:59 +00:00
## TXT
Often used for outgoing mail validations, public keys, lots of arbitrary stuff.
```json
{
2019-10-20 05:52:59 +00:00
"name": "aol.com",
"type": 16,
"typeName": "TXT",
"class": 1,
"className": "IN",
"ttl": 1926,
"data": ["v=spf1 ptr:mx.aol.com ?all"]
}
```
2019-10-20 05:52:59 +00:00
## SRV
A way to associate a service with a port and other relevant information.
Used for federated / dencentralized protocols (like XMPP) and mDNS/DLNA/UPnP/DNS-SD type stuff.
```json
{
2019-10-20 05:52:59 +00:00
"name": "_xmpp-server._tcp.gmail.com",
"type": 33,
"typeName": "SRV",
"class": 1,
"className": "IN",
"ttl": 900,
"priority": 5,
"weight": 0,
"port": 5269,
"target": "xmpp-server.l.google.com"
}
```
2019-10-20 05:52:59 +00:00
## PTR
Used for mDNS/DNS-SD type discoveries and anti-spam reverse lookup verification for mail servers.
```json
{
2019-10-20 05:52:59 +00:00
"name": "_pdl-datastream._tcp.local",
"type": 12,
"typeName": "PTR",
"class": 1,
"className": "IN",
"ttl": 255,
"data": "Canon MF620C Series._pdl-datastream._tcp.local"
2017-10-06 20:39:54 +00:00
}
```
2019-10-20 05:52:59 +00:00
## All Properties
2017-10-06 20:39:54 +00:00
For simplicity, here's a list of all properties, just for fun:
```js
{
// All RRs
"name": "example.com",
"type": 1,
"typeName": "A",
"class": 1,
"className": "IN",
"ttl": 600,
// SOA
"primary": "ns1.yahoo.com",
"admin": "hostmaster.yahoo-inc.com",
"serial": 2017092539,
"refresh": 3600,
"retry": 300,
"expiration": 1814400,
"minimum": 600,
// A, AAAA
"address": "72.14.191.202",
// CNAME, NS, PTR
"data": "ns3.google.com",
// TXT
// "data": [ "v=spf1 ptr:mx.aol.com ?all" ],
// MX
"priority": 10,
"exchange": "microsoft-com.mail.protection.outlook.com",
// SRV
"priority": 5,
"weight": 0,
"port": 5269,
"target": "xmpp-server.l.google.com"
}
```
2019-10-20 05:52:59 +00:00
# Contributing and Development
2017-02-11 15:51:47 +00:00
2019-10-20 05:52:59 +00:00
## How to add a new parser
2017-02-11 15:51:47 +00:00
Each RR (aka Resource Record or RData) parser is individual. Examples include:
2019-10-20 05:52:59 +00:00
- A (`parser/type.a.js`)
- AAAA (`parser/type.aaaa.js`)
- CNAME (`parser/type.cname.js`)
- TXT (`parser/type.txt.js`)
- SRV (`parser/type.srv.js`)
2017-02-11 15:51:47 +00:00
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
2017-02-17 23:22:56 +00:00
the name of the type in the format `parser/type.<typename>.js`.
2017-02-11 15:51:47 +00:00
For example, if `CNAME` wasn't already supported and I wanted to add support for
it I would follow these steps:
2019-10-20 05:52:59 +00:00
1. Update `dns.types.js` if it's not there already.
2017-02-11 15:51:47 +00:00
```
A: 0x01 // 1
, NS: 0x02 // 2
, CNAME: 0x05 // 5 // I would simply add this line
, SOA: 0x06 // 6
```
2019-10-20 05:52:59 +00:00
2. Capture a packet to `test/fixtures/<domain>.<tld>.<type>.bin`
2017-02-11 15:51:47 +00:00
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
npm install -g dig.js
dig.js --name www.google.com --type CNAME --output ./samples/
2017-02-11 15:51:47 +00:00
```
2019-10-20 05:52:59 +00:00
3. Create `parser/type.cname.js`
2017-02-11 15:51:47 +00:00
2017-02-17 23:22:56 +00:00
Copy `parser/type.TEMPLATE.js` to the type for which you wish to create support
(`parser/type.cname.js` in this example) and fill in the blanks.
2017-02-11 15:51:47 +00:00
```
var unpackLabels = exports.DNS_UNPACK_LABELS || require('./dns.unpack-labels.js').DNS_UNPACK_LABELS;
2017-02-17 23:22:56 +00:00
exports.DNS_PARSER_TYPE_CNAME = function (ab, packet, record) {
2017-02-11 15:51:47 +00:00
// 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));
```
2019-10-20 05:52:59 +00:00
4. Document what you've learned in `doc/<type>.txt`
2017-02-11 15:51:47 +00:00
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.
2019-10-20 05:52:59 +00:00
5. Check that my changes include these files
2017-02-11 15:51:47 +00:00
```
├── README.md
├── demo.html (add the appropriate script tag)
├── doc
| └── cname.txt
├── dns.classes.js (not necessarily, but potentially)
├── dns.types.js
├── package.json (bump the minor version)
2017-02-17 23:22:56 +00:00
├── packer
| └── type.cname.js
├── parser
| └── type.cname.js
2017-02-11 15:51:47 +00:00
└── test
└── fixtures
   ├── www.google.com.cname.bin
   └── www.google.com.cname.js
```