Problem: Message::to_raw could only encode queries: It asserted the
answer and authority counts were zero and never wrote those. So, a
response — which carries answers — couldn’t be serialized at all.
Fix: Write the answer and authority records as well — alongside the
existing question and additional sections, and using the existing
ResourceRecord::to_raw. Then drop the query-only assertions. The order
matches the wire format: question, answer, authority, additional.
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.
When building with DNS_DEBUG or ENABLE_ALL_THE_DEBUG_MACROS there is a
linking issue with Libraries/LibDNS/Resolver.h:1003 with
undefined reference DNS::Messages::Records::SIG::to_string() const
Add DNS_API on DNS:Messages::Records::SIG definition to allow access
to the class in bin/dns, bin/TestDNSResolver, libexec/RequestServer
This first pass only applies to the following two cases:
- Public functions returning a view type into an object they own
- Public ctors storing a view type
This catches a grand total of one (1) issue, which is fixed in
the previous commit.
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.