diff options
Diffstat (limited to 'ext/node/polyfills/internal/dns')
-rw-r--r-- | ext/node/polyfills/internal/dns/promises.ts | 511 | ||||
-rw-r--r-- | ext/node/polyfills/internal/dns/utils.ts | 456 |
2 files changed, 967 insertions, 0 deletions
diff --git a/ext/node/polyfills/internal/dns/promises.ts b/ext/node/polyfills/internal/dns/promises.ts new file mode 100644 index 000000000..e5e15dc87 --- /dev/null +++ b/ext/node/polyfills/internal/dns/promises.ts @@ -0,0 +1,511 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +import { + validateBoolean, + validateNumber, + validateOneOf, + validateString, +} from "internal:deno_node/polyfills/internal/validators.mjs"; +import { isIP } from "internal:deno_node/polyfills/internal/net.ts"; +import { + emitInvalidHostnameWarning, + getDefaultResolver, + getDefaultVerbatim, + isFamily, + isLookupOptions, + Resolver as CallbackResolver, + validateHints, +} from "internal:deno_node/polyfills/internal/dns/utils.ts"; +import type { + LookupAddress, + LookupAllOptions, + LookupOneOptions, + LookupOptions, + Records, + ResolveOptions, + ResolveWithTtlOptions, +} from "internal:deno_node/polyfills/internal/dns/utils.ts"; +import { + dnsException, + ERR_INVALID_ARG_TYPE, + ERR_INVALID_ARG_VALUE, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import { + ChannelWrapQuery, + getaddrinfo, + GetAddrInfoReqWrap, + QueryReqWrap, +} from "internal:deno_node/polyfills/internal_binding/cares_wrap.ts"; +import { toASCII } from "internal:deno_node/polyfills/internal/idna.ts"; + +function onlookup( + this: GetAddrInfoReqWrap, + err: number | null, + addresses: string[], +) { + if (err) { + this.reject(dnsException(err, "getaddrinfo", this.hostname)); + return; + } + + const family = this.family || isIP(addresses[0]); + this.resolve({ address: addresses[0], family }); +} + +function onlookupall( + this: GetAddrInfoReqWrap, + err: number | null, + addresses: string[], +) { + if (err) { + this.reject(dnsException(err, "getaddrinfo", this.hostname)); + + return; + } + + const family = this.family; + const parsedAddresses = []; + + for (let i = 0; i < addresses.length; i++) { + const address = addresses[i]; + parsedAddresses[i] = { + address, + family: family ? family : isIP(address), + }; + } + + this.resolve(parsedAddresses); +} + +function createLookupPromise( + family: number, + hostname: string, + all: boolean, + hints: number, + verbatim: boolean, +): Promise<void | LookupAddress | LookupAddress[]> { + return new Promise((resolve, reject) => { + if (!hostname) { + emitInvalidHostnameWarning(hostname); + resolve(all ? [] : { address: null, family: family === 6 ? 6 : 4 }); + + return; + } + + const matchedFamily = isIP(hostname); + + if (matchedFamily !== 0) { + const result = { address: hostname, family: matchedFamily }; + resolve(all ? [result] : result); + + return; + } + + const req = new GetAddrInfoReqWrap(); + + req.family = family; + req.hostname = hostname; + req.oncomplete = all ? onlookupall : onlookup; + req.resolve = resolve; + req.reject = reject; + + const err = getaddrinfo(req, toASCII(hostname), family, hints, verbatim); + + if (err) { + reject(dnsException(err, "getaddrinfo", hostname)); + } + }); +} + +const validFamilies = [0, 4, 6]; + +export function lookup( + hostname: string, + family: number, +): Promise<void | LookupAddress | LookupAddress[]>; +export function lookup( + hostname: string, + options: LookupOneOptions, +): Promise<void | LookupAddress | LookupAddress[]>; +export function lookup( + hostname: string, + options: LookupAllOptions, +): Promise<void | LookupAddress | LookupAddress[]>; +export function lookup( + hostname: string, + options: LookupOptions, +): Promise<void | LookupAddress | LookupAddress[]>; +export function lookup( + hostname: string, + options: unknown, +): Promise<void | LookupAddress | LookupAddress[]> { + let hints = 0; + let family = 0; + let all = false; + let verbatim = getDefaultVerbatim(); + + // Parse arguments + if (hostname) { + validateString(hostname, "hostname"); + } + + if (isFamily(options)) { + validateOneOf(options, "family", validFamilies); + family = options; + } else if (!isLookupOptions(options)) { + throw new ERR_INVALID_ARG_TYPE("options", ["integer", "object"], options); + } else { + if (options?.hints != null) { + validateNumber(options.hints, "options.hints"); + hints = options.hints >>> 0; + validateHints(hints); + } + + if (options?.family != null) { + validateOneOf(options.family, "options.family", validFamilies); + family = options.family; + } + + if (options?.all != null) { + validateBoolean(options.all, "options.all"); + all = options.all; + } + + if (options?.verbatim != null) { + validateBoolean(options.verbatim, "options.verbatim"); + verbatim = options.verbatim; + } + } + + return createLookupPromise(family, hostname, all, hints, verbatim); +} + +function onresolve( + this: QueryReqWrap, + err: number, + records: Records, + ttls?: number[], +) { + if (err) { + this.reject(dnsException(err, this.bindingName, this.hostname)); + + return; + } + + const parsedRecords = ttls && this.ttl + ? (records as string[]).map((address: string, index: number) => ({ + address, + ttl: ttls[index], + })) + : records; + + this.resolve(parsedRecords); +} + +function createResolverPromise( + resolver: Resolver, + bindingName: keyof ChannelWrapQuery, + hostname: string, + ttl: boolean, +) { + return new Promise((resolve, reject) => { + const req = new QueryReqWrap(); + + req.bindingName = bindingName; + req.hostname = hostname; + req.oncomplete = onresolve; + req.resolve = resolve; + req.reject = reject; + req.ttl = ttl; + + const err = resolver._handle[bindingName](req, toASCII(hostname)); + + if (err) { + reject(dnsException(err, bindingName, hostname)); + } + }); +} + +function resolver(bindingName: keyof ChannelWrapQuery) { + function query( + this: Resolver, + name: string, + options?: unknown, + ) { + validateString(name, "name"); + + const ttl = !!(options && (options as ResolveOptions).ttl); + + return createResolverPromise(this, bindingName, name, ttl); + } + + Object.defineProperty(query, "name", { value: bindingName }); + + return query; +} + +const resolveMap = Object.create(null); + +class Resolver extends CallbackResolver { + // deno-lint-ignore no-explicit-any + [resolveMethod: string]: any; +} + +Resolver.prototype.resolveAny = resolveMap.ANY = resolver("queryAny"); +Resolver.prototype.resolve4 = resolveMap.A = resolver("queryA"); +Resolver.prototype.resolve6 = resolveMap.AAAA = resolver("queryAaaa"); +Resolver.prototype.resolveCaa = resolveMap.CAA = resolver("queryCaa"); +Resolver.prototype.resolveCname = resolveMap.CNAME = resolver("queryCname"); +Resolver.prototype.resolveMx = resolveMap.MX = resolver("queryMx"); +Resolver.prototype.resolveNs = resolveMap.NS = resolver("queryNs"); +Resolver.prototype.resolveTxt = resolveMap.TXT = resolver("queryTxt"); +Resolver.prototype.resolveSrv = resolveMap.SRV = resolver("querySrv"); +Resolver.prototype.resolvePtr = resolveMap.PTR = resolver("queryPtr"); +Resolver.prototype.resolveNaptr = resolveMap.NAPTR = resolver("queryNaptr"); +Resolver.prototype.resolveSoa = resolveMap.SOA = resolver("querySoa"); +Resolver.prototype.reverse = resolver("getHostByAddr"); +Resolver.prototype.resolve = _resolve; + +function _resolve( + this: Resolver, + hostname: string, + rrtype?: string, +) { + let resolver; + + if (typeof hostname !== "string") { + throw new ERR_INVALID_ARG_TYPE("name", "string", hostname); + } + + if (rrtype !== undefined) { + validateString(rrtype, "rrtype"); + + resolver = resolveMap[rrtype]; + + if (typeof resolver !== "function") { + throw new ERR_INVALID_ARG_VALUE("rrtype", rrtype); + } + } else { + resolver = resolveMap.A; + } + + return Reflect.apply(resolver, this, [hostname]); +} + +// The Node implementation uses `bindDefaultResolver` to set the follow methods +// on `module.exports` bound to the current `defaultResolver`. We don't have +// the same ability in ESM but can simulate this (at some cost) by explicitly +// exporting these methods which dynamically bind to the default resolver when +// called. + +export function getServers(): string[] { + return Resolver.prototype.getServers.bind(getDefaultResolver())(); +} + +export function resolveAny( + hostname: string, +) { + return Resolver.prototype.resolveAny.bind(getDefaultResolver() as Resolver)( + hostname, + ); +} + +export function resolve4( + hostname: string, +): Promise<void>; +export function resolve4( + hostname: string, + options: ResolveWithTtlOptions, +): Promise<void>; +export function resolve4( + hostname: string, + options: ResolveOptions, +): Promise<void>; +export function resolve4(hostname: string, options?: unknown) { + return Resolver.prototype.resolve4.bind(getDefaultResolver() as Resolver)( + hostname, + options, + ); +} + +export function resolve6(hostname: string): Promise<void>; +export function resolve6( + hostname: string, + options: ResolveWithTtlOptions, +): Promise<void>; +export function resolve6( + hostname: string, + options: ResolveOptions, +): Promise<void>; +export function resolve6(hostname: string, options?: unknown) { + return Resolver.prototype.resolve6.bind(getDefaultResolver() as Resolver)( + hostname, + options, + ); +} + +export function resolveCaa( + hostname: string, +) { + return Resolver.prototype.resolveCaa.bind(getDefaultResolver() as Resolver)( + hostname, + ); +} + +export function resolveCname( + hostname: string, +) { + return Resolver.prototype.resolveCname.bind(getDefaultResolver() as Resolver)( + hostname, + ); +} + +export function resolveMx( + hostname: string, +) { + return Resolver.prototype.resolveMx.bind(getDefaultResolver() as Resolver)( + hostname, + ); +} + +export function resolveNs(hostname: string) { + return Resolver.prototype.resolveNs.bind(getDefaultResolver() as Resolver)( + hostname, + ); +} + +export function resolveTxt(hostname: string) { + return Resolver.prototype.resolveTxt.bind(getDefaultResolver() as Resolver)( + hostname, + ); +} + +export function resolveSrv(hostname: string) { + return Resolver.prototype.resolveSrv.bind(getDefaultResolver() as Resolver)( + hostname, + ); +} + +export function resolvePtr(hostname: string) { + return Resolver.prototype.resolvePtr.bind(getDefaultResolver() as Resolver)( + hostname, + ); +} + +export function resolveNaptr(hostname: string) { + return Resolver.prototype.resolveNaptr.bind(getDefaultResolver() as Resolver)( + hostname, + ); +} + +export function resolveSoa(hostname: string) { + return Resolver.prototype.resolveSoa.bind(getDefaultResolver() as Resolver)( + hostname, + ); +} + +export function reverse(ip: string) { + return Resolver.prototype.reverse.bind(getDefaultResolver() as Resolver)( + ip, + ); +} + +export function resolve( + hostname: string, +): Promise<void>; +export function resolve( + hostname: string, + rrtype: "A", +): Promise<void>; +export function resolve( + hostname: string, + rrtype: "AAAA", +): Promise<void>; +export function resolve( + hostname: string, + rrtype: "ANY", +): Promise<void>; +export function resolve( + hostname: string, + rrtype: "CNAME", +): Promise<void>; +export function resolve( + hostname: string, + rrtype: "MX", +): Promise<void>; +export function resolve( + hostname: string, + rrtype: "NAPTR", +): Promise<void>; +export function resolve( + hostname: string, + rrtype: "NS", +): Promise<void>; +export function resolve( + hostname: string, + rrtype: "PTR", +): Promise<void>; +export function resolve( + hostname: string, + rrtype: "SOA", +): Promise<void>; +export function resolve( + hostname: string, + rrtype: "SRV", +): Promise<void>; +export function resolve( + hostname: string, + rrtype: "TXT", +): Promise<void>; +export function resolve( + hostname: string, + rrtype: string, +): Promise<void>; +export function resolve(hostname: string, rrtype?: string) { + return Resolver.prototype.resolve.bind(getDefaultResolver() as Resolver)( + hostname, + rrtype, + ); +} + +export { Resolver }; + +export default { + lookup, + Resolver, + getServers, + resolveAny, + resolve4, + resolve6, + resolveCaa, + resolveCname, + resolveMx, + resolveNs, + resolveTxt, + resolveSrv, + resolvePtr, + resolveNaptr, + resolveSoa, + resolve, + reverse, +}; diff --git a/ext/node/polyfills/internal/dns/utils.ts b/ext/node/polyfills/internal/dns/utils.ts new file mode 100644 index 000000000..0afd10617 --- /dev/null +++ b/ext/node/polyfills/internal/dns/utils.ts @@ -0,0 +1,456 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +import { getOptionValue } from "internal:deno_node/polyfills/internal/options.ts"; +import { emitWarning } from "internal:deno_node/polyfills/process.ts"; +import { + AI_ADDRCONFIG, + AI_ALL, + AI_V4MAPPED, +} from "internal:deno_node/polyfills/internal_binding/ares.ts"; +import { + ChannelWrap, + strerror, +} from "internal:deno_node/polyfills/internal_binding/cares_wrap.ts"; +import { + ERR_DNS_SET_SERVERS_FAILED, + ERR_INVALID_ARG_VALUE, + ERR_INVALID_IP_ADDRESS, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import type { ErrnoException } from "internal:deno_node/polyfills/internal/errors.ts"; +import { + validateArray, + validateInt32, + validateOneOf, + validateString, +} from "internal:deno_node/polyfills/internal/validators.mjs"; +import { isIP } from "internal:deno_node/polyfills/internal/net.ts"; + +export interface LookupOptions { + family?: number | undefined; + hints?: number | undefined; + all?: boolean | undefined; + verbatim?: boolean | undefined; +} + +export interface LookupOneOptions extends LookupOptions { + all?: false | undefined; +} + +export interface LookupAllOptions extends LookupOptions { + all: true; +} + +export interface LookupAddress { + address: string | null; + family: number; +} + +export function isLookupOptions( + options: unknown, +): options is LookupOptions | undefined { + return typeof options === "object" || typeof options === "undefined"; +} + +export function isLookupCallback( + options: unknown, +): options is (...args: unknown[]) => void { + return typeof options === "function"; +} + +export function isFamily(options: unknown): options is number { + return typeof options === "number"; +} + +export interface ResolveOptions { + ttl?: boolean; +} + +export interface ResolveWithTtlOptions extends ResolveOptions { + ttl: true; +} + +export interface RecordWithTtl { + address: string; + ttl: number; +} + +export interface AnyARecord extends RecordWithTtl { + type: "A"; +} + +export interface AnyAaaaRecord extends RecordWithTtl { + type: "AAAA"; +} + +export interface CaaRecord { + critial: number; + issue?: string | undefined; + issuewild?: string | undefined; + iodef?: string | undefined; + contactemail?: string | undefined; + contactphone?: string | undefined; +} + +export interface MxRecord { + priority: number; + exchange: string; +} + +export interface AnyMxRecord extends MxRecord { + type: "MX"; +} + +export interface NaptrRecord { + flags: string; + service: string; + regexp: string; + replacement: string; + order: number; + preference: number; +} + +export interface AnyNaptrRecord extends NaptrRecord { + type: "NAPTR"; +} + +export interface SoaRecord { + nsname: string; + hostmaster: string; + serial: number; + refresh: number; + retry: number; + expire: number; + minttl: number; +} + +export interface AnySoaRecord extends SoaRecord { + type: "SOA"; +} + +export interface SrvRecord { + priority: number; + weight: number; + port: number; + name: string; +} + +export interface AnySrvRecord extends SrvRecord { + type: "SRV"; +} + +export interface AnyTxtRecord { + type: "TXT"; + entries: string[]; +} + +export interface AnyNsRecord { + type: "NS"; + value: string; +} + +export interface AnyPtrRecord { + type: "PTR"; + value: string; +} + +export interface AnyCnameRecord { + type: "CNAME"; + value: string; +} + +export type AnyRecord = + | AnyARecord + | AnyAaaaRecord + | AnyCnameRecord + | AnyMxRecord + | AnyNaptrRecord + | AnyNsRecord + | AnyPtrRecord + | AnySoaRecord + | AnySrvRecord + | AnyTxtRecord; + +export type Records = + | string[] + | AnyRecord[] + | MxRecord[] + | NaptrRecord[] + | SoaRecord + | SrvRecord[] + | string[]; + +export type ResolveCallback = ( + err: ErrnoException | null, + addresses: Records, +) => void; + +export function isResolveCallback( + callback: unknown, +): callback is ResolveCallback { + return typeof callback === "function"; +} + +const IANA_DNS_PORT = 53; +const IPv6RE = /^\[([^[\]]*)\]/; +const addrSplitRE = /(^.+?)(?::(\d+))?$/; + +export function validateTimeout(options?: { timeout?: number }) { + const { timeout = -1 } = { ...options }; + validateInt32(timeout, "options.timeout", -1, 2 ** 31 - 1); + return timeout; +} + +export function validateTries(options?: { tries?: number }) { + const { tries = 4 } = { ...options }; + validateInt32(tries, "options.tries", 1, 2 ** 31 - 1); + return tries; +} + +export interface ResolverOptions { + timeout?: number | undefined; + /** + * @default 4 + */ + tries?: number; +} + +/** + * An independent resolver for DNS requests. + * + * Creating a new resolver uses the default server settings. Setting + * the servers used for a resolver using `resolver.setServers()` does not affect + * other resolvers: + * + * ```js + * const { Resolver } = require('dns'); + * const resolver = new Resolver(); + * resolver.setServers(['4.4.4.4']); + * + * // This request will use the server at 4.4.4.4, independent of global settings. + * resolver.resolve4('example.org', (err, addresses) => { + * // ... + * }); + * ``` + * + * The following methods from the `dns` module are available: + * + * - `resolver.getServers()` + * - `resolver.resolve()` + * - `resolver.resolve4()` + * - `resolver.resolve6()` + * - `resolver.resolveAny()` + * - `resolver.resolveCaa()` + * - `resolver.resolveCname()` + * - `resolver.resolveMx()` + * - `resolver.resolveNaptr()` + * - `resolver.resolveNs()` + * - `resolver.resolvePtr()` + * - `resolver.resolveSoa()` + * - `resolver.resolveSrv()` + * - `resolver.resolveTxt()` + * - `resolver.reverse()` + * - `resolver.setServers()` + */ +export class Resolver { + _handle!: ChannelWrap; + + constructor(options?: ResolverOptions) { + const timeout = validateTimeout(options); + const tries = validateTries(options); + this._handle = new ChannelWrap(timeout, tries); + } + + cancel() { + this._handle.cancel(); + } + + getServers(): string[] { + return this._handle.getServers().map((val: [string, number]) => { + if (!val[1] || val[1] === IANA_DNS_PORT) { + return val[0]; + } + + const host = isIP(val[0]) === 6 ? `[${val[0]}]` : val[0]; + return `${host}:${val[1]}`; + }); + } + + setServers(servers: ReadonlyArray<string>) { + validateArray(servers, "servers"); + + // Cache the original servers because in the event of an error while + // setting the servers, c-ares won't have any servers available for + // resolution. + const orig = this._handle.getServers(); + const newSet: [number, string, number][] = []; + + servers.forEach((serv, index) => { + validateString(serv, `servers[${index}]`); + let ipVersion = isIP(serv); + + if (ipVersion !== 0) { + return newSet.push([ipVersion, serv, IANA_DNS_PORT]); + } + + const match = serv.match(IPv6RE); + + // Check for an IPv6 in brackets. + if (match) { + ipVersion = isIP(match[1]); + + if (ipVersion !== 0) { + const port = Number.parseInt(serv.replace(addrSplitRE, "$2")) || + IANA_DNS_PORT; + + return newSet.push([ipVersion, match[1], port]); + } + } + + // addr::port + const addrSplitMatch = serv.match(addrSplitRE); + + if (addrSplitMatch) { + const hostIP = addrSplitMatch[1]; + const port = addrSplitMatch[2] || `${IANA_DNS_PORT}`; + + ipVersion = isIP(hostIP); + + if (ipVersion !== 0) { + return newSet.push([ipVersion, hostIP, Number.parseInt(port)]); + } + } + + throw new ERR_INVALID_IP_ADDRESS(serv); + }); + + const errorNumber = this._handle.setServers(newSet); + + if (errorNumber !== 0) { + // Reset the servers to the old servers, because ares probably unset them. + this._handle.setServers(orig.join(",")); + const err = strerror(errorNumber); + + throw new ERR_DNS_SET_SERVERS_FAILED(err, servers.toString()); + } + } + + /** + * The resolver instance will send its requests from the specified IP address. + * This allows programs to specify outbound interfaces when used on multi-homed + * systems. + * + * If a v4 or v6 address is not specified, it is set to the default, and the + * operating system will choose a local address automatically. + * + * The resolver will use the v4 local address when making requests to IPv4 DNS + * servers, and the v6 local address when making requests to IPv6 DNS servers. + * The `rrtype` of resolution requests has no impact on the local address used. + * + * @param [ipv4='0.0.0.0'] A string representation of an IPv4 address. + * @param [ipv6='::0'] A string representation of an IPv6 address. + */ + setLocalAddress(ipv4: string, ipv6?: string) { + validateString(ipv4, "ipv4"); + + if (ipv6 !== undefined) { + validateString(ipv6, "ipv6"); + } + + this._handle.setLocalAddress(ipv4, ipv6); + } +} + +let defaultResolver = new Resolver(); + +export function getDefaultResolver(): Resolver { + return defaultResolver; +} + +export function setDefaultResolver<T extends Resolver>(resolver: T) { + defaultResolver = resolver; +} + +export function validateHints(hints: number) { + if ((hints & ~(AI_ADDRCONFIG | AI_ALL | AI_V4MAPPED)) !== 0) { + throw new ERR_INVALID_ARG_VALUE("hints", hints, "is invalid"); + } +} + +let invalidHostnameWarningEmitted = false; + +export function emitInvalidHostnameWarning(hostname: string) { + if (invalidHostnameWarningEmitted) { + return; + } + + invalidHostnameWarningEmitted = true; + + emitWarning( + `The provided hostname "${hostname}" is not a valid ` + + "hostname, and is supported in the dns module solely for compatibility.", + "DeprecationWarning", + "DEP0118", + ); +} + +let dnsOrder = getOptionValue("--dns-result-order") || "ipv4first"; + +export function getDefaultVerbatim() { + switch (dnsOrder) { + case "verbatim": { + return true; + } + case "ipv4first": { + return false; + } + default: { + return false; + } + } +} + +/** + * Set the default value of `verbatim` in `lookup` and `dnsPromises.lookup()`. + * The value could be: + * + * - `ipv4first`: sets default `verbatim` `false`. + * - `verbatim`: sets default `verbatim` `true`. + * + * The default is `ipv4first` and `setDefaultResultOrder` have higher + * priority than `--dns-result-order`. When using `worker threads`, + * `setDefaultResultOrder` from the main thread won't affect the default + * dns orders in workers. + * + * @param order must be `'ipv4first'` or `'verbatim'`. + */ +export function setDefaultResultOrder(order: "ipv4first" | "verbatim") { + validateOneOf(order, "dnsOrder", ["verbatim", "ipv4first"]); + dnsOrder = order; +} + +export function defaultResolverSetServers(servers: string[]) { + const resolver = new Resolver(); + + resolver.setServers(servers); + setDefaultResolver(resolver); +} |