summaryrefslogtreecommitdiff
path: root/ext/node/polyfills/internal/dns
diff options
context:
space:
mode:
Diffstat (limited to 'ext/node/polyfills/internal/dns')
-rw-r--r--ext/node/polyfills/internal/dns/promises.ts511
-rw-r--r--ext/node/polyfills/internal/dns/utils.ts456
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);
+}