summaryrefslogtreecommitdiff
path: root/ext/node/polyfills/internal/dns/utils.ts
diff options
context:
space:
mode:
authorBartek IwaƄczuk <biwanczuk@gmail.com>2023-02-14 17:38:45 +0100
committerGitHub <noreply@github.com>2023-02-14 17:38:45 +0100
commitd47147fb6ad229b1c039aff9d0959b6e281f4df5 (patch)
tree6e9e790f2b9bc71b5f0c9c7e64b95cae31579d58 /ext/node/polyfills/internal/dns/utils.ts
parent1d00bbe47e2ca14e2d2151518e02b2324461a065 (diff)
feat(ext/node): embed std/node into the snapshot (#17724)
This commit moves "deno_std/node" in "ext/node" crate. The code is transpiled and snapshotted during the build process. During the first pass a minimal amount of work was done to create the snapshot, a lot of code in "ext/node" depends on presence of "Deno" global. This code will be gradually fixed in the follow up PRs to migrate it to import relevant APIs from "internal:" modules. Currently the code from snapshot is not used in any way, and all Node/npm compatibility still uses code from "https://deno.land/std/node" (or from the location specified by "DENO_NODE_COMPAT_URL"). This will also be handled in a follow up PRs. --------- Co-authored-by: crowlkats <crowlkats@toaxl.com> Co-authored-by: Divy Srivastava <dj.srivastava23@gmail.com> Co-authored-by: Yoshiya Hinosawa <stibium121@gmail.com>
Diffstat (limited to 'ext/node/polyfills/internal/dns/utils.ts')
-rw-r--r--ext/node/polyfills/internal/dns/utils.ts456
1 files changed, 456 insertions, 0 deletions
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);
+}