Post off-thread font, script, and DNS completion work back to direct
Core::EventLoop references. These callbacks target the process main
loop, which is intentionally kept alive for the lifetime of the
process.
Enable -Wexit-time-destructors for all in-tree library targets and
update process-lifetime library statics so they no longer register
exit-time destructors. Long-lived caches, lookup tables, singleton
registries, and generated constants now use NeverDestroyed or leaked
references where the data is intended to live until process exit.
Update LibWeb, LibLine, and the binding generators so regenerated
sources follow the same rule instead of reintroducing destructed
statics.
A DNS label length octet is a 6-bit value, so a label can be at most 63
octets. Several code paths violated this and aborted the process:
- DomainName::from_raw() treated any length octet that was not a
compression pointer as an ordinary label, including the reserved
0b01/0b10 top-bit encodings, yielding labels of up to 191 octets.
- DomainName::to_raw() then VERIFY-aborted on any label longer than 63
octets while encoding it back to the wire.
- Resolver::lookup() encoded the outgoing query with MUST(to_raw(...)),
so even once to_raw() fails gracefully the MUST would still abort.
Together these crash RequestServer, which drives DNS for the whole
browser. When a custom resolver is configured (e.g. --dns-server), a web
page that references a host name containing a label longer than 63
characters aborts RequestServer while the query is encoded. A response
carrying a reserved-length label is also accepted by the parser and then
aborts when the name is re-encoded (for example during DNSSEC
validation).
Reject over-long labels when parsing, return an error instead of
aborting when encoding, and reject the lookup promise instead of
MUST-aborting at the query call site.
Only the literal "localhost" was special-cased, so names within
".localhost" fell through to the host resolver or upstream DNS
server, neither of which is guaranteed to map them to loopback.
RFC 6761 reserves these names as always resolving to loopback for
address queries, so answer them in-process and never forward them
upstream.
https://www.rfc-editor.org/rfc/rfc6761#section-6.3
This commit splits out synchronization primitives from LibThreading into
LibSync. This is because LibThreading depends on LibCore, while LibCore
needs the synchronization primitives from LibThreading. This worked
while they were header only, but when I tried to add an implementation
file it ran into the circular dependency. To abstract away the pthread
implementation using cpp files is necessary so the synchronization
primitives were moved to a separate library.
getaddrinfo can hang for many seconds when the system stub resolver
misbehaves; running it inline on the event loop froze every other
request, IPC, and curl socket event for the duration.
New PendingSystemResolution coalesces concurrent lookups for the same
name, dispatches the call to a Threading::ThreadPool worker, and
deferred-invokes the result back to the originating event loop. Each
caller of lookup() gets its own Core::Promise so concurrent
when_resolved/when_rejected handlers can't clobber each other; the
pending state fans out to every joined caller on completion.
Workers issue A and AAAA in parallel on separate sockets to sidestep
the systemd-resolved AAAA-drop bug, and resolve the user's promise
after the first side returns records (with a 50 ms RFC 8305 grace
window for the other side).
Adds Core::Socket::AddressFamily and an optional parameter to
resolve_host so workers can request A or AAAA specifically.
lookup_in_cache required records of every desired type, so a completed
lookup that returned only A records (e.g. for a host with no AAAA)
missed the cache when the next caller asked for {A, AAAA}, triggering
a re-query.
Pass later=true to has_record_of_type for is_done() entries so "we
asked about AAAA and got none" is treated as a definitive answer.
In-flight entries still fall through to the join-pending path.
Per-request and per-connection logging that surfaces enough detail
to diagnose where time goes when a page load misbehaves. Gated by a
new REQUESTSERVER_WIRE_DEBUG cmakedefine.
Documentation/RequestServerWireLogging.md describes each label
(wire/wire+/wire++/wire^, wire-batch, wire-stall, wire-burst,
wire-pipe-pressure, LibDNS wire-dns, UI wire-cookie) and how to read
them.
Previously, we only returned the first result that looked like an IPv6
or IPv4 address.
This cropped up when attempting to connect to https://cxbyte.me/ whilst
IPv6 on the server wasn't working. Since we only returned the first
result, which happened to be the IPv6 address, we wasn't able to
connect.
Returning all results allows curl to attempt to connect to a different
IP if one of them isn't working, and potentially make a successful
connection.
Previously, we would stop the repeat timer even if we got a null result.
This caused the pending lookup to:
- Never resolve, and
- Never get purged for too many retries
I believe the underlying issue is something on the socket level, but we
should handle this case regardless.
...and make sure it will eventually complete (or fail) by adding a
timeout retry sequence.
Fixes an issue where RequestServer would stick around after exit,
waiting for piled up DNS requests for a long time.