Created
February 6, 2015 13:34
-
-
Save jpenalbae/80b697dc25b5ec4ac8dd to your computer and use it in GitHub Desktop.
Quick PoC extending node.js dns module to add AXFR request support
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var dns = require('dns'); | |
var net = require('net'); | |
var axfrReqProloge = | |
"\x00\x00" + /* Size */ | |
"\x00\x00" + /* Transaction ID */ | |
"\x00\x20" + /* Flags: Standard Query */ | |
"\x00\x01" + /* Number of questions */ | |
"\x00\x00" + /* Number of answers */ | |
"\x00\x00" + /* Number of Authority RRs */ | |
"\x00\x00"; /* Number of Aditional RRs */ | |
var axfrReqEpiloge = | |
"\x00" + /* End of name */ | |
"\x00\xfc" + /* Type: AXFR */ | |
"\x00\x01"; /* Class: IN */ | |
function inet_ntoa(num){ | |
var nbuffer = new ArrayBuffer(4); | |
var ndv = new DataView(nbuffer); | |
ndv.setUint32(0, num); | |
var a = new Array(); | |
for(var i = 0; i < 4; i++){ | |
a[i] = ndv.getUint8(i); | |
} | |
return a.join('.'); | |
} | |
function decompressLabel(data, offset) { | |
var res = { len: 0, name: '' }; | |
var loffset = offset; | |
var tmpoff = 0; | |
while (data[loffset] != 0x00) { | |
/* Check for pointers */ | |
if ((data[loffset] & 0xc0) == 0xc0) { | |
var newoffset = data.readUInt16BE(loffset) & 0x3FFF; | |
var label = decompressLabel(data, (newoffset + 2)); | |
res.name += label.name; | |
loffset += 1; | |
break; | |
/* Normal label */ | |
} else { | |
tmpoff = loffset + 1; | |
res.name += data.toString('utf8', tmpoff, (tmpoff + data[loffset])); | |
res.name += '.'; | |
loffset += data[loffset] + 1; | |
} | |
} | |
res.next = loffset + 1; | |
//console.log(res.name); | |
return res; | |
} | |
function parseResponse(response) { | |
var offset = 14; | |
var entry, tentry, tchild = {}; | |
var table = []; | |
var rclass, ttl, rlen, ip, pref, txt; | |
var len = response.readUInt16BE(0); | |
var result = { questions: [], answers: [] }; | |
/* Check for valid length */ | |
if (response.length != (len + 2) ) { | |
console.log('len error'); | |
return -1; | |
} | |
/* Check for query response packet */ | |
if ((response[4] & 0x80) != 0x80) { | |
console.log('res error'); | |
return -2; | |
} | |
/* Check for error code */ | |
if ((response[5] & 0x0F) != 0) { | |
console.log('code error'); | |
return -3; | |
} | |
var questions = response.readUInt16BE(6); | |
var answers = response.readUInt16BE(8); | |
var authRRs = response.readUInt16BE(10); | |
var aditRRs = response.readUInt16BE(12); | |
/* | |
console.log('questions: ' + questions); | |
console.log('answers: ' + answers); | |
console.log('authRRs: ' + authRRs); | |
console.log('aditRRs: ' + aditRRs); */ | |
/* Parse queries */ | |
for (var x = 0; x < questions; x++) { | |
entry = decompressLabel(response, offset); | |
//console.log('entry: ' + entry.name); | |
result.questions.push({ | |
name: entry.name, | |
type: 'AXFR' | |
}); | |
/* Skip type and class (4 bytes) */ | |
offset = entry.next + 4; | |
}; | |
/* Parse answers */ | |
for (var x = 0; x < answers; x++) { | |
entry = decompressLabel(response, offset); | |
//console.log('[+] entry: ' + entry.name); | |
offset = entry.next; | |
tentry = tchild = {}; | |
tentry.name = entry.name; | |
//console.log('offset2: ' + offset); | |
tentry.type = response.readUInt16BE(offset); | |
rclass = response.readUInt16BE(offset+2); | |
ttl = response.readUInt32BE(offset+4); | |
rlen = response.readUInt16BE(offset+8); | |
//console.log('tentry.len: ' + tentry.rlen); | |
/* Skip classes != INET */ | |
if (rclass != 0x01) { | |
offset += rlen + 10; | |
continue; | |
} | |
/* Parse answer rdata */ | |
switch (tentry.type) { | |
/* A Record */ | |
case 0x01: | |
tentry.type = 'A'; | |
tentry.a = inet_ntoa(response.readUInt32BE(offset+10)); | |
//console.log(' - A: ' + inet_ntoa(ip)); | |
break; | |
/* NS Record */ | |
case 0x02: | |
tentry.type = 'NS'; | |
tentry.ns = decompressLabel(response, (offset + 10)).name; | |
//console.log(' - NS: ' + entry.name); | |
break; | |
/* CNAME Record */ | |
case 0x05: | |
tentry.type = 'CNAME'; | |
tentry.cname = decompressLabel(response, (offset + 10)).name; | |
//console.log(' - CNAME: ' + entry.name); | |
break; | |
/* SOA Record */ | |
case 0x06: | |
tentry.type = 'SOA'; | |
tchild = decompressLabel(response, (offset + 10)); | |
tentry.dns = tchild.name; | |
tchild = decompressLabel(response, (tchild.next)); | |
tentry.mail = tchild.name; | |
tentry.serial = response.readUInt32BE(tchild.next); | |
tentry.refresInterval = response.readUInt32BE(tchild.next+4); | |
tentry.retryInterval = response.readUInt32BE(tchild.next+8); | |
tentry.expireLimit = response.readUInt32BE(tchild.next+12); | |
tentry.minTTL = response.readUInt32BE(tchild.next+16); | |
break; | |
/* MX Record */ | |
case 0x0f: | |
tentry.type = 'MX'; | |
tentry.pref = response.readUInt16BE(offset+10); | |
tentry.mx = decompressLabel(response, (offset + 12)).name; | |
//console.log(' - MX ' + pref + ': ' + entry.name); | |
break; | |
/* TXT Record */ | |
case 0x10: | |
tentry.type = 'TXT'; | |
len = response[offset+10]; | |
tentry.txt = response.toString('utf8', offset+11, offset+11+len); | |
//console.log(' - TXT: ' + txt); | |
break; | |
} | |
result.answers.push(tentry); | |
offset += rlen + 10; | |
//console.log('new offset: ' + offset); | |
}; | |
//console.log('offset: ' + offset); | |
//var temp = response.slice(offset, offset + 8); | |
//console.log(util.inspect(temp)); | |
//console.log(util.inspect(result)); | |
//console.log('ok'); | |
return result; | |
} | |
dns.resolveAxfr = function(server, domain, callback) { | |
var buffers = []; | |
var split = domain.split('.'); | |
buffers.push(new Buffer(axfrReqProloge, 'binary')); | |
split.forEach(function(elem) { | |
var label = new Buffer('\00' + elem, 'utf8'); | |
label.writeUInt8(elem.length, 0); | |
buffers.push(label); | |
}); | |
buffers.push(new Buffer(axfrReqEpiloge, 'binary')); | |
var buffer = Buffer.concat(buffers); | |
/* Set size and transaction ID */ | |
buffer.writeUInt16BE(buffer.length - 2, 0); | |
buffer.writeUInt16BE(Math.floor((Math.random() * 65535) + 1) , 2); | |
var socket = net.connect(53, server, function(arguments) { | |
//console.log("Connected") | |
socket.write(buffer.toString('binary'), 'binary'); | |
}); | |
socket.on('data', function(data) { | |
//console.log(util.inspect(data)); | |
//console.log(data.toString()); | |
var res = parseResponse(data); | |
socket.end(); | |
if (typeof res === 'object') | |
callback(0, res); | |
else | |
callback(res, "Error"); | |
}); | |
/* | |
socket.on('end', function() { | |
console.log("Connection closed"); | |
}); */ | |
//console.log(util.inspect(buffer)); | |
//console.log(util.inspect(buffers)); | |
}; | |
module.exports = dns; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var dns = require('./index'); | |
var util = require('util'); | |
dns.resolveAxfr('dns01.domain.com' ,'domain.com', function(err, addrs) { | |
console.log(util.inspect(addrs)); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment