dnsd: DNS encoder, decoder, and server
dnsd is a Node.js package for working with DNS. It converts binary DNS messages to and from convenient JavaScript objects; and it provides a server API, for running a custom name server.
dnsd is available as an npm module.
$ npm install dnsd
Example: Running a server
This simple DNS server responds with an "A" (address) record of 1.2.3.4
for every request.
var dnsd = dnsdconsole
Now test your server:
$ dig @localhost -p 5353 foo.example A
; <<>> DiG 9.8.1-P1 <<>> @localhost -p 5353 foo.example A
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 27955
;; flags: qr rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available
;; QUESTION SECTION:
;foo.example. IN A
;; ANSWER SECTION:
foo.example. 3600 IN A 1.2.3.4
;; Query time: 1 msec
;; SERVER: 127.0.0.1#5353(127.0.0.1)
;; WHEN: Wed Aug 8 05:10:40 2012
;; MSG SIZE rcvd: 45
This example logs all requests. For address (A) queries, it returns two records, with a random TTL, and the final octet of the IP address is the length of the hostname queried.
var dnsd = var server = dnsdserver console { console var question = resquestion0 hostname = questionname length = hostnamelength ttl = Math ifquestiontype == 'A' resanswer resanswer res}
Test the SOA response:
$ dig @localhost -p 5353 example.com soa
; <<>> DiG 9.8.1-P1 <<>> @localhost -p 5353 example.com soa
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 30176
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available
;; QUESTION SECTION:
;example.com. IN SOA
;; ANSWER SECTION:
example.com. 600 IN SOA ns1.example.com. us.example.com. 1344403648 7200 1800 1209600 600
;; Query time: 5 msec
;; SERVER: 127.0.0.1#5353(127.0.0.1)
;; WHEN: Wed Aug 8 05:27:32 2012
;; MSG SIZE rcvd: 72
And test the address (A) response:
$ dig @localhost -p 5353 example.com a
; <<>> DiG 9.8.1-P1 <<>> @localhost -p 5353 example.com a
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19419
;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available
;; QUESTION SECTION:
;example.com. IN A
;; ANSWER SECTION:
example.com. 1222 IN A 1.1.1.11
example.com. 1222 IN A 2.2.2.11
;; Query time: 1 msec
;; SERVER: 127.0.0.1#5353(127.0.0.1)
;; WHEN: Wed Aug 8 05:27:34 2012
;; MSG SIZE rcvd: 61
Server output for these queries:
Server running at 127.0.0.1:5353
127.0.0.1:34427/udp4 {"id":30176,"type":"request","responseCode":0,"opcode":"query","authoritative":false,"truncated":false,"recursion_desired":true,"recursion_available":false,"authenticated":false,"checking_disabled":false,"question":[{"name":"example.com","type":"SOA","class":"IN"}]}
127.0.0.1:59596/udp4 {"id":19419,"type":"request","responseCode":0,"opcode":"query","authoritative":false,"truncated":false,"recursion_desired":true,"recursion_available":false,"authenticated":false,"checking_disabled":false,"question":[{"name":"example.com","type":"A","class":"IN"}]}
Example: MX Records
This is an example if you need to route your mail server with an MX record.
// Example MX response with dnsd//// To test:// 1. Run this program// 2. dig @localhost -p 5353 example.com mx var dnsd = var server = dnsdserverserverconsole { var question = resquestion && resquestion0 ifquestiontype != 'MX' return res console resanswer resanswer return res}
The MX data attribute needs to be an Array to work properly, the first value is the priority, the second is the server. This server name must be a domain string and not an IP address. Make sure you have an A record or CNAME setup for this.
See http://support.google.com/a/bin/answer.py?hl=en&answer=140034 for more info on MX records and configuration.
Example: Parse a message
var fs = var dnsd = var msg_file = require msg_data = fs message = dnsd consoledirmessage
Output
id: 34233 type: 'response' responseCode: 0 opcode: 'query' authoritative: false truncated: false recursion_desired: true recursion_available: true authenticated: false checking_disabled: false question: name: 'registry.npmjs.org' type: 'A' class: 'IN' answer: name: 'registry.npmjs.org' type: 'CNAME' class: 'IN' ttl: 85 data: 'isaacs.iriscouch.net' name: 'isaacs.iriscouch.net' type: 'CNAME' class: 'IN' ttl: 2821 data: 'ec2-23-23-147-24.compute-1.amazonaws.com' name: 'ec2-23-23-147-24.compute-1.amazonaws.com' type: 'A' class: 'IN' ttl: 356336 data: '23.23.147.24'
Example: Encode a message
var dnsd = var questions = name:'example.com' class:'IN' type:'TXT' message = type:'query' id:123 opcode:'query' recursion_desired:true question:questions msg_data = dnsd console message = dnsd consoleconsoledirmessage
Output:
Encoded = 01231001000000710112097109112108101399111109001601Round trip: id: 123 type: 'request' responseCode: 0 opcode: 'query' authoritative: false truncated: false recursion_desired: true recursion_available: false authenticated: false checking_disabled: false question: name: 'example.com' type: 'TXT' class: 'IN'
Defaults
dnsd
is defaultable. The option convenient
(true
by default) adds convenience code when running a server. Convenience mode adds several features, mostly to build standards-compliant name servers.
var dnsd_easy = var dnsd_hard = dnsd_easy
First, your handler's response object already has .type = "response"
set; then there are many helpers processing your response:
- You can pass a value to
res.end()
, with special handling depending on type:- Array: those values will be added to the
res.answer
section. - Object: that object will be sent as a response (
res
is unused). - String: the response will add an anser
A
record with your value as the IP address.
- Array: those values will be added to the
- Automatically respond to
SOA
queries with theSOA
record. - Responses to an
A
query with no answers will add theSOA
record to the response. - If the response records are missing a TTL, use the one from the
.zone()
definition (theSOA
record)
Without convenience mode, dnsd will simply send your response verbatim, as you define it (or throw an encoding error for missing or bad data).
Tests
Follow uses node-tap. If you clone this Git repository, tap is included.
$ tap test
ok test/api.js ........................................ 10/10
ok test/convenience.js ................................ 22/22
ok test/message.js .................................. 176/176
ok test/print.js ...................................... 10/10
ok test/server.js ..................................... 35/35
total ............................................... 253/253
ok
License
Apache 2.0
See the Apache 2.0 license.