R13B04 inet_res:resolve/4 inet_udp Port leak
Bob Ippolito
bob@REDACTED
Wed May 26 02:00:39 CEST 2010
It appears that there may be an inet_udp Port leak in
inet_res:resolve/4, our current workaround is to spawn a new process
to call this function. We've noticed this primarily for a service that
regularly does a UDP DNS query that fails (because the response is too
big) and then we retry over TCP.
This is what the state of the process looked like when it was leaking ports:
(node@REDACTED)1> length(lists:filter(fun erlang:is_port/1, element(2,
erlang:process_info(whereis(dns_gen_server), links)))).
577
(node@REDACTED)2> lists:usort([erlang:port_info(P, name) || P <-
lists:filter(fun erlang:is_port/1, element(2,
erlang:process_info(whereis(dns_gen_server), links)))]).
[{name,"udp_inet"}]
The code looked like this, before the workaround was implemented:
%% @spec dns(string()) -> [string()]
%% @doc Return the A records (IPv4 IPs) as strings for the given Host name.
%% This may return an empty list if there no A records for this Host name.
dns(Host) when is_list(Host) ->
dns(Host, fun inet_res:resolve/4).
dns(Host, ResolveFun) ->
case ResolveFun(Host, in, a, []) of
{ok, Msg} ->
ips_for_answers(Msg);
{error, {nxdomain, _}} ->
[];
{error, timeout} ->
%% retry with TCP
case ResolveFun(Host, in, a, [{usevc, true}]) of
{ok, Msg} ->
ips_for_answers(Msg);
{error, {nxdomain, _}} ->
[];
Error = {error, _} ->
Error
end;
Error = {error, _} ->
Error
end.
ips_for_answers(Msg) ->
[inet_parse:ntoa(inet_dns:rr(Answer, data))
|| Answer <- inet_dns:msg(Msg, anlist)].
The workaround we used was to call it indirectly with this function, I
couldn't find anything in OTP that did the same thing that didn't have
local call optimizations.
%% @spec process_apply(atom(), atom(), [term()]) -> term()
%% @doc erlang:apply(M, F, A) in a temporary process and return the results.
process_apply(M,F,A) ->
%% We can't just use rpc here because there's a local call optimization.
Parent = self(),
Fun = fun () ->
try
Parent ! {self(), erlang:apply(M, F, A)}
catch
Class:Reason ->
Stacktrace = erlang:get_stacktrace(),
Parent ! {self(), Class, Reason, Stacktrace}
end
end,
{Pid, Ref} = erlang:spawn_monitor(Fun),
receive
{Pid, Res} ->
receive {'DOWN', Ref, process, Pid, _} -> ok end,
Res;
{Pid, Class, Reason, Stacktrace} ->
receive {'DOWN', Ref, process, Pid, _} -> ok end,
erlang:error(erlang:raise(Class, Reason, Stacktrace));
{'DOWN', Ref, process, Pid, Reason} ->
erlang:exit(Reason)
end.
More information about the erlang-bugs
mailing list