Current section
Files
Jump to
Current section
Files
lib/srv.ex
defmodule MCPing.Srv do
@moduledoc """
Functions for resolving SRV records.
"""
@doc """
Resolves a SRV record for a given service, protocol, and hostname.
## Examples
iex> MCPing.Srv.resolve_srv_record("minecraft", "tcp", "hypixel.net", 5000)
{~c"mc.hypixel.net", 25565}
"""
def resolve_srv_record(service, protocol, hostname, timeout) do
minecraft_srv = to_charlist("_" <> service <> "._" <> protocol <> "." <> hostname)
case :inet_res.getbyname(minecraft_srv, :srv, timeout) do
{:ok, {:hostent, _, _, :srv, _, records}} -> {:ok, find_first_eligible_srv_record(records)}
_ -> nil
end
end
@doc false
def find_first_eligible_srv_record(records) when is_list(records) and length(records) == 1 do
{_, _, port, host} = hd(records)
{host, port}
end
@doc false
def find_first_eligible_srv_record(records) when is_list(records) do
{t, _} = find_first_eligible_srv_record(records, :rand.seed_s(:default))
t
end
@doc false
def find_first_eligible_srv_record(records, rand_state) do
picked_by_priority = records
|> Enum.sort_by(fn {priority, weight, _, _} -> {priority, -weight} end)
|> Enum.group_by(fn {priority, _, _, _} -> priority end, fn {_, weight, port, host} -> {weight, port, host} end)
max_priority = Map.keys(picked_by_priority) |> Enum.min()
weighted = Map.get(picked_by_priority, max_priority)
pick_weighted_random_s(weighted, rand_state)
end
defp hd_or_zero([]), do: 0
defp hd_or_zero(list), do: hd(list)
defp pick_weighted_random_s(entries, rand_state) do
# Accumulate the weights of the entries. We'll need to do this to "normalize" the weights in the provided list.
total_weights = Enum.reduce(entries, [], fn {weight, _, _}, acc -> acc ++ [weight + hd_or_zero(acc)] end)
total_weight = List.last(total_weights)
reweighted = Enum.zip_with(entries, total_weights, fn {_, port, host}, total_weight -> {total_weight, port, host} end)
{random_weight, next_state} = :rand.uniform_s(total_weight, rand_state)
{_, port, host} = Enum.find(reweighted, fn {weight, _, _} -> random_weight <= weight end)
{{host, port}, next_state}
end
end