diff options
author | 2023-01-22 19:31:08 -0300 | |
---|---|---|
committer | 2023-01-22 14:31:08 -0800 | |
commit | 481dbf7c6e1a900136f6988218f3613720b31f97 (patch) | |
tree | ce58bfa8bf70c4462137b450d567903247fb0956 | |
parent | 8d692f1511f4c017d0fcef94032f4d89d404f2a8 (diff) | |
download | bun-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.json | 438 | ||||
-rw-r--r-- | packages/bun-types/dns.d.ts | 16 | ||||
-rw-r--r-- | packages/bun-types/dns/promises.d.ts | 8 | ||||
-rw-r--r-- | src/bun.js/api/bun/dns_resolver.zig | 366 | ||||
-rw-r--r-- | src/bun.js/base.zig | 15 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGlobalObject.cpp | 5 | ||||
-rw-r--r-- | src/bun.js/bindings/bindings.zig | 2 | ||||
-rw-r--r-- | src/bun.js/node-dns.exports.js | 38 | ||||
-rw-r--r-- | src/deps/c_ares.zig | 238 | ||||
-rw-r--r-- | src/deps/uws.zig | 12 | ||||
-rw-r--r-- | test/bun.js/node-dns.test.js | 21 | ||||
-rw-r--r-- | test/bun.js/resolve-dns.test.ts | 2 |
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 |