aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Ciro Spaciari <ciro.spaciari@gmail.com> 2023-01-22 19:31:08 -0300
committerGravatar GitHub <noreply@github.com> 2023-01-22 14:31:08 -0800
commit481dbf7c6e1a900136f6988218f3613720b31f97 (patch)
treece58bfa8bf70c4462137b450d567903247fb0956
parent8d692f1511f4c017d0fcef94032f4d89d404f2a8 (diff)
downloadbun-481dbf7c6e1a900136f6988218f3613720b31f97.tar.gz
bun-481dbf7c6e1a900136f6988218f3613720b31f97.tar.zst
bun-481dbf7c6e1a900136f6988218f3613720b31f97.zip
feat(dns) resolveSrv (#1870)
* initial steps * in-progress resolveSrv support * fix memory leak and ZigGlobalObject * promise.resolve + fix priority and weight mistake * fix node dns export * little better test * add poll_ref to keep alive loop in callback for resolveSrv and lookup * add test for error case in resolveSrc * fix Bun.dns.resolveSrv type
-rw-r--r--packages/bun-error/package-lock.json438
-rw-r--r--packages/bun-types/dns.d.ts16
-rw-r--r--packages/bun-types/dns/promises.d.ts8
-rw-r--r--src/bun.js/api/bun/dns_resolver.zig366
-rw-r--r--src/bun.js/base.zig15
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.cpp5
-rw-r--r--src/bun.js/bindings/bindings.zig2
-rw-r--r--src/bun.js/node-dns.exports.js38
-rw-r--r--src/deps/c_ares.zig238
-rw-r--r--src/deps/uws.zig12
-rw-r--r--test/bun.js/node-dns.test.js21
-rw-r--r--test/bun.js/resolve-dns.test.ts2
12 files changed, 1129 insertions, 32 deletions
diff --git a/packages/bun-error/package-lock.json b/packages/bun-error/package-lock.json
index 8c97c5c13..308205754 100644
--- a/packages/bun-error/package-lock.json
+++ b/packages/bun-error/package-lock.json
@@ -1,8 +1,444 @@
{
"name": "bun-error",
"version": "1.0.0",
- "lockfileVersion": 1,
+ "lockfileVersion": 2,
"requires": true,
+ "packages": {
+ "": {
+ "name": "bun-error",
+ "version": "1.0.0",
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "latest",
+ "react": "^17.0.2",
+ "react-dom": "^17.0.2"
+ },
+ "devDependencies": {
+ "@types/react": "^17.0.39"
+ }
+ },
+ "node_modules/@types/prop-types": {
+ "version": "15.7.5",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
+ "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
+ "dev": true
+ },
+ "node_modules/@types/react": {
+ "version": "17.0.47",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.47.tgz",
+ "integrity": "sha512-mk0BL8zBinf2ozNr3qPnlu1oyVTYq+4V7WA76RgxUAtf0Em/Wbid38KN6n4abEkvO4xMTBWmnP1FtQzgkEiJoA==",
+ "dev": true,
+ "dependencies": {
+ "@types/prop-types": "*",
+ "@types/scheduler": "*",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/scheduler": {
+ "version": "0.16.2",
+ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
+ "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==",
+ "dev": true
+ },
+ "node_modules/csstype": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
+ "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==",
+ "dev": true
+ },
+ "node_modules/esbuild": {
+ "version": "0.14.48",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.48.tgz",
+ "integrity": "sha512-w6N1Yn5MtqK2U1/WZTX9ZqUVb8IOLZkZ5AdHkT6x3cHDMVsYWC7WPdiLmx19w3i4Rwzy5LqsEMtVihG3e4rFzA==",
+ "hasInstallScript": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "esbuild-android-64": "0.14.48",
+ "esbuild-android-arm64": "0.14.48",
+ "esbuild-darwin-64": "0.14.48",
+ "esbuild-darwin-arm64": "0.14.48",
+ "esbuild-freebsd-64": "0.14.48",
+ "esbuild-freebsd-arm64": "0.14.48",
+ "esbuild-linux-32": "0.14.48",
+ "esbuild-linux-64": "0.14.48",
+ "esbuild-linux-arm": "0.14.48",
+ "esbuild-linux-arm64": "0.14.48",
+ "esbuild-linux-mips64le": "0.14.48",
+ "esbuild-linux-ppc64le": "0.14.48",
+ "esbuild-linux-riscv64": "0.14.48",
+ "esbuild-linux-s390x": "0.14.48",
+ "esbuild-netbsd-64": "0.14.48",
+ "esbuild-openbsd-64": "0.14.48",
+ "esbuild-sunos-64": "0.14.48",
+ "esbuild-windows-32": "0.14.48",
+ "esbuild-windows-64": "0.14.48",
+ "esbuild-windows-arm64": "0.14.48"
+ }
+ },
+ "node_modules/esbuild-android-64": {
+ "version": "0.14.48",
+ "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.48.tgz",
+ "integrity": "sha512-3aMjboap/kqwCUpGWIjsk20TtxVoKck8/4Tu19rubh7t5Ra0Yrpg30Mt1QXXlipOazrEceGeWurXKeFJgkPOUg==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-android-arm64": {
+ "version": "0.14.48",
+ "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.48.tgz",
+ "integrity": "sha512-vptI3K0wGALiDq+EvRuZotZrJqkYkN5282iAfcffjI5lmGG9G1ta/CIVauhY42MBXwEgDJkweiDcDMRLzBZC4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-darwin-64": {
+ "version": "0.14.48",
+ "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.48.tgz",
+ "integrity": "sha512-gGQZa4+hab2Va/Zww94YbshLuWteyKGD3+EsVon8EWTWhnHFRm5N9NbALNbwi/7hQ/hM1Zm4FuHg+k6BLsl5UA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-darwin-arm64": {
+ "version": "0.14.48",
+ "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.48.tgz",
+ "integrity": "sha512-bFjnNEXjhZT+IZ8RvRGNJthLWNHV5JkCtuOFOnjvo5pC0sk2/QVk0Qc06g2PV3J0TcU6kaPC3RN9yy9w2PSLEA==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-freebsd-64": {
+ "version": "0.14.48",
+ "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.48.tgz",
+ "integrity": "sha512-1NOlwRxmOsnPcWOGTB10JKAkYSb2nue0oM1AfHWunW/mv3wERfJmnYlGzL3UAOIUXZqW8GeA2mv+QGwq7DToqA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-freebsd-arm64": {
+ "version": "0.14.48",
+ "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.48.tgz",
+ "integrity": "sha512-gXqKdO8wabVcYtluAbikDH2jhXp+Klq5oCD5qbVyUG6tFiGhrC9oczKq3vIrrtwcxDQqK6+HDYK8Zrd4bCA9Gw==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-linux-32": {
+ "version": "0.14.48",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.48.tgz",
+ "integrity": "sha512-ghGyDfS289z/LReZQUuuKq9KlTiTspxL8SITBFQFAFRA/IkIvDpnZnCAKTCjGXAmUqroMQfKJXMxyjJA69c/nQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-linux-64": {
+ "version": "0.14.48",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.48.tgz",
+ "integrity": "sha512-vni3p/gppLMVZLghI7oMqbOZdGmLbbKR23XFARKnszCIBpEMEDxOMNIKPmMItQrmH/iJrL1z8Jt2nynY0bE1ug==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-linux-arm": {
+ "version": "0.14.48",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.48.tgz",
+ "integrity": "sha512-+VfSV7Akh1XUiDNXgqgY1cUP1i2vjI+BmlyXRfVz5AfV3jbpde8JTs5Q9sYgaoq5cWfuKfoZB/QkGOI+QcL1Tw==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-linux-arm64": {
+ "version": "0.14.48",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.48.tgz",
+ "integrity": "sha512-3CFsOlpoxlKPRevEHq8aAntgYGYkE1N9yRYAcPyng/p4Wyx0tPR5SBYsxLKcgPB9mR8chHEhtWYz6EZ+H199Zw==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-linux-mips64le": {
+ "version": "0.14.48",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.48.tgz",
+ "integrity": "sha512-cs0uOiRlPp6ymknDnjajCgvDMSsLw5mST2UXh+ZIrXTj2Ifyf2aAP3Iw4DiqgnyYLV2O/v/yWBJx+WfmKEpNLA==",
+ "cpu": [
+ "mips64el"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-linux-ppc64le": {
+ "version": "0.14.48",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.48.tgz",
+ "integrity": "sha512-+2F0vJMkuI0Wie/wcSPDCqXvSFEELH7Jubxb7mpWrA/4NpT+/byjxDz0gG6R1WJoeDefcrMfpBx4GFNN1JQorQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-linux-riscv64": {
+ "version": "0.14.48",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.48.tgz",
+ "integrity": "sha512-BmaK/GfEE+5F2/QDrIXteFGKnVHGxlnK9MjdVKMTfvtmudjY3k2t8NtlY4qemKSizc+QwyombGWTBDc76rxePA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-linux-s390x": {
+ "version": "0.14.48",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.48.tgz",
+ "integrity": "sha512-tndw/0B9jiCL+KWKo0TSMaUm5UWBLsfCKVdbfMlb3d5LeV9WbijZ8Ordia8SAYv38VSJWOEt6eDCdOx8LqkC4g==",
+ "cpu": [
+ "s390x"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-netbsd-64": {
+ "version": "0.14.48",
+ "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.48.tgz",
+ "integrity": "sha512-V9hgXfwf/T901Lr1wkOfoevtyNkrxmMcRHyticybBUHookznipMOHoF41Al68QBsqBxnITCEpjjd4yAos7z9Tw==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-openbsd-64": {
+ "version": "0.14.48",
+ "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.48.tgz",
+ "integrity": "sha512-+IHf4JcbnnBl4T52egorXMatil/za0awqzg2Vy6FBgPcBpisDWT2sVz/tNdrK9kAqj+GZG/jZdrOkj7wsrNTKA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-sunos-64": {
+ "version": "0.14.48",
+ "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.48.tgz",
+ "integrity": "sha512-77m8bsr5wOpOWbGi9KSqDphcq6dFeJyun8TA+12JW/GAjyfTwVtOnN8DOt6DSPUfEV+ltVMNqtXUeTeMAxl5KA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-windows-32": {
+ "version": "0.14.48",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.48.tgz",
+ "integrity": "sha512-EPgRuTPP8vK9maxpTGDe5lSoIBHGKO/AuxDncg5O3NkrPeLNdvvK8oywB0zGaAZXxYWfNNSHskvvDgmfVTguhg==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-windows-64": {
+ "version": "0.14.48",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.48.tgz",
+ "integrity": "sha512-YmpXjdT1q0b8ictSdGwH3M8VCoqPpK1/UArze3X199w6u8hUx3V8BhAi1WjbsfDYRBanVVtduAhh2sirImtAvA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-windows-arm64": {
+ "version": "0.14.48",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.48.tgz",
+ "integrity": "sha512-HHaOMCsCXp0rz5BT2crTka6MPWVno121NKApsGs/OIW5QC0ggC69YMGs1aJct9/9FSUF4A1xNE/cLvgB5svR4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
+ "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
+ "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1",
+ "scheduler": "^0.20.2"
+ },
+ "peerDependencies": {
+ "react": "17.0.2"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
+ "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1"
+ }
+ }
+ },
"dependencies": {
"@types/prop-types": {
"version": "15.7.5",
diff --git a/packages/bun-types/dns.d.ts b/packages/bun-types/dns.d.ts
index b5c9a777b..a51afe145 100644
--- a/packages/bun-types/dns.d.ts
+++ b/packages/bun-types/dns.d.ts
@@ -650,13 +650,13 @@ declare module "dns" {
* ```
* @since v0.1.27
*/
- // export function resolveSrv(
- // hostname: string,
- // callback: (err: ErrnoException | null, addresses: SrvRecord[]) => void,
- // ): void;
- // export namespace resolveSrv {
- // function __promisify__(hostname: string): Promise<SrvRecord[]>;
- // }
+ export function resolveSrv(
+ hostname: string,
+ callback: (err: ErrnoException | null, addresses: SrvRecord[]) => void,
+ ): void;
+ export namespace resolveSrv {
+ function __promisify__(hostname: string): Promise<SrvRecord[]>;
+ }
/**
* Uses the DNS protocol to resolve text queries (`TXT` records) for the`hostname`. The `records` argument passed to the `callback` function is a
* two-dimensional array of the text records available for `hostname` (e.g.`[ ['v=spf1 ip4:0.0.0.0 ', '~all' ] ]`). Each sub-array contains TXT chunks of
@@ -867,7 +867,7 @@ declare module "dns" {
// resolveNs: typeof resolveNs;
// resolvePtr: typeof resolvePtr;
// resolveSoa: typeof resolveSoa;
- // resolveSrv: typeof resolveSrv;
+ resolveSrv: typeof resolveSrv;
// resolveTxt: typeof resolveTxt;
// reverse: typeof reverse;
/**
diff --git a/packages/bun-types/dns/promises.d.ts b/packages/bun-types/dns/promises.d.ts
index 6460128b2..2b84f2c0f 100644
--- a/packages/bun-types/dns/promises.d.ts
+++ b/packages/bun-types/dns/promises.d.ts
@@ -15,7 +15,7 @@ declare module "dns/promises" {
// MxRecord,
// NaptrRecord,
// SoaRecord,
- // SrvRecord,
+ SrvRecord,
ResolveWithTtlOptions,
RecordWithTtl,
ResolveOptions,
@@ -144,7 +144,7 @@ declare module "dns/promises" {
// function resolve(hostname: string, rrtype: "NS"): Promise<string[]>;
// function resolve(hostname: string, rrtype: "PTR"): Promise<string[]>;
// function resolve(hostname: string, rrtype: "SOA"): Promise<SoaRecord>;
- // function resolve(hostname: string, rrtype: "SRV"): Promise<SrvRecord[]>;
+ function resolveSrv(hostname: string): Promise<SrvRecord[]>;
// function resolve(hostname: string, rrtype: "TXT"): Promise<string[][]>;
// function resolve(
// hostname: string,
@@ -319,7 +319,7 @@ declare module "dns/promises" {
* ```
* @since v10.6.0
*/
- // function resolveSrv(hostname: string): Promise<SrvRecord[]>;
+ function resolveSrv(hostname: string): Promise<SrvRecord[]>;
/**
* Uses the DNS protocol to resolve text queries (`TXT` records) for the`hostname`. On success, the `Promise` is resolved with a two-dimensional array
* of the text records available for `hostname` (e.g.`[ ['v=spf1 ip4:0.0.0.0 ', '~all' ] ]`). Each sub-array contains TXT chunks of
@@ -390,7 +390,7 @@ declare module "dns/promises" {
// resolveNs: typeof resolveNs;
// resolvePtr: typeof resolvePtr;
// resolveSoa: typeof resolveSoa;
- // resolveSrv: typeof resolveSrv;
+ resolveSrv: typeof resolveSrv;
// resolveTxt: typeof resolveTxt;
// reverse: typeof reverse;
// setLocalAddress(ipv4?: string, ipv6?: string): void;
diff --git a/src/bun.js/api/bun/dns_resolver.zig b/src/bun.js/api/bun/dns_resolver.zig
index ac29ae145..baf7b5b3d 100644
--- a/src/bun.js/api/bun/dns_resolver.zig
+++ b/src/bun.js/api/bun/dns_resolver.zig
@@ -614,6 +614,111 @@ pub const GetAddrInfo = struct {
};
};
+
+
+
+
+pub const ResolveSrvInfoRequest = struct {
+ const log = Output.scoped(.ResolveSrvInfoRequest, false);
+
+ resolver_for_caching: ?*DNSResolver = null,
+ hash: u64 = 0,
+ cache: ResolveSrvInfoRequest.CacheConfig = ResolveSrvInfoRequest.CacheConfig{},
+ head: SrvLookup,
+ tail: *SrvLookup = undefined,
+ task: bun.ThreadPool.Task = undefined,
+
+ pub fn init(
+ cache: DNSResolver.SrvCacheHit,
+ resolver: ?*DNSResolver,
+ name: [] const u8,
+ globalThis: *JSC.JSGlobalObject,
+ comptime cache_field: []const u8,
+ ) !*ResolveSrvInfoRequest {
+
+ var request = try globalThis.allocator().create(ResolveSrvInfoRequest);
+ var hasher = std.hash.Wyhash.init(0);
+ hasher.update(name);
+ const hash = hasher.final();
+ var poll_ref = JSC.PollRef.init();
+ poll_ref.ref(globalThis.bunVM());
+ request.* = .{
+ .resolver_for_caching = resolver,
+ .hash = hash,
+ .head = .{
+ .poll_ref = poll_ref,
+ .globalThis = globalThis,
+ .promise = JSC.JSPromise.Strong.init(globalThis),
+ .allocated = false,
+ },
+ };
+ request.tail = &request.head;
+ if (cache == .new) {
+ request.resolver_for_caching = resolver;
+ request.cache = ResolveSrvInfoRequest.CacheConfig{
+ .pending_cache = true,
+ .entry_cache = false,
+ .pos_in_pending = @truncate(u5, @field(resolver.?, cache_field).indexOf(cache.new).?),
+ .name_len = @truncate(u9, name.len),
+ };
+ cache.new.lookup = request;
+ }
+ return request;
+ }
+
+ pub const Task = bun.JSC.WorkTask(ResolveSrvInfoRequest, false);
+
+ pub const CacheConfig = packed struct(u16) {
+ pending_cache: bool = false,
+ entry_cache: bool = false,
+ pos_in_pending: u5 = 0,
+ name_len: u9 = 0,
+ };
+
+ pub const PendingCacheKey = struct {
+ hash: u64,
+ len: u16,
+ lookup: *ResolveSrvInfoRequest = undefined,
+
+ pub fn append(this: *PendingCacheKey, srv_lookup: *SrvLookup) void {
+ var tail = this.lookup.tail;
+ tail.next = srv_lookup;
+ this.lookup.tail = srv_lookup;
+ }
+
+ pub fn init(name: []const u8) PendingCacheKey {
+ var hasher = std.hash.Wyhash.init(0);
+ hasher.update(name);
+ const hash = hasher.final();
+ return PendingCacheKey{
+ .hash = hash,
+ .len = @truncate(u16, name.len),
+ .lookup = undefined,
+ };
+ }
+ };
+
+ pub fn onCaresComplete(this: *ResolveSrvInfoRequest, err_: ?c_ares.Error, timeout: i32, result: ?*c_ares.struct_ares_srv_reply) void {
+ if (this.resolver_for_caching) |resolver| {
+
+ if (this.cache.pending_cache) {
+ resolver.drainPendingSrvCares(
+ this.cache.pos_in_pending,
+ err_,
+ timeout,
+ result,
+ );
+ return;
+ }
+ }
+
+ var head = this.head;
+ bun.default_allocator.destroy(this);
+
+ head.processResolveSrv(err_, timeout, result);
+ }
+};
+
pub const GetAddrInfoRequest = struct {
const log = Output.scoped(.GetAddrInfoRequest, false);
@@ -634,12 +739,15 @@ pub const GetAddrInfoRequest = struct {
comptime cache_field: []const u8,
) !*GetAddrInfoRequest {
var request = try globalThis.allocator().create(GetAddrInfoRequest);
+ var poll_ref = JSC.PollRef.init();
+ poll_ref.ref(globalThis.bunVM());
request.* = .{
.backend = backend,
.resolver_for_caching = resolver,
.hash = query.hash(),
.head = .{
.globalThis = globalThis,
+ .poll_ref = poll_ref,
.promise = JSC.JSPromise.Strong.init(globalThis),
.allocated = false,
},
@@ -832,6 +940,82 @@ pub const GetAddrInfoRequest = struct {
}
};
+pub const SrvLookup = struct {
+ const log = Output.scoped(.SrvLookup, true);
+
+ globalThis: *JSC.JSGlobalObject = undefined,
+ promise: JSC.JSPromise.Strong,
+ poll_ref: JSC.PollRef,
+ allocated: bool = false,
+ next: ?*SrvLookup = null,
+
+ pub fn init(globalThis: *JSC.JSGlobalObject, allocator: std.mem.Allocator) !*SrvLookup {
+ var this = try allocator.create(SrvLookup);
+ var poll_ref = JSC.PollRef.init();
+ poll_ref.ref(globalThis.bunVM());
+ this.* = .{
+ .globalThis = globalThis,
+ .promise = JSC.JSPromise.Strong.init(globalThis),
+ .poll_ref = poll_ref,
+ .allocated = true,
+ };
+ return this;
+ }
+
+ pub fn processResolveSrv(this: *SrvLookup, err_: ?c_ares.Error, _: i32, result: ?*c_ares.struct_ares_srv_reply) void {
+ if (err_) |err| {
+ var promise = this.promise;
+ var globalThis = this.globalThis;
+ const error_value = globalThis.createErrorInstance("SRV lookup failed: {s}", .{err.label()});
+ error_value.put(
+ globalThis,
+ JSC.ZigString.static("code"),
+ JSC.ZigString.init(err.code()).toValueGC(globalThis),
+ );
+
+ promise.reject(globalThis, error_value);
+ this.deinit();
+ return;
+ }
+
+ if (result == null or result.?.next == null) {
+ var promise = this.promise;
+ var globalThis = this.globalThis;
+ const error_value = globalThis.createErrorInstance("SRV lookup failed: {s}", .{"No results"});
+ error_value.put(
+ globalThis,
+ JSC.ZigString.static("code"),
+ JSC.ZigString.init("EUNREACHABLE").toValueGC(globalThis),
+ );
+
+ promise.reject(globalThis, error_value);
+ this.deinit();
+ return;
+ }
+ var node = result.?;
+ const array = node.toJSArray(this.globalThis.allocator(), this.globalThis);
+ this.onComplete(array);
+ return;
+ }
+
+
+
+ pub fn onComplete(this: *SrvLookup, result: JSC.JSValue) void {
+ var promise = this.promise;
+ var globalThis = this.globalThis;
+ this.promise = .{};
+
+ promise.resolve(globalThis, result);
+ this.deinit();
+ }
+
+ pub fn deinit(this: *SrvLookup) void {
+ this.poll_ref.unrefOnNextTick(this.globalThis.bunVM());
+
+ if (this.allocated)
+ this.globalThis.allocator().destroy(this);
+ }
+};
pub const DNSLookup = struct {
const log = Output.scoped(.DNSLookup, true);
@@ -839,11 +1023,16 @@ pub const DNSLookup = struct {
promise: JSC.JSPromise.Strong,
allocated: bool = false,
next: ?*DNSLookup = null,
+ poll_ref: JSC.PollRef,
pub fn init(globalThis: *JSC.JSGlobalObject, allocator: std.mem.Allocator) !*DNSLookup {
var this = try allocator.create(DNSLookup);
+ var poll_ref = JSC.PollRef.init();
+ poll_ref.ref(globalThis.bunVM());
+
this.* = .{
.globalThis = globalThis,
+ .poll_ref = poll_ref,
.promise = JSC.JSPromise.Strong.init(globalThis),
.allocated = true,
};
@@ -893,9 +1082,8 @@ pub const DNSLookup = struct {
JSC.ZigString.init(err.code()).toValueGC(globalThis),
);
- this.deinit();
-
promise.reject(globalThis, error_value);
+ this.deinit();
return;
}
@@ -909,8 +1097,8 @@ pub const DNSLookup = struct {
JSC.ZigString.init("EUNREACHABLE").toValueGC(globalThis),
);
- this.deinit();
promise.reject(globalThis, error_value);
+ this.deinit();
return;
}
@@ -927,11 +1115,12 @@ pub const DNSLookup = struct {
var globalThis = this.globalThis;
this.promise = .{};
+ promise.resolve(globalThis, result);
this.deinit();
- promise.resolveOnNextTick(globalThis, result);
}
pub fn deinit(this: *DNSLookup) void {
+ this.poll_ref.unrefOnNextTick(this.globalThis.bunVM());
if (this.allocated)
this.globalThis.allocator().destroy(this);
}
@@ -961,10 +1150,12 @@ pub const DNSResolver = struct {
polls: std.AutoArrayHashMap(i32, ?*JSC.FilePoll) = undefined,
pending_host_cache_cares: PendingCache = PendingCache.init(),
+ pending_srv_cache_cares: SrvPendingCache = SrvPendingCache.init(),
pending_host_cache_native: PendingCache = PendingCache.init(),
// entry_host_cache: std.BoundedArray(128)
const PendingCache = bun.HiveArray(GetAddrInfoRequest.PendingCacheKey, 32);
+ const SrvPendingCache = bun.HiveArray(ResolveSrvInfoRequest.PendingCacheKey, 32);
fn getKey(this: *DNSResolver, index: u8, comptime cache_name: []const u8) GetAddrInfoRequest.PendingCacheKey {
var cache: *PendingCache = &@field(this, cache_name);
@@ -978,6 +1169,60 @@ pub const DNSResolver = struct {
return entry;
}
+ fn getSrvKey(this: *DNSResolver, index: u8, comptime cache_name: []const u8) ResolveSrvInfoRequest.PendingCacheKey {
+ var cache: *SrvPendingCache = &@field(this, cache_name);
+ std.debug.assert(!cache.available.isSet(index));
+ const entry = cache.buffer[index];
+ cache.buffer[index] = undefined;
+
+ var available = cache.available;
+ available.set(index);
+ cache.available = available;
+
+ return entry;
+ }
+
+ pub fn drainPendingSrvCares(this: *DNSResolver, index: u8, err: ?c_ares.Error, timeout: i32, result: ?*c_ares.struct_ares_srv_reply) void {
+ const key = this.getSrvKey(index, "pending_srv_cache_cares");
+
+ var addr = result orelse {
+ var pending: ?*SrvLookup = key.lookup.head.next;
+ key.lookup.head.processResolveSrv(err, timeout, null);
+ bun.default_allocator.destroy(key.lookup);
+
+ while (pending) |value| {
+ pending = value.next;
+ value.processResolveSrv(err, timeout, null);
+ }
+ return;
+ };
+
+ var pending: ?*SrvLookup = key.lookup.head.next;
+ var prev_global = key.lookup.head.globalThis;
+ var array = addr.toJSArray(this.vm.allocator, prev_global);
+ defer addr.deinit();
+ array.ensureStillAlive();
+ key.lookup.head.onComplete(array);
+ bun.default_allocator.destroy(key.lookup);
+
+ array.ensureStillAlive();
+
+
+ while (pending) |value| {
+ var new_global = value.globalThis;
+ if (prev_global != new_global) {
+ array = addr.toJSArray(this.vm.allocator, new_global);
+ prev_global = new_global;
+ }
+ pending = value.next;
+
+ {
+ array.ensureStillAlive();
+ value.onComplete(array);
+ array.ensureStillAlive();
+ }
+ }
+ }
pub fn drainPendingHostCares(this: *DNSResolver, index: u8, err: ?c_ares.Error, timeout: i32, result: ?*c_ares.AddrInfo) void {
const key = this.getKey(index, "pending_host_cache_cares");
@@ -1070,6 +1315,39 @@ pub const DNSResolver = struct {
new: *GetAddrInfoRequest.PendingCacheKey,
disabled: void,
};
+ pub const SrvCacheHit = union(enum) {
+ inflight: *ResolveSrvInfoRequest.PendingCacheKey,
+ new: *ResolveSrvInfoRequest.PendingCacheKey,
+ disabled: void,
+ };
+
+ pub fn getOrPutIntoSrvPendingCache(
+ this: *DNSResolver,
+ key: ResolveSrvInfoRequest.PendingCacheKey,
+ comptime field: std.meta.FieldEnum(DNSResolver),
+ ) SrvCacheHit {
+ var cache: *SrvPendingCache = &@field(this, @tagName(field));
+ var inflight_iter = cache.available.iterator(
+ .{
+ .kind = .unset,
+ },
+ );
+
+ while (inflight_iter.next()) |index| {
+ var entry: *ResolveSrvInfoRequest.PendingCacheKey = &cache.buffer[index];
+ if (entry.hash == key.hash and entry.len == key.len) {
+ return .{ .inflight = entry };
+ }
+ }
+
+ if (cache.get()) |new| {
+ new.hash = key.hash;
+ new.len = key.len;
+ return .{ .new = new };
+ }
+
+ return .{ .disabled = {} };
+ }
pub fn getOrPutIntoPendingCache(
this: *DNSResolver,
@@ -1330,6 +1608,80 @@ pub const DNSResolver = struct {
};
}
+ pub fn resolveSrv(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
+ const arguments = callframe.arguments(2);
+ if (arguments.len < 1) {
+ globalThis.throwNotEnoughArguments("resolveSrv", 2, arguments.len);
+ return .zero;
+ }
+
+ const name_value = arguments.ptr[0];
+
+ if (name_value.isEmptyOrUndefinedOrNull() or !name_value.isString()) {
+ globalThis.throwInvalidArgumentType("resolveSrv", "hostname", "string");
+ return .zero;
+ }
+
+ const name_str = name_value.toStringOrNull(globalThis) orelse {
+ return .zero;
+ };
+
+ if (name_str.length() == 0) {
+ globalThis.throwInvalidArgumentType("resolveSrv", "hostname", "non-empty string");
+ return .zero;
+ }
+
+
+ const name = name_str.toSlice(globalThis, bun.default_allocator);
+ defer name.deinit();
+ var vm = globalThis.bunVM();
+ var resolver = vm.rareData().globalDNSResolver(vm);
+
+ return resolver.doResolveSrv(name.slice(), globalThis);
+ }
+
+ pub fn doResolveSrv(this: *DNSResolver, name: []const u8, globalThis: *JSC.JSGlobalObject) JSC.JSValue {
+ var channel: *c_ares.Channel = switch (this.getChannel()) {
+ .result => |res| res,
+ .err => |err| {
+ const system_error = JSC.SystemError{
+ .errno = -1,
+ .code = JSC.ZigString.init(err.code()),
+ .message = JSC.ZigString.init(err.label()),
+ };
+
+ globalThis.throwValue(system_error.toErrorInstance(globalThis));
+ return .zero;
+ },
+ };
+
+ const key = ResolveSrvInfoRequest.PendingCacheKey.init(name);
+
+ var cache = this.getOrPutIntoSrvPendingCache(key, .pending_srv_cache_cares);
+ if (cache == .inflight) {
+ var srv_lookup = SrvLookup.init(globalThis, globalThis.allocator()) catch unreachable;
+ cache.inflight.append(srv_lookup);
+ return srv_lookup.promise.value();
+ }
+
+ var request = ResolveSrvInfoRequest.init(
+ cache,
+ this,
+ name,
+ globalThis,
+ "pending_srv_cache_cares",
+ ) catch unreachable;
+ const promise = request.tail.promise.value();
+
+ channel.resolveSrv(
+ name,
+ ResolveSrvInfoRequest,
+ request,
+ ResolveSrvInfoRequest.onCaresComplete,
+ );
+
+ return promise;
+ }
pub fn c_aresLookupWithNormalizedName(this: *DNSResolver, query: GetAddrInfo, globalThis: *JSC.JSGlobalObject) JSC.JSValue {
var channel: *c_ares.Channel = switch (this.getChannel()) {
.result => |res| res,
@@ -1386,6 +1738,12 @@ pub const DNSResolver = struct {
.name = "Bun__DNSResolver__lookup",
},
);
+ @export(
+ resolveSrv,
+ .{
+ .name = "Bun__DNSResolver__resolveSrv",
+ },
+ );
}
// pub fn lookupService(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
// const arguments = callframe.arguments(3);
diff --git a/src/bun.js/base.zig b/src/bun.js/base.zig
index 79498b8fc..7d36662b6 100644
--- a/src/bun.js/base.zig
+++ b/src/bun.js/base.zig
@@ -3225,19 +3225,22 @@ pub const PollRef = struct {
if (this.status != .active)
return;
this.status = .inactive;
- log("unref", .{});
- vm.uws_event_loop.?.num_polls -= 1;
- vm.uws_event_loop.?.active -= 1;
+ vm.uws_event_loop.?.unref();
+ }
+ /// Prevent a poll from keeping the process alive on the next tick.
+ pub fn unrefOnNextTick(this: *PollRef, vm: *JSC.VirtualMachine) void {
+ if (this.status != .active)
+ return;
+ this.status = .inactive;
+ vm.uws_event_loop.?.nextTick(*uws.Loop, vm.uws_event_loop.?, uws.Loop.unref);
}
/// Allow a poll to keep the process alive.
pub fn ref(this: *PollRef, vm: *JSC.VirtualMachine) void {
if (this.status != .inactive)
return;
- log("ref", .{});
this.status = .active;
- vm.uws_event_loop.?.num_polls += 1;
- vm.uws_event_loop.?.active += 1;
+ vm.uws_event_loop.?.ref();
}
};
diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp
index b37e280f3..11b67b433 100644
--- a/src/bun.js/bindings/ZigGlobalObject.cpp
+++ b/src/bun.js/bindings/ZigGlobalObject.cpp
@@ -2169,6 +2169,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionPerformMicrotask, (JSGlobalObject * globalObj
}
extern "C" EncodedJSValue Bun__DNSResolver__lookup(JSGlobalObject*, JSC::CallFrame*);
+extern "C" EncodedJSValue Bun__DNSResolver__resolveSrv(JSGlobalObject*, JSC::CallFrame*);
JSC_DEFINE_HOST_FUNCTION(jsFunctionPerformMicrotaskVariadic, (JSGlobalObject * globalObject, CallFrame* callframe))
{
@@ -3321,9 +3322,11 @@ void GlobalObject::installAPIGlobals(JSClassRef* globals, int count, JSC::VM& vm
JSC::JSObject* dnsObject = JSC::constructEmptyObject(this);
dnsObject->putDirectNativeFunction(vm, this, JSC::Identifier::fromString(vm, "lookup"_s), 2, Bun__DNSResolver__lookup, ImplementationVisibility::Public, NoIntrinsic,
JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0);
+ dnsObject->putDirectNativeFunction(vm, this, JSC::Identifier::fromString(vm, "resolveSrv"_s), 2, Bun__DNSResolver__resolveSrv, ImplementationVisibility::Public, NoIntrinsic,
+ JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0);
object->putDirect(vm, PropertyName(identifier), dnsObject, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
}
-
+
{
JSC::Identifier identifier = JSC::Identifier::fromString(vm, "plugin"_s);
JSFunction* pluginFunction = JSFunction::create(vm, this, 1, String("plugin"_s), jsFunctionBunPlugin, ImplementationVisibility::Public, NoIntrinsic);
diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig
index d77a9dbb6..aa051e712 100644
--- a/src/bun.js/bindings/bindings.zig
+++ b/src/bun.js/bindings/bindings.zig
@@ -2860,7 +2860,7 @@ pub const JSValue = enum(JSValueReprInt) {
return switch (comptime Number) {
JSValue => number,
f32, f64 => jsNumberFromDouble(@as(f64, number)),
- u8, i16, i32, c_int, i8, u16 => jsNumberFromInt32(@intCast(i32, number)),
+ c_ushort, u8, i16, i32, c_int, i8, u16 => jsNumberFromInt32(@intCast(i32, number)),
u32, u52, c_uint, i64 => jsNumberFromInt64(@intCast(i64, number)),
usize, u64 => jsNumberFromUint64(@intCast(u64, number)),
comptime_int => switch (number) {
diff --git a/src/bun.js/node-dns.exports.js b/src/bun.js/node-dns.exports.js
index 570903b40..74ec1b46a 100644
--- a/src/bun.js/node-dns.exports.js
+++ b/src/bun.js/node-dns.exports.js
@@ -1,4 +1,4 @@
-// only resolve4, resolve, lookup, and resolve6 are implemented.
+// only resolve4, resolve, lookup, resolve6 and resolveSrv are implemented.
const { dns } = globalThis.Bun;
@@ -25,6 +25,22 @@ function lookup(domain, options, callback) {
);
}
+function resolveSrv(hostname, callback) {
+
+ if (typeof callback != "function") {
+ throw new TypeError("callback must be a function");
+ }
+
+ dns.resolveSrv(hostname, callback).then(
+ (results) => {
+ callback(null, results);
+ },
+ (error) => {
+ callback(error);
+ },
+ );
+}
+
function lookupService(address, port, callback) {
if (typeof callback != "function") {
throw new TypeError("callback must be a function");
@@ -138,7 +154,18 @@ var InternalResolver = class Resolver {
}
resolveSrv(hostname, callback) {
- callback(null, []);
+ if (typeof callback != "function") {
+ throw new TypeError("callback must be a function");
+ }
+
+ dns.resolveSrv(hostname, callback).then(
+ (results) => {
+ callback(null, results);
+ },
+ (error) => {
+ callback(error);
+ },
+ );
}
resolveCaa(hostname, callback) {
@@ -209,6 +236,10 @@ export const promises = {
return dns.lookup(hostname, { family: 6 });
},
+ resolveSrv(hostname) {
+ return dns.resolveSrv(hostname);
+ },
+
Resolver: class Resolver {
constructor(options) {}
@@ -259,7 +290,7 @@ export const promises = {
}
resolveSrv(hostname) {
- return Promise.resolve([]);
+ return dns.resolveSrv(hostname);
}
resolveCaa(hostname) {
@@ -286,7 +317,6 @@ for (const key of [
"resolveNs",
"resolvePtr",
"resolveSoa",
- "resolveSrv",
"resolveTxt",
"reverse",
]) {
diff --git a/src/deps/c_ares.zig b/src/deps/c_ares.zig
index 6d692ad86..bb7bda5db 100644
--- a/src/deps/c_ares.zig
+++ b/src/deps/c_ares.zig
@@ -11,6 +11,145 @@ pub const ares_socket_t = c_int;
pub const ares_sock_state_cb = ?*const fn (?*anyopaque, ares_socket_t, c_int, c_int) callconv(.C) void;
pub const struct_apattern = opaque {};
const fd_set = c.fd_set;
+
+pub const NSClass = enum(c_int) {
+ /// Cookie.
+ ns_c_invalid = 0,
+ /// Internet.
+ ns_c_in = 1,
+ /// unallocated/unsupported.
+ ns_c_2 = 2,
+ /// MIT Chaos-net.
+ ns_c_chaos = 3,
+ /// MIT Hesiod.
+ ns_c_hs = 4,
+ /// Query class values which do not appear in resource records
+ /// for prereq. sections in update requests
+ ns_c_none = 254,
+ /// Wildcard match.
+ ns_c_any = 255,
+ ns_c_max = 65536,
+};
+
+pub const NSType = enum(c_int) {
+ /// Cookie.
+ ns_t_invalid = 0,
+ /// Host address.
+ ns_t_a = 1,
+ /// Authoritative server.
+ ns_t_ns = 2,
+ /// Mail destination.
+ ns_t_md = 3,
+ /// Mail forwarder.
+ ns_t_mf = 4,
+ /// Canonical name.
+ ns_t_cname = 5,
+ /// Start of authority zone.
+ ns_t_soa = 6,
+ /// Mailbox domain name.
+ ns_t_mb = 7,
+ /// Mail group member.
+ ns_t_mg = 8,
+ /// Mail rename name.
+ ns_t_mr = 9,
+ /// Null resource record.
+ ns_t_null = 10,
+ /// Well known service.
+ ns_t_wks = 11,
+ /// Domain name pointer.
+ ns_t_ptr = 12,
+ /// Host information.
+ ns_t_hinfo = 13,
+ /// Mailbox information.
+ ns_t_minfo = 14,
+ /// Mail routing information.
+ ns_t_mx = 15,
+ /// Text strings.
+ ns_t_txt = 16,
+ /// Responsible person.
+ ns_t_rp = 17,
+ /// AFS cell database.
+ ns_t_afsdb = 18,
+ /// X_25 calling address.
+ ns_t_x25 = 19,
+ /// ISDN calling address.
+ ns_t_isdn = 20,
+ /// Router.
+ ns_t_rt = 21,
+ /// NSAP address.
+ ns_t_nsap = 22,
+ /// Reverse NSAP lookup (deprecated).
+ ns_t_nsap_ptr = 23,
+ /// Security signature.
+ ns_t_sig = 24,
+ /// Security key.
+ ns_t_key = 25,
+ /// X.400 mail mapping.
+ ns_t_px = 26,
+ /// Geographical position (withdrawn).
+ ns_t_gpos = 27,
+ /// Ip6 Address.
+ ns_t_aaaa = 28,
+ /// Location Information.
+ ns_t_loc = 29,
+ /// Next domain (security).
+ ns_t_nxt = 30,
+ /// Endpoint identifier.
+ ns_t_eid = 31,
+ /// Nimrod Locator.
+ ns_t_nimloc = 32,
+ /// Server Selection.
+ ns_t_srv = 33,
+ /// ATM Address
+ ns_t_atma = 34,
+ /// Naming Authority PoinTeR
+ ns_t_naptr = 35,
+ /// Key Exchange
+ ns_t_kx = 36,
+ /// Certification record
+ ns_t_cert = 37,
+ /// IPv6 address (deprecates AAAA)
+ ns_t_a6 = 38,
+ /// Non-terminal DNAME (for IPv6)
+ ns_t_dname = 39,
+ /// Kitchen sink (experimentatl)
+ ns_t_sink = 40,
+ /// EDNS0 option (meta-RR)
+ ns_t_opt = 41,
+ /// Address prefix list (RFC3123)
+ ns_t_apl = 42,
+ /// Delegation Signer (RFC4034)
+ ns_t_ds = 43,
+ /// SSH Key Fingerprint (RFC4255)
+ ns_t_sshfp = 44,
+ /// Resource Record Signature (RFC4034)
+ ns_t_rrsig = 46,
+ /// Next Secure (RFC4034)
+ ns_t_nsec = 47,
+ /// DNS Public Key (RFC4034)
+ ns_t_dnskey = 48,
+ /// Transaction key
+ ns_t_tkey = 249,
+ /// Transaction signature.
+ ns_t_tsig = 250,
+ /// Incremental zone transfer.
+ ns_t_ixfr = 251,
+ /// Transfer zone of authority.
+ ns_t_axfr = 252,
+ /// Transfer mailbox records.
+ ns_t_mailb = 253,
+ /// Transfer mail agent records.
+ ns_t_maila = 254,
+ /// Wildcard match.
+ ns_t_any = 255,
+ /// Uniform Resource Identifier (RFC7553)
+ ns_t_uri = 256,
+ /// Certification Authority Authorization.
+ ns_t_caa = 257,
+ ns_t_max = 65536,
+ _,
+};
+
pub const Options = extern struct {
flags: c_int = 0,
timeout: c_int = 0,
@@ -152,6 +291,7 @@ pub const AddrInfo_hints = extern struct {
return this.ai_flags == 0 and this.ai_family == 0 and this.ai_socktype == 0 and this.ai_protocol == 0;
}
};
+
pub const Channel = opaque {
pub fn init(comptime Container: type, this: *Container) ?Error {
var channel: *Channel = undefined;
@@ -273,6 +413,21 @@ pub const Channel = opaque {
ares_getaddrinfo(this, host_ptr, port_ptr, hints_, AddrInfo.callbackWrapper(Type, callback), ctx);
}
+ pub fn resolveSrv(this: *Channel, name: []const u8, comptime Type: type, ctx: *Type, comptime callback: struct_ares_srv_reply.Callback(Type)) void {
+ var name_buf: [1024]u8 = undefined;
+ const name_ptr: ?[*:0]const u8 = brk: {
+ if (name.len == 0 or name.len >= 1023) {
+ break :brk null;
+ }
+ const len = @min(name_buf.len, name_buf.len - 1);
+ @memcpy(&name_buf, name.ptr, len);
+ name_buf[len] = 0;
+ break :brk name_buf[0..len :0];
+ };
+
+ ares_query(this, name_ptr, NSClass.ns_c_in, NSType.ns_t_srv, struct_ares_srv_reply.callbackWrapper(Type, callback), ctx);
+ }
+
pub inline fn process(this: *Channel, fd: i32, readable: bool, writable: bool) void {
ares_process_fd(
this,
@@ -334,7 +489,7 @@ pub const ares_socket_functions = extern struct {
};
pub extern fn ares_set_socket_functions(channel: *Channel, funcs: ?*const ares_socket_functions, user_data: ?*anyopaque) void;
pub extern fn ares_send(channel: *Channel, qbuf: [*c]const u8, qlen: c_int, callback: ares_callback, arg: ?*anyopaque) void;
-pub extern fn ares_query(channel: *Channel, name: [*c]const u8, dnsclass: c_int, @"type": c_int, callback: ares_callback, arg: ?*anyopaque) void;
+pub extern fn ares_query(channel: *Channel, name: [*c]const u8, dnsclass: NSClass, @"type": NSType, callback: ares_callback, arg: ?*anyopaque) void;
pub extern fn ares_search(channel: *Channel, name: [*c]const u8, dnsclass: c_int, @"type": c_int, callback: ares_callback, arg: ?*anyopaque) void;
pub extern fn ares_gethostbyname(channel: *Channel, name: [*c]const u8, family: c_int, callback: ares_host_callback, arg: ?*anyopaque) void;
pub extern fn ares_gethostbyname_file(channel: *Channel, name: [*c]const u8, family: c_int, host: [*:null]?*struct_hostent) c_int;
@@ -372,11 +527,90 @@ pub const struct_ares_caa_reply = extern struct {
length: usize,
};
pub const struct_ares_srv_reply = extern struct {
- next: [*c]struct_ares_srv_reply,
+ next: ?*struct_ares_srv_reply,
host: [*c]u8,
priority: c_ushort,
weight: c_ushort,
port: c_ushort,
+ const JSC = bun.JSC;
+
+ pub fn toJSArray(this: *struct_ares_srv_reply, parent_allocator: std.mem.Allocator, globalThis: *JSC.JSGlobalObject) JSC.JSValue {
+ var stack = std.heap.stackFallback(2048, parent_allocator);
+ var arena = std.heap.ArenaAllocator.init(stack.get());
+ defer arena.deinit();
+
+ var allocator = arena.allocator();
+ var count: usize = 0;
+ var srv: ?*struct_ares_srv_reply = this;
+ while (srv != null) : (srv = srv.?.next) {
+ count += 1;
+ }
+
+ const array = JSC.JSValue.createEmptyArray(globalThis, count);
+
+ srv = this;
+ var i: u32 = 0;
+ while (srv != null) {
+ var node = srv.?;
+ array.putIndex(globalThis, i, node.toJS(globalThis, allocator));
+ srv = node.next;
+ i += 1;
+ }
+
+ return array;
+ }
+
+ pub fn toJS(this: *struct_ares_srv_reply, globalThis: *JSC.JSGlobalObject, _: std.mem.Allocator) JSC.JSValue {
+ var obj = JSC.JSValue.createEmptyObject(globalThis, 4);
+ // {
+ // priority: 10,
+ // weight: 5,
+ // port: 21223,
+ // name: 'service.example.com'
+ // }
+
+ obj.put(globalThis, JSC.ZigString.static("priority"), JSC.JSValue.jsNumber(this.weight));
+ obj.put(globalThis, JSC.ZigString.static("weight"), JSC.JSValue.jsNumber(this.weight));
+ obj.put(globalThis, JSC.ZigString.static("port"), JSC.JSValue.jsNumber(this.port));
+
+
+ const len = bun.len(this.host);
+ var host = this.host[0..len];
+ obj.put(globalThis, JSC.ZigString.static("name"), JSC.ZigString.fromUTF8(host).toValueGC(globalThis));
+
+ return obj;
+ }
+
+ pub fn Callback(comptime Type: type) type {
+ return fn (*Type, status: ?Error, timeouts: i32, results: ?*struct_ares_srv_reply) void;
+ }
+
+ pub fn callbackWrapper(
+ comptime Type: type,
+ comptime function: Callback(Type),
+ ) ares_callback {
+ return &struct {
+ pub fn handleSrv(ctx: ?*anyopaque, status: c_int, timeouts: c_int, buffer: [*c]u8, buffer_length: c_int) callconv(.C) void {
+ var this = bun.cast(*Type, ctx.?);
+ if (status != ARES_SUCCESS) {
+ function(this, Error.get(status), timeouts, null);
+ return;
+ }
+
+ var srv_start: [*c]struct_ares_srv_reply = undefined;
+ var result = ares_parse_srv_reply(buffer, buffer_length, &srv_start);
+ if (result != ARES_SUCCESS) {
+ function(this, Error.get(result), timeouts, null);
+ return;
+ }
+ function(this, null, timeouts, srv_start);
+ }
+ }.handleSrv;
+ }
+
+ pub fn deinit(this: *struct_ares_srv_reply) void {
+ ares_free_data(this);
+ }
};
pub const struct_ares_mx_reply = extern struct {
next: [*c]struct_ares_mx_reply,
diff --git a/src/deps/uws.zig b/src/deps/uws.zig
index 485af99e8..177fc1973 100644
--- a/src/deps/uws.zig
+++ b/src/deps/uws.zig
@@ -429,6 +429,7 @@ pub const Loop = extern struct {
ready_polls: [1024]EventType align(16),
const EventType = if (Environment.isLinux) std.os.linux.epoll_event else if (Environment.isMac) std.os.system.kevent64_s;
+ const log = bun.Output.scoped(.Loop, false);
pub const InternalLoopData = extern struct {
pub const us_internal_async = opaque {};
@@ -452,6 +453,17 @@ pub const Loop = extern struct {
}
};
+ pub fn ref(this: *Loop) void {
+ log("ref", .{});
+ this.num_polls += 1;
+ this.active += 1;
+ }
+ pub fn unref(this: *Loop) void {
+ log("unref", .{});
+ this.num_polls -= 1;
+ this.active -= 1;
+ }
+
pub fn get() ?*Loop {
return uws_get_loop();
}
diff --git a/test/bun.js/node-dns.test.js b/test/bun.js/node-dns.test.js
index 150dcb33d..287645bd9 100644
--- a/test/bun.js/node-dns.test.js
+++ b/test/bun.js/node-dns.test.js
@@ -31,3 +31,24 @@ test("dns.lookup (example.com)", (done) => {
done(err);
});
});
+
+//TODO: use a bun.sh SRV for testing
+test("dns.resolveSrv (_test._tcp.test.socketify.dev)", (done) => {
+ dns.resolveSrv("_test._tcp.test.socketify.dev", (err, results) => {
+ expect(err).toBeNull();
+ expect(results instanceof Array).toBe(true);
+ expect(results[0].name).toBe("_dc-srv.130c90ab9de1._test._tcp.test.socketify.dev");
+ expect(results[0].priority).toBe(50);
+ expect(results[0].weight).toBe(50);
+ expect(results[0].port).toBe(80);
+ done(err);
+ });
+});
+
+test("dns.resolveSrv (_test._tcp.invalid.localhost)", (done) => {
+ dns.resolveSrv("_test._tcp.invalid.localhost", (err, results) => {
+ expect(err).toBeTruthy();
+ expect(results).toBeUndefined(true);
+ done();
+ });
+}); \ No newline at end of file
diff --git a/test/bun.js/resolve-dns.test.ts b/test/bun.js/resolve-dns.test.ts
index f0db49b20..225a11e2a 100644
--- a/test/bun.js/resolve-dns.test.ts
+++ b/test/bun.js/resolve-dns.test.ts
@@ -51,4 +51,4 @@ describe("dns.lookup", () => {
},
);
}
-});
+}); \ No newline at end of file