diff options
author | 2023-01-07 07:09:48 -0800 | |
---|---|---|
committer | 2023-01-07 07:09:48 -0800 | |
commit | 87983464d8a331c1ddd09eced9920269a759f0a9 (patch) | |
tree | b08a5aef5c2d18f25a5ee46c88bec84d5b8ee907 /src | |
parent | d5565ab2cdd7099a5852ba5ba6d180ef291af084 (diff) | |
download | bun-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.zig | 1371 | ||||
-rw-r--r-- | src/bun.js/base.zig | 78 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGeneratedCode.cpp | 32 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGlobalObject.cpp | 10 | ||||
-rw-r--r-- | src/bun.js/bindings/ares_build.h | 42 | ||||
-rw-r--r-- | src/bun.js/bindings/bindings.cpp | 56 | ||||
-rw-r--r-- | src/bun.js/bindings/bindings.zig | 66 | ||||
-rw-r--r-- | src/bun.js/bindings/glibc-versions-hack.cpp | 4 | ||||
-rw-r--r-- | src/bun.js/bindings/headers-cpp.h | 2 | ||||
-rw-r--r-- | src/bun.js/bindings/headers.h | 5 | ||||
-rw-r--r-- | src/bun.js/bindings/headers.zig | 3 | ||||
-rw-r--r-- | src/bun.js/event_loop.zig | 25 | ||||
-rw-r--r-- | src/bun.js/fs_promises.exports.js | 2 | ||||
-rw-r--r-- | src/bun.js/node/syscall.zig | 1 | ||||
-rw-r--r-- | src/bun.js/rare_data.zig | 10 | ||||
-rw-r--r-- | src/bun.zig | 9 | ||||
-rw-r--r-- | src/c.zig | 23 | ||||
m--------- | src/deps/c-ares | 0 | ||||
-rw-r--r-- | src/deps/c_ares.zig | 687 | ||||
-rw-r--r-- | src/io/io_darwin.cpp | 14 | ||||
-rw-r--r-- | src/jsc.zig | 1 | ||||
-rw-r--r-- | src/string_immutable.zig | 8 |
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"); @@ -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, |