Skip to content

Instantly share code, notes, and snippets.

@jpenalbae
Created February 6, 2015 13:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save jpenalbae/80b697dc25b5ec4ac8dd to your computer and use it in GitHub Desktop.
Save jpenalbae/80b697dc25b5ec4ac8dd to your computer and use it in GitHub Desktop.
Quick PoC extending node.js dns module to add AXFR request support
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;
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