Skip to content

Commit 9b09119

Browse files
authored
fix: use OS resolver for .local hostnames to fix mDNS resolution (usebruno#8072)
1 parent 8cd7c26 commit 9b09119

2 files changed

Lines changed: 68 additions & 0 deletions

File tree

packages/bruno-requests/src/network/fast-lookup.spec.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,58 @@ describe('fastLookup', () => {
3131
});
3232
});
3333

34+
it('should use dns.lookup directly for .local hostnames (mDNS)', (done) => {
35+
mockResolve('resolve4', ['192.0.78.134']); // bogus public-DNS result
36+
mockLookup('192.168.33.254', 4); // correct mDNS result
37+
38+
fastLookup('fhir.local', {}, (err, address, family) => {
39+
expect(err).toBeNull();
40+
expect(address).toBe('192.168.33.254');
41+
expect(family).toBe(4);
42+
expect(dns.resolve4).not.toHaveBeenCalled();
43+
done();
44+
});
45+
});
46+
47+
it('should use dns.lookup for mixed-case .LOCAL hostnames', (done) => {
48+
mockResolve('resolve4', ['192.0.78.134']);
49+
mockLookup('192.168.33.254', 4);
50+
51+
fastLookup('FHIR.LOCAL', {}, (err, address, family) => {
52+
expect(err).toBeNull();
53+
expect(address).toBe('192.168.33.254');
54+
expect(family).toBe(4);
55+
expect(dns.resolve4).not.toHaveBeenCalled();
56+
done();
57+
});
58+
});
59+
60+
it('should use dns.lookup directly for localhost (RFC 6761)', (done) => {
61+
mockResolve('resolve4', ['93.184.216.34']); // hijacked result
62+
mockLookup('127.0.0.1', 4);
63+
64+
fastLookup('localhost', {}, (err, address, family) => {
65+
expect(err).toBeNull();
66+
expect(address).toBe('127.0.0.1');
67+
expect(family).toBe(4);
68+
expect(dns.resolve4).not.toHaveBeenCalled();
69+
done();
70+
});
71+
});
72+
73+
it('should use dns.lookup for .localhost subdomains', (done) => {
74+
mockResolve('resolve4', ['93.184.216.34']);
75+
mockLookup('127.0.0.1', 4);
76+
77+
fastLookup('api.localhost', {}, (err, address, family) => {
78+
expect(err).toBeNull();
79+
expect(address).toBe('127.0.0.1');
80+
expect(family).toBe(4);
81+
expect(dns.resolve4).not.toHaveBeenCalled();
82+
done();
83+
});
84+
});
85+
3486
it('should fall back to dns.lookup when both resolvers fail', (done) => {
3587
mockResolve('resolve4', [], new Error('ENOTFOUND'));
3688
mockResolve('resolve6', [], new Error('ENOTFOUND'));

packages/bruno-requests/src/network/fast-lookup.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import dns from 'node:dns';
66
* Tries dns.resolve4 then dns.resolve6 (async, c-ares based),
77
* falls back to dns.lookup for /etc/hosts and mDNS hostnames.
88
*
9+
* .local hostnames are reserved for mDNS (RFC 6762) and must always use the
10+
* OS resolver (dns.lookup / getaddrinfo) — c-ares doesn't speak mDNS, so
11+
* dns.resolve4 can return bogus public-DNS results for .local names.
12+
*
913
* NOTE: `options.family` is not currently respected — the function always
1014
* tries IPv4 first regardless of the caller's preference. This is safe today
1115
* because Bruno's HTTP agents use the default family (0), but should be
@@ -16,6 +20,18 @@ export function fastLookup(
1620
options: dns.LookupOptions | undefined,
1721
callback: (err: Error | null, address: string | dns.LookupAddress[], family?: number) => void
1822
): void {
23+
// .local domains use mDNS (RFC 6762 — https://datatracker.ietf.org/doc/html/rfc6762)
24+
// which only the OS resolver understands. c-ares queries public DNS and may
25+
// return wrong results.
26+
// localhost is reserved (RFC 6761 — https://datatracker.ietf.org/doc/html/rfc6761)
27+
// and must always resolve via the OS — c-ares could return hijacked results.
28+
const lower = hostname.toLowerCase();
29+
if (lower.endsWith('.local') || lower === 'localhost' || lower.endsWith('.localhost')) {
30+
return dns.lookup(hostname, options ?? {}, (err, address, family) => {
31+
callback(err, address, family);
32+
});
33+
}
34+
1935
dns.resolve4(hostname, (err4, addresses4) => {
2036
if (!err4 && addresses4?.length) {
2137
return options?.all

0 commit comments

Comments
 (0)