diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bun.js/api/bun/dns_resolver.zig | 366 | ||||
-rw-r--r-- | src/bun.js/base.zig | 15 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGlobalObject.cpp | 5 | ||||
-rw-r--r-- | src/bun.js/bindings/bindings.zig | 2 | ||||
-rw-r--r-- | src/bun.js/node-dns.exports.js | 38 | ||||
-rw-r--r-- | src/deps/c_ares.zig | 238 | ||||
-rw-r--r-- | src/deps/uws.zig | 12 |
7 files changed, 658 insertions, 18 deletions
diff --git a/src/bun.js/api/bun/dns_resolver.zig b/src/bun.js/api/bun/dns_resolver.zig index ac29ae145..baf7b5b3d 100644 --- a/src/bun.js/api/bun/dns_resolver.zig +++ b/src/bun.js/api/bun/dns_resolver.zig @@ -614,6 +614,111 @@ pub const GetAddrInfo = struct { }; }; + + + + +pub const ResolveSrvInfoRequest = struct { + const log = Output.scoped(.ResolveSrvInfoRequest, false); + + resolver_for_caching: ?*DNSResolver = null, + hash: u64 = 0, + cache: ResolveSrvInfoRequest.CacheConfig = ResolveSrvInfoRequest.CacheConfig{}, + head: SrvLookup, + tail: *SrvLookup = undefined, + task: bun.ThreadPool.Task = undefined, + + pub fn init( + cache: DNSResolver.SrvCacheHit, + resolver: ?*DNSResolver, + name: [] const u8, + globalThis: *JSC.JSGlobalObject, + comptime cache_field: []const u8, + ) !*ResolveSrvInfoRequest { + + var request = try globalThis.allocator().create(ResolveSrvInfoRequest); + var hasher = std.hash.Wyhash.init(0); + hasher.update(name); + const hash = hasher.final(); + var poll_ref = JSC.PollRef.init(); + poll_ref.ref(globalThis.bunVM()); + request.* = .{ + .resolver_for_caching = resolver, + .hash = hash, + .head = .{ + .poll_ref = poll_ref, + .globalThis = globalThis, + .promise = JSC.JSPromise.Strong.init(globalThis), + .allocated = false, + }, + }; + request.tail = &request.head; + if (cache == .new) { + request.resolver_for_caching = resolver; + request.cache = ResolveSrvInfoRequest.CacheConfig{ + .pending_cache = true, + .entry_cache = false, + .pos_in_pending = @truncate(u5, @field(resolver.?, cache_field).indexOf(cache.new).?), + .name_len = @truncate(u9, name.len), + }; + cache.new.lookup = request; + } + return request; + } + + pub const Task = bun.JSC.WorkTask(ResolveSrvInfoRequest, false); + + pub const CacheConfig = packed struct(u16) { + pending_cache: bool = false, + entry_cache: bool = false, + pos_in_pending: u5 = 0, + name_len: u9 = 0, + }; + + pub const PendingCacheKey = struct { + hash: u64, + len: u16, + lookup: *ResolveSrvInfoRequest = undefined, + + pub fn append(this: *PendingCacheKey, srv_lookup: *SrvLookup) void { + var tail = this.lookup.tail; + tail.next = srv_lookup; + this.lookup.tail = srv_lookup; + } + + pub fn init(name: []const u8) PendingCacheKey { + var hasher = std.hash.Wyhash.init(0); + hasher.update(name); + const hash = hasher.final(); + return PendingCacheKey{ + .hash = hash, + .len = @truncate(u16, name.len), + .lookup = undefined, + }; + } + }; + + pub fn onCaresComplete(this: *ResolveSrvInfoRequest, err_: ?c_ares.Error, timeout: i32, result: ?*c_ares.struct_ares_srv_reply) void { + if (this.resolver_for_caching) |resolver| { + + if (this.cache.pending_cache) { + resolver.drainPendingSrvCares( + this.cache.pos_in_pending, + err_, + timeout, + result, + ); + return; + } + } + + var head = this.head; + bun.default_allocator.destroy(this); + + head.processResolveSrv(err_, timeout, result); + } +}; + pub const GetAddrInfoRequest = struct { const log = Output.scoped(.GetAddrInfoRequest, false); @@ -634,12 +739,15 @@ pub const GetAddrInfoRequest = struct { comptime cache_field: []const u8, ) !*GetAddrInfoRequest { var request = try globalThis.allocator().create(GetAddrInfoRequest); + var poll_ref = JSC.PollRef.init(); + poll_ref.ref(globalThis.bunVM()); request.* = .{ .backend = backend, .resolver_for_caching = resolver, .hash = query.hash(), .head = .{ .globalThis = globalThis, + .poll_ref = poll_ref, .promise = JSC.JSPromise.Strong.init(globalThis), .allocated = false, }, @@ -832,6 +940,82 @@ pub const GetAddrInfoRequest = struct { } }; +pub const SrvLookup = struct { + const log = Output.scoped(.SrvLookup, true); + + globalThis: *JSC.JSGlobalObject = undefined, + promise: JSC.JSPromise.Strong, + poll_ref: JSC.PollRef, + allocated: bool = false, + next: ?*SrvLookup = null, + + pub fn init(globalThis: *JSC.JSGlobalObject, allocator: std.mem.Allocator) !*SrvLookup { + var this = try allocator.create(SrvLookup); + var poll_ref = JSC.PollRef.init(); + poll_ref.ref(globalThis.bunVM()); + this.* = .{ + .globalThis = globalThis, + .promise = JSC.JSPromise.Strong.init(globalThis), + .poll_ref = poll_ref, + .allocated = true, + }; + return this; + } + + pub fn processResolveSrv(this: *SrvLookup, err_: ?c_ares.Error, _: i32, result: ?*c_ares.struct_ares_srv_reply) void { + if (err_) |err| { + var promise = this.promise; + var globalThis = this.globalThis; + const error_value = globalThis.createErrorInstance("SRV lookup failed: {s}", .{err.label()}); + error_value.put( + globalThis, + JSC.ZigString.static("code"), + JSC.ZigString.init(err.code()).toValueGC(globalThis), + ); + + promise.reject(globalThis, error_value); + this.deinit(); + return; + } + + if (result == null or result.?.next == null) { + var promise = this.promise; + var globalThis = this.globalThis; + const error_value = globalThis.createErrorInstance("SRV lookup failed: {s}", .{"No results"}); + error_value.put( + globalThis, + JSC.ZigString.static("code"), + JSC.ZigString.init("EUNREACHABLE").toValueGC(globalThis), + ); + + promise.reject(globalThis, error_value); + this.deinit(); + return; + } + var node = result.?; + const array = node.toJSArray(this.globalThis.allocator(), this.globalThis); + this.onComplete(array); + return; + } + + + + pub fn onComplete(this: *SrvLookup, result: JSC.JSValue) void { + var promise = this.promise; + var globalThis = this.globalThis; + this.promise = .{}; + + promise.resolve(globalThis, result); + this.deinit(); + } + + pub fn deinit(this: *SrvLookup) void { + this.poll_ref.unrefOnNextTick(this.globalThis.bunVM()); + + if (this.allocated) + this.globalThis.allocator().destroy(this); + } +}; pub const DNSLookup = struct { const log = Output.scoped(.DNSLookup, true); @@ -839,11 +1023,16 @@ pub const DNSLookup = struct { promise: JSC.JSPromise.Strong, allocated: bool = false, next: ?*DNSLookup = null, + poll_ref: JSC.PollRef, pub fn init(globalThis: *JSC.JSGlobalObject, allocator: std.mem.Allocator) !*DNSLookup { var this = try allocator.create(DNSLookup); + var poll_ref = JSC.PollRef.init(); + poll_ref.ref(globalThis.bunVM()); + this.* = .{ .globalThis = globalThis, + .poll_ref = poll_ref, .promise = JSC.JSPromise.Strong.init(globalThis), .allocated = true, }; @@ -893,9 +1082,8 @@ pub const DNSLookup = struct { JSC.ZigString.init(err.code()).toValueGC(globalThis), ); - this.deinit(); - promise.reject(globalThis, error_value); + this.deinit(); return; } @@ -909,8 +1097,8 @@ pub const DNSLookup = struct { JSC.ZigString.init("EUNREACHABLE").toValueGC(globalThis), ); - this.deinit(); promise.reject(globalThis, error_value); + this.deinit(); return; } @@ -927,11 +1115,12 @@ pub const DNSLookup = struct { var globalThis = this.globalThis; this.promise = .{}; + promise.resolve(globalThis, result); this.deinit(); - promise.resolveOnNextTick(globalThis, result); } pub fn deinit(this: *DNSLookup) void { + this.poll_ref.unrefOnNextTick(this.globalThis.bunVM()); if (this.allocated) this.globalThis.allocator().destroy(this); } @@ -961,10 +1150,12 @@ pub const DNSResolver = struct { polls: std.AutoArrayHashMap(i32, ?*JSC.FilePoll) = undefined, pending_host_cache_cares: PendingCache = PendingCache.init(), + pending_srv_cache_cares: SrvPendingCache = SrvPendingCache.init(), pending_host_cache_native: PendingCache = PendingCache.init(), // entry_host_cache: std.BoundedArray(128) const PendingCache = bun.HiveArray(GetAddrInfoRequest.PendingCacheKey, 32); + const SrvPendingCache = bun.HiveArray(ResolveSrvInfoRequest.PendingCacheKey, 32); fn getKey(this: *DNSResolver, index: u8, comptime cache_name: []const u8) GetAddrInfoRequest.PendingCacheKey { var cache: *PendingCache = &@field(this, cache_name); @@ -978,6 +1169,60 @@ pub const DNSResolver = struct { return entry; } + fn getSrvKey(this: *DNSResolver, index: u8, comptime cache_name: []const u8) ResolveSrvInfoRequest.PendingCacheKey { + var cache: *SrvPendingCache = &@field(this, cache_name); + std.debug.assert(!cache.available.isSet(index)); + const entry = cache.buffer[index]; + cache.buffer[index] = undefined; + + var available = cache.available; + available.set(index); + cache.available = available; + + return entry; + } + + pub fn drainPendingSrvCares(this: *DNSResolver, index: u8, err: ?c_ares.Error, timeout: i32, result: ?*c_ares.struct_ares_srv_reply) void { + const key = this.getSrvKey(index, "pending_srv_cache_cares"); + + var addr = result orelse { + var pending: ?*SrvLookup = key.lookup.head.next; + key.lookup.head.processResolveSrv(err, timeout, null); + bun.default_allocator.destroy(key.lookup); + + while (pending) |value| { + pending = value.next; + value.processResolveSrv(err, timeout, null); + } + return; + }; + + var pending: ?*SrvLookup = key.lookup.head.next; + var prev_global = key.lookup.head.globalThis; + var array = addr.toJSArray(this.vm.allocator, prev_global); + defer addr.deinit(); + array.ensureStillAlive(); + key.lookup.head.onComplete(array); + bun.default_allocator.destroy(key.lookup); + + array.ensureStillAlive(); + + + while (pending) |value| { + var new_global = value.globalThis; + if (prev_global != new_global) { + array = addr.toJSArray(this.vm.allocator, new_global); + prev_global = new_global; + } + pending = value.next; + + { + array.ensureStillAlive(); + value.onComplete(array); + array.ensureStillAlive(); + } + } + } pub fn drainPendingHostCares(this: *DNSResolver, index: u8, err: ?c_ares.Error, timeout: i32, result: ?*c_ares.AddrInfo) void { const key = this.getKey(index, "pending_host_cache_cares"); @@ -1070,6 +1315,39 @@ pub const DNSResolver = struct { new: *GetAddrInfoRequest.PendingCacheKey, disabled: void, }; + pub const SrvCacheHit = union(enum) { + inflight: *ResolveSrvInfoRequest.PendingCacheKey, + new: *ResolveSrvInfoRequest.PendingCacheKey, + disabled: void, + }; + + pub fn getOrPutIntoSrvPendingCache( + this: *DNSResolver, + key: ResolveSrvInfoRequest.PendingCacheKey, + comptime field: std.meta.FieldEnum(DNSResolver), + ) SrvCacheHit { + var cache: *SrvPendingCache = &@field(this, @tagName(field)); + var inflight_iter = cache.available.iterator( + .{ + .kind = .unset, + }, + ); + + while (inflight_iter.next()) |index| { + var entry: *ResolveSrvInfoRequest.PendingCacheKey = &cache.buffer[index]; + if (entry.hash == key.hash and entry.len == key.len) { + return .{ .inflight = entry }; + } + } + + if (cache.get()) |new| { + new.hash = key.hash; + new.len = key.len; + return .{ .new = new }; + } + + return .{ .disabled = {} }; + } pub fn getOrPutIntoPendingCache( this: *DNSResolver, @@ -1330,6 +1608,80 @@ pub const DNSResolver = struct { }; } + pub fn resolveSrv(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const arguments = callframe.arguments(2); + if (arguments.len < 1) { + globalThis.throwNotEnoughArguments("resolveSrv", 2, arguments.len); + return .zero; + } + + const name_value = arguments.ptr[0]; + + if (name_value.isEmptyOrUndefinedOrNull() or !name_value.isString()) { + globalThis.throwInvalidArgumentType("resolveSrv", "hostname", "string"); + return .zero; + } + + const name_str = name_value.toStringOrNull(globalThis) orelse { + return .zero; + }; + + if (name_str.length() == 0) { + globalThis.throwInvalidArgumentType("resolveSrv", "hostname", "non-empty string"); + return .zero; + } + + + const name = name_str.toSlice(globalThis, bun.default_allocator); + defer name.deinit(); + var vm = globalThis.bunVM(); + var resolver = vm.rareData().globalDNSResolver(vm); + + return resolver.doResolveSrv(name.slice(), globalThis); + } + + pub fn doResolveSrv(this: *DNSResolver, name: []const u8, globalThis: *JSC.JSGlobalObject) JSC.JSValue { + var channel: *c_ares.Channel = switch (this.getChannel()) { + .result => |res| res, + .err => |err| { + const system_error = JSC.SystemError{ + .errno = -1, + .code = JSC.ZigString.init(err.code()), + .message = JSC.ZigString.init(err.label()), + }; + + globalThis.throwValue(system_error.toErrorInstance(globalThis)); + return .zero; + }, + }; + + const key = ResolveSrvInfoRequest.PendingCacheKey.init(name); + + var cache = this.getOrPutIntoSrvPendingCache(key, .pending_srv_cache_cares); + if (cache == .inflight) { + var srv_lookup = SrvLookup.init(globalThis, globalThis.allocator()) catch unreachable; + cache.inflight.append(srv_lookup); + return srv_lookup.promise.value(); + } + + var request = ResolveSrvInfoRequest.init( + cache, + this, + name, + globalThis, + "pending_srv_cache_cares", + ) catch unreachable; + const promise = request.tail.promise.value(); + + channel.resolveSrv( + name, + ResolveSrvInfoRequest, + request, + ResolveSrvInfoRequest.onCaresComplete, + ); + + return promise; + } pub fn c_aresLookupWithNormalizedName(this: *DNSResolver, query: GetAddrInfo, globalThis: *JSC.JSGlobalObject) JSC.JSValue { var channel: *c_ares.Channel = switch (this.getChannel()) { .result => |res| res, @@ -1386,6 +1738,12 @@ pub const DNSResolver = struct { .name = "Bun__DNSResolver__lookup", }, ); + @export( + resolveSrv, + .{ + .name = "Bun__DNSResolver__resolveSrv", + }, + ); } // pub fn lookupService(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { // const arguments = callframe.arguments(3); diff --git a/src/bun.js/base.zig b/src/bun.js/base.zig index 79498b8fc..7d36662b6 100644 --- a/src/bun.js/base.zig +++ b/src/bun.js/base.zig @@ -3225,19 +3225,22 @@ pub const PollRef = struct { if (this.status != .active) return; this.status = .inactive; - log("unref", .{}); - vm.uws_event_loop.?.num_polls -= 1; - vm.uws_event_loop.?.active -= 1; + vm.uws_event_loop.?.unref(); + } + /// Prevent a poll from keeping the process alive on the next tick. + pub fn unrefOnNextTick(this: *PollRef, vm: *JSC.VirtualMachine) void { + if (this.status != .active) + return; + this.status = .inactive; + vm.uws_event_loop.?.nextTick(*uws.Loop, vm.uws_event_loop.?, uws.Loop.unref); } /// Allow a poll to keep the process alive. pub fn ref(this: *PollRef, vm: *JSC.VirtualMachine) void { if (this.status != .inactive) return; - log("ref", .{}); this.status = .active; - vm.uws_event_loop.?.num_polls += 1; - vm.uws_event_loop.?.active += 1; + vm.uws_event_loop.?.ref(); } }; diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index b37e280f3..11b67b433 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -2169,6 +2169,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionPerformMicrotask, (JSGlobalObject * globalObj } extern "C" EncodedJSValue Bun__DNSResolver__lookup(JSGlobalObject*, JSC::CallFrame*); +extern "C" EncodedJSValue Bun__DNSResolver__resolveSrv(JSGlobalObject*, JSC::CallFrame*); JSC_DEFINE_HOST_FUNCTION(jsFunctionPerformMicrotaskVariadic, (JSGlobalObject * globalObject, CallFrame* callframe)) { @@ -3321,9 +3322,11 @@ void GlobalObject::installAPIGlobals(JSClassRef* globals, int count, JSC::VM& vm JSC::JSObject* dnsObject = JSC::constructEmptyObject(this); dnsObject->putDirectNativeFunction(vm, this, JSC::Identifier::fromString(vm, "lookup"_s), 2, Bun__DNSResolver__lookup, ImplementationVisibility::Public, NoIntrinsic, JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0); + dnsObject->putDirectNativeFunction(vm, this, JSC::Identifier::fromString(vm, "resolveSrv"_s), 2, Bun__DNSResolver__resolveSrv, ImplementationVisibility::Public, NoIntrinsic, + JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0); object->putDirect(vm, PropertyName(identifier), dnsObject, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0); } - + { JSC::Identifier identifier = JSC::Identifier::fromString(vm, "plugin"_s); JSFunction* pluginFunction = JSFunction::create(vm, this, 1, String("plugin"_s), jsFunctionBunPlugin, ImplementationVisibility::Public, NoIntrinsic); diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index d77a9dbb6..aa051e712 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -2860,7 +2860,7 @@ pub const JSValue = enum(JSValueReprInt) { return switch (comptime Number) { JSValue => number, f32, f64 => jsNumberFromDouble(@as(f64, number)), - u8, i16, i32, c_int, i8, u16 => jsNumberFromInt32(@intCast(i32, number)), + c_ushort, u8, i16, i32, c_int, i8, u16 => jsNumberFromInt32(@intCast(i32, number)), u32, u52, c_uint, i64 => jsNumberFromInt64(@intCast(i64, number)), usize, u64 => jsNumberFromUint64(@intCast(u64, number)), comptime_int => switch (number) { diff --git a/src/bun.js/node-dns.exports.js b/src/bun.js/node-dns.exports.js index 570903b40..74ec1b46a 100644 --- a/src/bun.js/node-dns.exports.js +++ b/src/bun.js/node-dns.exports.js @@ -1,4 +1,4 @@ -// only resolve4, resolve, lookup, and resolve6 are implemented. +// only resolve4, resolve, lookup, resolve6 and resolveSrv are implemented. const { dns } = globalThis.Bun; @@ -25,6 +25,22 @@ function lookup(domain, options, callback) { ); } +function resolveSrv(hostname, callback) { + + if (typeof callback != "function") { + throw new TypeError("callback must be a function"); + } + + dns.resolveSrv(hostname, callback).then( + (results) => { + callback(null, results); + }, + (error) => { + callback(error); + }, + ); +} + function lookupService(address, port, callback) { if (typeof callback != "function") { throw new TypeError("callback must be a function"); @@ -138,7 +154,18 @@ var InternalResolver = class Resolver { } resolveSrv(hostname, callback) { - callback(null, []); + if (typeof callback != "function") { + throw new TypeError("callback must be a function"); + } + + dns.resolveSrv(hostname, callback).then( + (results) => { + callback(null, results); + }, + (error) => { + callback(error); + }, + ); } resolveCaa(hostname, callback) { @@ -209,6 +236,10 @@ export const promises = { return dns.lookup(hostname, { family: 6 }); }, + resolveSrv(hostname) { + return dns.resolveSrv(hostname); + }, + Resolver: class Resolver { constructor(options) {} @@ -259,7 +290,7 @@ export const promises = { } resolveSrv(hostname) { - return Promise.resolve([]); + return dns.resolveSrv(hostname); } resolveCaa(hostname) { @@ -286,7 +317,6 @@ for (const key of [ "resolveNs", "resolvePtr", "resolveSoa", - "resolveSrv", "resolveTxt", "reverse", ]) { diff --git a/src/deps/c_ares.zig b/src/deps/c_ares.zig index 6d692ad86..bb7bda5db 100644 --- a/src/deps/c_ares.zig +++ b/src/deps/c_ares.zig @@ -11,6 +11,145 @@ pub const ares_socket_t = c_int; pub const ares_sock_state_cb = ?*const fn (?*anyopaque, ares_socket_t, c_int, c_int) callconv(.C) void; pub const struct_apattern = opaque {}; const fd_set = c.fd_set; + +pub const 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 Options = extern struct { flags: c_int = 0, timeout: c_int = 0, @@ -152,6 +291,7 @@ pub const AddrInfo_hints = extern struct { return this.ai_flags == 0 and this.ai_family == 0 and this.ai_socktype == 0 and this.ai_protocol == 0; } }; + pub const Channel = opaque { pub fn init(comptime Container: type, this: *Container) ?Error { var channel: *Channel = undefined; @@ -273,6 +413,21 @@ pub const Channel = opaque { ares_getaddrinfo(this, host_ptr, port_ptr, hints_, AddrInfo.callbackWrapper(Type, callback), ctx); } + pub fn resolveSrv(this: *Channel, name: []const u8, comptime Type: type, ctx: *Type, comptime callback: struct_ares_srv_reply.Callback(Type)) void { + var name_buf: [1024]u8 = undefined; + const name_ptr: ?[*:0]const u8 = brk: { + if (name.len == 0 or name.len >= 1023) { + break :brk null; + } + const len = @min(name_buf.len, name_buf.len - 1); + @memcpy(&name_buf, name.ptr, len); + name_buf[len] = 0; + break :brk name_buf[0..len :0]; + }; + + ares_query(this, name_ptr, NSClass.ns_c_in, NSType.ns_t_srv, struct_ares_srv_reply.callbackWrapper(Type, callback), ctx); + } + pub inline fn process(this: *Channel, fd: i32, readable: bool, writable: bool) void { ares_process_fd( this, @@ -334,7 +489,7 @@ pub const ares_socket_functions = extern struct { }; pub extern fn ares_set_socket_functions(channel: *Channel, funcs: ?*const ares_socket_functions, user_data: ?*anyopaque) void; pub extern fn ares_send(channel: *Channel, qbuf: [*c]const u8, qlen: c_int, callback: ares_callback, arg: ?*anyopaque) void; -pub extern fn ares_query(channel: *Channel, name: [*c]const u8, dnsclass: c_int, @"type": c_int, callback: ares_callback, arg: ?*anyopaque) void; +pub extern fn ares_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; @@ -372,11 +527,90 @@ pub const struct_ares_caa_reply = extern struct { length: usize, }; pub const struct_ares_srv_reply = extern struct { - next: [*c]struct_ares_srv_reply, + next: ?*struct_ares_srv_reply, host: [*c]u8, priority: c_ushort, weight: c_ushort, port: c_ushort, + const JSC = bun.JSC; + + pub fn toJSArray(this: *struct_ares_srv_reply, parent_allocator: std.mem.Allocator, globalThis: *JSC.JSGlobalObject) JSC.JSValue { + var stack = std.heap.stackFallback(2048, parent_allocator); + var arena = std.heap.ArenaAllocator.init(stack.get()); + defer arena.deinit(); + + var allocator = arena.allocator(); + var count: usize = 0; + var srv: ?*struct_ares_srv_reply = this; + while (srv != null) : (srv = srv.?.next) { + count += 1; + } + + const array = JSC.JSValue.createEmptyArray(globalThis, count); + + srv = this; + var i: u32 = 0; + while (srv != null) { + var node = srv.?; + 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 { + var 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.weight)); + 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); + var host = this.host[0..len]; + obj.put(globalThis, JSC.ZigString.static("name"), JSC.ZigString.fromUTF8(host).toValueGC(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 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 { + var 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; + var 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: [*c]struct_ares_mx_reply, diff --git a/src/deps/uws.zig b/src/deps/uws.zig index 485af99e8..177fc1973 100644 --- a/src/deps/uws.zig +++ b/src/deps/uws.zig @@ -429,6 +429,7 @@ pub const Loop = extern struct { ready_polls: [1024]EventType align(16), const EventType = if (Environment.isLinux) std.os.linux.epoll_event else if (Environment.isMac) std.os.system.kevent64_s; + const log = bun.Output.scoped(.Loop, false); pub const InternalLoopData = extern struct { pub const us_internal_async = opaque {}; @@ -452,6 +453,17 @@ pub const Loop = extern struct { } }; + pub fn ref(this: *Loop) void { + log("ref", .{}); + this.num_polls += 1; + this.active += 1; + } + pub fn unref(this: *Loop) void { + log("unref", .{}); + this.num_polls -= 1; + this.active -= 1; + } + pub fn get() ?*Loop { return uws_get_loop(); } |