1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-25 16:49:18 -05:00
denoland-deno/ext/node/polyfills/internal/dns/utils.ts
Bartek Iwańczuk d47147fb6a
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>
2023-02-14 17:38:45 +01:00

456 lines
12 KiB
TypeScript

// 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);
}