node/lib/internal/dns/promises.js
btea 2ac5e9889a
dns: expose getDefaultResultOrder
PR-URL: https://github.com/nodejs/node/pull/46973
Fixes: https://github.com/nodejs/node/issues/46919
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
2023-04-26 08:56:04 +00:00

370 lines
8.4 KiB
JavaScript

'use strict';
const {
ArrayPrototypeMap,
ObjectDefineProperty,
Promise,
ReflectApply,
Symbol,
} = primordials;
const {
bindDefaultResolver,
createResolverClass,
validateHints,
emitInvalidHostnameWarning,
getDefaultVerbatim,
errorCodes: dnsErrorCodes,
getDefaultResultOrder,
setDefaultResultOrder,
setDefaultResolver,
} = require('internal/dns/utils');
const {
NODATA,
FORMERR,
SERVFAIL,
NOTFOUND,
NOTIMP,
REFUSED,
BADQUERY,
BADNAME,
BADFAMILY,
BADRESP,
CONNREFUSED,
TIMEOUT,
EOF,
FILE,
NOMEM,
DESTRUCTION,
BADSTR,
BADFLAGS,
NONAME,
BADHINTS,
NOTINITIALIZED,
LOADIPHLPAPI,
ADDRGETNETWORKPARAMS,
CANCELLED,
} = dnsErrorCodes;
const { codes, dnsException } = require('internal/errors');
const { toASCII } = require('internal/idna');
const { isIP } = require('internal/net');
const {
getaddrinfo,
getnameinfo,
GetAddrInfoReqWrap,
GetNameInfoReqWrap,
QueryReqWrap,
} = internalBinding('cares_wrap');
const {
ERR_INVALID_ARG_TYPE,
ERR_INVALID_ARG_VALUE,
ERR_MISSING_ARGS,
} = codes;
const {
validateBoolean,
validateNumber,
validateOneOf,
validatePort,
validateString,
} = require('internal/validators');
const kPerfHooksDnsLookupContext = Symbol('kPerfHooksDnsLookupContext');
const kPerfHooksDnsLookupServiceContext = Symbol('kPerfHooksDnsLookupServiceContext');
const kPerfHooksDnsLookupResolveContext = Symbol('kPerfHooksDnsLookupResolveContext');
const {
hasObserver,
startPerf,
stopPerf,
} = require('internal/perf/observe');
function onlookup(err, addresses) {
if (err) {
this.reject(dnsException(err, 'getaddrinfo', this.hostname));
return;
}
const family = this.family || isIP(addresses[0]);
this.resolve({ address: addresses[0], family });
if (this[kPerfHooksDnsLookupContext] && hasObserver('dns')) {
stopPerf(this, kPerfHooksDnsLookupContext, { detail: { addresses } });
}
}
function onlookupall(err, addresses) {
if (err) {
this.reject(dnsException(err, 'getaddrinfo', this.hostname));
return;
}
const family = this.family;
for (let i = 0; i < addresses.length; i++) {
const address = addresses[i];
addresses[i] = {
address,
family: family || isIP(addresses[i]),
};
}
this.resolve(addresses);
if (this[kPerfHooksDnsLookupContext] && hasObserver('dns')) {
stopPerf(this, kPerfHooksDnsLookupContext, { detail: { addresses } });
}
}
function createLookupPromise(family, hostname, all, hints, verbatim) {
return new Promise((resolve, reject) => {
if (!hostname) {
emitInvalidHostnameWarning(hostname);
resolve(all ? [] : { address: null, family: family === 6 ? 6 : 4 });
return;
}
const matchedFamily = isIP(hostname);
if (matchedFamily !== 0) {
const result = { address: hostname, family: matchedFamily };
resolve(all ? [result] : result);
return;
}
const req = new GetAddrInfoReqWrap();
req.family = family;
req.hostname = hostname;
req.oncomplete = all ? onlookupall : onlookup;
req.resolve = resolve;
req.reject = reject;
const err = getaddrinfo(req, toASCII(hostname), family, hints, verbatim);
if (err) {
reject(dnsException(err, 'getaddrinfo', hostname));
} else if (hasObserver('dns')) {
const detail = {
hostname,
family,
hints,
verbatim,
};
startPerf(req, kPerfHooksDnsLookupContext, { type: 'dns', name: 'lookup', detail });
}
});
}
const validFamilies = [0, 4, 6];
function lookup(hostname, options) {
let hints = 0;
let family = 0;
let all = false;
let verbatim = getDefaultVerbatim();
// Parse arguments
if (hostname) {
validateString(hostname, 'hostname');
}
if (typeof options === 'number') {
validateOneOf(options, 'family', validFamilies);
family = options;
} else if (options !== undefined && typeof options !== 'object') {
throw new ERR_INVALID_ARG_TYPE('options', ['integer', 'object'], options);
} else {
if (options?.hints != null) {
validateNumber(options.hints, 'options.hints');
hints = options.hints >>> 0;
validateHints(hints);
}
if (options?.family != null) {
validateOneOf(options.family, 'options.family', validFamilies);
family = options.family;
}
if (options?.all != null) {
validateBoolean(options.all, 'options.all');
all = options.all;
}
if (options?.verbatim != null) {
validateBoolean(options.verbatim, 'options.verbatim');
verbatim = options.verbatim;
}
}
return createLookupPromise(family, hostname, all, hints, verbatim);
}
function onlookupservice(err, hostname, service) {
if (err) {
this.reject(dnsException(err, 'getnameinfo', this.host));
return;
}
this.resolve({ hostname, service });
if (this[kPerfHooksDnsLookupServiceContext] && hasObserver('dns')) {
stopPerf(this, kPerfHooksDnsLookupServiceContext, { detail: { hostname, service } });
}
}
function createLookupServicePromise(hostname, port) {
return new Promise((resolve, reject) => {
const req = new GetNameInfoReqWrap();
req.hostname = hostname;
req.port = port;
req.oncomplete = onlookupservice;
req.resolve = resolve;
req.reject = reject;
const err = getnameinfo(req, hostname, port);
if (err)
reject(dnsException(err, 'getnameinfo', hostname));
else if (hasObserver('dns')) {
startPerf(req, kPerfHooksDnsLookupServiceContext, {
type: 'dns',
name: 'lookupService',
detail: {
host: hostname,
port,
},
});
}
});
}
function lookupService(address, port) {
if (arguments.length !== 2)
throw new ERR_MISSING_ARGS('address', 'port');
if (isIP(address) === 0)
throw new ERR_INVALID_ARG_VALUE('address', address);
validatePort(port);
return createLookupServicePromise(address, +port);
}
function onresolve(err, result, ttls) {
if (err) {
this.reject(dnsException(err, this.bindingName, this.hostname));
return;
}
if (ttls && this.ttl)
result = ArrayPrototypeMap(
result, (address, index) => ({ address, ttl: ttls[index] }));
this.resolve(result);
if (this[kPerfHooksDnsLookupResolveContext] && hasObserver('dns')) {
stopPerf(this, kPerfHooksDnsLookupResolveContext, { detail: { result } });
}
}
function createResolverPromise(resolver, bindingName, hostname, ttl) {
return new Promise((resolve, reject) => {
const req = new QueryReqWrap();
req.bindingName = bindingName;
req.hostname = hostname;
req.oncomplete = onresolve;
req.resolve = resolve;
req.reject = reject;
req.ttl = ttl;
const err = resolver._handle[bindingName](req, toASCII(hostname));
if (err)
reject(dnsException(err, bindingName, hostname));
else if (hasObserver('dns')) {
startPerf(req, kPerfHooksDnsLookupResolveContext, {
type: 'dns',
name: bindingName,
detail: {
host: hostname,
ttl,
},
});
}
});
}
function resolver(bindingName) {
function query(name, options) {
validateString(name, 'name');
const ttl = !!(options && options.ttl);
return createResolverPromise(this, bindingName, name, ttl);
}
ObjectDefineProperty(query, 'name', { __proto__: null, value: bindingName });
return query;
}
function resolve(hostname, rrtype) {
let resolver;
if (rrtype !== undefined) {
validateString(rrtype, 'rrtype');
resolver = resolveMap[rrtype];
if (typeof resolver !== 'function')
throw new ERR_INVALID_ARG_VALUE('rrtype', rrtype);
} else {
resolver = resolveMap.A;
}
return ReflectApply(resolver, this, [hostname]);
}
// Promise-based resolver.
const { Resolver, resolveMap } = createResolverClass(resolver);
Resolver.prototype.resolve = resolve;
function defaultResolverSetServers(servers) {
const resolver = new Resolver();
resolver.setServers(servers);
setDefaultResolver(resolver);
bindDefaultResolver(module.exports, Resolver.prototype);
}
module.exports = {
lookup,
lookupService,
Resolver,
getDefaultResultOrder,
setDefaultResultOrder,
setServers: defaultResolverSetServers,
// ERROR CODES
NODATA,
FORMERR,
SERVFAIL,
NOTFOUND,
NOTIMP,
REFUSED,
BADQUERY,
BADNAME,
BADFAMILY,
BADRESP,
CONNREFUSED,
TIMEOUT,
EOF,
FILE,
NOMEM,
DESTRUCTION,
BADSTR,
BADFLAGS,
NONAME,
BADHINTS,
NOTINITIALIZED,
LOADIPHLPAPI,
ADDRGETNETWORKPARAMS,
CANCELLED,
};
bindDefaultResolver(module.exports, Resolver.prototype);