From 2f318607a1ce35695202d8096e228feab22c249a Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Mon, 18 Sep 2017 17:40:11 -0600 Subject: [PATCH] factor out dns query --- bin/dig.js | 301 ++++++++++++++++++++++--------------------------- dns-request.js | 89 +++++++++++++++ 2 files changed, 221 insertions(+), 169 deletions(-) create mode 100644 dns-request.js diff --git a/bin/dig.js b/bin/dig.js index 1c741d9..b17356a 100755 --- a/bin/dig.js +++ b/bin/dig.js @@ -1,10 +1,10 @@ #!/usr/bin/env node 'use strict'; -var dnsjs = require('dns-suite'); +var dig = require('../dns-request'); var cli = require('cli'); -var hexdump = require('../hexdump'); -var crypto = require('crypto'); +var defaultNameservers = require('dns').getServers(); + cli.parse({ // 'b': [ false, 'set source IP address (defaults to 0.0.0.0)', 'string' ] 'class': [ 'c', 'class (defaults to IN)', 'string', 'IN' ] @@ -18,178 +18,14 @@ cli.parse({ , 'timeout': [ false, "How long, in milliseconds, to wait for a response. Alias of +time=", 'int', false ] , 'output': [ 'o', 'output prefix to use for writing query and response(s) to disk', 'file' ] , 'port': [ 'p', 'port (defaults to 53 for dns and 5353 for mdns)', 'int' ] +, 'nameserver': [ false, 'the nameserver to use for DNS resolution (defaults to ' + defaultNameservers.join(',') + ')', 'string' ] //, 'serve': [ 's', 'path to json file with array of responses to issue for given queries', 'string' ] , 'type': [ 't', 'type (defaults to ANY for dns and PTR for mdns)', 'string' ] , 'query': [ 'q', 'a superfluous explicit option to set the query as a command line flag' ] }); -var fs = require('fs'); -var dgram = require('dgram'); var common = require('../common.js'); -var count = 0; - -function request(query, opts) { - var queryAb = dnsjs.DNSPacket.write(query); - - if (opts.debug) { - console.log(''); - console.log('DNS Question:'); - console.log(''); - console.log(query); - console.log(''); - console.log(hexdump(queryAb)); - console.log(''); - console.log(dnsjs.DNSPacket.parse(queryAb)); - console.log(''); - } - - var handlers = {}; - var server = dgram.createSocket({ - type: 'udp4' - , reuseAddr: true - }); - - handlers.onError = function (err) { - console.error("error:", err.stack); - server.close(); - }; - handlers.onMessage = function (nb) { - var packet = dnsjs.DNSPacket.parse(nb.buffer.slice(nb.byteOffset, nb.byteOffset + nb.byteLength)); - var flags = ""; - - if (packet.id !== query.id) { - console.log('ignoring packet for ', packet.question[0].name); - } - - if (!opts.mdns) { - server.close(); - } - - if (opts.debug) { - console.log(''); - console.log('DNS Response:'); - console.log(packet); - } - - console.log(';; Got answer:'); - // TODO opcode 0 QUERY rcode 0 NOERROR - console.log(';; ->>HEADER<<- [opcode: ' + packet.header.opcode + ', status: ' + packet.header.rcode + '], id: ' + packet.header.id); - if (packet.header.tc) { console.log("Truncated [tc] (we don't know the normal way to print a tc packet... you should record this with -o tc-packet.dig and send it to us)"); } - flags += ";; flags:"; - if (packet.header.qr) { flags += " qr"; } - if (packet.header.aa) { flags += " aa"; } - if (packet.header.rd) { flags += " rd"; } - if (packet.header.ra) { flags += " ra"; } - flags += "; QUERY: " + packet.question.length + ", ANSWER: " + packet.answer.length + ", AUTHORITY: " + packet.authority.length + ", ADDITIONAL: " + packet.additional.length; - console.log(flags); - if (packet.header.res1) { console.log("[res1] (we don't know how to print a packet with res1 yet)"); } - if (packet.header.res2) { console.log("[res2] (we don't know how to print a packet with res2 yet)"); } - if (packet.header.res3) { console.log("[res3] (we don't know how to print a packet with res2 yet)"); } - // {"id":32736,"qr":1,"opcode":0,"aa":0,"tc":0,"rd":1,"ra":0,"res1":0,"res2":0,"res3":0,"rcode":5} - //console.log(JSON.stringify(packet.header)); - console.log(''); - console.log(';; QUESTION SECTION:'); - packet.question.forEach(function (q) { - console.log(';' + q.name + '.', ' ', q.className, q.typeName); - }); - function print(q) { - var printer = common.printers[q.typeName] || common.printers.ANY; - printer(q); - } - if (packet.answer.length) { - console.log(''); - console.log(';; ANSWER SECTION:'); - packet.answer.forEach(print); - } - if (packet.authority.length) { - console.log(''); - console.log(';; AUTHORITY SECTION:'); - packet.authority.forEach(print); - } - if (packet.additional.length) { - console.log(''); - console.log(';; ADDITIONAL SECTION:'); - packet.additional.forEach(print); - } - console.log(''); - console.log(';; Query time: ' + (Date.now() - opts._ts) + ' msec'); - // ;; SERVER: 8.8.8.8#53(8.8.8.8) - console.log(';; SERVER: ' + opts._nameserver + '#' + opts.port + '(' + opts._nameserver + ')'); - // TODO ;; WHEN: Fri Sep 15 18:25:53 2017 - console.log(';; WHEN: ' + new Date().toString()); - console.log(';; MSG SIZE rcvd: ' + nb.byteLength); - console.log(''); - - if (opts.output) { - console.log(''); - common.writeQuery(opts, query, queryAb); - common.writeResponse(opts, query, nb, packet); - } - }; - handlers.onListening = function () { - /*jshint validthis:true*/ - var server = this; - var nameserver = opts.nameserver; - var nameservers; - var index; - - if (!nameserver) { - nameservers = require('dns').getServers(); - index = crypto.randomBytes(2).readUInt16BE(0) % nameservers.length; - nameserver = nameservers[index]; - if (opts.debug) { - console.log(index, nameservers); - } - } - - if (opts.mdns || '224.0.0.251' === opts.nameserver) { - server.setBroadcast(true); - server.addMembership(opts.nameserver); - } - - if (opts.debug) { - console.log(''); - console.log('Bound and Listening:'); - console.log(server.address()); - } - - if (opts.debug) { - console.log("querying '" + nameserver + "':'" + opts.port + "'"); - } - server.send(Buffer.from(queryAb), opts.port, nameserver, function () { - opts._nameserver = nameserver; - opts._ts = Date.now(); - if (opts.debug) { - console.log(''); - console.log('request sent'); - } - }); - }; - - console.log(''); - if (!cli.nocmd) { - console.log('; <<>> dig.js ' + 'v0.0.0' + ' <<>> ' + process.argv.slice(2).join(' ')); - console.log(';; global options: +cmd'); - } - server.on('error', handlers.onError); - server.on('message', handlers.onMessage); - server.on('listening', handlers.onListening); - - // 0 dns request - // 53 dns server - // 5353 mdns - if (opts.mdns) { - server.bind(opts.port /*5353*/); - setTimeout(function () { - server.close(); - }, opts.timeout || (5 * 1000)); - } - else { - server.bind(0); - } -} - cli.main(function (args, cli) { args.forEach(function (arg) { if (-1 !== common.types.concat([ 'ANY' ]).indexOf(arg.toUpperCase())) { @@ -298,5 +134,132 @@ cli.main(function (args, cli) { ] }; - request(query, cli); + var dnsjs = require('dns-suite'); + var queryAb = dnsjs.DNSPacket.write(query); + var hexdump = require('../hexdump'); + + if (cli.debug) { + console.log(''); + console.log('DNS Question:'); + console.log(''); + console.log(query); + console.log(''); + console.log(hexdump(queryAb)); + console.log(''); + console.log(dnsjs.DNSPacket.parse(queryAb)); + console.log(''); + } + + cli.onError = function (err) { + console.error("error:", err.stack); + }; + cli.onMessage = function (nb) { + var packet = dnsjs.DNSPacket.parse(nb.buffer.slice(nb.byteOffset, nb.byteOffset + nb.byteLength)); + var flags = ""; + + if (packet.id !== query.id) { + console.log('ignoring packet for ', packet.question[0].name); + } + + if (cli.debug) { + console.log(''); + console.log('DNS Response:'); + console.log(packet); + } + + console.log(';; Got answer:'); + // TODO opcode 0 QUERY rcode 0 NOERROR + console.log(';; ->>HEADER<<- [opcode: ' + packet.header.opcode + ', status: ' + packet.header.rcode + '], id: ' + packet.header.id); + if (packet.header.tc) { console.log("Truncated [tc] (we don't know the normal way to print a tc packet... you should record this with -o tc-packet.dig and send it to us)"); } + flags += ";; flags:"; + if (packet.header.qr) { flags += " qr"; } + if (packet.header.aa) { flags += " aa"; } + if (packet.header.rd) { flags += " rd"; } + if (packet.header.ra) { flags += " ra"; } + flags += "; QUERY: " + packet.question.length + ", ANSWER: " + packet.answer.length + ", AUTHORITY: " + packet.authority.length + ", ADDITIONAL: " + packet.additional.length; + console.log(flags); + if (packet.header.res1) { console.log("[res1] (we don't know how to print a packet with res1 yet)"); } + if (packet.header.res2) { console.log("[res2] (we don't know how to print a packet with res2 yet)"); } + if (packet.header.res3) { console.log("[res3] (we don't know how to print a packet with res2 yet)"); } + // {"id":32736,"qr":1,"opcode":0,"aa":0,"tc":0,"rd":1,"ra":0,"res1":0,"res2":0,"res3":0,"rcode":5} + //console.log(JSON.stringify(packet.header)); + console.log(''); + console.log(';; QUESTION SECTION:'); + packet.question.forEach(function (q) { + console.log(';' + q.name + '.', ' ', q.className, q.typeName); + }); + function print(q) { + var printer = common.printers[q.typeName] || common.printers.ANY; + printer(q); + } + if (packet.answer.length) { + console.log(''); + console.log(';; ANSWER SECTION:'); + packet.answer.forEach(print); + } + if (packet.authority.length) { + console.log(''); + console.log(';; AUTHORITY SECTION:'); + packet.authority.forEach(print); + } + if (packet.additional.length) { + console.log(''); + console.log(';; ADDITIONAL SECTION:'); + packet.additional.forEach(print); + } + console.log(''); + console.log(';; Query time: ' + (Date.now() - cli._ts) + ' msec'); + // ;; SERVER: 8.8.8.8#53(8.8.8.8) + console.log(';; SERVER: ' + cli._nameserver + '#' + cli.port + '(' + cli._nameserver + ')'); + // TODO ;; WHEN: Fri Sep 15 18:25:53 2017 + console.log(';; WHEN: ' + new Date().toString()); + console.log(';; MSG SIZE rcvd: ' + nb.byteLength); + console.log(''); + + if (cli.output) { + console.log(''); + common.writeQuery(cli, query, queryAb); + common.writeResponse(cli, query, nb, packet); + } + }; + cli.onListening = function () { + /*jshint validthis:true*/ + var server = this; + + if (cli.debug) { + console.log(''); + console.log('Bound and Listening:'); + console.log(server.address()); + } + + // technicially this should be a seperate event + if (cli.debug) { + console.log("querying '" + server.nameserver + "':'" + cli.port + "'"); + } + }; + + console.log(''); + if (!cli.nocmd) { + console.log('; <<>> dig.js ' + 'v0.0.0' + ' <<>> ' + process.argv.slice(2).join(' ')); + console.log(';; global options: +cmd'); + } + + var opts = { + onError: cli.onError + , onMessage: cli.onMessage + , onListening: cli.onListening + , onSent: function (res) { + cli._nameserver = res.nameserver; + cli._ts = Date.now(); + if (cli.debug) { + console.log(''); + console.log('request sent to', res.nameserver); + } + } + , mdns: cli.mdns + , nameserver: cli.nameserver + , port: cli.port + , timeout: cli.timeout + }; + dig.request(queryAb, opts); }); diff --git a/dns-request.js b/dns-request.js new file mode 100644 index 0000000..878ab5e --- /dev/null +++ b/dns-request.js @@ -0,0 +1,89 @@ +'use strict'; + +var dnsjs = require('dns-suite'); +var crypto = require('crypto'); +var dgram = require('dgram'); + +function request(queryAb, opts) { + var handlers = {}; + var server = dgram.createSocket({ + type: 'udp4' + , reuseAddr: true + }); + + handlers.onError = function (err) { + if (opts.onError) { opts.onError(err); } + server.close(); + }; + handlers.onMessage = function (bin) { + if (!opts.mdns) { + server.close(); + } + + if (opts.onMessage) { opts.onMessage(bin); } + }; + handlers.onListening = function () { + /*jshint validthis:true*/ + var server = this; + var nameservers; + var index; + server.nameserver = opts.nameserver; + + if (!server.nameserver) { + nameservers = require('dns').getServers(); + index = crypto.randomBytes(2).readUInt16BE(0) % nameservers.length; + server.nameserver = nameservers[index]; + } + + if (opts.mdns || '224.0.0.251' === opts.nameserver) { + server.setBroadcast(true); + server.addMembership(opts.nameserver); + } + + if (opts.onListening) { opts.onListening.apply(server); } + + server.send(Buffer.from(queryAb), opts.port, server.nameserver, function () { + if (opts.onSent) { opts.onSent({ port: opts.port, nameserver: server.nameserver }); } + }); + }; + + server.on('error', handlers.onError); + server.on('message', handlers.onMessage); + server.on('listening', handlers.onListening); + + // 0 dns request + // 53 dns server + // 5353 mdns + if (opts.mdns) { + server.bind(opts.port /*5353*/); + setTimeout(function () { + server.close(); + }, opts.timeout || (5 * 1000)); + } + else { + server.bind(0); + } +} + +function requestJson(query, opts) { + var queryAb = dnsjs.DNSPacket.write(query); + var options = { + onError: opts.onError + , onMessage: function (nb) { + var packet = dnsjs.DNSPacket.parse(nb.buffer.slice(nb.byteOffset, nb.byteOffset + nb.byteLength)); + + opts.onMessage(packet); + } + , onListening: opts.onListening + , onSent: opts.onSent + , mdns: opts.mdns + , nameserver: opts.nameserver + , port: opts.port + , timeout: opts.timeout + }; + + return request(queryAb, options); +} + +module.exports.request = request; +module.exports.requestJson = requestJson;