Skip to content

Commit

Permalink
fix(ext/node): better dns.lookup compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
kt3k committed Feb 3, 2025
1 parent 08cc221 commit 1d63322
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 22 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ext/node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions ext/node/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<P>,
ops::fs::op_node_fs_exists<P>,
ops::fs::op_node_cp_sync<P>,
Expand Down
50 changes: 50 additions & 0 deletions ext/node/ops/dns.rs
Original file line number Diff line number Diff line change
@@ -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<Vec<GetAddrInfoResult>, 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::<Vec<_>>()
})
}
1 change: 1 addition & 0 deletions ext/node/ops/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
41 changes: 19 additions & 22 deletions ext/node/polyfills/internal_binding/cares_wrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@
// 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,
providerType,
} 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;
Expand Down Expand Up @@ -67,46 +67,43 @@ 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;
}

return 0;
});
}

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;
Expand Down

0 comments on commit 1d63322

Please sign in to comment.