Skip to content

Commit 54a5095

Browse files
authored
dns: do not consult hosts file in reverse lookups
dns.reverse() is documented with dns.resolve*() as always performing DNS queries without using hosts file configuration. However, c-ares ares_gethostbyaddr() honors the channel lookup order, which can include the files source and cause reverse lookups to return entries from /etc/hosts. Configure c-ares resolver channels with ARES_OPT_LOOKUPS set to "b" so getHostByAddr uses DNS/PTR lookups only. This keeps dns.reverse() aligned with dns.resolve*() and preserves the distinction from system-resolution APIs such as dns.lookup() and dns.lookupService(). This is preferable to documenting the current behavior because hosts- file reverse lookup results are platform-dependent and expose hostent alias semantics rather than DNS PTR record semantics. Signed-off-by: Archkon <180910180+Archkon@users.noreply.github.com> PR-URL: #64268 Fixes: #64257 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 4a5eb1c commit 54a5095

4 files changed

Lines changed: 44 additions & 15 deletions

File tree

doc/api/dns.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,10 @@ treated separately.
867867

868868
<!-- YAML
869869
added: v0.1.16
870+
changes:
871+
- version: REPLACEME
872+
pr-url: https://github.com/nodejs/node/pull/64268
873+
description: Reverse lookups no longer consult hosts files.
870874
-->
871875

872876
* `ip` {string}

src/cares_wrap.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,8 @@ void ChannelWrap::Setup() {
10201020
options.timeout = timeout_;
10211021
options.tries = tries_;
10221022
options.qcache_max_ttl = 0;
1023+
// Resolver APIs always perform DNS queries and must not consult hosts files.
1024+
options.lookups = const_cast<char*>("b");
10231025

10241026
int r;
10251027
if (!library_inited_) {
@@ -1033,7 +1035,7 @@ void ChannelWrap::Setup() {
10331035

10341036
/* We do the call to ares_init_option for caller. */
10351037
int optmask = ARES_OPT_FLAGS | ARES_OPT_TIMEOUTMS | ARES_OPT_SOCK_STATE_CB |
1036-
ARES_OPT_TRIES | ARES_OPT_QUERY_CACHE;
1038+
ARES_OPT_TRIES | ARES_OPT_QUERY_CACHE | ARES_OPT_LOOKUPS;
10371039

10381040
if (max_timeout_ > 0) {
10391041
options.maxtimeout = max_timeout_;

test/parallel/test-c-ares.js

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -76,17 +76,3 @@ dns.lookup('::1', common.mustSucceed((result, addressType) => {
7676

7777
assert.throws(() => dnsPromises.resolve('www.google.com', val), err);
7878
});
79-
80-
// Windows doesn't usually have an entry for localhost 127.0.0.1 in
81-
// C:\Windows\System32\drivers\etc\hosts
82-
// so we disable this test on Windows.
83-
// IBMi reports `ENOTFOUND` when get hostname by address 127.0.0.1
84-
if (!common.isWindows && !common.isIBMi) {
85-
dns.reverse('127.0.0.1', common.mustSucceed((domains) => {
86-
assert.ok(Array.isArray(domains));
87-
}));
88-
89-
(async function() {
90-
assert.ok(Array.isArray(await dnsPromises.reverse('127.0.0.1')));
91-
})().then(common.mustCall());
92-
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const assert = require('assert');
5+
const dgram = require('dgram');
6+
const dns = require('dns');
7+
const dnstools = require('../common/dns');
8+
9+
const server = dgram.createSocket('udp4');
10+
const resolver = new dns.Resolver();
11+
const expectedHostname = 'reverse-dns-only.example';
12+
13+
server.on('message', common.mustCall((msg, { address, port }) => {
14+
const parsed = dnstools.parseDNSPacket(msg);
15+
const question = parsed.questions[0];
16+
17+
assert.strictEqual(question.domain, '1.0.0.127.in-addr.arpa');
18+
assert.strictEqual(question.type, 'PTR');
19+
20+
server.send(dnstools.writeDNSPacket({
21+
id: parsed.id,
22+
questions: parsed.questions,
23+
answers: [{
24+
type: 'PTR',
25+
domain: question.domain,
26+
value: expectedHostname,
27+
}],
28+
}), port, address);
29+
}));
30+
31+
server.bind(0, common.mustCall(() => {
32+
resolver.setServers([`127.0.0.1:${server.address().port}`]);
33+
resolver.reverse('127.0.0.1', common.mustSucceed((hostnames) => {
34+
assert.deepStrictEqual(hostnames, [expectedHostname]);
35+
server.close();
36+
}));
37+
}));

0 commit comments

Comments
 (0)