Compare commits

...

119 Commits

Author SHA1 Message Date
Kai Tamkun
1f984829df Update src/js/node/dns.ts
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2025-01-03 14:41:10 -08:00
Kai Tamkun
f5b37a8f8c ??? 2025-01-02 13:58:41 -08:00
Kai Tamkun
14b43f1ae4 Fix missing parameter 2025-01-02 13:57:14 -08:00
Kai Tamkun
b306c29c01 Lazily call getRuntimeDefaultResultOrderOption and fix some tests 2025-01-02 13:23:59 -08:00
Kai Tamkun
887ba186a7 oops 2024-12-31 16:29:20 -08:00
Kai Tamkun
5611f2561a Merge remote-tracking branch 'origin/main' into kai/dns-test-fixes 2024-12-31 16:23:08 -08:00
Kai Tamkun
493e34c992 Increase node:dns test timeout to 45 seconds 2024-12-31 15:45:00 -08:00
Kai Tamkun
0fe347a0e2 Address feedback 2024-12-31 15:45:00 -08:00
Kai Tamkun
c7f68bfe75 Fix node-dns.test.js 2024-12-31 15:45:00 -08:00
Kai Tamkun
a1a2c618ea Get test-dns.js and test-dns-lookupService.js passing simultaneously 2024-12-31 15:45:00 -08:00
Kai Tamkun
f143598ca6 Set SystemError name property to "DNSException" 2024-12-31 15:45:00 -08:00
Kai Tamkun
95d0601935 Didn't mean to add those yet, whoops 2024-12-31 15:45:00 -08:00
Kai Tamkun
e5017b2301 Fix test-dns.js 2024-12-31 15:45:00 -08:00
Kai Tamkun
1c2cc7bd73 Add test-dns-setservers-type-check.js 2024-12-31 15:45:00 -08:00
Kai Tamkun
b283bc5317 Add test-dns-setserver-when-querying.js 2024-12-31 15:45:00 -08:00
Kai Tamkun
2c71326168 Don't allow setting DNS servers while any queries are active 2024-12-31 15:45:00 -08:00
Kai Tamkun
200639de08 Fix some memory leaks 2024-12-31 15:45:00 -08:00
Kai Tamkun
9bd562d5d6 Implement dns.Resolver.setLocalAddress 2024-12-31 15:45:00 -08:00
Kai Tamkun
bcfd17c89a Add test-dns-set-default-order.js 2024-12-31 15:45:00 -08:00
Kai Tamkun
6b40b21649 Validate hostname type 2024-12-31 15:45:00 -08:00
Kai Tamkun
832b43edc9 Reimplement resolveAny with an actual ANY query 2024-12-31 15:45:00 -08:00
Kai Tamkun
d9aaeecd2b Use SystemError in c_ares.zig 2024-12-31 15:45:00 -08:00
Kai Tamkun
444c220f9e Make rrtype default to A if unspecified 2024-12-31 15:45:00 -08:00
Kai Tamkun
4c1a233238 Add test-dns-resolve-promises.js 2024-12-31 15:45:00 -08:00
Kai Tamkun
a6c13ce72c Add error codes to dns.promises 2024-12-31 15:45:00 -08:00
Kai Tamkun
84ddb9697e Add test-dns-multi-channel.js 2024-12-31 15:45:00 -08:00
Kai Tamkun
381fa5ba5d Add test-dns-lookupService-promises.js 2024-12-31 15:45:00 -08:00
Kai Tamkun
78c6048294 Fix test-dns-lookup.js 2024-12-31 15:45:00 -08:00
Kai Tamkun
7e3cb6fc58 Add test-dns-lookup-promises-options-deprecated.js 2024-12-31 15:45:00 -08:00
Kai Tamkun
80a3a730eb Update test-dns-default-order-ipv{4,6}.js 2024-12-31 15:45:00 -08:00
Kai Tamkun
c8b431cff2 Implement --dns-result-order 2024-12-31 15:45:00 -08:00
Kai Tamkun
2b1ac64193 Obey DNS result order 2024-12-31 15:45:00 -08:00
Kai Tamkun
394a69970e Add support for default DNS lookup result order 2024-12-31 15:45:00 -08:00
Kai Tamkun
7031e822ec Add test-dns-channel-timeout.js 2024-12-31 15:45:00 -08:00
Kai Tamkun
032a05df21 Better ref()ing 2024-12-31 15:45:00 -08:00
Kai Tamkun
c786922c61 Fix test-dns.js 2024-12-31 15:45:00 -08:00
Kai Tamkun
74a6c066d8 Proper refcounting for DNSResolvers 2024-12-31 15:45:00 -08:00
Kai Tamkun
a79111a5ed Initial work on DNS timeout support
TODO: ref the DNSResolvers when creating promises
2024-12-31 15:45:00 -08:00
Kai Tamkun
07d0aa26f4 Per-resolver reversing 2024-12-31 15:45:00 -08:00
Kai Tamkun
6c1bef6827 Add test-dns-channel-cancel-promise.js 2024-12-31 15:45:00 -08:00
Kai Tamkun
29dec6e5a2 Implement Resolver.cancel() 2024-12-31 15:45:00 -08:00
Kai Tamkun
19d11e843d Add test-dns-lookupService.js 2024-12-31 15:45:00 -08:00
Kai Tamkun
98f5e749dd Properly emit invalid hostname warnings 2024-12-31 15:45:00 -08:00
Kai Tamkun
47eb157922 Honor type and code parameters of process.emitWarning 2024-12-31 15:45:00 -08:00
Kai Tamkun
15d49610b3 Don't overwrite "name" properties of warning objects 2024-12-31 15:45:00 -08:00
Kai Tamkun
b197c767d3 Add+fix test-dns-lookup.js 2024-12-31 15:45:00 -08:00
Kai Tamkun
547cf84a97 Include ws2def.h instead of netdb.h on Windows 2024-12-31 15:44:59 -08:00
Kai Tamkun
0ccdc5482d Fix Windows compilation probably 2024-12-31 15:44:59 -08:00
Kai Tamkun
b58e1df84a Customizable timeout and tries options for DNS resolvers 2024-12-31 15:44:59 -08:00
Kai Tamkun
fbbaeac8a6 Add test-dns-get-server.js 2024-12-31 15:44:59 -08:00
Kai Tamkun
00a56c7b58 Add test-dns.js 2024-12-31 15:44:59 -08:00
Kai Tamkun
dca054123c Fix A record resolution 2024-12-31 15:44:59 -08:00
Don Isaac
24634a10d0 fix(module-loader): use a more descriptive crash message (#15831)
Co-authored-by: Don Isaac <don@bun.sh>
Co-authored-by: DonIsaac <DonIsaac@users.noreply.github.com>
2024-12-31 15:44:59 -08:00
Jarred Sumner
845ba7e4a4 Bump WebKit (#15828)
Co-authored-by: Dylan Conway <35280289+dylan-conway@users.noreply.github.com>
2024-12-31 15:44:59 -08:00
Dylan Conway
541825043b fix(install): npm version to git resolution package-lock.json migration (#15810) 2024-12-31 15:44:59 -08:00
Michael H
b42d914ea1 vscode: allow trailing comma in bun.lock (#15747) 2024-12-31 15:44:59 -08:00
190n
e161d054f2 fix(napi): set lossless parameter in napi_get_value_bigint_{int64,uint64}, and trim leading zeroes in napi_create_bigint_words (#15804) 2024-12-31 15:44:59 -08:00
Dylan Conway
361fa58b4c add install.saveTextLockfile to bunfig.toml (#15827) 2024-12-31 15:44:59 -08:00
Jarred Sumner
e7153af2ad Fix process.on from non-mainthread (#15825) 2024-12-31 15:44:59 -08:00
Jarred Sumner
57ea50da4f Bump 2024-12-31 15:44:59 -08:00
Jarred Sumner
d9bce21a29 Update plugins.md 2024-12-31 15:44:59 -08:00
Jarred Sumner
2c1cf4e62b Update plugins.md 2024-12-31 15:44:59 -08:00
Jarred Sumner
7ac1067060 Update plugins.md 2024-12-31 15:44:59 -08:00
Jarred Sumner
c933ec0865 Move bundler plugin docs 2024-12-31 15:44:59 -08:00
Jarred Sumner
ae08844a44 Be more careful about setting the rlimit max 2024-12-31 15:44:59 -08:00
Don Isaac
e5c1d5315c fix: Bun.deepMatch on circular objects causing segfault (#15672)
Co-authored-by: Don Isaac <don@bun.sh>
Co-authored-by: DonIsaac <DonIsaac@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-12-31 15:44:59 -08:00
dave caruso
696e2e2f13 pass all upstream node:os tests, all supported node:async_hooks tests (#15802) 2024-12-31 15:44:59 -08:00
Jarred Sumner
b22037a13e CI: Remove unnecessary config 2024-12-31 15:44:59 -08:00
Jarred Sumner
003381f2c9 CI: test concurrency group 2024-12-31 15:44:59 -08:00
Jarred Sumner
9cc5374244 CI: cancel previous canary build 2024-12-31 15:44:59 -08:00
Jarred Sumner
219c067b69 Rename estimateDirectMemoryUsageOf to estimateShallowMemoryUsageOf 2024-12-31 15:44:59 -08:00
Jarred Sumner
d8043f9a5c Fix heap snapshots memory usage stats. Introduce estimateDirectMemoryUsageOf function in "bun:jsc" (#15790) 2024-12-31 15:44:59 -08:00
Zack Radisic
f2063ffe87 CSS fixes (#15806) 2024-12-31 15:44:59 -08:00
pfg
291c035adf Add lldb scripts for zig & jsc (#15807) 2024-12-31 15:44:59 -08:00
Jarred Sumner
60330a810e Prevent unnecessary postinstall script from causing bun install to hang in unreliable networks 2024-12-31 15:44:59 -08:00
Michael H
b4504e6469 vscode extension: use new debug terminal provider (#15801)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-12-31 15:44:59 -08:00
Ashcon Partovi
2bbbbef7c8 ci: Retry and detect flaky tests (#15798) 2024-12-31 15:44:58 -08:00
Dylan Conway
350ceb1c50 Use the same hoisting logic for text lockfile (#15778) 2024-12-31 15:44:58 -08:00
Michael H
547f069580 attempt to fix debugger (#15788)
Co-authored-by: RiskyMH <RiskyMH@users.noreply.github.com>
2024-12-31 15:44:58 -08:00
Jarred Sumner
a6c5bafe01 Fix text input with ink (#15800) 2024-12-31 15:44:58 -08:00
190n
6db95a891a fix(napi): Make napi_wrap work on regular objects (#15622)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-12-31 15:44:58 -08:00
Jarred Sumner
3bf6fe9384 ✂️ 2024-12-31 15:44:58 -08:00
Sam
041887e814 docs: dns.prefetch doesn't require port anymore (#15792) 2024-12-31 15:44:58 -08:00
Michael H
fe364ca0a4 document npm:<package-name> in install docs (#15754) 2024-12-31 15:44:58 -08:00
Jarred Sumner
197c3db868 Deflake doesnt_crash.test.ts 2024-12-31 15:44:58 -08:00
github-actions[bot]
c99f8ae2ee deps: update c-ares to v1.34.4 (#15773)
Co-authored-by: Jarred-Sumner <Jarred-Sumner@users.noreply.github.com>
2024-12-31 15:44:58 -08:00
Jarred Sumner
59f521d477 Deflake require.cache test 2024-12-31 15:44:58 -08:00
Jarred Sumner
dfb47d846f Deflake process test 2024-12-31 15:44:58 -08:00
Brian Donovan
e08573eb06 docs(bun-native-plugin-rs): fix typos (#15764) 2024-12-31 15:44:58 -08:00
Jarred Sumner
79395dac0c Add "bin" field to bun.lock (#15763)
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
2024-12-31 15:44:58 -08:00
Jarred Sumner
a8f3bf2b3a Make git dependencies faster + further optimize bun install (#15771) 2024-12-31 15:44:58 -08:00
Jarred Sumner
cfd6f7c164 Don't open node_modules 1,618 times (#15762) 2024-12-31 15:44:58 -08:00
Jarred Sumner
97a63fbe3f Add debugger to entitlements plist 2024-12-31 15:44:58 -08:00
Jarred Sumner
ad57ced58d Delete incorrect debug assertion 2024-12-31 15:44:58 -08:00
Dylan Conway
897d4a5ec3 bun.lock: fix --frozen-lockfile and resolving extra dependencies (#15748) 2024-12-31 15:44:58 -08:00
Jarred Sumner
e20d0082d5 Make --expose gc work in nodetests 2024-12-31 15:44:58 -08:00
Meghan Denny
517bc28da8 node: fix test-zlib-from-gzip-with-trailing-garbage.js (#15757) 2024-12-31 15:44:58 -08:00
Jarred Sumner
0d3be0aca0 fix clangd 2024-12-31 15:44:58 -08:00
Jarred Sumner
a9de84c1e9 Fix flaky signal handlers on posix (#15751) 2024-12-31 15:44:58 -08:00
Kai Tamkun
2462dc33c9 Don't do regular lookup for A/AAAA 2024-12-31 15:44:58 -08:00
Kai Tamkun
3e3941797b Add ENOSERVER 2024-12-31 15:44:58 -08:00
Kai Tamkun
b8205e0b70 There isn't actually a _socket parameter to dgram sockets' error handlers 2024-12-31 15:44:58 -08:00
Kai Tamkun
52002a1088 No more NAPTR in resolveAny 2024-12-31 15:44:58 -08:00
Kai Tamkun
18fee6090c Isolate promisified Resolver objects 2024-12-31 15:44:58 -08:00
Kai Tamkun
0b509dbf89 Simplify some Zig again 2024-12-31 15:44:58 -08:00
Kai Tamkun
aa61b7bd1d Some dns.ts fixes 2024-12-31 15:44:58 -08:00
Kai Tamkun
bdf1db838a Simplify some Zig 2024-12-31 15:44:58 -08:00
Kai Tamkun
26f99caac3 Remove a comment that's not quite true anymore 2024-12-31 15:44:58 -08:00
Kai Tamkun
7997556699 Glorious promises 2024-12-31 15:44:58 -08:00
Kai Tamkun
1c12cbb561 CAA record support 2024-12-31 15:44:58 -08:00
Kai Tamkun
bcfa11d4e6 Implement resolveAny via horrible callback hell 2024-12-31 15:44:58 -08:00
Kai Tamkun
de67a6fa9e Isolated resolution 2024-12-31 15:44:58 -08:00
Kai Tamkun
614a6abdff Initial work on dns.Resolver isolation 2024-12-31 15:44:58 -08:00
Kai Tamkun
2bdf9d63e6 Better DNS error objects 2024-12-31 15:44:58 -08:00
Kai Tamkun
22fa28f102 Better DNS lookup 2024-12-31 15:44:58 -08:00
Kai Tamkun
8215e18a1a Expose AI_ALL, AI_ADDRCONFIG and AI_V4MAPPED from netdb.h under dns 2024-12-31 15:44:58 -08:00
Kai Tamkun
e052252c63 Add an argument to ERR_INVALID_IP_ADDRESS 2024-12-31 15:44:58 -08:00
Kai Tamkun
ed7d361b4c Special case for falsey hostnames in dns.lookup 2024-12-31 15:44:58 -08:00
Kai Tamkun
18d3bda19a Add dns.setServers 2024-12-31 15:44:55 -08:00
52 changed files with 3855 additions and 671 deletions

2
LATEST
View File

@@ -1 +1 @@
1.1.41
1.1.41

View File

@@ -72,6 +72,24 @@ $ bun install --yarn
print = "yarn"
```
### Text-based lockfile
Bun v1.1.39 introduced `bun.lock`, a JSONC formatted lockfile. `bun.lock` is human-readable and git-diffable without configuration, at [no cost to performance](https://bun.sh/blog/bun-lock-text-lockfile#cached-bun-install-gets-30-faster).
To generate the lockfile, use `--save-text-lockfile` with `bun install`. You can do this for new projects and existing projects already using `bun.lockb` (resolutions will be preserved).
```bash
$ bun install --save-text-lockfile
$ head -n3 bun.lock
{
"lockfileVersion": 0,
"workspaces": {
```
Once `bun.lock` is generated, Bun will use it for all subsequent installs and updates through commands that read and modify the lockfile. If both lockfiles exist, `bun.lock` will be choosen over `bun.lockb`.
Bun v1.2.0 will switch the default lockfile format to `bun.lock`.
{% /codetabs %}
### Text-based lockfile

View File

@@ -1034,6 +1034,10 @@ declare module "bun" {
errors: number;
totalCount: number;
};
ADDRCONFIG: number;
ALL: number;
V4MAPPED: number;
};
interface DNSLookup {

View File

@@ -49,6 +49,13 @@ const spawnTimeout = 5_000;
const testTimeout = 3 * 60_000;
const integrationTimeout = 5 * 60_000;
function getNodeParallelTestTimeout(testPath) {
if (testPath.includes("test-dns")) {
return 45_000;
}
return 10_000;
}
const { values: options, positionals: filters } = parseArgs({
allowPositionals: true,
options: {
@@ -251,7 +258,7 @@ async function runTests() {
const { ok, error, stdout } = await spawnBun(execPath, {
cwd: cwd,
args: [title],
timeout: 10_000,
timeout: getNodeParallelTestTimeout(title),
env: {
FORCE_COLOR: "0",
},

View File

@@ -10,6 +10,7 @@ const Async = @import("async");
const uv = bun.windows.libuv;
const StatWatcherScheduler = @import("../node/node_fs_stat_watcher.zig").StatWatcherScheduler;
const Timer = @This();
const DNSResolver = @import("./bun/dns_resolver.zig").DNSResolver;
/// TimeoutMap is map of i32 to nullable Timeout structs
/// i32 is exposed to JavaScript and can be used with clearTimeout, clearInterval, etc.
@@ -730,6 +731,7 @@ pub const EventLoopTimer = struct {
TestRunner,
StatWatcherScheduler,
UpgradedDuplex,
DNSResolver,
WindowsNamedPipe,
PostgresSQLConnectionTimeout,
PostgresSQLConnectionMaxLifetime,
@@ -741,6 +743,7 @@ pub const EventLoopTimer = struct {
.TestRunner => JSC.Jest.TestRunner,
.StatWatcherScheduler => StatWatcherScheduler,
.UpgradedDuplex => uws.UpgradedDuplex,
.DNSResolver => DNSResolver,
.WindowsNamedPipe => uws.WindowsNamedPipe,
.PostgresSQLConnectionTimeout => JSC.Postgres.PostgresSQLConnection,
.PostgresSQLConnectionMaxLifetime => JSC.Postgres.PostgresSQLConnection,
@@ -754,6 +757,7 @@ pub const EventLoopTimer = struct {
UpgradedDuplex,
PostgresSQLConnectionTimeout,
PostgresSQLConnectionMaxLifetime,
DNSResolver,
pub fn Type(comptime T: Tag) type {
return switch (T) {
@@ -764,6 +768,7 @@ pub const EventLoopTimer = struct {
.UpgradedDuplex => uws.UpgradedDuplex,
.PostgresSQLConnectionTimeout => JSC.Postgres.PostgresSQLConnection,
.PostgresSQLConnectionMaxLifetime => JSC.Postgres.PostgresSQLConnection,
.DNSResolver => DNSResolver,
};
}
};
@@ -841,6 +846,10 @@ pub const EventLoopTimer = struct {
return .disarm;
}
if (comptime t.Type() == DNSResolver) {
return container.checkTimeouts(now, vm);
}
return container.callback(container);
},
}

File diff suppressed because it is too large Load Diff

View File

@@ -34,22 +34,32 @@
#include "ErrorCode.h"
#include "GeneratedBunObject.h"
BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__lookup);
BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolve);
BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolveSrv);
BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolveTxt);
BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolveSoa);
BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolveNaptr);
BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolveMx);
BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolveCaa);
BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolveNs);
BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolvePtr);
BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__resolveCname);
BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__getServers);
BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__reverse);
BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__lookupService);
BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__prefetch);
BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__getCacheStats);
#ifdef WIN32
#include <ws2def.h>
#else
#include <netdb.h>
#endif
BUN_DECLARE_HOST_FUNCTION(Bun__DNS__lookup);
BUN_DECLARE_HOST_FUNCTION(Bun__DNS__resolve);
BUN_DECLARE_HOST_FUNCTION(Bun__DNS__resolveSrv);
BUN_DECLARE_HOST_FUNCTION(Bun__DNS__resolveTxt);
BUN_DECLARE_HOST_FUNCTION(Bun__DNS__resolveSoa);
BUN_DECLARE_HOST_FUNCTION(Bun__DNS__resolveNaptr);
BUN_DECLARE_HOST_FUNCTION(Bun__DNS__resolveMx);
BUN_DECLARE_HOST_FUNCTION(Bun__DNS__resolveCaa);
BUN_DECLARE_HOST_FUNCTION(Bun__DNS__resolveNs);
BUN_DECLARE_HOST_FUNCTION(Bun__DNS__resolvePtr);
BUN_DECLARE_HOST_FUNCTION(Bun__DNS__resolveCname);
BUN_DECLARE_HOST_FUNCTION(Bun__DNS__resolveAny);
BUN_DECLARE_HOST_FUNCTION(Bun__DNS__getServers);
BUN_DECLARE_HOST_FUNCTION(Bun__DNS__setServers);
BUN_DECLARE_HOST_FUNCTION(Bun__DNS__reverse);
BUN_DECLARE_HOST_FUNCTION(Bun__DNS__lookupService);
BUN_DECLARE_HOST_FUNCTION(Bun__DNS__prefetch);
BUN_DECLARE_HOST_FUNCTION(Bun__DNS__getCacheStats);
BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__new);
BUN_DECLARE_HOST_FUNCTION(Bun__DNSResolver__cancel);
BUN_DECLARE_HOST_FUNCTION(Bun__fetch);
BUN_DECLARE_HOST_FUNCTION(Bun__fetchPreconnect);
BUN_DECLARE_HOST_FUNCTION(Bun__randomUUIDv7);
@@ -336,37 +346,47 @@ static JSValue constructDNSObject(VM& vm, JSObject* bunObject)
{
JSGlobalObject* globalObject = bunObject->globalObject();
JSC::JSObject* dnsObject = JSC::constructEmptyObject(globalObject);
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "lookup"_s), 2, Bun__DNSResolver__lookup, ImplementationVisibility::Public, NoIntrinsic,
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "lookup"_s), 2, Bun__DNS__lookup, ImplementationVisibility::Public, NoIntrinsic,
JSC::PropertyAttribute::DontDelete | 0);
dnsObject->putDirectNativeFunction(vm, globalObject, vm.propertyNames->resolve, 2, Bun__DNSResolver__resolve, ImplementationVisibility::Public, NoIntrinsic,
dnsObject->putDirectNativeFunction(vm, globalObject, vm.propertyNames->resolve, 2, Bun__DNS__resolve, ImplementationVisibility::Public, NoIntrinsic,
JSC::PropertyAttribute::DontDelete | 0);
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "resolveSrv"_s), 2, Bun__DNSResolver__resolveSrv, ImplementationVisibility::Public, NoIntrinsic,
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "resolveSrv"_s), 2, Bun__DNS__resolveSrv, ImplementationVisibility::Public, NoIntrinsic,
JSC::PropertyAttribute::DontDelete | 0);
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "resolveTxt"_s), 2, Bun__DNSResolver__resolveTxt, ImplementationVisibility::Public, NoIntrinsic,
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "resolveTxt"_s), 2, Bun__DNS__resolveTxt, ImplementationVisibility::Public, NoIntrinsic,
JSC::PropertyAttribute::DontDelete | 0);
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "resolveSoa"_s), 2, Bun__DNSResolver__resolveSoa, ImplementationVisibility::Public, NoIntrinsic,
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "resolveSoa"_s), 2, Bun__DNS__resolveSoa, ImplementationVisibility::Public, NoIntrinsic,
JSC::PropertyAttribute::DontDelete | 0);
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "resolveNaptr"_s), 2, Bun__DNSResolver__resolveNaptr, ImplementationVisibility::Public, NoIntrinsic,
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "resolveNaptr"_s), 2, Bun__DNS__resolveNaptr, ImplementationVisibility::Public, NoIntrinsic,
JSC::PropertyAttribute::DontDelete | 0);
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "resolveMx"_s), 2, Bun__DNSResolver__resolveMx, ImplementationVisibility::Public, NoIntrinsic,
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "resolveMx"_s), 2, Bun__DNS__resolveMx, ImplementationVisibility::Public, NoIntrinsic,
JSC::PropertyAttribute::DontDelete | 0);
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "resolveCaa"_s), 2, Bun__DNSResolver__resolveCaa, ImplementationVisibility::Public, NoIntrinsic,
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "resolveCaa"_s), 2, Bun__DNS__resolveCaa, ImplementationVisibility::Public, NoIntrinsic,
JSC::PropertyAttribute::DontDelete | 0);
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "resolveNs"_s), 2, Bun__DNSResolver__resolveNs, ImplementationVisibility::Public, NoIntrinsic,
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "resolveNs"_s), 2, Bun__DNS__resolveNs, ImplementationVisibility::Public, NoIntrinsic,
JSC::PropertyAttribute::DontDelete | 0);
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "resolvePtr"_s), 2, Bun__DNSResolver__resolvePtr, ImplementationVisibility::Public, NoIntrinsic,
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "resolvePtr"_s), 2, Bun__DNS__resolvePtr, ImplementationVisibility::Public, NoIntrinsic,
JSC::PropertyAttribute::DontDelete | 0);
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "resolveCname"_s), 2, Bun__DNSResolver__resolveCname, ImplementationVisibility::Public, NoIntrinsic,
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "resolveCname"_s), 2, Bun__DNS__resolveCname, ImplementationVisibility::Public, NoIntrinsic,
JSC::PropertyAttribute::DontDelete | 0);
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "getServers"_s), 2, Bun__DNSResolver__getServers, ImplementationVisibility::Public, NoIntrinsic,
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "resolveAny"_s), 2, Bun__DNS__resolveAny, ImplementationVisibility::Public, NoIntrinsic,
JSC::PropertyAttribute::DontDelete | 0);
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "reverse"_s), 2, Bun__DNSResolver__reverse, ImplementationVisibility::Public, NoIntrinsic,
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "getServers"_s), 2, Bun__DNS__getServers, ImplementationVisibility::Public, NoIntrinsic,
JSC::PropertyAttribute::DontDelete | 0);
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "lookupService"_s), 2, Bun__DNSResolver__lookupService, ImplementationVisibility::Public, NoIntrinsic,
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "setServers"_s), 2, Bun__DNS__setServers, ImplementationVisibility::Public, NoIntrinsic,
JSC::PropertyAttribute::DontDelete | 0);
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "prefetch"_s), 2, Bun__DNSResolver__prefetch, ImplementationVisibility::Public, NoIntrinsic,
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "reverse"_s), 2, Bun__DNS__reverse, ImplementationVisibility::Public, NoIntrinsic,
JSC::PropertyAttribute::DontDelete | 0);
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "getCacheStats"_s), 0, Bun__DNSResolver__getCacheStats, ImplementationVisibility::Public, NoIntrinsic,
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "lookupService"_s), 2, Bun__DNS__lookupService, ImplementationVisibility::Public, NoIntrinsic,
JSC::PropertyAttribute::DontDelete | 0);
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "prefetch"_s), 2, Bun__DNS__prefetch, ImplementationVisibility::Public, NoIntrinsic,
JSC::PropertyAttribute::DontDelete | 0);
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "getCacheStats"_s), 0, Bun__DNS__getCacheStats, ImplementationVisibility::Public, NoIntrinsic,
JSC::PropertyAttribute::DontDelete | 0);
dnsObject->putDirect(vm, JSC::Identifier::fromString(vm, "ADDRCONFIG"_s), jsNumber(AI_ADDRCONFIG),
JSC::PropertyAttribute::DontDelete | 0);
dnsObject->putDirect(vm, JSC::Identifier::fromString(vm, "ALL"_s), jsNumber(AI_ALL),
JSC::PropertyAttribute::DontDelete | 0);
dnsObject->putDirect(vm, JSC::Identifier::fromString(vm, "V4MAPPED"_s), jsNumber(AI_V4MAPPED),
JSC::PropertyAttribute::DontDelete | 0);
return dnsObject;
}

View File

@@ -1149,6 +1149,24 @@ JSC_DEFINE_HOST_FUNCTION(Process_emitWarning, (JSGlobalObject * lexicalGlobalObj
return err;
})();
if (callFrame->argumentCount() > 1) {
auto clientData = WebCore::clientData(vm);
JSValue arg1 = callFrame->uncheckedArgument(1);
if (arg1.isString()) {
errorInstance->putDirect(vm, vm.propertyNames->name, arg1, JSC::PropertyAttribute::DontEnum | 0);
if (callFrame->argumentCount() > 2) {
if (JSValue arg2 = callFrame->uncheckedArgument(2); arg2.isString()) {
errorInstance->putDirect(vm, clientData->builtinNames().codePublicName(), arg2, JSC::PropertyAttribute::DontEnum | 0);
}
}
}
// TODO: handle `options` and `ctor` parameters
}
if (!errorInstance->hasProperty(lexicalGlobalObject, vm.propertyNames->name)) {
errorInstance->putDirect(vm, vm.propertyNames->name, jsString(vm, String("warn"_s)), JSC::PropertyAttribute::DontEnum | 0);
}
auto ident = Identifier::fromString(vm, "warning"_s);
if (process->wrapped().hasEventListeners(ident)) {
JSC::MarkedArgumentBuffer args;

View File

@@ -811,6 +811,16 @@ JSC_DEFINE_HOST_FUNCTION(Bun::jsFunctionMakeErrorWithCode, (JSC::JSGlobalObject
return JSValue::encode(ERR_INVALID_ARG_TYPE(scope, globalObject, arg0, arg1, arg2));
}
case Bun::ErrorCode::ERR_INVALID_IP_ADDRESS: {
JSValue arg0 = callFrame->argument(1);
auto param = arg0.toWTFString(globalObject);
RETURN_IF_EXCEPTION(scope, {});
return JSValue::encode(createError(globalObject, ErrorCode::ERR_INVALID_IP_ADDRESS, makeString("Invalid IP address: "_s, param)));
}
default: {
break;
}

View File

@@ -52,6 +52,7 @@ export default [
["ERR_SCRIPT_EXECUTION_TIMEOUT", Error, "Error"],
["ERR_SCRIPT_EXECUTION_INTERRUPTED", Error, "Error"],
["ERR_UNHANDLED_ERROR", Error],
["ERR_INVALID_IP_ADDRESS", TypeError, "TypeError"],
// Bun-specific
["ERR_FORMDATA_PARSE_ERROR", TypeError],
@@ -62,6 +63,9 @@ export default [
// Console
["ERR_CONSOLE_WRITABLE_STREAM", TypeError, "TypeError"],
// DNS
["ERR_DNS_SET_SERVERS_FAILED", Error],
// NET
["ERR_SOCKET_CLOSED_BEFORE_CONNECTION", Error],
["ERR_SOCKET_CLOSED", Error],

View File

@@ -1981,6 +1981,12 @@ JSC__JSValue SystemError__toErrorInstance(const SystemError* arg0,
JSC::PropertyAttribute::DontDelete | 0);
}
if (err.hostname.tag != BunStringTag::Empty) {
JSC::JSValue hostname = Bun::toJS(globalObject, err.hostname);
result->putDirect(vm, names.hostnamePublicName(), hostname,
JSC::PropertyAttribute::DontDelete | 0);
}
result->putDirect(vm, names.errnoPublicName(), JSC::JSValue(err.errno_),
JSC::PropertyAttribute::DontDelete | 0);

View File

@@ -1711,6 +1711,7 @@ pub const SystemError = extern struct {
message: String = String.empty,
path: String = String.empty,
syscall: String = String.empty,
hostname: String = String.empty,
fd: bun.FileDescriptor = bun.toFD(-1),
pub fn Maybe(comptime Result: type) type {
@@ -1735,6 +1736,7 @@ pub const SystemError = extern struct {
this.code.deref();
this.message.deref();
this.syscall.deref();
this.hostname.deref();
}
pub fn ref(this: *SystemError) void {
@@ -1742,15 +1744,11 @@ pub const SystemError = extern struct {
this.code.ref();
this.message.ref();
this.syscall.ref();
this.hostname.ref();
}
pub fn toErrorInstance(this: *const SystemError, global: *JSGlobalObject) JSValue {
defer {
this.path.deref();
this.code.deref();
this.message.deref();
this.syscall.deref();
}
defer this.deref();
return shim.cppFn("toErrorInstance", .{ this, global });
}
@@ -1775,12 +1773,7 @@ pub const SystemError = extern struct {
/// implementing follows this convention. It is exclusively used
/// to match the error code that `node:os` throws.
pub fn toErrorInstanceWithInfoObject(this: *const SystemError, global: *JSGlobalObject) JSValue {
defer {
this.path.deref();
this.code.deref();
this.message.deref();
this.syscall.deref();
}
defer this.deref();
return SystemError__toErrorInstanceWithInfoObject(this, global);
}
@@ -3116,6 +3109,16 @@ pub const JSGlobalObject = opaque {
return this.throwValue(this.createInvalidArgumentType(name_, field, typename));
}
pub fn throwInvalidArgumentValue(
this: *JSGlobalObject,
argname: []const u8,
value: JSValue,
) bun.JSError {
var formatter = JSC.ConsoleObject.Formatter{ .globalThis = this };
defer formatter.deinit();
return this.ERR_INVALID_ARG_VALUE("The \"{s}\" argument is invalid. Received {}", .{ argname, value.toFmt(&formatter) }).throw();
}
pub fn throwInvalidArgumentTypeValue(
this: *JSGlobalObject,
argname: []const u8,
@@ -3123,6 +3126,7 @@ pub const JSGlobalObject = opaque {
value: JSValue,
) bun.JSError {
var formatter = JSC.ConsoleObject.Formatter{ .globalThis = this };
defer formatter.deinit();
return this.ERR_INVALID_ARG_TYPE("The \"{s}\" argument must be of type {s}. Received {}", .{ argname, typename, value.toFmt(&formatter) }).throw();
}
@@ -4203,6 +4207,25 @@ pub const JSValue = enum(i64) {
};
}
pub fn toPortNumber(this: JSValue, global: *JSGlobalObject) bun.JSError!u16 {
if (this.isNumber()) {
// const double = try this.toNumber(global);
const double = this.coerceToDouble(global);
if (std.math.isNan(double)) {
return JSC.Error.ERR_SOCKET_BAD_PORT.throw(global, "Invalid port number", .{});
}
const port = this.to(i64);
if (0 <= port and port <= 65535) {
return @as(u16, @truncate(@max(0, port)));
} else {
return JSC.Error.ERR_SOCKET_BAD_PORT.throw(global, "Port number out of range: {d}", .{port});
}
}
return JSC.Error.ERR_SOCKET_BAD_PORT.throw(global, "Invalid port number", .{});
}
pub fn isInstanceOf(this: JSValue, global: *JSGlobalObject, constructor: JSValue) bool {
if (!this.isCell())
return false;
@@ -6796,6 +6819,7 @@ pub fn toJSHostFunction(comptime Function: JSHostZigFunction) JSC.JSHostFunction
if (value != .zero) {
if (globalThis.hasException()) {
var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis };
defer formatter.deinit();
bun.Output.prettyErrorln(
\\<r><red>Assertion failed<r>: Native function returned a non-zero JSValue while an exception is pending
\\

View File

@@ -77,4 +77,5 @@ pub const Classes = struct {
pub const NativeZlib = JSC.API.NativeZlib;
pub const NativeBrotli = JSC.API.NativeBrotli;
pub const FrameworkFileSystemRouter = bun.bake.FrameworkRouter.JSFrameworkRouter;
pub const DNSResolver = JSC.DNS.DNSResolver;
};

View File

@@ -124,6 +124,7 @@ typedef struct SystemError {
BunString message;
BunString path;
BunString syscall;
BunString hostname;
int fd;
} SystemError;

View File

@@ -83,6 +83,7 @@ const PendingResolution = @import("../resolver/resolver.zig").PendingResolution;
const ThreadSafeFunction = JSC.napi.ThreadSafeFunction;
const PackageManager = @import("../install/install.zig").PackageManager;
const IPC = @import("ipc.zig");
const DNSResolver = @import("api/bun/dns_resolver.zig").DNSResolver;
pub const GenericWatcher = @import("../watcher.zig");
const ModuleLoader = JSC.ModuleLoader;
@@ -790,6 +791,7 @@ pub const VirtualMachine = struct {
unhandled_pending_rejection_to_capture: ?*JSC.JSValue = null,
standalone_module_graph: ?*bun.StandaloneModuleGraph = null,
smol: bool = false,
dns_result_order: DNSResolver.Order = .verbatim,
hot_reload: bun.CLI.Command.HotReload = .none,
jsc: *JSC.VM = undefined,
@@ -1967,6 +1969,7 @@ pub const VirtualMachine = struct {
env_loader: ?*DotEnv.Loader = null,
store_fd: bool = false,
smol: bool = false,
dns_result_order: DNSResolver.Order = .verbatim,
// --print needs the result from evaluating the main module
eval: bool = false,
@@ -2060,6 +2063,7 @@ pub const VirtualMachine = struct {
vm.regular_event_loop.virtual_machine = vm;
vm.jsc = vm.global.vm();
vm.smol = opts.smol;
vm.dns_result_order = opts.dns_result_order;
if (opts.smol)
is_smol_mode = opts.smol;

View File

@@ -924,36 +924,36 @@ JSC_DEFINE_HOST_FUNCTION(functionPercentAvailableMemoryInUse, (JSGlobalObject *
// clang-format off
/* Source for BunJSCModuleTable.lut.h
@begin BunJSCModuleTable
callerSourceOrigin functionCallerSourceOrigin Function 0
jscDescribe functionDescribe Function 0
jscDescribeArray functionDescribeArray Function 0
drainMicrotasks functionDrainMicrotasks Function 0
edenGC functionEdenGC Function 0
fullGC functionFullGC Function 0
gcAndSweep functionGCAndSweep Function 0
getRandomSeed functionGetRandomSeed Function 0
heapSize functionHeapSize Function 0
heapStats functionMemoryUsageStatistics Function 0
startSamplingProfiler functionStartSamplingProfiler Function 0
samplingProfilerStackTraces functionSamplingProfilerStackTraces Function 0
noInline functionNeverInlineFunction Function 0
isRope functionIsRope Function 0
memoryUsage functionCreateMemoryFootprint Function 0
noFTL functionNoFTL Function 0
noOSRExitFuzzing functionNoOSRExitFuzzing Function 0
numberOfDFGCompiles functionNumberOfDFGCompiles Function 0
optimizeNextInvocation functionOptimizeNextInvocation Function 0
releaseWeakRefs functionReleaseWeakRefs Function 0
reoptimizationRetryCount functionReoptimizationRetryCount Function 0
setRandomSeed functionSetRandomSeed Function 0
startRemoteDebugger functionStartRemoteDebugger Function 0
totalCompileTime functionTotalCompileTime Function 0
getProtectedObjects functionGetProtectedObjects Function 0
generateHeapSnapshotForDebugging functionGenerateHeapSnapshotForDebugging Function 0
profile functionRunProfiler Function 0
setTimeZone functionSetTimeZone Function 0
serialize functionSerialize Function 0
deserialize functionDeserialize Function 0
callerSourceOrigin functionCallerSourceOrigin Function 0
jscDescribe functionDescribe Function 0
jscDescribeArray functionDescribeArray Function 0
drainMicrotasks functionDrainMicrotasks Function 0
edenGC functionEdenGC Function 0
fullGC functionFullGC Function 0
gcAndSweep functionGCAndSweep Function 0
getRandomSeed functionGetRandomSeed Function 0
heapSize functionHeapSize Function 0
heapStats functionMemoryUsageStatistics Function 0
startSamplingProfiler functionStartSamplingProfiler Function 0
samplingProfilerStackTraces functionSamplingProfilerStackTraces Function 0
noInline functionNeverInlineFunction Function 0
isRope functionIsRope Function 0
memoryUsage functionCreateMemoryFootprint Function 0
noFTL functionNoFTL Function 0
noOSRExitFuzzing functionNoOSRExitFuzzing Function 0
numberOfDFGCompiles functionNumberOfDFGCompiles Function 0
optimizeNextInvocation functionOptimizeNextInvocation Function 0
releaseWeakRefs functionReleaseWeakRefs Function 0
reoptimizationRetryCount functionReoptimizationRetryCount Function 0
setRandomSeed functionSetRandomSeed Function 0
startRemoteDebugger functionStartRemoteDebugger Function 0
totalCompileTime functionTotalCompileTime Function 0
getProtectedObjects functionGetProtectedObjects Function 0
generateHeapSnapshotForDebugging functionGenerateHeapSnapshotForDebugging Function 0
profile functionRunProfiler Function 0
setTimeZone functionSetTimeZone Function 0
serialize functionSerialize Function 0
deserialize functionDeserialize Function 0
estimateShallowMemoryUsageOf functionEstimateDirectMemoryUsageOf Function 1
percentAvailableMemoryInUse functionPercentAvailableMemoryInUse Function 0
@end

View File

@@ -1,6 +1,80 @@
import { define } from "../../codegen/class-definitions";
export default [
define({
name: "DNSResolver",
construct: false,
noConstructor: true,
finalize: true,
configurable: false,
klass: {},
proto: {
setServers: {
fn: "setServers",
length: 1,
},
getServers: {
fn: "getServers",
length: 0,
},
resolve: {
fn: "resolve",
length: 3,
},
resolveSrv: {
fn: "resolveSrv",
length: 1,
},
resolveTxt: {
fn: "resolveTxt",
length: 1,
},
resolveSoa: {
fn: "resolveSoa",
length: 1,
},
resolveNaptr: {
fn: "resolveNaptr",
length: 1,
},
resolveMx: {
fn: "resolveMx",
length: 1,
},
resolveCaa: {
fn: "resolveCaa",
length: 1,
},
resolveNs: {
fn: "resolveNs",
length: 1,
},
resolvePtr: {
fn: "resolvePtr",
length: 1,
},
resolveCname: {
fn: "resolveCname",
length: 1,
},
resolveAny: {
fn: "resolveAny",
length: 1,
},
setLocalAddress: {
fn: "setLocalAddress",
length: 1,
},
cancel: {
fn: "cancel",
length: 0,
},
reverse: {
fn: "reverse",
length: 1,
},
},
}),
define({
name: "FSWatcher",
construct: false,

View File

@@ -422,6 +422,7 @@ pub fn spawnIPCContext(rare: *RareData, vm: *JSC.VirtualMachine) *uws.SocketCont
pub fn globalDNSResolver(rare: *RareData, vm: *JSC.VirtualMachine) *JSC.DNS.DNSResolver {
if (rare.global_dns_data == null) {
rare.global_dns_data = JSC.DNS.GlobalData.init(vm.allocator, vm);
rare.global_dns_data.?.resolver.ref(); // live forever
}
return &rare.global_dns_data.?.resolver;

View File

@@ -30,6 +30,7 @@ const which = @import("which.zig").which;
const JSC = bun.JSC;
const AsyncHTTP = bun.http.AsyncHTTP;
const Arena = @import("./mimalloc_arena.zig").Arena;
const DNSResolver = @import("bun.js/api/bun/dns_resolver.zig").DNSResolver;
const OpaqueWrap = JSC.OpaqueWrap;
const VirtualMachine = JSC.VirtualMachine;
@@ -199,6 +200,7 @@ pub const Run = struct {
.smol = ctx.runtime_options.smol,
.eval = ctx.runtime_options.eval.eval_and_print,
.debugger = ctx.runtime_options.debugger,
.dns_result_order = DNSResolver.Order.fromStringOrDie(ctx.runtime_options.dns_result_order),
.is_main_thread = true,
},
),

View File

@@ -231,7 +231,8 @@ pub const Arguments = struct {
clap.parseParam("--conditions <STR>... Pass custom conditions to resolve") catch unreachable,
clap.parseParam("--fetch-preconnect <STR>... Preconnect to a URL while code is loading") catch unreachable,
clap.parseParam("--max-http-header-size <INT> Set the maximum size of HTTP headers in bytes. Default is 16KiB") catch unreachable,
clap.parseParam("--expose-internals Expose internals used for testing Bun itself. Usage of these APIs are completely unsupported.") catch unreachable,
clap.parseParam("--expose-internals Expose internals used for testing Bun itself. Usage of these APIs is completely unsupported.") catch unreachable,
clap.parseParam("--dns-result-order <STR> Set the default order of DNS lookup results. Valid orders: verbatim (default), ipv4first, ipv6first") catch unreachable,
};
const auto_or_run_params = [_]ParamType{
@@ -755,6 +756,10 @@ pub const Arguments = struct {
ctx.runtime_options.smol = args.flag("--smol");
ctx.runtime_options.preconnect = args.options("--fetch-preconnect");
if (args.option("--dns-result-order")) |order| {
ctx.runtime_options.dns_result_order = order;
}
if (args.option("--inspect")) |inspect_flag| {
ctx.runtime_options.debugger = if (inspect_flag.len == 0)
Command.Debugger{ .enable = .{} }
@@ -1479,6 +1484,7 @@ pub const Command = struct {
eval_and_print: bool = false,
} = .{},
preconnect: []const []const u8 = &[_][]const u8{},
dns_result_order: []const u8 = "verbatim",
};
var global_cli_ctx: Context = undefined;

View File

@@ -192,6 +192,7 @@ pub const Options = extern struct {
evsys: ares_evsys_t = 0,
server_failover_opts: struct_ares_server_failover_options = @import("std").mem.zeroes(struct_ares_server_failover_options),
};
pub const struct_hostent = extern struct {
h_name: [*c]u8,
h_aliases: [*c][*c]u8,
@@ -200,39 +201,34 @@ pub const struct_hostent = extern struct {
h_addr_list: [*c][*c]u8,
pub fn toJSResponse(this: *struct_hostent, _: std.mem.Allocator, globalThis: *JSC.JSGlobalObject, comptime lookup_name: []const u8) JSC.JSValue {
// A cname lookup always returns a single record but we follow the common API here.
if (comptime strings.eqlComptime(lookup_name, "cname")) {
if (this.h_name != null) {
const array = JSC.JSValue.createEmptyArray(globalThis, 1);
const h_name_len = bun.len(this.h_name);
const h_name_slice = this.h_name[0..h_name_len];
array.putIndex(globalThis, 0, JSC.ZigString.fromUTF8(h_name_slice).toJS(globalThis));
return array;
}
return JSC.JSValue.createEmptyArray(globalThis, 0);
} else {
if (this.h_aliases == null) {
// A cname lookup always returns a single record but we follow the common API here.
if (this.h_name == null) {
return JSC.JSValue.createEmptyArray(globalThis, 0);
}
var count: u32 = 0;
while (this.h_aliases[count] != null) {
count += 1;
}
const array = JSC.JSValue.createEmptyArray(globalThis, count);
count = 0;
while (this.h_aliases[count]) |alias| {
const alias_len = bun.len(alias);
const alias_slice = alias[0..alias_len];
array.putIndex(globalThis, count, JSC.ZigString.fromUTF8(alias_slice).toJS(globalThis));
count += 1;
}
return array;
return bun.String.toJSArray(globalThis, &[_]bun.String{bun.String.fromUTF8(this.h_name[0..bun.len(this.h_name)])});
}
if (this.h_aliases == null) {
return JSC.JSValue.createEmptyArray(globalThis, 0);
}
var count: u32 = 0;
while (this.h_aliases[count] != null) {
count += 1;
}
const array = JSC.JSValue.createEmptyArray(globalThis, count);
count = 0;
while (this.h_aliases[count]) |alias| {
const alias_len = bun.len(alias);
const alias_slice = alias[0..alias_len];
array.putIndex(globalThis, count, JSC.ZigString.fromUTF8(alias_slice).toJS(globalThis));
count += 1;
}
return array;
}
pub fn Callback(comptime Type: type) type {
@@ -269,7 +265,17 @@ pub const struct_hostent = extern struct {
}
var start: [*c]struct_hostent = undefined;
if (comptime strings.eqlComptime(lookup_name, "ns")) {
if (comptime strings.eqlComptime(lookup_name, "cname")) {
var addrttls: [256]struct_ares_addrttl = undefined;
var naddrttls: i32 = 256;
const result = ares_parse_a_reply(buffer, buffer_length, &start, &addrttls, &naddrttls);
if (result != ARES_SUCCESS) {
function(this, Error.get(result), timeouts, null);
return;
}
function(this, null, timeouts, start);
} else if (comptime strings.eqlComptime(lookup_name, "ns")) {
const result = ares_parse_ns_reply(buffer, buffer_length, &start);
if (result != ARES_SUCCESS) {
function(this, Error.get(result), timeouts, null);
@@ -283,16 +289,8 @@ pub const struct_hostent = extern struct {
return;
}
function(this, null, timeouts, start);
} else if (comptime strings.eqlComptime(lookup_name, "cname")) {
var addrttls: [256]struct_ares_addrttl = undefined;
var naddrttls: i32 = 256;
const result = ares_parse_a_reply(buffer, buffer_length, &start, &addrttls, &naddrttls);
if (result != ARES_SUCCESS) {
function(this, Error.get(result), timeouts, null);
return;
}
function(this, null, timeouts, start);
} else {
@compileError(std.fmt.comptimePrint("Unsupported struct_hostent record type: {s}", .{lookup_name}));
}
}
}.handle;
@@ -303,6 +301,129 @@ pub const struct_hostent = extern struct {
}
};
pub const hostent_with_ttls = struct {
hostent: *struct_hostent,
ttls: [256]c_int = [_]c_int{-1} ** 256,
pub fn toJSResponse(this: *hostent_with_ttls, _: std.mem.Allocator, globalThis: *JSC.JSGlobalObject, comptime lookup_name: []const u8) JSC.JSValue {
if (comptime strings.eqlComptime(lookup_name, "a") or strings.eqlComptime(lookup_name, "aaaa")) {
if (this.hostent.h_addr_list == null) {
return JSC.JSValue.createEmptyArray(globalThis, 0);
}
var count: u32 = 0;
while (this.hostent.h_addr_list[count] != null) {
count += 1;
}
const array = JSC.JSValue.createEmptyArray(globalThis, count);
count = 0;
const addressKey = JSC.ZigString.static("address").withEncoding();
const ttlKey = JSC.ZigString.static("ttl").withEncoding();
while (this.hostent.h_addr_list[count]) |addr| : (count += 1) {
const addrString = (if (this.hostent.h_addrtype == AF.INET6)
bun.dns.addressToJS(&std.net.Address.initIp6(addr[0..16].*, 0, 0, 0), globalThis)
else
bun.dns.addressToJS(&std.net.Address.initIp4(addr[0..4].*, 0), globalThis)) catch return globalThis.throwOutOfMemoryValue();
const ttl: ?c_int = if (count < this.ttls.len) this.ttls[count] else null;
const resultObject = JSC.JSValue.createObject2(globalThis, &addressKey, &ttlKey, addrString, if (ttl) |val| JSC.jsNumber(val) else .undefined);
array.putIndex(globalThis, count, resultObject);
}
return array;
} else {
@compileError(std.fmt.comptimePrint("Unsupported hostent_with_ttls record type: {s}", .{lookup_name}));
}
}
pub fn Callback(comptime Type: type) type {
return fn (*Type, status: ?Error, timeouts: i32, results: ?*hostent_with_ttls) void;
}
pub fn hostCallbackWrapper(
comptime Type: type,
comptime function: Callback(Type),
) ares_host_callback {
return &struct {
pub fn handle(ctx: ?*anyopaque, status: c_int, timeouts: c_int, hostent: ?*hostent_with_ttls) callconv(.C) void {
const this = bun.cast(*Type, ctx.?);
if (status != ARES_SUCCESS) {
function(this, Error.get(status), timeouts, null);
return;
}
function(this, null, timeouts, hostent);
}
}.handle;
}
pub fn callbackWrapper(
comptime lookup_name: []const u8,
comptime Type: type,
comptime function: Callback(Type),
) ares_callback {
return &struct {
pub fn handle(ctx: ?*anyopaque, status: c_int, timeouts: c_int, buffer: [*c]u8, buffer_length: c_int) callconv(.C) void {
const this = bun.cast(*Type, ctx.?);
if (status != ARES_SUCCESS) {
function(this, Error.get(status), timeouts, null);
return;
}
switch (parse(lookup_name, buffer, buffer_length)) {
.result => |result| function(this, null, timeouts, result),
.err => |err| function(this, err, timeouts, null),
}
}
}.handle;
}
pub fn parse(comptime lookup_name: []const u8, buffer: [*c]u8, buffer_length: c_int) JSC.Node.Maybe(*hostent_with_ttls, Error) {
var start: ?*struct_hostent = null;
if (comptime strings.eqlComptime(lookup_name, "a")) {
var addrttls: [256]struct_ares_addrttl = undefined;
var naddrttls: c_int = 256;
const result = ares_parse_a_reply(buffer, buffer_length, &start, &addrttls, &naddrttls);
if (result != ARES_SUCCESS) {
return .{ .err = Error.get(result).? };
}
var with_ttls = bun.default_allocator.create(hostent_with_ttls) catch bun.outOfMemory();
with_ttls.hostent = start.?;
for (addrttls[0..@intCast(naddrttls)], 0..) |ttl, i| {
with_ttls.ttls[i] = ttl.ttl;
}
return .{ .result = with_ttls };
}
if (comptime strings.eqlComptime(lookup_name, "aaaa")) {
var addr6ttls: [256]struct_ares_addr6ttl = undefined;
var naddr6ttls: c_int = 256;
const result = ares_parse_aaaa_reply(buffer, buffer_length, &start, &addr6ttls, &naddr6ttls);
if (result != ARES_SUCCESS) {
return .{ .err = Error.get(result).? };
}
var with_ttls = bun.default_allocator.create(hostent_with_ttls) catch bun.outOfMemory();
with_ttls.hostent = start.?;
for (addr6ttls[0..@intCast(naddr6ttls)], 0..) |ttl, i| {
with_ttls.ttls[i] = ttl.ttl;
}
return .{ .result = with_ttls };
}
@compileError(std.fmt.comptimePrint("Unsupported hostent_with_ttls record type: {s}", .{lookup_name}));
}
pub fn deinit(this: *hostent_with_ttls) void {
this.hostent.deinit();
bun.default_allocator.destroy(this);
}
};
pub const struct_nameinfo = extern struct {
node: [*c]u8,
service: [*c]u8,
@@ -351,7 +472,7 @@ pub const struct_nameinfo = extern struct {
}
};
pub const struct_timeval = opaque {};
pub const struct_timeval = std.posix.timeval;
pub const struct_Channeldata = opaque {};
pub const AddrInfo_cname = extern struct {
ttl: c_int,
@@ -389,10 +510,7 @@ pub const AddrInfo = extern struct {
globalThis: *JSC.JSGlobalObject,
) JSC.JSValue {
var node = addr_info.node orelse return JSC.JSValue.createEmptyArray(globalThis, 0);
const array = JSC.JSValue.createEmptyArray(
globalThis,
node.count(),
);
const array = JSC.JSValue.createEmptyArray(globalThis, node.count());
{
var j: u32 = 0;
@@ -462,8 +580,13 @@ pub const AddrInfo_hints = extern struct {
}
};
pub const ChannelOptions = struct {
timeout: ?i32 = null,
tries: ?i32 = null,
};
pub const Channel = opaque {
pub fn init(comptime Container: type, this: *Container) ?Error {
pub fn init(comptime Container: type, this: *Container, options: ChannelOptions) ?Error {
var channel: *Channel = undefined;
libraryInit();
@@ -483,8 +606,8 @@ pub const Channel = opaque {
opts.flags = ARES_FLAG_NOCHECKRESP;
opts.sock_state_cb = &SockStateWrap.onSockState;
opts.sock_state_cb_data = @as(*anyopaque, @ptrCast(this));
opts.timeout = -1;
opts.tries = 4;
opts.timeout = options.timeout orelse -1;
opts.tries = options.tries orelse 4;
const optmask: c_int =
ARES_OPT_FLAGS | ARES_OPT_TIMEOUTMS |
@@ -499,6 +622,10 @@ pub const Channel = opaque {
return null;
}
pub fn deinit(this: *Channel) void {
ares_destroy(this);
}
///
///The ares_getaddrinfo function initiates a host query by name on the name service channel identified by channel. The name and service parameters give the hostname and service as NULL-terminated C strings. The hints parameter is an ares_addrinfo_hints structure:
///
@@ -719,6 +846,7 @@ pub extern fn ares_create_query(name: [*c]const u8, dnsclass: c_int, @"type": c_
pub extern fn ares_mkquery(name: [*c]const u8, dnsclass: c_int, @"type": c_int, id: c_ushort, rd: c_int, buf: [*c][*c]u8, buflen: [*c]c_int) c_int;
pub extern fn ares_expand_name(encoded: [*c]const u8, abuf: [*c]const u8, alen: c_int, s: [*c][*c]u8, enclen: [*c]c_long) c_int;
pub extern fn ares_expand_string(encoded: [*c]const u8, abuf: [*c]const u8, alen: c_int, s: [*c][*c]u8, enclen: [*c]c_long) c_int;
pub extern fn ares_queue_active_queries(channel: *const Channel) usize;
const union_unnamed_2 = extern union {
_S6_u8: [16]u8,
};
@@ -726,7 +854,7 @@ pub const struct_ares_in6_addr = extern struct {
_S6_un: union_unnamed_2,
};
pub const struct_ares_addrttl = extern struct {
ipaddr: struct_in_addr,
ipaddr: u32,
ttl: c_int,
};
pub const struct_ares_addr6ttl = extern struct {
@@ -1009,6 +1137,28 @@ pub const struct_ares_txt_reply = extern struct {
return array;
}
pub fn toJSForAny(this: *struct_ares_txt_reply, _: std.mem.Allocator, globalThis: *JSC.JSGlobalObject, comptime _: []const u8) JSC.JSValue {
var count: usize = 0;
var txt: ?*struct_ares_txt_reply = this;
while (txt != null) : (txt = txt.?.next) {
count += 1;
}
const array = JSC.JSValue.createEmptyArray(globalThis, count);
txt = this;
var i: u32 = 0;
while (txt != null) : (txt = txt.?.next) {
var node = txt.?;
array.putIndex(globalThis, i, JSC.ZigString.fromUTF8(node.txt[0..node.length]).toJS(globalThis));
i += 1;
}
return JSC.JSObject.create(.{
.entries = array,
}, globalThis).toJS();
}
pub fn Callback(comptime Type: type) type {
return fn (*Type, status: ?Error, timeouts: i32, results: ?*struct_ares_txt_reply) void;
}
@@ -1218,6 +1368,207 @@ pub const struct_ares_uri_reply = extern struct {
uri: [*c]u8,
ttl: c_int,
};
pub const struct_any_reply = struct {
a_reply: ?*hostent_with_ttls = null,
aaaa_reply: ?*hostent_with_ttls = null,
mx_reply: ?*struct_ares_mx_reply = null,
ns_reply: ?*struct_hostent = null,
txt_reply: ?*struct_ares_txt_reply = null,
srv_reply: ?*struct_ares_srv_reply = null,
ptr_reply: ?*struct_hostent = null,
naptr_reply: ?*struct_ares_naptr_reply = null,
soa_reply: ?*struct_ares_soa_reply = null,
caa_reply: ?*struct_ares_caa_reply = null,
pub fn toJSResponse(this: *struct_any_reply, parent_allocator: std.mem.Allocator, globalThis: *JSC.JSGlobalObject, comptime _: []const u8) JSC.JSValue {
var stack = std.heap.stackFallback(2048, parent_allocator);
var arena = bun.ArenaAllocator.init(stack.get());
defer arena.deinit();
const allocator = arena.allocator();
return this.toJS(globalThis, allocator);
}
fn append(globalThis: *JSC.JSGlobalObject, array: JSC.JSValue, i: *u32, response: JSC.JSValue, comptime lookup_name: []const u8) void {
const transformed = if (response.isString())
JSC.JSObject.create(.{
.value = response,
}, globalThis).toJS()
else blk: {
bun.assert(response.isObject());
break :blk response;
};
var upper = comptime lookup_name[0..lookup_name.len].*;
inline for (&upper) |*char| {
char.* = std.ascii.toUpper(char.*);
}
transformed.put(globalThis, "type", bun.String.ascii(&upper).toJS(globalThis));
array.putIndex(globalThis, i.*, transformed);
i.* += 1;
}
fn appendAll(globalThis: *JSC.JSGlobalObject, allocator: std.mem.Allocator, array: JSC.JSValue, i: *u32, reply: anytype, comptime lookup_name: []const u8) void {
const response: JSC.JSValue = if (comptime @hasDecl(@TypeOf(reply.*), "toJSForAny"))
reply.toJSForAny(allocator, globalThis, lookup_name)
else
reply.toJSResponse(allocator, globalThis, lookup_name);
if (response.isArray()) {
var iterator = response.arrayIterator(globalThis);
while (iterator.next()) |item| {
append(globalThis, array, i, item, lookup_name);
}
} else {
append(globalThis, array, i, response, lookup_name);
}
}
pub fn toJS(this: *struct_any_reply, globalThis: *JSC.JSGlobalObject, allocator: std.mem.Allocator) JSC.JSValue {
const array = JSC.JSValue.createEmptyArray(globalThis, blk: {
var len: usize = 0;
inline for (comptime @typeInfo(struct_any_reply).Struct.fields) |field| {
if (comptime std.mem.endsWith(u8, field.name, "_reply")) {
len += @intFromBool(@field(this, field.name) != null);
}
}
break :blk len;
});
var i: u32 = 0;
inline for (comptime @typeInfo(struct_any_reply).Struct.fields) |field| {
if (comptime std.mem.endsWith(u8, field.name, "_reply")) {
if (@field(this, field.name)) |reply| {
const lookup_name = comptime field.name[0 .. field.name.len - "_reply".len];
appendAll(globalThis, allocator, array, &i, reply, lookup_name);
}
}
}
return array;
}
pub fn Callback(comptime Type: type) type {
return fn (*Type, status: ?Error, timeouts: i32, results: ?*struct_any_reply) void;
}
pub fn callbackWrapper(
comptime _: []const u8,
comptime Type: type,
comptime function: Callback(Type),
) ares_callback {
return &struct {
pub fn handleAny(ctx: ?*anyopaque, status: c_int, timeouts: c_int, buffer: [*c]u8, buffer_length: c_int) callconv(.C) void {
const this = bun.cast(*Type, ctx.?);
if (status != ARES_SUCCESS) {
function(this, Error.get(status), timeouts, null);
return;
}
var any_success = false;
var last_error: ?c_int = null;
var reply = bun.default_allocator.create(struct_any_reply) catch bun.outOfMemory();
reply.* = .{};
switch (hostent_with_ttls.parse("a", buffer, buffer_length)) {
.result => |result| {
reply.a_reply = result;
any_success = true;
},
.err => |err| last_error = @intFromEnum(err),
}
switch (hostent_with_ttls.parse("aaaa", buffer, buffer_length)) {
.result => |result| {
reply.aaaa_reply = result;
any_success = true;
},
.err => |err| last_error = @intFromEnum(err),
}
var result = ares_parse_mx_reply(buffer, buffer_length, &reply.mx_reply);
if (result == ARES_SUCCESS) {
any_success = true;
} else {
last_error = result;
}
result = ares_parse_ns_reply(buffer, buffer_length, &reply.ns_reply);
if (result == ARES_SUCCESS) {
any_success = true;
} else {
last_error = result;
}
result = ares_parse_txt_reply(buffer, buffer_length, &reply.txt_reply);
if (result == ARES_SUCCESS) {
any_success = true;
} else {
last_error = result;
}
result = ares_parse_srv_reply(buffer, buffer_length, &reply.srv_reply);
if (result == ARES_SUCCESS) {
any_success = true;
} else {
last_error = result;
}
result = ares_parse_ptr_reply(buffer, buffer_length, null, 0, AF.INET, &reply.ptr_reply);
if (result == ARES_SUCCESS) {
any_success = true;
} else {
last_error = result;
}
result = ares_parse_naptr_reply(buffer, buffer_length, &reply.naptr_reply);
if (result == ARES_SUCCESS) {
any_success = true;
} else {
last_error = result;
}
result = ares_parse_soa_reply(buffer, buffer_length, &reply.soa_reply);
if (result == ARES_SUCCESS) {
any_success = true;
} else {
last_error = result;
}
result = ares_parse_caa_reply(buffer, buffer_length, &reply.caa_reply);
if (result == ARES_SUCCESS) {
any_success = true;
} else {
last_error = result;
}
if (!any_success) {
reply.deinit();
function(this, Error.get(last_error.?), timeouts, null);
return;
}
function(this, null, timeouts, reply);
}
}.handleAny;
}
pub fn deinit(this: *struct_any_reply) void {
inline for (@typeInfo(struct_any_reply).Struct.fields) |field| {
if (comptime std.mem.endsWith(u8, field.name, "_reply")) {
if (@field(this, field.name)) |reply| {
reply.deinit();
}
}
}
bun.default_allocator.destroy(this);
}
};
pub extern fn ares_parse_a_reply(abuf: [*c]const u8, alen: c_int, host: [*c]?*struct_hostent, addrttls: [*c]struct_ares_addrttl, naddrttls: [*c]c_int) c_int;
pub extern fn ares_parse_aaaa_reply(abuf: [*c]const u8, alen: c_int, host: [*c]?*struct_hostent, addrttls: [*c]struct_ares_addr6ttl, naddrttls: [*c]c_int) c_int;
pub extern fn ares_parse_caa_reply(abuf: [*c]const u8, alen: c_int, caa_out: [*c][*c]struct_ares_caa_reply) c_int;
@@ -1288,6 +1639,7 @@ pub const ARES_ELOADIPHLPAPI = 22;
pub const ARES_EADDRGETNETWORKPARAMS = 23;
pub const ARES_ECANCELLED = 24;
pub const ARES_ESERVICE = 25;
pub const ARES_ENOSERVER = 26;
pub const Error = enum(i32) {
ENODATA = ARES_ENODATA,
@@ -1315,25 +1667,105 @@ pub const Error = enum(i32) {
EADDRGETNETWORKPARAMS = ARES_EADDRGETNETWORKPARAMS,
ECANCELLED = ARES_ECANCELLED,
ESERVICE = ARES_ESERVICE,
ENOSERVER = ARES_ENOSERVER,
const Deferred = struct {
errno: Error,
syscall: []const u8,
hostname: ?bun.String,
promise: JSC.JSPromise.Strong,
pub usingnamespace bun.New(@This());
pub fn init(errno: Error, syscall: []const u8, hostname: ?bun.String, promise: JSC.JSPromise.Strong) *Deferred {
return Deferred.new(.{
.errno = errno,
.syscall = syscall,
.hostname = hostname,
.promise = promise,
});
}
pub fn reject(this: *Deferred, globalThis: *JSC.JSGlobalObject) void {
const system_error = JSC.SystemError{
.errno = @intFromEnum(this.errno),
.code = bun.String.static(this.errno.code()),
.message = if (this.hostname) |hostname| bun.String.createFormat("{s} {s} {s}", .{ this.syscall, this.errno.code()[4..], hostname }) catch bun.outOfMemory() else bun.String.empty,
.syscall = bun.String.createUTF8(this.syscall),
.hostname = this.hostname orelse bun.String.empty,
};
const instance = system_error.toErrorInstance(globalThis);
instance.put(globalThis, "name", bun.String.static("DNSException").toJS(globalThis));
this.promise.reject(globalThis, instance);
this.hostname = null;
this.deinit();
}
pub fn rejectLater(this: *Deferred, globalThis: *JSC.JSGlobalObject) void {
const Context = struct {
deferred: *Deferred,
globalThis: *JSC.JSGlobalObject,
pub fn callback(context: *@This()) void {
context.deferred.reject(context.globalThis);
}
};
const context = bun.default_allocator.create(Context) catch bun.outOfMemory();
context.deferred = this;
context.globalThis = globalThis;
// TODO(@heimskr): new custom Task type
globalThis.bunVM().enqueueTask(JSC.ManagedTask.New(Context, Context.callback).init(context));
}
pub fn deinit(this: *@This()) void {
if (this.hostname) |hostname| {
hostname.deref();
}
this.promise.deinit();
this.destroy();
}
};
pub fn toDeferred(this: Error, syscall: []const u8, hostname: ?[]const u8, promise: *JSC.JSPromise.Strong) *Deferred {
const host_string: ?bun.String = if (hostname) |host|
bun.String.createUTF8(host)
else
null;
defer promise.* = .{};
return Deferred.init(this, syscall, host_string, promise.*);
}
pub fn toJS(this: Error, globalThis: *JSC.JSGlobalObject) JSC.JSValue {
const error_value = globalThis.createErrorInstance("{s}", .{this.label()});
error_value.put(
globalThis,
JSC.ZigString.static("name"),
bun.String.static("DNSException").toJS(globalThis),
);
error_value.put(
globalThis,
JSC.ZigString.static("code"),
JSC.ZigString.init(this.code()).toJS(globalThis),
);
error_value.put(
globalThis,
JSC.ZigString.static("errno"),
JSC.jsNumber(@intFromEnum(this)),
);
return error_value;
const instance = (JSC.SystemError{
.errno = @intFromEnum(this),
.code = bun.String.static(this.code()),
}).toErrorInstance(globalThis);
instance.put(globalThis, "name", bun.String.static("DNSException").toJS(globalThis));
return instance;
}
pub fn toJSWithSyscall(this: Error, globalThis: *JSC.JSGlobalObject, comptime syscall: []const u8) JSC.JSValue {
const instance = (JSC.SystemError{
.errno = @intFromEnum(this),
.code = bun.String.static(this.code()),
.syscall = bun.String.static((syscall ++ "\x00")[0..syscall.len :0]),
}).toErrorInstance(globalThis);
instance.put(globalThis, "name", bun.String.static("DNSException").toJS(globalThis));
return instance;
}
pub fn toJSWithSyscallAndHostname(this: Error, globalThis: *JSC.JSGlobalObject, comptime syscall: []const u8, hostname: []const u8) JSC.JSValue {
const instance = (JSC.SystemError{
.errno = @intFromEnum(this),
.code = bun.String.static(this.code()),
.message = bun.String.createFormat("{s} {s} {s}", .{ syscall, this.code()[4..], hostname }) catch bun.outOfMemory(),
.syscall = bun.String.static((syscall ++ "\x00")[0..syscall.len :0]),
.hostname = bun.String.createUTF8(hostname),
}).toErrorInstance(globalThis);
instance.put(globalThis, "name", bun.String.static("DNSException").toJS(globalThis));
return instance;
}
pub fn initEAI(rc: i32) ?Error {
@@ -1422,6 +1854,7 @@ pub const Error = enum(i32) {
.{ .EADDRGETNETWORKPARAMS, "DNS_EADDRGETNETWORKPARAMS" },
.{ .ECANCELLED, "DNS_ECANCELLED" },
.{ .ESERVICE, "DNS_ESERVICE" },
.{ .ENOSERVER, "DNS_ENOSERVER" },
});
pub const label = bun.enumMap(Error, .{
@@ -1450,6 +1883,7 @@ pub const Error = enum(i32) {
.{ .EADDRGETNETWORKPARAMS, "EADDRGETNETWORKPARAMS" },
.{ .ECANCELLED, "DNS query cancelled" },
.{ .ESERVICE, "Service not available" },
.{ .ENOSERVER, "No DNS servers were configured" },
});
pub fn get(rc: i32) ?Error {
@@ -1460,8 +1894,8 @@ pub const Error = enum(i32) {
return switch (rc) {
0 => null,
1...ARES_ESERVICE => @as(Error, @enumFromInt(rc)),
-ARES_ESERVICE...-1 => @as(Error, @enumFromInt(-rc)),
1...ARES_ENOSERVER => @as(Error, @enumFromInt(rc)),
-ARES_ENOSERVER...-1 => @as(Error, @enumFromInt(-rc)),
else => unreachable,
};
}

View File

@@ -3,6 +3,12 @@ const std = @import("std");
const JSC = bun.JSC;
const JSValue = JSC.JSValue;
const netdb = if (bun.Environment.isWindows) .{
.AI_V4MAPPED = @as(c_int, 2048),
.AI_ADDRCONFIG = @as(c_int, 1024),
.AI_ALL = @as(c_int, 256),
} else @cImport(@cInclude("netdb.h"));
pub const GetAddrInfo = struct {
name: []const u8 = "",
port: u16 = 0,
@@ -95,6 +101,9 @@ pub const GetAddrInfo = struct {
return error.InvalidFlags;
options.flags = flags.coerce(i32, globalObject);
if (options.flags & ~(netdb.AI_ALL | netdb.AI_ADDRCONFIG | netdb.AI_V4MAPPED) != 0)
return error.InvalidFlags;
}
return options;

View File

@@ -818,7 +818,7 @@ pub const Stringifier = struct {
any = true;
}
try writer.writeAll(
\\ "os":
\\ "os":
);
try Negatable(Npm.OperatingSystem).toJson(meta.os, writer);
}
@@ -830,7 +830,7 @@ pub const Stringifier = struct {
any = true;
}
try writer.writeAll(
\\ "cpu":
\\ "cpu":
);
try Negatable(Npm.Architecture).toJson(meta.arch, writer);
}

View File

@@ -913,6 +913,7 @@ pub const Tree = struct {
// 1 (return hoisted) - de-duplicate (skip) the package
// 2 (return id) - move the package to the top directory
// 3 (return dependency_loop) - leave the package at the same (relative) directory
// 4 (replace) - replace the existing (same name) parent dependency
fn hoistDependency(
this: *Tree,
comptime as_defined: bool,

View File

@@ -354,7 +354,7 @@ Socket.prototype.bind = function (port_, address_ /* , callback */) {
family,
});
},
error: (_socket, error) => {
error: error => {
this.emit("error", error);
},
},

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -220,8 +220,6 @@ test("dns.resolveNs (empty string) ", () => {
dns.resolveNs("", (err, results) => {
try {
expect(err).toBeNull();
console.log("resolveNs:", results);
expect(results instanceof Array).toBe(true);
// root servers
expect(results.sort()).toStrictEqual(
@@ -254,7 +252,6 @@ test("dns.resolvePtr (ptr.socketify.dev)", () => {
dns.resolvePtr("ptr.socketify.dev", (err, results) => {
try {
expect(err).toBeNull();
console.log("resolvePtr:", results);
expect(results instanceof Array).toBe(true);
expect(results[0]).toBe("bun.sh");
resolve();
@@ -270,7 +267,6 @@ test("dns.resolveCname (cname.socketify.dev)", () => {
dns.resolveCname("cname.socketify.dev", (err, results) => {
try {
expect(err).toBeNull();
console.log("resolveCname:", results);
expect(results instanceof Array).toBe(true);
expect(results[0]).toBe("bun.sh");
resolve();
@@ -427,7 +423,7 @@ describe("test invalid arguments", () => {
}).toThrow("Expected address to be a non-empty string for 'lookupService'.");
expect(() => {
dns.lookupService("google.com", 443, (err, hostname, service) => {});
}).toThrow("Expected address to be a invalid address for 'lookupService'.");
}).toThrow('The "address" argument is invalid. Received google.com');
});
});
@@ -486,7 +482,7 @@ describe("dns.lookupService", () => {
["1.1.1.1", 80, ["one.one.one.one", "http"]],
["1.1.1.1", 443, ["one.one.one.one", "https"]],
])("promises.lookupService(%s, %d)", async (address, port, expected) => {
const [hostname, service] = await dns.promises.lookupService(address, port);
const { hostname, service } = await dns.promises.lookupService(address, port);
expect(hostname).toStrictEqual(expected[0]);
expect(service).toStrictEqual(expected[1]);
});

View File

@@ -15,6 +15,7 @@ const types = {
TXT: 16,
ANY: 255,
CAA: 257,
SRV: 33,
};
const classes = {

View File

@@ -0,0 +1,28 @@
'use strict';
const common = require('../common');
const dnstools = require('../common/dns');
const { Resolver } = require('dns');
const assert = require('assert');
const dgram = require('dgram');
const server = dgram.createSocket('udp4');
const resolver = new Resolver();
server.bind(0, common.mustCall(() => {
resolver.setServers([`127.0.0.1:${server.address().port}`]);
resolver.reverse('123.45.67.89', common.mustCall((err, res) => {
assert.strictEqual(err.code, 'ECANCELLED');
assert.strictEqual(err.syscall, 'getHostByAddr');
assert.strictEqual(err.hostname, '123.45.67.89');
server.close();
}));
}));
server.on('message', common.mustCall((msg, { address, port }) => {
const parsed = dnstools.parseDNSPacket(msg);
const domain = parsed.questions[0].domain;
assert.strictEqual(domain, '89.67.45.123.in-addr.arpa');
// Do not send a reply.
resolver.cancel();
}));

View File

@@ -0,0 +1,59 @@
'use strict';
const common = require('../common');
const { promises: dnsPromises } = require('dns');
const assert = require('assert');
const dgram = require('dgram');
const server = dgram.createSocket('udp4');
const resolver = new dnsPromises.Resolver();
server.bind(0, common.mustCall(async () => {
resolver.setServers([`127.0.0.1:${server.address().port}`]);
// Single promise
{
server.once('message', () => {
resolver.cancel();
});
const hostname = 'example0.org';
await assert.rejects(
resolver.resolve4(hostname),
{
code: 'ECANCELLED',
syscall: 'queryA',
hostname
}
);
}
// Multiple promises
{
server.once('message', () => {
resolver.cancel();
});
const assertions = [];
const assertionCount = 10;
for (let i = 1; i <= assertionCount; i++) {
const hostname = `example${i}.org`;
assertions.push(
assert.rejects(
resolver.resolve4(hostname),
{
code: 'ECANCELLED',
syscall: 'queryA',
hostname: hostname
}
)
);
}
await Promise.all(assertions);
}
server.close();
}));

View File

@@ -0,0 +1,46 @@
'use strict';
const common = require('../common');
const { Resolver } = require('dns');
const assert = require('assert');
const dgram = require('dgram');
const server = dgram.createSocket('udp4');
const resolver = new Resolver();
const desiredQueries = 11;
let finishedQueries = 0;
server.bind(0, common.mustCall(async () => {
resolver.setServers([`127.0.0.1:${server.address().port}`]);
const callback = common.mustCall((err, res) => {
assert.strictEqual(err.code, 'ECANCELLED');
assert.strictEqual(err.syscall, 'queryA');
assert.strictEqual(err.hostname, `example${finishedQueries}.org`);
finishedQueries++;
if (finishedQueries === desiredQueries) {
server.close();
}
}, desiredQueries);
const next = (...args) => {
callback(...args);
server.once('message', () => {
resolver.cancel();
});
// Multiple queries
for (let i = 1; i < desiredQueries; i++) {
resolver.resolve4(`example${i}.org`, callback);
}
};
server.once('message', () => {
resolver.cancel();
});
// Single query
resolver.resolve4('example0.org', next);
}));

View File

@@ -0,0 +1,53 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const dgram = require('dgram');
const dns = require('dns');
for (const ctor of [dns.Resolver, dns.promises.Resolver]) {
for (const timeout of [null, true, false, '', '2']) {
assert.throws(() => new ctor({ timeout }), {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
});
}
for (const timeout of [-2, 4.2, 2 ** 31]) {
assert.throws(() => new ctor({ timeout }), {
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
});
}
for (const timeout of [-1, 0, 1]) new ctor({ timeout }); // OK
}
for (const timeout of [0, 1, 2]) {
const server = dgram.createSocket('udp4');
server.bind(0, '127.0.0.1', common.mustCall(() => {
const resolver = new dns.Resolver({ timeout });
resolver.setServers([`127.0.0.1:${server.address().port}`]);
resolver.resolve4('nodejs.org', common.mustCall((err) => {
assert.throws(() => { throw err; }, {
code: 'ETIMEOUT',
name: /^(DNSException|Error)$/,
});
server.close();
}));
}));
}
for (const timeout of [0, 1, 2]) {
const server = dgram.createSocket('udp4');
server.bind(0, '127.0.0.1', common.mustCall(() => {
const resolver = new dns.promises.Resolver({ timeout });
resolver.setServers([`127.0.0.1:${server.address().port}`]);
resolver.resolve4('nodejs.org').catch(common.mustCall((err) => {
assert.throws(() => { throw err; }, {
code: 'ETIMEOUT',
name: /^(DNSException|Error)$/,
});
server.close();
}));
}));
}

View File

@@ -0,0 +1,51 @@
// Flags: --dns-result-order=ipv4first
'use strict';
const common = require('../common');
const assert = require('assert');
const { promisify } = require('util');
// Test that --dns-result-order=ipv4first works as expected.
if (!process.execArgv.includes("--dns-result-order=ipv4first")) {
process.exit(0);
}
const originalLookup = Bun.dns.lookup;
const calls = [];
Bun.dns.lookup = common.mustCallAtLeast((...args) => {
calls.push(args);
return originalLookup(...args);
}, 1);
const dns = require('dns');
const dnsPromises = dns.promises;
// We want to test the parameter of ipv4first only so that we
// ignore possible errors here.
function allowFailed(fn) {
return fn.catch((_err) => {
//
});
}
(async () => {
let callsLength = 0;
const checkParameter = (expected) => {
assert.strictEqual(calls.length, callsLength + 1);
const { order } = calls[callsLength][1];
assert.strictEqual(order, expected);
callsLength += 1;
};
await allowFailed(promisify(dns.lookup)('example.org'));
checkParameter('ipv4first');
await allowFailed(dnsPromises.lookup('example.org'));
checkParameter('ipv4first');
await allowFailed(promisify(dns.lookup)('example.org', {}));
checkParameter('ipv4first');
await allowFailed(dnsPromises.lookup('example.org', {}));
checkParameter('ipv4first');
})().then(common.mustCall());

View File

@@ -0,0 +1,51 @@
// Flags: --dns-result-order=ipv6first
'use strict';
const common = require('../common');
const assert = require('assert');
const { promisify } = require('util');
// Test that --dns-result-order=ipv6first works as expected.
if (!process.execArgv.includes("--dns-result-order=ipv6first")) {
process.exit(0);
}
const originalLookup = Bun.dns.lookup;
const calls = [];
Bun.dns.lookup = common.mustCallAtLeast((...args) => {
calls.push(args);
return originalLookup(...args);
}, 1);
const dns = require('dns');
const dnsPromises = dns.promises;
// We want to test the parameter of ipv6first only so that we
// ignore possible errors here.
function allowFailed(fn) {
return fn.catch((_err) => {
//
});
}
(async () => {
let callsLength = 0;
const checkParameter = (expected) => {
assert.strictEqual(calls.length, callsLength + 1);
const { order } = calls[callsLength][1];
assert.strictEqual(order, expected);
callsLength += 1;
};
await allowFailed(promisify(dns.lookup)('example.org'));
checkParameter('ipv6first');
await allowFailed(dnsPromises.lookup('example.org'));
checkParameter('ipv6first');
await allowFailed(promisify(dns.lookup)('example.org', {}));
checkParameter('ipv6first');
await allowFailed(dnsPromises.lookup('example.org', {}));
checkParameter('ipv6first');
})().then(common.mustCall());

View File

@@ -0,0 +1,55 @@
// Flags: --dns-result-order=verbatim
'use strict';
const common = require('../common');
const assert = require('assert');
const { promisify } = require('util');
const originalLookup = Bun.dns.lookup;
const calls = [];
Bun.dns.lookup = common.mustCallAtLeast((...args) => {
calls.push(args);
return originalLookup(...args);
}, 1);
const dns = require('dns');
const dnsPromises = dns.promises;
// We want to test the parameter of verbatim only so that we
// ignore possible errors here.
function allowFailed(fn) {
return fn.catch((_err) => {
//
});
}
(async () => {
let callsLength = 0;
const checkParameter = (expected) => {
assert.strictEqual(calls.length, callsLength + 1);
const { order } = calls[callsLength][1];
assert.strictEqual(order, expected);
callsLength += 1;
};
await allowFailed(promisify(dns.lookup)('example.org'));
checkParameter("verbatim");
await allowFailed(dnsPromises.lookup('example.org'));
checkParameter("verbatim");
await allowFailed(promisify(dns.lookup)('example.org', {}));
checkParameter("verbatim");
await allowFailed(dnsPromises.lookup('example.org', {}));
checkParameter("verbatim");
await allowFailed(
promisify(dns.lookup)('example.org', { order: 'ipv4first' })
);
checkParameter("ipv4first");
await allowFailed(
promisify(dns.lookup)('example.org', { order: 'ipv6first' })
);
checkParameter("ipv6first");
})().then(common.mustCall());

View File

@@ -0,0 +1,11 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const { Resolver } = require('dns');
const resolver = new Resolver();
assert(resolver.getServers().length > 0);
resolver._handle.getServers = common.mustCall();
assert.strictEqual(resolver.getServers().length, 0);

View File

@@ -0,0 +1,44 @@
'use strict';
const common = require('../common');
const assert = require('assert');
Bun.dns.lookup = hostname => {
throw Object.assign(new Error('Out of memory'), {
name: 'DNSException',
code: 'ENOMEM',
syscall: 'getaddrinfo',
hostname,
});
};
// This test ensures that dns.lookup issues a DeprecationWarning
// when invalid options type is given
const dnsPromises = require('dns/promises');
common.expectWarning({
// 'internal/test/binding': [
// 'These APIs are for internal testing only. Do not use them.',
// ],
});
assert.throws(() => {
dnsPromises.lookup('127.0.0.1', { hints: '-1' });
}, {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError'
});
assert.throws(() => dnsPromises.lookup('127.0.0.1', { hints: -1 }),
{ code: 'ERR_INVALID_ARG_VALUE' });
assert.throws(() => dnsPromises.lookup('127.0.0.1', { family: '6' }),
{ code: 'ERR_INVALID_ARG_VALUE' });
assert.throws(() => dnsPromises.lookup('127.0.0.1', { all: 'true' }),
{ code: 'ERR_INVALID_ARG_TYPE' });
assert.throws(() => dnsPromises.lookup('127.0.0.1', { verbatim: 'true' }),
{ code: 'ERR_INVALID_ARG_TYPE' });
assert.throws(() => dnsPromises.lookup('127.0.0.1', { order: 'true' }),
{ code: 'ERR_INVALID_ARG_VALUE' });
assert.throws(() => dnsPromises.lookup('127.0.0.1', '6'),
{ code: 'ERR_INVALID_ARG_TYPE' });
assert.throws(() => dnsPromises.lookup('localhost'),
{ code: 'ENOMEM' });

View File

@@ -0,0 +1,214 @@
'use strict';
const common = require('../common');
const assert = require('assert');
// Stub `getaddrinfo` to *always* error. This has to be done before we load the
// `dns` module to guarantee that the `dns` module uses the stub.
Bun.dns.lookup = (hostname) => Promise.reject(Object.assign(new Error('Out of memory'), { code: 'ENOMEM', hostname }));
const dns = require('dns');
const dnsPromises = dns.promises;
{
const err = {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: /^The "hostname" argument must be of type string\. Received( type number|: "number")/
};
assert.throws(() => dns.lookup(1, {}), err);
assert.throws(() => dnsPromises.lookup(1, {}), err);
}
// This also verifies different expectWarning notations.
common.expectWarning({
// For 'internal/test/binding' module.
// 'internal/test/binding': [
// 'These APIs are for internal testing only. Do not use them.',
// ],
// For calling `dns.lookup` with falsy `hostname`.
'DeprecationWarning': {
DEP0118: 'The provided hostname "false" is not a valid ' +
'hostname, and is supported in the dns module solely for compatibility.'
}
});
assert.throws(() => {
dns.lookup(false, 'cb');
}, {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError'
});
assert.throws(() => {
dns.lookup(false, 'options', 'cb');
}, {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError'
});
{
const err = {
code: 'ERR_INVALID_ARG_VALUE',
name: 'TypeError',
message: /The (argument 'hints'|"hints" option) is invalid\. Received:? 100/
};
const options = {
hints: 100,
family: 0,
all: false
};
assert.throws(() => { dnsPromises.lookup(false, options); }, err);
assert.throws(() => {
dns.lookup(false, options, common.mustNotCall());
}, err);
}
{
const family = 20;
const err = {
code: 'ERR_INVALID_ARG_VALUE',
name: 'TypeError',
message: /^The (property 'options.family' must be one of: 0, 4, 6|"family" option must be one of 0, 4 or 6)\. Received:? 20$/
};
const options = {
hints: 0,
family,
all: false
};
assert.throws(() => { dnsPromises.lookup(false, options); }, err);
assert.throws(() => {
dns.lookup(false, options, common.mustNotCall());
}, err);
}
[1, 0n, 1n, '', '0', Symbol(), true, false, {}, [], () => {}]
.forEach((family) => {
const err = { code: 'ERR_INVALID_ARG_VALUE' };
const options = { family };
assert.throws(() => { dnsPromises.lookup(false, options); }, err);
assert.throws(() => {
dns.lookup(false, options, common.mustNotCall());
}, err);
});
[0n, 1n, '', '0', Symbol(), true, false].forEach((family) => {
const err = { code: 'ERR_INVALID_ARG_TYPE' };
assert.throws(() => { dnsPromises.lookup(false, family); }, err);
assert.throws(() => {
dns.lookup(false, family, common.mustNotCall());
}, err);
});
assert.throws(() => dnsPromises.lookup(false, () => {}),
{ code: 'ERR_INVALID_ARG_TYPE' });
[0n, 1n, '', '0', Symbol(), true, false, {}, [], () => {}].forEach((hints) => {
const err = { code: 'ERR_INVALID_ARG_TYPE' };
const options = { hints };
assert.throws(() => { dnsPromises.lookup(false, options); }, err);
assert.throws(() => {
dns.lookup(false, options, common.mustNotCall());
}, err);
});
[0, 1, 0n, 1n, '', '0', Symbol(), {}, [], () => {}].forEach((all) => {
const err = { code: 'ERR_INVALID_ARG_TYPE' };
const options = { all };
assert.throws(() => { dnsPromises.lookup(false, options); }, err);
assert.throws(() => {
dns.lookup(false, options, common.mustNotCall());
}, err);
});
[0, 1, 0n, 1n, '', '0', Symbol(), {}, [], () => {}].forEach((verbatim) => {
const err = { code: 'ERR_INVALID_ARG_TYPE' };
const options = { verbatim };
assert.throws(() => { dnsPromises.lookup(false, options); }, err);
assert.throws(() => {
dns.lookup(false, options, common.mustNotCall());
}, err);
});
[0, 1, 0n, 1n, '', '0', Symbol(), {}, [], () => {}].forEach((order) => {
const err = { code: 'ERR_INVALID_ARG_VALUE' };
const options = { order };
assert.throws(() => { dnsPromises.lookup(false, options); }, err);
assert.throws(() => {
dns.lookup(false, options, common.mustNotCall());
}, err);
});
(async function() {
let res;
res = await dnsPromises.lookup(false, {
hints: 0,
family: 0,
all: true
});
assert.deepStrictEqual(res, []);
res = await dnsPromises.lookup('127.0.0.1', {
hints: 0,
family: 4,
all: true
});
assert.deepStrictEqual(res, [{ address: '127.0.0.1', family: 4 }]);
res = await dnsPromises.lookup('127.0.0.1', {
hints: 0,
family: 4,
all: false
});
assert.deepStrictEqual(res, { address: '127.0.0.1', family: 4 });
})().then(common.mustCall());
dns.lookup(false, {
hints: 0,
family: 0,
all: true
}, common.mustSucceed((result, addressType) => {
assert.deepStrictEqual(result, []);
assert.strictEqual(addressType, undefined);
}));
dns.lookup('127.0.0.1', {
hints: 0,
family: 4,
all: true
}, common.mustSucceed((result, addressType) => {
assert.deepStrictEqual(result, [{
address: '127.0.0.1',
family: 4
}]);
assert.strictEqual(addressType, undefined);
}));
dns.lookup('127.0.0.1', {
hints: 0,
family: 4,
all: false
}, common.mustSucceed((result, addressType) => {
assert.strictEqual(result, '127.0.0.1');
assert.strictEqual(addressType, 4);
}));
let tickValue = 0;
// Should fail due to stub.
dns.lookup('example.com', common.mustCall((error, result, addressType) => {
assert(error);
assert.strictEqual(tickValue, 1);
assert.strictEqual(error.code, 'ENOMEM');
const descriptor = Object.getOwnPropertyDescriptor(error, 'message');
// The error message should be non-enumerable.
assert.strictEqual(descriptor.enumerable, false);
}));
// Make sure that the error callback is called on next tick.
tickValue = 1;
// Should fail due to stub.
assert.rejects(dnsPromises.lookup('example.com'),
{ code: 'ENOMEM', hostname: 'example.com' }).then(common.mustCall());

View File

@@ -0,0 +1,19 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const dnsPromises = require('dns').promises;
dnsPromises.lookupService('127.0.0.1', 22).then(common.mustCall((result) => {
assert(['ssh', '22'].includes(result.service));
assert.strictEqual(typeof result.hostname, 'string');
assert.notStrictEqual(result.hostname.length, 0);
}));
// Use an IP from the RFC 5737 test range to cause an error.
// Refs: https://tools.ietf.org/html/rfc5737
assert.rejects(
() => dnsPromises.lookupService('192.0.2.1', 22),
{ code: /^(?:ENOTFOUND|EAI_AGAIN)$/ }
).then(common.mustCall());

View File

@@ -0,0 +1,28 @@
'use strict';
const common = require('../common');
const assert = require('assert');
// Stub `getnameinfo` to *always* error.
Bun.dns.lookupService = (addr, port) => {
throw Object.assign(new Error(`getnameinfo ENOENT ${addr}`), {code: 'ENOENT', syscall: 'getnameinfo'});
};
const dns = require('dns');
assert.throws(
() => dns.lookupService('127.0.0.1', 80, common.mustNotCall()),
{
code: 'ENOENT',
message: 'getnameinfo ENOENT 127.0.0.1',
syscall: 'getnameinfo'
}
);
assert.rejects(
dns.promises.lookupService('127.0.0.1', 80),
{
code: 'ENOENT',
message: 'getnameinfo ENOENT 127.0.0.1',
syscall: 'getnameinfo'
}
).then(common.mustCall());

View File

@@ -0,0 +1,52 @@
'use strict';
const common = require('../common');
const dnstools = require('../common/dns');
const { Resolver } = require('dns');
const assert = require('assert');
const dgram = require('dgram');
const servers = [
{
socket: dgram.createSocket('udp4'),
reply: { type: 'A', address: '1.2.3.4', ttl: 123, domain: 'example.org' }
},
{
socket: dgram.createSocket('udp4'),
reply: { type: 'A', address: '5.6.7.8', ttl: 123, domain: 'example.org' }
},
];
let waiting = servers.length;
for (const { socket, reply } of servers) {
socket.on('message', common.mustCall((msg, { address, port }) => {
const parsed = dnstools.parseDNSPacket(msg);
const domain = parsed.questions[0].domain;
assert.strictEqual(domain, 'example.org');
socket.send(dnstools.writeDNSPacket({
id: parsed.id,
questions: parsed.questions,
answers: [reply],
}), port, address);
}));
socket.bind(0, common.mustCall(() => {
if (--waiting === 0) ready();
}));
}
function ready() {
const resolvers = servers.map((server) => ({
server,
resolver: new Resolver()
}));
for (const { server: { socket, reply }, resolver } of resolvers) {
resolver.setServers([`127.0.0.1:${socket.address().port}`]);
resolver.resolve4('example.org', common.mustSucceed((res) => {
assert.deepStrictEqual(res, [reply.address]);
socket.close();
}));
}
}

View File

@@ -0,0 +1,33 @@
'use strict';
require('../common');
const assert = require('assert');
const dnsPromises = require('dns/promises');
const dns = require('dns');
assert.strictEqual(dnsPromises, dns.promises);
assert.strictEqual(dnsPromises.NODATA, dns.NODATA);
assert.strictEqual(dnsPromises.FORMERR, dns.FORMERR);
assert.strictEqual(dnsPromises.SERVFAIL, dns.SERVFAIL);
assert.strictEqual(dnsPromises.NOTFOUND, dns.NOTFOUND);
assert.strictEqual(dnsPromises.NOTIMP, dns.NOTIMP);
assert.strictEqual(dnsPromises.REFUSED, dns.REFUSED);
assert.strictEqual(dnsPromises.BADQUERY, dns.BADQUERY);
assert.strictEqual(dnsPromises.BADNAME, dns.BADNAME);
assert.strictEqual(dnsPromises.BADFAMILY, dns.BADFAMILY);
assert.strictEqual(dnsPromises.BADRESP, dns.BADRESP);
assert.strictEqual(dnsPromises.CONNREFUSED, dns.CONNREFUSED);
assert.strictEqual(dnsPromises.TIMEOUT, dns.TIMEOUT);
assert.strictEqual(dnsPromises.EOF, dns.EOF);
assert.strictEqual(dnsPromises.FILE, dns.FILE);
assert.strictEqual(dnsPromises.NOMEM, dns.NOMEM);
assert.strictEqual(dnsPromises.DESTRUCTION, dns.DESTRUCTION);
assert.strictEqual(dnsPromises.BADSTR, dns.BADSTR);
assert.strictEqual(dnsPromises.BADFLAGS, dns.BADFLAGS);
assert.strictEqual(dnsPromises.NONAME, dns.NONAME);
assert.strictEqual(dnsPromises.BADHINTS, dns.BADHINTS);
assert.strictEqual(dnsPromises.NOTINITIALIZED, dns.NOTINITIALIZED);
assert.strictEqual(dnsPromises.LOADIPHLPAPI, dns.LOADIPHLPAPI);
assert.strictEqual(dnsPromises.ADDRGETNETWORKPARAMS, dns.ADDRGETNETWORKPARAMS);
assert.strictEqual(dnsPromises.CANCELLED, dns.CANCELLED);

View File

@@ -0,0 +1,15 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const dnsPromises = require('dns').promises;
Bun.dns.resolve = (hostname, rrtype) => Promise.reject({code: 'EPERM', syscall: 'query' + rrtype[0].toUpperCase() + rrtype.substr(1), hostname});
assert.rejects(
dnsPromises.resolve('example.org'),
{
code: 'EPERM',
syscall: 'queryA',
hostname: 'example.org'
}
).then(common.mustCall());

View File

@@ -0,0 +1,55 @@
'use strict';
const common = require('../common');
const dnstools = require('../common/dns');
const dns = require('dns');
const assert = require('assert');
const dgram = require('dgram');
const dnsPromises = dns.promises;
const server = dgram.createSocket('udp4');
const resolver = new dns.Resolver({ timeout: 100, tries: 1 });
const resolverPromises = new dnsPromises.Resolver({ timeout: 100, tries: 1 });
server.on('message', common.mustCallAtLeast((msg, { address, port }) => {
const parsed = dnstools.parseDNSPacket(msg);
const domain = parsed.questions[0].domain;
assert.strictEqual(domain, 'example.org');
const buf = dnstools.writeDNSPacket({
id: parsed.id,
questions: parsed.questions,
answers: { type: 'A', address: '1.2.3.4', ttl: 123, domain },
});
// Overwrite the # of answers with 2, which is incorrect. The response is
// discarded in c-ares >= 1.21.0. This is the reason why a small timeout is
// used in the `Resolver` constructor. See
// https://github.com/nodejs/node/pull/50743#issue-1994909204
buf.writeUInt16LE(2, 6);
server.send(buf, port, address);
}, 2));
server.bind(0, common.mustCall(async () => {
const address = server.address();
resolver.setServers([`127.0.0.1:${address.port}`]);
resolverPromises.setServers([`127.0.0.1:${address.port}`]);
resolverPromises.resolveAny('example.org')
.then(common.mustNotCall())
.catch(common.expectsError({
// May return EBADRESP or ETIMEOUT
code: /^(?:EBADRESP|ETIMEOUT)$/,
syscall: 'queryAny',
hostname: 'example.org'
}));
resolver.resolveAny('example.org', common.mustCall((err) => {
assert.notStrictEqual(err.code, 'SUCCESS');
assert.strictEqual(err.syscall, 'queryAny');
assert.strictEqual(err.hostname, 'example.org');
const descriptor = Object.getOwnPropertyDescriptor(err, 'message');
// The error message should be non-enumerable.
assert.strictEqual(descriptor.enumerable, false);
server.close();
}));
}));

View File

@@ -0,0 +1,69 @@
'use strict';
const common = require('../common');
const dnstools = require('../common/dns');
const dns = require('dns');
const assert = require('assert');
const dgram = require('dgram');
const dnsPromises = dns.promises;
const answers = [
{ type: 'A', address: '1.2.3.4', ttl: 123 },
{ type: 'AAAA', address: '::42', ttl: 123 },
{ type: 'MX', priority: 42, exchange: 'foobar.com', ttl: 124 },
{ type: 'NS', value: 'foobar.org', ttl: 457 },
{ type: 'TXT', entries: [ 'v=spf1 ~all xyz\0foo' ] },
{ type: 'PTR', value: 'baz.org', ttl: 987 },
{
type: 'SOA',
nsname: 'ns1.example.com',
hostmaster: 'admin.example.com',
serial: 156696742,
refresh: 900,
retry: 900,
expire: 1800,
minttl: 60
},
{
type: 'CAA',
critical: 128,
issue: 'platynum.ch'
},
];
const server = dgram.createSocket('udp4');
server.on('message', common.mustCall((msg, { address, port }) => {
const parsed = dnstools.parseDNSPacket(msg);
const domain = parsed.questions[0].domain;
assert.strictEqual(domain, 'example.org');
server.send(dnstools.writeDNSPacket({
id: parsed.id,
questions: parsed.questions,
answers: answers.map((answer) => Object.assign({ domain }, answer)),
}), port, address);
}, 2));
server.bind(0, common.mustCall(async () => {
const address = server.address();
dns.setServers([`127.0.0.1:${address.port}`]);
validateResults(await dnsPromises.resolveAny('example.org'));
dns.resolveAny('example.org', common.mustSucceed((res) => {
validateResults(res);
server.close();
}));
}));
function validateResults(res) {
// TTL values are only provided for A and AAAA entries.
assert.deepStrictEqual(res.map(maybeRedactTTL), answers.map(maybeRedactTTL));
}
function maybeRedactTTL(r) {
const ret = { ...r };
if (!['A', 'AAAA'].includes(r.type))
delete ret.ttl;
return ret;
}

View File

@@ -0,0 +1,55 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
require('../common');
// This test ensures `dns.resolveNs()` does not raise a C++-land assertion error
// and throw a JavaScript TypeError instead.
// Issue https://github.com/nodejs/node-v0.x-archive/issues/7070
const assert = require('assert');
const dns = require('dns');
const dnsPromises = dns.promises;
assert.throws(
() => dnsPromises.resolveNs([]), // bad name
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: /^(The "(host)?name" argument must be of type string|Expected hostname to be a string)/
}
);
assert.throws(
() => dns.resolveNs([]), // bad name
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: /^(The "(host)?name" argument must be of type string|Expected hostname to be a string)/
}
);
assert.throws(
() => dns.resolveNs(''), // bad callback
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError'
}
);

View File

@@ -0,0 +1,108 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const { promisify } = require('util');
// Test that `dns.setDefaultResultOrder()` and
// `dns.promises.setDefaultResultOrder()` work as expected.
const originalLookup = Bun.dns.lookup;
const calls = [];
Bun.dns.lookup = common.mustCallAtLeast((...args) => {
calls.push(args);
return originalLookup(...args);
}, 1);
const dns = require('dns');
const dnsPromises = dns.promises;
// We want to test the parameter of order only so that we
// ignore possible errors here.
function allowFailed(fn) {
return fn.catch((_err) => {
//
});
}
assert.throws(() => dns.setDefaultResultOrder('my_order'), {
code: 'ERR_INVALID_ARG_VALUE',
});
assert.throws(() => dns.promises.setDefaultResultOrder('my_order'), {
code: 'ERR_INVALID_ARG_VALUE',
});
assert.throws(() => dns.setDefaultResultOrder(4), {
code: 'ERR_INVALID_ARG_VALUE',
});
assert.throws(() => dns.promises.setDefaultResultOrder(4), {
code: 'ERR_INVALID_ARG_VALUE',
});
(async () => {
let callsLength = 0;
const checkParameter = (expected) => {
assert.strictEqual(calls.length, callsLength + 1);
const { order } = calls[callsLength][1];
assert.strictEqual(order, expected);
callsLength += 1;
};
dns.setDefaultResultOrder('verbatim');
await allowFailed(promisify(dns.lookup)('example.org'));
checkParameter('verbatim');
await allowFailed(dnsPromises.lookup('example.org'));
checkParameter('verbatim');
await allowFailed(promisify(dns.lookup)('example.org', {}));
checkParameter('verbatim');
await allowFailed(dnsPromises.lookup('example.org', {}));
checkParameter('verbatim');
dns.setDefaultResultOrder('ipv4first');
await allowFailed(promisify(dns.lookup)('example.org'));
checkParameter('ipv4first');
await allowFailed(dnsPromises.lookup('example.org'));
checkParameter('ipv4first');
await allowFailed(promisify(dns.lookup)('example.org', {}));
checkParameter('ipv4first');
await allowFailed(dnsPromises.lookup('example.org', {}));
checkParameter('ipv4first');
dns.setDefaultResultOrder('ipv6first');
await allowFailed(promisify(dns.lookup)('example.org'));
checkParameter('ipv6first');
await allowFailed(dnsPromises.lookup('example.org'));
checkParameter('ipv6first');
await allowFailed(promisify(dns.lookup)('example.org', {}));
checkParameter('ipv6first');
await allowFailed(dnsPromises.lookup('example.org', {}));
checkParameter('ipv6first');
dns.promises.setDefaultResultOrder('verbatim');
await allowFailed(promisify(dns.lookup)('example.org'));
checkParameter('verbatim');
await allowFailed(dnsPromises.lookup('example.org'));
checkParameter('verbatim');
await allowFailed(promisify(dns.lookup)('example.org', {}));
checkParameter('verbatim');
await allowFailed(dnsPromises.lookup('example.org', {}));
checkParameter('verbatim');
dns.promises.setDefaultResultOrder('ipv4first');
await allowFailed(promisify(dns.lookup)('example.org'));
checkParameter('ipv4first');
await allowFailed(dnsPromises.lookup('example.org'));
checkParameter('ipv4first');
await allowFailed(promisify(dns.lookup)('example.org', {}));
checkParameter('ipv4first');
await allowFailed(dnsPromises.lookup('example.org', {}));
checkParameter('ipv4first');
dns.promises.setDefaultResultOrder('ipv6first');
await allowFailed(promisify(dns.lookup)('example.org'));
checkParameter('ipv6first');
await allowFailed(dnsPromises.lookup('example.org'));
checkParameter('ipv6first');
await allowFailed(promisify(dns.lookup)('example.org', {}));
checkParameter('ipv6first');
await allowFailed(dnsPromises.lookup('example.org', {}));
checkParameter('ipv6first');
})().then(common.mustCall());

View File

@@ -0,0 +1,40 @@
'use strict';
require('../common');
const assert = require('assert');
const dns = require('dns');
const resolver = new dns.Resolver();
const promiseResolver = new dns.promises.Resolver();
// Verifies that setLocalAddress succeeds with IPv4 and IPv6 addresses
{
resolver.setLocalAddress('127.0.0.1');
resolver.setLocalAddress('::1');
resolver.setLocalAddress('127.0.0.1', '::1');
promiseResolver.setLocalAddress('127.0.0.1', '::1');
}
// Verify that setLocalAddress throws if called with an invalid address
{
assert.throws(() => {
resolver.setLocalAddress('127.0.0.1', '127.0.0.1');
}, Error);
assert.throws(() => {
resolver.setLocalAddress('::1', '::1');
}, Error);
assert.throws(() => {
resolver.setLocalAddress('bad');
}, Error);
assert.throws(() => {
resolver.setLocalAddress(123);
}, { code: 'ERR_INVALID_ARG_TYPE' });
assert.throws(() => {
resolver.setLocalAddress('127.0.0.1', 42);
}, { code: 'ERR_INVALID_ARG_TYPE' });
assert.throws(() => {
resolver.setLocalAddress();
}, Error);
assert.throws(() => {
promiseResolver.setLocalAddress();
}, Error);
}

View File

@@ -0,0 +1,29 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const dns = require('dns');
const localhost = [ '127.0.0.1' ];
{
// Fix https://github.com/nodejs/node/issues/14734
{
const resolver = new dns.Resolver();
resolver.resolve('localhost', common.mustCall());
assert.throws(resolver.setServers.bind(resolver, localhost), {
code: 'ERR_DNS_SET_SERVERS_FAILED',
message: /[Tt]here are pending queries/
});
}
{
dns.resolve('localhost', common.mustCall());
// should not throw
dns.setServers(localhost);
}
}

View File

@@ -0,0 +1,117 @@
'use strict';
const common = require('../common');
const { addresses } = require('../common/internet');
const assert = require('assert');
const dns = require('dns');
const resolver = new dns.promises.Resolver();
const dnsPromises = dns.promises;
const promiseResolver = new dns.promises.Resolver();
{
[
null,
undefined,
Number(addresses.DNS4_SERVER),
addresses.DNS4_SERVER,
{
address: addresses.DNS4_SERVER
},
].forEach((val) => {
const errObj = {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: /^The "servers" argument must be an instance of Array\./
};
assert.throws(
() => {
dns.setServers(val);
}, errObj
);
assert.throws(
() => {
resolver.setServers(val);
}, errObj
);
assert.throws(
() => {
dnsPromises.setServers(val);
}, errObj
);
assert.throws(
() => {
promiseResolver.setServers(val);
}, errObj
);
});
}
{
[
[null],
[undefined],
[Number(addresses.DNS4_SERVER)],
[
{
address: addresses.DNS4_SERVER
},
],
].forEach((val) => {
const errObj = {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: /^The "servers\[0\]" argument must be of type string\./
};
assert.throws(
() => {
dns.setServers(val);
}, errObj
);
assert.throws(
() => {
resolver.setServers(val);
}, errObj
);
assert.throws(
() => {
dnsPromises.setServers(val);
}, errObj
);
assert.throws(
() => {
promiseResolver.setServers(val);
}, errObj
);
});
}
// This test for 'dns/promises'
{
const {
setServers
} = require('dns/promises');
// This should not throw any error.
(async () => {
setServers([ '127.0.0.1' ]);
})().then(common.mustCall());
[
[null],
[undefined],
[Number(addresses.DNS4_SERVER)],
[
{
address: addresses.DNS4_SERVER
},
],
].forEach((val) => {
const errObj = {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: /^The "servers\[0\]" argument must be of type string\./
};
assert.throws(() => {
setServers(val);
}, errObj);
});
}

View File

@@ -0,0 +1,461 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
const common = require('../common');
const dnstools = require('../common/dns');
const assert = require('assert');
const dns = require('dns');
const dnsPromises = dns.promises;
const dgram = require('dgram');
const existing = dns.getServers();
assert(existing.length > 0);
// Verify that setServers() handles arrays with holes and other oddities
{
const servers = [];
servers[0] = '127.0.0.1';
servers[2] = '0.0.0.0';
dns.setServers(servers);
assert.deepStrictEqual(dns.getServers(), ['127.0.0.1', '0.0.0.0']);
}
{
const servers = ['127.0.0.1', '192.168.1.1'];
servers[3] = '127.1.0.1';
servers[4] = '127.1.0.1';
servers[5] = '127.1.1.1';
Object.defineProperty(servers, 2, {
enumerable: true,
get: () => {
servers.length = 3;
return '0.0.0.0';
}
});
dns.setServers(servers);
assert.deepStrictEqual(dns.getServers(), [
'127.0.0.1',
'192.168.1.1',
'0.0.0.0',
]);
}
{
// Various invalidities, all of which should throw a clean error.
const invalidServers = [
' ',
'\n',
'\0',
'1'.repeat(3 * 4),
// Check for REDOS issues.
':'.repeat(100000),
'['.repeat(100000),
'['.repeat(100000) + ']'.repeat(100000) + 'a',
];
invalidServers.forEach((serv) => {
assert.throws(
() => {
dns.setServers([serv]);
},
{
name: 'TypeError',
code: 'ERR_INVALID_IP_ADDRESS'
}
);
});
}
const goog = [
'8.8.8.8',
'8.8.4.4',
];
dns.setServers(goog);
assert.deepStrictEqual(dns.getServers(), goog);
assert.throws(() => dns.setServers(['foobar']), {
code: 'ERR_INVALID_IP_ADDRESS',
name: 'TypeError',
message: 'Invalid IP address: foobar'
});
assert.throws(() => dns.setServers(['127.0.0.1:va']), {
code: 'ERR_INVALID_IP_ADDRESS',
name: 'TypeError',
message: 'Invalid IP address: 127.0.0.1:va'
});
assert.deepStrictEqual(dns.getServers(), goog);
const goog6 = [
'2001:4860:4860::8888',
'2001:4860:4860::8844',
];
dns.setServers(goog6);
assert.deepStrictEqual(dns.getServers(), goog6);
goog6.push('4.4.4.4');
dns.setServers(goog6);
assert.deepStrictEqual(dns.getServers(), goog6);
const ports = [
'4.4.4.4:53',
'[2001:4860:4860::8888]:53',
'103.238.225.181:666',
'[fe80::483a:5aff:fee6:1f04]:666',
'[fe80::483a:5aff:fee6:1f04]',
];
const portsExpected = [
'4.4.4.4',
'2001:4860:4860::8888',
'103.238.225.181:666',
'[fe80::483a:5aff:fee6:1f04]:666',
'fe80::483a:5aff:fee6:1f04',
];
dns.setServers(ports);
assert.deepStrictEqual(dns.getServers(), portsExpected);
dns.setServers([]);
assert.deepStrictEqual(dns.getServers(), []);
{
const errObj = {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: /The "rrtype" argument must be of type string\. Received( an instance of Array|: \[\]|: "object")$/
};
assert.throws(() => {
dns.resolve('example.com', [], common.mustNotCall());
}, errObj);
assert.throws(() => {
dnsPromises.resolve('example.com', []);
}, errObj);
}
{
const errObj = {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: /The "(host)?name" argument must be of type string\. Received:? undefined$/
};
assert.throws(() => {
dnsPromises.resolve();
}, errObj);
}
// dns.lookup should accept only falsey and string values
{
const errorReg = {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: /^The "hostname" argument must be of type string\. Received:? .*|^Expected hostname to be a string/
};
assert.throws(() => dns.lookup({}, common.mustNotCall()), errorReg);
assert.throws(() => dns.lookup([], common.mustNotCall()), errorReg);
assert.throws(() => dns.lookup(true, common.mustNotCall()), errorReg);
assert.throws(() => dns.lookup(1, common.mustNotCall()), errorReg);
assert.throws(() => dns.lookup(common.mustNotCall(), common.mustNotCall()),
errorReg);
assert.throws(() => dnsPromises.lookup({}), errorReg);
assert.throws(() => dnsPromises.lookup([]), errorReg);
assert.throws(() => dnsPromises.lookup(true), errorReg);
assert.throws(() => dnsPromises.lookup(1), errorReg);
assert.throws(() => dnsPromises.lookup(common.mustNotCall()), errorReg);
}
// dns.lookup should accept falsey values
{
const checkCallback = (err, address, family) => {
assert.ifError(err);
assert.strictEqual(address, null);
assert.strictEqual(family, 4);
};
['', null, undefined, 0, NaN].forEach(async (value) => {
const res = await dnsPromises.lookup(value);
assert.deepStrictEqual(res, { address: null, family: 4 });
dns.lookup(value, common.mustCall(checkCallback));
});
}
{
// Make sure that dns.lookup throws if hints does not represent a valid flag.
// (dns.V4MAPPED | dns.ADDRCONFIG | dns.ALL) + 1 is invalid because:
// - it's different from dns.V4MAPPED and dns.ADDRCONFIG and dns.ALL.
// - it's different from any subset of them bitwise ored.
// - it's different from 0.
// - it's an odd number different than 1, and thus is invalid, because
// flags are either === 1 or even.
const hints = (dns.V4MAPPED | dns.ADDRCONFIG | dns.ALL) + 1;
const err = {
code: 'ERR_INVALID_ARG_VALUE',
name: 'TypeError',
message: /The (argument 'hints'|"hints" option) is invalid\. Received:? \d+/
};
assert.throws(() => {
dnsPromises.lookup('nodejs.org', { hints });
}, err);
assert.throws(() => {
dns.lookup('nodejs.org', { hints }, common.mustNotCall());
}, err);
}
assert.throws(() => dns.lookup('nodejs.org'), {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError'
});
assert.throws(() => dns.lookup('nodejs.org', 4), {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError'
});
assert.throws(() => dns.lookup('', {
family: 'nodejs.org',
hints: dns.ADDRCONFIG | dns.V4MAPPED | dns.ALL,
}), {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError'
});
dns.lookup('', { family: 4, hints: 0 }, common.mustCall());
dns.lookup('', {
family: 6,
hints: dns.ADDRCONFIG
}, common.mustCall());
dns.lookup('', { hints: dns.V4MAPPED }, common.mustCall());
dns.lookup('', {
hints: dns.ADDRCONFIG | dns.V4MAPPED
}, common.mustCall());
dns.lookup('', {
hints: dns.ALL
}, common.mustCall());
dns.lookup('', {
hints: dns.V4MAPPED | dns.ALL
}, common.mustCall());
dns.lookup('', {
hints: dns.ADDRCONFIG | dns.V4MAPPED | dns.ALL
}, common.mustCall());
dns.lookup('', {
hints: dns.ADDRCONFIG | dns.V4MAPPED | dns.ALL,
family: 'IPv4'
}, common.mustCall());
dns.lookup('', {
hints: dns.ADDRCONFIG | dns.V4MAPPED | dns.ALL,
family: 'IPv6'
}, common.mustCall());
(async function() {
await dnsPromises.lookup('', { family: 4, hints: 0 });
await dnsPromises.lookup('', { family: 6, hints: dns.ADDRCONFIG });
await dnsPromises.lookup('', { hints: dns.V4MAPPED });
await dnsPromises.lookup('', { hints: dns.ADDRCONFIG | dns.V4MAPPED });
await dnsPromises.lookup('', { hints: dns.ALL });
await dnsPromises.lookup('', { hints: dns.V4MAPPED | dns.ALL });
await dnsPromises.lookup('', {
hints: dns.ADDRCONFIG | dns.V4MAPPED | dns.ALL
});
await dnsPromises.lookup('', { order: 'verbatim' });
})().then(common.mustCall());
{
const err = {
code: 'ERR_MISSING_ARGS',
name: 'TypeError',
message: 'The "address", "port", and "callback" arguments must be ' +
'specified'
};
assert.throws(() => dns.lookupService('0.0.0.0'), err);
err.message = 'The "address" and "port" arguments must be specified';
assert.throws(() => dnsPromises.lookupService('0.0.0.0'), err);
}
{
const invalidAddress = 'fasdfdsaf';
const err = {
code: 'ERR_INVALID_ARG_VALUE',
name: 'TypeError',
message: /The (argument 'address'|"address" argument) is invalid\. Received/
};
assert.throws(() => {
dnsPromises.lookupService(invalidAddress, 0);
}, err);
assert.throws(() => {
dns.lookupService(invalidAddress, 0, common.mustNotCall());
}, err);
}
const portErr = (port) => {
const err = {
code: 'ERR_SOCKET_BAD_PORT',
name: 'RangeError'
};
assert.throws(() => {
dnsPromises.lookupService('0.0.0.0', port);
}, err);
assert.throws(() => {
dns.lookupService('0.0.0.0', port, common.mustNotCall());
}, err);
};
[null, undefined, 65538, 'test', NaN, Infinity, Symbol(), 0n, true, false, '', () => {}, {}].forEach(portErr);
assert.throws(() => {
dns.lookupService('0.0.0.0', 80, null);
}, {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError'
});
{
dns.resolveMx('foo.onion', function(err) {
assert.strictEqual(err.code, 'ENOTFOUND');
assert.strictEqual(err.syscall, 'queryMx');
assert.strictEqual(err.hostname, 'foo.onion');
assert.strictEqual(err.message, 'queryMx ENOTFOUND foo.onion');
});
}
{
const cases = [
// { method: 'resolveAny',
// answers: [
// { type: 'A', address: '1.2.3.4', ttl: 0 },
// { type: 'AAAA', address: '::42', ttl: 0 },
// { type: 'MX', priority: 42, exchange: 'foobar.com', ttl: 0 },
// { type: 'NS', value: 'foobar.org', ttl: 0 },
// { type: 'PTR', value: 'baz.org', ttl: 0 },
// {
// type: 'SOA',
// nsname: 'ns1.example.com',
// hostmaster: 'admin.example.com',
// serial: 3210987654,
// refresh: 900,
// retry: 900,
// expire: 1800,
// minttl: 3333333333
// },
// ] },
{ method: 'resolve4',
options: { ttl: true },
answers: [ { type: 'A', address: '1.2.3.4', ttl: 0 } ] },
{ method: 'resolve6',
options: { ttl: true },
answers: [ { type: 'AAAA', address: '::42', ttl: 0 } ] },
{ method: 'resolveSoa',
answers: [
{
type: 'SOA',
nsname: 'ns1.example.com',
hostmaster: 'admin.example.com',
serial: 3210987654,
refresh: 900,
retry: 900,
expire: 1800,
minttl: 3333333333
},
] },
];
const server = dgram.createSocket('udp4');
server.on('message', common.mustCallAtLeast((msg, { address, port }) => {
const parsed = dnstools.parseDNSPacket(msg);
const domain = parsed.questions[0].domain;
assert.strictEqual(domain, 'example.org');
server.send(dnstools.writeDNSPacket({
id: parsed.id,
questions: parsed.questions,
answers: cases[0].answers.map(
(answer) => Object.assign({ domain }, answer)
),
}), port, address);
}, cases.length * 2 - 1));
server.bind(0, common.mustCall(() => {
const address = server.address();
dns.setServers([`127.0.0.1:${address.port}`]);
function validateResults(res) {
if (!Array.isArray(res))
res = [res];
assert.deepStrictEqual(res.map(tweakEntry),
cases[0].answers.map(tweakEntry));
}
function tweakEntry(r) {
const ret = { ...r };
const { method } = cases[0];
// TTL values are only provided for A and AAAA entries.
if (!['A', 'AAAA'].includes(ret.type) && !/^resolve(4|6)?$/.test(method))
delete ret.ttl;
if (method !== 'resolveAny')
delete ret.type;
return ret;
}
(async function nextCase() {
if (cases.length === 0)
return server.close();
const { method, options } = cases[0];
validateResults(await dnsPromises[method]('example.org', options));
dns[method]('example.org', ...(options? [options] : []), common.mustSucceed((res) => {
validateResults(res);
cases.shift();
nextCase();
}));
})().then(common.mustCall());
}));
}