dns: remove the remaining unsafe from the resolver module

JA
2mo ago
+4589-5385

What

Same programme as #40055 / #40135 / #40136 / #40139 / #40187, applied to DNS: src/runtime/dns_jsc/dns.rs 259 → 0, cares_jsc.rs 38 → 0, dns_sd.rs 43 → 0, by giving the C libraries typed, owning wrappers one layer down instead of open-coding pointer handling in the resolver.

  • c-ares (bun_cares_sys): OwnedChannel (Drop = ares_destroy) created with Channel::init(ThisPtr<C: ChannelOwner>); queries take a Box<H: QueryHandler | AddrInfoHandler | HostentHandler | NameInfoHandler> and hand it back exactly once through one generic trampoline per callback shape (c-ares guarantees one callback on success / ECANCELLED / EDESTRUCTION); replies are AresBox<T: AresAllocated> freed with the right deallocator; struct_hostent / reply structs expose safe accessors and iterators instead of raw NULL-terminated arrays; set_servers, get_sockaddr (now returns a sockaddr_storage — the old code wrote a sockaddr_in6 through a 16-byte &mut sockaddr), inet_pton, parse_* are safe.
  • Resolver ownership: Resolver is #[derive(RefCounted)]; every in-flight lookup holds Option<RefPtr<Resolver>> (was IntrusiveRc::init_ref), the armed timeout timer's ref is a timer_ref slot; host fns / dispatch take this: ThisPtr<Self>. The per-type pending caches hold their waiters by value (Vec<Option<PendingEntry<L>>> + occupancy mask) instead of intrusive next/tail pointers into HiveArray slots; coalescing, the 32-entry limit, waiter order and request_completed points are unchanged.
  • Process-wide connect-path cache (internal): requests are OwnedThis<Request> in the locked global cache; C (usockets, QUIC) receives ThisPtr<Request>; cross-thread holders are BackRef<Request, Mut>; refcount/valid are atomics, notify a guarded vec, the result an OnceLock<AddrInfoResult>. The C-facing addrinfo_result{,_entry} and the DnsWaitingSocket / DnsPendingConnect notify handles live in bun_uws_sys::addrinfo / bun_http::H3 (Send handles expose only the thread-safe notify; same-thread notification goes through the borrowed socket / owned box). All Bun__addrinfo_*, Bun__DNS__*, __bun_dns_prefetch are HOST_EXPORTs (the prefetch export now takes a length instead of treating install's non-terminated slice as a ZStr).
  • libuv (Windows): bun_libuv_sys::getaddrinfo(loop, Box<T: GetAddrInfoRequest>, ..) round-trips the box through req.data; InflightGetAddrInfo::cancel; UvAddrInfo (Drop = uv_freeaddrinfo); SocketPoll<T> for the c-ares fd polls. libinfo / dns_sd (macOS): bun_sys::dns_sd::{Connection, Query} with one reply trampoline; the shared connection is a thread-local Rc. POSIX: bun_sys::net::AddrInfoList (owned getaddrinfo list), Address::from_sockaddr_bytes; bun_io::OwnedFilePoll.
  • ManagedTask::new_boxed<T: RunOnce>; generator learns Option<&CStr>; #[host_fn] this: ThisPtr<Self> receiver support and bun_ptr::OwnedThis are cherry-picked byte-for-byte from #40139.

Testing

Debug+ASAN: test/js/bun/dns (87), test/js/node/dns (171), all 27 test-dns* / test-worker-dns* / test-net-dns* node parallel tests, socket-dns-error, socket.test, websocket, node-http, fetch pass (fetch's root-permission cases and the 150 ms AbortSignal.timeout redirect case fail on any debug build here). Hand-driven: lookup via c-ares/libc/system backends + coalescing, resolve4/reverse/lookupService, Resolver.cancel() mid-query against a black-hole UDP socket (ECANCELLED), prefetch + getCacheStats, fetch through the connect-path cache, workers exiting/terminated with queries in flight, interior-NUL names — exit 0, no ASAN output. clippy clean on every touched crate; rust-check-all x86_64/aarch64-windows-msvc + aarch64-apple-darwin pass (Windows/macOS paths type-checked, not executed here). An additional adversarial review pass over the cache redesign and the cross-thread cache found no behavioural regressions; its latent findings are fixed in the second commit.

Follow-up (review): bun_dns::ResultAny is gone (native completions carry Option<&ResultList>; GetAddrInfoResult::to_list takes &bun_sys::net::AddrInfoList; Windows AddrInfoList::from_uv adopts the libuv block) — options_jsc.rs is now 0 unsafe too; the connect-path waiter lists live under GlobalCache's single lock (no nested Guarded), and an orphaned request whose refcount hits 0 is freed immediately; Backend/BackendDnsSd collapsed to a #[cfg(target_os="macos")] dns_sd: JsCell<QueryState> field; ManagedTask::new_owned retired in favour of new_boxed (RunTestsTask, QueuedEvent implement RunOnce); libuv cancel handle and dns_sd parent/child ordering are structural (Rc token / Rc<PrimaryRef>).