Skip to content

-sNODERAWSOCKETS DNS resolution, with general readiness - #27182

Closed
guybedford wants to merge 2 commits into
emscripten-core:mainfrom
guybedford:async-dns
Closed

guybedford wants to merge 2 commits into
emscripten-core:mainfrom
guybedford:async-dns

Conversation

@guybedford

@guybedford guybedford commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

Async DNS, built on top of #27207 for async readiness. Implementation diff is the last commit - b5af97b.

This adds real DNS resolution under -sNODERAWSOCKETS via node:dns, along with an async syscall variant of getaddrinfo for non-JSPI environments.

getaddrinfo() resolves numeric addresses and /etc/hosts entries (read fresh through emscripten's FS) synchronously, and returns a full addrinfo list. For a real hostname: without JSPI it returns EAI_AGAIN (resolve via the async API); under JSPI it suspends the wasm stack on the node:dns lookup and returns the addresses directly.

Async API (available in all builds):

  • emscripten_dns_lookup_async(node, service, hint) — same inputs as getaddrinfo(), returns a pollable fd that becomes readable when resolution completes.
  • emscripten_dns_lookup_result(fd, struct addrinfo **res) — reads the outcome: 0 on success (writing the addrinfo list to *res, freed with freeaddrinfo), or an EAI_* code.

The original PR in #27162 allowed the async callback for DNS lookup to be registered via emscripten_set_socket_message_callback. Instead of "hijacking" that mechanism, we can now directly use an epoll'able file descriptor to represent the dns lookup operation. On readiness, the actual emscripten_dns_lookup_result() just gets called again.

freeaddrinfo now frees the whole ai_next chain; EAI_AGAIN is added to the generated struct info.

Tested with test_dns_async, test_dns_callback (completion via emscripten_poll_with_callback), test_dns_async_net, test_dns_async_default, and test_dns_jspi, including PROXY_TO_PTHREAD variants.

@guybedford
guybedford force-pushed the async-dns branch 2 times, most recently from 5bae5d9 to 917c563 Compare June 25, 2026 19:03
@guybedford guybedford changed the title Add asynchronous DNS resolution (emscripten_dns_lookup_async) Support proper DNS resolution, with readiness Jun 25, 2026
@guybedford guybedford changed the title Support proper DNS resolution, with readiness -sNODERAWSOCKETS DNS resolution, with shared readiness Jun 25, 2026
@guybedford guybedford changed the title -sNODERAWSOCKETS DNS resolution, with shared readiness -sNODERAWSOCKETS DNS resolution, with shared readiness callback Jun 25, 2026
@guybedford guybedford changed the title -sNODERAWSOCKETS DNS resolution, with shared readiness callback -sNODERAWSOCKETS DNS resolution, with general readiness callback Jun 25, 2026
@guybedford
guybedford force-pushed the async-dns branch 3 times, most recently from d5328e2 to 4f24376 Compare June 26, 2026 23:29
@guybedford guybedford changed the title -sNODERAWSOCKETS DNS resolution, with general readiness callback -sNODERAWSOCKETS DNS resolution, with general readiness Jun 27, 2026
@guybedford
guybedford force-pushed the async-dns branch 6 times, most recently from 3319bf4 to cbebf40 Compare July 1, 2026 22:46
@sbc100

sbc100 commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Does this change actually depend on the epoll implementation?

@guybedford

Copy link
Copy Markdown
Collaborator Author

Does this change actually depend on the epoll implementation?

Formally speaking, it doesn't, but to integrate the non-JSPI support, the ability to associate a callback with the file descriptor readiness comes from the epoll PR, which previously required a custom callback integration.

@guybedford
guybedford force-pushed the async-dns branch 3 times, most recently from 6b79942 to 8afc091 Compare July 9, 2026 01:19
guybedford added a commit to guybedford/emscripten that referenced this pull request Jul 24, 2026
guybedford added a commit to guybedford/emscripten that referenced this pull request Jul 28, 2026
@guybedford
guybedford force-pushed the async-dns branch 2 times, most recently from 5410b30 to 4a79642 Compare August 19, 2026 00:00
Under -sNODERAWSOCKETS, getaddrinfo() previously fabricated fake addresses via
DNS.lookup_name. This adds real resolution backed by node:dns, plus an async
getaddrinfo so names can be resolved without blocking.

getaddrinfo() now resolves numeric addresses and /etc/hosts entries (read fresh
through emscripten's FS) synchronously, and returns a full addrinfo list (one
node per address). For a real hostname:

- without JSPI it returns EAI_AGAIN (no synchronous DNS); resolve it via the
  async API below.
- under JSPI it suspends the wasm stack on the node:dns lookup and returns the
  addresses directly (gated on ASYNCIFY == 2).

The async API (available in all builds):

- emscripten_dns_lookup_async(node, service, hint) takes the same inputs as
  getaddrinfo() and returns a pollable fd that becomes readable when resolution
  completes.
- emscripten_dns_lookup_result(fd, struct addrinfo **res) reads the outcome: 0
  on success (writing the addrinfo list to *res, freed with freeaddrinfo), or an
  EAI_* code on failure.

The completion fd is a plain pollable descriptor: wait on it with
epoll/poll/select.

freeaddrinfo now frees the whole ai_next chain; EAI_AGAIN is added to the
generated struct info.

Tested with test_dns_async, test_dns_async_net, test_dns_async_default, and
test_dns_jspi, including PROXY_TO_PTHREAD variants.
@guybedford guybedford closed this Sep 15, 2026
guybedford added a commit that referenced this pull request Sep 16, 2026
This gives `getaddrinfo()` real name resolution under
`-sNODERAWSOCKETS`. Split out from #27182, which also added a new
asynchronous DNS API; this PR is only the change to the existing
blocking API, allowing any async API work to follow separately.

Previously every hostname resolved to a fake `172.29.x.x` address, which
is fine for the websocket proxy transport but not for real Node.js
sockets, where the address has to be connectable.

* Hostnames resolve via `node:dns` (which honors the host's
`/etc/hosts`). The lookup is asynchronous, so `getaddrinfo` blocks by
returning a Promise where the calling stack can wait on it: a
sync-proxied pthread (`PROXY_SYNC_ASYNC`, e.g. `main()` under
`PROXY_TO_PTHREAD`), or `ASYNCIFY`/`JSPI` by suspending through
`Asyncify.handleAsync`, which also holds a runtime keepalive so a user
callback completing mid-lookup does not exit the runtime. Numeric
addresses and errors still return synchronously, so `JSPI` does not pay
a microtask on those.
* Where there is no stack that can wait (the event-loop thread itself),
a hostname returns `EAI_AGAIN` without starting a lookup.
* Results may now be an `addrinfo` linked list (multiple DNS records,
`AF_UNSPEC`), and `freeaddrinfo` walks the whole chain.

Everything is gated on `NODERAWSOCKETS`; without it `getaddrinfo` is
unchanged.

Implementation-wise `getaddrinfo` keeps its existing body and returns
either an `EAI_*` code or a Promise of one. The pthread proxy receive
side now accepts a synchronous value from a `PROXY_SYNC_ASYNC` function
(`Promise.resolve(rtn).then(...)`), so no wrapper is needed for the sync
paths.

Tested with `test_noderawsockets_dns` (numeric, `EAI_AGAIN`
single-threaded), and
`test_noderawsockets_dns_blocking{,_asyncify,_jspi}` (multi-address
lists across families, and a timer firing during the lookup under
`EXIT_RUNTIME`).
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.

2 participants