mirror of
https://github.com/oven-sh/bun
synced 2026-02-12 11:59:00 +00:00
2096 lines
84 KiB
Zig
2096 lines
84 KiB
Zig
pub const socklen_t = c.socklen_t;
|
|
pub const ares_ssize_t = isize;
|
|
pub const ares_socket_t = if (bun.Environment.isWindows) std.os.windows.ws2_32.SOCKET else 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 {};
|
|
|
|
pub const AF = std.posix.AF;
|
|
|
|
pub const NSClass = enum(c_int) {
|
|
/// Cookie.
|
|
ns_c_invalid = 0,
|
|
/// Internet.
|
|
ns_c_in = 1,
|
|
/// unallocated/unsupported.
|
|
ns_c_2 = 2,
|
|
/// MIT Chaos-net.
|
|
ns_c_chaos = 3,
|
|
/// MIT Hesiod.
|
|
ns_c_hs = 4,
|
|
/// Query class values which do not appear in resource records
|
|
/// for prereq. sections in update requests
|
|
ns_c_none = 254,
|
|
/// Wildcard match.
|
|
ns_c_any = 255,
|
|
ns_c_max = 65536,
|
|
};
|
|
|
|
pub const NSType = enum(c_int) {
|
|
/// Cookie.
|
|
ns_t_invalid = 0,
|
|
/// Host address.
|
|
ns_t_a = 1,
|
|
/// Authoritative server.
|
|
ns_t_ns = 2,
|
|
/// Mail destination.
|
|
ns_t_md = 3,
|
|
/// Mail forwarder.
|
|
ns_t_mf = 4,
|
|
/// Canonical name.
|
|
ns_t_cname = 5,
|
|
/// Start of authority zone.
|
|
ns_t_soa = 6,
|
|
/// Mailbox domain name.
|
|
ns_t_mb = 7,
|
|
/// Mail group member.
|
|
ns_t_mg = 8,
|
|
/// Mail rename name.
|
|
ns_t_mr = 9,
|
|
/// Null resource record.
|
|
ns_t_null = 10,
|
|
/// Well known service.
|
|
ns_t_wks = 11,
|
|
/// Domain name pointer.
|
|
ns_t_ptr = 12,
|
|
/// Host information.
|
|
ns_t_hinfo = 13,
|
|
/// Mailbox information.
|
|
ns_t_minfo = 14,
|
|
/// Mail routing information.
|
|
ns_t_mx = 15,
|
|
/// Text strings.
|
|
ns_t_txt = 16,
|
|
/// Responsible person.
|
|
ns_t_rp = 17,
|
|
/// AFS cell database.
|
|
ns_t_afsdb = 18,
|
|
/// X_25 calling address.
|
|
ns_t_x25 = 19,
|
|
/// ISDN calling address.
|
|
ns_t_isdn = 20,
|
|
/// Router.
|
|
ns_t_rt = 21,
|
|
/// NSAP address.
|
|
ns_t_nsap = 22,
|
|
/// Reverse NSAP lookup (deprecated).
|
|
ns_t_nsap_ptr = 23,
|
|
/// Security signature.
|
|
ns_t_sig = 24,
|
|
/// Security key.
|
|
ns_t_key = 25,
|
|
/// X.400 mail mapping.
|
|
ns_t_px = 26,
|
|
/// Geographical position (withdrawn).
|
|
ns_t_gpos = 27,
|
|
/// Ip6 Address.
|
|
ns_t_aaaa = 28,
|
|
/// Location Information.
|
|
ns_t_loc = 29,
|
|
/// Next domain (security).
|
|
ns_t_nxt = 30,
|
|
/// Endpoint identifier.
|
|
ns_t_eid = 31,
|
|
/// Nimrod Locator.
|
|
ns_t_nimloc = 32,
|
|
/// Server Selection.
|
|
ns_t_srv = 33,
|
|
/// ATM Address
|
|
ns_t_atma = 34,
|
|
/// Naming Authority PoinTeR
|
|
ns_t_naptr = 35,
|
|
/// Key Exchange
|
|
ns_t_kx = 36,
|
|
/// Certification record
|
|
ns_t_cert = 37,
|
|
/// IPv6 address (deprecates AAAA)
|
|
ns_t_a6 = 38,
|
|
/// Non-terminal DNAME (for IPv6)
|
|
ns_t_dname = 39,
|
|
/// Kitchen sink (experimentatl)
|
|
ns_t_sink = 40,
|
|
/// EDNS0 option (meta-RR)
|
|
ns_t_opt = 41,
|
|
/// Address prefix list (RFC3123)
|
|
ns_t_apl = 42,
|
|
/// Delegation Signer (RFC4034)
|
|
ns_t_ds = 43,
|
|
/// SSH Key Fingerprint (RFC4255)
|
|
ns_t_sshfp = 44,
|
|
/// Resource Record Signature (RFC4034)
|
|
ns_t_rrsig = 46,
|
|
/// Next Secure (RFC4034)
|
|
ns_t_nsec = 47,
|
|
/// DNS Public Key (RFC4034)
|
|
ns_t_dnskey = 48,
|
|
/// Transaction key
|
|
ns_t_tkey = 249,
|
|
/// Transaction signature.
|
|
ns_t_tsig = 250,
|
|
/// Incremental zone transfer.
|
|
ns_t_ixfr = 251,
|
|
/// Transfer zone of authority.
|
|
ns_t_axfr = 252,
|
|
/// Transfer mailbox records.
|
|
ns_t_mailb = 253,
|
|
/// Transfer mail agent records.
|
|
ns_t_maila = 254,
|
|
/// Wildcard match.
|
|
ns_t_any = 255,
|
|
/// Uniform Resource Identifier (RFC7553)
|
|
ns_t_uri = 256,
|
|
/// Certification Authority Authorization.
|
|
ns_t_caa = 257,
|
|
ns_t_max = 65536,
|
|
_,
|
|
};
|
|
pub const struct_ares_server_failover_options = extern struct {
|
|
retry_chance: c_ushort = 0,
|
|
retry_delay: usize = 0,
|
|
};
|
|
const ARES_EVSYS_DEFAULT: c_int = 0;
|
|
const ARES_EVSYS_WIN32: c_int = 1;
|
|
const ARES_EVSYS_EPOLL: c_int = 2;
|
|
const ARES_EVSYS_KQUEUE: c_int = 3;
|
|
const ARES_EVSYS_POLL: c_int = 4;
|
|
const ARES_EVSYS_SELECT: c_int = 5;
|
|
const ares_evsys_t = c_uint;
|
|
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: ?[*][*:0]u8 = null,
|
|
ndomains: c_int = 0,
|
|
lookups: ?[*:0]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,
|
|
udp_max_queries: c_int = 0,
|
|
maxtimeout: c_int = 0,
|
|
qcache_max_ttl: c_uint = 0,
|
|
evsys: ares_evsys_t = 0,
|
|
server_failover_opts: struct_ares_server_failover_options = @import("std").mem.zeroes(struct_ares_server_failover_options),
|
|
};
|
|
|
|
pub const struct_hostent = extern struct {
|
|
h_name: ?[*:0]u8,
|
|
h_aliases: ?[*:null]?[*:0]u8,
|
|
h_addrtype: hostent_int,
|
|
h_length: hostent_int,
|
|
h_addr_list: ?[*:null]?[*:0]u8,
|
|
|
|
// hostent in glibc uses int for h_addrtype and h_length, whereas hostent in winsock2.h uses short.
|
|
const hostent_int = if (bun.Environment.isWindows) c_short else c_int;
|
|
|
|
pub fn toJSResponse(this: *struct_hostent, _: std.mem.Allocator, globalThis: *jsc.JSGlobalObject, comptime lookup_name: []const u8) bun.JSError!jsc.JSValue {
|
|
if (comptime strings.eqlComptime(lookup_name, "cname")) {
|
|
// A cname lookup always returns a single record but we follow the common API here.
|
|
if (this.h_name == null) {
|
|
return try jsc.JSValue.createEmptyArray(globalThis, 0);
|
|
}
|
|
return bun.String.toJSArray(globalThis, &[_]bun.String{bun.String.borrowUTF8(this.h_name.?[0..bun.len(this.h_name.?)])});
|
|
}
|
|
|
|
if (this.h_aliases == null) {
|
|
return try jsc.JSValue.createEmptyArray(globalThis, 0);
|
|
}
|
|
|
|
var count: u32 = 0;
|
|
while (this.h_aliases.?[count] != null) {
|
|
count += 1;
|
|
}
|
|
|
|
const array = try jsc.JSValue.createEmptyArray(globalThis, count);
|
|
count = 0;
|
|
|
|
while (this.h_aliases.?[count]) |alias| {
|
|
const alias_len = bun.len(alias);
|
|
const alias_slice = alias[0..alias_len];
|
|
try array.putIndex(globalThis, count, jsc.ZigString.fromUTF8(alias_slice).toJS(globalThis));
|
|
count += 1;
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
pub fn Callback(comptime Type: type) type {
|
|
return fn (*Type, status: ?Error, timeouts: i32, results: ?*struct_hostent) void;
|
|
}
|
|
|
|
pub fn hostCallbackWrapper(
|
|
comptime Type: type,
|
|
comptime function: Callback(Type),
|
|
) ares_host_callback {
|
|
return &struct {
|
|
pub fn handle(ctx: ?*anyopaque, status: c_int, timeouts: c_int, hostent: ?*struct_hostent) callconv(.C) void {
|
|
const this = bun.cast(*Type, ctx.?);
|
|
if (status != ARES_SUCCESS) {
|
|
function(this, Error.get(status), timeouts, null);
|
|
return;
|
|
}
|
|
function(this, null, timeouts, hostent);
|
|
}
|
|
}.handle;
|
|
}
|
|
|
|
pub fn callbackWrapper(
|
|
comptime lookup_name: []const u8,
|
|
comptime Type: type,
|
|
comptime function: Callback(Type),
|
|
) ares_callback {
|
|
return &struct {
|
|
pub fn handle(ctx: ?*anyopaque, status: c_int, timeouts: c_int, buffer: [*c]u8, buffer_length: c_int) callconv(.C) void {
|
|
const this = bun.cast(*Type, ctx.?);
|
|
if (status != ARES_SUCCESS) {
|
|
function(this, Error.get(status), timeouts, null);
|
|
return;
|
|
}
|
|
|
|
var start: [*c]struct_hostent = undefined;
|
|
if (comptime strings.eqlComptime(lookup_name, "cname")) {
|
|
var addrttls: [256]struct_ares_addrttl = undefined;
|
|
var naddrttls: i32 = 256;
|
|
|
|
const result = ares_parse_a_reply(buffer, buffer_length, &start, &addrttls, &naddrttls);
|
|
if (result != ARES_SUCCESS) {
|
|
function(this, Error.get(result), timeouts, null);
|
|
return;
|
|
}
|
|
function(this, null, timeouts, start);
|
|
} else if (comptime strings.eqlComptime(lookup_name, "ns")) {
|
|
const result = ares_parse_ns_reply(buffer, buffer_length, &start);
|
|
if (result != ARES_SUCCESS) {
|
|
function(this, Error.get(result), timeouts, null);
|
|
return;
|
|
}
|
|
function(this, null, timeouts, start);
|
|
} else if (comptime strings.eqlComptime(lookup_name, "ptr")) {
|
|
const result = ares_parse_ptr_reply(buffer, buffer_length, null, 0, AF.INET, &start);
|
|
if (result != ARES_SUCCESS) {
|
|
function(this, Error.get(result), timeouts, null);
|
|
return;
|
|
}
|
|
function(this, null, timeouts, start);
|
|
} else {
|
|
@compileError(std.fmt.comptimePrint("Unsupported struct_hostent record type: {s}", .{lookup_name}));
|
|
}
|
|
}
|
|
}.handle;
|
|
}
|
|
|
|
pub fn deinit(this: *struct_hostent) void {
|
|
ares_free_hostent(this);
|
|
}
|
|
};
|
|
|
|
pub const hostent_with_ttls = struct {
|
|
hostent: *struct_hostent,
|
|
ttls: [256]c_int = [_]c_int{-1} ** 256,
|
|
|
|
pub fn toJSResponse(this: *hostent_with_ttls, _: std.mem.Allocator, globalThis: *jsc.JSGlobalObject, comptime lookup_name: []const u8) bun.JSError!jsc.JSValue {
|
|
if (comptime strings.eqlComptime(lookup_name, "a") or strings.eqlComptime(lookup_name, "aaaa")) {
|
|
if (this.hostent.h_addr_list == null) {
|
|
return try jsc.JSValue.createEmptyArray(globalThis, 0);
|
|
}
|
|
|
|
var count: u32 = 0;
|
|
while (this.hostent.h_addr_list.?[count] != null) {
|
|
count += 1;
|
|
}
|
|
|
|
const array = try jsc.JSValue.createEmptyArray(globalThis, count);
|
|
count = 0;
|
|
|
|
const addressKey = jsc.ZigString.static("address").withEncoding();
|
|
const ttlKey = jsc.ZigString.static("ttl").withEncoding();
|
|
|
|
while (this.hostent.h_addr_list.?[count]) |addr| : (count += 1) {
|
|
const addrString = (if (this.hostent.h_addrtype == AF.INET6)
|
|
bun.dns.addressToJS(&std.net.Address.initIp6(addr[0..16].*, 0, 0, 0), globalThis)
|
|
else
|
|
bun.dns.addressToJS(&std.net.Address.initIp4(addr[0..4].*, 0), globalThis)) catch return globalThis.throwOutOfMemoryValue();
|
|
|
|
const ttl: ?c_int = if (count < this.ttls.len) this.ttls[count] else null;
|
|
const resultObject = try jsc.JSValue.createObject2(globalThis, &addressKey, &ttlKey, addrString, if (ttl) |val| .jsNumber(val) else .js_undefined);
|
|
try array.putIndex(globalThis, count, resultObject);
|
|
}
|
|
|
|
return array;
|
|
} else {
|
|
@compileError(std.fmt.comptimePrint("Unsupported hostent_with_ttls record type: {s}", .{lookup_name}));
|
|
}
|
|
}
|
|
|
|
pub fn Callback(comptime Type: type) type {
|
|
return fn (*Type, status: ?Error, timeouts: i32, results: ?*hostent_with_ttls) void;
|
|
}
|
|
|
|
pub fn hostCallbackWrapper(
|
|
comptime Type: type,
|
|
comptime function: Callback(Type),
|
|
) ares_host_callback {
|
|
return &struct {
|
|
pub fn handle(ctx: ?*anyopaque, status: c_int, timeouts: c_int, hostent: ?*hostent_with_ttls) callconv(.C) void {
|
|
const this = bun.cast(*Type, ctx.?);
|
|
if (status != ARES_SUCCESS) {
|
|
function(this, Error.get(status), timeouts, null);
|
|
return;
|
|
}
|
|
function(this, null, timeouts, hostent);
|
|
}
|
|
}.handle;
|
|
}
|
|
|
|
pub fn callbackWrapper(
|
|
comptime lookup_name: []const u8,
|
|
comptime Type: type,
|
|
comptime function: Callback(Type),
|
|
) ares_callback {
|
|
return &struct {
|
|
pub fn handle(ctx: ?*anyopaque, status: c_int, timeouts: c_int, buffer: [*c]u8, buffer_length: c_int) callconv(.C) void {
|
|
const this = bun.cast(*Type, ctx.?);
|
|
if (status != ARES_SUCCESS) {
|
|
function(this, Error.get(status), timeouts, null);
|
|
return;
|
|
}
|
|
|
|
switch (parse(lookup_name, buffer, buffer_length)) {
|
|
.result => |result| function(this, null, timeouts, result),
|
|
.err => |err| function(this, err, timeouts, null),
|
|
}
|
|
}
|
|
}.handle;
|
|
}
|
|
|
|
pub fn parse(comptime lookup_name: []const u8, buffer: [*c]u8, buffer_length: c_int) jsc.Node.Maybe(*hostent_with_ttls, Error) {
|
|
var start: ?*struct_hostent = null;
|
|
|
|
if (comptime strings.eqlComptime(lookup_name, "a")) {
|
|
var addrttls: [256]struct_ares_addrttl = undefined;
|
|
var naddrttls: c_int = 256;
|
|
|
|
const result = ares_parse_a_reply(buffer, buffer_length, &start, &addrttls, &naddrttls);
|
|
if (result != ARES_SUCCESS) {
|
|
return .{ .err = Error.get(result).? };
|
|
}
|
|
var with_ttls = bun.handleOom(bun.default_allocator.create(hostent_with_ttls));
|
|
with_ttls.hostent = start.?;
|
|
for (addrttls[0..@intCast(naddrttls)], 0..) |ttl, i| {
|
|
with_ttls.ttls[i] = ttl.ttl;
|
|
}
|
|
return .{ .result = with_ttls };
|
|
}
|
|
|
|
if (comptime strings.eqlComptime(lookup_name, "aaaa")) {
|
|
var addr6ttls: [256]struct_ares_addr6ttl = undefined;
|
|
var naddr6ttls: c_int = 256;
|
|
|
|
const result = ares_parse_aaaa_reply(buffer, buffer_length, &start, &addr6ttls, &naddr6ttls);
|
|
if (result != ARES_SUCCESS) {
|
|
return .{ .err = Error.get(result).? };
|
|
}
|
|
var with_ttls = bun.handleOom(bun.default_allocator.create(hostent_with_ttls));
|
|
with_ttls.hostent = start.?;
|
|
for (addr6ttls[0..@intCast(naddr6ttls)], 0..) |ttl, i| {
|
|
with_ttls.ttls[i] = ttl.ttl;
|
|
}
|
|
return .{ .result = with_ttls };
|
|
}
|
|
|
|
@compileError(std.fmt.comptimePrint("Unsupported hostent_with_ttls record type: {s}", .{lookup_name}));
|
|
}
|
|
|
|
pub fn deinit(this: *hostent_with_ttls) void {
|
|
this.hostent.deinit();
|
|
bun.default_allocator.destroy(this);
|
|
}
|
|
};
|
|
|
|
pub const struct_nameinfo = extern struct {
|
|
node: [*c]u8,
|
|
service: [*c]u8,
|
|
|
|
pub fn toJSResponse(this: *struct_nameinfo, _: std.mem.Allocator, globalThis: *jsc.JSGlobalObject) bun.JSError!jsc.JSValue {
|
|
const array = try jsc.JSValue.createEmptyArray(globalThis, 2); // [node, service]
|
|
|
|
if (this.node != null) {
|
|
const node_len = bun.len(this.node);
|
|
const node_slice = this.node[0..node_len];
|
|
try array.putIndex(globalThis, 0, jsc.ZigString.fromUTF8(node_slice).toJS(globalThis));
|
|
} else {
|
|
try array.putIndex(globalThis, 0, .js_undefined);
|
|
}
|
|
|
|
if (this.service != null) {
|
|
const service_len = bun.len(this.service);
|
|
const service_slice = this.service[0..service_len];
|
|
try array.putIndex(globalThis, 1, jsc.ZigString.fromUTF8(service_slice).toJS(globalThis));
|
|
} else {
|
|
try array.putIndex(globalThis, 1, .js_undefined);
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
pub fn Callback(comptime Type: type) type {
|
|
return fn (*Type, status: ?Error, timeouts: i32, node: ?struct_nameinfo) void;
|
|
}
|
|
|
|
pub fn CallbackWrapper(
|
|
comptime Type: type,
|
|
comptime function: Callback(Type),
|
|
) ares_nameinfo_callback {
|
|
return &struct {
|
|
pub fn handle(ctx: ?*anyopaque, status: c_int, timeouts: c_int, node: [*c]u8, service: [*c]u8) callconv(.C) void {
|
|
const this = bun.cast(*Type, ctx.?);
|
|
if (status != ARES_SUCCESS) {
|
|
function(this, Error.get(status), timeouts, null);
|
|
return;
|
|
}
|
|
function(this, null, timeouts, .{ .node = node, .service = service });
|
|
return;
|
|
}
|
|
}.handle;
|
|
}
|
|
};
|
|
|
|
pub const struct_timeval = std.posix.timeval;
|
|
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,
|
|
|
|
pub fn toJSArray(addr_info: *AddrInfo, globalThis: *jsc.JSGlobalObject) bun.JSError!jsc.JSValue {
|
|
var node = addr_info.node orelse return try jsc.JSValue.createEmptyArray(globalThis, 0);
|
|
const array = try jsc.JSValue.createEmptyArray(globalThis, node.count());
|
|
|
|
{
|
|
var j: u32 = 0;
|
|
var current: ?*AddrInfo_node = addr_info.node;
|
|
while (current) |this_node| : (current = this_node.next) {
|
|
try array.putIndex(
|
|
globalThis,
|
|
j,
|
|
try GetAddrInfo.Result.toJS(
|
|
&.{
|
|
.address = switch (this_node.family) {
|
|
AF.INET => std.net.Address{ .in = .{ .sa = bun.cast(*const std.posix.sockaddr.in, this_node.addr.?).* } },
|
|
AF.INET6 => std.net.Address{ .in6 = .{ .sa = bun.cast(*const std.posix.sockaddr.in6, this_node.addr.?).* } },
|
|
else => unreachable,
|
|
},
|
|
.ttl = this_node.ttl,
|
|
},
|
|
globalThis,
|
|
),
|
|
);
|
|
j += 1;
|
|
}
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
pub inline fn name(this: *const AddrInfo) []const u8 {
|
|
const name_ = this.name_ orelse return "";
|
|
return bun.span(name_);
|
|
}
|
|
|
|
pub inline fn cnames(this: *const AddrInfo) []const AddrInfo_node {
|
|
const 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 {
|
|
const 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 ChannelOptions = struct {
|
|
timeout: ?i32 = null,
|
|
tries: ?i32 = null,
|
|
};
|
|
|
|
pub const Channel = opaque {
|
|
pub fn init(comptime Container: type, this: *Container, options: ChannelOptions) ?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 {
|
|
const container = bun.cast(*Container, ctx.?);
|
|
Container.onDNSSocketState(container, 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 = @as(*anyopaque, @ptrCast(this));
|
|
opts.timeout = options.timeout orelse -1;
|
|
opts.tries = options.tries orelse 4;
|
|
|
|
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;
|
|
}
|
|
|
|
pub fn deinit(this: *Channel) void {
|
|
ares_destroy(this);
|
|
}
|
|
|
|
///
|
|
///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: {
|
|
const len = @min(host.len, host_buf.len - 1);
|
|
@memcpy(host_buf[0..len], host[0..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)], 0..) |hint, i| {
|
|
hints_buf[i] = hint;
|
|
}
|
|
const 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 fn resolve(this: *Channel, name: []const u8, comptime lookup_name: []const u8, comptime Type: type, ctx: *Type, comptime cares_type: type, comptime callback: cares_type.Callback(Type)) void {
|
|
if (name.len >= 1023 or (name.len == 0 and !(bun.strings.eqlComptime(lookup_name, "ns") or bun.strings.eqlComptime(lookup_name, "soa")))) {
|
|
return cares_type.callbackWrapper(lookup_name, Type, callback).?(ctx, ARES_EBADNAME, 0, null, 0);
|
|
}
|
|
|
|
var name_buf: [1024]u8 = undefined;
|
|
const name_ptr: [*:0]const u8 = brk: {
|
|
const len = @min(name.len, name_buf.len - 1);
|
|
@memcpy(name_buf[0..len], name[0..len]);
|
|
|
|
name_buf[len] = 0;
|
|
break :brk name_buf[0..len :0];
|
|
};
|
|
|
|
const field_name = comptime std.fmt.comptimePrint("ns_t_{s}", .{lookup_name});
|
|
ares_query(this, name_ptr, NSClass.ns_c_in, @field(NSType, field_name), cares_type.callbackWrapper(lookup_name, Type, callback), ctx);
|
|
}
|
|
|
|
pub fn getHostByAddr(this: *Channel, ip_addr: []const u8, comptime Type: type, ctx: *Type, comptime callback: struct_hostent.Callback(Type)) void {
|
|
// "0000:0000:0000:0000:0000:ffff:192.168.100.228".length = 45
|
|
const buf_size = 46;
|
|
var addr_buf: [buf_size]u8 = undefined;
|
|
const addr_ptr: ?[*:0]const u8 = brk: {
|
|
if (ip_addr.len == 0 or ip_addr.len >= buf_size) {
|
|
break :brk null;
|
|
}
|
|
const len = @min(ip_addr.len, addr_buf.len - 1);
|
|
@memcpy(addr_buf[0..len], ip_addr[0..len]);
|
|
|
|
addr_buf[len] = 0;
|
|
break :brk addr_buf[0..len :0];
|
|
};
|
|
|
|
// https://c-ares.org/ares_inet_pton.html
|
|
// https://github.com/c-ares/c-ares/blob/7f3262312f246556d8c1bdd8ccc1844847f42787/src/lib/ares_gethostbyaddr.c#L71-L72
|
|
// `ares_inet_pton` allows passing raw bytes as `dst`,
|
|
// which can avoid the use of `struct_in_addr` to reduce extra bytes.
|
|
var addr: [16]u8 = undefined;
|
|
if (addr_ptr != null) {
|
|
if (ares_inet_pton(AF.INET, addr_ptr, &addr) > 0) {
|
|
ares_gethostbyaddr(this, &addr, 4, AF.INET, struct_hostent.hostCallbackWrapper(Type, callback), ctx);
|
|
return;
|
|
} else if (ares_inet_pton(AF.INET6, addr_ptr, &addr) > 0) {
|
|
return ares_gethostbyaddr(this, &addr, 16, AF.INET6, struct_hostent.hostCallbackWrapper(Type, callback), ctx);
|
|
}
|
|
}
|
|
struct_hostent.hostCallbackWrapper(Type, callback).?(ctx, ARES_ENOTIMP, 0, null);
|
|
}
|
|
|
|
// https://c-ares.org/ares_getnameinfo.html
|
|
pub fn getNameInfo(this: *Channel, sa: *std.posix.sockaddr, comptime Type: type, ctx: *Type, comptime callback: struct_nameinfo.Callback(Type)) void {
|
|
return ares_getnameinfo(
|
|
this,
|
|
sa,
|
|
if (sa.*.family == AF.INET) @sizeOf(std.posix.sockaddr.in) else @sizeOf(std.posix.sockaddr.in6),
|
|
// node returns ENOTFOUND for addresses like 255.255.255.255:80
|
|
// So, it requires setting the ARES_NI_NAMEREQD flag
|
|
ARES_NI_NAMEREQD | ARES_NI_LOOKUPHOST | ARES_NI_LOOKUPSERVICE,
|
|
struct_nameinfo.CallbackWrapper(Type, callback),
|
|
ctx,
|
|
);
|
|
}
|
|
|
|
pub inline fn process(this: *Channel, fd: ares_socket_t, 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.Value(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: NSClass, @"type": NSType, 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;
|
|
pub extern fn ares_queue_active_queries(channel: *const Channel) usize;
|
|
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: u32,
|
|
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: ?*struct_ares_caa_reply,
|
|
critical: c_int,
|
|
property: [*c]u8,
|
|
plength: usize,
|
|
value: [*c]u8,
|
|
length: usize,
|
|
|
|
pub fn toJSResponse(this: *struct_ares_caa_reply, parent_allocator: std.mem.Allocator, globalThis: *jsc.JSGlobalObject, comptime _: []const u8) bun.JSError!jsc.JSValue {
|
|
var stack = std.heap.stackFallback(2048, parent_allocator);
|
|
var arena = bun.ArenaAllocator.init(stack.get());
|
|
defer arena.deinit();
|
|
|
|
const allocator = arena.allocator();
|
|
var count: usize = 0;
|
|
var caa: ?*struct_ares_caa_reply = this;
|
|
while (caa != null) : (caa = caa.?.next) {
|
|
count += 1;
|
|
}
|
|
|
|
const array = try jsc.JSValue.createEmptyArray(globalThis, count);
|
|
|
|
caa = this;
|
|
var i: u32 = 0;
|
|
while (caa != null) {
|
|
var node = caa.?;
|
|
try array.putIndex(globalThis, i, node.toJS(globalThis, allocator));
|
|
caa = node.next;
|
|
i += 1;
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
pub fn toJS(this: *struct_ares_caa_reply, globalThis: *jsc.JSGlobalObject, _: std.mem.Allocator) jsc.JSValue {
|
|
var obj = jsc.JSValue.createEmptyObject(globalThis, 2);
|
|
|
|
obj.put(globalThis, jsc.ZigString.static("critical"), jsc.JSValue.jsNumber(this.critical));
|
|
|
|
const property = this.property[0..this.plength];
|
|
const value = this.value[0..this.length];
|
|
const property_str = jsc.ZigString.fromUTF8(property);
|
|
obj.put(globalThis, &property_str, jsc.ZigString.fromUTF8(value).toJS(globalThis));
|
|
|
|
return obj;
|
|
}
|
|
|
|
pub fn Callback(comptime Type: type) type {
|
|
return fn (*Type, status: ?Error, timeouts: i32, results: ?*struct_ares_caa_reply) void;
|
|
}
|
|
|
|
pub fn callbackWrapper(
|
|
comptime _: []const u8,
|
|
comptime Type: type,
|
|
comptime function: Callback(Type),
|
|
) ares_callback {
|
|
return &struct {
|
|
pub fn handle(ctx: ?*anyopaque, status: c_int, timeouts: c_int, buffer: [*c]u8, buffer_length: c_int) callconv(.C) void {
|
|
const this = bun.cast(*Type, ctx.?);
|
|
if (status != ARES_SUCCESS) {
|
|
function(this, Error.get(status), timeouts, null);
|
|
return;
|
|
}
|
|
|
|
var start: [*c]struct_ares_caa_reply = undefined;
|
|
const result = ares_parse_caa_reply(buffer, buffer_length, &start);
|
|
if (result != ARES_SUCCESS) {
|
|
function(this, Error.get(result), timeouts, null);
|
|
return;
|
|
}
|
|
function(this, null, timeouts, start);
|
|
}
|
|
}.handle;
|
|
}
|
|
|
|
pub fn deinit(this: *struct_ares_caa_reply) void {
|
|
ares_free_data(this);
|
|
}
|
|
};
|
|
pub const struct_ares_srv_reply = extern struct {
|
|
next: ?*struct_ares_srv_reply,
|
|
host: [*c]u8,
|
|
priority: c_ushort,
|
|
weight: c_ushort,
|
|
port: c_ushort,
|
|
|
|
pub fn toJSResponse(this: *struct_ares_srv_reply, parent_allocator: std.mem.Allocator, globalThis: *jsc.JSGlobalObject, comptime _: []const u8) bun.JSError!jsc.JSValue {
|
|
var stack = std.heap.stackFallback(2048, parent_allocator);
|
|
var arena = bun.ArenaAllocator.init(stack.get());
|
|
defer arena.deinit();
|
|
|
|
const allocator = arena.allocator();
|
|
var count: usize = 0;
|
|
var srv: ?*struct_ares_srv_reply = this;
|
|
while (srv != null) : (srv = srv.?.next) {
|
|
count += 1;
|
|
}
|
|
|
|
const array = try jsc.JSValue.createEmptyArray(globalThis, count);
|
|
|
|
srv = this;
|
|
var i: u32 = 0;
|
|
while (srv != null) {
|
|
var node = srv.?;
|
|
try array.putIndex(globalThis, i, node.toJS(globalThis, allocator));
|
|
srv = node.next;
|
|
i += 1;
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
pub fn toJS(this: *struct_ares_srv_reply, globalThis: *jsc.JSGlobalObject, _: std.mem.Allocator) jsc.JSValue {
|
|
const obj = jsc.JSValue.createEmptyObject(globalThis, 4);
|
|
// {
|
|
// priority: 10,
|
|
// weight: 5,
|
|
// port: 21223,
|
|
// name: 'service.example.com'
|
|
// }
|
|
|
|
obj.put(globalThis, jsc.ZigString.static("priority"), jsc.JSValue.jsNumber(this.priority));
|
|
obj.put(globalThis, jsc.ZigString.static("weight"), jsc.JSValue.jsNumber(this.weight));
|
|
obj.put(globalThis, jsc.ZigString.static("port"), jsc.JSValue.jsNumber(this.port));
|
|
|
|
const len = bun.len(this.host);
|
|
const host = this.host[0..len];
|
|
obj.put(globalThis, jsc.ZigString.static("name"), jsc.ZigString.fromUTF8(host).toJS(globalThis));
|
|
|
|
return obj;
|
|
}
|
|
|
|
pub fn Callback(comptime Type: type) type {
|
|
return fn (*Type, status: ?Error, timeouts: i32, results: ?*struct_ares_srv_reply) void;
|
|
}
|
|
|
|
pub fn callbackWrapper(
|
|
comptime _: []const u8,
|
|
comptime Type: type,
|
|
comptime function: Callback(Type),
|
|
) ares_callback {
|
|
return &struct {
|
|
pub fn handleSrv(ctx: ?*anyopaque, status: c_int, timeouts: c_int, buffer: [*c]u8, buffer_length: c_int) callconv(.C) void {
|
|
const this = bun.cast(*Type, ctx.?);
|
|
if (status != ARES_SUCCESS) {
|
|
function(this, Error.get(status), timeouts, null);
|
|
return;
|
|
}
|
|
|
|
var srv_start: [*c]struct_ares_srv_reply = undefined;
|
|
const result = ares_parse_srv_reply(buffer, buffer_length, &srv_start);
|
|
if (result != ARES_SUCCESS) {
|
|
function(this, Error.get(result), timeouts, null);
|
|
return;
|
|
}
|
|
function(this, null, timeouts, srv_start);
|
|
}
|
|
}.handleSrv;
|
|
}
|
|
|
|
pub fn deinit(this: *struct_ares_srv_reply) void {
|
|
ares_free_data(this);
|
|
}
|
|
};
|
|
pub const struct_ares_mx_reply = extern struct {
|
|
next: ?*struct_ares_mx_reply,
|
|
host: [*c]u8,
|
|
priority: c_ushort,
|
|
|
|
pub fn toJSResponse(this: *struct_ares_mx_reply, parent_allocator: std.mem.Allocator, globalThis: *jsc.JSGlobalObject, comptime _: []const u8) bun.JSError!jsc.JSValue {
|
|
var stack = std.heap.stackFallback(2048, parent_allocator);
|
|
var arena = bun.ArenaAllocator.init(stack.get());
|
|
defer arena.deinit();
|
|
|
|
const allocator = arena.allocator();
|
|
var count: usize = 0;
|
|
var mx: ?*struct_ares_mx_reply = this;
|
|
while (mx != null) : (mx = mx.?.next) {
|
|
count += 1;
|
|
}
|
|
|
|
const array = try jsc.JSValue.createEmptyArray(globalThis, count);
|
|
|
|
mx = this;
|
|
var i: u32 = 0;
|
|
while (mx != null) {
|
|
var node = mx.?;
|
|
try array.putIndex(globalThis, i, node.toJS(globalThis, allocator));
|
|
mx = node.next;
|
|
i += 1;
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
pub fn toJS(this: *struct_ares_mx_reply, globalThis: *jsc.JSGlobalObject, _: std.mem.Allocator) jsc.JSValue {
|
|
const obj = jsc.JSValue.createEmptyObject(globalThis, 2);
|
|
obj.put(globalThis, jsc.ZigString.static("priority"), jsc.JSValue.jsNumber(this.priority));
|
|
|
|
const host_len = bun.len(this.host);
|
|
const host = this.host[0..host_len];
|
|
obj.put(globalThis, jsc.ZigString.static("exchange"), jsc.ZigString.fromUTF8(host).toJS(globalThis));
|
|
|
|
return obj;
|
|
}
|
|
|
|
pub fn Callback(comptime Type: type) type {
|
|
return fn (*Type, status: ?Error, timeouts: i32, results: ?*struct_ares_mx_reply) void;
|
|
}
|
|
|
|
pub fn callbackWrapper(
|
|
comptime _: []const u8,
|
|
comptime Type: type,
|
|
comptime function: Callback(Type),
|
|
) ares_callback {
|
|
return &struct {
|
|
pub fn handle(ctx: ?*anyopaque, status: c_int, timeouts: c_int, buffer: [*c]u8, buffer_length: c_int) callconv(.C) void {
|
|
const this = bun.cast(*Type, ctx.?);
|
|
if (status != ARES_SUCCESS) {
|
|
function(this, Error.get(status), timeouts, null);
|
|
return;
|
|
}
|
|
|
|
var start: [*c]struct_ares_mx_reply = undefined;
|
|
const result = ares_parse_mx_reply(buffer, buffer_length, &start);
|
|
if (result != ARES_SUCCESS) {
|
|
function(this, Error.get(result), timeouts, null);
|
|
return;
|
|
}
|
|
function(this, null, timeouts, start);
|
|
}
|
|
}.handle;
|
|
}
|
|
|
|
pub fn deinit(this: *struct_ares_mx_reply) void {
|
|
ares_free_data(this);
|
|
}
|
|
};
|
|
pub const struct_ares_txt_reply = extern struct {
|
|
next: ?*struct_ares_txt_reply,
|
|
txt: [*c]u8,
|
|
length: usize,
|
|
|
|
pub fn toJSResponse(this: *struct_ares_txt_reply, parent_allocator: std.mem.Allocator, globalThis: *jsc.JSGlobalObject, comptime _: []const u8) bun.JSError!jsc.JSValue {
|
|
var stack = std.heap.stackFallback(2048, parent_allocator);
|
|
var arena = bun.ArenaAllocator.init(stack.get());
|
|
defer arena.deinit();
|
|
|
|
const allocator = arena.allocator();
|
|
var count: usize = 0;
|
|
var txt: ?*struct_ares_txt_reply = this;
|
|
while (txt != null) : (txt = txt.?.next) {
|
|
count += 1;
|
|
}
|
|
|
|
const array = try jsc.JSValue.createEmptyArray(globalThis, count);
|
|
|
|
txt = this;
|
|
var i: u32 = 0;
|
|
while (txt != null) {
|
|
var node = txt.?;
|
|
try array.putIndex(globalThis, i, try node.toJS(globalThis, allocator));
|
|
txt = node.next;
|
|
i += 1;
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
pub fn toJS(this: *struct_ares_txt_reply, globalThis: *jsc.JSGlobalObject, _: std.mem.Allocator) bun.JSError!jsc.JSValue {
|
|
const array = try jsc.JSValue.createEmptyArray(globalThis, 1);
|
|
const value = this.txt[0..this.length];
|
|
try array.putIndex(globalThis, 0, jsc.ZigString.fromUTF8(value).toJS(globalThis));
|
|
return array;
|
|
}
|
|
|
|
pub fn toJSForAny(this: *struct_ares_txt_reply, _: std.mem.Allocator, globalThis: *jsc.JSGlobalObject, comptime _: []const u8) bun.JSError!jsc.JSValue {
|
|
var count: usize = 0;
|
|
var txt: ?*struct_ares_txt_reply = this;
|
|
while (txt != null) : (txt = txt.?.next) {
|
|
count += 1;
|
|
}
|
|
|
|
const array = try jsc.JSValue.createEmptyArray(globalThis, count);
|
|
|
|
txt = this;
|
|
var i: u32 = 0;
|
|
while (txt != null) : (txt = txt.?.next) {
|
|
var node = txt.?;
|
|
try array.putIndex(globalThis, i, jsc.ZigString.fromUTF8(node.txt[0..node.length]).toJS(globalThis));
|
|
i += 1;
|
|
}
|
|
|
|
return (try jsc.JSObject.create(.{
|
|
.entries = array,
|
|
}, globalThis)).toJS();
|
|
}
|
|
|
|
pub fn Callback(comptime Type: type) type {
|
|
return fn (*Type, status: ?Error, timeouts: i32, results: ?*struct_ares_txt_reply) void;
|
|
}
|
|
|
|
pub fn callbackWrapper(
|
|
comptime _: []const u8,
|
|
comptime Type: type,
|
|
comptime function: Callback(Type),
|
|
) ares_callback {
|
|
return &struct {
|
|
pub fn handleTxt(ctx: ?*anyopaque, status: c_int, timeouts: c_int, buffer: [*c]u8, buffer_length: c_int) callconv(.C) void {
|
|
const this = bun.cast(*Type, ctx.?);
|
|
if (status != ARES_SUCCESS) {
|
|
function(this, Error.get(status), timeouts, null);
|
|
return;
|
|
}
|
|
|
|
var srv_start: [*c]struct_ares_txt_reply = undefined;
|
|
const result = ares_parse_txt_reply(buffer, buffer_length, &srv_start);
|
|
if (result != ARES_SUCCESS) {
|
|
function(this, Error.get(result), timeouts, null);
|
|
return;
|
|
}
|
|
function(this, null, timeouts, srv_start);
|
|
}
|
|
}.handleTxt;
|
|
}
|
|
|
|
pub fn deinit(this: *struct_ares_txt_reply) void {
|
|
ares_free_data(this);
|
|
}
|
|
};
|
|
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: ?*struct_ares_naptr_reply,
|
|
flags: [*c]u8,
|
|
service: [*c]u8,
|
|
regexp: [*c]u8,
|
|
replacement: [*c]u8,
|
|
order: c_ushort,
|
|
preference: c_ushort,
|
|
|
|
pub fn toJSResponse(this: *struct_ares_naptr_reply, parent_allocator: std.mem.Allocator, globalThis: *jsc.JSGlobalObject, comptime _: []const u8) bun.JSError!jsc.JSValue {
|
|
var stack = std.heap.stackFallback(2048, parent_allocator);
|
|
var arena = bun.ArenaAllocator.init(stack.get());
|
|
defer arena.deinit();
|
|
|
|
const allocator = arena.allocator();
|
|
var count: usize = 0;
|
|
var naptr: ?*struct_ares_naptr_reply = this;
|
|
while (naptr != null) : (naptr = naptr.?.next) {
|
|
count += 1;
|
|
}
|
|
|
|
const array = try jsc.JSValue.createEmptyArray(globalThis, count);
|
|
|
|
naptr = this;
|
|
var i: u32 = 0;
|
|
while (naptr != null) {
|
|
var node = naptr.?;
|
|
try array.putIndex(globalThis, i, node.toJS(globalThis, allocator));
|
|
naptr = node.next;
|
|
i += 1;
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
pub fn toJS(this: *struct_ares_naptr_reply, globalThis: *jsc.JSGlobalObject, _: std.mem.Allocator) jsc.JSValue {
|
|
const obj = jsc.JSValue.createEmptyObject(globalThis, 6);
|
|
|
|
obj.put(globalThis, jsc.ZigString.static("preference"), jsc.JSValue.jsNumber(this.preference));
|
|
obj.put(globalThis, jsc.ZigString.static("order"), jsc.JSValue.jsNumber(this.order));
|
|
|
|
const flags_len = bun.len(this.flags);
|
|
const flags = this.flags[0..flags_len];
|
|
obj.put(globalThis, jsc.ZigString.static("flags"), jsc.ZigString.fromUTF8(flags).toJS(globalThis));
|
|
|
|
const service_len = bun.len(this.service);
|
|
const service = this.service[0..service_len];
|
|
obj.put(globalThis, jsc.ZigString.static("service"), jsc.ZigString.fromUTF8(service).toJS(globalThis));
|
|
|
|
const regexp_len = bun.len(this.regexp);
|
|
const regexp = this.regexp[0..regexp_len];
|
|
obj.put(globalThis, jsc.ZigString.static("regexp"), jsc.ZigString.fromUTF8(regexp).toJS(globalThis));
|
|
|
|
const replacement_len = bun.len(this.replacement);
|
|
const replacement = this.replacement[0..replacement_len];
|
|
obj.put(globalThis, jsc.ZigString.static("replacement"), jsc.ZigString.fromUTF8(replacement).toJS(globalThis));
|
|
|
|
return obj;
|
|
}
|
|
|
|
pub fn Callback(comptime Type: type) type {
|
|
return fn (*Type, status: ?Error, timeouts: i32, results: ?*struct_ares_naptr_reply) void;
|
|
}
|
|
|
|
pub fn callbackWrapper(
|
|
comptime _: []const u8,
|
|
comptime Type: type,
|
|
comptime function: Callback(Type),
|
|
) ares_callback {
|
|
return &struct {
|
|
pub fn handleNaptr(ctx: ?*anyopaque, status: c_int, timeouts: c_int, buffer: [*c]u8, buffer_length: c_int) callconv(.C) void {
|
|
const this = bun.cast(*Type, ctx.?);
|
|
if (status != ARES_SUCCESS) {
|
|
function(this, Error.get(status), timeouts, null);
|
|
return;
|
|
}
|
|
|
|
var naptr_start: [*c]struct_ares_naptr_reply = undefined;
|
|
const result = ares_parse_naptr_reply(buffer, buffer_length, &naptr_start);
|
|
if (result != ARES_SUCCESS) {
|
|
function(this, Error.get(result), timeouts, null);
|
|
return;
|
|
}
|
|
function(this, null, timeouts, naptr_start);
|
|
}
|
|
}.handleNaptr;
|
|
}
|
|
|
|
pub fn deinit(this: *struct_ares_naptr_reply) void {
|
|
ares_free_data(this);
|
|
}
|
|
};
|
|
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 fn toJSResponse(this: *struct_ares_soa_reply, parent_allocator: std.mem.Allocator, globalThis: *jsc.JSGlobalObject, comptime _: []const u8) bun.JSError!jsc.JSValue {
|
|
var stack = std.heap.stackFallback(2048, parent_allocator);
|
|
var arena = bun.ArenaAllocator.init(stack.get());
|
|
defer arena.deinit();
|
|
|
|
const allocator = arena.allocator();
|
|
|
|
return this.toJS(globalThis, allocator);
|
|
}
|
|
|
|
pub fn toJS(this: *struct_ares_soa_reply, globalThis: *jsc.JSGlobalObject, _: std.mem.Allocator) jsc.JSValue {
|
|
const obj = jsc.JSValue.createEmptyObject(globalThis, 7);
|
|
|
|
obj.put(globalThis, jsc.ZigString.static("serial"), jsc.JSValue.jsNumber(this.serial));
|
|
obj.put(globalThis, jsc.ZigString.static("refresh"), jsc.JSValue.jsNumber(this.refresh));
|
|
obj.put(globalThis, jsc.ZigString.static("retry"), jsc.JSValue.jsNumber(this.retry));
|
|
obj.put(globalThis, jsc.ZigString.static("expire"), jsc.JSValue.jsNumber(this.expire));
|
|
obj.put(globalThis, jsc.ZigString.static("minttl"), jsc.JSValue.jsNumber(this.minttl));
|
|
|
|
const nsname_len = bun.len(this.nsname);
|
|
const nsname = this.nsname[0..nsname_len];
|
|
obj.put(globalThis, jsc.ZigString.static("nsname"), jsc.ZigString.fromUTF8(nsname).toJS(globalThis));
|
|
|
|
const hostmaster_len = bun.len(this.hostmaster);
|
|
const hostmaster = this.hostmaster[0..hostmaster_len];
|
|
obj.put(globalThis, jsc.ZigString.static("hostmaster"), jsc.ZigString.fromUTF8(hostmaster).toJS(globalThis));
|
|
|
|
return obj;
|
|
}
|
|
|
|
pub fn Callback(comptime Type: type) type {
|
|
return fn (*Type, status: ?Error, timeouts: i32, results: ?*struct_ares_soa_reply) void;
|
|
}
|
|
|
|
pub fn callbackWrapper(
|
|
comptime _: []const u8,
|
|
comptime Type: type,
|
|
comptime function: Callback(Type),
|
|
) ares_callback {
|
|
return &struct {
|
|
pub fn handleSoa(ctx: ?*anyopaque, status: c_int, timeouts: c_int, buffer: [*c]u8, buffer_length: c_int) callconv(.C) void {
|
|
const this = bun.cast(*Type, ctx.?);
|
|
if (status != ARES_SUCCESS) {
|
|
function(this, Error.get(status), timeouts, null);
|
|
return;
|
|
}
|
|
|
|
var soa_start: [*c]struct_ares_soa_reply = undefined;
|
|
const result = ares_parse_soa_reply(buffer, buffer_length, &soa_start);
|
|
if (result != ARES_SUCCESS) {
|
|
function(this, Error.get(result), timeouts, null);
|
|
return;
|
|
}
|
|
function(this, null, timeouts, soa_start);
|
|
}
|
|
}.handleSoa;
|
|
}
|
|
|
|
pub fn deinit(this: *struct_ares_soa_reply) void {
|
|
ares_free_data(this);
|
|
}
|
|
};
|
|
|
|
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 const struct_any_reply = struct {
|
|
a_reply: ?*hostent_with_ttls = null,
|
|
aaaa_reply: ?*hostent_with_ttls = null,
|
|
mx_reply: ?*struct_ares_mx_reply = null,
|
|
ns_reply: ?*struct_hostent = null,
|
|
txt_reply: ?*struct_ares_txt_reply = null,
|
|
srv_reply: ?*struct_ares_srv_reply = null,
|
|
ptr_reply: ?*struct_hostent = null,
|
|
naptr_reply: ?*struct_ares_naptr_reply = null,
|
|
soa_reply: ?*struct_ares_soa_reply = null,
|
|
caa_reply: ?*struct_ares_caa_reply = null,
|
|
|
|
pub fn toJSResponse(this: *struct_any_reply, parent_allocator: std.mem.Allocator, globalThis: *jsc.JSGlobalObject, comptime _: []const u8) bun.JSError!jsc.JSValue {
|
|
var stack = std.heap.stackFallback(2048, parent_allocator);
|
|
var arena = bun.ArenaAllocator.init(stack.get());
|
|
defer arena.deinit();
|
|
|
|
const allocator = arena.allocator();
|
|
|
|
return this.toJS(globalThis, allocator);
|
|
}
|
|
|
|
fn append(globalThis: *jsc.JSGlobalObject, array: jsc.JSValue, i: *u32, response: jsc.JSValue, comptime lookup_name: []const u8) bun.JSError!void {
|
|
const transformed = if (response.isString())
|
|
(try jsc.JSObject.create(.{
|
|
.value = response,
|
|
}, globalThis)).toJS()
|
|
else blk: {
|
|
bun.assert(response.isObject());
|
|
break :blk response;
|
|
};
|
|
|
|
var upper = comptime lookup_name[0..lookup_name.len].*;
|
|
inline for (&upper) |*char| {
|
|
char.* = std.ascii.toUpper(char.*);
|
|
}
|
|
|
|
transformed.put(globalThis, "type", bun.String.ascii(&upper).toJS(globalThis));
|
|
try array.putIndex(globalThis, i.*, transformed);
|
|
i.* += 1;
|
|
}
|
|
|
|
fn appendAll(globalThis: *jsc.JSGlobalObject, allocator: std.mem.Allocator, array: jsc.JSValue, i: *u32, reply: anytype, comptime lookup_name: []const u8) bun.JSError!void {
|
|
const response: jsc.JSValue = try if (comptime @hasDecl(@TypeOf(reply.*), "toJSForAny"))
|
|
reply.toJSForAny(allocator, globalThis, lookup_name)
|
|
else
|
|
reply.toJSResponse(allocator, globalThis, lookup_name);
|
|
|
|
if (response.isArray()) {
|
|
var iterator = try response.arrayIterator(globalThis);
|
|
while (try iterator.next()) |item| {
|
|
try append(globalThis, array, i, item, lookup_name);
|
|
}
|
|
} else {
|
|
try append(globalThis, array, i, response, lookup_name);
|
|
}
|
|
}
|
|
|
|
pub fn toJS(this: *struct_any_reply, globalThis: *jsc.JSGlobalObject, allocator: std.mem.Allocator) bun.JSError!jsc.JSValue {
|
|
const array = try jsc.JSValue.createEmptyArray(globalThis, blk: {
|
|
var len: usize = 0;
|
|
inline for (comptime @typeInfo(struct_any_reply).@"struct".fields) |field| {
|
|
if (comptime std.mem.endsWith(u8, field.name, "_reply")) {
|
|
len += @intFromBool(@field(this, field.name) != null);
|
|
}
|
|
}
|
|
break :blk len;
|
|
});
|
|
|
|
var i: u32 = 0;
|
|
|
|
inline for (comptime @typeInfo(struct_any_reply).@"struct".fields) |field| {
|
|
if (comptime std.mem.endsWith(u8, field.name, "_reply")) {
|
|
if (@field(this, field.name)) |reply| {
|
|
const lookup_name = comptime field.name[0 .. field.name.len - "_reply".len];
|
|
try appendAll(globalThis, allocator, array, &i, reply, lookup_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
pub fn Callback(comptime Type: type) type {
|
|
return fn (*Type, status: ?Error, timeouts: i32, results: ?*struct_any_reply) void;
|
|
}
|
|
|
|
pub fn callbackWrapper(
|
|
comptime _: []const u8,
|
|
comptime Type: type,
|
|
comptime function: Callback(Type),
|
|
) ares_callback {
|
|
return &struct {
|
|
pub fn handleAny(ctx: ?*anyopaque, status: c_int, timeouts: c_int, buffer: [*c]u8, buffer_length: c_int) callconv(.C) void {
|
|
const this = bun.cast(*Type, ctx.?);
|
|
if (status != ARES_SUCCESS) {
|
|
function(this, Error.get(status), timeouts, null);
|
|
return;
|
|
}
|
|
|
|
var any_success = false;
|
|
var last_error: ?c_int = null;
|
|
var reply = bun.handleOom(bun.default_allocator.create(struct_any_reply));
|
|
reply.* = .{};
|
|
|
|
switch (hostent_with_ttls.parse("a", buffer, buffer_length)) {
|
|
.result => |result| {
|
|
reply.a_reply = result;
|
|
any_success = true;
|
|
},
|
|
.err => |err| last_error = @intFromEnum(err),
|
|
}
|
|
|
|
switch (hostent_with_ttls.parse("aaaa", buffer, buffer_length)) {
|
|
.result => |result| {
|
|
reply.aaaa_reply = result;
|
|
any_success = true;
|
|
},
|
|
.err => |err| last_error = @intFromEnum(err),
|
|
}
|
|
|
|
var result = ares_parse_mx_reply(buffer, buffer_length, &reply.mx_reply);
|
|
if (result == ARES_SUCCESS) {
|
|
any_success = true;
|
|
} else {
|
|
last_error = result;
|
|
}
|
|
|
|
result = ares_parse_ns_reply(buffer, buffer_length, &reply.ns_reply);
|
|
if (result == ARES_SUCCESS) {
|
|
any_success = true;
|
|
} else {
|
|
last_error = result;
|
|
}
|
|
|
|
result = ares_parse_txt_reply(buffer, buffer_length, &reply.txt_reply);
|
|
if (result == ARES_SUCCESS) {
|
|
any_success = true;
|
|
} else {
|
|
last_error = result;
|
|
}
|
|
|
|
result = ares_parse_srv_reply(buffer, buffer_length, &reply.srv_reply);
|
|
if (result == ARES_SUCCESS) {
|
|
any_success = true;
|
|
} else {
|
|
last_error = result;
|
|
}
|
|
|
|
result = ares_parse_ptr_reply(buffer, buffer_length, null, 0, AF.INET, &reply.ptr_reply);
|
|
if (result == ARES_SUCCESS) {
|
|
any_success = true;
|
|
} else {
|
|
last_error = result;
|
|
}
|
|
|
|
result = ares_parse_naptr_reply(buffer, buffer_length, &reply.naptr_reply);
|
|
if (result == ARES_SUCCESS) {
|
|
any_success = true;
|
|
} else {
|
|
last_error = result;
|
|
}
|
|
|
|
result = ares_parse_soa_reply(buffer, buffer_length, &reply.soa_reply);
|
|
if (result == ARES_SUCCESS) {
|
|
any_success = true;
|
|
} else {
|
|
last_error = result;
|
|
}
|
|
|
|
result = ares_parse_caa_reply(buffer, buffer_length, &reply.caa_reply);
|
|
if (result == ARES_SUCCESS) {
|
|
any_success = true;
|
|
} else {
|
|
last_error = result;
|
|
}
|
|
|
|
if (!any_success) {
|
|
reply.deinit();
|
|
function(this, Error.get(last_error.?), timeouts, null);
|
|
return;
|
|
}
|
|
|
|
function(this, null, timeouts, reply);
|
|
}
|
|
}.handleAny;
|
|
}
|
|
|
|
pub fn deinit(this: *struct_any_reply) void {
|
|
inline for (@typeInfo(struct_any_reply).@"struct".fields) |field| {
|
|
if (comptime std.mem.endsWith(u8, field.name, "_reply")) {
|
|
if (@field(this, field.name)) |reply| {
|
|
reply.deinit();
|
|
}
|
|
}
|
|
}
|
|
|
|
bun.default_allocator.destroy(this);
|
|
}
|
|
};
|
|
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: ?*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: ?*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: *?*struct_ares_addr_port_node) c_int;
|
|
pub extern fn ares_get_servers_ports(channel: *Channel, servers: *?*struct_ares_addr_port_node) c_int;
|
|
/// https://c-ares.org/docs/ares_inet_ntop.html
|
|
pub extern fn ares_inet_ntop(af: c_int, src: ?*const anyopaque, dst: [*]u8, size: ares_socklen_t) ?[*:0]const u8;
|
|
/// https://c-ares.org/docs/ares_inet_pton.html
|
|
///
|
|
/// ## Returns
|
|
/// - `1` if `src` was valid for the specified address family
|
|
/// - `0` if `src` was not parseable in the specified address family
|
|
/// - `-1` if some system error occurred. `errno` will have been set.
|
|
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 ARES_ENOSERVER = 26;
|
|
|
|
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,
|
|
ENOSERVER = ARES_ENOSERVER,
|
|
|
|
const Deferred = struct {
|
|
errno: Error,
|
|
syscall: []const u8,
|
|
hostname: ?bun.String,
|
|
promise: jsc.JSPromise.Strong,
|
|
|
|
pub const new = bun.TrivialNew(@This());
|
|
|
|
pub fn init(errno: Error, syscall: []const u8, hostname: ?bun.String, promise: jsc.JSPromise.Strong) *Deferred {
|
|
return Deferred.new(.{
|
|
.errno = errno,
|
|
.syscall = syscall,
|
|
.hostname = hostname,
|
|
.promise = promise,
|
|
});
|
|
}
|
|
|
|
pub fn reject(this: *Deferred, globalThis: *jsc.JSGlobalObject) bun.JSTerminated!void {
|
|
const system_error = jsc.SystemError{
|
|
.errno = @intFromEnum(this.errno),
|
|
.code = bun.String.static(this.errno.code()),
|
|
.message = if (this.hostname) |hostname|
|
|
bun.handleOom(bun.String.createFormat("{s} {s} {s}", .{ this.syscall, this.errno.code()[4..], hostname }))
|
|
else
|
|
bun.handleOom(bun.String.createFormat("{s} {s}", .{ this.syscall, this.errno.code()[4..] })),
|
|
.syscall = bun.String.cloneUTF8(this.syscall),
|
|
.hostname = this.hostname orelse bun.String.empty,
|
|
};
|
|
|
|
const instance = system_error.toErrorInstance(globalThis);
|
|
instance.put(globalThis, "name", bun.String.static("DNSException").toJS(globalThis));
|
|
|
|
defer this.deinit();
|
|
defer this.hostname = null;
|
|
return this.promise.reject(globalThis, instance);
|
|
}
|
|
|
|
pub fn rejectLater(this: *Deferred, globalThis: *jsc.JSGlobalObject) void {
|
|
const Context = struct {
|
|
deferred: *Deferred,
|
|
globalThis: *jsc.JSGlobalObject,
|
|
pub fn callback(context: *@This()) bun.JSTerminated!void {
|
|
defer bun.default_allocator.destroy(context);
|
|
try context.deferred.reject(context.globalThis);
|
|
}
|
|
};
|
|
|
|
const context = bun.handleOom(bun.default_allocator.create(Context));
|
|
context.deferred = this;
|
|
context.globalThis = globalThis;
|
|
// TODO(@heimskr): new custom Task type
|
|
globalThis.bunVM().enqueueTask(jsc.ManagedTask.New(Context, Context.callback).init(context));
|
|
}
|
|
|
|
pub fn deinit(this: *@This()) void {
|
|
if (this.hostname) |hostname| {
|
|
hostname.deref();
|
|
}
|
|
this.promise.deinit();
|
|
bun.destroy(this);
|
|
}
|
|
};
|
|
|
|
pub fn toDeferred(this: Error, syscall: []const u8, hostname: ?[]const u8, promise: *jsc.JSPromise.Strong) *Deferred {
|
|
const host_string: ?bun.String = if (hostname) |host|
|
|
bun.String.cloneUTF8(host)
|
|
else
|
|
null;
|
|
defer promise.* = .{};
|
|
return Deferred.init(this, syscall, host_string, promise.*);
|
|
}
|
|
|
|
pub fn toJSWithSyscall(this: Error, globalThis: *jsc.JSGlobalObject, comptime syscall: [:0]const u8) jsc.JSValue {
|
|
const instance = (jsc.SystemError{
|
|
.errno = @intFromEnum(this),
|
|
.code = bun.String.static(this.code()[4..]),
|
|
.syscall = bun.String.static(syscall),
|
|
.message = bun.handleOom(bun.String.createFormat("{s} {s}", .{ syscall, this.code()[4..] })),
|
|
}).toErrorInstance(globalThis);
|
|
instance.put(globalThis, "name", bun.String.static("DNSException").toJS(globalThis));
|
|
return instance;
|
|
}
|
|
|
|
pub fn toJSWithSyscallAndHostname(this: Error, globalThis: *jsc.JSGlobalObject, comptime syscall: [:0]const u8, hostname: []const u8) jsc.JSValue {
|
|
const instance = (jsc.SystemError{
|
|
.errno = @intFromEnum(this),
|
|
.code = bun.String.static(this.code()[4..]),
|
|
.message = bun.handleOom(bun.String.createFormat("{s} {s} {s}", .{ syscall, this.code()[4..], hostname })),
|
|
.syscall = bun.String.static(syscall),
|
|
.hostname = bun.String.cloneUTF8(hostname),
|
|
}).toErrorInstance(globalThis);
|
|
instance.put(globalThis, "name", bun.String.static("DNSException").toJS(globalThis));
|
|
return instance;
|
|
}
|
|
|
|
pub fn initEAI(rc: i32) ?Error {
|
|
if (comptime bun.Environment.isWindows) {
|
|
// https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/lib/internal/errors.js#L807-L815
|
|
if (rc == libuv.UV_EAI_NODATA or rc == libuv.UV_EAI_NONAME) {
|
|
return Error.ENOTFOUND;
|
|
}
|
|
|
|
// TODO: revisit this
|
|
return switch (rc) {
|
|
0 => null,
|
|
libuv.UV_EAI_AGAIN => Error.ETIMEOUT,
|
|
libuv.UV_EAI_ADDRFAMILY => Error.EBADFAMILY,
|
|
libuv.UV_EAI_BADFLAGS => Error.EBADFLAGS,
|
|
libuv.UV_EAI_BADHINTS => Error.EBADHINTS,
|
|
libuv.UV_EAI_CANCELED => Error.ECANCELLED,
|
|
libuv.UV_EAI_FAIL => Error.ENOTFOUND,
|
|
libuv.UV_EAI_FAMILY => Error.EBADFAMILY,
|
|
libuv.UV_EAI_MEMORY => Error.ENOMEM,
|
|
libuv.UV_EAI_NODATA => Error.ENODATA,
|
|
libuv.UV_EAI_NONAME => Error.ENONAME,
|
|
libuv.UV_EAI_OVERFLOW => Error.ENOMEM,
|
|
libuv.UV_EAI_PROTOCOL => Error.EBADQUERY,
|
|
libuv.UV_EAI_SERVICE => Error.ESERVICE,
|
|
libuv.UV_EAI_SOCKTYPE => Error.ECONNREFUSED,
|
|
else => Error.ENOTFOUND, //UV_ENOENT and non documented errors
|
|
};
|
|
}
|
|
|
|
const eai: std.posix.system.EAI = @enumFromInt(rc);
|
|
|
|
// https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/lib/internal/errors.js#L807-L815
|
|
if (eai == .NODATA or eai == .NONAME) {
|
|
return Error.ENOTFOUND;
|
|
}
|
|
|
|
if (comptime bun.Environment.isLinux) {
|
|
switch (eai) {
|
|
.SOCKTYPE => return Error.ECONNREFUSED,
|
|
.IDN_ENCODE => return Error.EBADSTR,
|
|
.ALLDONE => return Error.ENOTFOUND,
|
|
.INPROGRESS => return Error.ETIMEOUT,
|
|
.CANCELED => return Error.ECANCELLED,
|
|
.NOTCANCELED => return Error.ECANCELLED,
|
|
else => {},
|
|
}
|
|
}
|
|
|
|
return switch (eai) {
|
|
@as(std.posix.system.EAI, @enumFromInt(0)) => return null,
|
|
.ADDRFAMILY => Error.EBADFAMILY,
|
|
.BADFLAGS => Error.EBADFLAGS, // Invalid hints
|
|
.FAIL => Error.EBADRESP,
|
|
.FAMILY => Error.EBADFAMILY,
|
|
.MEMORY => Error.ENOMEM,
|
|
.SERVICE => Error.ESERVICE,
|
|
.SYSTEM => Error.ESERVFAIL,
|
|
else => bun.todo(@src(), Error.ENOTIMP),
|
|
};
|
|
}
|
|
|
|
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_ENOTFOUND" },
|
|
.{ .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_ENOTFOUND" },
|
|
.{ .EBADHINTS, "DNS_EBADHINTS" },
|
|
.{ .ENOTINITIALIZED, "DNS_ENOTINITIALIZED" },
|
|
.{ .ELOADIPHLPAPI, "DNS_ELOADIPHLPAPI" },
|
|
.{ .EADDRGETNETWORKPARAMS, "DNS_EADDRGETNETWORKPARAMS" },
|
|
.{ .ECANCELLED, "DNS_ECANCELLED" },
|
|
.{ .ESERVICE, "DNS_ESERVICE" },
|
|
.{ .ENOSERVER, "DNS_ENOSERVER" },
|
|
});
|
|
|
|
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" },
|
|
.{ .ENOSERVER, "No DNS servers were configured" },
|
|
});
|
|
|
|
pub fn get(rc: i32) ?Error {
|
|
// https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/lib/internal/errors.js#L807-L815
|
|
if (rc == ARES_ENODATA or rc == ARES_ENONAME) {
|
|
return get(ARES_ENOTFOUND);
|
|
}
|
|
|
|
return switch (rc) {
|
|
0 => null,
|
|
1...ARES_ENOSERVER => @as(Error, @enumFromInt(rc)),
|
|
-ARES_ENOSERVER...-1 => @as(Error, @enumFromInt(-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 = if (bun.Environment.isWindows) std.os.windows.ws2_32.INVALID_SOCKET else -@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;
|
|
|
|
comptime {
|
|
const Bun__canonicalizeIP = jsc.toJSHostFn(Bun__canonicalizeIP_);
|
|
@export(&Bun__canonicalizeIP, .{ .name = "Bun__canonicalizeIP" });
|
|
}
|
|
pub fn Bun__canonicalizeIP_(globalThis: *jsc.JSGlobalObject, callframe: *jsc.CallFrame) bun.JSError!jsc.JSValue {
|
|
jsc.markBinding(@src());
|
|
|
|
const arguments = callframe.arguments();
|
|
|
|
if (arguments.len == 0) {
|
|
return globalThis.throwInvalidArguments("canonicalizeIP() expects a string but received no arguments.", .{});
|
|
}
|
|
// windows uses 65 bytes for ipv6 addresses and linux/macos uses 46
|
|
const INET6_ADDRSTRLEN = if (comptime bun.Environment.isWindows) 65 else 46;
|
|
|
|
const addr_arg = try arguments[0].toSlice(globalThis, bun.default_allocator);
|
|
defer addr_arg.deinit();
|
|
const addr_str = addr_arg.slice();
|
|
if (addr_str.len >= INET6_ADDRSTRLEN)
|
|
return .js_undefined;
|
|
|
|
// CIDR not allowed
|
|
if (strings.containsChar(addr_str, '/'))
|
|
return .js_undefined;
|
|
|
|
var ip_binary: [16]u8 = undefined; // 16 bytes is enough for both IPv4 and IPv6
|
|
|
|
// we need a null terminated string as input
|
|
var ip_addr: [INET6_ADDRSTRLEN + 1]u8 = undefined;
|
|
bun.copy(u8, &ip_addr, addr_str);
|
|
ip_addr[addr_str.len] = 0;
|
|
|
|
var af: c_int = AF.INET;
|
|
// get the binary representation of the IP
|
|
if (ares_inet_pton(af, &ip_addr, &ip_binary) != 1) {
|
|
af = AF.INET6;
|
|
if (ares_inet_pton(af, &ip_addr, &ip_binary) != 1) {
|
|
return .js_undefined;
|
|
}
|
|
}
|
|
// ip_addr will contain the null-terminated string of the canonicalized IP
|
|
if (ares_inet_ntop(af, &ip_binary, &ip_addr, @sizeOf(@TypeOf(ip_addr))) == null) {
|
|
return .js_undefined;
|
|
}
|
|
// use the null-terminated size to return the string
|
|
const slice = bun.sliceTo(ip_addr[0..], 0);
|
|
if (bun.strings.eql(addr_str, slice)) {
|
|
return arguments[0];
|
|
}
|
|
|
|
return bun.String.createUTF8ForJS(globalThis, slice);
|
|
}
|
|
|
|
/// Creates a sockaddr structure from an address, port.
|
|
///
|
|
/// # Parameters
|
|
/// - `addr`: A byte slice representing the IP address.
|
|
/// - `port`: A 16-bit unsigned integer representing the port number.
|
|
/// - `sa`: A pointer to a sockaddr structure where the result will be stored.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// This function returns 0 on success.
|
|
pub fn getSockaddr(addr: []const u8, port: u16, sa: *std.posix.sockaddr) c_int {
|
|
const buf_size = 128;
|
|
|
|
var buf: [buf_size]u8 = undefined;
|
|
const addr_ptr: [*:0]const u8 = brk: {
|
|
if (addr.len == 0 or addr.len >= buf_size) {
|
|
return -1;
|
|
}
|
|
const len = @min(addr.len, buf.len - 1);
|
|
@memcpy(buf[0..len], addr[0..len]);
|
|
|
|
buf[len] = 0;
|
|
break :brk buf[0..len :0];
|
|
};
|
|
|
|
{
|
|
const in: *std.posix.sockaddr.in = @alignCast(@ptrCast(sa));
|
|
if (ares_inet_pton(AF.INET, addr_ptr, &in.addr) == 1) {
|
|
in.*.family = AF.INET;
|
|
in.*.port = std.mem.nativeToBig(u16, port);
|
|
return 0;
|
|
}
|
|
}
|
|
{
|
|
const in6: *std.posix.sockaddr.in6 = @alignCast(@ptrCast(sa));
|
|
if (ares_inet_pton(AF.INET6, addr_ptr, &in6.addr) == 1) {
|
|
in6.*.family = AF.INET6;
|
|
in6.*.port = std.mem.nativeToBig(u16, port);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
const std = @import("std");
|
|
const iovec = @import("std").os.iovec;
|
|
|
|
const bun = @import("bun");
|
|
const jsc = bun.jsc;
|
|
const strings = bun.strings;
|
|
const GetAddrInfo = bun.dns.GetAddrInfo;
|
|
const libuv = bun.windows.libuv;
|
|
|
|
const c = @import("std").c;
|
|
const ares_socklen_t = c.socklen_t;
|
|
const fd_set = c.fd_set;
|
|
|
|
const struct_sockaddr = std.posix.sockaddr;
|
|
const struct_in_addr = std.posix.sockaddr.in;
|