Skip to main content
Module

std/node/internal/dns/utils.ts

Deno standard library
Go to Latest
File
// 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 "../options.ts";import { emitWarning } from "../../process.ts";import { AI_ADDRCONFIG, AI_ALL, AI_V4MAPPED,} from "../../internal_binding/ares.ts";import { ChannelWrap, strerror } from "../../internal_binding/cares_wrap.ts";import { ERR_DNS_SET_SERVERS_FAILED, ERR_INVALID_ARG_VALUE, ERR_INVALID_IP_ADDRESS,} from "../errors.ts";import type { ErrnoException } from "../errors.ts";import { validateArray, validateInt32, validateOneOf, validateString,} from "../validators.mjs";import { isIP } from "../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);}