#ifndef __ResolverThreadScope_h__ #define __ResolverThreadScope_h__ #include "ThreadScope.h" #include namespace mail { /// /// ResolverThreadScope /// /// glibc's classic resolver state (_res, touched by res_query() and /// getaddrinfo()) is thread-LOCAL, not process-global - confirmed /// directly (a small standalone test program): a fresh thread's own /// _res starts zeroed, and its first real resolver call lazily /// auto-initializes it to glibc's own compiled-in defaults /// (RES_TIMEOUT=5s, RES_DFLRETRY=2 tries - see /usr/include/resolv.h), /// completely ignoring anything main() set beforehand. An earlier fix /// attempt called res_init()+set _res.retrans/_res.retry once in /// main() before EPoll::start() spawned its worker threads - that /// only ever affected the MAIN thread's own copy, a pure no-op for /// every one of the 4 worker threads that actually run /// SPFChecker/DMARCChecker's res_query()/getaddrinfo() calls for real /// inbound mail. Same shape as MysqlThreadScope's own reason to /// exist (see its doc comment) - a client library keying off /// per-thread state that nothing else in this stack initializes on /// EPoll's worker threads. /// /// Bounding these here is what actually keeps a single slow or /// non-responding DNS server from blocking a worker thread long /// enough to stall every OTHER connection on every listening port /// too (SMTP/SMTPTLS/IMAP/POP3/admin all share these same 4 threads) - /// SPF's own "include:" mechanism can chain up to 10 sequential /// lookups for a single message, so the per-query bound matters more /// here than it would for an isolated one-off query. /// class ResolverThreadScope : public core::ThreadScope { public: void onThreadStart() override { res_init(); _res.retrans = 2; _res.retry = 1; } }; } #endif