From e0e1b88f80baf1a9e42715e37ca0450ceaddeb9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Deuc=D0=B5?= <shurd@sasktel.net> Date: Sun, 4 Apr 2021 00:43:29 -0400 Subject: [PATCH] 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. --- exec/load/dns.js | 888 ++++++++++++++++++++++++++++------------------- 1 file changed, 525 insertions(+), 363 deletions(-) diff --git a/exec/load/dns.js b/exec/load/dns.js index 0d88fc0a58..e7d4f470af 100644 --- a/exec/load/dns.js +++ b/exec/load/dns.js @@ -1,5 +1,5 @@ /* 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; +}; -- GitLab