Skip to content

Portable listeners - #358

Merged
NelsonVides merged 4 commits into
mainfrom
fix/os-socket-portability
Jul 27, 2026
Merged

Portable listeners#358
NelsonVides merged 4 commits into
mainfrom
fix/os-socket-portability

Conversation

@NelsonVides

@NelsonVides NelsonVides commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

As reported by #357, erldns did not boot on Windows. Three socket options were at fault, and the last of them only became visible once the platforms themselves were running in CI. So I just took the chance to add many platforms into CI and fix what was not working for each.

  • ipv6_v6only cannot be unset on Windows or OpenBSD, so a wildcard listener never bound. It now binds IPv4 there and warns at boot, naming :: as the way to also serve IPv6.
  • Windows cannot bind a second socket to a port already in use, so listeners there run one acceptor and one listen socket whatever parallel_factor says.
  • reuseport_lb is FreeBSD's own option and is now sent only there. Linux tolerated it only because it maps onto reuseport.
  • On FreeBSD, SO_REUSEPORT_LB does not balance IPv4 arriving v4-mapped on a dual-stack socket, so every query reached one socket. A wildcard listener there now binds a socket per family, which CI measures at four times the throughput.
  • CI runs on Windows, macOS, FreeBSD, OpenBSD and NetBSD. socket_SUITE asserts the behaviour above against a running listener, so a platform that stops behaving as recorded fails the build.
  • Three test-harness bugs fixed on the way, each independent of the above: peer nodes were never stopped and accumulated for a whole run, ports were hardcoded, and a socket was read out of a gen_server's state by tuple position.

Platform behaviour is documented in erldns_listeners per configuration option, and summarised as a support matrix in the README. All platform branching lives in erldns_config; no OS name appears elsewhere in src/.

Linux behaviour is unchanged.

Fixes #357

🔍 QA

Scenario: Linux is unaffected

  • Run make test. Everything passes as before.
  • Start erldns with the default config and confirm dig -p 8053 @127.0.0.1 example.com a answers over both IPv4 and IPv6.

Scenario: a wildcard listener on Windows or OpenBSD

  • Boot with the default ip => any listener and confirm it starts and answers over IPv4.
  • Confirm the boot log contains listener_ipv4_only naming :: as the remedy.
  • Add a second listener with ip => "::" and confirm IPv6 is served.

📋 Deployment Pre/Post tasks

N/A

:shipit: Deployment Verification

  • Existing Linux deployments answer queries as before, with no change in listener count or socket options.
  • No listener_ipv4_only warnings appear in the logs of a Linux deployment.

@NelsonVides NelsonVides self-assigned this Jul 18, 2026
@NelsonVides NelsonVides added the enhancement New feature, enhancement or code changes, not related to defects label Jul 18, 2026
@NelsonVides
NelsonVides force-pushed the fix/os-socket-portability branch 5 times, most recently from 82321eb to 834d2b5 Compare July 19, 2026 12:00
Ports 8053, 8054 and 8083 were fixed constants, so two suites bound the same
port and any of them could collide with whatever else is on the host, or with
the ranges Windows reserves for dynamic allocation.

Listeners that no test sends traffic to now use port 0. The rest need a known
port on both UDP and TCP, which port 0 cannot give since each protocol would
resolve its own, so app_helper:reserve_port/0 picks a free one instead.
Finding the port of a UDP listener meant reaching into a gen_server's state
and pulling the socket out by tuple position, which breaks silently the moment
a field is added or reordered.

The acceptor answers get_socket/1 instead, so callers ask the module rather
than knowing its call protocol. Both it and the module are -doc false, so
nothing is added to the public API, and the lookup loses three levels of
nesting that only existed to turn a bad match into a named error.
@NelsonVides
NelsonVides force-pushed the fix/os-socket-portability branch 3 times, most recently from 18bb849 to 3f92e90 Compare July 19, 2026 12:37
erldns did not boot on Windows (#357), and nothing noticed because nothing
had ever run there. Three socket options were at fault, each in a different
way, and finding them needed CI on the platforms themselves.

`ipv6_v6only` cannot be unset on Windows or OpenBSD, so a wildcard listener
never bound: it now binds IPv4 there and warns, naming `::` as the way to
serve IPv6. Windows cannot bind a second socket to a port already in use, so
listeners there run one acceptor and one listen socket whatever
`parallel_factor` says. `reuseport_lb` is FreeBSD's own option and is sent
only there; Linux tolerated it only because it maps onto `reuseport`.

FreeBSD needed more than options. `SO_REUSEPORT_LB` does not balance IPv4
arriving v4-mapped on a dual-stack socket, so every query reached one socket
and a native IPv4 socket received four times as much. A wildcard listener
there now binds a socket per family, sharing the acceptor and listen-socket
counts between them so `parallel_factor` still describes the total, and
aggregating statistics so the split stays out of the public stats shape.

All of this lives behind `erldns_config`, so no OS name appears elsewhere in
`src/`. socket_SUITE asserts it against a real listener on every platform CI
covers: that each family binds, that the acceptor count matches what the
platform can bind, and that traffic spreads wherever the kernel does that.
Linux and FreeBSD spread it; macOS, OpenBSD, NetBSD and Windows do not.
@NelsonVides
NelsonVides force-pushed the fix/os-socket-portability branch from 3f92e90 to 1c88361 Compare July 19, 2026 12:49
@NelsonVides
NelsonVides marked this pull request as ready for review July 19, 2026 12:53
@NelsonVides
NelsonVides requested review from a team and DXTimer July 19, 2026 12:55
@NelsonVides
NelsonVides merged commit 85adc92 into main Jul 27, 2026
11 checks passed
@NelsonVides
NelsonVides deleted the fix/os-socket-portability branch July 27, 2026 10:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature, enhancement or code changes, not related to defects

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Example configuration does not work on Windows

2 participants