Portable listeners - #358
Merged
Merged
Conversation
NelsonVides
force-pushed
the
fix/os-socket-portability
branch
5 times, most recently
from
July 19, 2026 12:00
82321eb to
834d2b5
Compare
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
force-pushed
the
fix/os-socket-portability
branch
3 times, most recently
from
July 19, 2026 12:37
18bb849 to
3f92e90
Compare
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
force-pushed
the
fix/os-socket-portability
branch
from
July 19, 2026 12:49
3f92e90 to
1c88361
Compare
2 tasks
NelsonVides
marked this pull request as ready for review
July 19, 2026 12:53
DXTimer
approved these changes
Jul 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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_v6onlycannot 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.parallel_factorsays.reuseport_lbis FreeBSD's own option and is now sent only there. Linux tolerated it only because it maps ontoreuseport.SO_REUSEPORT_LBdoes 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.socket_SUITEasserts the behaviour above against a running listener, so a platform that stops behaving as recorded fails the build.gen_server's state by tuple position.Platform behaviour is documented in
erldns_listenersper configuration option, and summarised as a support matrix in the README. All platform branching lives inerldns_config; no OS name appears elsewhere insrc/.Linux behaviour is unchanged.
Fixes #357
🔍 QA
Scenario: Linux is unaffected
make test. Everything passes as before.dig -p 8053 @127.0.0.1 example.com aanswers over both IPv4 and IPv6.Scenario: a wildcard listener on Windows or OpenBSD
ip => anylistener and confirm it starts and answers over IPv4.listener_ipv4_onlynaming::as the remedy.ip => "::"and confirm IPv6 is served.📋 Deployment Pre/Post tasks
N/A
listener_ipv4_onlywarnings appear in the logs of a Linux deployment.