curl/curl
DNS resolution
Active contributors: Daniel Stenberg, Stefan Eissing, Viktor Szakats
Purpose
curl resolves hostnames in one of four ways, all sharing a single in-memory cache. The frontend is lib/hostip.c; the backends are lib/asyn-base.c (async dispatch), lib/asyn-ares.c (c-ares), lib/asyn-thrdd.c (thread pool), and lib/doh.c (DNS-over-HTTPS). The resolved-address cache is lib/dnscache.c.
Key abstractions
| Symbol | File | Description |
|---|---|---|
struct Curl_dns_entry |
lib/hostip.h |
A cached set of addresses for one (hostname, port) pair |
struct Curl_addrinfo |
lib/curl_addrinfo.h |
Linked list of addresses, replaces getaddrinfo for portability |
struct Curl_async |
lib/asyn.h |
Per-handle async-resolver state |
Curl_resolv() |
lib/hostip.c |
The "resolve this name" entry point |
Curl_resolv_pollset() |
lib/hostip.c |
Tells the multi loop which FDs to poll while resolving |
Curl_dnscache_* |
lib/dnscache.c |
Insert / lookup / evict cached entries |
Curl_doh() |
lib/doh.c |
DoH worker: builds an HTTPS request, parses RFC 8484 wire format |
Resolver selection
Selection is compile-time via configure/CMake flags:
| Build flag | Backend | Notes |
|---|---|---|
--enable-ares / CURL_USE_ARES |
c-ares | Fully async; no extra threads. lib/asyn-ares.c |
--enable-threaded-resolver |
POSIX thread pool | One thread per pending resolve. lib/asyn-thrdd.c |
| (default if neither) | Synchronous getaddrinfo |
Blocks the calling thread. lib/hostip4.c, lib/hostip6.c |
| Runtime | DNS-over-HTTPS | Activated via CURLOPT_DOH_URL. lib/doh.c |
Most modern builds use threaded-resolver because it works on every platform and integrates with the multi loop without external dependencies.
How it works
sequenceDiagram
participant Multi
participant Hostip as hostip.c
participant Cache as dnscache.c
participant Async as asyn-thrdd / asyn-ares
participant DoH as doh.c
Multi->>Hostip: Curl_resolv("example.com",443)
Hostip->>Cache: lookup
alt cache hit
Cache-->>Hostip: entry
Hostip-->>Multi: ready
else cache miss
alt CURLOPT_DOH_URL set
Hostip->>DoH: spawn private easy handle
DoH-->>Hostip: RFC 8484 reply parsed
else
Hostip->>Async: kickoff
end
Multi->>Multi: state = MSTATE_RESOLVING
Note over Multi,Async: each tick, hostip checks if async finished
Async-->>Hostip: addresses
Hostip->>Cache: insert
Hostip-->>Multi: ready
endWhile MSTATE_RESOLVING, the resolver-specific Curl_resolv_pollset reports the FDs the multi loop should poll (c-ares' control socket, the DoH transfer's socket, or none for the threaded resolver). Once the resolver returns, Curl_dnscache_insert puts the entry in the cache and the connection setup proceeds to MSTATE_CONNECTING.
DoH
DoH is unusual: instead of a system resolver call, libcurl issues a real HTTPS request to a DoH server (like https://dns.google/dns-query) for the resolution. The implementation in lib/doh.c reuses the regular libcurl easy/multi machinery: it allocates a private easy handle, sends the RFC 8484 wire-format query, parses the response, and feeds the addresses into the standard cache.
This means a DoH-resolved host itself still has to be resolved — the DoH server hostname is resolved once (synchronously or via the configured resolver) and then the connection to it is reused for all subsequent DoH lookups via the conn cache.
DNS cache
lib/dnscache.c is a hash from (host, port) to Curl_dns_entry. Entries have a TTL (CURLOPT_DNS_CACHE_TIMEOUT, default 60 seconds). Lookups are constant-time; eviction is lazy (checked on lookup) plus explicit (curl_share_cleanup).
The cache is shareable across multi handles via curl_share_setopt(sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS) — see Connection management.
fake_addrinfo (lib/fake_addrinfo.c) supplies a synthetic resolver result for --resolve and CURLOPT_RESOLVE overrides, e.g. --resolve example.com:443:127.0.0.1. These pre-populate the cache and never expire until curl_share_cleanup or end of run.
HTTPS RR
Recent versions parse HTTPS resource records (lib/httpsrr.c) so that, for example, an HTTPS RR can advertise an alternative HTTP/3 endpoint or ECH support. The parser is invoked alongside A/AAAA lookups and feeds into the connection setup.
Integration points
- Transfer engine:
MSTATE_RESOLVINGpollsCurl_resolv_pollset. See Transfer engine. - Connection cache: resolved entries are looked up before connection-cache lookup so HSTS/alt-svc can rewrite the destination first. See Connection management.
- Share handle:
CURL_LOCK_DATA_DNSshares the cache between handles. - Happy Eyeballs: cf-ip-happy uses both v4 and v6 addresses returned by the resolver. See Connection filters.
Entry points for modification
- New resolver backend → add a new
lib/asyn-*.cimplementingCurl_resolver_*functions; plumb selection inlib/curl_setup.hand the build files. - Different cache eviction policy →
Curl_dnscache_*inlib/dnscache.c. - Add or fix a DoH transport detail →
lib/doh.c. Note that DoH responses use the same code path as any other HTTPS request: a bug there can manifest as a DoH bug. - HTTPS RR parsing →
lib/httpsrr.c. The integration point with the connection setup is inlib/url.c.
Related pages
- Connection management
- Transfer engine
- API → easy —
CURLOPT_RESOLVE,CURLOPT_DOH_URL
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.