Repository: denoland/deno
Pull request: fix(ext/node): `dns.resolve6` compatibility

Closes #30936

The `compressIPv6` function is causing this issue. Maybe it was used in the past to compress IPv6 address returned from the op function, though I might be wrong about this.

Given this code:

```ts
import { resolve6 } from 'node:dns/promises';

const urls = [ 'api.chunk.gg', 'deno.land' ]

for (const url of urls) {
    const resolvedUrl = await resolve6(url);
    const denoResolved = await Deno.resolveDns(url, "AAAA");
    console.log({ url, resolvedUrl, denoResolved });
}
```

Deno 2.5.4 outputs:

```bash
{
  url: "api.chunk.gg",
  resolvedUrl: [ 
    "2a09:8280:1::39:5e5e:1",
    "2a09:8280:1::39:5e5e:", # Incorrect
  ],
  denoResolved: [ "2a09:8280:1::39:5e5e:0", "2a09:8280:1::39:5e5e:1" ]
}
{
  url: "deno.land",
  resolvedUrl: [ "2600:1901::6d85::" ], # Incorrect
  denoResolved: [ "2600:1901:0:6d85::" ]
}
```

Both `Deno.resolveDns` and the `resolve6` polyfill use `op_dns_resolve` op function, which returns a result that already matches with the Node.js' `resolve6`. The incorrect result is due to our `resolve6` polyfill applying the `compressIPv6` function to the result.

Changed files:
filename: ext/node/polyfills/internal_binding/cares_wrap.ts
patch: @@ -182,23 +182,6 @@ function fqdnToHostname(fqdn: string): string {
   return fqdn.replace(/\.$/, "");
 }
 
-function compressIPv6(address: string): string {
-  const formatted = address.replace(/\b(?:0+:){2,}/, ":");
-  const finalAddress = formatted
-    .split(":")
-    .map((octet) => {
-      if (octet.match(/^\d+\.\d+\.\d+\.\d+$/)) {
-        // decimal
-        return Number(octet.replaceAll(".", "")).toString(16);
-      }
-
-      return octet.replace(/\b0+/g, "");
-    })
-    .join(":");
-
-  return finalAddress;
-}
-
 export class ChannelWrap extends AsyncWrap implements ChannelWrapQuery {
   #servers: [string, number][] = [];
   #timeout: number;
@@ -293,7 +276,7 @@ export class ChannelWrap extends AsyncWrap implements ChannelWrapQuery {
         }),
         this.#query(name, "AAAA").then(({ ret }) => {
           (ret as string[]).forEach((record) =>
-            records.push({ type: "AAAA", address: compressIPv6(record) })
+            records.push({ type: "AAAA", address: record })
           );
         }),
         this.#query(name, "CAA").then(({ ret }) => {
@@ -406,11 +389,9 @@ export class ChannelWrap extends AsyncWrap implements ChannelWrapQuery {
       let recordsWithTtl;
       if (req.ttl) {
         recordsWithTtl = (ret as Deno.RecordWithTtl[]).map((val) => ({
-          address: compressIPv6(val?.data as string),
+          address: val?.data as string,
           ttl: val?.ttl,
         }));
-      } else {
-        ret = (ret as string[]).map((record) => compressIPv6(record));
       }
 
       req.oncomplete(code, recordsWithTtl ?? ret);

filename: tests/unit_node/net_test.ts
patch: @@ -5,6 +5,7 @@ import { assert, assertEquals } from "@std/assert";
 import * as path from "@std/path";
 import * as http from "node:http";
 import * as dns from "node:dns";
+import * as dnsPromises from "node:dns/promises";
 import util from "node:util";
 import console from "node:console";
 
@@ -283,3 +284,22 @@ Deno.test("[node/dns] dns.lookup (all=true) util promisify", async () => {
   assert(typeof result[0].address === "string");
   assert(typeof result[0].family === "number");
 });
+
+Deno.test("[node/dns] resolve IPv6 addresses", async () => {
+  const resolveResults = await dnsPromises.resolve("deno.land", "AAAA");
+  for (const address of resolveResults) {
+    await dnsPromises.lookup(address);
+  }
+
+  const resolve6Results = await dnsPromises.resolve6("deno.land");
+  for (const address of resolve6Results) {
+    await dnsPromises.lookup(address);
+  }
+
+  const resolve6ttlResults = await dnsPromises.resolve6("deno.land", {
+    ttl: true,
+  });
+  for (const { address } of resolve6ttlResults) {
+    await dnsPromises.lookup(address);
+  }
+});