Skip to content

Commit

Permalink
dns: add promisified dns module
Browse files Browse the repository at this point in the history
PR-URL: #21264
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
  • Loading branch information
cjihrig authored and targos committed Jun 20, 2018
1 parent 9cef72d commit aa864ba
Show file tree
Hide file tree
Showing 16 changed files with 1,551 additions and 406 deletions.
466 changes: 466 additions & 0 deletions doc/api/dns.md

Large diffs are not rendered by default.

149 changes: 35 additions & 114 deletions lib/dns.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,15 @@ const { isIP, isIPv4, isLegalPort } = require('internal/net');
const { customPromisifyArgs } = require('internal/util');
const errors = require('internal/errors');
const {
ERR_DNS_SET_SERVERS_FAILED,
bindDefaultResolver,
getDefaultResolver,
setDefaultResolver,
Resolver,
validateHints
} = require('internal/dns/utils');
const {
ERR_INVALID_ARG_TYPE,
ERR_INVALID_CALLBACK,
ERR_INVALID_IP_ADDRESS,
ERR_INVALID_OPT_VALUE,
ERR_MISSING_ARGS,
ERR_SOCKET_BAD_PORT
Expand All @@ -39,12 +44,13 @@ const {
GetAddrInfoReqWrap,
GetNameInfoReqWrap,
QueryReqWrap,
ChannelWrap,
} = cares;

const IANA_DNS_PORT = 53;
const dnsException = errors.dnsException;

let promisesWarn = true;
let promises; // Lazy loaded

function onlookup(err, addresses) {
if (err) {
return this.callback(dnsException(err, 'getaddrinfo', this.hostname));
Expand Down Expand Up @@ -97,12 +103,7 @@ function lookup(hostname, options, callback) {
all = options.all === true;
verbatim = options.verbatim === true;

if (hints !== 0 &&
hints !== cares.AI_ADDRCONFIG &&
hints !== cares.AI_V4MAPPED &&
hints !== (cares.AI_ADDRCONFIG | cares.AI_V4MAPPED)) {
throw new ERR_INVALID_OPT_VALUE('hints', hints);
}
validateHints(hints);
} else {
family = options >>> 0;
}
Expand Down Expand Up @@ -197,17 +198,6 @@ function onresolve(err, result, ttls) {
this.callback(null, result);
}

// Resolver instances correspond 1:1 to c-ares channels.
class Resolver {
constructor() {
this._handle = new ChannelWrap();
}

cancel() {
this._handle.cancel();
}
}

function resolver(bindingName) {
function query(name, /* options, */ callback) {
var options;
Expand Down Expand Up @@ -270,101 +260,15 @@ function resolve(hostname, rrtype, callback) {
}
}


Resolver.prototype.getServers = getServers;
function getServers() {
const ret = this._handle.getServers();
return ret.map((val) => {
if (!val[1] || val[1] === IANA_DNS_PORT) return val[0];

const host = isIP(val[0]) === 6 ? `[${val[0]}]` : val[0];
return `${host}:${val[1]}`;
});
}


Resolver.prototype.setServers = setServers;
function setServers(servers) {
// cache the original servers because in the event of an error setting the
// servers cares won't have any servers available for resolution
const orig = this._handle.getServers();
const newSet = [];
const IPv6RE = /^\[([^[\]]*)\]/;
const addrSplitRE = /(^.+?)(?::(\d+))?$/;

servers.forEach((serv) => {
var ipVersion = isIP(serv);
if (ipVersion !== 0)
return newSet.push([ipVersion, serv, IANA_DNS_PORT]);

const match = serv.match(IPv6RE);
// we have an IPv6 in brackets
if (match) {
ipVersion = isIP(match[1]);
if (ipVersion !== 0) {
const port =
parseInt(serv.replace(addrSplitRE, '$2')) ||
IANA_DNS_PORT;
return newSet.push([ipVersion, match[1], port]);
}
}

// addr::port
const addrSplitMatch = serv.match(addrSplitRE);
if (addrSplitMatch) {
const hostIP = addrSplitMatch[1];
const port = addrSplitMatch[2] || IANA_DNS_PORT;

ipVersion = isIP(hostIP);
if (ipVersion !== 0) {
return newSet.push([ipVersion, hostIP, parseInt(port)]);
}
}

throw new ERR_INVALID_IP_ADDRESS(serv);
});

const errorNumber = this._handle.setServers(newSet);

if (errorNumber !== 0) {
// reset the servers to the old servers, because ares probably unset them
this._handle.setServers(orig.join(','));

var err = cares.strerror(errorNumber);
throw new ERR_DNS_SET_SERVERS_FAILED(err, servers);
}
}

let defaultResolver = new Resolver();

const resolverKeys = [
'getServers',
'resolve',
'resolveAny',
'resolve4',
'resolve6',
'resolveCname',
'resolveMx',
'resolveNs',
'resolveTxt',
'resolveSrv',
'resolvePtr',
'resolveNaptr',
'resolveSoa',
'reverse'
];

function setExportsFunctions() {
resolverKeys.forEach((key) => {
module.exports[key] = defaultResolver[key].bind(defaultResolver);
});
}

function defaultResolverSetServers(servers) {
const resolver = new Resolver();

resolver.setServers(servers);
defaultResolver = resolver;
setExportsFunctions();
setDefaultResolver(resolver);
bindDefaultResolver(module.exports, Resolver.prototype);

if (promises !== undefined)
bindDefaultResolver(promises, promises.Resolver.prototype);
}

module.exports = {
Expand Down Expand Up @@ -405,4 +309,21 @@ module.exports = {
CANCELLED: 'ECANCELLED'
};

setExportsFunctions();
bindDefaultResolver(module.exports, getDefaultResolver());

Object.defineProperties(module.exports, {
promises: {
configurable: true,
enumerable: false,
get() {
if (promisesWarn) {
promises = require('internal/dns/promises');
promises.setServers = defaultResolverSetServers;
promisesWarn = false;
process.emitWarning('The dns.promises API is experimental',
'ExperimentalWarning');
}
return promises;
}
}
});
Loading

0 comments on commit aa864ba

Please sign in to comment.