mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-11 09:51:30 +00:00
fix: use OS resolver for .local hostnames to fix mDNS resolution (#8072)
This commit is contained in:
@@ -31,6 +31,58 @@ describe('fastLookup', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should use dns.lookup directly for .local hostnames (mDNS)', (done) => {
|
||||
mockResolve('resolve4', ['192.0.78.134']); // bogus public-DNS result
|
||||
mockLookup('192.168.33.254', 4); // correct mDNS result
|
||||
|
||||
fastLookup('fhir.local', {}, (err, address, family) => {
|
||||
expect(err).toBeNull();
|
||||
expect(address).toBe('192.168.33.254');
|
||||
expect(family).toBe(4);
|
||||
expect(dns.resolve4).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should use dns.lookup for mixed-case .LOCAL hostnames', (done) => {
|
||||
mockResolve('resolve4', ['192.0.78.134']);
|
||||
mockLookup('192.168.33.254', 4);
|
||||
|
||||
fastLookup('FHIR.LOCAL', {}, (err, address, family) => {
|
||||
expect(err).toBeNull();
|
||||
expect(address).toBe('192.168.33.254');
|
||||
expect(family).toBe(4);
|
||||
expect(dns.resolve4).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should use dns.lookup directly for localhost (RFC 6761)', (done) => {
|
||||
mockResolve('resolve4', ['93.184.216.34']); // hijacked result
|
||||
mockLookup('127.0.0.1', 4);
|
||||
|
||||
fastLookup('localhost', {}, (err, address, family) => {
|
||||
expect(err).toBeNull();
|
||||
expect(address).toBe('127.0.0.1');
|
||||
expect(family).toBe(4);
|
||||
expect(dns.resolve4).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should use dns.lookup for .localhost subdomains', (done) => {
|
||||
mockResolve('resolve4', ['93.184.216.34']);
|
||||
mockLookup('127.0.0.1', 4);
|
||||
|
||||
fastLookup('api.localhost', {}, (err, address, family) => {
|
||||
expect(err).toBeNull();
|
||||
expect(address).toBe('127.0.0.1');
|
||||
expect(family).toBe(4);
|
||||
expect(dns.resolve4).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fall back to dns.lookup when both resolvers fail', (done) => {
|
||||
mockResolve('resolve4', [], new Error('ENOTFOUND'));
|
||||
mockResolve('resolve6', [], new Error('ENOTFOUND'));
|
||||
|
||||
@@ -6,6 +6,10 @@ import dns from 'node:dns';
|
||||
* Tries dns.resolve4 then dns.resolve6 (async, c-ares based),
|
||||
* falls back to dns.lookup for /etc/hosts and mDNS hostnames.
|
||||
*
|
||||
* .local hostnames are reserved for mDNS (RFC 6762) and must always use the
|
||||
* OS resolver (dns.lookup / getaddrinfo) — c-ares doesn't speak mDNS, so
|
||||
* dns.resolve4 can return bogus public-DNS results for .local names.
|
||||
*
|
||||
* NOTE: `options.family` is not currently respected — the function always
|
||||
* tries IPv4 first regardless of the caller's preference. This is safe today
|
||||
* because Bruno's HTTP agents use the default family (0), but should be
|
||||
@@ -16,6 +20,18 @@ export function fastLookup(
|
||||
options: dns.LookupOptions | undefined,
|
||||
callback: (err: Error | null, address: string | dns.LookupAddress[], family?: number) => void
|
||||
): void {
|
||||
// .local domains use mDNS (RFC 6762 — https://datatracker.ietf.org/doc/html/rfc6762)
|
||||
// which only the OS resolver understands. c-ares queries public DNS and may
|
||||
// return wrong results.
|
||||
// localhost is reserved (RFC 6761 — https://datatracker.ietf.org/doc/html/rfc6761)
|
||||
// and must always resolve via the OS — c-ares could return hijacked results.
|
||||
const lower = hostname.toLowerCase();
|
||||
if (lower.endsWith('.local') || lower === 'localhost' || lower.endsWith('.localhost')) {
|
||||
return dns.lookup(hostname, options ?? {}, (err, address, family) => {
|
||||
callback(err, address, family);
|
||||
});
|
||||
}
|
||||
|
||||
dns.resolve4(hostname, (err4, addresses4) => {
|
||||
if (!err4 && addresses4?.length) {
|
||||
return options?.all
|
||||
|
||||
Reference in New Issue
Block a user