Current section
Files
Jump to
Current section
Files
lib/dns.ex
defmodule DNS do
@type nameserver :: {String.t(), port :: 1..65535}
@type res_option ::
:inet_res.res_option()
| {:alt_nameservers, [nameserver()]}
| {:nameservers, [nameserver()]}
# :inet_res.rr_type is not exported
@type rr_type :: :a | :aaaa | :caa | :cname | :gid | :hinfo | :ns | :mb | :md | :mg
| :mf | :minfo | :mx | :naptr | :null | :ptr | :soa | :spf | :srv
| :txt | :uid | :uinfo | :unspec | :uri | :wks
@doc """
Resolves the answer for a DNS query.
## Examples
iex> DNS.resolve("tungdao.com")
{:ok, [{1, 1, 1, 1}]}
iex> DNS.resolve("tungdao.com", :txt)
{:ok, [['v=spf1 a mx ~all']]}
iex> DNS.resolve("tungdao.com", :a, nameservers: [{"8.8.4.4", 53}])
{:ok, [{1, 1, 1, 1}]}
iex> DNS.resolve("tungdao.com", :a, usevc: true)
{:ok, [{1, 1, 1, 1}]}
"""
@spec resolve(String.t(), rr_type(), [res_option()], timeout()) :: {:ok, list()} | {:error, :inet_res.res_error()}
def resolve(domain, type \\ :a, opts \\ [], timeout \\ :infinity) do
case query(domain, type, opts, timeout) do
{:ok, %DNS.Record{anlist: anlist}} when is_list(anlist) and length(anlist) > 0 ->
data =
anlist
|> Enum.map(& &1.data)
|> Enum.reject(&is_nil/1)
{:ok, data}
{:ok, %DNS.Record{anlist: anlist}} when is_list(anlist) and length(anlist) == 0 ->
{:error, :not_found}
error ->
error
end
end
@doc """
Queries the DNS server and returns the result.
## Examples
Queries for A records:
iex> DNS.query("tungdao.com")
Queries for the MX records:
iex> DNS.query("tungdao.com", :mx)
Queries for A records, using OpenDNS:
iex> DNS.query("tungdao.com", :a, nameservers: [{"8.8.4.4", 53}])
Queries for A records, using OpenDNS, with TCP:
iex> DNS.query("tungdao.com", :a, nameservers: [{"8.8.4.4", 53}], usevc: true)
"""
@spec query(String.t(), rr_type(), [res_option()], timeout()) :: {:ok, DNS.Record.t()} | {:error, :inet_res.res_error()}
def query(name, type \\ :a, opts \\ [], timeout \\ :infinity) do
case :inet_res.resolve(to_charlist(name), :in, type, transform_opts(opts), timeout) do
{:ok, response} ->
{:ok, DNS.Record.from_record(response)}
{:error, error} ->
{:error, error}
end
end
defp transform_opts(opts) do
Enum.map(opts, fn {key, value} -> {key, transform_opt(key, value)} end)
end
defp transform_opt(key, nameservers) when key in [:alt_nameservers, :nameservers] and is_list(nameservers) do
Enum.map(nameservers, fn {nameserver, port} ->
case nameserver do
n when is_tuple(n) -> {n, port}
n when is_binary(n) ->
{:ok, address} = :inet.parse_address(to_charlist(n))
{address, port}
end
end)
end
defp transform_opt(_key, value) do
value
end
end