Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dns: add promisified dns module #21264

Merged
merged 2 commits into from
Jun 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
458 changes: 458 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