Current section

Files

Jump to
erldns src erldns_config.erl
Raw

src/erldns_config.erl

-module(erldns_config).
-moduledoc "Provide application-wide configuration access.".
-export([
use_root_hints/0,
socket_opts/2,
split_socket_opts/1,
split_socket_opts/2,
balances_load/0,
socket_count/1,
socket_count/2
]).
-doc "Use IANA DNS root servers as hints".
-spec use_root_hints() -> boolean().
use_root_hints() ->
case application:get_env(erldns, use_root_hints) of
{ok, Flag} when is_boolean(Flag) ->
Flag;
_ ->
true
end.
-doc false.
-spec socket_opts([atom() | tuple()], os:type()) -> [atom() | tuple()].
socket_opts(Opts, OsType) ->
reuse_opts(family_opts(Opts, OsType), OsType).
%% Windows and OpenBSD reject `{ipv6_v6only, false}', so a dual-stack wildcard
%% never binds there; fall back to IPv4 rather than not listening at all.
family_opts(Opts, OsType) ->
case lists:member({ipv6_v6only, false}, Opts) andalso not supports_dual_stack(OsType) of
true ->
[inet | Opts -- [inet6, {ipv6_v6only, false}]];
false ->
Opts
end.
%% `reuseaddr' is left to ranch and `base_udp_opts/0', which set it already;
%% ranch also rejects it as an explicit listen option.
reuse_opts(Opts, OsType) ->
case reuse_opt(OsType) of
none -> Opts;
Opt -> Opts ++ [{Opt, true} || not lists:keymember(Opt, 1, Opts)]
end.
%% On FreeBSD `SO_REUSEPORT_LB' is a distinct option from `SO_REUSEPORT', and
%% asking for both leaves the classic non-balancing one in charge: measured, all
%% traffic reaches the last socket bound. Linux maps the two onto one option and
%% DragonFly, whose implementation FreeBSD adapted, balances under the plain
%% name, so `reuseport' is right for both. Windows shares no ports at all.
reuse_opt({win32, _}) ->
none;
reuse_opt({unix, freebsd}) ->
reuseport_lb;
reuse_opt(_) ->
reuseport.
supports_dual_stack({win32, _}) ->
false;
supports_dual_stack({unix, openbsd}) ->
false;
supports_dual_stack(_) ->
true.
-doc false.
-spec split_socket_opts([atom() | tuple()]) -> [[atom() | tuple()]].
split_socket_opts(Opts) ->
split_socket_opts(Opts, os:type()).
%% One option list per socket family a listener should bind. FreeBSD's
%% `SO_REUSEPORT_LB' does not balance IPv4 traffic arriving on a dual-stack
%% socket, so a wildcard listener there delivers every packet to one acceptor.
%% Giving the acceptors a family each restores balancing within both. Only
%% FreeBSD is known to need this; platforms without CI keep one socket.
-doc false.
-spec split_socket_opts([atom() | tuple()], os:type()) -> [[atom() | tuple()]].
split_socket_opts(Opts, OsType) ->
case lists:member({ipv6_v6only, false}, Opts) andalso not balances_dual_stack(OsType) of
true ->
Rest = Opts -- [inet6, {ipv6_v6only, false}, {ip, any}],
[
[inet, {ip, any} | Rest],
[inet6, {ipv6_v6only, true}, {ip, any} | Rest]
];
false ->
[Opts]
end.
balances_dual_stack({unix, freebsd}) ->
false;
balances_dual_stack(_) ->
true.
-doc false.
-spec balances_load() -> boolean().
balances_load() ->
balances_load(os:type()).
%% Whether the kernel spreads traffic across sockets sharing a port rather than
%% delivering all of it to one of them. These two are the platforms CI measures
%% doing so; everywhere else the extra acceptors bind but never receive.
%% DragonFly is likely to belong here, since its own implementation is the one
%% FreeBSD adapted, but nothing verifies that.
-spec balances_load(os:type()) -> boolean().
balances_load({unix, linux}) ->
true;
balances_load({unix, freebsd}) ->
true;
balances_load(_) ->
false.
-doc false.
-spec socket_count(pos_integer()) -> pos_integer().
socket_count(Wanted) ->
socket_count(Wanted, os:type()).
%% How many sockets a listener should open on one port. Windows refuses to bind
%% a second, so the extras would each fail to start rather than start smaller.
-doc false.
-spec socket_count(pos_integer(), os:type()) -> pos_integer().
socket_count(_, {win32, _}) ->
1;
socket_count(Wanted, _) ->
Wanted.