Skip to content

Commit a71df76

Browse files
oyydtargos
authored andcommitted
dns: allow --dns-result-order to change default dns verbatim
Allow the `--dns-result-order` option to change the default value of verbatim in `dns.lookup()`. This is useful when running Node.js in ipv6-only environments to avoid possible ENETUNREACH errors. PR-URL: #38099 Refs: #31566 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
1 parent 84c3990 commit a71df76

File tree

10 files changed

+298
-9
lines changed

10 files changed

+298
-9
lines changed

‎doc/api/cli.md‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,19 @@ Make built-in language features like `eval` and `new Function` that generate
187187
code from strings throw an exception instead. This does not affect the Node.js
188188
`vm` module.
189189

190+
### `--dns-result-order=order`
191+
<!-- YAML
192+
added: REPLACEME
193+
-->
194+
195+
Set the default value of `verbatim` in [`dns.lookup()`][] and
196+
[`dnsPromises.lookup()`][]. The value could be:
197+
*`ipv4first`: sets default `verbatim``false`.
198+
*`verbatim`: sets default `verbatim``true`.
199+
200+
The default is `ipv4first` and [`dns.setDefaultResultOrder()`][] have higher
201+
priority than `--dns-result-order`.
202+
190203
### `--enable-fips`
191204
<!-- YAML
192205
added: v6.0.0
@@ -1377,6 +1390,7 @@ Node.js options that are allowed are:
13771390
*`--conditions`, `-C`
13781391
*`--diagnostic-dir`
13791392
*`--disable-proto`
1393+
*`--dns-result-order`
13801394
*`--enable-fips`
13811395
*`--enable-source-maps`
13821396
*`--experimental-abortcontroller`
@@ -1765,6 +1779,9 @@ $ node --max-old-space-size=1536 index.js
17651779
[`NODE_OPTIONS`]: #cli_node_options_options
17661780
[`NO_COLOR`]: https://no-color.org
17671781
[`SlowBuffer`]: buffer.md#buffer_class_slowbuffer
1782+
[`dns.lookup()`]: dns.md#dns_dns_lookup_hostname_options_callback
1783+
[`dns.setDefaultResultOrder()`]: dns.md#dns_dns_setdefaultresultorder_order
1784+
[`dnsPromises.lookup()`]: dns.md#dns_dnspromises_lookup_hostname_options
17681785
[`process.setUncaughtExceptionCaptureCallback()`]: process.md#process_process_setuncaughtexceptioncapturecallback_fn
17691786
[`tls.DEFAULT_MAX_VERSION`]: tls.md#tls_tls_default_max_version
17701787
[`tls.DEFAULT_MIN_VERSION`]: tls.md#tls_tls_default_min_version

‎doc/api/dns.md‎

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,9 @@ changes:
184184
addresses in the order the DNS resolver returned them. When `false`,
185185
IPv4 addresses are placed before IPv6 addresses.
186186
**Default:** currently `false` (addresses are reordered) but this is
187-
expected to change in the not too distant future.
188-
New code should use `{verbatim: true }`.
187+
expected to change in the not too distant future. Default value is
188+
configurable using [`dns.setDefaultResultOrder()`][] or
189+
[`--dns-result-order`][]. New code should use `{verbatim: true }`.
189190
*`callback`{Function}
190191
*`err`{Error}
191192
*`address`{string} A string representation of an IPv4 or IPv6 address.
@@ -629,6 +630,23 @@ array of host names.
629630
On error, `err` is an [`Error`][] object, where `err.code` is
630631
one of the [DNS error codes][].
631632

633+
## `dns.setDefaultResultOrder(order)`
634+
<!-- YAML
635+
added: REPLACEME
636+
-->
637+
638+
*`order`{string} must be `'ipv4first'` or `'verbatim'`.
639+
640+
Set the default value of `verbatim` in [`dns.lookup()`][] and
641+
[`dnsPromises.lookup()`][]. The value could be:
642+
*`ipv4first`: sets default `verbatim``false`.
643+
*`verbatim`: sets default `verbatim``true`.
644+
645+
The default is `ipv4first` and [`dns.setDefaultResultOrder()`][] have higher
646+
priority than [`--dns-result-order`][]. When using [worker threads][],
647+
[`dns.setDefaultResultOrder()`][] from the main thread won't affect the default
648+
dns orders in workers.
649+
632650
## `dns.setServers(servers)`
633651
<!-- YAML
634652
added: v0.11.3
@@ -777,8 +795,9 @@ added: v10.6.0
777795
IPv6 addresses in the order the DNS resolver returned them. When `false`,
778796
IPv4 addresses are placed before IPv6 addresses.
779797
**Default:** currently `false` (addresses are reordered) but this is
780-
expected to change in the not too distant future.
781-
New code should use `{verbatim: true }`.
798+
expected to change in the not too distant future. Default value is
799+
configurable using [`dns.setDefaultResultOrder()`][] or
800+
[`--dns-result-order`][]. New code should use `{verbatim: true }`.
782801

783802
Resolves a host name (e.g. `'nodejs.org'`) into the first found A (IPv4) or
784803
AAAA (IPv6) record. All `option` properties are optional. If `options` is an
@@ -1132,6 +1151,23 @@ array of host names.
11321151
On error, the `Promise` is rejected with an [`Error`][] object, where `err.code`
11331152
is one of the [DNS error codes](#dns_error_codes).
11341153

1154+
### `dnsPromises.setDefaultResultOrder(order)`
1155+
<!-- YAML
1156+
added: REPLACEME
1157+
-->
1158+
1159+
*`order`{string} must be `'ipv4first'` or `'verbatim'`.
1160+
1161+
Set the default value of `verbatim` in [`dns.lookup()`][] and
1162+
[`dnsPromises.lookup()`][]. The value could be:
1163+
*`ipv4first`: sets default `verbatim``false`.
1164+
*`verbatim`: sets default `verbatim``true`.
1165+
1166+
The default is `ipv4first` and [`dnsPromises.setDefaultResultOrder()`][] have
1167+
higher priority than [`--dns-result-order`][]. When using [worker threads][],
1168+
[`dnsPromises.setDefaultResultOrder()`][] from the main thread won't affect the
1169+
default dns orders in workers.
1170+
11351171
### `dnsPromises.setServers(servers)`
11361172
<!-- YAML
11371173
added: v10.6.0
@@ -1241,6 +1277,7 @@ uses. For instance, _they do not use the configuration from `/etc/hosts`_.
12411277
[Implementation considerations section]: #dns_implementation_considerations
12421278
[RFC 5952]: https://tools.ietf.org/html/rfc5952#section-6
12431279
[RFC 8482]: https://tools.ietf.org/html/rfc8482
1280+
[`--dns-result-order`]: cli.md#cli_dns_result_order_order
12441281
[`Error`]: errors.md#errors_class_error
12451282
[`UV_THREADPOOL_SIZE`]: cli.md#cli_uv_threadpool_size_size
12461283
[`dgram.createSocket()`]: dgram.md#dgram_dgram_createsocket_options_callback
@@ -1260,6 +1297,7 @@ uses. For instance, _they do not use the configuration from `/etc/hosts`_.
12601297
[`dns.resolveSrv()`]: #dns_dns_resolvesrv_hostname_callback
12611298
[`dns.resolveTxt()`]: #dns_dns_resolvetxt_hostname_callback
12621299
[`dns.reverse()`]: #dns_dns_reverse_ip_callback
1300+
[`dns.setDefaultResultOrder()`]: #dns_dns_setdefaultresultorder_order
12631301
[`dns.setServers()`]: #dns_dns_setservers_servers
12641302
[`dnsPromises.getServers()`]: #dns_dnspromises_getservers
12651303
[`dnsPromises.lookup()`]: #dns_dnspromises_lookup_hostname_options
@@ -1277,7 +1315,9 @@ uses. For instance, _they do not use the configuration from `/etc/hosts`_.
12771315
[`dnsPromises.resolveSrv()`]: #dns_dnspromises_resolvesrv_hostname
12781316
[`dnsPromises.resolveTxt()`]: #dns_dnspromises_resolvetxt_hostname
12791317
[`dnsPromises.reverse()`]: #dns_dnspromises_reverse_ip
1318+
[`dnsPromises.setDefaultResultOrder()`]: #dns_dnspromises_setdefaultresultorder_order
12801319
[`dnsPromises.setServers()`]: #dns_dnspromises_setservers_servers
12811320
[`socket.connect()`]: net.md#net_socket_connect_options_connectlistener
12821321
[`util.promisify()`]: util.md#util_util_promisify_original
12831322
[supported `getaddrinfo` flags]: #dns_supported_getaddrinfo_flags
1323+
[worker threads]: worker_threads.md

‎lib/dns.js‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ const{
4141
Resolver,
4242
validateHints,
4343
emitInvalidHostnameWarning,
44+
getDefaultVerbatim,
45+
setDefaultResultOrder,
4446
}=require('internal/dns/utils');
4547
const{
4648
ERR_INVALID_ARG_TYPE,
@@ -96,7 +98,7 @@ function lookup(hostname, options, callback){
9698
lethints=0;
9799
letfamily=-1;
98100
letall=false;
99-
letverbatim=false;
101+
letverbatim=getDefaultVerbatim();
100102

101103
// Parse arguments
102104
if(hostname){
@@ -113,7 +115,9 @@ function lookup(hostname, options, callback){
113115
hints=options.hints>>>0;
114116
family=options.family>>>0;
115117
all=options.all===true;
116-
verbatim=options.verbatim===true;
118+
if(typeofoptions.verbatim==='boolean'){
119+
verbatim=options.verbatim===true;
120+
}
117121

118122
validateHints(hints);
119123
}else{
@@ -286,6 +290,7 @@ module.exports ={
286290
lookupService,
287291

288292
Resolver,
293+
setDefaultResultOrder,
289294
setServers: defaultResolverSetServers,
290295

291296
// uv_getaddrinfo flags
@@ -330,6 +335,7 @@ ObjectDefineProperties(module.exports,{
330335
if(promises===null){
331336
promises=require('internal/dns/promises');
332337
promises.setServers=defaultResolverSetServers;
338+
promises.setDefaultResultOrder=setDefaultResultOrder;
333339
}
334340
returnpromises;
335341
}

‎lib/internal/dns/promises.js‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
'use strict';
2-
32
const{
43
ArrayPrototypeMap,
54
ObjectCreate,
@@ -14,6 +13,7 @@ const{
1413
validateHints,
1514
validateTimeout,
1615
emitInvalidHostnameWarning,
16+
getDefaultVerbatim,
1717
}=require('internal/dns/utils');
1818
const{ codes, dnsException }=require('internal/errors');
1919
const{ toASCII }=require('internal/idna');
@@ -103,7 +103,7 @@ function lookup(hostname, options){
103103
varhints=0;
104104
varfamily=-1;
105105
varall=false;
106-
varverbatim=false;
106+
varverbatim=getDefaultVerbatim();
107107

108108
// Parse arguments
109109
if(hostname&&typeofhostname!=='string'){
@@ -112,7 +112,9 @@ function lookup(hostname, options){
112112
hints=options.hints>>>0;
113113
family=options.family>>>0;
114114
all=options.all===true;
115-
verbatim=options.verbatim===true;
115+
if(typeofoptions.verbatim==='boolean'){
116+
verbatim=options.verbatim===true;
117+
}
116118

117119
validateHints(hints);
118120
}else{

‎lib/internal/dns/utils.js‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ const{
1313

1414
consterrors=require('internal/errors');
1515
const{ isIP }=require('internal/net');
16+
const{ getOptionValue }=require('internal/options');
1617
const{
1718
validateArray,
1819
validateInt32,
20+
validateOneOf,
1921
validateString,
2022
}=require('internal/validators');
2123
const{
@@ -184,6 +186,23 @@ function emitInvalidHostnameWarning(hostname){
184186
);
185187
}
186188

189+
letdnsOrder=getOptionValue('--dns-result-order')||'ipv4first';
190+
191+
functiongetDefaultVerbatim(){
192+
switch(dnsOrder){
193+
case'verbatim':
194+
returntrue;
195+
case'ipv4first':
196+
default:
197+
returnfalse;
198+
}
199+
}
200+
201+
functionsetDefaultResultOrder(value){
202+
validateOneOf(value,'dnsOrder',['verbatim','ipv4first']);
203+
dnsOrder=value;
204+
}
205+
187206
module.exports={
188207
bindDefaultResolver,
189208
getDefaultResolver,
@@ -192,4 +211,6 @@ module.exports ={
192211
validateTimeout,
193212
Resolver,
194213
emitInvalidHostnameWarning,
214+
getDefaultVerbatim,
215+
setDefaultResultOrder,
195216
};

‎src/node_options.cc‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,13 @@ EnvironmentOptionsParser::EnvironmentOptionsParser(){
298298
" (default: current working directory)",
299299
&EnvironmentOptions::diagnostic_dir,
300300
kAllowedInEnvironment);
301+
AddOption("--dns-result-order",
302+
"set default value of verbatim in dns.lookup. Options are "
303+
"'ipv4first' (IPv4 addresses are placed before IPv6 addresses) "
304+
"'verbatim' (addresses are in the order the DNS resolver "
305+
"returned)",
306+
&EnvironmentOptions::dns_result_order,
307+
kAllowedInEnvironment);
301308
AddOption("--enable-source-maps",
302309
"Source Map V3 support for stack traces",
303310
&EnvironmentOptions::enable_source_maps,

‎src/node_options.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ class EnvironmentOptions : public Options{
101101
public:
102102
bool abort_on_uncaught_exception = false;
103103
std::vector<std::string> conditions;
104+
std::string dns_result_order;
104105
bool enable_source_maps = false;
105106
bool experimental_json_modules = false;
106107
bool experimental_modules = false;
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Flags: --expose-internals --dns-result-order=ipv4first
2+
'use strict';
3+
constcommon=require('../common');
4+
constassert=require('assert');
5+
const{ internalBinding }=require('internal/test/binding');
6+
constcares=internalBinding('cares_wrap');
7+
const{ promisify }=require('util');
8+
9+
// Test that --dns-result-order=ipv4first works as expected.
10+
11+
constoriginalGetaddrinfo=cares.getaddrinfo;
12+
constcalls=[];
13+
cares.getaddrinfo=common.mustCallAtLeast((...args)=>{
14+
calls.push(args);
15+
originalGetaddrinfo(...args);
16+
},1);
17+
18+
constdns=require('dns');
19+
constdnsPromises=dns.promises;
20+
21+
letverbatim;
22+
23+
// We want to test the parameter of verbatim only so that we
24+
// ignore possible errors here.
25+
functionallowFailed(fn){
26+
returnfn.catch((_err)=>{
27+
//
28+
});
29+
}
30+
31+
(async()=>{
32+
letcallsLength=0;
33+
constcheckParameter=(expected)=>{
34+
assert.strictEqual(calls.length,callsLength+1);
35+
verbatim=calls[callsLength][4];
36+
assert.strictEqual(verbatim,expected);
37+
callsLength+=1;
38+
};
39+
40+
awaitallowFailed(promisify(dns.lookup)('example.org'));
41+
checkParameter(false);
42+
43+
awaitallowFailed(dnsPromises.lookup('example.org'));
44+
checkParameter(false);
45+
46+
awaitallowFailed(promisify(dns.lookup)('example.org',{}));
47+
checkParameter(false);
48+
49+
awaitallowFailed(dnsPromises.lookup('example.org',{}));
50+
checkParameter(false);
51+
})().then(common.mustCall());
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Flags: --expose-internals --dns-result-order=verbatim
2+
'use strict';
3+
constcommon=require('../common');
4+
constassert=require('assert');
5+
const{ internalBinding }=require('internal/test/binding');
6+
constcares=internalBinding('cares_wrap');
7+
const{ promisify }=require('util');
8+
9+
// Test that --dns-result-order=verbatim works as expected.
10+
11+
constoriginalGetaddrinfo=cares.getaddrinfo;
12+
constcalls=[];
13+
cares.getaddrinfo=common.mustCallAtLeast((...args)=>{
14+
calls.push(args);
15+
originalGetaddrinfo(...args);
16+
},1);
17+
18+
constdns=require('dns');
19+
constdnsPromises=dns.promises;
20+
21+
letverbatim;
22+
23+
// We want to test the parameter of verbatim only so that we
24+
// ignore possible errors here.
25+
functionallowFailed(fn){
26+
returnfn.catch((_err)=>{
27+
//
28+
});
29+
}
30+
31+
(async()=>{
32+
letcallsLength=0;
33+
constcheckParameter=(expected)=>{
34+
assert.strictEqual(calls.length,callsLength+1);
35+
verbatim=calls[callsLength][4];
36+
assert.strictEqual(verbatim,expected);
37+
callsLength+=1;
38+
};
39+
40+
awaitallowFailed(promisify(dns.lookup)('example.org'));
41+
checkParameter(true);
42+
43+
awaitallowFailed(dnsPromises.lookup('example.org'));
44+
checkParameter(true);
45+
46+
awaitallowFailed(promisify(dns.lookup)('example.org',{}));
47+
checkParameter(true);
48+
49+
awaitallowFailed(dnsPromises.lookup('example.org',{}));
50+
checkParameter(true);
51+
})().then(common.mustCall());

0 commit comments

Comments
(0)