Git Product home page Git Product logo

dnsd's Introduction

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', '[email protected]', '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', '[email protected]', '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.

dnsd's People

Contributors

indexzero avatar jhs avatar krishnaswathi avatar mattjay avatar mmalecki avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dnsd's Issues

Indicate license

Please indicate what license this software is under. This module sounds very nice, but I'm reluctant to use it without knowing its license. "Copyright 2012 Iris Couch, all rights reserved." is not a license; a license is something like the MIT license or the BSD license or the GNU GPL or some custom language prepared by your lawyers.

Run dnsd inside a container

Is there a way to run this service inside a container?

I tried the first example (the one that returns 1.2.3.4 always) with a Dockerfile that exposes port 6464 and then built it and ran it with the command:

> docker run --name dns -d --restart=always --publish 53:6464/tcp --publish 53:6464/udp --volume :/code dns

When I run dig:

> dig @localhost -p 53 foo.example A

I get:

;; connection timed out; no servers could be reached.

I am running MacOSX and I am trying to setup a local DNS server that I can then add into the /etc/resolv.conf as a nameserver.

Thank you

How to return an error?

Hi, I was wondering if my server was unable to respond to the request, how do I issue an error so that the device continues on to the next DNS in list?

AWS Elastic Load Balancing Compatibility

When setting up load balancing in AWS it gives you an A record to add to your DNS which is a domain name since the load balancer might change IP addresses. When trying to add this to dnsd it does not pass the regex for the IP address in the encode.js file.

When trying to mess with this file I can get it to stop crashing but not to actually work and fulfill the DNS request as I'm not generating an Array with a list of each match.

From Amazon

$INSTANCE_NAME.us-east-1.elb.amazonaws.com (A Record)
ipv6.$INSTANCE_NAME.us-east-1.elb.amazonaws.com (AAAA Record)
dualstack.$INSTANCE_NAME.us-east-1.elb.amazonaws.com (A or AAAA Record)

Note: Because the set of IP addresses associated with a LoadBalancer can change over time, you should never create an "A" record with any specific IP address.

defaultable not working?

When i try to do:

var dnsd_easy = require('dnsd')
var dnsd = dnsd_easy.defaults({convenient: false})

y get this error:

var dnsd = dnsd_easy.defaults({convenient: false})
                     ^
TypeError: Object #<Object> has no method 'defaults'

createServer crash on malformed packet

It is possible to crash a DNS server created with .createServer by sending it a bad packet. An exception is thrown by the DNS message parser which cannot be caught using the .createServer API.

The crash can be reproduced by running the following server code:

var dnsd = require('dnsd');

dnsd.createServer(function(req, res) {
  res.end('1.2.3.4');
}).listen(5300);

Once the server is started, send it a non-DNS packet:

netcat -u localhost 5300 <<< test

The server crashes with the following message:

buffer.js:582
    throw new RangeError('Trying to access beyond buffer length');
          ^
RangeError: Trying to access beyond buffer length
    at checkOffset (buffer.js:582:11)
    at Buffer.readUInt16BE (buffer.js:602:5)
    at record_count (/home/tim/test/node_modules/dnsd/parse.js:76:16)
    at Object.sections (/home/tim/test/node_modules/dnsd/parse.js:144:30)
    at Request.DNSMessage.parse (/home/tim/test/node_modules/dnsd/message.js:99:30)
    at Request.DNSMessage (/home/tim/test/node_modules/dnsd/message.js:55:10)
    at new Request (/home/tim/test/node_modules/dnsd/server.js:177:11)
    at Server.on_udp (/home/tim/test/node_modules/dnsd/server.js:167:13)
    at Socket.<anonymous> (/home/tim/test/node_modules/dnsd/server.js:45:54)
    at Socket.emit (events.js:98:17)

Uncaught, unspecified "error" event

Hello,
When running dnsd in production, we occasionally get this error:

events.js:165
throw err;
^

Error: Uncaught, unspecified "error" event. (Error processing request)
at Server.emit (events.js:163:17)
at Server.on_udp (/usr/local/lib/node_modules/dnsd/server.js:177:10)
at Socket.<anonymous> (/usr/local/lib/node_modules/dnsd/server.js:45:54)
at emitTwo (events.js:106:13)
at Socket.emit (events.js:191:7)
at UDP.onMessage (dgram.js:540:8)

It happens about once a day, sometimes more.

Here's a simplified version of our app:

var dnsd = tk.require('dnsd');

var dns_server = dnsd.createServer(
	function(req, res){
		try{
			//var hostname = <insert random app logic>;

			if(question.type=='A'){
				res.answer.push({
					name: hostname,
					type: 'A',
					data: ip,
					ttl: 604800
				});
			}
			res.end();
		}
		catch(e){
			res.end();
		}
	}
)
.zone(
	'ourdomain.com',
	'ns1.ourdomain.com',
	'[email protected]',
	'now',
	'2h',
	'30m',
	'2w',
	'52w'
)
.listen(53);

Is this an issue with dnsd? Or is there something we should be doing differently in our app code?

Thanks so much!

How to return an authority response

Hello,
I am trying to use dnsd to return authority (SOA) record, based on the host in the question.
For example, if, the query is for host in *.sub.domain.com, and local dnsd serves domain.com, I want to redirect the client to another dnsd instance, which serves sub.domain.com.
Tried several ways, like:
type: "SOA",
data:{ 'mname': "ns1.sub.domain.com"
, 'rname': "info.domain.com"
, 'serial': 2018021005
, 'refresh': 10000
, 'retry' : 100
, 'expire' : 100
, 'ttl' : 300
},
or
res.authority.push(["ns1.sub.domain.com","info.domain.com", 2018021005 ,10000, 100, 100, 300]);

In the first case I got a valid response, but the Authority section is empty, in the second I get an error:
Error processing request:TypeError: Cannot read property 'replace' of undefined

Any help will be highly appreciated.
Best,
Kiril.

RangeError

Get an interesting error & I'm not sure what is causing it.
I've included the most recent question I got to the server and the error dump.

IPADDRESS:21018/udp4 {"id":11130,"type":"request","responseCode":0,"opcode":"query","authoritative":false,"truncated":false,"recursion_desired":false,"recursion_available":false,"authenticated":false,"checking_disabled":false,"question":[{"name":"MYDOMAIN.com","type":"A","class":"IN"}]}

buffer.js:579
throw new RangeError('Trying to access beyond buffer length');
^
RangeError: Trying to access beyond buffer length
at checkOffset (buffer.js:579:11)
at Buffer.readUInt8 (buffer.js:585:5)
at domain_parts (/home/MYREPO/node_modules/dnsd/parse.js:274:20)
at add_record (/home/MYREPO/node_modules/dnsd/parse.js:167:16)
at Object.sections (/home/MYREPO/node_modules/dnsd/parse.js:161:7)
at Request.DNSMessage.parse (/home/MYREPO/node_modules/dnsd/message.js:99:30)
at Request.DNSMessage (/home/MYREPO/node_modules/dnsd/message.js:55:10)
at new Request (/home/MYREPO/node_modules/dnsd/server.js:158:11)
at Server.on_udp (/home/MYREPO/node_modules/dnsd/server.js:148:13)
at Socket. (/home/MYREPO/node_modules/dnsd/server.js:45:54)
at Socket.EventEmitter.emit (events.js:98:17)

Any help or suggestions would be greatly appreciated.

Dns server not accessible from another machine

My issue is when i run the dig command, i am able to get the response when i am running it on the machine. But if i run dig command from other machine, the dns server gets the request and generates the response but i still cant get the response on my terminal.

I ran the following command:
dig @localhost tester.inheap.com A

I have checked the logs and the server is able to process the request.
Also the server is allowed to process the remote request. Nginx serves as the proxy server to the dns server running on port 2000. Please find below the netstat output.

tcp 0 0 0.0.0.0:53 0.0.0.0:* LISTEN 10172/nginx
udp 0 0 0.0.0.0:53 0.0.0.0:* 10172/nginx
tcp6 0 0 :::2000 :::* LISTEN 17415/iotdns
udp 0 0 0.0.0.0:2000 0.0.0.0:* 17415/iotdns

Please help me if there is an issue with any config.

How to add aa to flags, setting AUTHORITY to 1

Hello,
When I do a dig command I get this answer:

; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 58836
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1

I'm trying to add the flag "aa" and change authority to 1 . The response should look like:

; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 58836
;; flags: qr rd ra aa; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1

Any tips on how to do that? Couldn't find it in the documentation.
Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.