mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
Move some dns code into bun.dns (#10728)
Co-authored-by: Jarred-Sumner <Jarred-Sumner@users.noreply.github.com>
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -3192,3 +3192,5 @@ pub inline fn unsafeAssert(condition: bool) void {
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
pub const dns = @import("./dns.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;
|
||||
|
||||
438
src/dns.zig
Normal file
438
src/dns.zig
Normal file
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user