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 |
|
4 | An [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 | ```
|
7 | npm install dns-packet
|
8 | ```
|
9 |
|
10 | ## UDP Usage
|
11 |
|
12 | ``` js
|
13 | const dnsPacket = require('dns-packet')
|
14 | const dgram = require('dgram')
|
15 |
|
16 | const socket = dgram.createSocket('udp4')
|
17 |
|
18 | const 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 |
|
28 | socket.on('message', message => {
|
29 | console.log(dnsPacket.decode(message)) // prints out a response from google dns
|
30 | })
|
31 |
|
32 | socket.send(buf, 0, buf.length, 53, '8.8.8.8')
|
33 | ```
|
34 |
|
35 | Also see [the UDP example](examples/udp.js).
|
36 |
|
37 | ## TCP, TLS, HTTPS
|
38 |
|
39 | While 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 |
|
49 | Encodes a DNS packet into a buffer containing a UDP payload.
|
50 |
|
51 | #### `var packet = packets.decode(buf, [offset])`
|
52 |
|
53 | Decode a DNS packet from a buffer containing a UDP payload.
|
54 |
|
55 | #### `var buf = packets.streamEncode(packet, [buf], [offset])`
|
56 |
|
57 | Encodes a DNS packet into a buffer containing a TCP payload.
|
58 |
|
59 | #### `var packet = packets.streamDecode(buf, [offset])`
|
60 |
|
61 | Decode a DNS packet from a buffer containing a TCP payload.
|
62 |
|
63 | #### `var len = packets.encodingLength(packet)`
|
64 |
|
65 | Returns how many bytes are needed to encode the DNS packet
|
66 |
|
67 | ## Packets
|
68 |
|
69 | Packets 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 |
|
83 | The bit flags available are
|
84 |
|
85 | ``` js
|
86 | packet.RECURSION_DESIRED
|
87 | packet.RECURSION_AVAILABLE
|
88 | packet.TRUNCATED_RESPONSE
|
89 | packet.AUTHORITATIVE_ANSWER
|
90 | packet.AUTHENTIC_DATA
|
91 | packet.CHECKING_DISABLED
|
92 | ```
|
93 |
|
94 | To use more than one flag bitwise-or them together
|
95 |
|
96 | ``` js
|
97 | var flags = packet.RECURSION_DESIRED | packet.RECURSION_AVAILABLE
|
98 | ```
|
99 |
|
100 | And to check for a flag use bitwise-and
|
101 |
|
102 | ``` js
|
103 | var isRecursive = message.flags & packet.RECURSION_DESIRED
|
104 | ```
|
105 |
|
106 | A 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 |
|
116 | And 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 |
|
286 | The 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 |
|
359 | When 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 |
|
361 | If you need another record type, open an issue and we'll try to add it.
|
362 |
|
363 | ## License
|
364 |
|
365 | MIT
|