Skip to content

Fix bootstrap socket watcher exhaustion - #213

Open
szybnev wants to merge 1 commit into
aarond10:masterfrom
szybnev:fix/dynamic-bootstrap-watchers
Open

Fix bootstrap socket watcher exhaustion#213
szybnev wants to merge 1 commit into
aarond10:masterfrom
szybnev:fix/dynamic-bootstrap-watchers

Conversation

@szybnev

@szybnev szybnev commented Sep 9, 2026

Copy link
Copy Markdown

Bootstrap DNS resolution can terminate the proxy with:

c-ares needed more IO event handler, than the number of provided nameservers: 2

dns_poller_init() allocates one libev watcher per configured nameserver. The socket-state callback exits when c-ares needs another watcher, even though the nameserver count is not a bound on simultaneous sockets. Related report: #205. This PR addresses that fatal allocation limit, not the separate WAN recovery behavior in #207.

Allocate individually owned watcher nodes on demand and reuse inactive nodes. Their addresses stay stable while registered with libev; growing a contiguous array with realloc() would invalidate those pointers. Stop and free all nodes during poller cleanup. Allocation failure remains fatal.

Only bootstrap watcher bookkeeping changes at runtime. This does not alter DNS provider selection, TLS verification, HTTP socket handling, routing, or OpenWrt configuration.

Verification

  • Added a CTest regression that drives the real socket-state callback with 40 sockets and two configured bootstrap servers, checks pointer stability and reuse, and exercises cleanup with an active watcher. It makes no external DNS queries.
  • The test exits with the fatal error on unpatched upstream; passes with this change.
  • Valgrind: 157 allocations / 157 frees, zero memory errors, zero bytes remaining at exit.
  • ARM64/musl build succeeded with c-ares 1.34.8 and libev 4.33.
  • The same runtime fix built against the OpenWrt package's source revision was tested on an OpenWrt 25.12.5 router: 30/30 isolated DoH queries, followed by 18/18 client DNS queries after deployment. This is a bounded functional check, not a long-term stability claim. The precise network event that originally required the extra socket was not captured.
  • Existing Robot suite, run as a non-root user in the same Alpine ARM64 container: 7 passed / 7 failed on both unpatched upstream and this patch, with the same failing test names and timeout/connection-failure messages. The three existing Valgrind tests pass in both runs. I am not claiming the full suite is green in this environment.

Run the new regression independently with:

tests/unit/run_dns_poller_test.sh

It is also registered with CTest as dns_poller_watchers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant