dnsd

Dynamic authoritative name server

  • 所有者: iriscouch/dnsd
  • 平台:
  • 许可证: Apache License 2.0
  • 分类:
  • 主题:
  • 喜欢:
    1
      比较:

Github星跟踪图

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 = require('dnsd')
dnsd.createServer(function(req, res) {
  res.end('1.2.3.4')
}).listen(5353, '127.0.0.1')
console.log('Server running at 127.0.0.1:5353')

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 = require('dnsd')

var server = dnsd.createServer(handler)
server.zone('example.com', 'ns1.example.com', 'us@example.com', 'now', '2h', '30m', '2w', '10m')
      .listen(5353, '127.0.0.1')
console.log('Server running at 127.0.0.1:5353')

function handler(req, res) {
  console.log('%s:%s/%s %j', req.connection.remoteAddress, req.connection.remotePort, req.connection.type, req)

  var question = res.question[0]
    , hostname = question.name
    , length = hostname.length
    , ttl = Math.floor(Math.random() * 3600)

  if(question.type == 'A') {
    res.answer.push({name:hostname, type:'A', data:"1.1.1."+length, 'ttl':ttl})
    res.answer.push({name:hostname, type:'A', data:"2.2.2."+length, 'ttl':ttl})
  }
  res.end()
}

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 = require('dnsd')
 
var server = dnsd.createServer(handler)
server.zone('example.com', 'ns1.example.com', 'us@example.com', 'now', '2h', '30m', '2w', '10m')
server.listen(5353, '127.0.0.1')
console.log('Listening at 127.0.0.1:5353')
 
function handler(req, res) {
  var question = res.question && res.question[0]
 
  if(question.type != 'MX')
    return res.end()
 
  console.log('MX lookup for domain: %s', question.name)
  res.answer.push({'name':question.name, 'type':'MX', 'data':[10, 'mail.example.com']})
  res.answer.push({'name':question.name, 'type':'MX', 'data':[20, 'mail.backupexample.com']})
  
  return res.end()
}

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 = require('fs')
var dnsd = require('dnsd')

var msg_file = require.resolve('dnsd/_test_data/registry.npmjs.org-response')
  , msg_data = fs.readFileSync(msg_file)
  , message = dnsd.parse(msg_data)

console.dir(message)

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 = require('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.binify(message)

console.log('Encoded = %j', Array.prototype.slice.apply(msg_data))

message = dnsd.parse(msg_data)

console.log('Round trip:')
console.dir(message)

Output:

Encoded = [0,123,1,0,0,1,0,0,0,0,0,0,7,101,120,97,109,112,108,101,3,99,111,109,0,0,16,0,1]
Round 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 = require('dnsd')
var dnsd_hard = dnsd_easy.defaults({convenient: false})

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.
  • Automatically respond to SOA queries with the SOA record.
  • Responses to an A query with no answers will add the SOA record to the response.
  • If the response records are missing a TTL, use the one from the .zone() definition (the SOA 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.

主要指标

概览
名称与所有者iriscouch/dnsd
主编程语言JavaScript
编程语言JavaScript (语言数: 1)
平台
许可证Apache License 2.0
所有者活动
创建于2012-07-27 00:26:47
推送于2022-08-15 22:38:53
最后一次提交2014-08-28 02:47:14
发布数5
最新版本名称v0.9.4 (发布于 2013-09-10 09:07:07)
第一版名称v0.9.0 (发布于 2013-01-31 02:35:26)
用户参与
星数268
关注者数10
派生数67
提交数95
已启用问题?
问题数14
打开的问题数13
拉请求数4
打开的拉请求数4
关闭的拉请求数1
项目设置
已启用Wiki?
已存档?
是复刻?
已锁定?
是镜像?
是私有?