import type { ErrnoException } from "../internal/errors.ts";import { isIPv4 } from "../internal/net.ts";import { codeMap } from "./uv.ts";import { AsyncWrap, providerType } from "./async_wrap.ts";import { ares_strerror } from "./ares.ts";import { notImplemented } from "../_utils.ts";import { isWindows } from "../../_util/os.ts";
interface LookupAddress { address: string; family: number;}
export class GetAddrInfoReqWrap extends AsyncWrap { family!: number; hostname!: string;
callback!: ( err: ErrnoException | null, addressOrAddresses?: string | LookupAddress[] | null, family?: number, ) => void; resolve!: (addressOrAddresses: LookupAddress | LookupAddress[]) => void; reject!: (err: ErrnoException | null) => void; oncomplete!: (err: number | null, addresses: string[]) => void;
constructor() { super(providerType.GETADDRINFOREQWRAP); }}
export function getaddrinfo( req: GetAddrInfoReqWrap, hostname: string, family: number, _hints: number, verbatim: boolean,): number { let addresses: string[] = [];
const recordTypes: ("A" | "AAAA")[] = [];
if (family === 0 || family === 4) { recordTypes.push("A"); } if (family === 0 || family === 6) { recordTypes.push("AAAA"); }
(async () => { await Promise.allSettled( recordTypes.map((recordType) => Deno.resolveDns(hostname, recordType).then((records) => { records.forEach((record) => addresses.push(record)); }) ), );
const error = addresses.length ? 0 : codeMap.get("EAI_NODATA")!;
if (!verbatim) { addresses.sort((a: string, b: string): number => { if (isIPv4(a)) { return -1; } else if (isIPv4(b)) { return 1; }
return 0; }); }
if (isWindows && hostname === "localhost") { addresses = addresses.filter((address) => isIPv4(address)); }
req.oncomplete(error, addresses); })();
return 0;}
export class QueryReqWrap extends AsyncWrap { bindingName!: string; hostname!: string; ttl!: boolean;
callback!: ( err: ErrnoException | null, records?: any, ) => void; resolve!: (records: any) => void; reject!: (err: ErrnoException | null) => void; oncomplete!: ( err: number, records: any, ttls?: number[], ) => void;
constructor() { super(providerType.QUERYWRAP); }}
export interface ChannelWrapQuery { queryAny(req: QueryReqWrap, name: string): number; queryA(req: QueryReqWrap, name: string): number; queryAaaa(req: QueryReqWrap, name: string): number; queryCaa(req: QueryReqWrap, name: string): number; queryCname(req: QueryReqWrap, name: string): number; queryMx(req: QueryReqWrap, name: string): number; queryNs(req: QueryReqWrap, name: string): number; queryTxt(req: QueryReqWrap, name: string): number; querySrv(req: QueryReqWrap, name: string): number; queryPtr(req: QueryReqWrap, name: string): number; queryNaptr(req: QueryReqWrap, name: string): number; querySoa(req: QueryReqWrap, name: string): number; getHostByAddr(req: QueryReqWrap, name: string): number;}
export class ChannelWrap extends AsyncWrap implements ChannelWrapQuery { #timeout: number; #tries: number;
constructor(timeout: number, tries: number) { super(providerType.DNSCHANNEL);
this.#timeout = timeout; this.#tries = tries; }
async #query(query: string, recordType: Deno.RecordType) {
let ret: Awaited<ReturnType<typeof Deno.resolveDns>> = []; let code = 0;
try { ret = await Deno.resolveDns(query, recordType); } catch (e) { if (e instanceof Deno.errors.NotFound) { code = codeMap.get("EAI_NODATA")!; } else { code = codeMap.get("UNKNOWN")!; } }
return { code, ret }; }
queryAny(req: QueryReqWrap, name: string): number { (async () => { const records: { type: Deno.RecordType; [key: string]: unknown }[] = [];
await Promise.allSettled([ this.#query(name, "A").then(({ ret }) => { ret.forEach((record) => records.push({ type: "A", address: record })); }), this.#query(name, "AAAA").then(({ ret }) => { ret.forEach((record) => records.push({ type: "AAAA", address: record }) ); }), this.#query(name, "CNAME").then(({ ret }) => { ret.forEach((record) => records.push({ type: "CNAME", value: record }) ); }), this.#query(name, "MX").then(({ ret }) => { (ret as Deno.MXRecord[]).forEach(({ preference, exchange }) => records.push({ type: "MX", priority: preference, exchange }) ); }), this.#query(name, "PTR").then(({ ret }) => { ret.forEach((record) => records.push({ type: "PTR", value: record })); }), this.#query(name, "SRV").then(({ ret }) => { (ret as Deno.SRVRecord[]).forEach( ({ priority, weight, port, target }) => records.push({ type: "SRV", priority, weight, port, name: target, }), ); }), this.#query(name, "TXT").then(({ ret }) => { ret.forEach((record) => records.push({ type: "TXT", entries: record }) ); }), ]);
const err = records.length ? 0 : codeMap.get("EAI_NODATA")!;
req.oncomplete(err, records); })();
return 0; }
queryA(req: QueryReqWrap, name: string): number { this.#query(name, "A").then(({ code, ret }) => { req.oncomplete(code, ret); });
return 0; }
queryAaaa(req: QueryReqWrap, name: string): number { this.#query(name, "AAAA").then(({ code, ret }) => { req.oncomplete(code, ret); });
return 0; }
queryCaa(_req: QueryReqWrap, _name: string): number { notImplemented("cares.ChannelWrap.prototype.queryCaa"); }
queryCname(req: QueryReqWrap, name: string): number { this.#query(name, "CNAME").then(({ code, ret }) => { req.oncomplete(code, ret); });
return 0; }
queryMx(req: QueryReqWrap, name: string): number { this.#query(name, "MX").then(({ code, ret }) => { const records = (ret as Deno.MXRecord[]).map( ({ preference, exchange }) => ({ priority: preference, exchange, }), );
req.oncomplete(code, records); });
return 0; }
queryNs(_req: QueryReqWrap, _name: string): number { notImplemented("cares.ChannelWrap.prototype.queryNs"); }
queryTxt(req: QueryReqWrap, name: string): number { this.#query(name, "TXT").then(({ code, ret }) => { req.oncomplete(code, ret); });
return 0; }
querySrv(req: QueryReqWrap, name: string): number { this.#query(name, "SRV").then(({ code, ret }) => { const records = (ret as Deno.SRVRecord[]).map( ({ priority, weight, port, target }) => ({ priority, weight, port, name: target, }), );
req.oncomplete(code, records); });
return 0; }
queryPtr(req: QueryReqWrap, name: string): number { this.#query(name, "PTR").then(({ code, ret }) => { req.oncomplete(code, ret); });
return 0; }
queryNaptr(_req: QueryReqWrap, _name: string): number { notImplemented("cares.ChannelWrap.prototype.queryNaptr"); }
querySoa(_req: QueryReqWrap, _name: string): number { notImplemented("cares.ChannelWrap.prototype.querySoa"); }
getHostByAddr(_req: QueryReqWrap, _name: string): number { notImplemented("cares.ChannelWrap.prototype.getHostByAddr"); }
getServers(): [string, number][] { notImplemented("cares.ChannelWrap.prototype.getServers"); }
setServers(_servers: string | [number, string, number][]): number { notImplemented("cares.ChannelWrap.prototype.setServers"); }
setLocalAddress(_addr0: string, _addr1?: string): void { notImplemented("cares.ChannelWrap.prototype.setLocalAddress"); }
cancel() { notImplemented("cares.ChannelWrap.prototype.cancel"); }}
const DNS_ESETSRVPENDING = -1000;const EMSG_ESETSRVPENDING = "There are pending queries.";
export function strerror(code: number) { return code === DNS_ESETSRVPENDING ? EMSG_ESETSRVPENDING : ares_strerror(code);}