aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js
diff options
context:
space:
mode:
authorGravatar Ai Hoshino <ambiguous404@gmail.com> 2023-08-29 11:10:33 +0800
committerGravatar GitHub <noreply@github.com> 2023-08-28 20:10:33 -0700
commitc53372c9f34fe9cbcc2fd8593ce5bbfaefb3f9db (patch)
treee94b42b9583971f0c51b911f88652a16add6d594 /src/bun.js
parentd1c2d6b25ceaeb58513d8687ba7945447adb6618 (diff)
downloadbun-c53372c9f34fe9cbcc2fd8593ce5bbfaefb3f9db.tar.gz
bun-c53372c9f34fe9cbcc2fd8593ce5bbfaefb3f9db.tar.zst
bun-c53372c9f34fe9cbcc2fd8593ce5bbfaefb3f9db.zip
feat(node:dns): implement `dns.reverse`. (#4332)
* feat(node:dns): implement `dns.reverse`. Close: #4299 * fix dns reverse for ipv6 --------- Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Diffstat (limited to 'src/bun.js')
-rw-r--r--src/bun.js/api/bun/dns_resolver.zig284
-rw-r--r--src/bun.js/bindings/BunObject.cpp3
2 files changed, 284 insertions, 3 deletions
diff --git a/src/bun.js/api/bun/dns_resolver.zig b/src/bun.js/api/bun/dns_resolver.zig
index 1a370cd60..9c0bc8543 100644
--- a/src/bun.js/api/bun/dns_resolver.zig
+++ b/src/bun.js/api/bun/dns_resolver.zig
@@ -719,6 +719,99 @@ pub fn ResolveInfoRequest(comptime cares_type: type, comptime type_name: []const
};
}
+pub const GetHostByAddrInfoRequest = struct {
+ const request_type = @This();
+
+ const log = Output.scoped(@This(), false);
+
+ resolver_for_caching: ?*DNSResolver = null,
+ hash: u64 = 0,
+ cache: @This().CacheConfig = @This().CacheConfig{},
+ head: CAresReverse,
+ tail: *CAresReverse = undefined,
+
+ pub fn init(
+ cache: DNSResolver.LookupCacheHit(@This()),
+ resolver: ?*DNSResolver,
+ name: []const u8,
+ globalThis: *JSC.JSGlobalObject,
+ comptime cache_field: []const u8,
+ ) !*@This() {
+ var request = try globalThis.allocator().create(@This());
+ var hasher = std.hash.Wyhash.init(0);
+ hasher.update(name);
+ const hash = hasher.final();
+ var poll_ref = JSC.PollRef.init();
+ poll_ref.ref(globalThis.bunVM());
+ request.* = .{
+ .resolver_for_caching = resolver,
+ .hash = hash,
+ .head = .{ .poll_ref = poll_ref, .globalThis = globalThis, .promise = JSC.JSPromise.Strong.init(globalThis), .allocated = false, .name = name },
+ };
+ request.tail = &request.head;
+ if (cache == .new) {
+ request.resolver_for_caching = resolver;
+ request.cache = @This().CacheConfig{
+ .pending_cache = true,
+ .entry_cache = false,
+ .pos_in_pending = @as(u5, @truncate(@field(resolver.?, cache_field).indexOf(cache.new).?)),
+ .name_len = @as(u9, @truncate(name.len)),
+ };
+ cache.new.lookup = request;
+ }
+ return request;
+ }
+
+ pub const CacheConfig = packed struct(u16) {
+ pending_cache: bool = false,
+ entry_cache: bool = false,
+ pos_in_pending: u5 = 0,
+ name_len: u9 = 0,
+ };
+
+ pub const PendingCacheKey = struct {
+ hash: u64,
+ len: u16,
+ lookup: *request_type = undefined,
+
+ pub fn append(this: *PendingCacheKey, cares_lookup: *CAresReverse) void {
+ var tail = this.lookup.tail;
+ tail.next = cares_lookup;
+ this.lookup.tail = cares_lookup;
+ }
+
+ pub fn init(name: []const u8) PendingCacheKey {
+ var hasher = std.hash.Wyhash.init(0);
+ hasher.update(name);
+ const hash = hasher.final();
+ return PendingCacheKey{
+ .hash = hash,
+ .len = @as(u16, @truncate(name.len)),
+ .lookup = undefined,
+ };
+ }
+ };
+
+ pub fn onCaresComplete(this: *@This(), err_: ?c_ares.Error, timeout: i32, result: ?*c_ares.struct_hostent) void {
+ if (this.resolver_for_caching) |resolver| {
+ if (this.cache.pending_cache) {
+ resolver.drainPendingAddrCares(
+ this.cache.pos_in_pending,
+ err_,
+ timeout,
+ result,
+ );
+ return;
+ }
+ }
+
+ var head = this.head;
+ bun.default_allocator.destroy(this);
+
+ head.processResolve(err_, timeout, result);
+ }
+};
+
pub const GetAddrInfoRequest = struct {
const log = Output.scoped(.GetAddrInfoRequest, false);
@@ -945,6 +1038,76 @@ pub const GetAddrInfoRequest = struct {
}
};
+pub const CAresReverse = struct {
+ const log = Output.scoped(@This(), true);
+
+ globalThis: *JSC.JSGlobalObject = undefined,
+ promise: JSC.JSPromise.Strong,
+ poll_ref: JSC.PollRef,
+ allocated: bool = false,
+ next: ?*@This() = null,
+ name: []const u8,
+
+ pub fn init(globalThis: *JSC.JSGlobalObject, allocator: std.mem.Allocator, name: []const u8) !*@This() {
+ var this = try allocator.create(@This());
+ var poll_ref = JSC.PollRef.init();
+ poll_ref.ref(globalThis.bunVM());
+ this.* = .{ .globalThis = globalThis, .promise = JSC.JSPromise.Strong.init(globalThis), .poll_ref = poll_ref, .allocated = true, .name = name };
+ return this;
+ }
+
+ pub fn processResolve(this: *@This(), err_: ?c_ares.Error, _: i32, result: ?*c_ares.struct_hostent) void {
+ if (err_) |err| {
+ var promise = this.promise;
+ var globalThis = this.globalThis;
+ const error_value = globalThis.createErrorInstance("reverse failed: {s}", .{err.label()});
+ error_value.put(
+ globalThis,
+ JSC.ZigString.static("code"),
+ JSC.ZigString.init(err.code()).toValueGC(globalThis),
+ );
+
+ promise.reject(globalThis, error_value);
+ this.deinit();
+ return;
+ }
+ if (result == null) {
+ var promise = this.promise;
+ var globalThis = this.globalThis;
+ const error_value = globalThis.createErrorInstance("reverse failed: No results", .{});
+ error_value.put(
+ globalThis,
+ JSC.ZigString.static("code"),
+ JSC.ZigString.init("EUNREACHABLE").toValueGC(globalThis),
+ );
+
+ promise.reject(globalThis, error_value);
+ this.deinit();
+ return;
+ }
+ var node = result.?;
+ const array = node.toJSReponse(this.globalThis.allocator(), this.globalThis, "");
+ this.onComplete(array);
+ return;
+ }
+
+ pub fn onComplete(this: *@This(), result: JSC.JSValue) void {
+ var promise = this.promise;
+ var globalThis = this.globalThis;
+ this.promise = .{};
+ promise.resolve(globalThis, result);
+ this.deinit();
+ }
+
+ pub fn deinit(this: *@This()) void {
+ this.poll_ref.unrefOnNextTick(this.globalThis.bunVM());
+ bun.default_allocator.free(this.name);
+
+ if (this.allocated)
+ this.globalThis.allocator().destroy(this);
+ }
+};
+
pub fn CAresLookup(comptime cares_type: type, comptime type_name: []const u8) type {
return struct {
const log = Output.scoped(@This(), true);
@@ -1161,6 +1324,7 @@ pub const DNSResolver = struct {
pending_ns_cache_cares: NSPendingCache = NSPendingCache.init(),
pending_ptr_cache_cares: PtrPendingCache = PtrPendingCache.init(),
pending_cname_cache_cares: CnamePendingCache = CnamePendingCache.init(),
+ pending_addr_cache_crares: AddrPendingCache = AddrPendingCache.init(),
const PendingCache = bun.HiveArray(GetAddrInfoRequest.PendingCacheKey, 32);
const SrvPendingCache = bun.HiveArray(ResolveInfoRequest(c_ares.struct_ares_srv_reply, "srv").PendingCacheKey, 32);
@@ -1172,6 +1336,7 @@ pub const DNSResolver = struct {
const NSPendingCache = bun.HiveArray(ResolveInfoRequest(c_ares.struct_hostent, "ns").PendingCacheKey, 32);
const PtrPendingCache = bun.HiveArray(ResolveInfoRequest(c_ares.struct_hostent, "ptr").PendingCacheKey, 32);
const CnamePendingCache = bun.HiveArray(ResolveInfoRequest(c_ares.struct_hostent, "cname").PendingCacheKey, 32);
+ const AddrPendingCache = bun.HiveArray(GetHostByAddrInfoRequest.PendingCacheKey, 32);
fn getKey(this: *DNSResolver, index: u8, comptime cache_name: []const u8, comptime request_type: type) request_type.PendingCacheKey {
var cache = &@field(this, cache_name);
@@ -1315,6 +1480,47 @@ pub const DNSResolver = struct {
}
}
+ pub fn drainPendingAddrCares(this: *DNSResolver, index: u8, err: ?c_ares.Error, timeout: i32, result: ?*c_ares.struct_hostent) void {
+ const key = this.getKey(index, "pending_addr_cache_crares", GetHostByAddrInfoRequest);
+
+ var addr = result orelse {
+ var pending: ?*CAresReverse = key.lookup.head.next;
+ key.lookup.head.processResolve(err, timeout, null);
+ bun.default_allocator.destroy(key.lookup);
+
+ while (pending) |value| {
+ pending = value.next;
+ value.processResolve(err, timeout, null);
+ }
+ return;
+ };
+
+ var pending: ?*CAresReverse = key.lookup.head.next;
+ var prev_global = key.lookup.head.globalThis;
+ var array = addr.toJSReponse(this.vm.allocator, prev_global, "");
+ defer addr.deinit();
+ array.ensureStillAlive();
+ key.lookup.head.onComplete(array);
+ bun.default_allocator.destroy(key.lookup);
+
+ array.ensureStillAlive();
+
+ while (pending) |value| {
+ var new_global = value.globalThis;
+ if (prev_global != new_global) {
+ array = addr.toJSReponse(this.vm.allocator, new_global, "");
+ prev_global = new_global;
+ }
+ pending = value.next;
+
+ {
+ array.ensureStillAlive();
+ value.onComplete(array);
+ array.ensureStillAlive();
+ }
+ }
+ }
+
pub const CacheHit = union(enum) {
inflight: *GetAddrInfoRequest.PendingCacheKey,
new: *GetAddrInfoRequest.PendingCacheKey,
@@ -1582,10 +1788,76 @@ pub const DNSResolver = struct {
},
}
}
- // pub fn reverse(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
- // const arguments = callframe.arguments(3);
- // }
+ pub fn reverse(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
+ const arguments = callframe.arguments(2);
+ if (arguments.len < 1) {
+ globalThis.throwNotEnoughArguments("reverse", 2, arguments.len);
+ return .zero;
+ }
+
+ const ip_value = arguments.ptr[0];
+ if (ip_value.isEmptyOrUndefinedOrNull() or !ip_value.isString()) {
+ globalThis.throwInvalidArgumentType("reverse", "ip", "string");
+ return .zero;
+ }
+
+ const ip_str = ip_value.toStringOrNull(globalThis) orelse {
+ return .zero;
+ };
+ if (ip_str.length() == 0) {
+ globalThis.throwInvalidArgumentType("reverse", "ip", "non-empty string");
+ return .zero;
+ }
+
+ const ip = ip_str.toSliceClone(globalThis, bun.default_allocator).slice();
+
+ var vm = globalThis.bunVM();
+ var resolver = vm.rareData().globalDNSResolver(vm);
+ var channel: *c_ares.Channel = switch (resolver.getChannel()) {
+ .result => |res| res,
+ .err => |err| {
+ const system_error = JSC.SystemError{
+ .errno = -1,
+ .code = bun.String.static(err.code()),
+ .message = bun.String.static(err.label()),
+ };
+
+ globalThis.throwValue(system_error.toErrorInstance(globalThis));
+ return .zero;
+ },
+ };
+
+ const key = GetHostByAddrInfoRequest.PendingCacheKey.init(ip);
+ var cache = resolver.getOrPutIntoResolvePendingCache(
+ GetHostByAddrInfoRequest,
+ key,
+ "pending_addr_cache_crares",
+ );
+ if (cache == .inflight) {
+ var cares_reverse = CAresReverse.init(globalThis, globalThis.allocator(), ip) catch unreachable;
+ cache.inflight.append(cares_reverse);
+ return cares_reverse.promise.value();
+ }
+
+ var request = GetHostByAddrInfoRequest.init(
+ cache,
+ resolver,
+ ip,
+ globalThis,
+ "pending_addr_cache_crares",
+ ) catch unreachable;
+
+ const promise = request.tail.promise.value();
+ channel.getHostByAddr(
+ ip,
+ GetHostByAddrInfoRequest,
+ request,
+ GetHostByAddrInfoRequest.onCaresComplete,
+ );
+
+ return promise;
+ }
pub fn lookup(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
const arguments = callframe.arguments(2);
@@ -2185,6 +2457,12 @@ pub const DNSResolver = struct {
.name = "Bun__DNSResolver__getServers",
},
);
+ @export(
+ reverse,
+ .{
+ .name = "Bun__DNSResolver__reverse",
+ },
+ );
}
// pub fn lookupService(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
// const arguments = callframe.arguments(3);
diff --git a/src/bun.js/bindings/BunObject.cpp b/src/bun.js/bindings/BunObject.cpp
index aaf16beb8..0b78d1367 100644
--- a/src/bun.js/bindings/BunObject.cpp
+++ b/src/bun.js/bindings/BunObject.cpp
@@ -214,6 +214,7 @@ extern "C" EncodedJSValue Bun__DNSResolver__resolveNs(JSGlobalObject*, JSC::Call
extern "C" EncodedJSValue Bun__DNSResolver__resolvePtr(JSGlobalObject*, JSC::CallFrame*);
extern "C" EncodedJSValue Bun__DNSResolver__resolveCname(JSGlobalObject*, JSC::CallFrame*);
extern "C" EncodedJSValue Bun__DNSResolver__getServers(JSGlobalObject*, JSC::CallFrame*);
+extern "C" EncodedJSValue Bun__DNSResolver__reverse(JSGlobalObject*, JSC::CallFrame*);
static JSValue constructDNSObject(VM& vm, JSObject* bunObject)
{
@@ -243,6 +244,8 @@ static JSValue constructDNSObject(VM& vm, JSObject* bunObject)
JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0);
dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "getServers"_s), 2, Bun__DNSResolver__getServers, ImplementationVisibility::Public, NoIntrinsic,
JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0);
+ dnsObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "reverse"_s), 2, Bun__DNSResolver__reverse, ImplementationVisibility::Public, NoIntrinsic,
+ JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0);
return dnsObject;
}