1# dns-packet
2[![](https://img.shields.io/npm/v/dns-packet.svg?style=flat)](https://www.npmjs.org/package/dns-packet) [![](https://img.shields.io/npm/dm/dns-packet.svg)](https://www.npmjs.org/package/dns-packet) [![](https://api.travis-ci.org/mafintosh/dns-packet.svg?style=flat)](https://travis-ci.org/mafintosh/dns-packet) [![Coverage Status](https://coveralls.io/repos/github/mafintosh/dns-packet/badge.svg?branch=master)](https://coveralls.io/github/mafintosh/dns-packet?branch=master)
3
4An [abstract-encoding](https://github.com/mafintosh/abstract-encoding) compliant module for encoding / decoding DNS packets. Lifted out of [multicast-dns](https://github.com/mafintosh/multicast-dns) as a separate module.
5
6```
7npm install dns-packet
8```
9
10## UDP Usage
11
12``` js
13const dnsPacket = require('dns-packet')
14const dgram = require('dgram')
15
16const socket = dgram.createSocket('udp4')
17
18const buf = dnsPacket.encode({
19  type: 'query',
20  id: 1,
21  flags: dnsPacket.RECURSION_DESIRED,
22  questions: [{
23    type: 'A',
24    name: 'google.com'
25  }]
26})
27
28socket.on('message', message => {
29  console.log(dnsPacket.decode(message)) // prints out a response from google dns
30})
31
32socket.send(buf, 0, buf.length, 53, '8.8.8.8')
33```
34
35Also see [the UDP example](examples/udp.js).
36
37## TCP, TLS, HTTPS
38
39While DNS has traditionally been used over a datagram transport, it is increasingly being carried over TCP for larger responses commonly including DNSSEC responses and TLS or HTTPS for enhanced security. See below examples on how to use `dns-packet` to wrap DNS packets in these protocols:
40
41- [TCP](examples/tcp.js)
42- [DNS over TLS](examples/tls.js)
43- [DNS over HTTPS](examples/doh.js)
44
45## API
46
47#### `var buf = packets.encode(packet, [buf], [offset])`
48
49Encodes a DNS packet into a buffer containing a UDP payload.
50
51#### `var packet = packets.decode(buf, [offset])`
52
53Decode a DNS packet from a buffer containing a UDP payload.
54
55#### `var buf = packets.streamEncode(packet, [buf], [offset])`
56
57Encodes a DNS packet into a buffer containing a TCP payload.
58
59#### `var packet = packets.streamDecode(buf, [offset])`
60
61Decode a DNS packet from a buffer containing a TCP payload.
62
63#### `var len = packets.encodingLength(packet)`
64
65Returns how many bytes are needed to encode the DNS packet
66
67## Packets
68
69Packets look like this
70
71``` js
72{
73  type: 'query|response',
74  id: optionalIdNumber,
75  flags: optionalBitFlags,
76  questions: [...],
77  answers: [...],
78  additionals: [...],
79  authorities: [...]
80}
81```
82
83The bit flags available are
84
85``` js
86packet.RECURSION_DESIRED
87packet.RECURSION_AVAILABLE
88packet.TRUNCATED_RESPONSE
89packet.AUTHORITATIVE_ANSWER
90packet.AUTHENTIC_DATA
91packet.CHECKING_DISABLED
92```
93
94To use more than one flag bitwise-or them together
95
96``` js
97var flags = packet.RECURSION_DESIRED | packet.RECURSION_AVAILABLE
98```
99
100And to check for a flag use bitwise-and
101
102``` js
103var isRecursive = message.flags & packet.RECURSION_DESIRED
104```
105
106A question looks like this
107
108``` js
109{
110  type: 'A', // or SRV, AAAA, etc
111  class: 'IN', // one of IN, CS, CH, HS, ANY. Default: IN
112  name: 'google.com' // which record are you looking for
113}
114```
115
116And an answer, additional, or authority looks like this
117
118``` js
119{
120  type: 'A', // or SRV, AAAA, etc
121  class: 'IN', // one of IN, CS, CH, HS
122  name: 'google.com', // which name is this record for
123  ttl: optionalTimeToLiveInSeconds,
124  (record specific data, see below)
125}
126```
127
128## Supported record types
129
130#### `A`
131
132``` js
133{
134  data: 'IPv4 address' // fx 127.0.0.1
135}
136```
137
138#### `AAAA`
139
140``` js
141{
142  data: 'IPv6 address' // fx fe80::1
143}
144```
145
146#### `CAA`
147
148``` js
149{
150  flags: 128, // octet
151  tag: 'issue|issuewild|iodef',
152  value: 'ca.example.net',
153  issuerCritical: false
154}
155```
156
157#### `CNAME`
158
159``` js
160{
161  data: 'cname.to.another.record'
162}
163```
164
165#### `DNAME`
166
167``` js
168{
169  data: 'dname.to.another.record'
170}
171```
172
173#### `DNSKEY`
174
175``` js
176{
177  flags: 257, // 16 bits
178  algorithm: 1, // octet
179  key: Buffer
180}
181```
182
183#### `DS`
184
185``` js
186{
187  keyTag: 12345,
188  algorithm: 8,
189  digestType: 1,
190  digest: Buffer
191}
192```
193
194#### `HINFO`
195
196``` js
197{
198  data: {
199    cpu: 'cpu info',
200    os: 'os info'
201  }
202}
203```
204
205#### `MX`
206
207``` js
208{
209  preference: 10,
210  exchange: 'mail.example.net'
211}
212```
213
214#### `NS`
215
216``` js
217{
218  data: nameServer
219}
220```
221
222#### `NSEC`
223
224``` js
225{
226  nextDomain: 'a.domain',
227  rrtypes: ['A', 'TXT', 'RRSIG']
228}
229```
230
231#### `NSEC3`
232
233``` js
234{
235  algorithm: 1,
236  flags: 0,
237  iterations: 2,
238  salt: Buffer,
239  nextDomain: Buffer, // Hashed per RFC5155
240  rrtypes: ['A', 'TXT', 'RRSIG']
241}
242```
243
244#### `NULL`
245
246``` js
247{
248  data: Buffer('any binary data')
249}
250```
251
252#### `OPT`
253
254[EDNS0](https://tools.ietf.org/html/rfc6891) options.
255
256``` js
257{
258  type: 'OPT',
259  name: '.',
260  udpPayloadSize: 4096,
261  flags: packet.DNSSEC_OK,
262  options: [{
263    // pass in any code/data for generic EDNS0 options
264    code: 12,
265    data: Buffer.alloc(31)
266  }, {
267    // Several EDNS0 options have enhanced support
268    code: 'PADDING',
269    length: 31,
270  }, {
271    code: 'CLIENT_SUBNET',
272    family: 2, // 1 for IPv4, 2 for IPv6
273    sourcePrefixLength: 64, // used to truncate IP address
274    scopePrefixLength: 0,
275    ip: 'fe80::',
276  }, {
277    code: 'TCP_KEEPALIVE',
278    timeout: 150 // increments of 100ms.  This means 15s.
279  }, {
280    code: 'KEY_TAG',
281    tags: [1, 2, 3],
282  }]
283}
284```
285
286The options `PADDING`, `CLIENT_SUBNET`, `TCP_KEEPALIVE` and `KEY_TAG` support enhanced de/encoding. See [optionscodes.js](https://github.com/mafintosh/dns-packet/blob/master/optioncodes.js) for all supported option codes. If the `data` property is present on a option, it takes precedence. On decoding, `data` will always be defined.
287
288#### `PTR`
289
290``` js
291{
292  data: 'points.to.another.record'
293}
294```
295
296#### `RP`
297
298``` js
299{
300  mbox: 'admin.example.com',
301  txt: 'txt.example.com'
302}
303```
304
305#### `RRSIG`
306
307``` js
308{
309  typeCovered: 'A',
310  algorithm: 8,
311  labels: 1,
312  originalTTL: 3600,
313  expiration: timestamp,
314  inception: timestamp,
315  keyTag: 12345,
316  signersName: 'a.name',
317  signature: Buffer
318}
319```
320
321#### `SOA`
322
323``` js
324{
325  data:
326    {
327      mname: domainName,
328      rname: mailbox,
329      serial: zoneSerial,
330      refresh: refreshInterval,
331      retry: retryInterval,
332      expire: expireInterval,
333      minimum: minimumTTL
334    }
335}
336```
337
338#### `SRV`
339
340``` js
341{
342  data: {
343    port: servicePort,
344    target: serviceHostName,
345    priority: optionalServicePriority,
346    weight: optionalServiceWeight
347  }
348}
349```
350
351#### `TXT`
352
353``` js
354{
355  data: 'text' || Buffer || [ Buffer || 'text' ]
356}
357```
358
359When encoding, scalar values are converted to an array and strings are converted to UTF-8 encoded Buffers. When decoding, the return value will always be an array of Buffer.
360
361If you need another record type, open an issue and we'll try to add it.
362
363## License
364
365MIT
366