aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2023-01-07 07:09:48 -0800
committerGravatar GitHub <noreply@github.com> 2023-01-07 07:09:48 -0800
commit87983464d8a331c1ddd09eced9920269a759f0a9 (patch)
treeb08a5aef5c2d18f25a5ee46c88bec84d5b8ee907 /src
parentd5565ab2cdd7099a5852ba5ba6d180ef291af084 (diff)
downloadbun-87983464d8a331c1ddd09eced9920269a759f0a9.tar.gz
bun-87983464d8a331c1ddd09eced9920269a759f0a9.tar.zst
bun-87983464d8a331c1ddd09eced9920269a759f0a9.zip
Implement DNS module (#1691)
* Boilerplate for DNS stuff * Add c-ares * lookup * make * Implement dns.lookup * Create c-ares * wip * normalize * repro * Revert "repro" This reverts commit 8b93e0c295b335b8882a9601da47720348549beb. * Implement macOS `getaddrinfo_async_start` * embiggen * Update string_immutable.zig * Update Makefile * alright * Update .gitignore * Add types * more ccache * Update Dockerfile * Update Dockerfile * Update Dockerfile * Update bun.d.ts Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
Diffstat (limited to 'src')
-rw-r--r--src/bun.js/api/bun/dns_resolver.zig1371
-rw-r--r--src/bun.js/base.zig78
-rw-r--r--src/bun.js/bindings/ZigGeneratedCode.cpp32
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.cpp10
-rw-r--r--src/bun.js/bindings/ares_build.h42
-rw-r--r--src/bun.js/bindings/bindings.cpp56
-rw-r--r--src/bun.js/bindings/bindings.zig66
-rw-r--r--src/bun.js/bindings/glibc-versions-hack.cpp4
-rw-r--r--src/bun.js/bindings/headers-cpp.h2
-rw-r--r--src/bun.js/bindings/headers.h5
-rw-r--r--src/bun.js/bindings/headers.zig3
-rw-r--r--src/bun.js/event_loop.zig25
-rw-r--r--src/bun.js/fs_promises.exports.js2
-rw-r--r--src/bun.js/node/syscall.zig1
-rw-r--r--src/bun.js/rare_data.zig10
-rw-r--r--src/bun.zig9
-rw-r--r--src/c.zig23
m---------src/deps/c-ares0
-rw-r--r--src/deps/c_ares.zig687
-rw-r--r--src/io/io_darwin.cpp14
-rw-r--r--src/jsc.zig1
-rw-r--r--src/string_immutable.zig8
22 files changed, 2400 insertions, 49 deletions
diff --git a/src/bun.js/api/bun/dns_resolver.zig b/src/bun.js/api/bun/dns_resolver.zig
new file mode 100644
index 000000000..285f09f92
--- /dev/null
+++ b/src/bun.js/api/bun/dns_resolver.zig
@@ -0,0 +1,1371 @@
+const Bun = @This();
+const default_allocator = @import("bun").default_allocator;
+const bun = @import("bun");
+const Environment = bun.Environment;
+const NetworkThread = @import("bun").HTTP.NetworkThread;
+const Global = bun.Global;
+const strings = bun.strings;
+const string = bun.string;
+const Output = @import("bun").Output;
+const MutableString = @import("bun").MutableString;
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const JSC = @import("bun").JSC;
+const JSValue = JSC.JSValue;
+const JSGlobalObject = JSC.JSGlobalObject;
+const c_ares = bun.c_ares;
+
+const GetAddrInfoAsyncCallback = fn (i32, ?*std.c.addrinfo, ?*anyopaque) callconv(.C) void;
+
+const LibInfo = struct {
+ // static int32_t (*getaddrinfo_async_start)(mach_port_t*,
+ // const char*,
+ // const char*,
+ // const struct addrinfo*,
+ // getaddrinfo_async_callback,
+ // void*);
+ // static int32_t (*getaddrinfo_async_handle_reply)(void*);
+ // static void (*getaddrinfo_async_cancel)(mach_port_t);
+ // typedef void getaddrinfo_async_callback(int32_t, struct addrinfo*, void*)
+ const GetaddrinfoAsyncStart = fn (*?*anyopaque, noalias node: ?[*:0]const u8, noalias service: ?[*:0]const u8, noalias hints: ?*const std.c.addrinfo, callback: *const GetAddrInfoAsyncCallback, noalias context: ?*anyopaque) callconv(.C) i32;
+ const GetaddrinfoAsyncHandleReply = fn (?**anyopaque) callconv(.C) i32;
+ const GetaddrinfoAsyncCancel = fn (?**anyopaque) callconv(.C) void;
+
+ var handle: ?*anyopaque = null;
+ var loaded = false;
+ pub fn getHandle() ?*anyopaque {
+ if (loaded)
+ return handle;
+ loaded = true;
+ const RTLD_LAZY = 1;
+ const RTLD_LOCAL = 4;
+
+ handle = std.c.dlopen("libinfo.dylib", RTLD_LAZY | RTLD_LOCAL);
+ if (handle == null)
+ Output.debug("libinfo.dylib not found", .{});
+ return handle;
+ }
+
+ pub const getaddrinfo_async_start = struct {
+ pub fn get() ?*const GetaddrinfoAsyncStart {
+ return bun.C.dlsymWithHandle(*const GetaddrinfoAsyncStart, "getaddrinfo_async_start", getHandle);
+ }
+ }.get;
+
+ pub const getaddrinfo_async_handle_reply = struct {
+ pub fn get() ?*const GetaddrinfoAsyncHandleReply {
+ return bun.C.dlsymWithHandle(*const GetaddrinfoAsyncHandleReply, "getaddrinfo_async_handle_reply", getHandle);
+ }
+ }.get;
+
+ pub fn get() ?*const GetaddrinfoAsyncCancel {
+ return bun.C.dlsymWithHandle(*const GetaddrinfoAsyncCancel, "getaddrinfo_async_cancel", getHandle);
+ }
+
+ pub fn lookup(this: *DNSResolver, query: GetAddrInfo, globalThis: *JSC.JSGlobalObject) JSC.JSValue {
+ const getaddrinfo_async_start_ = LibInfo.getaddrinfo_async_start() orelse return LibC.lookup(this, query, globalThis);
+
+ var key = GetAddrInfoRequest.PendingCacheKey.init(query);
+ var cache = this.getOrPutIntoPendingCache(key, .pending_host_cache_native);
+
+ if (cache == .inflight) {
+ var dns_lookup = DNSLookup.init(globalThis, globalThis.allocator()) catch unreachable;
+
+ cache.inflight.append(dns_lookup);
+
+ return dns_lookup.promise.value();
+ }
+
+ var name_buf: [1024]u8 = undefined;
+ _ = strings.copy(name_buf[0..], query.name);
+
+ name_buf[query.name.len] = 0;
+ var name_z = name_buf[0..query.name.len :0];
+
+ var request = GetAddrInfoRequest.init(
+ cache,
+ .{ .libinfo = undefined },
+ this,
+ query,
+ globalThis,
+ "pending_host_cache_native",
+ ) catch unreachable;
+ const promise_value = request.head.promise.value();
+
+ const errno = getaddrinfo_async_start_(
+ &request.backend.libinfo.machport,
+ name_z.ptr,
+ null,
+ null,
+ GetAddrInfoRequest.getAddrInfoAsyncCallback,
+ request,
+ );
+
+ if (errno != 0) {
+ request.head.promise.reject(globalThis, globalThis.createErrorInstance("getaddrinfo_async_start error: {s}", .{@tagName(std.c.getErrno(errno))}));
+ if (request.cache.pending_cache) this.pending_host_cache_native.available.set(request.cache.pos_in_pending);
+ this.vm.allocator.destroy(request);
+
+ return promise_value;
+ }
+ std.debug.assert(request.backend.libinfo.machport != null);
+ request.backend.libinfo.file_poll = bun.JSC.FilePoll.init(this.vm, std.math.maxInt(i32) - 1, .{}, GetAddrInfoRequest, request);
+ std.debug.assert(
+ request.backend.libinfo.file_poll.?.registerWithFd(
+ this.vm.uws_event_loop.?,
+ .machport,
+ true,
+ @ptrToInt(request.backend.libinfo.machport),
+ ) == .result,
+ );
+
+ return promise_value;
+ }
+};
+
+const LibC = struct {
+ pub fn lookup(this: *DNSResolver, query_init: GetAddrInfo, globalThis: *JSC.JSGlobalObject) JSC.JSValue {
+ const key = GetAddrInfoRequest.PendingCacheKey.init(query_init);
+
+ var cache = this.getOrPutIntoPendingCache(key, .pending_host_cache_native);
+ if (cache == .inflight) {
+ var dns_lookup = DNSLookup.init(globalThis, globalThis.allocator()) catch unreachable;
+
+ cache.inflight.append(dns_lookup);
+
+ return dns_lookup.promise.value();
+ }
+
+ var query = query_init.clone();
+
+ var request = GetAddrInfoRequest.init(
+ cache,
+ .{
+ .libc = .{
+ .query = query,
+ },
+ },
+ this,
+ query,
+ globalThis,
+ "pending_host_cache_native",
+ ) catch unreachable;
+ const promise_value = request.head.promise.value();
+
+ var io = GetAddrInfoRequest.Task.createOnJSThread(this.vm.allocator, globalThis, request) catch unreachable;
+
+ io.schedule();
+
+ return promise_value;
+ }
+};
+
+pub fn addressToString(
+ allocator: std.mem.Allocator,
+ address: std.net.Address,
+) JSC.ZigString {
+ const str: []const u8 = brk: {
+ switch (address.any.family) {
+ std.os.AF.INET => {
+ var self = address.in;
+ const bytes = @ptrCast(*const [4]u8, &self.sa.addr);
+ break :brk std.fmt.allocPrint(allocator, "{}.{}.{}.{}", .{
+ bytes[0],
+ bytes[1],
+ bytes[2],
+ bytes[3],
+ }) catch unreachable;
+ },
+ std.os.AF.INET6 => {
+ var out = std.fmt.allocPrint(allocator, "{any}", .{address}) catch unreachable;
+ // TODO: this is a hack, fix it
+ // This removes [.*]:port
+ // ^ ^^^^^^
+ break :brk out[1 .. out.len - 1 - std.fmt.count("{d}", .{address.in6.getPort()}) - 1];
+ },
+ std.os.AF.UNIX => {
+ break :brk std.mem.sliceTo(&address.un.path, 0);
+ },
+ else => break :brk "",
+ }
+ };
+
+ return JSC.ZigString.init(str);
+}
+
+pub fn normalizeDNSName(name: []const u8, backend: *GetAddrInfo.Backend) []const u8 {
+ if (backend.* == .c_ares) {
+ // https://github.com/c-ares/c-ares/issues/477
+ if (strings.endsWithComptime(name, ".localhost")) {
+ backend.* = .system;
+ return "localhost";
+ } else if (strings.endsWithComptime(name, ".local")) {
+ backend.* = .system;
+ // https://github.com/c-ares/c-ares/pull/463
+ } else if (strings.isIPV6Address(name)) {
+ backend.* = .system;
+ }
+ }
+
+ return name;
+}
+
+pub fn addressToJS(
+ allocator: std.mem.Allocator,
+ address: std.net.Address,
+ globalThis: *JSC.JSGlobalObject,
+) JSC.JSValue {
+ return addressToString(allocator, address).toValueGC(globalThis);
+}
+
+fn addrInfoCount(addrinfo: *std.c.addrinfo) u32 {
+ var count: u32 = 1;
+ var current: ?*std.c.addrinfo = addrinfo.next;
+ while (current != null) : (current = current.?.next) {
+ count += @boolToInt(current.?.addr != null);
+ }
+ return count;
+}
+
+pub fn addrInfoToJSArray(
+ parent_allocator: std.mem.Allocator,
+ addr_info: *std.c.addrinfo,
+ globalThis: *JSC.JSGlobalObject,
+) JSC.JSValue {
+ var stack = std.heap.stackFallback(2048, parent_allocator);
+ var arena = std.heap.ArenaAllocator.init(stack.get());
+ const array = JSC.JSValue.createEmptyArray(
+ globalThis,
+ addrInfoCount(addr_info),
+ );
+
+ {
+ defer arena.deinit();
+
+ var allocator = arena.allocator();
+ var j: u32 = 0;
+ var current: ?*std.c.addrinfo = addr_info;
+ while (current) |this_node| : (current = current.?.next) {
+ array.putIndex(
+ globalThis,
+ j,
+ bun.JSC.DNS.GetAddrInfo.Result.toJS(
+ &(bun.JSC.DNS.GetAddrInfo.Result.fromAddrInfo(this_node) orelse continue),
+ globalThis,
+ allocator,
+ ),
+ );
+ j += 1;
+ }
+ }
+
+ return array;
+}
+
+pub const GetAddrInfo = struct {
+ name: []const u8 = "",
+ port: u16 = 0,
+ options: Options = Options{},
+
+ pub fn clone(this: GetAddrInfo) GetAddrInfo {
+ return GetAddrInfo{
+ .name = bun.default_allocator.dupe(u8, this.name) catch unreachable,
+ .port = this.port,
+ .options = this.options,
+ };
+ }
+
+ pub fn toCAres(this: GetAddrInfo) bun.c_ares.AddrInfo_hints {
+ var hints: bun.c_ares.AddrInfo_hints = undefined;
+ @memset(std.mem.asBytes(&hints), 0, @sizeOf(bun.c_ares.AddrInfo_hints));
+
+ hints.ai_family = this.options.family.toLibC();
+ hints.ai_socktype = this.options.socktype.toLibC();
+ hints.ai_protocol = this.options.protocol.toLibC();
+ hints.ai_flags = this.options.flags;
+
+ return hints;
+ }
+
+ pub fn hash(self: GetAddrInfo) u64 {
+ var hasher = std.hash.Wyhash.init(0);
+ const bytes =
+ std.mem.asBytes(&self.port) ++
+ std.mem.asBytes(&self.options);
+
+ hasher.update(bytes);
+ hasher.update(self.name);
+
+ return hasher.final();
+ }
+
+ pub const Options = packed struct {
+ family: Family = .unspecified,
+ socktype: SocketType = .unspecified,
+ protocol: Protocol = .unspecified,
+ backend: Backend = Backend.default,
+ flags: i32 = 0,
+
+ pub fn toLibC(this: Options) ?std.c.addrinfo {
+ if (this.family == .unspecified and this.socktype == .unspecified and this.protocol == .unspecified and this.flags == 0) {
+ return null;
+ }
+
+ var hints: std.c.addrinfo = undefined;
+ @memset(std.mem.asBytes(&hints), 0, @sizeOf(std.c.addrinfo));
+
+ hints.family = this.family.toLibC();
+ hints.socktype = this.socktype.toLibC();
+ hints.protocol = this.protocol.toLibC();
+ hints.flags = this.flags;
+ return hints;
+ }
+
+ pub fn fromJS(value: JSC.JSValue, globalObject: *JSC.JSGlobalObject) !Options {
+ if (value.isEmptyOrUndefinedOrNull())
+ return Options{};
+
+ if (value.isObject()) {
+ var options = Options{};
+
+ if (value.get(globalObject, "family")) |family| {
+ options.family = try Family.fromJS(family, globalObject);
+ }
+
+ if (value.get(globalObject, "socketType") orelse value.get(globalObject, "socktype")) |socktype| {
+ options.socktype = try SocketType.fromJS(socktype, globalObject);
+ }
+
+ if (value.get(globalObject, "protocol")) |protocol| {
+ options.protocol = try Protocol.fromJS(protocol, globalObject);
+ }
+
+ if (value.get(globalObject, "backend")) |backend| {
+ options.backend = try Backend.fromJS(backend, globalObject);
+ }
+
+ if (value.get(globalObject, "flags")) |flags| {
+ if (!flags.isNumber())
+ return error.InvalidFlags;
+
+ options.flags = flags.coerce(i32, globalObject);
+ }
+
+ return options;
+ }
+
+ return error.InvalidOptions;
+ }
+ };
+
+ pub const Family = enum(u2) {
+ unspecified,
+ inet,
+ inet6,
+ unix,
+
+ pub const map = bun.ComptimeStringMap(Family, .{
+ .{ "IPv4", Family.inet },
+ .{ "IPv6", Family.inet6 },
+ .{ "ipv4", Family.inet },
+ .{ "ipv6", Family.inet6 },
+ .{ "any", Family.unspecified },
+ });
+
+ pub fn fromJS(value: JSC.JSValue, globalObject: *JSC.JSGlobalObject) !Family {
+ if (value.isEmptyOrUndefinedOrNull())
+ return .unspecified;
+
+ if (value.isNumber()) {
+ return switch (value.to(i32)) {
+ 0 => .unspecified,
+ 4 => .inet,
+ 6 => .inet6,
+ else => return error.InvalidFamily,
+ };
+ }
+
+ if (value.isString()) {
+ const str = value.getZigString(globalObject);
+ if (str.len == 0)
+ return .unspecified;
+
+ return map.getWithEql(str, JSC.ZigString.eqlComptime) orelse return error.InvalidFamily;
+ }
+
+ return error.InvalidFamily;
+ }
+
+ pub fn toLibC(this: Family) i32 {
+ return switch (this) {
+ .unspecified => 0,
+ .inet => std.os.AF.INET,
+ .inet6 => std.os.AF.INET6,
+ .unix => std.os.AF.UNIX,
+ };
+ }
+ };
+
+ pub const SocketType = enum(u2) {
+ unspecified,
+ stream,
+ dgram,
+
+ const map = bun.ComptimeStringMap(SocketType, .{
+ .{ "stream", SocketType.stream },
+ .{ "dgram", SocketType.dgram },
+ .{ "tcp", SocketType.stream },
+ .{ "udp", SocketType.dgram },
+ });
+
+ pub fn toLibC(this: SocketType) i32 {
+ switch (this) {
+ .unspecified => return 0,
+ .stream => return std.os.SOCK.STREAM,
+ .dgram => return std.os.SOCK.DGRAM,
+ }
+ }
+
+ pub fn fromJS(value: JSC.JSValue, globalObject: *JSC.JSGlobalObject) !SocketType {
+ if (value.isEmptyOrUndefinedOrNull())
+ return .unspecified;
+
+ if (value.isNumber()) {
+ return switch (value.to(i32)) {
+ 0 => .unspecified,
+ 1 => .stream,
+ 2 => .dgram,
+ else => return error.InvalidSocketType,
+ };
+ }
+
+ if (value.isString()) {
+ const str = value.getZigString(globalObject);
+ if (str.len == 0)
+ return .unspecified;
+
+ return map.getWithEql(str, JSC.ZigString.eqlComptime) orelse return error.InvalidSocketType;
+ }
+
+ return error.InvalidSocketType;
+ }
+ };
+
+ pub const Protocol = enum(u2) {
+ unspecified,
+ tcp,
+ udp,
+
+ const map = bun.ComptimeStringMap(Protocol, .{
+ .{ "tcp", Protocol.tcp },
+ .{ "udp", Protocol.udp },
+ });
+
+ pub fn fromJS(value: JSC.JSValue, globalObject: *JSC.JSGlobalObject) !Protocol {
+ if (value.isEmptyOrUndefinedOrNull())
+ return .unspecified;
+
+ if (value.isNumber()) {
+ return switch (value.to(i32)) {
+ 0 => .unspecified,
+ 6 => .tcp,
+ 17 => .udp,
+ else => return error.InvalidProtocol,
+ };
+ }
+
+ if (value.isString()) {
+ const str = value.getZigString(globalObject);
+ if (str.len == 0)
+ return .unspecified;
+
+ return map.getWithEql(str, JSC.ZigString.eqlComptime) orelse return error.InvalidProtocol;
+ }
+
+ return error.InvalidProtocol;
+ }
+
+ pub fn toLibC(this: Protocol) i32 {
+ switch (this) {
+ .unspecified => return 0,
+ .tcp => return std.os.IPPROTO.TCP,
+ .udp => return std.os.IPPROTO.UDP,
+ }
+ }
+ };
+
+ pub const Backend = enum(u2) {
+ c_ares,
+ system,
+ libc,
+
+ pub const label = bun.ComptimeStringMap(GetAddrInfo.Backend, .{
+ .{ "c-ares", .c_ares },
+ .{ "c_ares", .c_ares },
+ .{ "cares", .c_ares },
+ .{ "async", .c_ares },
+ .{ "libc", .libc },
+ .{ "system", .system },
+ .{ "getaddrinfo", .libc },
+ });
+
+ pub const default: GetAddrInfo.Backend = if (Environment.isMac)
+ GetAddrInfo.Backend.system
+ else
+ GetAddrInfo.Backend.c_ares;
+
+ pub fn fromJS(value: JSC.JSValue, globalObject: *JSC.JSGlobalObject) !Backend {
+ if (value.isEmptyOrUndefinedOrNull())
+ return default;
+
+ if (value.isString()) {
+ const str = value.getZigString(globalObject);
+ if (str.len == 0)
+ return default;
+
+ return label.getWithEql(str, JSC.ZigString.eqlComptime) orelse return error.InvalidBackend;
+ }
+
+ return error.InvalidBackend;
+ }
+ };
+
+ pub const Result = struct {
+ address: std.net.Address,
+ ttl: i32 = 0,
+
+ pub const List = std.ArrayList(Result);
+
+ pub const Any = union(enum) {
+ addrinfo: ?*std.c.addrinfo,
+ list: List,
+
+ pub fn toJS(this: Any, globalThis: *JSC.JSGlobalObject) ?JSC.JSValue {
+ return switch (this) {
+ .addrinfo => |addrinfo| addrInfoToJSArray(globalThis.allocator(), addrinfo orelse return null, globalThis),
+ .list => |list| brk: {
+ var stack = std.heap.stackFallback(2048, globalThis.allocator());
+ var arena = std.heap.ArenaAllocator.init(stack.get());
+ const array = JSC.JSValue.createEmptyArray(globalThis, @truncate(u32, list.items.len));
+ var i: u32 = 0;
+ const items: []const Result = list.items;
+ for (items) |item| {
+ array.putIndex(globalThis, i, item.toJS(globalThis, arena.allocator()));
+ i += 1;
+ }
+ break :brk array;
+ },
+ };
+ }
+
+ pub fn deinit(this: Any) void {
+ switch (this) {
+ .addrinfo => |addrinfo| {
+ if (addrinfo) |a| {
+ std.c.freeaddrinfo(a);
+ }
+ },
+ .list => |list| {
+ var list_ = list;
+ list_.deinit();
+ },
+ }
+ }
+ };
+
+ pub fn toList(allocator: std.mem.Allocator, addrinfo: *std.c.addrinfo) !List {
+ var list = try List.initCapacity(allocator, addrInfoCount(addrinfo));
+
+ var addr: ?*std.c.addrinfo = addrinfo;
+ while (addr) |a| : (addr = a.next) {
+ list.appendAssumeCapacity(fromAddrInfo(a) orelse continue);
+ }
+
+ return list;
+ }
+
+ pub fn fromAddrInfo(addrinfo: *std.c.addrinfo) ?Result {
+ return Result{
+ .address = std.net.Address.initPosix(@alignCast(4, addrinfo.addr orelse return null)),
+ // no TTL in POSIX getaddrinfo()
+ .ttl = 0,
+ };
+ }
+
+ pub fn toJS(this: *const Result, globalThis: *JSC.JSGlobalObject, allocator: std.mem.Allocator) JSValue {
+ const obj = JSC.JSValue.createEmptyObject(globalThis, 3);
+ obj.put(globalThis, JSC.ZigString.static("address"), addressToJS(allocator, this.address, globalThis));
+ obj.put(globalThis, JSC.ZigString.static("family"), switch (this.address.any.family) {
+ std.os.AF.INET => JSValue.jsNumber(4),
+ std.os.AF.INET6 => JSValue.jsNumber(6),
+ else => JSValue.jsNumber(0),
+ });
+ obj.put(globalThis, JSC.ZigString.static("ttl"), JSValue.jsNumber(this.ttl));
+ return obj;
+ }
+ };
+};
+
+pub const GetAddrInfoRequest = struct {
+ const log = Output.scoped(.GetAddrInfoRequest, false);
+
+ backend: Backend = undefined,
+ resolver_for_caching: ?*DNSResolver = null,
+ hash: u64 = 0,
+ cache: CacheConfig = CacheConfig{},
+ head: DNSLookup,
+ tail: *DNSLookup = undefined,
+ task: bun.ThreadPool.Task = undefined,
+
+ pub fn init(
+ cache: DNSResolver.CacheHit,
+ backend: Backend,
+ resolver: ?*DNSResolver,
+ query: GetAddrInfo,
+ globalThis: *JSC.JSGlobalObject,
+ comptime cache_field: []const u8,
+ ) !*GetAddrInfoRequest {
+ var request = try globalThis.allocator().create(GetAddrInfoRequest);
+ request.* = .{
+ .backend = backend,
+ .resolver_for_caching = resolver,
+ .hash = query.hash(),
+ .head = .{
+ .globalThis = globalThis,
+ .promise = JSC.JSPromise.Strong.init(globalThis),
+ .allocated = false,
+ },
+ };
+ request.tail = &request.head;
+ if (cache == .new) {
+ request.resolver_for_caching = resolver;
+ request.cache = CacheConfig{
+ .pending_cache = true,
+ .entry_cache = false,
+ .pos_in_pending = @truncate(u5, @field(resolver.?, cache_field).indexOf(cache.new).?),
+ .name_len = @truncate(u9, query.name.len),
+ };
+ cache.new.lookup = request;
+ }
+ return request;
+ }
+
+ pub const Task = bun.JSC.WorkTask(GetAddrInfoRequest, false);
+
+ pub const CacheConfig = packed struct(u16) {
+ pending_cache: bool = false,
+ entry_cache: bool = false,
+ pos_in_pending: u5 = 0,
+ name_len: u9 = 0,
+ };
+
+ pub const PendingCacheKey = struct {
+ hash: u64,
+ len: u16,
+ lookup: *GetAddrInfoRequest = undefined,
+
+ pub fn append(this: *PendingCacheKey, dns_lookup: *DNSLookup) void {
+ var tail = this.lookup.tail;
+ tail.next = dns_lookup;
+ this.lookup.tail = dns_lookup;
+ }
+
+ pub fn init(query: GetAddrInfo) PendingCacheKey {
+ return PendingCacheKey{
+ .hash = query.hash(),
+ .len = @truncate(u16, query.name.len),
+ .lookup = undefined,
+ };
+ }
+ };
+
+ pub fn getAddrInfoAsyncCallback(
+ status: i32,
+ addr_info: ?*std.c.addrinfo,
+ arg: ?*anyopaque,
+ ) callconv(.C) void {
+ const this = @intToPtr(*GetAddrInfoRequest, @ptrToInt(arg));
+ log("getAddrInfoAsyncCallback: status={d}", .{status});
+
+ if (this.backend == .libinfo) {
+ if (this.backend.libinfo.file_poll) |poll| poll.deinit();
+ }
+
+ if (this.resolver_for_caching) |resolver| {
+ if (this.cache.pending_cache) {
+ resolver.drainPendingHostNative(this.cache.pos_in_pending, this.head.globalThis, status, .{ .addrinfo = addr_info });
+ return;
+ }
+ }
+
+ var head = this.head;
+ bun.default_allocator.destroy(this);
+ head.processGetAddrInfoNative(status, addr_info);
+ }
+
+ pub const Backend = union(enum) {
+ c_ares: void,
+ libinfo: GetAddrInfoRequest.Backend.LibInfo,
+ libc: union(enum) {
+ success: GetAddrInfo.Result.List,
+ err: i32,
+ query: GetAddrInfo,
+
+ pub fn run(this: *@This()) void {
+ const query = this.query;
+ defer bun.default_allocator.free(bun.constStrToU8(query.name));
+ var hints = query.options.toLibC();
+ var port_buf: [128]u8 = undefined;
+ var port = std.fmt.bufPrintIntToSlice(&port_buf, query.port, 10, .lower, .{});
+ port_buf[port.len] = 0;
+ var portZ = port_buf[0..port.len :0];
+ var hostname: [bun.MAX_PATH_BYTES]u8 = undefined;
+ _ = strings.copy(hostname[0..], query.name);
+ hostname[query.name.len] = 0;
+ var addrinfo: *std.c.addrinfo = undefined;
+ var host = hostname[0..query.name.len :0];
+ const debug_timer = bun.Output.DebugTimer.start();
+ const err = std.c.getaddrinfo(
+ host.ptr,
+ if (port.len > 0) portZ.ptr else null,
+ if (hints) |*hint| hint else null,
+ &addrinfo,
+ );
+ defer std.c.freeaddrinfo(addrinfo);
+ JSC.Node.Syscall.syslog("getaddrinfo({s}, {d}) = {d} ({any})", .{
+ query.name,
+ port,
+ err,
+ debug_timer,
+ });
+ if (@enumToInt(err) != 0) {
+ this.* = .{ .err = @enumToInt(err) };
+ return;
+ }
+
+ this.* = .{ .success = GetAddrInfo.Result.toList(default_allocator, addrinfo) catch unreachable };
+ }
+ },
+
+ pub const LibInfo = struct {
+ file_poll: ?*bun.JSC.FilePoll = null,
+ machport: ?*anyopaque = null,
+
+ extern fn getaddrinfo_send_reply(*anyopaque, *const JSC.DNS.LibInfo.GetaddrinfoAsyncHandleReply) bool;
+ pub fn onMachportChange(this: *GetAddrInfoRequest) void {
+ if (!getaddrinfo_send_reply(this.backend.libinfo.machport.?, JSC.DNS.LibInfo.getaddrinfo_async_handle_reply().?)) {
+ log("onMachportChange: getaddrinfo_send_reply failed", .{});
+ getAddrInfoAsyncCallback(-1, null, this);
+ }
+ }
+ };
+ };
+
+ pub const onMachportChange = Backend.LibInfo.onMachportChange;
+
+ pub fn run(this: *GetAddrInfoRequest, task: *Task) void {
+ this.backend.libc.run();
+ task.onFinish();
+ }
+
+ pub fn then(this: *GetAddrInfoRequest, _: *JSC.JSGlobalObject) void {
+ switch (this.backend.libc) {
+ .success => |result| {
+ const any = GetAddrInfo.Result.Any{ .list = result };
+ defer any.deinit();
+ if (this.resolver_for_caching) |resolver| {
+ // if (this.cache.entry_cache and result != null and result.?.node != null) {
+ // resolver.putEntryInCache(this.hash, this.cache.name_len, result.?);
+ // }
+
+ if (this.cache.pending_cache) {
+ resolver.drainPendingHostNative(this.cache.pos_in_pending, this.head.globalThis, 0, any);
+ return;
+ }
+ }
+ var head = this.head;
+ bun.default_allocator.destroy(this);
+ head.onCompleteNative(any);
+ },
+ .err => |err| {
+ getAddrInfoAsyncCallback(err, null, this);
+ },
+ else => unreachable,
+ }
+ }
+
+ pub fn onCaresComplete(this: *GetAddrInfoRequest, err_: ?c_ares.Error, timeout: i32, result: ?*c_ares.AddrInfo) void {
+ if (this.resolver_for_caching) |resolver| {
+ // if (this.cache.entry_cache and result != null and result.?.node != null) {
+ // resolver.putEntryInCache(this.hash, this.cache.name_len, result.?);
+ // }
+
+ if (this.cache.pending_cache) {
+ resolver.drainPendingHostCares(
+ this.cache.pos_in_pending,
+ err_,
+ timeout,
+ result,
+ );
+ return;
+ }
+ }
+
+ var head = this.head;
+ bun.default_allocator.destroy(this);
+
+ head.processGetAddrInfo(err_, timeout, result);
+ }
+};
+
+pub const DNSLookup = struct {
+ const log = Output.scoped(.DNSLookup, true);
+
+ globalThis: *JSC.JSGlobalObject = undefined,
+ promise: JSC.JSPromise.Strong,
+ allocated: bool = false,
+ next: ?*DNSLookup = null,
+
+ pub fn init(globalThis: *JSC.JSGlobalObject, allocator: std.mem.Allocator) !*DNSLookup {
+ var this = try allocator.create(DNSLookup);
+ this.* = .{
+ .globalThis = globalThis,
+ .promise = JSC.JSPromise.Strong.init(globalThis),
+ .allocated = true,
+ };
+ return this;
+ }
+
+ pub fn onCompleteNative(this: *DNSLookup, result: GetAddrInfo.Result.Any) void {
+ const array = result.toJS(this.globalThis).?;
+ this.onCompleteWithArray(array);
+ }
+
+ pub fn processGetAddrInfoNative(this: *DNSLookup, status: i32, result: ?*std.c.addrinfo) void {
+ if (c_ares.Error.initEAI(status)) |err| {
+ var promise = this.promise;
+ var globalThis = this.globalThis;
+
+ const error_value = brk: {
+ if (err == .ESERVFAIL) {
+ break :brk JSC.Node.Syscall.Error.fromCode(std.c.getErrno(-1), .getaddrinfo).toJSC(globalThis);
+ }
+ const error_value = globalThis.createErrorInstance("DNS lookup failed: {s}", .{err.label()});
+ error_value.put(
+ globalThis,
+ JSC.ZigString.static("code"),
+ JSC.ZigString.init(err.code()).toValueGC(globalThis),
+ );
+ break :brk error_value;
+ };
+
+ this.deinit();
+
+ promise.reject(globalThis, error_value);
+ return;
+ }
+
+ onCompleteNative(this, .{ .addrinfo = result });
+ }
+
+ pub fn processGetAddrInfo(this: *DNSLookup, err_: ?c_ares.Error, _: i32, result: ?*c_ares.AddrInfo) void {
+ if (err_) |err| {
+ var promise = this.promise;
+ var globalThis = this.globalThis;
+ const error_value = globalThis.createErrorInstance("DNS lookup failed: {s}", .{err.label()});
+ error_value.put(
+ globalThis,
+ JSC.ZigString.static("code"),
+ JSC.ZigString.init(err.code()).toValueGC(globalThis),
+ );
+
+ this.deinit();
+
+ promise.reject(globalThis, error_value);
+ return;
+ }
+
+ if (result == null or result.?.node == null) {
+ var promise = this.promise;
+ var globalThis = this.globalThis;
+ const error_value = globalThis.createErrorInstance("DNS lookup failed: {s}", .{"No results"});
+ error_value.put(
+ globalThis,
+ JSC.ZigString.static("code"),
+ JSC.ZigString.init("EUNREACHABLE").toValueGC(globalThis),
+ );
+
+ this.deinit();
+ promise.reject(globalThis, error_value);
+ return;
+ }
+
+ this.onComplete(result.?);
+ }
+
+ pub fn onComplete(this: *DNSLookup, result: *c_ares.AddrInfo) void {
+ const array = result.toJSArray(this.globalThis.allocator(), this.globalThis);
+ this.onCompleteWithArray(array);
+ }
+
+ pub fn onCompleteWithArray(this: *DNSLookup, result: JSC.JSValue) void {
+ var promise = this.promise;
+ var globalThis = this.globalThis;
+ this.promise = .{};
+
+ this.deinit();
+ promise.resolveOnNextTick(globalThis, result);
+ }
+
+ pub fn deinit(this: *DNSLookup) void {
+ if (this.allocated)
+ this.globalThis.allocator().destroy(this);
+ }
+};
+
+pub const GlobalData = struct {
+ resolver: DNSResolver,
+
+ pub fn init(allocator: std.mem.Allocator, vm: *JSC.VirtualMachine) *GlobalData {
+ var global = allocator.create(GlobalData) catch unreachable;
+ global.* = .{
+ .resolver = .{
+ .vm = vm,
+ .polls = std.AutoArrayHashMap(i32, ?*JSC.FilePoll).init(allocator),
+ },
+ };
+
+ return global;
+ }
+};
+
+pub const DNSResolver = struct {
+ const log = Output.scoped(.DNSResolver, true);
+
+ channel: ?*c_ares.Channel = null,
+ vm: *JSC.VirtualMachine,
+ polls: std.AutoArrayHashMap(i32, ?*JSC.FilePoll) = undefined,
+
+ pending_host_cache_cares: PendingCache = PendingCache.init(),
+ pending_host_cache_native: PendingCache = PendingCache.init(),
+ // entry_host_cache: std.BoundedArray(128)
+
+ const PendingCache = bun.HiveArray(GetAddrInfoRequest.PendingCacheKey, 32);
+
+ pub fn drainPendingHostCares(this: *DNSResolver, index: u8, err: ?c_ares.Error, timeout: i32, result: ?*c_ares.AddrInfo) void {
+ const key: GetAddrInfoRequest.PendingCacheKey = brk: {
+ std.debug.assert(!this.pending_host_cache_cares.available.isSet(index));
+ const entry = this.pending_host_cache_cares.buffer[index];
+ this.pending_host_cache_cares.buffer[index] = undefined;
+ this.pending_host_cache_cares.available.unset(index);
+ break :brk entry;
+ };
+
+ var addr = result orelse {
+ var pending: ?*DNSLookup = key.lookup.head.next;
+ key.lookup.head.processGetAddrInfo(err, timeout, null);
+ bun.default_allocator.destroy(key.lookup);
+
+ while (pending) |value| {
+ pending = value.next;
+ value.processGetAddrInfo(err, timeout, null);
+ }
+ return;
+ };
+
+ var pending: ?*DNSLookup = key.lookup.head.next;
+ var prev_global = key.lookup.head.globalThis;
+ var array = addr.toJSArray(this.vm.allocator, prev_global);
+ defer addr.deinit();
+ array.ensureStillAlive();
+ key.lookup.head.onCompleteWithArray(array);
+ bun.default_allocator.destroy(key.lookup);
+
+ array.ensureStillAlive();
+ // std.c.addrinfo
+
+ while (pending) |value| {
+ var new_global = value.globalThis;
+ if (prev_global != new_global) {
+ array = addr.toJSArray(this.vm.allocator, new_global);
+ prev_global = new_global;
+ }
+ array.ensureStillAlive();
+ value.onCompleteWithArray(array);
+ array.ensureStillAlive();
+ pending = value.next;
+ }
+ }
+
+ pub fn drainPendingHostNative(this: *DNSResolver, index: u8, globalObject: *JSC.JSGlobalObject, err: i32, result: GetAddrInfo.Result.Any) void {
+ const key: GetAddrInfoRequest.PendingCacheKey = brk: {
+ std.debug.assert(!this.pending_host_cache_native.available.isSet(index));
+ const entry = this.pending_host_cache_native.buffer[index];
+ this.pending_host_cache_native.buffer[index] = undefined;
+ this.pending_host_cache_native.available.unset(index);
+ break :brk entry;
+ };
+
+ var array = result.toJS(globalObject) orelse {
+ var pending: ?*DNSLookup = key.lookup.head.next;
+ var head = key.lookup.head;
+ head.processGetAddrInfoNative(err, null);
+ bun.default_allocator.destroy(key.lookup);
+
+ while (pending) |value| {
+ pending = value.next;
+ value.processGetAddrInfoNative(err, null);
+ }
+
+ return;
+ };
+ var pending: ?*DNSLookup = key.lookup.head.next;
+ var prev_global = key.lookup.head.globalThis;
+ array.ensureStillAlive();
+ key.lookup.head.onCompleteWithArray(array);
+ bun.default_allocator.destroy(key.lookup);
+ array.ensureStillAlive();
+ // std.c.addrinfo
+
+ while (pending) |value| {
+ var new_global = value.globalThis;
+ if (prev_global != new_global) {
+ array = result.toJS(new_global).?;
+ prev_global = new_global;
+ }
+ array.ensureStillAlive();
+ value.onCompleteWithArray(array);
+ array.ensureStillAlive();
+ pending = value.next;
+ }
+ }
+
+ pub const CacheHit = union(enum) {
+ inflight: *GetAddrInfoRequest.PendingCacheKey,
+ new: *GetAddrInfoRequest.PendingCacheKey,
+ disabled: void,
+ };
+
+ pub fn getOrPutIntoPendingCache(
+ this: *DNSResolver,
+ key: GetAddrInfoRequest.PendingCacheKey,
+ comptime field: std.meta.FieldEnum(DNSResolver),
+ ) CacheHit {
+ var cache: *PendingCache = &@field(this, @tagName(field));
+ var inflight_iter = cache.available.iterator(.{
+ .kind = .unset,
+ });
+
+ while (inflight_iter.next()) |index| {
+ var entry: *GetAddrInfoRequest.PendingCacheKey = &cache.buffer[index];
+ if (entry.hash == key.hash and entry.len == key.len) {
+ return .{ .inflight = entry };
+ }
+ }
+
+ if (cache.get()) |new| {
+ new.hash = key.hash;
+ new.len = key.len;
+ return .{ .new = new };
+ }
+
+ return .{ .disabled = {} };
+ }
+
+ pub const ChannelResult = union(enum) {
+ err: c_ares.Error,
+ result: *c_ares.Channel,
+ };
+ pub fn getChannel(this: *DNSResolver) ChannelResult {
+ if (this.channel == null) {
+ if (c_ares.Channel.init(DNSResolver, this)) |err| {
+ return .{ .err = err };
+ }
+ }
+
+ return .{ .result = this.channel.? };
+ }
+
+ pub fn onDNSPoll(
+ this: *DNSResolver,
+ poll: *JSC.FilePoll,
+ ) void {
+ var channel = this.channel orelse {
+ _ = this.polls.orderedRemove(@intCast(i32, poll.fd));
+ poll.deinit();
+ return;
+ };
+
+ channel.process(
+ @intCast(i32, poll.fd),
+ poll.isReadable(),
+ poll.isWritable(),
+ );
+ }
+
+ pub fn onDNSSocketState(
+ this: *DNSResolver,
+ fd: i32,
+ readable: bool,
+ writable: bool,
+ ) void {
+ var vm = this.vm;
+
+ if (!readable and !writable) {
+ // read == 0 and write == 0 this is c-ares's way of notifying us that
+ // the socket is now closed. We must free the data associated with
+ // socket.
+ if (this.polls.fetchOrderedRemove(fd)) |entry| {
+ if (entry.value) |val| {
+ val.deinitWithVM(vm);
+ }
+ }
+
+ return;
+ }
+
+ var poll_entry = this.polls.getOrPut(fd) catch unreachable;
+
+ if (!poll_entry.found_existing) {
+ poll_entry.value_ptr.* = JSC.FilePoll.init(vm, fd, .{}, DNSResolver, this);
+ }
+
+ var poll = poll_entry.value_ptr.*.?;
+
+ if (readable and !poll.flags.contains(.poll_readable))
+ _ = poll.register(vm.uws_event_loop.?, .readable, false);
+
+ if (writable and !poll.flags.contains(.poll_writable))
+ _ = poll.register(vm.uws_event_loop.?, .writable, false);
+ }
+
+ const DNSQuery = struct {
+ name: JSC.ZigString.Slice,
+ record_type: RecordType,
+
+ ttl: i32 = 0,
+ };
+
+ pub const RecordType = enum(u8) {
+ A = 1,
+ AAAA = 28,
+ CNAME = 5,
+ MX = 15,
+ NS = 2,
+ PTR = 12,
+ SOA = 6,
+ SRV = 33,
+ TXT = 16,
+
+ pub const default = RecordType.A;
+
+ pub const map = bun.ComptimeStringMap(RecordType, .{
+ .{ "A", .A },
+ .{ "AAAA", .AAAA },
+ .{ "CNAME", .CNAME },
+ .{ "MX", .MX },
+ .{ "NS", .NS },
+ .{ "PTR", .PTR },
+ .{ "SOA", .SOA },
+ .{ "SRV", .SRV },
+ .{ "TXT", .TXT },
+ .{ "a", .A },
+ .{ "aaaa", .AAAA },
+ .{ "cname", .CNAME },
+ .{ "mx", .MX },
+ .{ "ns", .NS },
+ .{ "ptr", .PTR },
+ .{ "soa", .SOA },
+ .{ "srv", .SRV },
+ .{ "txt", .TXT },
+ });
+ };
+
+ pub fn resolve(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
+ const arguments = callframe.arguments(3);
+ if (arguments.len < 1) {
+ globalThis.throwNotEnoughArguments("resolve", 2, arguments.len);
+ return .zero;
+ }
+
+ const record_type: RecordType = if (arguments.len == 1)
+ RecordType.default
+ else brk: {
+ const record_type_value = arguments.ptr[1];
+ if (record_type_value.isEmptyOrUndefinedOrNull() or !record_type_value.isString()) {
+ break :brk RecordType.default;
+ }
+
+ const record_type_str = record_type_value.toStringOrNull(globalThis) orelse {
+ return .zero;
+ };
+
+ if (record_type_str.length() == 0) {
+ break :brk RecordType.default;
+ }
+
+ break :brk RecordType.map.getWithEql(record_type_str.getZigString(globalThis), JSC.ZigString.eqlComptime) orelse {
+ globalThis.throwInvalidArgumentType("resolve", "record", "one of: A, AAAA, CNAME, MX, NS, PTR, SOA, SRV, TXT");
+ return .zero;
+ };
+ };
+ _ = record_type;
+
+ const name_value = arguments.ptr[0];
+
+ if (name_value.isEmptyOrUndefinedOrNull() or !name_value.isString()) {
+ globalThis.throwInvalidArgumentType("resolve", "name", "string");
+ return .zero;
+ }
+
+ const name_str = name_value.toStringOrNull(globalThis) orelse {
+ return .zero;
+ };
+
+ if (name_str.length() == 0) {
+ globalThis.throwInvalidArgumentType("resolve", "name", "non-empty string");
+ return .zero;
+ }
+
+ // const name = name_str.toSliceZ(globalThis).cloneZ(bun.default_allocator) catch unreachable;
+ // TODO:
+ return JSC.JSValue.jsUndefined();
+ }
+ // pub fn reverse(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
+ // const arguments = callframe.arguments(3);
+
+ // }
+
+ pub fn lookup(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
+ const arguments = callframe.arguments(2);
+ if (arguments.len < 1) {
+ globalThis.throwNotEnoughArguments("lookup", 2, arguments.len);
+ return .zero;
+ }
+
+ const name_value = arguments.ptr[0];
+
+ if (name_value.isEmptyOrUndefinedOrNull() or !name_value.isString()) {
+ globalThis.throwInvalidArgumentType("lookup", "hostname", "string");
+ return .zero;
+ }
+
+ const name_str = name_value.toStringOrNull(globalThis) orelse {
+ return .zero;
+ };
+
+ if (name_str.length() == 0) {
+ globalThis.throwInvalidArgumentType("lookup", "hostname", "non-empty string");
+ return .zero;
+ }
+
+ var options = GetAddrInfo.Options{};
+ var port: u16 = 0;
+
+ if (arguments.len > 1 and arguments.ptr[1].isCell()) {
+ if (arguments.ptr[1].get(globalThis, "port")) |port_value| {
+ if (port_value.isNumber()) {
+ port = port_value.to(u16);
+ }
+ }
+
+ options = GetAddrInfo.Options.fromJS(arguments.ptr[1], globalThis) catch |err| {
+ globalThis.throw("Invalid options passed to lookup(): {s}", .{@errorName(err)});
+ return .zero;
+ };
+ }
+
+ const name = name_str.toSlice(globalThis, bun.default_allocator);
+ defer name.deinit();
+ var vm = globalThis.bunVM();
+ var resolver = vm.rareData().globalDNSResolver(vm);
+
+ return resolver.doLookup(name.slice(), port, options, globalThis);
+ }
+
+ pub fn doLookup(this: *DNSResolver, name: []const u8, port: u16, options: GetAddrInfo.Options, globalThis: *JSC.JSGlobalObject) JSC.JSValue {
+ var opts = options;
+ var backend = opts.backend;
+ const normalized = normalizeDNSName(name, &backend);
+ opts.backend = backend;
+ const query = GetAddrInfo{
+ .options = opts,
+ .port = port,
+ .name = normalized,
+ };
+ return switch (opts.backend) {
+ .c_ares => this.c_aresLookupWithNormalizedName(query, globalThis),
+ .libc => LibC.lookup(this, query, globalThis),
+ .system => if (comptime Environment.isMac)
+ LibInfo.lookup(this, query, globalThis)
+ else
+ LibC.lookup(this, query, globalThis),
+ };
+ }
+
+ pub fn c_aresLookupWithNormalizedName(this: *DNSResolver, query: GetAddrInfo, globalThis: *JSC.JSGlobalObject) JSC.JSValue {
+ var channel: *c_ares.Channel = switch (this.getChannel()) {
+ .result => |res| res,
+ .err => |err| {
+ const system_error = JSC.SystemError{
+ .errno = -1,
+ .code = JSC.ZigString.init(err.code()),
+ .message = JSC.ZigString.init(err.label()),
+ };
+
+ globalThis.throwValue(system_error.toErrorInstance(globalThis));
+ return .zero;
+ },
+ };
+
+ const key = GetAddrInfoRequest.PendingCacheKey.init(query);
+
+ var cache = this.getOrPutIntoPendingCache(key, .pending_host_cache_cares);
+ if (cache == .inflight) {
+ var dns_lookup = DNSLookup.init(globalThis, globalThis.allocator()) catch unreachable;
+ cache.inflight.append(dns_lookup);
+ return dns_lookup.promise.value();
+ }
+
+ // var hints_buf = &[_]c_ares.AddrInfo_hints{query.toCAres()};
+ var request = GetAddrInfoRequest.init(
+ cache,
+ .{
+ .c_ares = {},
+ },
+ this,
+ query,
+ globalThis,
+ "pending_host_cache_cares",
+ ) catch unreachable;
+ const promise = request.tail.promise.value();
+
+ channel.getAddrInfo(
+ query.name,
+ query.port,
+ &.{},
+ GetAddrInfoRequest,
+ request,
+ GetAddrInfoRequest.onCaresComplete,
+ );
+
+ return promise;
+ }
+
+ comptime {
+ @export(
+ lookup,
+ .{
+ .name = "Bun__DNSResolver__lookup",
+ },
+ );
+ }
+ // pub fn lookupService(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
+ // const arguments = callframe.arguments(3);
+
+ // }
+ // pub fn cancel(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
+ // const arguments = callframe.arguments(3);
+
+ // }
+};
diff --git a/src/bun.js/base.zig b/src/bun.js/base.zig
index 671a4c197..333ea36c9 100644
--- a/src/bun.js/base.zig
+++ b/src/bun.js/base.zig
@@ -3260,6 +3260,8 @@ pub const FilePoll = struct {
const Subprocess = JSC.Subprocess;
const BufferedInput = Subprocess.BufferedInput;
const BufferedOutput = Subprocess.BufferedOutput;
+ const DNSResolver = JSC.DNS.DNSResolver;
+ const GetAddrInfoRequest = JSC.DNS.GetAddrInfoRequest;
const Deactivated = opaque {
pub var owner: Owner = Owner.init(@intToPtr(*Deactivated, @as(usize, 0xDEADBEEF)));
};
@@ -3271,6 +3273,8 @@ pub const FilePoll = struct {
BufferedInput,
FIFO,
Deactivated,
+ DNSResolver,
+ GetAddrInfoRequest,
});
fn updateFlags(poll: *FilePoll, updated: Flags.Set) void {
@@ -3278,6 +3282,7 @@ pub const FilePoll = struct {
flags.remove(.readable);
flags.remove(.writable);
flags.remove(.process);
+ flags.remove(.machport);
flags.remove(.eof);
flags.remove(.hup);
@@ -3331,19 +3336,24 @@ pub const FilePoll = struct {
this.deinitWithVM(vm);
}
- pub fn deinitWithVM(this: *FilePoll, vm: *JSC.VirtualMachine) void {
+ pub fn deinitWithoutVM(this: *FilePoll, loop: *uws.Loop, polls: *JSC.FilePoll.HiveArray) void {
if (this.isRegistered()) {
- _ = this.unregister(vm.uws_event_loop.?);
+ _ = this.unregister(loop);
}
this.owner = Deactivated.owner;
this.flags = Flags.Set{};
this.fd = invalid_fd;
- vm.rareData().filePolls(vm).put(this);
+ polls.put(this);
+ }
+
+ pub fn deinitWithVM(this: *FilePoll, vm: *JSC.VirtualMachine) void {
+ var loop = vm.uws_event_loop.?;
+ this.deinitWithoutVM(loop, vm.rareData().filePolls(vm));
}
pub fn isRegistered(this: *const FilePoll) bool {
- return this.flags.contains(.poll_writable) or this.flags.contains(.poll_readable) or this.flags.contains(.poll_process);
+ return this.flags.contains(.poll_writable) or this.flags.contains(.poll_readable) or this.flags.contains(.poll_process) or this.flags.contains(.poll_machport);
}
const kqueue_or_epoll = if (Environment.isMac) "kevent" else "epoll";
@@ -3371,6 +3381,18 @@ pub const FilePoll = struct {
loader.onPoll(size_or_offset, 0);
},
+ @field(Owner.Tag, "DNSResolver") => {
+ log("onUpdate " ++ kqueue_or_epoll ++ " (fd: {d}) DNSResolver", .{poll.fd});
+ var loader: *DNSResolver = ptr.as(DNSResolver);
+ loader.onDNSPoll(poll);
+ },
+
+ @field(Owner.Tag, "GetAddrInfoRequest") => {
+ log("onUpdate " ++ kqueue_or_epoll ++ " (fd: {d}) GetAddrInfoRequest", .{poll.fd});
+ var loader: *GetAddrInfoRequest = ptr.as(GetAddrInfoRequest);
+ loader.onMachportChange();
+ },
+
else => {
log("onUpdate " ++ kqueue_or_epoll ++ " (fd: {d}) disconnected?", .{poll.fd});
},
@@ -3389,12 +3411,16 @@ pub const FilePoll = struct {
/// Poll for process-related events
poll_process,
+ /// Poll for machport events
+ poll_machport,
+
// What did the event loop tell us?
readable,
writable,
process,
eof,
hup,
+ machport,
// What is the type of file descriptor?
fifo,
@@ -3414,6 +3440,7 @@ pub const FilePoll = struct {
.readable => .poll_readable,
.writable => .poll_writable,
.process => .poll_process,
+ .machport => .poll_machport,
else => this,
};
}
@@ -3440,6 +3467,9 @@ pub const FilePoll = struct {
} else if (kqueue_event.filter == std.os.system.EVFILT_PROC) {
log("proc", .{});
flags.insert(Flags.process);
+ } else if (kqueue_event.filter == std.os.system.EVFILT_MACHPORT) {
+ log("machport", .{});
+ flags.insert(Flags.machport);
}
return flags;
}
@@ -3590,18 +3620,21 @@ pub const FilePoll = struct {
const timeout = std.mem.zeroes(std.os.timespec);
const kevent = std.c.kevent;
const linux = std.os.linux;
+
pub fn register(this: *FilePoll, loop: *uws.Loop, flag: Flags, one_shot: bool) JSC.Maybe(void) {
+ return registerWithFd(this, loop, flag, one_shot, this.fd);
+ }
+ pub fn registerWithFd(this: *FilePoll, loop: *uws.Loop, flag: Flags, one_shot: bool, fd: u64) JSC.Maybe(void) {
const watcher_fd = loop.fd;
- const fd = this.fd;
log("register: {s} ({d})", .{ @tagName(flag), fd });
+ std.debug.assert(fd != invalid_fd);
+
if (one_shot) {
this.flags.insert(.one_shot);
}
- std.debug.assert(this.fd != invalid_fd);
-
if (comptime Environment.isLinux) {
const one_shot_flag: u32 = if (!this.flags.contains(.one_shot)) 0 else linux.EPOLL.ONESHOT;
@@ -3656,6 +3689,15 @@ pub const FilePoll = struct {
.flags = std.c.EV_ADD | one_shot_flag,
.ext = .{ this.generation_number, 0 },
},
+ .machport => .{
+ .ident = @intCast(u64, fd),
+ .filter = std.os.system.EVFILT_MACHPORT,
+ .data = 0,
+ .fflags = 0,
+ .udata = @ptrToInt(Pollable.init(this).ptr()),
+ .flags = std.c.EV_ADD | one_shot_flag,
+ .ext = .{ this.generation_number, 0 },
+ },
else => unreachable,
};
@@ -3711,6 +3753,7 @@ pub const FilePoll = struct {
.readable => .poll_readable,
.process => if (comptime Environment.isLinux) .poll_readable else .poll_process,
.writable => .poll_writable,
+ .machport => .poll_machport,
else => unreachable,
});
this.flags.remove(.needs_rearm);
@@ -3721,12 +3764,15 @@ pub const FilePoll = struct {
const invalid_fd = bun.invalid_fd;
pub fn unregister(this: *FilePoll, loop: *uws.Loop) JSC.Maybe(void) {
- if (!(this.flags.contains(.poll_readable) or this.flags.contains(.poll_writable) or this.flags.contains(.poll_process))) {
+ return this.unregisterWithFd(loop, this.fd);
+ }
+
+ pub fn unregisterWithFd(this: *FilePoll, loop: *uws.Loop, fd: u64) JSC.Maybe(void) {
+ if (!(this.flags.contains(.poll_readable) or this.flags.contains(.poll_writable) or this.flags.contains(.poll_process) or this.flags.contains(.poll_machport))) {
// no-op
return JSC.Maybe(void).success;
}
- const fd = this.fd;
std.debug.assert(fd != invalid_fd);
const watcher_fd = loop.fd;
const flag: Flags = brk: {
@@ -3736,6 +3782,9 @@ pub const FilePoll = struct {
break :brk .writable;
if (this.flags.contains(.poll_process))
break :brk .process;
+
+ if (this.flags.contains(.poll_machport))
+ break :brk .machport;
return JSC.Maybe(void).success;
};
@@ -3744,6 +3793,7 @@ pub const FilePoll = struct {
this.flags.remove(.poll_process);
this.flags.remove(.poll_readable);
this.flags.remove(.poll_process);
+ this.flags.remove(.poll_machport);
return JSC.Maybe(void).success;
}
@@ -3773,6 +3823,15 @@ pub const FilePoll = struct {
.flags = std.c.EV_DELETE,
.ext = .{ 0, 0 },
},
+ .machport => .{
+ .ident = @intCast(u64, fd),
+ .filter = std.os.system.EVFILT_MACHPORT,
+ .data = 0,
+ .fflags = 0,
+ .udata = @ptrToInt(Pollable.init(this).ptr()),
+ .flags = std.c.EV_DELETE,
+ .ext = .{ 0, 0 },
+ },
.writable => .{
.ident = @intCast(u64, fd),
.filter = std.os.system.EVFILT_WRITE,
@@ -3836,6 +3895,7 @@ pub const FilePoll = struct {
this.flags.remove(.poll_readable);
this.flags.remove(.poll_writable);
this.flags.remove(.poll_process);
+ this.flags.remove(.poll_machport);
if (this.isActive())
this.deactivate(loop);
diff --git a/src/bun.js/bindings/ZigGeneratedCode.cpp b/src/bun.js/bindings/ZigGeneratedCode.cpp
index 7b8807255..d7b48aa04 100644
--- a/src/bun.js/bindings/ZigGeneratedCode.cpp
+++ b/src/bun.js/bindings/ZigGeneratedCode.cpp
@@ -36,7 +36,7 @@ extern "C" void FFI__ptr__put(JSC::JSGlobalObject *globalObject, JSC::EncodedJSV
static const JSC::DOMJIT::Signature DOMJIT_ptr_signature(
FFI__ptr__fastpathWrapper,
thisObject->classInfo(),
- JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
+JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
JSC::SpecDoubleReal,
JSC::SpecUint8Array
);
@@ -78,7 +78,7 @@ extern "C" void Reader__u8__put(JSC::JSGlobalObject *globalObject, JSC::EncodedJ
static const JSC::DOMJIT::Signature DOMJIT_u8_signature(
Reader__u8__fastpathWrapper,
thisObject->classInfo(),
- JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
+JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
JSC::SpecInt32Only,
JSC::SpecInt52Any,
JSC::SpecInt32Only
@@ -121,7 +121,7 @@ extern "C" void Reader__u16__put(JSC::JSGlobalObject *globalObject, JSC::Encoded
static const JSC::DOMJIT::Signature DOMJIT_u16_signature(
Reader__u16__fastpathWrapper,
thisObject->classInfo(),
- JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
+JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
JSC::SpecInt32Only,
JSC::SpecInt52Any,
JSC::SpecInt32Only
@@ -164,7 +164,7 @@ extern "C" void Reader__u32__put(JSC::JSGlobalObject *globalObject, JSC::Encoded
static const JSC::DOMJIT::Signature DOMJIT_u32_signature(
Reader__u32__fastpathWrapper,
thisObject->classInfo(),
- JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
+JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
JSC::SpecInt32Only,
JSC::SpecInt52Any,
JSC::SpecInt32Only
@@ -207,7 +207,7 @@ extern "C" void Reader__ptr__put(JSC::JSGlobalObject *globalObject, JSC::Encoded
static const JSC::DOMJIT::Signature DOMJIT_ptr_signature(
Reader__ptr__fastpathWrapper,
thisObject->classInfo(),
- JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
+JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
JSC::SpecInt52Any,
JSC::SpecInt52Any,
JSC::SpecInt32Only
@@ -250,7 +250,7 @@ extern "C" void Reader__i8__put(JSC::JSGlobalObject *globalObject, JSC::EncodedJ
static const JSC::DOMJIT::Signature DOMJIT_i8_signature(
Reader__i8__fastpathWrapper,
thisObject->classInfo(),
- JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
+JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
JSC::SpecInt32Only,
JSC::SpecInt52Any,
JSC::SpecInt32Only
@@ -293,7 +293,7 @@ extern "C" void Reader__i16__put(JSC::JSGlobalObject *globalObject, JSC::Encoded
static const JSC::DOMJIT::Signature DOMJIT_i16_signature(
Reader__i16__fastpathWrapper,
thisObject->classInfo(),
- JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
+JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
JSC::SpecInt32Only,
JSC::SpecInt52Any,
JSC::SpecInt32Only
@@ -336,7 +336,7 @@ extern "C" void Reader__i32__put(JSC::JSGlobalObject *globalObject, JSC::Encoded
static const JSC::DOMJIT::Signature DOMJIT_i32_signature(
Reader__i32__fastpathWrapper,
thisObject->classInfo(),
- JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
+JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
JSC::SpecInt32Only,
JSC::SpecInt52Any,
JSC::SpecInt32Only
@@ -379,7 +379,7 @@ extern "C" void Reader__i64__put(JSC::JSGlobalObject *globalObject, JSC::Encoded
static const JSC::DOMJIT::Signature DOMJIT_i64_signature(
Reader__i64__fastpathWrapper,
thisObject->classInfo(),
- JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
+JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
JSC::SpecHeapTop,
JSC::SpecInt52Any,
JSC::SpecInt32Only
@@ -422,7 +422,7 @@ extern "C" void Reader__u64__put(JSC::JSGlobalObject *globalObject, JSC::Encoded
static const JSC::DOMJIT::Signature DOMJIT_u64_signature(
Reader__u64__fastpathWrapper,
thisObject->classInfo(),
- JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
+JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
JSC::SpecHeapTop,
JSC::SpecInt52Any,
JSC::SpecInt32Only
@@ -465,7 +465,7 @@ extern "C" void Reader__intptr__put(JSC::JSGlobalObject *globalObject, JSC::Enco
static const JSC::DOMJIT::Signature DOMJIT_intptr_signature(
Reader__intptr__fastpathWrapper,
thisObject->classInfo(),
- JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
+JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
JSC::SpecInt52Any,
JSC::SpecInt52Any,
JSC::SpecInt32Only
@@ -508,7 +508,7 @@ extern "C" void Reader__f32__put(JSC::JSGlobalObject *globalObject, JSC::Encoded
static const JSC::DOMJIT::Signature DOMJIT_f32_signature(
Reader__f32__fastpathWrapper,
thisObject->classInfo(),
- JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
+JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
JSC::SpecDoubleReal,
JSC::SpecInt52Any,
JSC::SpecInt32Only
@@ -551,7 +551,7 @@ extern "C" void Reader__f64__put(JSC::JSGlobalObject *globalObject, JSC::Encoded
static const JSC::DOMJIT::Signature DOMJIT_f64_signature(
Reader__f64__fastpathWrapper,
thisObject->classInfo(),
- JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
+JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
JSC::SpecDoubleReal,
JSC::SpecInt52Any,
JSC::SpecInt32Only
@@ -594,7 +594,7 @@ extern "C" void Crypto__getRandomValues__put(JSC::JSGlobalObject *globalObject,
static const JSC::DOMJIT::Signature DOMJIT_getRandomValues_signature(
Crypto__getRandomValues__fastpathWrapper,
thisObject->classInfo(),
- JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
+JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
JSC::SpecHeapTop,
JSC::SpecUint8Array
);
@@ -636,7 +636,7 @@ extern "C" void Crypto__randomUUID__put(JSC::JSGlobalObject *globalObject, JSC::
static const JSC::DOMJIT::Signature DOMJIT_randomUUID_signature(
Crypto__randomUUID__fastpathWrapper,
thisObject->classInfo(),
- JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
+JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
JSC::SpecString);
JSFunction* function = JSFunction::create(
globalObject->vm(),
@@ -676,7 +676,7 @@ extern "C" void Crypto__timingSafeEqual__put(JSC::JSGlobalObject *globalObject,
static const JSC::DOMJIT::Signature DOMJIT_timingSafeEqual_signature(
Crypto__timingSafeEqual__fastpathWrapper,
thisObject->classInfo(),
- JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
+JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()),
JSC::SpecHeapTop,
JSC::SpecUint8Array,
JSC::SpecUint8Array
diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp
index 1a9a1dc1b..12401d8d1 100644
--- a/src/bun.js/bindings/ZigGlobalObject.cpp
+++ b/src/bun.js/bindings/ZigGlobalObject.cpp
@@ -2168,6 +2168,8 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionPerformMicrotask, (JSGlobalObject * globalObj
return JSValue::encode(jsUndefined());
}
+extern "C" EncodedJSValue Bun__DNSResolver__lookup(JSGlobalObject*, JSC::CallFrame*);
+
JSC_DEFINE_HOST_FUNCTION(jsFunctionPerformMicrotaskVariadic, (JSGlobalObject * globalObject, CallFrame* callframe))
{
auto& vm = globalObject->vm();
@@ -3304,6 +3306,14 @@ void GlobalObject::installAPIGlobals(JSClassRef* globals, int count, JSC::VM& vm
}
{
+ JSC::Identifier identifier = JSC::Identifier::fromString(vm, "dns"_s);
+ JSC::JSObject* dnsObject = JSC::constructEmptyObject(this);
+ dnsObject->putDirectNativeFunction(vm, this, JSC::Identifier::fromString(vm, "lookup"_s), 2, Bun__DNSResolver__lookup, ImplementationVisibility::Public, NoIntrinsic,
+ JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0);
+ object->putDirect(vm, PropertyName(identifier), dnsObject, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0);
+ }
+
+ {
JSC::Identifier identifier = JSC::Identifier::fromString(vm, "plugin"_s);
JSFunction* pluginFunction = JSFunction::create(vm, this, 1, String("plugin"_s), jsFunctionBunPlugin, ImplementationVisibility::Public, NoIntrinsic);
pluginFunction->putDirectNativeFunction(vm, this, JSC::Identifier::fromString(vm, "clearAll"_s), 1, jsFunctionBunPluginClear, ImplementationVisibility::Public, NoIntrinsic,
diff --git a/src/bun.js/bindings/ares_build.h b/src/bun.js/bindings/ares_build.h
new file mode 100644
index 000000000..2c4927ba5
--- /dev/null
+++ b/src/bun.js/bindings/ares_build.h
@@ -0,0 +1,42 @@
+#ifndef __CARES_BUILD_H
+#define __CARES_BUILD_H
+
+#define CARES_TYPEOF_ARES_SOCKLEN_T socklen_t
+#define CARES_TYPEOF_ARES_SSIZE_T ssize_t
+
+/* Prefix names with CARES_ to make sure they don't conflict with other config.h
+ * files. We need to include some dependent headers that may be system specific
+ * for C-Ares */
+#define CARES_HAVE_SYS_TYPES_H
+#define CARES_HAVE_SYS_SOCKET_H
+/* #undef CARES_HAVE_WINDOWS_H */
+/* #undef CARES_HAVE_WS2TCPIP_H */
+/* #undef CARES_HAVE_WINSOCK2_H */
+/* #undef CARES_HAVE_WINDOWS_H */
+#define CARES_HAVE_ARPA_NAMESER_H
+#define CARES_HAVE_ARPA_NAMESER_COMPAT_H
+
+#ifdef CARES_HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef CARES_HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef CARES_HAVE_WINSOCK2_H
+#include <winsock2.h>
+#endif
+
+#ifdef CARES_HAVE_WS2TCPIP_H
+#include <ws2tcpip.h>
+#endif
+
+#ifdef CARES_HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+typedef CARES_TYPEOF_ARES_SOCKLEN_T ares_socklen_t;
+typedef CARES_TYPEOF_ARES_SSIZE_T ares_ssize_t;
+
+#endif /* __CARES_BUILD_H */
diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp
index f73a1b6e0..5f3dc3dac 100644
--- a/src/bun.js/bindings/bindings.cpp
+++ b/src/bun.js/bindings/bindings.cpp
@@ -1982,6 +1982,50 @@ void JSC__JSPromise__resolve(JSC__JSPromise* arg0, JSC__JSGlobalObject* arg1,
{
arg0->resolve(arg1, JSC::JSValue::decode(JSValue2));
}
+
+// This implementation closely mimicks the one in JSC::JSPromise::resolve
+void JSC__JSPromise__resolveOnNextTick(JSC__JSPromise* promise, JSC__JSGlobalObject* lexicalGlobalObject,
+ JSC__JSValue encoedValue)
+{
+ JSC::JSValue value = JSC::JSValue::decode(encoedValue);
+ VM& vm = lexicalGlobalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ uint32_t flags = promise->internalField(JSC::JSPromise::Field::Flags).get().asUInt32();
+ if (!(flags & JSC::JSPromise::isFirstResolvingFunctionCalledFlag)) {
+ promise->internalField(JSC::JSPromise::Field::Flags).set(vm, promise, jsNumber(flags | JSC::JSPromise::isFirstResolvingFunctionCalledFlag));
+ auto* globalObject = jsCast<Zig::GlobalObject*>(promise->globalObject());
+
+ globalObject->queueMicrotask(
+ globalObject->performMicrotaskFunction(),
+ globalObject->resolvePromiseFunction(),
+ promise,
+ value,
+ JSValue {});
+ RETURN_IF_EXCEPTION(scope, void());
+ }
+}
+
+// This implementation closely mimicks the one in JSC::JSPromise::reject
+void JSC__JSPromise__rejectOnNextTick(JSC__JSPromise* promise, JSC__JSGlobalObject* lexicalGlobalObject,
+ JSC__JSValue encoedValue)
+{
+ JSC::JSValue value = JSC::JSValue::decode(encoedValue);
+ VM& vm = lexicalGlobalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ uint32_t flags = promise->internalField(JSC::JSPromise::Field::Flags).get().asUInt32();
+ if (!(flags & JSC::JSPromise::isFirstResolvingFunctionCalledFlag)) {
+ promise->internalField(JSC::JSPromise::Field::Flags).set(vm, promise, jsNumber(flags | JSC::JSPromise::isFirstResolvingFunctionCalledFlag));
+ auto* globalObject = jsCast<Zig::GlobalObject*>(promise->globalObject());
+
+ globalObject->queueMicrotask(
+ globalObject->performMicrotaskFunction(),
+ globalObject->rejectPromiseFunction(),
+ promise,
+ value,
+ JSValue {});
+ RETURN_IF_EXCEPTION(scope, void());
+ }
+}
JSC__JSPromise* JSC__JSPromise__resolvedPromise(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1)
{
Zig::GlobalObject* global = reinterpret_cast<Zig::GlobalObject*>(arg0);
@@ -3574,4 +3618,16 @@ extern "C" size_t JSC__VM__externalMemorySize(JSC__VM* vm)
#else
return 0;
#endif
+}
+
+extern "C" void JSC__JSGlobalObject__queueMicrotaskJob(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue JSValue2, JSC__JSValue JSValue3, JSC__JSValue JSValue4)
+{
+ Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(arg0);
+ JSC::VM& vm = globalObject->vm();
+ globalObject->queueMicrotask(
+ JSValue(globalObject->performMicrotaskFunction()),
+ JSC::JSValue::decode(JSValue1),
+ JSC::JSValue::decode(JSValue2),
+ JSC::JSValue::decode(JSValue3),
+ JSC::JSValue::decode(JSValue4));
} \ No newline at end of file
diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig
index 4da47ec70..f86faf7fd 100644
--- a/src/bun.js/bindings/bindings.zig
+++ b/src/bun.js/bindings/bindings.zig
@@ -1464,10 +1464,18 @@ pub const JSPromise = extern struct {
this.swap().reject(globalThis, val);
}
+ pub fn rejectOnNextTick(this: *Strong, globalThis: *JSC.JSGlobalObject, val: JSC.JSValue) void {
+ this.swap().rejectOnNextTick(globalThis, val);
+ }
+
pub fn resolve(this: *Strong, globalThis: *JSC.JSGlobalObject, val: JSC.JSValue) void {
this.swap().resolve(globalThis, val);
}
+ pub fn resolveOnNextTick(this: *Strong, globalThis: *JSC.JSGlobalObject, val: JSC.JSValue) void {
+ this.swap().resolveOnNextTick(globalThis, val);
+ }
+
pub fn init(globalThis: *JSC.JSGlobalObject) Strong {
return Strong{
.strong = JSC.Strong.create(
@@ -1530,6 +1538,14 @@ pub const JSPromise = extern struct {
return cppFn("resolvedPromise", .{ globalThis, value });
}
+ pub fn resolveOnNextTick(promise: *JSC.JSPromise, globalThis: *JSGlobalObject, value: JSC.JSValue) void {
+ return cppFn("resolveOnNextTick", .{ promise, globalThis, value });
+ }
+
+ pub fn rejectOnNextTick(promise: *JSC.JSPromise, globalThis: *JSGlobalObject, value: JSC.JSValue) void {
+ return cppFn("rejectOnNextTick", .{ promise, globalThis, value });
+ }
+
/// Create a new promise with an already fulfilled value
/// This is the faster function for doing that.
pub fn resolvedPromiseValue(globalThis: *JSGlobalObject, value: JSValue) JSValue {
@@ -1572,21 +1588,23 @@ pub const JSPromise = extern struct {
}
pub const Extern = [_][]const u8{
- "rejectWithCaughtException",
- "status",
- "result",
+ "asValue",
+ "create",
"isHandled",
- "resolvedPromise",
- "rejectedPromise",
- "resolve",
"reject",
"rejectAsHandled",
- // "rejectException",
"rejectAsHandledException",
+ "rejectOnNextTick",
+ "rejectWithCaughtException",
+ "rejectedPromise",
"rejectedPromiseValue",
+ "resolve",
+ "resolveOnNextTick",
+ "resolvedPromise",
"resolvedPromiseValue",
- "asValue",
- "create",
+ "result",
+ "status",
+ // "rejectException",
};
};
@@ -2065,6 +2083,35 @@ pub const JSGlobalObject = extern struct {
this.vm().throwError(this, this.createErrorInstance(fmt, args));
}
+ pub fn queueMicrotask(
+ this: *JSGlobalObject,
+ function: JSValue,
+ args: []JSC.JSValue,
+ ) void {
+ this.queueMicrotaskJob(
+ function,
+ if (args.len > 0) args[0] else .zero,
+ if (args.len > 1) args[1] else .zero,
+ if (args.len > 2) args[2] else .zero,
+ );
+ }
+
+ pub fn queueMicrotaskJob(
+ this: *JSGlobalObject,
+ function: JSValue,
+ first: JSValue,
+ second: JSValue,
+ third: JSValue,
+ ) void {
+ shim.cppFn("queueMicrotaskJob", .{
+ this,
+ function,
+ first,
+ second,
+ third,
+ });
+ }
+
pub fn throwValue(
this: *JSGlobalObject,
value: JSC.JSValue,
@@ -2211,6 +2258,7 @@ pub const JSGlobalObject = extern struct {
"startRemoteInspector",
"handleRejectedPromises",
"createSyntheticModule_",
+ "queueMicrotaskJob",
// "createError",
// "throwError",
};
diff --git a/src/bun.js/bindings/glibc-versions-hack.cpp b/src/bun.js/bindings/glibc-versions-hack.cpp
index 1be279202..33d94c809 100644
--- a/src/bun.js/bindings/glibc-versions-hack.cpp
+++ b/src/bun.js/bindings/glibc-versions-hack.cpp
@@ -2,7 +2,7 @@
#if defined(__linux__)
#include <fcntl.h>
-//#include <sys/stat.h>
+// #include <sys/stat.h>
#include <stdarg.h>
#include <math.h>
#include <errno.h>
@@ -181,4 +181,4 @@ extern "C" int __wrap_mknodat(int dirfd, const char* path, __mode_t mode, __dev_
return __xmknodat(_MKNOD_VER, dirfd, path, mode, dev);
}
-#endif \ No newline at end of file
+#endif
diff --git a/src/bun.js/bindings/headers-cpp.h b/src/bun.js/bindings/headers-cpp.h
index 4ed3115af..0079b0ab7 100644
--- a/src/bun.js/bindings/headers-cpp.h
+++ b/src/bun.js/bindings/headers-cpp.h
@@ -1,4 +1,4 @@
-//-- AUTOGENERATED FILE -- 1672499809
+//-- AUTOGENERATED FILE -- 1673090883
// clang-format off
#pragma once
diff --git a/src/bun.js/bindings/headers.h b/src/bun.js/bindings/headers.h
index 0a1968637..728c81afb 100644
--- a/src/bun.js/bindings/headers.h
+++ b/src/bun.js/bindings/headers.h
@@ -1,5 +1,5 @@
// clang-format off
-//-- AUTOGENERATED FILE -- 1672499809
+//-- AUTOGENERATED FILE -- 1673090883
#pragma once
#include <stddef.h>
@@ -200,10 +200,12 @@ CPP_DECL void JSC__JSPromise__rejectAsHandled(JSC__JSPromise* arg0, JSC__JSGloba
CPP_DECL void JSC__JSPromise__rejectAsHandledException(JSC__JSPromise* arg0, JSC__JSGlobalObject* arg1, JSC__Exception* arg2);
CPP_DECL JSC__JSPromise* JSC__JSPromise__rejectedPromise(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1);
CPP_DECL JSC__JSValue JSC__JSPromise__rejectedPromiseValue(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1);
+CPP_DECL void JSC__JSPromise__rejectOnNextTick(JSC__JSPromise* arg0, JSC__JSGlobalObject* arg1, JSC__JSValue JSValue2);
CPP_DECL void JSC__JSPromise__rejectWithCaughtException(JSC__JSPromise* arg0, JSC__JSGlobalObject* arg1, bJSC__ThrowScope arg2);
CPP_DECL void JSC__JSPromise__resolve(JSC__JSPromise* arg0, JSC__JSGlobalObject* arg1, JSC__JSValue JSValue2);
CPP_DECL JSC__JSPromise* JSC__JSPromise__resolvedPromise(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1);
CPP_DECL JSC__JSValue JSC__JSPromise__resolvedPromiseValue(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1);
+CPP_DECL void JSC__JSPromise__resolveOnNextTick(JSC__JSPromise* arg0, JSC__JSGlobalObject* arg1, JSC__JSValue JSValue2);
CPP_DECL JSC__JSValue JSC__JSPromise__result(JSC__JSPromise* arg0, JSC__VM* arg1);
CPP_DECL uint32_t JSC__JSPromise__status(const JSC__JSPromise* arg0, JSC__VM* arg1);
@@ -235,6 +237,7 @@ CPP_DECL JSC__JSValue JSC__JSGlobalObject__generateHeapSnapshot(JSC__JSGlobalObj
CPP_DECL JSC__JSValue JSC__JSGlobalObject__getCachedObject(JSC__JSGlobalObject* arg0, const ZigString* arg1);
CPP_DECL void JSC__JSGlobalObject__handleRejectedPromises(JSC__JSGlobalObject* arg0);
CPP_DECL JSC__JSValue JSC__JSGlobalObject__putCachedObject(JSC__JSGlobalObject* arg0, const ZigString* arg1, JSC__JSValue JSValue2);
+CPP_DECL void JSC__JSGlobalObject__queueMicrotaskJob(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue JSValue2, JSC__JSValue JSValue3, JSC__JSValue JSValue4);
CPP_DECL void JSC__JSGlobalObject__reload(JSC__JSGlobalObject* arg0);
CPP_DECL bool JSC__JSGlobalObject__startRemoteInspector(JSC__JSGlobalObject* arg0, unsigned char* arg1, uint16_t arg2);
CPP_DECL JSC__VM* JSC__JSGlobalObject__vm(JSC__JSGlobalObject* arg0);
diff --git a/src/bun.js/bindings/headers.zig b/src/bun.js/bindings/headers.zig
index 408c90212..27967eb13 100644
--- a/src/bun.js/bindings/headers.zig
+++ b/src/bun.js/bindings/headers.zig
@@ -137,10 +137,12 @@ pub extern fn JSC__JSPromise__rejectAsHandled(arg0: ?*bindings.JSPromise, arg1:
pub extern fn JSC__JSPromise__rejectAsHandledException(arg0: ?*bindings.JSPromise, arg1: *bindings.JSGlobalObject, arg2: [*c]bindings.Exception) void;
pub extern fn JSC__JSPromise__rejectedPromise(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue) ?*bindings.JSPromise;
pub extern fn JSC__JSPromise__rejectedPromiseValue(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue) JSC__JSValue;
+pub extern fn JSC__JSPromise__rejectOnNextTick(arg0: ?*bindings.JSPromise, arg1: *bindings.JSGlobalObject, JSValue2: JSC__JSValue) void;
pub extern fn JSC__JSPromise__rejectWithCaughtException(arg0: ?*bindings.JSPromise, arg1: *bindings.JSGlobalObject, arg2: bJSC__ThrowScope) void;
pub extern fn JSC__JSPromise__resolve(arg0: ?*bindings.JSPromise, arg1: *bindings.JSGlobalObject, JSValue2: JSC__JSValue) void;
pub extern fn JSC__JSPromise__resolvedPromise(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue) ?*bindings.JSPromise;
pub extern fn JSC__JSPromise__resolvedPromiseValue(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue) JSC__JSValue;
+pub extern fn JSC__JSPromise__resolveOnNextTick(arg0: ?*bindings.JSPromise, arg1: *bindings.JSGlobalObject, JSValue2: JSC__JSValue) void;
pub extern fn JSC__JSPromise__result(arg0: ?*bindings.JSPromise, arg1: *bindings.VM) JSC__JSValue;
pub extern fn JSC__JSPromise__status(arg0: [*c]const JSC__JSPromise, arg1: *bindings.VM) u32;
pub extern fn JSC__JSInternalPromise__create(arg0: *bindings.JSGlobalObject) [*c]bindings.JSInternalPromise;
@@ -163,6 +165,7 @@ pub extern fn JSC__JSGlobalObject__generateHeapSnapshot(arg0: *bindings.JSGlobal
pub extern fn JSC__JSGlobalObject__getCachedObject(arg0: *bindings.JSGlobalObject, arg1: [*c]const ZigString) JSC__JSValue;
pub extern fn JSC__JSGlobalObject__handleRejectedPromises(arg0: *bindings.JSGlobalObject) void;
pub extern fn JSC__JSGlobalObject__putCachedObject(arg0: *bindings.JSGlobalObject, arg1: [*c]const ZigString, JSValue2: JSC__JSValue) JSC__JSValue;
+pub extern fn JSC__JSGlobalObject__queueMicrotaskJob(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue, JSValue2: JSC__JSValue, JSValue3: JSC__JSValue, JSValue4: JSC__JSValue) void;
pub extern fn JSC__JSGlobalObject__reload(arg0: *bindings.JSGlobalObject) void;
pub extern fn JSC__JSGlobalObject__startRemoteInspector(arg0: *bindings.JSGlobalObject, arg1: [*c]u8, arg2: u16) bool;
pub extern fn JSC__JSGlobalObject__vm(arg0: *bindings.JSGlobalObject) *bindings.VM;
diff --git a/src/bun.js/event_loop.zig b/src/bun.js/event_loop.zig
index 6331480d8..825bd7b1b 100644
--- a/src/bun.js/event_loop.zig
+++ b/src/bun.js/event_loop.zig
@@ -83,10 +83,16 @@ pub fn ConcurrentPromiseTask(comptime Context: type) type {
}
pub fn IOTask(comptime Context: type) type {
+ return WorkTask(Context, false);
+}
+
+pub fn WorkTask(comptime Context: type, comptime async_io: bool) type {
return struct {
+ const TaskType = if (async_io) NetworkThread.Task else WorkPoolTask;
+
const This = @This();
ctx: *Context,
- task: NetworkThread.Task = .{ .callback = &runFromThreadPool },
+ task: TaskType = .{ .callback = &runFromThreadPool },
event_loop: *JSC.EventLoop,
allocator: std.mem.Allocator,
globalThis: *JSGlobalObject,
@@ -108,7 +114,7 @@ pub fn IOTask(comptime Context: type) type {
return this;
}
- pub fn runFromThreadPool(task: *NetworkThread.Task) void {
+ pub fn runFromThreadPool(task: *TaskType) void {
var this = @fieldParentPtr(This, "task", task);
Context.run(this.ctx, this);
}
@@ -121,8 +127,12 @@ pub fn IOTask(comptime Context: type) type {
pub fn schedule(this: *This) void {
this.ref.ref(this.event_loop.virtual_machine);
- NetworkThread.init() catch return;
- NetworkThread.global.schedule(NetworkThread.Batch.from(&this.task));
+ if (comptime async_io) {
+ NetworkThread.init() catch return;
+ NetworkThread.global.schedule(NetworkThread.Batch.from(&this.task));
+ } else {
+ WorkPool.schedule(&this.task);
+ }
}
pub fn onFinish(this: *This) void {
@@ -175,6 +185,7 @@ const MicrotaskForDefaultGlobalObject = JSC.MicrotaskForDefaultGlobalObject;
const HotReloadTask = JSC.HotReloader.HotReloadTask;
const PollPendingModulesTask = JSC.ModuleLoader.AsyncModule.Queue;
// const PromiseTask = JSInternalPromise.Completion.PromiseTask;
+const GetAddrInfoRequestTask = JSC.DNS.GetAddrInfoRequest.Task;
pub const Task = TaggedPointerUnion(.{
FetchTasklet,
Microtask,
@@ -189,6 +200,7 @@ pub const Task = TaggedPointerUnion(.{
CppTask,
HotReloadTask,
PollPendingModulesTask,
+ GetAddrInfoRequestTask,
// PromiseTask,
// TimeoutTasklet,
});
@@ -416,6 +428,11 @@ pub const EventLoop = struct {
@field(Task.Tag, typeBaseName(@typeName(PollPendingModulesTask))) => {
this.virtual_machine.modules.onPoll();
},
+ @field(Task.Tag, typeBaseName(@typeName(GetAddrInfoRequestTask))) => {
+ var any: *GetAddrInfoRequestTask = task.get(GetAddrInfoRequestTask).?;
+ any.runFromJS();
+ any.deinit();
+ },
else => if (Environment.allow_assert) {
bun.Output.prettyln("\nUnexpected tag: {s}\n", .{@tagName(task.tag())});
} else unreachable,
diff --git a/src/bun.js/fs_promises.exports.js b/src/bun.js/fs_promises.exports.js
index 5ae756347..2ba18f3c6 100644
--- a/src/bun.js/fs_promises.exports.js
+++ b/src/bun.js/fs_promises.exports.js
@@ -8,7 +8,7 @@ function promisify(fsFunction) {
// we can use new Function() here instead
// based on fsFucntion.length
var obj = {
- [fsFunction.name]: function (resolve, reject, args) {
+ ["::bunternal::"]: function (resolve, reject, args) {
var result;
try {
result = fsFunction.apply(fs, args);
diff --git a/src/bun.js/node/syscall.zig b/src/bun.js/node/syscall.zig
index 06b007ef1..035a8738f 100644
--- a/src/bun.js/node/syscall.zig
+++ b/src/bun.js/node/syscall.zig
@@ -105,6 +105,7 @@ pub const Tag = enum(u8) {
kill,
waitpid,
posix_spawn,
+ getaddrinfo,
pub var strings = std.EnumMap(Tag, JSC.C.JSStringRef).initFull(null);
};
const PathString = @import("bun").PathString;
diff --git a/src/bun.js/rare_data.zig b/src/bun.js/rare_data.zig
index 4f760c0d6..a14b3282a 100644
--- a/src/bun.js/rare_data.zig
+++ b/src/bun.js/rare_data.zig
@@ -25,6 +25,8 @@ cleanup_hook: ?*CleanupHook = null,
file_polls_: ?*JSC.FilePoll.HiveArray = null,
+global_dns_data: ?*JSC.DNS.GlobalData = null,
+
pub fn filePolls(this: *RareData, vm: *JSC.VirtualMachine) *JSC.FilePoll.HiveArray {
return this.file_polls_ orelse {
this.file_polls_ = vm.allocator.create(JSC.FilePoll.HiveArray) catch unreachable;
@@ -228,3 +230,11 @@ pub fn stdin(rare: *RareData) *Blob.Store {
break :brk store;
};
}
+
+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);
+ }
+
+ return &rare.global_dns_data.?.resolver;
+}
diff --git a/src/bun.zig b/src/bun.zig
index 2ea39b2b9..00bceeea4 100644
--- a/src/bun.zig
+++ b/src/bun.zig
@@ -756,3 +756,12 @@ pub fn enumMap(comptime T: type, comptime args: anytype) (fn (T) []const u8) {
return Map.get;
}
+
+/// Write 0's for every byte in Type
+/// Ignores default struct values.
+pub fn zero(comptime Type: type) Type {
+ var out: [@sizeOf(Type)]u8 align(@alignOf(Type)) = undefined;
+ @memset(@ptrCast([*]u8, &out), 0, out.len);
+ return @bitCast(Type, out);
+}
+pub const c_ares = @import("./deps/c_ares.zig");
diff --git a/src/c.zig b/src/c.zig
index 6e1a8632f..54be0878c 100644
--- a/src/c.zig
+++ b/src/c.zig
@@ -393,7 +393,7 @@ const LazyStatus = enum {
failed,
};
-pub fn dlsym(comptime Type: type, comptime name: [:0]const u8) ?Type {
+pub fn dlsymWithHandle(comptime Type: type, comptime name: [:0]const u8, comptime handle_getter: fn () ?*anyopaque) ?Type {
if (comptime @typeInfo(Type) != .Pointer) {
@compileError("dlsym must be a pointer type (e.g. ?const *fn()). Received " ++ @typeName(Type) ++ ".");
}
@@ -404,11 +404,7 @@ pub fn dlsym(comptime Type: type, comptime name: [:0]const u8) ?Type {
};
if (Wrapper.loaded == .pending) {
- const RTLD_DEFAULT = if (bun.Environment.isMac)
- @intToPtr(?*anyopaque, @bitCast(usize, @as(isize, -2)))
- else
- @intToPtr(?*anyopaque, @as(usize, 0));
- const result = std.c.dlsym(RTLD_DEFAULT, name);
+ const result = std.c.dlsym(@call(.always_inline, handle_getter, .{}), name);
if (result) |ptr| {
Wrapper.function = bun.cast(Type, ptr);
@@ -427,6 +423,21 @@ pub fn dlsym(comptime Type: type, comptime name: [:0]const u8) ?Type {
return null;
}
+pub fn dlsym(comptime Type: type, comptime name: [:0]const u8) ?Type {
+ const handle_getter = struct {
+ const RTLD_DEFAULT = if (bun.Environment.isMac)
+ @intToPtr(?*anyopaque, @bitCast(usize, @as(isize, -2)))
+ else
+ @intToPtr(?*anyopaque, @as(usize, 0));
+
+ pub fn getter() ?*anyopaque {
+ return RTLD_DEFAULT;
+ }
+ }.getter;
+
+ return dlsymWithHandle(Type, name, handle_getter);
+}
+
// set in c-bindings.cpp
pub extern fn get_process_priority(pid: c_uint) i32;
pub extern fn set_process_priority(pid: c_uint, priority: c_int) i32;
diff --git a/src/deps/c-ares b/src/deps/c-ares
new file mode 160000
+Subproject 0e7a5dee0fbb04080750cf6eabbe89d8bae87fa
diff --git a/src/deps/c_ares.zig b/src/deps/c_ares.zig
new file mode 100644
index 000000000..a13b74fca
--- /dev/null
+++ b/src/deps/c_ares.zig
@@ -0,0 +1,687 @@
+const c = @import("std").c;
+const std = @import("std");
+const bun = @import("bun");
+const iovec = @import("std").os.iovec;
+const struct_in_addr = std.os.sockaddr.in;
+const struct_sockaddr = std.os.sockaddr;
+pub const socklen_t = c.socklen_t;
+const ares_socklen_t = c.socklen_t;
+pub const ares_ssize_t = isize;
+pub const ares_socket_t = c_int;
+pub const ares_sock_state_cb = ?*const fn (?*anyopaque, ares_socket_t, c_int, c_int) callconv(.C) void;
+pub const struct_apattern = opaque {};
+const fd_set = c.fd_set;
+pub const Options = extern struct {
+ flags: c_int = 0,
+ timeout: c_int = 0,
+ tries: c_int = 0,
+ ndots: c_int = 0,
+ udp_port: c_ushort = 0,
+ tcp_port: c_ushort = 0,
+ socket_send_buffer_size: c_int = 0,
+ socket_receive_buffer_size: c_int = 0,
+ servers: [*c]struct_in_addr = null,
+ nservers: c_int = 0,
+ domains: [*c][*:0]u8 = null,
+ ndomains: c_int = 0,
+ lookups: [*c]u8 = null,
+ sock_state_cb: ares_sock_state_cb = null,
+ sock_state_cb_data: ?*anyopaque = null,
+ sortlist: ?*struct_apattern = null,
+ nsort: c_int = 0,
+ ednspsz: c_int = 0,
+ resolvconf_path: ?[*:0]u8 = null,
+ hosts_path: ?[*:0]u8 = null,
+};
+pub const struct_hostent = opaque {};
+pub const struct_timeval = opaque {};
+pub const struct_Channeldata = opaque {};
+pub const AddrInfo_cname = extern struct {
+ ttl: c_int,
+ alias: [*c]u8,
+ name: [*c]u8,
+ next: [*c]AddrInfo_cname,
+};
+pub const AddrInfo_node = extern struct {
+ ttl: c_int = 0,
+ flags: c_int = 0,
+ family: c_int = 0,
+ socktype: c_int = 0,
+ protocol: c_int = 0,
+ addrlen: ares_socklen_t,
+ addr: ?*struct_sockaddr = null,
+ next: ?*AddrInfo_node = null,
+
+ pub fn count(this: *AddrInfo_node) u32 {
+ var len: u32 = 0;
+ var node: ?*AddrInfo_node = this;
+ while (node != null) : (node = node.?.next) {
+ len += 1;
+ }
+ return len;
+ }
+};
+pub const AddrInfo = extern struct {
+ cnames_: [*c]AddrInfo_cname = null,
+ node: ?*AddrInfo_node = null,
+ name_: ?[*:0]u8 = null,
+
+ const JSC = bun.JSC;
+
+ pub fn toJSArray(
+ addr_info: *AddrInfo,
+ parent_allocator: std.mem.Allocator,
+ globalThis: *JSC.JSGlobalObject,
+ ) JSC.JSValue {
+ var stack = std.heap.stackFallback(2048, parent_allocator);
+ var arena = std.heap.ArenaAllocator.init(stack.get());
+ var node = addr_info.node.?;
+ const array = JSC.JSValue.createEmptyArray(
+ globalThis,
+ node.count(),
+ );
+
+ {
+ defer arena.deinit();
+
+ var allocator = arena.allocator();
+ var j: u32 = 0;
+ var current: ?*AddrInfo_node = addr_info.node;
+ while (current) |this_node| : (current = this_node.next) {
+ array.putIndex(
+ globalThis,
+ j,
+ bun.JSC.DNS.GetAddrInfo.Result.toJS(
+ &.{
+ .address = switch (this_node.family) {
+ std.os.AF.INET => std.net.Address{ .in = .{ .sa = bun.cast(*const std.os.sockaddr.in, this_node.addr.?).* } },
+ std.os.AF.INET6 => std.net.Address{ .in6 = .{ .sa = bun.cast(*const std.os.sockaddr.in6, this_node.addr.?).* } },
+ else => unreachable,
+ },
+ .ttl = this_node.ttl,
+ },
+ globalThis,
+ allocator,
+ ),
+ );
+ j += 1;
+ }
+ }
+
+ return array;
+ }
+
+ pub inline fn name(this: *const AddrInfo) []const u8 {
+ var name_ = this.name_ orelse return "";
+ return bun.span(name_);
+ }
+
+ pub inline fn cnames(this: *const AddrInfo) []const AddrInfo_node {
+ var cnames_ = this.cnames_ orelse return &.{};
+ return bun.span(cnames_);
+ }
+
+ pub fn Callback(comptime Type: type) type {
+ return fn (*Type, status: ?Error, timeouts: i32, results: ?*AddrInfo) void;
+ }
+
+ pub fn callbackWrapper(
+ comptime Type: type,
+ comptime function: Callback(Type),
+ ) ares_addrinfo_callback {
+ return &struct {
+ pub fn handleAddrInfo(ctx: ?*anyopaque, status: c_int, timeouts: c_int, addr_info: ?*AddrInfo) callconv(.C) void {
+ var this = bun.cast(*Type, ctx.?);
+
+ function(this, Error.get(status), timeouts, addr_info);
+ }
+ }.handleAddrInfo;
+ }
+
+ pub fn deinit(this: *AddrInfo) void {
+ ares_freeaddrinfo(this);
+ }
+};
+pub const AddrInfo_hints = extern struct {
+ ai_flags: c_int = 0,
+ ai_family: c_int = 0,
+ ai_socktype: c_int = 0,
+ ai_protocol: c_int = 0,
+
+ pub fn isEmpty(this: AddrInfo_hints) bool {
+ return this.ai_flags == 0 and this.ai_family == 0 and this.ai_socktype == 0 and this.ai_protocol == 0;
+ }
+};
+pub const Channel = opaque {
+ pub fn init(comptime Container: type, this: *Container) ?Error {
+ var channel: *Channel = undefined;
+
+ libraryInit();
+
+ if (Error.get(ares_init(&channel))) |err| {
+ return err;
+ }
+ const SockStateWrap = struct {
+ pub fn onSockState(ctx: ?*anyopaque, socket: ares_socket_t, readable: c_int, writable: c_int) callconv(.C) void {
+ var container = bun.cast(*Container, ctx.?);
+ Container.onDNSSocketState(container, @intCast(i32, socket), readable != 0, writable != 0);
+ }
+ };
+
+ var opts = bun.zero(Options);
+
+ opts.flags = ARES_FLAG_NOCHECKRESP;
+ opts.sock_state_cb = &SockStateWrap.onSockState;
+ opts.sock_state_cb_data = @ptrCast(*anyopaque, this);
+ opts.timeout = 1000;
+ opts.tries = 3;
+
+ const optmask: c_int =
+ ARES_OPT_FLAGS | ARES_OPT_TIMEOUTMS |
+ ARES_OPT_SOCK_STATE_CB | ARES_OPT_TRIES;
+
+ if (Error.get(ares_init_options(&channel, &opts, optmask))) |err| {
+ ares_library_cleanup();
+ return err;
+ }
+
+ this.channel = channel;
+ return null;
+ }
+
+ ///
+ ///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:
+ ///
+ ///struct ares_addrinfo_hints { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; };
+ ///
+ ///ai_family Specifies desired address family. AF_UNSPEC means return both AF_INET and AF_INET6.
+ ///
+ ///ai_socktype Specifies desired socket type, for example SOCK_STREAM or SOCK_DGRAM. Setting this to 0 means any type.
+ ///
+ ///ai_protocol Setting this to 0 means any protocol.
+ ///
+ ///ai_flags Specifies additional options, see below.
+ ///
+ ///ARES_AI_NUMERICSERV If this option is set service field will be treated as a numeric value.
+ ///
+ ///ARES_AI_CANONNAME The ares_addrinfo structure will return a canonical names list.
+ ///
+ ///ARES_AI_NOSORT Result addresses will not be sorted and no connections to resolved addresses will be attempted.
+ ///
+ ///ARES_AI_ENVHOSTS Read hosts file path from the environment variable CARES_HOSTS .
+ ///
+ ///When the query is complete or has failed, the ares library will invoke callback. Completion or failure of the query may happen immediately, or may happen during a later call to ares_process, ares_destroy or ares_cancel.
+ ///
+ ///The callback argument arg is copied from the ares_getaddrinfo argument arg. The callback argument status indicates whether the query succeeded and, if not, how it failed. It may have any of the following values:
+ ///
+ ///ARES_SUCCESS The host lookup completed successfully.
+ ///
+ ///ARES_ENOTIMP The ares library does not know how to find addresses of type family.
+ ///
+ ///ARES_ENOTFOUND The name was not found.
+ ///
+ ///ARES_ENOMEM Memory was exhausted.
+ ///
+ ///ARES_ECANCELLED The query was cancelled.
+ ///
+ ///ARES_EDESTRUCTION The name service channel channel is being destroyed; the query will not be completed.
+ ///
+ ///On successful completion of the query, the callback argument result points to a struct ares_addrinfo which contains two linked lists, one with resolved addresses and another with canonical names. Also included is the official name of the host (analogous to gethostbyname() h_name).
+ ///
+ ///struct ares_addrinfo { struct ares_addrinfo_cname *cnames; struct ares_addrinfo_node *nodes; char *name; };
+ ///
+ ///ares_addrinfo_node structure is similar to RFC 3493 addrinfo, but without canonname and with extra ttl field.
+ ///
+ ///struct ares_addrinfo_node { int ai_ttl; int ai_flags; int ai_family; int ai_socktype; int ai_protocol; ares_socklen_t ai_addrlen; struct sockaddr *ai_addr; struct ares_addrinfo_node *ai_next; };
+ ///
+ ///ares_addrinfo_cname structure is a linked list of CNAME records where ttl is a time to live alias is a label of the resource record and name is a value (canonical name) of the resource record. See RFC 2181 10.1.1. CNAME terminology.
+ ///
+ ///struct ares_addrinfo_cname { int ttl; char *alias; char *name; struct ares_addrinfo_cname *next; };
+ ///
+ ///The reserved memory has to be deleted by ares_freeaddrinfo.
+ ///
+ ///The result is sorted according to RFC 6724 except: - Rule 3 (Avoid deprecated addresses) - Rule 4 (Prefer home addresses) - Rule 7 (Prefer native transport)
+ ///
+ ///Please note that the function will attempt a connection on each of the resolved addresses as per RFC 6724.
+ ///
+ pub fn getAddrInfo(this: *Channel, host: []const u8, port: u16, hints: []const AddrInfo_hints, comptime Type: type, ctx: *Type, comptime callback: AddrInfo.Callback(Type)) void {
+ var host_buf: [1024]u8 = undefined;
+ var port_buf: [52]u8 = undefined;
+ const host_ptr: ?[*:0]const u8 = brk: {
+ if (!(host.len > 0 and !bun.strings.eqlComptime(host, "0.0.0.0") and !bun.strings.eqlComptime(host, "::0"))) {
+ break :brk null;
+ }
+ const len = @min(host.len, host_buf.len - 1);
+ @memcpy(&host_buf, host.ptr, len);
+ host_buf[len] = 0;
+ break :brk host_buf[0..len :0].ptr;
+ };
+
+ const port_ptr: ?[*:0]const u8 = brk: {
+ if (port == 0) {
+ break :brk null;
+ }
+
+ break :brk (std.fmt.bufPrintZ(&port_buf, "{d}", .{port}) catch unreachable).ptr;
+ };
+
+ var hints_buf: [3]AddrInfo_hints = bun.zero([3]AddrInfo_hints);
+ for (hints[0..@min(hints.len, 2)]) |hint, i| {
+ hints_buf[i] = hint;
+ }
+ var hints_: [*c]const AddrInfo_hints = if (hints.len > 0) &hints_buf else null;
+ ares_getaddrinfo(this, host_ptr, port_ptr, hints_, AddrInfo.callbackWrapper(Type, callback), ctx);
+ }
+
+ pub inline fn process(this: *Channel, fd: i32, readable: bool, writable: bool) void {
+ ares_process_fd(
+ this,
+ if (readable) fd else ARES_SOCKET_BAD,
+ if (writable) fd else ARES_SOCKET_BAD,
+ );
+ }
+};
+
+var ares_has_loaded = std.atomic.Atomic(bool).init(false);
+fn libraryInit() void {
+ if (ares_has_loaded.swap(true, .Monotonic))
+ return;
+
+ const rc = ares_library_init_mem(
+ ARES_LIB_INIT_ALL,
+ bun.Mimalloc.mi_malloc,
+ bun.Mimalloc.mi_free,
+ bun.Mimalloc.mi_realloc,
+ );
+ if (rc != ARES_SUCCESS) {
+ std.debug.panic("ares_library_init_mem failed: {any}", .{rc});
+ unreachable;
+ }
+}
+
+pub const ares_callback = ?*const fn (?*anyopaque, c_int, c_int, [*c]u8, c_int) callconv(.C) void;
+pub const ares_host_callback = ?*const fn (?*anyopaque, c_int, c_int, ?*struct_hostent) callconv(.C) void;
+pub const ares_nameinfo_callback = ?*const fn (?*anyopaque, c_int, c_int, [*c]u8, [*c]u8) callconv(.C) void;
+pub const ares_sock_create_callback = ?*const fn (ares_socket_t, c_int, ?*anyopaque) callconv(.C) c_int;
+pub const ares_sock_config_callback = ?*const fn (ares_socket_t, c_int, ?*anyopaque) callconv(.C) c_int;
+pub const ares_addrinfo_callback = *const fn (?*anyopaque, c_int, c_int, ?*AddrInfo) callconv(.C) void;
+pub extern fn ares_library_init(flags: c_int) c_int;
+pub extern fn ares_library_init_mem(flags: c_int, amalloc: ?*const fn (usize) callconv(.C) ?*anyopaque, afree: ?*const fn (?*anyopaque) callconv(.C) void, arealloc: ?*const fn (?*anyopaque, usize) callconv(.C) ?*anyopaque) c_int;
+pub extern fn ares_library_initialized() c_int;
+pub extern fn ares_library_cleanup() void;
+pub extern fn ares_version(version: [*c]c_int) [*c]const u8;
+pub extern fn ares_init(channelptr: **Channel) c_int;
+pub extern fn ares_init_options(channelptr: **Channel, options: ?*Options, optmask: c_int) c_int;
+pub extern fn ares_save_options(channel: *Channel, options: ?*Options, optmask: *c_int) c_int;
+pub extern fn ares_destroy_options(options: *Options) void;
+pub extern fn ares_dup(dest: ?*Channel, src: *Channel) c_int;
+pub extern fn ares_destroy(channel: *Channel) void;
+pub extern fn ares_cancel(channel: *Channel) void;
+pub extern fn ares_set_local_ip4(channel: *Channel, local_ip: c_uint) void;
+pub extern fn ares_set_local_ip6(channel: *Channel, local_ip6: [*c]const u8) void;
+pub extern fn ares_set_local_dev(channel: *Channel, local_dev_name: [*c]const u8) void;
+pub extern fn ares_set_socket_callback(channel: *Channel, callback: ares_sock_create_callback, user_data: ?*anyopaque) void;
+pub extern fn ares_set_socket_configure_callback(channel: *Channel, callback: ares_sock_config_callback, user_data: ?*anyopaque) void;
+pub extern fn ares_set_sortlist(channel: *Channel, sortstr: [*c]const u8) c_int;
+pub extern fn ares_getaddrinfo(channel: *Channel, node: ?[*:0]const u8, service: ?[*:0]const u8, hints: [*c]const AddrInfo_hints, callback: ares_addrinfo_callback, arg: ?*anyopaque) void;
+pub extern fn ares_freeaddrinfo(ai: *AddrInfo) void;
+pub const ares_socket_functions = extern struct {
+ socket: ?*const fn (c_int, c_int, c_int, ?*anyopaque) callconv(.C) ares_socket_t = null,
+ close: ?*const fn (ares_socket_t, ?*anyopaque) callconv(.C) c_int = null,
+ connect: ?*const fn (ares_socket_t, [*c]const struct_sockaddr, ares_socklen_t, ?*anyopaque) callconv(.C) c_int = null,
+ recvfrom: ?*const fn (ares_socket_t, ?*anyopaque, usize, c_int, [*c]struct_sockaddr, [*c]ares_socklen_t, ?*anyopaque) callconv(.C) ares_ssize_t = null,
+ sendv: ?*const fn (ares_socket_t, [*c]const iovec, c_int, ?*anyopaque) callconv(.C) ares_ssize_t = null,
+};
+pub extern fn ares_set_socket_functions(channel: *Channel, funcs: ?*const ares_socket_functions, user_data: ?*anyopaque) void;
+pub extern fn ares_send(channel: *Channel, qbuf: [*c]const u8, qlen: c_int, callback: ares_callback, arg: ?*anyopaque) void;
+pub extern fn ares_query(channel: *Channel, name: [*c]const u8, dnsclass: c_int, @"type": c_int, callback: ares_callback, arg: ?*anyopaque) void;
+pub extern fn ares_search(channel: *Channel, name: [*c]const u8, dnsclass: c_int, @"type": c_int, callback: ares_callback, arg: ?*anyopaque) void;
+pub extern fn ares_gethostbyname(channel: *Channel, name: [*c]const u8, family: c_int, callback: ares_host_callback, arg: ?*anyopaque) void;
+pub extern fn ares_gethostbyname_file(channel: *Channel, name: [*c]const u8, family: c_int, host: [*:null]?*struct_hostent) c_int;
+pub extern fn ares_gethostbyaddr(channel: *Channel, addr: ?*const anyopaque, addrlen: c_int, family: c_int, callback: ares_host_callback, arg: ?*anyopaque) void;
+pub extern fn ares_getnameinfo(channel: *Channel, sa: [*c]const struct_sockaddr, salen: ares_socklen_t, flags: c_int, callback: ares_nameinfo_callback, arg: ?*anyopaque) void;
+// pub extern fn ares_fds(channel: *Channel, read_fds: *fd_set, write_fds: *fd_set) c_int;
+pub extern fn ares_getsock(channel: *Channel, socks: [*c]ares_socket_t, numsocks: c_int) c_int;
+pub extern fn ares_timeout(channel: *Channel, maxtv: ?*struct_timeval, tv: ?*struct_timeval) ?*struct_timeval;
+// pub extern fn ares_process(channel: *Channel, read_fds: *fd_set, write_fds: *fd_set) void;
+pub extern fn ares_process_fd(channel: *Channel, read_fd: ares_socket_t, write_fd: ares_socket_t) void;
+pub extern fn ares_create_query(name: [*c]const u8, dnsclass: c_int, @"type": c_int, id: c_ushort, rd: c_int, buf: [*c][*c]u8, buflen: [*c]c_int, max_udp_size: c_int) c_int;
+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;
+const union_unnamed_2 = extern union {
+ _S6_u8: [16]u8,
+};
+pub const struct_ares_in6_addr = extern struct {
+ _S6_un: union_unnamed_2,
+};
+pub const struct_ares_addrttl = extern struct {
+ ipaddr: struct_in_addr,
+ ttl: c_int,
+};
+pub const struct_ares_addr6ttl = extern struct {
+ ip6addr: struct_ares_in6_addr,
+ ttl: c_int,
+};
+pub const struct_ares_caa_reply = extern struct {
+ next: [*c]struct_ares_caa_reply,
+ critical: c_int,
+ property: [*c]u8,
+ plength: usize,
+ value: [*c]u8,
+ length: usize,
+};
+pub const struct_ares_srv_reply = extern struct {
+ next: [*c]struct_ares_srv_reply,
+ host: [*c]u8,
+ priority: c_ushort,
+ weight: c_ushort,
+ port: c_ushort,
+};
+pub const struct_ares_mx_reply = extern struct {
+ next: [*c]struct_ares_mx_reply,
+ host: [*c]u8,
+ priority: c_ushort,
+};
+pub const struct_ares_txt_reply = extern struct {
+ next: [*c]struct_ares_txt_reply,
+ txt: [*c]u8,
+ length: usize,
+};
+pub const struct_ares_txt_ext = extern struct {
+ next: [*c]struct_ares_txt_ext,
+ txt: [*c]u8,
+ length: usize,
+ record_start: u8,
+};
+pub const struct_ares_naptr_reply = extern struct {
+ next: [*c]struct_ares_naptr_reply,
+ flags: [*c]u8,
+ service: [*c]u8,
+ regexp: [*c]u8,
+ replacement: [*c]u8,
+ order: c_ushort,
+ preference: c_ushort,
+};
+pub const struct_ares_soa_reply = extern struct {
+ nsname: [*c]u8,
+ hostmaster: [*c]u8,
+ serial: c_uint,
+ refresh: c_uint,
+ retry: c_uint,
+ expire: c_uint,
+ minttl: c_uint,
+};
+pub const struct_ares_uri_reply = extern struct {
+ next: [*c]struct_ares_uri_reply,
+ priority: c_ushort,
+ weight: c_ushort,
+ uri: [*c]u8,
+ ttl: c_int,
+};
+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;
+pub extern fn ares_parse_ptr_reply(abuf: [*c]const u8, alen: c_int, addr: ?*const anyopaque, addrlen: c_int, family: c_int, host: [*c]?*struct_hostent) c_int;
+pub extern fn ares_parse_ns_reply(abuf: [*c]const u8, alen: c_int, host: [*c]?*struct_hostent) c_int;
+pub extern fn ares_parse_srv_reply(abuf: [*c]const u8, alen: c_int, srv_out: [*c][*c]struct_ares_srv_reply) c_int;
+pub extern fn ares_parse_mx_reply(abuf: [*c]const u8, alen: c_int, mx_out: [*c][*c]struct_ares_mx_reply) c_int;
+pub extern fn ares_parse_txt_reply(abuf: [*c]const u8, alen: c_int, txt_out: [*c][*c]struct_ares_txt_reply) c_int;
+pub extern fn ares_parse_txt_reply_ext(abuf: [*c]const u8, alen: c_int, txt_out: [*c][*c]struct_ares_txt_ext) c_int;
+pub extern fn ares_parse_naptr_reply(abuf: [*c]const u8, alen: c_int, naptr_out: [*c][*c]struct_ares_naptr_reply) c_int;
+pub extern fn ares_parse_soa_reply(abuf: [*c]const u8, alen: c_int, soa_out: [*c][*c]struct_ares_soa_reply) c_int;
+pub extern fn ares_parse_uri_reply(abuf: [*c]const u8, alen: c_int, uri_out: [*c][*c]struct_ares_uri_reply) c_int;
+pub extern fn ares_free_string(str: ?*anyopaque) void;
+pub extern fn ares_free_hostent(host: ?*struct_hostent) void;
+pub extern fn ares_free_data(dataptr: ?*anyopaque) void;
+pub extern fn ares_strerror(code: c_int) [*c]const u8;
+const union_unnamed_3 = extern union {
+ addr4: struct_in_addr,
+ addr6: struct_ares_in6_addr,
+};
+pub const struct_ares_addr_node = extern struct {
+ next: [*c]struct_ares_addr_node,
+ family: c_int,
+ addr: union_unnamed_3,
+};
+const union_unnamed_4 = extern union {
+ addr4: struct_in_addr,
+ addr6: struct_ares_in6_addr,
+};
+pub const struct_ares_addr_port_node = extern struct {
+ next: [*c]struct_ares_addr_port_node,
+ family: c_int,
+ addr: union_unnamed_4,
+ udp_port: c_int,
+ tcp_port: c_int,
+};
+pub extern fn ares_set_servers(channel: *Channel, servers: [*c]struct_ares_addr_node) c_int;
+pub extern fn ares_set_servers_ports(channel: *Channel, servers: [*c]struct_ares_addr_port_node) c_int;
+pub extern fn ares_set_servers_csv(channel: *Channel, servers: [*c]const u8) c_int;
+pub extern fn ares_set_servers_ports_csv(channel: *Channel, servers: [*c]const u8) c_int;
+pub extern fn ares_get_servers(channel: *Channel, servers: [*c][*c]struct_ares_addr_node) c_int;
+pub extern fn ares_get_servers_ports(channel: *Channel, servers: [*c][*c]struct_ares_addr_port_node) c_int;
+pub extern fn ares_inet_ntop(af: c_int, src: ?*const anyopaque, dst: [*c]u8, size: ares_socklen_t) [*c]const u8;
+pub extern fn ares_inet_pton(af: c_int, src: [*c]const u8, dst: ?*anyopaque) c_int;
+pub const ARES_SUCCESS = 0;
+pub const ARES_ENODATA = 1;
+pub const ARES_EFORMERR = 2;
+pub const ARES_ESERVFAIL = 3;
+pub const ARES_ENOTFOUND = 4;
+pub const ARES_ENOTIMP = 5;
+pub const ARES_EREFUSED = 6;
+pub const ARES_EBADQUERY = 7;
+pub const ARES_EBADNAME = 8;
+pub const ARES_EBADFAMILY = 9;
+pub const ARES_EBADRESP = 10;
+pub const ARES_ECONNREFUSED = 11;
+pub const ARES_ETIMEOUT = 12;
+pub const ARES_EOF = 13;
+pub const ARES_EFILE = 14;
+pub const ARES_ENOMEM = 15;
+pub const ARES_EDESTRUCTION = 16;
+pub const ARES_EBADSTR = 17;
+pub const ARES_EBADFLAGS = 18;
+pub const ARES_ENONAME = 19;
+pub const ARES_EBADHINTS = 20;
+pub const ARES_ENOTINITIALIZED = 21;
+pub const ARES_ELOADIPHLPAPI = 22;
+pub const ARES_EADDRGETNETWORKPARAMS = 23;
+pub const ARES_ECANCELLED = 24;
+pub const ARES_ESERVICE = 25;
+
+pub const Error = enum(i32) {
+ ENODATA = ARES_ENODATA,
+ EFORMERR = ARES_EFORMERR,
+ ESERVFAIL = ARES_ESERVFAIL,
+ ENOTFOUND = ARES_ENOTFOUND,
+ ENOTIMP = ARES_ENOTIMP,
+ EREFUSED = ARES_EREFUSED,
+ EBADQUERY = ARES_EBADQUERY,
+ EBADNAME = ARES_EBADNAME,
+ EBADFAMILY = ARES_EBADFAMILY,
+ EBADRESP = ARES_EBADRESP,
+ ECONNREFUSED = ARES_ECONNREFUSED,
+ ETIMEOUT = ARES_ETIMEOUT,
+ EOF = ARES_EOF,
+ EFILE = ARES_EFILE,
+ ENOMEM = ARES_ENOMEM,
+ EDESTRUCTION = ARES_EDESTRUCTION,
+ EBADSTR = ARES_EBADSTR,
+ EBADFLAGS = ARES_EBADFLAGS,
+ ENONAME = ARES_ENONAME,
+ EBADHINTS = ARES_EBADHINTS,
+ ENOTINITIALIZED = ARES_ENOTINITIALIZED,
+ ELOADIPHLPAPI = ARES_ELOADIPHLPAPI,
+ EADDRGETNETWORKPARAMS = ARES_EADDRGETNETWORKPARAMS,
+ ECANCELLED = ARES_ECANCELLED,
+ ESERVICE = ARES_ESERVICE,
+
+ pub fn initEAI(rc: i32) ?Error {
+ return switch (@intToEnum(std.os.system.EAI, rc)) {
+ @intToEnum(std.os.system.EAI, 0) => return null,
+ .ADDRFAMILY => Error.EBADFAMILY,
+ .BADFLAGS => Error.EBADFLAGS, // Invalid hints
+ .FAIL => Error.EBADRESP,
+ .FAMILY => Error.EBADFAMILY,
+ .MEMORY => Error.ENOMEM,
+ .NODATA => Error.ENODATA,
+ .NONAME => Error.ENONAME,
+ .SERVICE => Error.ESERVICE,
+ .SYSTEM => Error.ESERVFAIL,
+ else => unreachable,
+ };
+ }
+
+ pub const code = bun.enumMap(Error, .{
+ .{ .ENODATA, "DNS_ENODATA" },
+ .{ .EFORMERR, "DNS_EFORMERR" },
+ .{ .ESERVFAIL, "DNS_ESERVFAIL" },
+ .{ .ENOTFOUND, "DNS_ENOTFOUND" },
+ .{ .ENOTIMP, "DNS_ENOTIMP" },
+ .{ .EREFUSED, "DNS_EREFUSED" },
+ .{ .EBADQUERY, "DNS_EBADQUERY" },
+ .{ .EBADNAME, "DNS_EBADNAME" },
+ .{ .EBADFAMILY, "DNS_EBADFAMILY" },
+ .{ .EBADRESP, "DNS_EBADRESP" },
+ .{ .ECONNREFUSED, "DNS_ECONNREFUSED" },
+ .{ .ETIMEOUT, "DNS_ETIMEOUT" },
+ .{ .EOF, "DNS_EOF" },
+ .{ .EFILE, "DNS_EFILE" },
+ .{ .ENOMEM, "DNS_ENOMEM" },
+ .{ .EDESTRUCTION, "DNS_EDESTRUCTION" },
+ .{ .EBADSTR, "DNS_EBADSTR" },
+ .{ .EBADFLAGS, "DNS_EBADFLAGS" },
+ .{ .ENONAME, "DNS_ENONAME" },
+ .{ .EBADHINTS, "DNS_EBADHINTS" },
+ .{ .ENOTINITIALIZED, "DNS_ENOTINITIALIZED" },
+ .{ .ELOADIPHLPAPI, "DNS_ELOADIPHLPAPI" },
+ .{ .EADDRGETNETWORKPARAMS, "DNS_EADDRGETNETWORKPARAMS" },
+ .{ .ECANCELLED, "DNS_ECANCELLED" },
+ .{ .ESERVICE, "DNS_ESERVICE" },
+ });
+
+ pub const label = bun.enumMap(Error, .{
+ .{ .ENODATA, "No data record of requested type" },
+ .{ .EFORMERR, "Malformed DNS query" },
+ .{ .ESERVFAIL, "Server failed to complete the DNS operation" },
+ .{ .ENOTFOUND, "Domain name not found" },
+ .{ .ENOTIMP, "DNS resolver does not implement requested operation" },
+ .{ .EREFUSED, "DNS operation refused" },
+ .{ .EBADQUERY, "Misformatted DNS query" },
+ .{ .EBADNAME, "Misformatted domain name" },
+ .{ .EBADFAMILY, "Misformatted DNS query (family)" },
+ .{ .EBADRESP, "Misformatted DNS reply" },
+ .{ .ECONNREFUSED, "Could not contact DNS servers" },
+ .{ .ETIMEOUT, "Timeout while contacting DNS servers" },
+ .{ .EOF, "End of file" },
+ .{ .EFILE, "Error reading file" },
+ .{ .ENOMEM, "Out of memory" },
+ .{ .EDESTRUCTION, "Channel is being destroyed" },
+ .{ .EBADSTR, "Misformatted string" },
+ .{ .EBADFLAGS, "Illegal flags specified" },
+ .{ .ENONAME, "Given hostname is not numeric" },
+ .{ .EBADHINTS, "Illegal hints flags specified" },
+ .{ .ENOTINITIALIZED, "Library initialization not yet performed" },
+ .{ .ELOADIPHLPAPI, "ELOADIPHLPAPI TODO WHAT DOES THIS MEAN" },
+ .{ .EADDRGETNETWORKPARAMS, "EADDRGETNETWORKPARAMS" },
+ .{ .ECANCELLED, "DNS query cancelled" },
+ .{ .ESERVICE, "Service not available" },
+ });
+
+ pub fn get(rc: i32) ?Error {
+ return switch (rc) {
+ 0 => null,
+ 1...ARES_ESERVICE => @intToEnum(Error, rc),
+ -ARES_ESERVICE...-1 => @intToEnum(Error, -rc),
+ else => unreachable,
+ };
+ }
+};
+
+pub const ARES_FLAG_USEVC = @as(c_int, 1) << @as(c_int, 0);
+pub const ARES_FLAG_PRIMARY = @as(c_int, 1) << @as(c_int, 1);
+pub const ARES_FLAG_IGNTC = @as(c_int, 1) << @as(c_int, 2);
+pub const ARES_FLAG_NORECURSE = @as(c_int, 1) << @as(c_int, 3);
+pub const ARES_FLAG_STAYOPEN = @as(c_int, 1) << @as(c_int, 4);
+pub const ARES_FLAG_NOSEARCH = @as(c_int, 1) << @as(c_int, 5);
+pub const ARES_FLAG_NOALIASES = @as(c_int, 1) << @as(c_int, 6);
+pub const ARES_FLAG_NOCHECKRESP = @as(c_int, 1) << @as(c_int, 7);
+pub const ARES_FLAG_EDNS = @as(c_int, 1) << @as(c_int, 8);
+pub const ARES_OPT_FLAGS = @as(c_int, 1) << @as(c_int, 0);
+pub const ARES_OPT_TIMEOUT = @as(c_int, 1) << @as(c_int, 1);
+pub const ARES_OPT_TRIES = @as(c_int, 1) << @as(c_int, 2);
+pub const ARES_OPT_NDOTS = @as(c_int, 1) << @as(c_int, 3);
+pub const ARES_OPT_UDP_PORT = @as(c_int, 1) << @as(c_int, 4);
+pub const ARES_OPT_TCP_PORT = @as(c_int, 1) << @as(c_int, 5);
+pub const ARES_OPT_SERVERS = @as(c_int, 1) << @as(c_int, 6);
+pub const ARES_OPT_DOMAINS = @as(c_int, 1) << @as(c_int, 7);
+pub const ARES_OPT_LOOKUPS = @as(c_int, 1) << @as(c_int, 8);
+pub const ARES_OPT_SOCK_STATE_CB = @as(c_int, 1) << @as(c_int, 9);
+pub const ARES_OPT_SORTLIST = @as(c_int, 1) << @as(c_int, 10);
+pub const ARES_OPT_SOCK_SNDBUF = @as(c_int, 1) << @as(c_int, 11);
+pub const ARES_OPT_SOCK_RCVBUF = @as(c_int, 1) << @as(c_int, 12);
+pub const ARES_OPT_TIMEOUTMS = @as(c_int, 1) << @as(c_int, 13);
+pub const ARES_OPT_ROTATE = @as(c_int, 1) << @as(c_int, 14);
+pub const ARES_OPT_EDNSPSZ = @as(c_int, 1) << @as(c_int, 15);
+pub const ARES_OPT_NOROTATE = @as(c_int, 1) << @as(c_int, 16);
+pub const ARES_OPT_RESOLVCONF = @as(c_int, 1) << @as(c_int, 17);
+pub const ARES_OPT_HOSTS_FILE = @as(c_int, 1) << @as(c_int, 18);
+pub const ARES_NI_NOFQDN = @as(c_int, 1) << @as(c_int, 0);
+pub const ARES_NI_NUMERICHOST = @as(c_int, 1) << @as(c_int, 1);
+pub const ARES_NI_NAMEREQD = @as(c_int, 1) << @as(c_int, 2);
+pub const ARES_NI_NUMERICSERV = @as(c_int, 1) << @as(c_int, 3);
+pub const ARES_NI_DGRAM = @as(c_int, 1) << @as(c_int, 4);
+pub const ARES_NI_TCP = @as(c_int, 0);
+pub const ARES_NI_UDP = ARES_NI_DGRAM;
+pub const ARES_NI_SCTP = @as(c_int, 1) << @as(c_int, 5);
+pub const ARES_NI_DCCP = @as(c_int, 1) << @as(c_int, 6);
+pub const ARES_NI_NUMERICSCOPE = @as(c_int, 1) << @as(c_int, 7);
+pub const ARES_NI_LOOKUPHOST = @as(c_int, 1) << @as(c_int, 8);
+pub const ARES_NI_LOOKUPSERVICE = @as(c_int, 1) << @as(c_int, 9);
+pub const ARES_NI_IDN = @as(c_int, 1) << @as(c_int, 10);
+pub const ARES_NI_IDN_ALLOW_UNASSIGNED = @as(c_int, 1) << @as(c_int, 11);
+pub const ARES_NI_IDN_USE_STD3_ASCII_RULES = @as(c_int, 1) << @as(c_int, 12);
+pub const ARES_AI_CANONNAME = @as(c_int, 1) << @as(c_int, 0);
+pub const ARES_AI_NUMERICHOST = @as(c_int, 1) << @as(c_int, 1);
+pub const ARES_AI_PASSIVE = @as(c_int, 1) << @as(c_int, 2);
+pub const ARES_AI_NUMERICSERV = @as(c_int, 1) << @as(c_int, 3);
+pub const ARES_AI_V4MAPPED = @as(c_int, 1) << @as(c_int, 4);
+pub const ARES_AI_ALL = @as(c_int, 1) << @as(c_int, 5);
+pub const ARES_AI_ADDRCONFIG = @as(c_int, 1) << @as(c_int, 6);
+pub const ARES_AI_NOSORT = @as(c_int, 1) << @as(c_int, 7);
+pub const ARES_AI_ENVHOSTS = @as(c_int, 1) << @as(c_int, 8);
+pub const ARES_AI_IDN = @as(c_int, 1) << @as(c_int, 10);
+pub const ARES_AI_IDN_ALLOW_UNASSIGNED = @as(c_int, 1) << @as(c_int, 11);
+pub const ARES_AI_IDN_USE_STD3_ASCII_RULES = @as(c_int, 1) << @as(c_int, 12);
+pub const ARES_AI_CANONIDN = @as(c_int, 1) << @as(c_int, 13);
+pub const ARES_AI_MASK = (((((ARES_AI_CANONNAME | ARES_AI_NUMERICHOST) | ARES_AI_PASSIVE) | ARES_AI_NUMERICSERV) | ARES_AI_V4MAPPED) | ARES_AI_ALL) | ARES_AI_ADDRCONFIG;
+pub const ARES_GETSOCK_MAXNUM = @as(c_int, 16);
+pub inline fn ARES_GETSOCK_READABLE(bits: anytype, num: anytype) @TypeOf(bits & (@as(c_int, 1) << num)) {
+ return bits & (@as(c_int, 1) << num);
+}
+pub inline fn ARES_GETSOCK_WRITABLE(bits: anytype, num: anytype) @TypeOf(bits & (@as(c_int, 1) << (num + ARES_GETSOCK_MAXNUM))) {
+ return bits & (@as(c_int, 1) << (num + ARES_GETSOCK_MAXNUM));
+}
+pub const ARES_LIB_INIT_NONE = @as(c_int, 0);
+pub const ARES_LIB_INIT_WIN32 = @as(c_int, 1) << @as(c_int, 0);
+pub const ARES_LIB_INIT_ALL = ARES_LIB_INIT_WIN32;
+pub const ARES_SOCKET_BAD = -@as(c_int, 1);
+pub const ares_socket_typedef = "";
+pub const ares_addrinfo_cname = AddrInfo_cname;
+pub const ares_addrinfo_node = AddrInfo_node;
+pub const ares_addrinfo = AddrInfo;
+pub const ares_addrinfo_hints = AddrInfo_hints;
+pub const ares_in6_addr = struct_ares_in6_addr;
+pub const ares_addrttl = struct_ares_addrttl;
+pub const ares_addr6ttl = struct_ares_addr6ttl;
+pub const ares_caa_reply = struct_ares_caa_reply;
+pub const ares_srv_reply = struct_ares_srv_reply;
+pub const ares_mx_reply = struct_ares_mx_reply;
+pub const ares_txt_reply = struct_ares_txt_reply;
+pub const ares_txt_ext = struct_ares_txt_ext;
+pub const ares_naptr_reply = struct_ares_naptr_reply;
+pub const ares_soa_reply = struct_ares_soa_reply;
+pub const ares_uri_reply = struct_ares_uri_reply;
+pub const ares_addr_node = struct_ares_addr_node;
+pub const ares_addr_port_node = struct_ares_addr_port_node;
diff --git a/src/io/io_darwin.cpp b/src/io/io_darwin.cpp
index fff41d373..61e3f1624 100644
--- a/src/io/io_darwin.cpp
+++ b/src/io/io_darwin.cpp
@@ -43,6 +43,20 @@ extern "C" mach_port_t io_darwin_create_machport(uint64_t wakeup, int32_t fd,
}
}
+extern "C" bool getaddrinfo_send_reply(mach_port_t port,
+ void (*sendReply)(void *)) {
+ mach_msg_empty_rcv_t msg;
+ mach_msg_return_t status;
+
+ status = mach_msg(&msg.header, MACH_RCV_MSG, 0, sizeof(msg), port,
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+ if (status != MACH_MSG_SUCCESS) {
+ return false;
+ }
+ sendReply(&msg);
+ return true;
+}
+
extern "C" bool io_darwin_schedule_wakeup(mach_port_t waker) {
mach_msg_empty_send_t message{};
message.header.msgh_size = sizeof(message);
diff --git a/src/jsc.zig b/src/jsc.zig
index 99080603f..114326d79 100644
--- a/src/jsc.zig
+++ b/src/jsc.zig
@@ -39,6 +39,7 @@ pub const API = struct {
pub const TLSSocket = @import("./bun.js/api/bun/socket.zig").TLSSocket;
pub const Listener = @import("./bun.js/api/bun/socket.zig").Listener;
};
+pub const DNS = @import("./bun.js/api/bun/dns_resolver.zig");
pub const FFI = @import("./bun.js/api/ffi.zig").FFI;
pub const Node = struct {
pub usingnamespace @import("./bun.js/node/types.zig");
diff --git a/src/string_immutable.zig b/src/string_immutable.zig
index 7c2b61c03..29b692ffb 100644
--- a/src/string_immutable.zig
+++ b/src/string_immutable.zig
@@ -3974,6 +3974,14 @@ pub fn isIPAddress(input: []const u8) bool {
}
}
+pub fn isIPV6Address(input: []const u8) bool {
+ if (std.net.Address.parseIp6(input, 0)) |_| {
+ return true;
+ } else |_| {
+ return false;
+ }
+}
+
pub fn cloneNormalizingSeparators(
allocator: std.mem.Allocator,
input: []const u8,