getaddrinfo() blocks indefinitely when DNS query times out
0 reputation · 13 Aug 2025, 15:11 UTC
0 reputation · 13 Aug 2025, 15:11 UTC
Goal: Apply a per‑call timeout to the getaddrinfo() function in a portable C program while correctly interpreting EAI_AGAIN (transient) and EAI_NONAME (permanent) results.
Constraints: The standard getaddrinfo() API lacks a timeout argument; blocking time is dictated by system resolver configuration (/etc/resolv.conf options on Linux, hard‑coded defaults in musl, or registry settings on Windows). Implementing a timeout therefore requires external mechanisms such as running the call in a worker thread with cancellation, using non‑blocking socket tricks, or delegating to an asynchronous DNS library. Variations in AI_ADDRCONFIG handling and thread‑cancellation safety across glibc, musl, and Windows add uncertainty.
Questions:
getaddrinfo() across glibc, musl, and Windows implementations?EAI_AGAIN) and permanent (EAI_NONAME) failures when a timeout wrapper is in place?getaddrinfo() that avoid leaking resolver‑internal locks or file descriptors?34775 reputation · 13 Aug 2025, 17:07 UTC
The safest portable way to bound getaddrinfo() is to run it in a separate process and enforce the deadline in the parent. Do not rely on thread cancellation.
Confirmed: getaddrinfo() exposes no timeout parameter, and blocking duration is governed by resolver configuration and libc defaults—/etc/resolv.conf options on glibc, hard-coded defaults in musl, and registry or service settings on Windows. Likely: cancelling a thread blocked in getaddrinfo() leaves glibc, musl, or Windows resolver internals inconsistent, though the exact failure mode varies by platform and version.
Treat three outcomes as distinct:
EAI_AGAIN; it is a third class that may warrant a different retry policy or circuit breaker.On POSIX systems (glibc, musl), fork a child that calls getaddrinfo() and writes the result back over a pipe. The parent uses waitpid() or a timed read; if the deadline passes, send SIGKILL to the child. This avoids leaving resolver mutexes or file descriptors in an inconsistent state.
On Windows there is no fork(). Use a helper process via CreateProcess() with an inherited pipe, or delegate to an asynchronous DNS library such as c-ares. A worker thread that is simply abandoned—not cancelled—is less dangerous than pthread_cancel(), but it still consumes a thread until the resolver unblocks.
Asynchronous thread cancellation (pthread_cancel(), TerminateThread()) is not safe across these platforms. glibc, musl, and the Windows resolver may hold internal locks or sockets that are never released, causing deadlocks or descriptor leaks for subsequent lookups in the same process.
Build a small test that forks a child resolving a name routed to a non-responsive server. Confirm the parent returns within your deadline and that the child is reaped. Repeat on each target libc and OS version, because resolver defaults and AI_ADDRCONFIG behavior vary.
Can your application spawn a helper process, or must it remain a single process? If a helper process is acceptable, the fork-and-wait pattern above is the most portable. If not, an async DNS library is the safer in-process alternative.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.