diff --git a/src/bun.js/api/bun/dns_resolver.zig b/src/bun.js/api/bun/dns_resolver.zig index 6d2ff6fadf..803014752a 100644 --- a/src/bun.js/api/bun/dns_resolver.zig +++ b/src/bun.js/api/bun/dns_resolver.zig @@ -258,41 +258,7 @@ const LibUVBackend = struct { } }; -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 = @as(*const [4]u8, @ptrCast(&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 => { - if (comptime std.net.has_unix_sockets) { - break :brk std.mem.sliceTo(&address.un.path, 0); - } - break :brk ""; - }, - else => break :brk "", - } - }; - - return JSC.ZigString.init(str); -} +const GetAddrInfo = bun.dns.GetAddrInfo; pub fn normalizeDNSName(name: []const u8, backend: *GetAddrInfo.Backend) []const u8 { if (backend.* == .c_ares) { @@ -311,402 +277,6 @@ pub fn normalizeDNSName(name: []const u8, backend: *GetAddrInfo.Backend) []const 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 += @intFromBool(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 = bun.ArenaAllocator.init(stack.get()); - const array = JSC.JSValue.createEmptyArray( - globalThis, - addrInfoCount(addr_info), - ); - - { - defer arena.deinit(); - - const 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)], 0); - - 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)], 0); - - 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.coerce(i32, globalObject)) { - 0 => .unspecified, - 4 => .inet, - 6 => .inet6, - else => return error.InvalidFamily, - }; - } - - if (value.isString()) { - const str = value.toBunString(globalObject); - if (str.isEmpty()) - return .unspecified; - - return str.inMap(map) 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 = switch (Environment.os) { - .mac, .windows => .system, - else => .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 = bun.ArenaAllocator.init(stack.get()); - const array = JSC.JSValue.createEmptyArray(globalThis, @as(u32, @truncate(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(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 fn ResolveInfoRequest(comptime cares_type: type, comptime type_name: []const u8) type { return struct { const request_type = @This(); diff --git a/src/bun.zig b/src/bun.zig index f82bb8d34a..761d51379f 100644 --- a/src/bun.zig +++ b/src/bun.zig @@ -3192,3 +3192,5 @@ pub inline fn unsafeAssert(condition: bool) void { unreachable; } } + +pub const dns = @import("./dns.zig"); diff --git a/src/deps/c_ares.zig b/src/deps/c_ares.zig index 1f4dbaab3b..7a264ea7cb 100644 --- a/src/deps/c_ares.zig +++ b/src/deps/c_ares.zig @@ -390,7 +390,7 @@ pub const AddrInfo = extern struct { array.putIndex( globalThis, j, - bun.JSC.DNS.GetAddrInfo.Result.toJS( + 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.?).* } }, @@ -1602,3 +1602,4 @@ comptime { _ = Bun__canonicalizeIP; } } +const GetAddrInfo = bun.dns.GetAddrInfo; diff --git a/src/dns.zig b/src/dns.zig new file mode 100644 index 0000000000..caa77f8f87 --- /dev/null +++ b/src/dns.zig @@ -0,0 +1,438 @@ +const bun = @import("root").bun; +const std = @import("std"); +const JSC = bun.JSC; +const JSValue = JSC.JSValue; + +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)], 0); + + 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)], 0); + + 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.coerce(i32, globalObject)) { + 0 => .unspecified, + 4 => .inet, + 6 => .inet6, + else => return error.InvalidFamily, + }; + } + + if (value.isString()) { + return map.fromJS(globalObject, value) orelse { + if (value.toString(globalObject).length() == 0) { + return .unspecified; + } + + 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()) { + return map.fromJS(globalObject, value) orelse { + if (value.toString(globalObject).length() == 0) + return .unspecified; + + 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()) { + return map.fromJS(globalObject, value) orelse { + const str = value.toString(globalObject); + if (str.length() == 0) + return .unspecified; + + 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 = switch (bun.Environment.os) { + .mac, .windows => .system, + else => .c_ares, + }; + + pub fn fromJS(value: JSC.JSValue, globalObject: *JSC.JSGlobalObject) !Backend { + if (value.isEmptyOrUndefinedOrNull()) + return default; + + if (value.isString()) { + return label.fromJS(globalObject, value) orelse { + if (value.toString(globalObject).length() == 0) { + return default; + } + + 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: *const Any, globalThis: *JSC.JSGlobalObject) ?JSC.JSValue { + return switch (this.*) { + .addrinfo => |addrinfo| addrInfoToJSArray(addrinfo orelse return null, globalThis), + .list => |list| brk: { + const array = JSC.JSValue.createEmptyArray(globalThis, @as(u32, @truncate(list.items.len))); + var i: u32 = 0; + const items: []const Result = list.items; + for (items) |item| { + array.putIndex(globalThis, i, item.toJS(globalThis)); + i += 1; + } + break :brk array; + }, + }; + } + + pub fn deinit(this: *const Any) void { + switch (this.*) { + .addrinfo => |addrinfo| { + if (addrinfo) |a| { + std.c.freeaddrinfo(a); + } + }, + .list => |list_| { + var list = list_; + list.clearAndFree(); + }, + } + } + }; + + 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(addrinfo.addr orelse return null)), + // no TTL in POSIX getaddrinfo() + .ttl = 0, + }; + } + + pub fn toJS(this: *const Result, globalThis: *JSC.JSGlobalObject) JSValue { + const obj = JSC.JSValue.createEmptyObject(globalThis, 3); + obj.put(globalThis, JSC.ZigString.static("address"), addressToJS(&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; + } + }; +}; +const String = bun.String; +const default_allocator = bun.default_allocator; +pub fn addressToString( + address: *const std.net.Address, +) !bun.String { + switch (address.any.family) { + std.os.AF.INET => { + var self = address.in; + const bytes = @as(*const [4]u8, @ptrCast(&self.sa.addr)); + return String.createFormat("{}.{}.{}.{}", .{ + bytes[0], + bytes[1], + bytes[2], + bytes[3], + }); + }, + std.os.AF.INET6 => { + var stack = std.heap.stackFallback(512, default_allocator); + const allocator = stack.get(); + var out = try std.fmt.allocPrint(allocator, "{any}", .{address.*}); + defer allocator.free(out); + // TODO: this is a hack, fix it + // This removes [.*]:port + // ^ ^^^^^^ + return String.createLatin1(out[1 .. out.len - 1 - std.fmt.count("{d}", .{address.in6.getPort()}) - 1]); + }, + std.os.AF.UNIX => { + if (comptime std.net.has_unix_sockets) { + return String.createLatin1(&address.un.path); + } + + return String.empty; + }, + else => return String.empty, + } +} + +pub fn addressToJS( + address: *const std.net.Address, + globalThis: *JSC.JSGlobalObject, +) JSC.JSValue { + const str = addressToString(address) catch { + globalThis.throwOutOfMemory(); + return .zero; + }; + defer str.deref(); + return str.toJS(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 += @intFromBool(current.?.addr != null); + } + return count; +} + +pub fn addrInfoToJSArray( + addr_info: *std.c.addrinfo, + globalThis: *JSC.JSGlobalObject, +) JSC.JSValue { + const array = JSC.JSValue.createEmptyArray( + globalThis, + addrInfoCount(addr_info), + ); + + { + var j: u32 = 0; + var current: ?*std.c.addrinfo = addr_info; + while (current) |this_node| : (current = current.?.next) { + array.putIndex( + globalThis, + j, + GetAddrInfo.Result.toJS( + &(GetAddrInfo.Result.fromAddrInfo(this_node) orelse continue), + globalThis, + ), + ); + j += 1; + } + } + + return array; +}