dns: add order option and support ipv6first

PR-URL: https://github.com/nodejs/node/pull/52492
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
This commit is contained in:
Paolo Insogna 2024-04-17 17:24:28 +02:00 committed by GitHub
parent b41347e496
commit 7e89369166
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 400 additions and 90 deletions

View File

@ -594,16 +594,20 @@ added:
- v16.4.0 - v16.4.0
- v14.18.0 - v14.18.0
changes: changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/52492
description: The `ipv6first` is supported now.
- version: v17.0.0 - version: v17.0.0
pr-url: https://github.com/nodejs/node/pull/39987 pr-url: https://github.com/nodejs/node/pull/39987
description: Changed default value to `verbatim`. description: Changed default value to `verbatim`.
--> -->
Set the default value of `verbatim` in [`dns.lookup()`][] and Set the default value of `order` in [`dns.lookup()`][] and
[`dnsPromises.lookup()`][]. The value could be: [`dnsPromises.lookup()`][]. The value could be:
* `ipv4first`: sets default `verbatim` `false`. * `ipv4first`: sets default `order` to `ipv4first`.
* `verbatim`: sets default `verbatim` `true`. * `ipv6first`: sets default `order` to `ipv6first`.
* `verbatim`: sets default `order` to `verbatim`.
The default is `verbatim` and [`dns.setDefaultResultOrder()`][] have higher The default is `verbatim` and [`dns.setDefaultResultOrder()`][] have higher
priority than `--dns-result-order`. priority than `--dns-result-order`.

View File

@ -179,6 +179,9 @@ section if a custom port is used.
<!-- YAML <!-- YAML
added: v0.1.90 added: v0.1.90
changes: changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/52492
description: The `verbatim` option is now deprecated in favor of the new `order` option.
- version: v18.4.0 - version: v18.4.0
pr-url: https://github.com/nodejs/node/pull/43054 pr-url: https://github.com/nodejs/node/pull/43054
description: For compatibility with `node:net`, when passing an option description: For compatibility with `node:net`, when passing an option
@ -211,9 +214,18 @@ changes:
flags may be passed by bitwise `OR`ing their values. flags may be passed by bitwise `OR`ing their values.
* `all` {boolean} When `true`, the callback returns all resolved addresses in * `all` {boolean} When `true`, the callback returns all resolved addresses in
an array. Otherwise, returns a single address. **Default:** `false`. an array. Otherwise, returns a single address. **Default:** `false`.
* `order` {string} When `verbatim`, the resolved addresses are return
unsorted. When `ipv4first`, the resolved addresses are sorted by placing
IPv4 addresses before IPv6 addresses. When `ipv6first`, the resolved
addresses are sorted by placing IPv6 addresses before IPv4 addresses.
**Default:** `verbatim` (addresses are not reordered).
Default value is configurable using [`dns.setDefaultResultOrder()`][] or
[`--dns-result-order`][].
* `verbatim` {boolean} When `true`, the callback receives IPv4 and IPv6 * `verbatim` {boolean} When `true`, the callback receives IPv4 and IPv6
addresses in the order the DNS resolver returned them. When `false`, addresses in the order the DNS resolver returned them. When `false`,
IPv4 addresses are placed before IPv6 addresses. IPv4 addresses are placed before IPv6 addresses.
This option will be deprecated in favor of `order`. When both are specified,
`order` has higher precedence. New code should only use `order`.
**Default:** `true` (addresses are not reordered). Default value is **Default:** `true` (addresses are not reordered). Default value is
configurable using [`dns.setDefaultResultOrder()`][] or configurable using [`dns.setDefaultResultOrder()`][] or
[`--dns-result-order`][]. [`--dns-result-order`][].
@ -775,18 +787,22 @@ added:
- v16.4.0 - v16.4.0
- v14.18.0 - v14.18.0
changes: changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/52492
description: The `ipv6first` value is supported now.
- version: v17.0.0 - version: v17.0.0
pr-url: https://github.com/nodejs/node/pull/39987 pr-url: https://github.com/nodejs/node/pull/39987
description: Changed default value to `verbatim`. description: Changed default value to `verbatim`.
--> -->
* `order` {string} must be `'ipv4first'` or `'verbatim'`. * `order` {string} must be `'ipv4first'`, `'ipv6first'` or `'verbatim'`.
Set the default value of `verbatim` in [`dns.lookup()`][] and Set the default value of `order` in [`dns.lookup()`][] and
[`dnsPromises.lookup()`][]. The value could be: [`dnsPromises.lookup()`][]. The value could be:
* `ipv4first`: sets default `verbatim` `false`. * `ipv4first`: sets default `order` to `ipv4first`.
* `verbatim`: sets default `verbatim` `true`. * `ipv6first`: sets default `order` to `ipv6first`.
* `verbatim`: sets default `order` to `verbatim`.
The default is `verbatim` and [`dns.setDefaultResultOrder()`][] have higher The default is `verbatim` and [`dns.setDefaultResultOrder()`][] have higher
priority than [`--dns-result-order`][]. When using [worker threads][], priority than [`--dns-result-order`][]. When using [worker threads][],
@ -799,13 +815,18 @@ dns orders in workers.
added: added:
- v20.1.0 - v20.1.0
- v18.17.0 - v18.17.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/52492
description: The `ipv6first` value is supported now.
--> -->
Get the default value for `verbatim` in [`dns.lookup()`][] and Get the default value for `order` in [`dns.lookup()`][] and
[`dnsPromises.lookup()`][]. The value could be: [`dnsPromises.lookup()`][]. The value could be:
* `ipv4first`: for `verbatim` defaulting to `false`. * `ipv4first`: for `order` defaulting to `ipv4first`.
* `verbatim`: for `verbatim` defaulting to `true`. * `ipv6first`: for `order` defaulting to `ipv6first`.
* `verbatim`: for `order` defaulting to `verbatim`.
## `dns.setServers(servers)` ## `dns.setServers(servers)`
@ -949,6 +970,10 @@ section if a custom port is used.
<!-- YAML <!-- YAML
added: v10.6.0 added: v10.6.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/52492
description: The `verbatim` option is now deprecated in favor of the new `order` option.
--> -->
* `hostname` {string} * `hostname` {string}
@ -961,13 +986,22 @@ added: v10.6.0
flags may be passed by bitwise `OR`ing their values. flags may be passed by bitwise `OR`ing their values.
* `all` {boolean} When `true`, the `Promise` is resolved with all addresses in * `all` {boolean} When `true`, the `Promise` is resolved with all addresses in
an array. Otherwise, returns a single address. **Default:** `false`. an array. Otherwise, returns a single address. **Default:** `false`.
* `order` {string} When `verbatim`, the `Promise` is resolved with IPv4 and
IPv6 addresses in the order the DNS resolver returned them. When `ipv4first`,
IPv4 addresses are placed before IPv6 addresses. When `ipv6first`,
IPv6 addresses are placed before IPv4 addresses.
**Default:** `verbatim` (addresses are not reordered).
Default value is configurable using [`dns.setDefaultResultOrder()`][] or
[`--dns-result-order`][]. New code should use `{ order: 'verbatim' }`.
* `verbatim` {boolean} When `true`, the `Promise` is resolved with IPv4 and * `verbatim` {boolean} When `true`, the `Promise` is resolved with IPv4 and
IPv6 addresses in the order the DNS resolver returned them. When `false`, IPv6 addresses in the order the DNS resolver returned them. When `false`,
IPv4 addresses are placed before IPv6 addresses. IPv4 addresses are placed before IPv6 addresses.
This option will be deprecated in favor of `order`. When both are specified,
`order` has higher precedence. New code should only use `order`.
**Default:** currently `false` (addresses are reordered) but this is **Default:** currently `false` (addresses are reordered) but this is
expected to change in the not too distant future. Default value is expected to change in the not too distant future. Default value is
configurable using [`dns.setDefaultResultOrder()`][] or configurable using [`dns.setDefaultResultOrder()`][] or
[`--dns-result-order`][]. New code should use `{ verbatim: true }`. [`--dns-result-order`][].
Resolves a host name (e.g. `'nodejs.org'`) into the first found A (IPv4) or Resolves a host name (e.g. `'nodejs.org'`) into the first found A (IPv4) or
AAAA (IPv6) record. All `option` properties are optional. If `options` is an AAAA (IPv6) record. All `option` properties are optional. If `options` is an
@ -1349,18 +1383,22 @@ added:
- v16.4.0 - v16.4.0
- v14.18.0 - v14.18.0
changes: changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/52492
description: The `ipv6first` value is supported now.
- version: v17.0.0 - version: v17.0.0
pr-url: https://github.com/nodejs/node/pull/39987 pr-url: https://github.com/nodejs/node/pull/39987
description: Changed default value to `verbatim`. description: Changed default value to `verbatim`.
--> -->
* `order` {string} must be `'ipv4first'` or `'verbatim'`. * `order` {string} must be `'ipv4first'`, `'ipv6first'` or `'verbatim'`.
Set the default value of `verbatim` in [`dns.lookup()`][] and Set the default value of `order` in [`dns.lookup()`][] and
[`dnsPromises.lookup()`][]. The value could be: [`dnsPromises.lookup()`][]. The value could be:
* `ipv4first`: sets default `verbatim` `false`. * `ipv4first`: sets default `order` to `ipv4first`.
* `verbatim`: sets default `verbatim` `true`. * `ipv6first`: sets default `order` to `ipv6first`.
* `verbatim`: sets default `order` to `verbatim`.
The default is `verbatim` and [`dnsPromises.setDefaultResultOrder()`][] have The default is `verbatim` and [`dnsPromises.setDefaultResultOrder()`][] have
higher priority than [`--dns-result-order`][]. When using [worker threads][], higher priority than [`--dns-result-order`][]. When using [worker threads][],

View File

@ -43,7 +43,6 @@ const {
setDefaultResolver, setDefaultResolver,
validateHints, validateHints,
emitInvalidHostnameWarning, emitInvalidHostnameWarning,
getDefaultVerbatim,
getDefaultResultOrder, getDefaultResultOrder,
setDefaultResultOrder, setDefaultResultOrder,
errorCodes: dnsErrorCodes, errorCodes: dnsErrorCodes,
@ -89,6 +88,9 @@ const {
const { const {
GetAddrInfoReqWrap, GetAddrInfoReqWrap,
GetNameInfoReqWrap, GetNameInfoReqWrap,
DNS_ORDER_VERBATIM,
DNS_ORDER_IPV4_FIRST,
DNS_ORDER_IPV6_FIRST,
} = cares; } = cares;
const kPerfHooksDnsLookupContext = Symbol('kPerfHooksDnsLookupContext'); const kPerfHooksDnsLookupContext = Symbol('kPerfHooksDnsLookupContext');
@ -141,7 +143,7 @@ function lookup(hostname, options, callback) {
let hints = 0; let hints = 0;
let family = 0; let family = 0;
let all = false; let all = false;
let verbatim = getDefaultVerbatim(); let dnsOrder = getDefaultResultOrder();
// Parse arguments // Parse arguments
if (hostname) { if (hostname) {
@ -187,7 +189,11 @@ function lookup(hostname, options, callback) {
} }
if (options?.verbatim != null) { if (options?.verbatim != null) {
validateBoolean(options.verbatim, 'options.verbatim'); validateBoolean(options.verbatim, 'options.verbatim');
verbatim = options.verbatim; dnsOrder = options.verbatim ? 'verbatim' : 'ipv4first';
}
if (options?.order != null) {
validateOneOf(options.order, 'options.order', ['ipv4first', 'ipv6first', 'verbatim']);
dnsOrder = options.dnsOrder;
} }
} }
@ -218,8 +224,16 @@ function lookup(hostname, options, callback) {
req.hostname = hostname; req.hostname = hostname;
req.oncomplete = all ? onlookupall : onlookup; req.oncomplete = all ? onlookupall : onlookup;
let order = DNS_ORDER_VERBATIM;
if (dnsOrder === 'ipv4first') {
order = DNS_ORDER_IPV4_FIRST;
} else if (dnsOrder === 'ipv6first') {
order = DNS_ORDER_IPV6_FIRST;
}
const err = cares.getaddrinfo( const err = cares.getaddrinfo(
req, hostname, family, hints, verbatim, req, hostname, family, hints, order,
); );
if (err) { if (err) {
process.nextTick(callback, new DNSException(err, 'getaddrinfo', hostname)); process.nextTick(callback, new DNSException(err, 'getaddrinfo', hostname));
@ -230,8 +244,10 @@ function lookup(hostname, options, callback) {
hostname, hostname,
family, family,
hints, hints,
verbatim, verbatim: order === DNS_ORDER_VERBATIM,
order: dnsOrder,
}; };
startPerf(req, kPerfHooksDnsLookupContext, { type: 'dns', name: 'lookup', detail }); startPerf(req, kPerfHooksDnsLookupContext, { type: 'dns', name: 'lookup', detail });
} }
return req; return req;

View File

@ -12,7 +12,6 @@ const {
createResolverClass, createResolverClass,
validateHints, validateHints,
emitInvalidHostnameWarning, emitInvalidHostnameWarning,
getDefaultVerbatim,
errorCodes: dnsErrorCodes, errorCodes: dnsErrorCodes,
getDefaultResultOrder, getDefaultResultOrder,
setDefaultResultOrder, setDefaultResultOrder,
@ -53,6 +52,9 @@ const {
GetAddrInfoReqWrap, GetAddrInfoReqWrap,
GetNameInfoReqWrap, GetNameInfoReqWrap,
QueryReqWrap, QueryReqWrap,
DNS_ORDER_VERBATIM,
DNS_ORDER_IPV4_FIRST,
DNS_ORDER_IPV6_FIRST,
} = internalBinding('cares_wrap'); } = internalBinding('cares_wrap');
const { const {
ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_TYPE,
@ -120,13 +122,13 @@ function onlookupall(err, addresses) {
* @param {boolean} all - Whether to resolve with all IP addresses for the hostname. * @param {boolean} all - Whether to resolve with all IP addresses for the hostname.
* @param {number} hints - One or more supported getaddrinfo flags (supply multiple via * @param {number} hints - One or more supported getaddrinfo flags (supply multiple via
* bitwise OR). * bitwise OR).
* @param {boolean} verbatim - Whether to use the hostname verbatim. * @param {number} dnsOrder - How to sort results. Must be `ipv4first`, `ipv6first` or `verbatim`.
* @returns {Promise<DNSLookupResult | DNSLookupResult[]>} The IP address(es) of the hostname. * @returns {Promise<DNSLookupResult | DNSLookupResult[]>} The IP address(es) of the hostname.
* @typedef {object} DNSLookupResult * @typedef {object} DNSLookupResult
* @property {string} address - The IP address. * @property {string} address - The IP address.
* @property {0 | 4 | 6} family - The IP address type. 4 for IPv4 or 6 for IPv6, or 0 (for both). * @property {0 | 4 | 6} family - The IP address type. 4 for IPv4 or 6 for IPv6, or 0 (for both).
*/ */
function createLookupPromise(family, hostname, all, hints, verbatim) { function createLookupPromise(family, hostname, all, hints, dnsOrder) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!hostname) { if (!hostname) {
emitInvalidHostnameWarning(hostname); emitInvalidHostnameWarning(hostname);
@ -150,7 +152,15 @@ function createLookupPromise(family, hostname, all, hints, verbatim) {
req.resolve = resolve; req.resolve = resolve;
req.reject = reject; req.reject = reject;
const err = getaddrinfo(req, hostname, family, hints, verbatim); let order = DNS_ORDER_VERBATIM;
if (dnsOrder === 'ipv4first') {
order = DNS_ORDER_IPV4_FIRST;
} else if (dnsOrder === 'ipv6first') {
order = DNS_ORDER_IPV6_FIRST;
}
const err = getaddrinfo(req, hostname, family, hints, order);
if (err) { if (err) {
reject(new DNSException(err, 'getaddrinfo', hostname)); reject(new DNSException(err, 'getaddrinfo', hostname));
@ -159,7 +169,8 @@ function createLookupPromise(family, hostname, all, hints, verbatim) {
hostname, hostname,
family, family,
hints, hints,
verbatim, verbatim: order === DNS_ORDER_VERBATIM,
order: dnsOrder,
}; };
startPerf(req, kPerfHooksDnsLookupContext, { type: 'dns', name: 'lookup', detail }); startPerf(req, kPerfHooksDnsLookupContext, { type: 'dns', name: 'lookup', detail });
} }
@ -175,14 +186,15 @@ const validFamilies = [0, 4, 6];
* @param {0 | 4 | 6} [options.family=0] - The record family. Must be 4, 6, or 0 (for both). * @param {0 | 4 | 6} [options.family=0] - The record family. Must be 4, 6, or 0 (for both).
* @param {number} [options.hints] - One or more supported getaddrinfo flags (supply multiple via * @param {number} [options.hints] - One or more supported getaddrinfo flags (supply multiple via
* bitwise OR). * bitwise OR).
* @param {boolean} [options.verbatim=false] - Return results in same order DNS resolved them; * @param {string} [options.order='verbatim'] - Return results in same order DNS resolved them;
* otherwise IPv4 then IPv6. New code should supply `true`. * Must be `ipv4first`, `ipv6first` or `verbatim`.
* New code should supply `verbatim`.
*/ */
function lookup(hostname, options) { function lookup(hostname, options) {
let hints = 0; let hints = 0;
let family = 0; let family = 0;
let all = false; let all = false;
let verbatim = getDefaultVerbatim(); let dnsOrder = getDefaultResultOrder();
// Parse arguments // Parse arguments
if (hostname) { if (hostname) {
@ -210,11 +222,15 @@ function lookup(hostname, options) {
} }
if (options?.verbatim != null) { if (options?.verbatim != null) {
validateBoolean(options.verbatim, 'options.verbatim'); validateBoolean(options.verbatim, 'options.verbatim');
verbatim = options.verbatim; dnsOrder = options.verbatim ? 'verbatim' : 'ipv4first';
}
if (options?.order != null) {
validateOneOf(options.order, 'options.order', ['ipv4first', 'ipv6first', 'verbatim']);
dnsOrder = options.order;
} }
} }
return createLookupPromise(family, hostname, all, hints, verbatim); return createLookupPromise(family, hostname, all, hints, dnsOrder);
} }

View File

@ -207,6 +207,7 @@ function initializeDns() {
dnsOrder ??= 'verbatim'; dnsOrder ??= 'verbatim';
} else { } else {
// Allow the deserialized application to override order from CLI. // Allow the deserialized application to override order from CLI.
validateOneOf(orderFromCLI, '--dns-result-order', ['verbatim', 'ipv4first', 'ipv6first']);
dnsOrder = orderFromCLI; dnsOrder = orderFromCLI;
} }
@ -277,12 +278,8 @@ function emitInvalidHostnameWarning(hostname) {
} }
} }
function getDefaultVerbatim() {
return dnsOrder !== 'ipv4first';
}
function setDefaultResultOrder(value) { function setDefaultResultOrder(value) {
validateOneOf(value, 'dnsOrder', ['verbatim', 'ipv4first']); validateOneOf(value, 'dnsOrder', ['verbatim', 'ipv4first', 'ipv6first']);
dnsOrder = value; dnsOrder = value;
} }
@ -351,7 +348,6 @@ module.exports = {
validateTimeout, validateTimeout,
validateTries, validateTries,
emitInvalidHostnameWarning, emitInvalidHostnameWarning,
getDefaultVerbatim,
getDefaultResultOrder, getDefaultResultOrder,
setDefaultResultOrder, setDefaultResultOrder,
errorCodes, errorCodes,

View File

@ -253,7 +253,7 @@ async function isLocalAddress(hostname) {
) { ) {
hostname = StringPrototypeSlice(hostname, 1, -1); hostname = StringPrototypeSlice(hostname, 1, -1);
} }
const addr = await dnsLookup(hostname, { verbatim: true }); const addr = await dnsLookup(hostname, { order: 'verbatim' });
const ipv = addr.family === 4 ? 'ipv4' : 'ipv6'; const ipv = addr.family === 4 ? 'ipv4' : 'ipv6';
return allowList.check(addr.address, ipv); return allowList.check(addr.address, ipv);
} catch { } catch {

View File

@ -72,6 +72,7 @@ using v8::Nothing;
using v8::Null; using v8::Null;
using v8::Object; using v8::Object;
using v8::String; using v8::String;
using v8::Uint32;
using v8::Value; using v8::Value;
namespace { namespace {
@ -665,12 +666,11 @@ void ChannelWrap::New(const FunctionCallbackInfo<Value>& args) {
new ChannelWrap(env, args.This(), timeout, tries); new ChannelWrap(env, args.This(), timeout, tries);
} }
GetAddrInfoReqWrap::GetAddrInfoReqWrap( GetAddrInfoReqWrap::GetAddrInfoReqWrap(Environment* env,
Environment* env, Local<Object> req_wrap_obj,
Local<Object> req_wrap_obj, uint8_t order)
bool verbatim)
: ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETADDRINFOREQWRAP), : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETADDRINFOREQWRAP),
verbatim_(verbatim) {} order_(order) {}
GetNameInfoReqWrap::GetNameInfoReqWrap( GetNameInfoReqWrap::GetNameInfoReqWrap(
Environment* env, Environment* env,
@ -1445,7 +1445,7 @@ void AfterGetAddrInfo(uv_getaddrinfo_t* req, int status, struct addrinfo* res) {
}; };
uint32_t n = 0; uint32_t n = 0;
const bool verbatim = req_wrap->verbatim(); const uint8_t order = req_wrap->order();
if (status == 0) { if (status == 0) {
Local<Array> results = Array::New(env->isolate()); Local<Array> results = Array::New(env->isolate());
@ -1477,11 +1477,21 @@ void AfterGetAddrInfo(uv_getaddrinfo_t* req, int status, struct addrinfo* res) {
return Just(true); return Just(true);
}; };
if (add(true, verbatim).IsNothing()) switch (order) {
return; case DNS_ORDER_IPV4_FIRST:
if (verbatim == false) { if (add(true, false).IsNothing()) return;
if (add(false, true).IsNothing()) if (add(false, true).IsNothing()) return;
return;
break;
case DNS_ORDER_IPV6_FIRST:
if (add(false, true).IsNothing()) return;
if (add(true, false).IsNothing()) return;
break;
default:
if (add(true, true).IsNothing()) return;
break;
} }
// No responses were found to return // No responses were found to return
@ -1492,9 +1502,13 @@ void AfterGetAddrInfo(uv_getaddrinfo_t* req, int status, struct addrinfo* res) {
argv[1] = results; argv[1] = results;
} }
TRACE_EVENT_NESTABLE_ASYNC_END2( TRACE_EVENT_NESTABLE_ASYNC_END2(TRACING_CATEGORY_NODE2(dns, native),
TRACING_CATEGORY_NODE2(dns, native), "lookup", req_wrap.get(), "lookup",
"count", n, "verbatim", verbatim); req_wrap.get(),
"count",
n,
"order",
order);
// Make the callback into JavaScript // Make the callback into JavaScript
req_wrap->MakeCallback(env->oncomplete_string(), arraysize(argv), argv); req_wrap->MakeCallback(env->oncomplete_string(), arraysize(argv), argv);
@ -1558,7 +1572,7 @@ void GetAddrInfo(const FunctionCallbackInfo<Value>& args) {
CHECK(args[0]->IsObject()); CHECK(args[0]->IsObject());
CHECK(args[1]->IsString()); CHECK(args[1]->IsString());
CHECK(args[2]->IsInt32()); CHECK(args[2]->IsInt32());
CHECK(args[4]->IsBoolean()); CHECK(args[4]->IsUint32());
Local<Object> req_wrap_obj = args[0].As<Object>(); Local<Object> req_wrap_obj = args[0].As<Object>();
node::Utf8Value hostname(env->isolate(), args[1]); node::Utf8Value hostname(env->isolate(), args[1]);
std::string ascii_hostname = ada::idna::to_ascii(hostname.ToStringView()); std::string ascii_hostname = ada::idna::to_ascii(hostname.ToStringView());
@ -1584,9 +1598,10 @@ void GetAddrInfo(const FunctionCallbackInfo<Value>& args) {
UNREACHABLE("bad address family"); UNREACHABLE("bad address family");
} }
auto req_wrap = std::make_unique<GetAddrInfoReqWrap>(env, Local<Uint32> order = args[4].As<Uint32>();
req_wrap_obj,
args[4]->IsTrue()); auto req_wrap =
std::make_unique<GetAddrInfoReqWrap>(env, req_wrap_obj, order->Value());
struct addrinfo hints; struct addrinfo hints;
memset(&hints, 0, sizeof(hints)); memset(&hints, 0, sizeof(hints));
@ -1905,6 +1920,31 @@ void Initialize(Local<Object> target,
target->Set(env->context(), FIXED_ONE_BYTE_STRING(env->isolate(), target->Set(env->context(), FIXED_ONE_BYTE_STRING(env->isolate(),
"AI_V4MAPPED"), "AI_V4MAPPED"),
Integer::New(env->isolate(), AI_V4MAPPED)).Check(); Integer::New(env->isolate(), AI_V4MAPPED)).Check();
target
->Set(env->context(),
FIXED_ONE_BYTE_STRING(env->isolate(), "DNS_ORDER_VERBATIM"),
Integer::New(env->isolate(), DNS_ORDER_VERBATIM))
.Check();
target
->Set(env->context(),
FIXED_ONE_BYTE_STRING(env->isolate(), "DNS_ORDER_IPV4_FIRST"),
Integer::New(env->isolate(), DNS_ORDER_IPV4_FIRST))
.Check();
target
->Set(env->context(),
FIXED_ONE_BYTE_STRING(env->isolate(), "DNS_ORDER_IPV6_FIRST"),
Integer::New(env->isolate(), DNS_ORDER_IPV6_FIRST))
.Check();
target
->Set(env->context(),
FIXED_ONE_BYTE_STRING(env->isolate(), "AF_INET"),
Integer::New(env->isolate(), AF_INET))
.Check();
target
->Set(env->context(),
FIXED_ONE_BYTE_STRING(env->isolate(), "AF_INET"),
Integer::New(env->isolate(), AF_INET))
.Check();
Local<FunctionTemplate> aiw = Local<FunctionTemplate> aiw =
BaseObject::MakeLazilyInitializedJSTemplate(env); BaseObject::MakeLazilyInitializedJSTemplate(env);

View File

@ -30,6 +30,9 @@ namespace cares_wrap {
constexpr int ns_t_cname_or_a = -1; constexpr int ns_t_cname_or_a = -1;
constexpr int DNS_ESETSRVPENDING = -1000; constexpr int DNS_ESETSRVPENDING = -1000;
constexpr uint8_t DNS_ORDER_VERBATIM = 0;
constexpr uint8_t DNS_ORDER_IPV4_FIRST = 1;
constexpr uint8_t DNS_ORDER_IPV6_FIRST = 2;
class ChannelWrap; class ChannelWrap;
@ -195,16 +198,16 @@ class GetAddrInfoReqWrap final : public ReqWrap<uv_getaddrinfo_t> {
public: public:
GetAddrInfoReqWrap(Environment* env, GetAddrInfoReqWrap(Environment* env,
v8::Local<v8::Object> req_wrap_obj, v8::Local<v8::Object> req_wrap_obj,
bool verbatim); uint8_t order);
SET_NO_MEMORY_INFO() SET_NO_MEMORY_INFO()
SET_MEMORY_INFO_NAME(GetAddrInfoReqWrap) SET_MEMORY_INFO_NAME(GetAddrInfoReqWrap)
SET_SELF_SIZE(GetAddrInfoReqWrap) SET_SELF_SIZE(GetAddrInfoReqWrap)
bool verbatim() const { return verbatim_; } uint8_t order() const { return order_; }
private: private:
const bool verbatim_; const uint8_t order_;
}; };
class GetNameInfoReqWrap final : public ReqWrap<uv_getnameinfo_t> { class GetNameInfoReqWrap final : public ReqWrap<uv_getnameinfo_t> {

View File

@ -374,6 +374,7 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
AddOption("--dns-result-order", AddOption("--dns-result-order",
"set default value of verbatim in dns.lookup. Options are " "set default value of verbatim in dns.lookup. Options are "
"'ipv4first' (IPv4 addresses are placed before IPv6 addresses) " "'ipv4first' (IPv4 addresses are placed before IPv6 addresses) "
"'ipv6first' (IPv6 addresses are placed before IPv4 addresses) "
"'verbatim' (addresses are in the order the DNS resolver " "'verbatim' (addresses are in the order the DNS resolver "
"returned)", "returned)",
&EnvironmentOptions::dns_result_order, &EnvironmentOptions::dns_result_order,

View File

@ -8,6 +8,9 @@ const dns = require('dns');
dns.setDefaultResultOrder('ipv4first'); dns.setDefaultResultOrder('ipv4first');
let dnsOrder = dns.getDefaultResultOrder(); let dnsOrder = dns.getDefaultResultOrder();
assert.ok(dnsOrder === 'ipv4first'); assert.ok(dnsOrder === 'ipv4first');
dns.setDefaultResultOrder('ipv6first');
dnsOrder = dns.getDefaultResultOrder();
assert.ok(dnsOrder === 'ipv6first');
dns.setDefaultResultOrder('verbatim'); dns.setDefaultResultOrder('verbatim');
dnsOrder = dns.getDefaultResultOrder(); dnsOrder = dns.getDefaultResultOrder();
assert.ok(dnsOrder === 'verbatim'); assert.ok(dnsOrder === 'verbatim');
@ -15,10 +18,26 @@ assert.ok(dnsOrder === 'verbatim');
{ {
(async function() { (async function() {
const result = await dns.promises.lookup('localhost'); const result = await dns.promises.lookup('localhost');
const result1 = await dns.promises.lookup('localhost', { verbatim: true }); const result1 = await dns.promises.lookup('localhost', { order: 'verbatim' });
assert.ok(result !== undefined); assert.ok(result !== undefined);
assert.ok(result1 !== undefined); assert.ok(result1 !== undefined);
assert.ok(result.address === result1.address); assert.ok(result.address === result1.address);
assert.ok(result.family === result1.family); assert.ok(result.family === result1.family);
})().then(common.mustCall()); })().then(common.mustCall());
} }
{
(async function() {
const result = await dns.promises.lookup('localhost', { order: 'ipv4first' });
assert.ok(result !== undefined);
assert.ok(result.family === 4);
})().then(common.mustCall());
}
if (common.hasIPv6) {
(async function() {
const result = await dns.promises.lookup('localhost', { order: 'ipv6first' });
assert.ok(result !== undefined);
assert.ok(result.family === 6);
})().then(common.mustCall());
}

View File

@ -722,7 +722,7 @@ console.log(`looking up ${addresses.INET4_HOST}..`);
const cares = internalBinding('cares_wrap'); const cares = internalBinding('cares_wrap');
const req = new cares.GetAddrInfoReqWrap(); const req = new cares.GetAddrInfoReqWrap();
cares.getaddrinfo(req, addresses.INET4_HOST, 4, cares.getaddrinfo(req, addresses.INET4_HOST, 4,
/* hints */ 0, /* verbatim */ true); /* hints */ 0, /* order */ cares.DNS_ORDER_VERBATIM);
req.oncomplete = function(err, domains) { req.oncomplete = function(err, domains) {
assert.strictEqual(err, 0); assert.strictEqual(err, 0);

View File

@ -0,0 +1,49 @@
// Flags: --expose-internals --dns-result-order=ipv4first
'use strict';
const common = require('../common');
const assert = require('assert');
const { internalBinding } = require('internal/test/binding');
const cares = internalBinding('cares_wrap');
const { promisify } = require('util');
// Test that --dns-result-order=ipv4first works as expected.
const originalGetaddrinfo = cares.getaddrinfo;
const calls = [];
cares.getaddrinfo = common.mustCallAtLeast((...args) => {
calls.push(args);
originalGetaddrinfo(...args);
}, 1);
const dns = require('dns');
const dnsPromises = dns.promises;
// We want to test the parameter of order only so that we
// ignore possible errors here.
function allowFailed(fn) {
return fn.catch((_err) => {
//
});
}
(async () => {
let callsLength = 0;
const checkParameter = (expected) => {
assert.strictEqual(calls.length, callsLength + 1);
const order = calls[callsLength][4];
assert.strictEqual(order, expected);
callsLength += 1;
};
await allowFailed(promisify(dns.lookup)('example.org'));
checkParameter(cares.DNS_ORDER_IPV4_FIRST);
await allowFailed(dnsPromises.lookup('example.org'));
checkParameter(cares.DNS_ORDER_IPV4_FIRST);
await allowFailed(promisify(dns.lookup)('example.org', {}));
checkParameter(cares.DNS_ORDER_IPV4_FIRST);
await allowFailed(dnsPromises.lookup('example.org', {}));
checkParameter(cares.DNS_ORDER_IPV4_FIRST);
})().then(common.mustCall());

View File

@ -0,0 +1,49 @@
// Flags: --expose-internals --dns-result-order=ipv6first
'use strict';
const common = require('../common');
const assert = require('assert');
const { internalBinding } = require('internal/test/binding');
const cares = internalBinding('cares_wrap');
const { promisify } = require('util');
// Test that --dns-result-order=verbatim works as expected.
const originalGetaddrinfo = cares.getaddrinfo;
const calls = [];
cares.getaddrinfo = common.mustCallAtLeast((...args) => {
calls.push(args);
originalGetaddrinfo(...args);
}, 1);
const dns = require('dns');
const dnsPromises = dns.promises;
// We want to test the parameter of verbatim only so that we
// ignore possible errors here.
function allowFailed(fn) {
return fn.catch((_err) => {
//
});
}
(async () => {
let callsLength = 0;
const checkParameter = (expected) => {
assert.strictEqual(calls.length, callsLength + 1);
const order = calls[callsLength][4];
assert.strictEqual(order, expected);
callsLength += 1;
};
await allowFailed(promisify(dns.lookup)('example.org'));
checkParameter(cares.DNS_ORDER_IPV6_FIRST);
await allowFailed(dnsPromises.lookup('example.org'));
checkParameter(cares.DNS_ORDER_IPV6_FIRST);
await allowFailed(promisify(dns.lookup)('example.org', {}));
checkParameter(cares.DNS_ORDER_IPV6_FIRST);
await allowFailed(dnsPromises.lookup('example.org', {}));
checkParameter(cares.DNS_ORDER_IPV6_FIRST);
})().then(common.mustCall());

View File

@ -0,0 +1,49 @@
// Flags: --expose-internals --dns-result-order=verbatim
'use strict';
const common = require('../common');
const assert = require('assert');
const { internalBinding } = require('internal/test/binding');
const cares = internalBinding('cares_wrap');
const { promisify } = require('util');
// Test that --dns-result-order=verbatim works as expected.
const originalGetaddrinfo = cares.getaddrinfo;
const calls = [];
cares.getaddrinfo = common.mustCallAtLeast((...args) => {
calls.push(args);
originalGetaddrinfo(...args);
}, 1);
const dns = require('dns');
const dnsPromises = dns.promises;
// We want to test the parameter of verbatim only so that we
// ignore possible errors here.
function allowFailed(fn) {
return fn.catch((_err) => {
//
});
}
(async () => {
let callsLength = 0;
const checkParameter = (expected) => {
assert.strictEqual(calls.length, callsLength + 1);
const order = calls[callsLength][4];
assert.strictEqual(order, expected);
callsLength += 1;
};
await allowFailed(promisify(dns.lookup)('example.org'));
checkParameter(cares.DNS_ORDER_VERBATIM);
await allowFailed(dnsPromises.lookup('example.org'));
checkParameter(cares.DNS_ORDER_VERBATIM);
await allowFailed(promisify(dns.lookup)('example.org', {}));
checkParameter(cares.DNS_ORDER_VERBATIM);
await allowFailed(dnsPromises.lookup('example.org', {}));
checkParameter(cares.DNS_ORDER_VERBATIM);
})().then(common.mustCall());

View File

@ -38,14 +38,14 @@ function allowFailed(fn) {
}; };
await allowFailed(promisify(dns.lookup)('example.org')); await allowFailed(promisify(dns.lookup)('example.org'));
checkParameter(false); checkParameter(cares.DNS_ORDER_IPV4_FIRST);
await allowFailed(dnsPromises.lookup('example.org')); await allowFailed(dnsPromises.lookup('example.org'));
checkParameter(false); checkParameter(cares.DNS_ORDER_IPV4_FIRST);
await allowFailed(promisify(dns.lookup)('example.org', {})); await allowFailed(promisify(dns.lookup)('example.org', {}));
checkParameter(false); checkParameter(cares.DNS_ORDER_IPV4_FIRST);
await allowFailed(dnsPromises.lookup('example.org', {})); await allowFailed(dnsPromises.lookup('example.org', {}));
checkParameter(false); checkParameter(cares.DNS_ORDER_IPV4_FIRST);
})().then(common.mustCall()); })().then(common.mustCall());

View File

@ -38,14 +38,14 @@ function allowFailed(fn) {
}; };
await allowFailed(promisify(dns.lookup)('example.org')); await allowFailed(promisify(dns.lookup)('example.org'));
checkParameter(true); checkParameter(cares.DNS_ORDER_VERBATIM);
await allowFailed(dnsPromises.lookup('example.org')); await allowFailed(dnsPromises.lookup('example.org'));
checkParameter(true); checkParameter(cares.DNS_ORDER_VERBATIM);
await allowFailed(promisify(dns.lookup)('example.org', {})); await allowFailed(promisify(dns.lookup)('example.org', {}));
checkParameter(true); checkParameter(cares.DNS_ORDER_VERBATIM);
await allowFailed(dnsPromises.lookup('example.org', {})); await allowFailed(dnsPromises.lookup('example.org', {}));
checkParameter(true); checkParameter(cares.DNS_ORDER_VERBATIM);
})().then(common.mustCall()); })().then(common.mustCall());

View File

@ -31,5 +31,7 @@ assert.throws(() => dnsPromises.lookup('127.0.0.1', { all: 'true' }),
{ code: 'ERR_INVALID_ARG_TYPE' }); { code: 'ERR_INVALID_ARG_TYPE' });
assert.throws(() => dnsPromises.lookup('127.0.0.1', { verbatim: 'true' }), assert.throws(() => dnsPromises.lookup('127.0.0.1', { verbatim: 'true' }),
{ code: 'ERR_INVALID_ARG_TYPE' }); { code: 'ERR_INVALID_ARG_TYPE' });
assert.throws(() => dnsPromises.lookup('127.0.0.1', { order: 'true' }),
{ code: 'ERR_INVALID_ARG_VALUE' });
assert.throws(() => dnsPromises.lookup('127.0.0.1', '6'), assert.throws(() => dnsPromises.lookup('127.0.0.1', '6'),
{ code: 'ERR_INVALID_ARG_TYPE' }); { code: 'ERR_INVALID_ARG_TYPE' });

View File

@ -133,6 +133,15 @@ assert.throws(() => dnsPromises.lookup(false, () => {}),
}, err); }, err);
}); });
[0, 1, 0n, 1n, '', '0', Symbol(), {}, [], () => {}].forEach((order) => {
const err = { code: 'ERR_INVALID_ARG_VALUE' };
const options = { order };
assert.throws(() => { dnsPromises.lookup(false, options); }, err);
assert.throws(() => {
dns.lookup(false, options, common.mustNotCall());
}, err);
});
(async function() { (async function() {
let res; let res;

View File

@ -42,6 +42,7 @@ process.on('exit', () => {
assert.strictEqual(typeof entry.detail.family, 'number'); assert.strictEqual(typeof entry.detail.family, 'number');
assert.strictEqual(typeof entry.detail.hints, 'number'); assert.strictEqual(typeof entry.detail.hints, 'number');
assert.strictEqual(typeof entry.detail.verbatim, 'boolean'); assert.strictEqual(typeof entry.detail.verbatim, 'boolean');
assert.strictEqual(typeof entry.detail.order, 'string');
assert.strictEqual(Array.isArray(entry.detail.addresses), true); assert.strictEqual(Array.isArray(entry.detail.addresses), true);
break; break;
case 'lookupService': case 'lookupService':

View File

@ -19,9 +19,7 @@ cares.getaddrinfo = common.mustCallAtLeast((...args) => {
const dns = require('dns'); const dns = require('dns');
const dnsPromises = dns.promises; const dnsPromises = dns.promises;
let verbatim; // We want to test the parameter of order only so that we
// We want to test the parameter of verbatim only so that we
// ignore possible errors here. // ignore possible errors here.
function allowFailed(fn) { function allowFailed(fn) {
return fn.catch((_err) => { return fn.catch((_err) => {
@ -46,48 +44,68 @@ assert.throws(() => dns.promises.setDefaultResultOrder(4), {
let callsLength = 0; let callsLength = 0;
const checkParameter = (expected) => { const checkParameter = (expected) => {
assert.strictEqual(calls.length, callsLength + 1); assert.strictEqual(calls.length, callsLength + 1);
verbatim = calls[callsLength][4]; const order = calls[callsLength][4];
assert.strictEqual(verbatim, expected); assert.strictEqual(order, expected);
callsLength += 1; callsLength += 1;
}; };
dns.setDefaultResultOrder('verbatim'); dns.setDefaultResultOrder('verbatim');
await allowFailed(promisify(dns.lookup)('example.org')); await allowFailed(promisify(dns.lookup)('example.org'));
checkParameter(true); checkParameter(cares.DNS_ORDER_VERBATIM);
await allowFailed(dnsPromises.lookup('example.org')); await allowFailed(dnsPromises.lookup('example.org'));
checkParameter(true); checkParameter(cares.DNS_ORDER_VERBATIM);
await allowFailed(promisify(dns.lookup)('example.org', {})); await allowFailed(promisify(dns.lookup)('example.org', {}));
checkParameter(true); checkParameter(cares.DNS_ORDER_VERBATIM);
await allowFailed(dnsPromises.lookup('example.org', {})); await allowFailed(dnsPromises.lookup('example.org', {}));
checkParameter(true); checkParameter(cares.DNS_ORDER_VERBATIM);
dns.setDefaultResultOrder('ipv4first'); dns.setDefaultResultOrder('ipv4first');
await allowFailed(promisify(dns.lookup)('example.org')); await allowFailed(promisify(dns.lookup)('example.org'));
checkParameter(false); checkParameter(cares.DNS_ORDER_IPV4_FIRST);
await allowFailed(dnsPromises.lookup('example.org')); await allowFailed(dnsPromises.lookup('example.org'));
checkParameter(false); checkParameter(cares.DNS_ORDER_IPV4_FIRST);
await allowFailed(promisify(dns.lookup)('example.org', {})); await allowFailed(promisify(dns.lookup)('example.org', {}));
checkParameter(false); checkParameter(cares.DNS_ORDER_IPV4_FIRST);
await allowFailed(dnsPromises.lookup('example.org', {})); await allowFailed(dnsPromises.lookup('example.org', {}));
checkParameter(false); checkParameter(cares.DNS_ORDER_IPV4_FIRST);
dns.setDefaultResultOrder('ipv6first');
await allowFailed(promisify(dns.lookup)('example.org'));
checkParameter(cares.DNS_ORDER_IPV6_FIRST);
await allowFailed(dnsPromises.lookup('example.org'));
checkParameter(cares.DNS_ORDER_IPV6_FIRST);
await allowFailed(promisify(dns.lookup)('example.org', {}));
checkParameter(cares.DNS_ORDER_IPV6_FIRST);
await allowFailed(dnsPromises.lookup('example.org', {}));
checkParameter(cares.DNS_ORDER_IPV6_FIRST);
dns.promises.setDefaultResultOrder('verbatim'); dns.promises.setDefaultResultOrder('verbatim');
await allowFailed(promisify(dns.lookup)('example.org')); await allowFailed(promisify(dns.lookup)('example.org'));
checkParameter(true); checkParameter(cares.DNS_ORDER_VERBATIM);
await allowFailed(dnsPromises.lookup('example.org')); await allowFailed(dnsPromises.lookup('example.org'));
checkParameter(true); checkParameter(cares.DNS_ORDER_VERBATIM);
await allowFailed(promisify(dns.lookup)('example.org', {})); await allowFailed(promisify(dns.lookup)('example.org', {}));
checkParameter(true); checkParameter(cares.DNS_ORDER_VERBATIM);
await allowFailed(dnsPromises.lookup('example.org', {})); await allowFailed(dnsPromises.lookup('example.org', {}));
checkParameter(true); checkParameter(cares.DNS_ORDER_VERBATIM);
dns.promises.setDefaultResultOrder('ipv4first'); dns.promises.setDefaultResultOrder('ipv4first');
await allowFailed(promisify(dns.lookup)('example.org')); await allowFailed(promisify(dns.lookup)('example.org'));
checkParameter(false); checkParameter(cares.DNS_ORDER_IPV4_FIRST);
await allowFailed(dnsPromises.lookup('example.org')); await allowFailed(dnsPromises.lookup('example.org'));
checkParameter(false); checkParameter(cares.DNS_ORDER_IPV4_FIRST);
await allowFailed(promisify(dns.lookup)('example.org', {})); await allowFailed(promisify(dns.lookup)('example.org', {}));
checkParameter(false); checkParameter(cares.DNS_ORDER_IPV4_FIRST);
await allowFailed(dnsPromises.lookup('example.org', {})); await allowFailed(dnsPromises.lookup('example.org', {}));
checkParameter(false); checkParameter(cares.DNS_ORDER_IPV4_FIRST);
dns.promises.setDefaultResultOrder('ipv6first');
await allowFailed(promisify(dns.lookup)('example.org'));
checkParameter(cares.DNS_ORDER_IPV6_FIRST);
await allowFailed(dnsPromises.lookup('example.org'));
checkParameter(cares.DNS_ORDER_IPV6_FIRST);
await allowFailed(promisify(dns.lookup)('example.org', {}));
checkParameter(cares.DNS_ORDER_IPV6_FIRST);
await allowFailed(dnsPromises.lookup('example.org', {}));
checkParameter(cares.DNS_ORDER_IPV6_FIRST);
})().then(common.mustCall()); })().then(common.mustCall());

View File

@ -292,7 +292,7 @@ dns.lookup('', {
await dnsPromises.lookup('', { await dnsPromises.lookup('', {
hints: dns.ADDRCONFIG | dns.V4MAPPED | dns.ALL hints: dns.ADDRCONFIG | dns.V4MAPPED | dns.ALL
}); });
await dnsPromises.lookup('', { verbatim: true }); await dnsPromises.lookup('', { order: 'verbatim' });
})().then(common.mustCall()); })().then(common.mustCall());
{ {