From 1d633228c1cb82941513fd860e16d49447ae80c6 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Thu, 30 Jan 2025 09:30:34 +0900 Subject: [PATCH] fix(ext/node): better dns.lookup compatibility --- Cargo.lock | 1 + ext/node/Cargo.toml | 1 + ext/node/lib.rs | 1 + ext/node/ops/dns.rs | 50 +++++++++++++++++++ ext/node/ops/mod.rs | 1 + .../polyfills/internal_binding/cares_wrap.ts | 41 +++++++-------- 6 files changed, 73 insertions(+), 22 deletions(-) create mode 100644 ext/node/ops/dns.rs diff --git a/Cargo.lock b/Cargo.lock index 49a668dee7570f..1ac47dc6838216 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2136,6 +2136,7 @@ dependencies = [ "thiserror 2.0.3", "tokio", "tokio-eld", + "tower-service", "url", "webpki-root-certs", "winapi", diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index 0e266c89a5cbf1..fdfa8831eda42a 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -98,6 +98,7 @@ sys_traits = { workspace = true, features = ["real", "winapi", "libc"] } thiserror.workspace = true tokio.workspace = true tokio-eld = "0.2" +tower-service.workspace = true url.workspace = true webpki-root-certs.workspace = true winapi.workspace = true diff --git a/ext/node/lib.rs b/ext/node/lib.rs index 68d16bfd6a2b80..9359f55980e8bc 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -312,6 +312,7 @@ deno_core::extension!(deno_node, ops::crypto::x509::op_node_x509_get_serial_number, ops::crypto::x509::op_node_x509_key_usage, ops::crypto::x509::op_node_x509_public_key, + ops::dns::op_getaddrinfo, ops::fs::op_node_fs_exists_sync

, ops::fs::op_node_fs_exists

, ops::fs::op_node_cp_sync

, diff --git a/ext/node/ops/dns.rs b/ext/node/ops/dns.rs new file mode 100644 index 00000000000000..5342f7897dec66 --- /dev/null +++ b/ext/node/ops/dns.rs @@ -0,0 +1,50 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::str::FromStr; + +use deno_core::op2; +use hyper_util::client::legacy::connect::dns::GaiResolver; +use hyper_util::client::legacy::connect::dns::Name; +use serde::Serialize; +use tower_service::Service; + +#[derive(Serialize, Debug)] +#[serde(rename_all = "camelCase")] +struct GetAddrInfoResult { + family: usize, + address: String, +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(generic)] +#[error("Could not resolve the hostname '{hostname}'")] +pub struct GetAddrInfoError { + hostname: String, +} + +#[op2(async, stack_trace)] +#[serde] +pub async fn op_getaddrinfo( + #[string] hostname: String, +) -> Result, GetAddrInfoError> { + let mut resolver = GaiResolver::new(); + let name = Name::from_str(&hostname).map_err(|_| GetAddrInfoError { + hostname: hostname.clone(), + })?; + resolver + .call(name) + .await + .map_err(|_| GetAddrInfoError { hostname }) + .map(|addrs| { + addrs + .into_iter() + .map(|addr| GetAddrInfoResult { + family: match addr { + std::net::SocketAddr::V4(_) => 4, + std::net::SocketAddr::V6(_) => 6, + }, + address: addr.ip().to_string(), + }) + .collect::>() + }) +} diff --git a/ext/node/ops/mod.rs b/ext/node/ops/mod.rs index 8dae6aeff68259..a251ec416cb47e 100644 --- a/ext/node/ops/mod.rs +++ b/ext/node/ops/mod.rs @@ -3,6 +3,7 @@ pub mod blocklist; pub mod buffer; pub mod crypto; +pub mod dns; pub mod fs; pub mod http; pub mod http2; diff --git a/ext/node/polyfills/internal_binding/cares_wrap.ts b/ext/node/polyfills/internal_binding/cares_wrap.ts index 6a864e5855c48c..fc52ac11dde8ce 100644 --- a/ext/node/polyfills/internal_binding/cares_wrap.ts +++ b/ext/node/polyfills/internal_binding/cares_wrap.ts @@ -28,7 +28,6 @@ // deno-lint-ignore-file prefer-primordials import type { ErrnoException } from "ext:deno_node/internal/errors.ts"; -import { isIPv4 } from "ext:deno_node/internal/net.ts"; import { codeMap } from "ext:deno_node/internal_binding/uv.ts"; import { AsyncWrap, @@ -36,6 +35,7 @@ import { } from "ext:deno_node/internal_binding/async_wrap.ts"; import { ares_strerror } from "ext:deno_node/internal_binding/ares.ts"; import { notImplemented } from "ext:deno_node/_utils.ts"; +import { op_getaddrinfo } from "ext:core/ops"; interface LookupAddress { address: string; @@ -67,38 +67,31 @@ export function getaddrinfo( _hints: number, verbatim: boolean, ): number { - const addresses: string[] = []; + type Addr = { + address: string; + family: number; + }; + let addresses: Addr[] = []; // TODO(cmorten): use hints // REF: https://nodejs.org/api/dns.html#dns_supported_getaddrinfo_flags - 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)); - }) - ), - ); + try { + addresses.push(...await op_getaddrinfo(hostname)); + } catch { + // pass + } const error = addresses.length ? 0 : codeMap.get("EAI_NODATA")!; // TODO(cmorten): needs work // REF: https://github.com/nodejs/node/blob/master/src/cares_wrap.cc#L1444 if (!verbatim) { - addresses.sort((a: string, b: string): number => { - if (isIPv4(a)) { + addresses.sort((a: Addr, b: Addr): number => { + if (a.family === 4 && b.family === 6) { return -1; - } else if (isIPv4(b)) { + } else if (a.family === 6 && b.family === 4) { return 1; } @@ -106,7 +99,11 @@ export function getaddrinfo( }); } - req.oncomplete(error, addresses); + if (family === 4 || family === 6) { + addresses = addresses.filter((addr) => addr.family === family); + } + + req.oncomplete(error, addresses.map((addr) => addr.address)); })(); return 0;