fix: close UDP sockets in error paths to prevent fd leak

get_bulk, set, and get_multi used 'with' chains that skipped
close_socket when the operation failed, leaking file descriptors.
Over time this exhausted the fd limit causing EMFILE crashes.

Switched to case + try/after to guarantee socket cleanup.
This commit is contained in:
Graham McIntire 2026-02-14 09:50:12 -06:00
parent 12b1a64f8d
commit e195e5c9e8

View file

@ -247,10 +247,19 @@ defmodule SnmpKit.SnmpLib.Manager do
if opts[:version] == :v1 do
{:error, :getbulk_requires_v2c}
else
with {:ok, socket} <- create_socket(opts),
{:ok, response} <- perform_bulk_operation(socket, host, normalized_oid, opts),
:ok <- close_socket(socket) do
extract_bulk_result(response)
case create_socket(opts) do
{:ok, socket} ->
try do
case perform_bulk_operation(socket, host, normalized_oid, opts) do
{:ok, response} -> extract_bulk_result(response)
{:error, reason} -> {:error, reason}
end
after
close_socket(socket)
end
{:error, reason} ->
{:error, reason}
end
end
end
@ -290,11 +299,19 @@ defmodule SnmpKit.SnmpLib.Manager do
opts = merge_default_opts(opts)
normalized_oid = normalize_oid(oid)
with {:ok, socket} <- create_socket(opts),
{:ok, response} <-
perform_set_operation(socket, host, normalized_oid, {type, value}, opts),
:ok <- close_socket(socket) do
extract_set_result(response)
case create_socket(opts) do
{:ok, socket} ->
try do
case perform_set_operation(socket, host, normalized_oid, {type, value}, opts) do
{:ok, response} -> extract_set_result(response)
{:error, reason} -> {:error, reason}
end
after
close_socket(socket)
end
{:error, reason} ->
{:error, reason}
end
end
@ -334,10 +351,17 @@ defmodule SnmpKit.SnmpLib.Manager do
opts = merge_default_opts(opts)
normalized_oids = Enum.map(oids, &normalize_oid/1)
with {:ok, socket} <- create_socket(opts) do
results = get_multi_with_socket(socket, host, normalized_oids, opts)
:ok = close_socket(socket)
process_multi_results(results)
case create_socket(opts) do
{:ok, socket} ->
try do
results = get_multi_with_socket(socket, host, normalized_oids, opts)
process_multi_results(results)
after
close_socket(socket)
end
{:error, reason} ->
{:error, reason}
end
end
end