Skip to content
Snippets Groups Projects
Commit 48a21fc6 authored by Deucе's avatar Deucе :ok_hand_tone4:
Browse files

Add DNS_blocking sub-class for non-event driven lookups

The DNS_blocking class supports the same API as the DNS class, but
does not use events, so any method will complete before returning.
As such, the callback argument is optional.
parent e25619bc
No related branches found
No related tags found
1 merge request!463MRC mods by Codefenix (2024-10-20)
/* DNS protocol library
* Uses events, so the calling script *MUST* set js.do_callbacks
* The DNS class Uses events, so the calling script *MUST* set js.do_callbacks
* Example usage:
*
* js.do_callbacks = true;
......@@ -11,327 +11,82 @@
* exit(0);
* }
*
* dns = new DNS();
* dns.resolve('example.com', handle);
*
* The DNS_blocking does not use events, so callback arguments are optional:
*
* load('dns.js');
* dns = new DNS_blocking();
* log(LOG_ERROR, dns.resolve('example.com').toSource());
*
*/
require('sockdefs.js', 'SOCK_DGRAM');
function DNS(servers) {
if (servers === undefined)
servers = system.name_servers;
var nextid = 0;
var outstanding = {};
this.sockets = [];
handle_response = function() {
var resp;
var id;
var offset = 0;
var queries;
var answers;
var nameservers;
var arecords;
var q;
var i;
var ret = {'queries':[], 'answers':[], 'nameservers':[], 'additional':[]};
var rdata;
var rdlen;
var tmp;
string_to_int16 = function(str) {
return ((ascii(str[0])<<8) | (ascii(str[1])));
}
string_to_int32 = function(str) {
return ((ascii(str[0])<<24) | (ascii(str[1]) << 16) | (ascii(str[1]) << 8) | (ascii(str[1])));
}
function get_string(resp, offset) {
var len = ascii(resp[offset]);
return {'len':len + 1, 'string':resp.substr(offset + 1, len)};
}
function get_name(resp, offset) {
var len;
var name = '';
var ret = 0;
var compressed = false;
do {
len = ascii(resp[offset]);
if (!compressed)
ret++;
offset++;
if (len > 63) {
offset = ((len & 0x3f) << 8) | ascii(resp[offset]);
if (!compressed)
ret++;
compressed = true;
}
else {
if (!compressed)
ret += len;
if (name.length > 0 && len > 0)
name += '.';
name += resp.substr(offset, len);
offset += len;
}
} while (len != 0);
return {'len':ret, 'name':name};
}
function parse_rdata(type, resp, offset, len) {
var tmp;
var tmp2;
var tmp3;
var tmp4;
switch(type) {
case 1: // A
return ascii(resp[offset]) + '.' +
ascii(resp[offset + 1]) + '.' +
ascii(resp[offset + 2]) + '.' +
ascii(resp[offset + 3]);
case 2: // NS
return get_name(resp, offset).name;
case 5: // CNAME
return get_name(resp, offset).name;
case 6: // SOA
tmp = {};
tmp2 = 0;
tmp3 = get_name(resp, offset + tmp2);
tmp.mname = tmp3.name;
tmp2 += tmp3.len;
tmp3 = get_name(resp, offset + tmp2);
tmp.rname = tmp3.name;
tmp2 += tmp3.len;
tmp.serial = string_to_int32(resp.substr(offset + tmp2, 4));
tmp2 += 4;
tmp.refresh = string_to_int32(resp.substr(offset + tmp2, 4));
tmp2 += 4;
tmp.retry = string_to_int32(resp.substr(offset + tmp2, 4));
tmp2 += 4;
tmp.export = string_to_int32(resp.substr(offset + tmp2, 4));
tmp2 += 4;
tmp.minimum = string_to_int32(resp.substr(offset + tmp2, 4));
tmp2 += 4;
return tmp;
case 11: // WKS
tmp = {};
tmp.address = ascii(resp[offset]) + '.' +
ascii(resp[offset + 1]) + '.' +
ascii(resp[offset + 2]) + '.' +
ascii(resp[offset + 3]);
tmp.protocol = ascii(resp[offset + 4]);
tmp2 = 5;
tmp.ports = [];
while (tmp2 < len) {
tmp3 = ascii(resp[offset + tmp2]);
for (tmp4 = 0; tmp4 < 8; tmp4++) {
if (tmp3 & (1 << tmp4))
tmp.ports.push(8 * (tmp2 - 5) + tmp3);
}
}
return tmp;
case 12: // PTR
return get_name(resp, offset).name;
case 13: // HINFO
tmp = get_string(resp, offset);
return {'cpu':tmp.string, 'os':get_string(resp, offset + tmp.len).string};
case 15: // MX
tmp = {};
tmp.preference = string_to_int16(resp.substr(offset, 2));
tmp.exchange = get_name(resp, offset + 2).name;
return tmp;
case 16: // TXT
tmp = [];
tmp2 = 0;
do {
tmp3 = get_string(resp, offset + tmp2);
tmp.push(tmp3.string);
tmp2 += tmp3.len;
} while (tmp2 < len);
return tmp;
case 28: // AAAA
return format("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
ascii(resp[offset + 0]), ascii(resp[offset + 1]),
ascii(resp[offset + 2]), ascii(resp[offset + 3]),
ascii(resp[offset + 4]), ascii(resp[offset + 5]),
ascii(resp[offset + 6]), ascii(resp[offset + 7]),
ascii(resp[offset + 8]), ascii(resp[offset + 9]),
ascii(resp[offset + 10]), ascii(resp[offset + 11]),
ascii(resp[offset + 12]), ascii(resp[offset + 13]),
ascii(resp[offset + 14]), ascii(resp[offset + 15])
).replace(/(0000:)+/, ':').replace(/(^|:)0{1,3}/g, '$1');
case 33: // SRV
tmp = {};
tmp.priority = string_to_int16(resp.substr(offset, 2));
offset += 2;
tmp.weight = string_to_int16(resp.substr(offset, 2));
offset += 2;
tmp.port = string_to_int16(resp.substr(offset, 2));
offset += 2;
tmp.target = get_name(resp, offset).name;
return tmp;
case 35: // NAPTR
tmp = {};
tmp.order = string_to_int16(resp.substr(offset, 2));
offset += 2;
tmp.preference = string_to_int16(resp.substr(offset, 2));
offset += 2;
tmp2 = get_string(resp, offset);
tmp.flags = tmp2.string;
offset += tmp2.len;
tmp2 = get_string(resp, offset);
tmp.services = tmp2.string;
offset += tmp2.len;
tmp2 = get_string(resp, offset);
tmp.regexp = tmp2.string;
offset += tmp2.len;
tmp.replacement = get_name(resp, offset).name;
return tmp;
case 256: // URI
tmp = {};
tmp.priority = string_to_int16(resp.substr(offset, 2));
offset += 2;
tmp.weight = string_to_int16(resp.substr(offset, 2));
offset += 2;
tmp.target = get_string(resp, offset).string;
tmp.target = tmp.target.replace(/\\"/g, '"');
tmp.target = tmp.target.replace(/^"(.*)"$/, '$1');
return tmp;
case 3: // MD
case 4: // MF
case 7: // MB
case 8: // MG
case 9: // MR
case 10: // NULL
case 14: // MINFO
return {'raw':resp.substr(offset, len)};
}
}
resp = this.recv(10000);
id = string_to_int16(resp);
if (this.outstanding[id] === undefined)
return false;
q = this.outstanding[id];
delete this.outstanding[id];
ret.id = id;
ret.response = !!(ascii(resp[2]) & 1);
ret.opcode = (ascii(resp[2]) & 0x1e) >> 1;
ret.authoritative = !!(ascii(resp[2]) & (1<<5));
ret.truncation = !!(ascii(resp[2]) & (1<<6));
ret.recusrion = !!(ascii(resp[2]) & (1<<7));
ret.reserved = ascii(resp[3]) & 7;
ret.rcode = ascii(resp[3] & 0xf1) >> 3;
queries = string_to_int16(resp.substr(4, 2));
answers = string_to_int16(resp.substr(6, 2));
nameservers = string_to_int16(resp.substr(8, 2));
arecords = string_to_int16(resp.substr(10, 2));
if (answers === 0)
return false;
js.clearTimeout(q.timeoutid);
offset = 12;
for (i = 0; i < queries; i++) {
rdata = {};
tmp = get_name(resp, offset);
rdata.name = tmp.name;
offset += tmp.len;
rdata.type = string_to_int16(resp.substr(offset, 2));
offset += 2;
rdata.class = string_to_int16(resp.substr(offset, 2));
offset += 2;
ret.queries.push(rdata);
}
for (i = 0; i < answers; i++) {
rdata = {};
tmp = get_name(resp, offset);
rdata.name = tmp.name;
offset += tmp.len;
rdata.type = string_to_int16(resp.substr(offset, 2));
offset += 2;
rdata.class = string_to_int16(resp.substr(offset, 2));
offset += 2;
rdata.ttl = string_to_int32(resp.substr(offset, 4));
offset += 4;
rdlen = string_to_int16(resp.substr(offset, 2));
offset += 2;
rdata.rdata = parse_rdata(rdata.type, resp, offset, rdlen);
offset += rdlen;
ret.answers.push(rdata);
}
var synchronous = false;
for (i = 0; i < nameservers; i++) {
rdata = {};
tmp = get_name(resp, offset);
rdata.name = tmp.name;
offset += tmp.len;
rdata.type = string_to_int16(resp.substr(offset, 2));
offset += 2;
rdata.class = string_to_int16(resp.substr(offset, 2));
offset += 2;
rdata.ttl = string_to_int32(resp.substr(offset, 4));
offset += 4;
rdlen = string_to_int16(resp.substr(offset, 2));
offset += 2;
rdata.rdata = parse_rdata(rdata.type, resp, offset, rdlen);
offset += rdlen;
ret.nameservers.push(rdata);
}
if (this.synchronous === undefined) {
Object.defineProperty(this, 'synchronous', {get: function() {
return synchronous;
}});
}
this.outstanding = {};
this.sockets = [];
for (i = 0; i < arecords; i++) {
rdata = {};
tmp = get_name(resp, offset);
rdata.name = tmp.name;
offset += tmp.len;
rdata.type = string_to_int16(resp.substr(offset, 2));
offset += 2;
rdata.class = string_to_int16(resp.substr(offset, 2));
offset += 2;
rdata.ttl = string_to_int32(resp.substr(offset, 4));
offset += 4;
rdlen = string_to_int16(resp.substr(offset, 2));
offset += 2;
rdata.rdata = parse_rdata(rdata.type, resp, offset, rdlen);
offset += rdlen;
ret.additional.push(rdata);
}
function handle_response() {
var resp = this.dnsObject.handle_response(this);
q.callback.call(q.thisObj, ret);
return true;
if (resp)
resp.q.callback.call(resp.q.thisObj, resp.ret);
}
if (servers === undefined)
servers = system.name_servers;
servers.forEach(function(server) {
var sock = new Socket(SOCK_DGRAM, "dns", server.indexOf(':') >= 0);
sock.bind();
sock.connect(server, 53);
sock.on('read', handle_response);
sock.outstanding = outstanding;
sock.cbID = sock.on('read', handle_response);
sock.dnsObject = this;
this.sockets.push(sock);
}, this);
if (this.sockets.length < 1)
throw('Unable to create any sockets');
increment_id = function() {
this.increment_id = function() {
var ret = nextid;
do {
nextid++;
if (nextid > 65535)
nextid = 0;
} while (outstanding[nextid] !== undefined);
} while (this.outstanding[nextid] !== undefined);
return ret;
}
};
}
function DNS_blocking(servers) {
var synchronous = false;
Object.defineProperty(this, 'synchronous', {get: function() {
return synchronous;
}});
DNS.call(this, servers);
// Disable callbacks
this.sockets.forEach(function(sock) {
sock.clearOn('read', sock.cbID);
});
}
DNS_blocking.prototype = Object.create(DNS.prototype);
/*
* Class properties and methods
*/
DNS.types = {
'A':1,
'NS':2,
......@@ -362,25 +117,27 @@ DNS.classes = {
'HS':4
};
DNS.prototype.query = function(queries, /* queryStr, type, class, */callback, thisObj, recursive, timeout, failures, failed) {
DNS.prototype.handle_timeout = function(os) {
os.failed++;
delete this.outstanding[os.id];
if (os.failed > os.failures)
os.callback.call(os);
else
os.obj.query(os.query, os.callback, os.thisObj, os.recursive,
os.timeout, os.failures, os.failed);
};
DNS.prototype.generate_query = function(queries, recursive) {
var id;
var namebits = {};
var query = {};
function int16_to_string(id) {
return ascii((id & 0xff00) >> 8) + ascii(id & 0xff);
}
function handle_timeout() {
this.failed++;
delete outstanding[this.id];
if (this.failed > 2)
this.callback.call(this);
else
this.obj.query(this.query, this.callback, this.thisObj, this.recursive, this.timeout, this.failures, this.failed);
}
queries.forEach(function(query) {
if (query.str === undefined)
query.str = 'example.com';
......@@ -402,28 +159,18 @@ DNS.prototype.query = function(queries, /* queryStr, type, class, */callback, th
}
});
if (callback === undefined)
return;
if (recursive === undefined)
recursive = true;
if (timeout === undefined)
timeout = 1000;
if (failures === undefined)
failures = 1;
if (failed === undefined)
failed = 0;
var query = '';
// Header
id = increment_id();
query = int16_to_string(id);
query += ascii(recursive ? 1 : 0);
query += ascii(0);
query += int16_to_string(queries.length); // Questions
query += int16_to_string(0); // Answers
query += int16_to_string(0); // Name Servers
query += int16_to_string(0); // Additional Records
query.id = this.increment_id();
query.buf = int16_to_string(query.id);
query.buf += ascii(recursive ? 1 : 0);
query.buf += ascii(0);
query.buf += int16_to_string(queries.length); // Questions
query.buf += int16_to_string(0); // Answers
query.buf += int16_to_string(0); // Name Servers
query.buf += int16_to_string(0); // Additional Records
// Queries
queries.forEach(function(oneQuery) {
......@@ -446,7 +193,7 @@ DNS.prototype.query = function(queries, /* queryStr, type, class, */callback, th
// Now fix up negative namebits...
Object.keys(namebits).forEach(function(name) {
if (namebits[name] < 0) {
namebits[name] = query.length + thisname.length + namebits[name];
namebits[name] = query.buf.length + thisname.length + namebits[name];
}
});
......@@ -457,19 +204,397 @@ DNS.prototype.query = function(queries, /* queryStr, type, class, */callback, th
}
else
thisname += ascii(0);
query += thisname;
query += int16_to_string(oneQuery.type);
query += int16_to_string(oneQuery.class);
}, this);
query.buf += thisname;
query.buf += int16_to_string(oneQuery.type);
query.buf += int16_to_string(oneQuery.class);
});
this.sockets[0].outstanding[id] = {'callback':callback, 'query':queries, 'recursive':recursive, 'timeout':timeout, 'thisObj':thisObj, 'id':id, 'obj':this, 'failed':failed};
this.sockets[0].outstanding[id].timeoutid = js.setTimeout(handle_timeout, timeout, this.sockets[0].outstanding[id]);
this.outstanding[query.id] = {'query':queries, 'recursive':recursive, 'id':query.id, 'obj':this};
return query;
};
this.sockets.forEach(function(sock) {
sock.write(query);
});
DNS.prototype.handle_response = function(sock) {
var resp;
var id;
var offset = 0;
var queries;
var answers;
var nameservers;
var arecords;
var q;
var i;
var ret = {'queries':[], 'answers':[], 'nameservers':[], 'additional':[]};
var rdata;
var rdlen;
var tmp;
function string_to_int16(str) {
return ((ascii(str[0])<<8) | (ascii(str[1])));
}
function string_to_int32(str) {
return ((ascii(str[0])<<24) | (ascii(str[1]) << 16) | (ascii(str[1]) << 8) | (ascii(str[1])));
}
function get_string(resp, offset) {
var len = ascii(resp[offset]);
return {'len':len + 1, 'string':resp.substr(offset + 1, len)};
}
function get_name(resp, offset) {
var len;
var name = '';
var ret = 0;
var compressed = false;
do {
len = ascii(resp[offset]);
if (!compressed)
ret++;
offset++;
if (len > 63) {
offset = ((len & 0x3f) << 8) | ascii(resp[offset]);
if (!compressed)
ret++;
compressed = true;
}
else {
if (!compressed)
ret += len;
if (name.length > 0 && len > 0)
name += '.';
name += resp.substr(offset, len);
offset += len;
}
} while (len != 0);
return {'len':ret, 'name':name};
}
function parse_rdata(type, resp, offset, len) {
var tmp;
var tmp2;
var tmp3;
var tmp4;
switch(type) {
case 1: // A
return ascii(resp[offset]) + '.' +
ascii(resp[offset + 1]) + '.' +
ascii(resp[offset + 2]) + '.' +
ascii(resp[offset + 3]);
case 2: // NS
return get_name(resp, offset).name;
case 5: // CNAME
return get_name(resp, offset).name;
case 6: // SOA
tmp = {};
tmp2 = 0;
tmp3 = get_name(resp, offset + tmp2);
tmp.mname = tmp3.name;
tmp2 += tmp3.len;
tmp3 = get_name(resp, offset + tmp2);
tmp.rname = tmp3.name;
tmp2 += tmp3.len;
tmp.serial = string_to_int32(resp.substr(offset + tmp2, 4));
tmp2 += 4;
tmp.refresh = string_to_int32(resp.substr(offset + tmp2, 4));
tmp2 += 4;
tmp.retry = string_to_int32(resp.substr(offset + tmp2, 4));
tmp2 += 4;
tmp.export = string_to_int32(resp.substr(offset + tmp2, 4));
tmp2 += 4;
tmp.minimum = string_to_int32(resp.substr(offset + tmp2, 4));
tmp2 += 4;
return tmp;
case 11: // WKS
tmp = {};
tmp.address = ascii(resp[offset]) + '.' +
ascii(resp[offset + 1]) + '.' +
ascii(resp[offset + 2]) + '.' +
ascii(resp[offset + 3]);
tmp.protocol = ascii(resp[offset + 4]);
tmp2 = 5;
tmp.ports = [];
while (tmp2 < len) {
tmp3 = ascii(resp[offset + tmp2]);
for (tmp4 = 0; tmp4 < 8; tmp4++) {
if (tmp3 & (1 << tmp4))
tmp.ports.push(8 * (tmp2 - 5) + tmp3);
}
}
return tmp;
case 12: // PTR
return get_name(resp, offset).name;
case 13: // HINFO
tmp = get_string(resp, offset);
return {'cpu':tmp.string, 'os':get_string(resp, offset + tmp.len).string};
case 15: // MX
tmp = {};
tmp.preference = string_to_int16(resp.substr(offset, 2));
tmp.exchange = get_name(resp, offset + 2).name;
return tmp;
case 16: // TXT
tmp = [];
tmp2 = 0;
do {
tmp3 = get_string(resp, offset + tmp2);
tmp.push(tmp3.string);
tmp2 += tmp3.len;
} while (tmp2 < len);
return tmp;
case 28: // AAAA
return format("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
ascii(resp[offset + 0]), ascii(resp[offset + 1]),
ascii(resp[offset + 2]), ascii(resp[offset + 3]),
ascii(resp[offset + 4]), ascii(resp[offset + 5]),
ascii(resp[offset + 6]), ascii(resp[offset + 7]),
ascii(resp[offset + 8]), ascii(resp[offset + 9]),
ascii(resp[offset + 10]), ascii(resp[offset + 11]),
ascii(resp[offset + 12]), ascii(resp[offset + 13]),
ascii(resp[offset + 14]), ascii(resp[offset + 15])
).replace(/(0000:)+/, ':').replace(/(^|:)0{1,3}/g, '$1');
case 33: // SRV
tmp = {};
tmp.priority = string_to_int16(resp.substr(offset, 2));
offset += 2;
tmp.weight = string_to_int16(resp.substr(offset, 2));
offset += 2;
tmp.port = string_to_int16(resp.substr(offset, 2));
offset += 2;
tmp.target = get_name(resp, offset).name;
return tmp;
case 35: // NAPTR
tmp = {};
tmp.order = string_to_int16(resp.substr(offset, 2));
offset += 2;
tmp.preference = string_to_int16(resp.substr(offset, 2));
offset += 2;
tmp2 = get_string(resp, offset);
tmp.flags = tmp2.string;
offset += tmp2.len;
tmp2 = get_string(resp, offset);
tmp.services = tmp2.string;
offset += tmp2.len;
tmp2 = get_string(resp, offset);
tmp.regexp = tmp2.string;
offset += tmp2.len;
tmp.replacement = get_name(resp, offset).name;
return tmp;
case 256: // URI
tmp = {};
tmp.priority = string_to_int16(resp.substr(offset, 2));
offset += 2;
tmp.weight = string_to_int16(resp.substr(offset, 2));
offset += 2;
tmp.target = get_string(resp, offset).string;
tmp.target = tmp.target.replace(/\\"/g, '"');
tmp.target = tmp.target.replace(/^"(.*)"$/, '$1');
return tmp;
case 3: // MD
case 4: // MF
case 7: // MB
case 8: // MG
case 9: // MR
case 10: // NULL
case 14: // MINFO
return {'raw':resp.substr(offset, len)};
}
}
resp = sock.recv(10000);
id = string_to_int16(resp);
if (this.outstanding[id] === undefined)
return null;
q = this.outstanding[id];
delete this.outstanding[id];
ret.id = id;
ret.response = !!(ascii(resp[2]) & 1);
ret.opcode = (ascii(resp[2]) & 0x1e) >> 1;
ret.authoritative = !!(ascii(resp[2]) & (1<<5));
ret.truncation = !!(ascii(resp[2]) & (1<<6));
ret.recusrion = !!(ascii(resp[2]) & (1<<7));
ret.reserved = ascii(resp[3]) & 7;
ret.rcode = ascii(resp[3] & 0xf1) >> 3;
queries = string_to_int16(resp.substr(4, 2));
answers = string_to_int16(resp.substr(6, 2));
nameservers = string_to_int16(resp.substr(8, 2));
arecords = string_to_int16(resp.substr(10, 2));
if (answers === 0)
return null;
if (q.timeoutid !== undefined)
js.clearTimeout(q.timeoutid);
offset = 12;
for (i = 0; i < queries; i++) {
rdata = {};
tmp = get_name(resp, offset);
rdata.name = tmp.name;
offset += tmp.len;
rdata.type = string_to_int16(resp.substr(offset, 2));
offset += 2;
rdata.class = string_to_int16(resp.substr(offset, 2));
offset += 2;
ret.queries.push(rdata);
}
for (i = 0; i < answers; i++) {
rdata = {};
tmp = get_name(resp, offset);
rdata.name = tmp.name;
offset += tmp.len;
rdata.type = string_to_int16(resp.substr(offset, 2));
offset += 2;
rdata.class = string_to_int16(resp.substr(offset, 2));
offset += 2;
rdata.ttl = string_to_int32(resp.substr(offset, 4));
offset += 4;
rdlen = string_to_int16(resp.substr(offset, 2));
offset += 2;
rdata.rdata = parse_rdata(rdata.type, resp, offset, rdlen);
offset += rdlen;
ret.answers.push(rdata);
}
for (i = 0; i < nameservers; i++) {
rdata = {};
tmp = get_name(resp, offset);
rdata.name = tmp.name;
offset += tmp.len;
rdata.type = string_to_int16(resp.substr(offset, 2));
offset += 2;
rdata.class = string_to_int16(resp.substr(offset, 2));
offset += 2;
rdata.ttl = string_to_int32(resp.substr(offset, 4));
offset += 4;
rdlen = string_to_int16(resp.substr(offset, 2));
offset += 2;
rdata.rdata = parse_rdata(rdata.type, resp, offset, rdlen);
offset += rdlen;
ret.nameservers.push(rdata);
}
for (i = 0; i < arecords; i++) {
rdata = {};
tmp = get_name(resp, offset);
rdata.name = tmp.name;
offset += tmp.len;
rdata.type = string_to_int16(resp.substr(offset, 2));
offset += 2;
rdata.class = string_to_int16(resp.substr(offset, 2));
offset += 2;
rdata.ttl = string_to_int32(resp.substr(offset, 4));
offset += 4;
rdlen = string_to_int16(resp.substr(offset, 2));
offset += 2;
rdata.rdata = parse_rdata(rdata.type, resp, offset, rdlen);
offset += rdlen;
ret.additional.push(rdata);
}
return {'q':q, 'ret':ret};
}
DNS.prototype.query = function(queries, /* queryStr, type, class, */callback, thisObj, recursive, timeout, failures, failed) {
var query;
if (callback === undefined)
throw new Error('callback is required');
if (thisObj === undefined)
thisObj = this;
if (recursive === undefined)
recursive = true;
if (timeout === undefined)
timeout = 1000;
if (failures === undefined)
failures = 1;
if (failed === undefined)
failed = 0;
queries.forEach(function(q) {
query = this.generate_query([q], recursive);
if (query) {
this.outstanding[query.id].callback = callback;
this.outstanding[query.id].thisObj = thisObj;
this.outstanding[query.id].timeout = timeout;
this.outstanding[query.id].failures = failures;
this.outstanding[query.id].failed = failed;
this.outstanding[query.id].timeoutid = js.setTimeout(this.handle_timeout, timeout,
this.outstanding[query.id]);
this.sockets.forEach(function(sock) {
sock.write(query.buf);
});
}
}, this);
return undefined;
};
DNS_blocking.prototype.query = function(queries, callback, thisObj, recursive, timeout, failures, failed) {
var query;
var ret;
var socket_array = [];
var end;
var to;
var resp;
var ready;
var done = false;
if (thisObj === undefined)
thisObj = this;
if (recursive === undefined)
recursive = true;
if (timeout === undefined)
timeout = 1000;
if (failures === undefined)
failures = 1;
if (failed === undefined)
failed = 0;
queries.forEach(function(q) {
query = this.generate_query([q], recursive);
if (query) {
this.outstanding[query.id].callback = callback;
this.outstanding[query.id].thisObj = thisObj;
this.outstanding[query.id].timeout = timeout;
this.outstanding[query.id].failures = failures;
this.outstanding[query.id].failed = failed;
this.sockets.forEach(function(sock) {
sock.write(query.buf);
});
}
}, this);
ret = [];
end = system.timer + timeout / 1000;
do {
to = end - system.timer;
if (to < 0)
to = 0;
ready = socket_select(this.sockets, to);
if (ready === null)
break;
ready.forEach(function(sid) {
resp = this.handle_response(this.sockets[sid]);
if (resp !== null) {
ret.push(resp.ret);
if (callback !== undefined) {
callback.call(thisObj, resp.ret);
}
}
}, this);
} while (!done && system.timer < end && ret.length < queries.length);
if (callback === undefined)
return ret;
};
DNS.prototype.resolve = function(host, callback, thisObj)
{
var ctx = {A:{}, AAAA:{}, unique_id:'DNS.prototype.resolve'};
......@@ -484,40 +609,59 @@ DNS.prototype.resolve = function(host, callback, thisObj)
if (host === undefined)
throw new Error('No host specified');
if (this.synchronous && callback === undefined)
throw new Error('No callback specified');
if (thisObj === undefined)
thisObj = this;
ctx.callback = callback;
ctx.thisObj = thisObj;
ctx.final = js.addEventListener(ctx.unique_id+'.final', function() {
var ret = [];
ctx.final_callback = function() {
this.ret = [];
this.AAAA.addrs.forEach(function(addr) {
ret.push(addr);
});
this.ret.push(addr);
}, this);
this.A.addrs.forEach(function(addr) {
ret.push(addr);
});
js.removeEventListener(this.final);
js.removeEventListener(this.respA);
js.removeEventListener(this.respAAAA);
this.callback.call(this.thisObj, ret);
});
this.ret.push(addr);
}, this);
if (this.callback !== undefined) {
js.removeEventListener(this.final);
js.removeEventListener(this.respA);
js.removeEventListener(this.respAAAA);
this.callback.call(this.thisObj, this.ret);
}
};
if (callback !== undefined)
ctx.final = js.addEventListener(ctx.unique_id+'.final', ctx.final_callback);
ctx.respA = js.addEventListener(ctx.unique_id+'.respA', function() {
ctx.respA_callback = function() {
this.A.done = true;
if (this.AAAA.done)
js.dispatchEvent(this.unique_id + '.final', this);
});
if (this.AAAA.done) {
if (this.final !== undefined)
js.dispatchEvent(this.unique_id + '.final', this);
else
this.final_callback(this);
}
};
if (callback !== undefined)
ctx.respA = js.addEventListener(ctx.unique_id+'.respA', ctx.respA_callback);
ctx.respAAAA = js.addEventListener(ctx.unique_id+'.respAAAA', function() {
ctx.respAAAA_callback = function() {
this.AAAA.done = true;
if (this.A.done)
js.dispatchEvent(ctx.unique_id + '.final', this);
});
if (this.A.done) {
if (this.final !== undefined)
js.dispatchEvent(ctx.unique_id + '.final', this);
else
this.final_callback(this);
}
}
if (callback !== undefined)
ctx.respAAAA = js.addEventListener(ctx.unique_id+'.respAAAA', ctx.respAAAA_callback);
function handle_response(resp) {
var rectype;
switch(resp.queries[0].type) {
case DNS.types.A:
rectype = 'A';
......@@ -536,12 +680,15 @@ DNS.prototype.resolve = function(host, callback, thisObj)
return;
this[rectype].addrs.push(ans.rdata);
}, this);
js.dispatchEvent(this.unique_id + '.resp'+rectype, this);
if (ctx.callback !== undefined)
js.dispatchEvent(this.unique_id + '.resp'+rectype, this);
else
this['resp'+rectype+'_callback']();
}
this.query([{query:host, type:'AAAA'}], handle_response, ctx);
this.query([{query:host, type:'A'}], handle_response, ctx);
}
this.query([{query:host, type:'AAAA'},{query:host, type:'A'}], handle_response, ctx);
return ctx.ret;
};
DNS.prototype.resolveTypeClass = function(host, type, class, callback, thisObj)
{
......@@ -549,6 +696,7 @@ DNS.prototype.resolveTypeClass = function(host, type, class, callback, thisObj)
var final;
var respA;
var respAAAA;
var ret;
this.sockets.forEach(function(sock) {
ctx.unique_id += '.'+sock.local_port;
......@@ -563,7 +711,7 @@ DNS.prototype.resolveTypeClass = function(host, type, class, callback, thisObj)
if (class === undefined)
throw new Error('No class specified');
if (callback === undefined)
if (this.synchronous && callback === undefined)
throw new Error('No callback specified');
ctx.callback = callback;
......@@ -572,19 +720,23 @@ DNS.prototype.resolveTypeClass = function(host, type, class, callback, thisObj)
ctx.thisObj = thisObj;
function handle_response(resp) {
ret = [];
if (this.ret === undefined)
this.ret = [];
if (resp !== undefined && resp.answers !== undefined) {
resp.answers.forEach(function(ans) {
if (resp.queries[0].type != ans.type || resp.queries[0].class != ans.class)
return;
ret.push(ans.rdata);
});
this.ret.push(ans.rdata);
}, this);
}
this.callback.call(this.thisObj, ret);
if (this.callback !== undefined)
this.callback.call(this.thisObj, this.ret);
}
this.query([{query:host, type:type, class:class}], handle_response, ctx);
}
if (callback === undefined)
return ctx.ret;
};
DNS.prototype.reverse = function(ip, callback, thisObj)
{
......@@ -596,6 +748,9 @@ DNS.prototype.reverse = function(ip, callback, thisObj)
if (ip === undefined)
throw new Error('No IP specified');
if (this.synchronous && callback === undefined)
throw new Error('No callback specified');
if (thisObj === undefined)
thisObj = this;
......@@ -641,8 +796,8 @@ DNS.prototype.reverse = function(ip, callback, thisObj)
qstr += 'ip6.arpa';
}
this.resolveTypeClass(qstr, 'PTR', 'IN', callback, thisObj);
}
return this.resolveTypeClass(qstr, 'PTR', 'IN', callback, thisObj);
};
DNS.prototype.resolveMX = function(host, callback, thisObj)
{
......@@ -655,6 +810,9 @@ DNS.prototype.resolveMX = function(host, callback, thisObj)
if (host === undefined)
throw new Error('No host specified');
if (this.synchronous && callback === undefined)
throw new Error('No callback specified');
if (thisObj === undefined)
thisObj = this;
ctx.thisObj = thisObj;
......@@ -667,8 +825,12 @@ DNS.prototype.resolveMX = function(host, callback, thisObj)
resp.forEach(function(r) {
ret.push(r.exchange);
});
this.callback.call(this.thisObj, ret);
if (this.callback === undefined)
this.ret = ret;
else
this.callback.call(this.thisObj, ret);
}
this.resolveTypeClass(host, 'MX', 'IN', handler, ctx);
}
return ctx.ret;
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment