DNS and networking
Workers and browsers have no node:dns and can't send UDP, so dns looks up records over HTTPS and returns the same results and error codes as node:dns/promises.
dnslooks up records with DNS-over-HTTPS.dns-packetencodes and decodes DNS messages in wire format.ipparses, compares, and classifies IPv4 and IPv6 addresses and CIDR blocks.rdaplooks up who holds a domain, an IP address, or an AS number.idnaconverts Unicode domain names to the ASCII form DNS uses.
Refuse to fetch internal addresses
A service that fetches URLs from its users must not connect to its own internal network. Resolve the host and check every address before you connect:
import * as Dns from '@portable/dns';
import * as Ip from '@portable/ip';
const resolver = Dns.create();
async function safeFetch(url: string, init: RequestInit = {}) {
// URL keeps the brackets around an IPv6 host, such as [::1].
const hostname = new URL(url).hostname.replace(/^\[|\]$/g, '');
const addresses = Ip.canParse(hostname)
? [hostname]
: await resolver.resolve4(hostname, {
signal: init.signal ?? undefined,
});
for (const address of addresses) {
if (Ip.range(Ip.unmap(address)) !== 'unicast') {
throw new Error(`Refusing to fetch ${address}`);
}
}
return fetch(url, init);
}This checks A records. Check AAAA records the same way with resolver.resolve6. The check doesn't cover redirects or a second DNS answer, so pass redirect: 'manual' and check each Location, and connect to the address you checked where your runtime allows it.