Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ Server listening at 8000
iex(3)> Process.exit(server_pid, :normal)
```

### DOH client

```elixir
iex> DOH.resolve("www.google.com")
{:ok, [{172, 217, 13, 228}]}
```

For more information, see [API Reference](https://hexdocs.pm/dns/2.1.2/api-reference.html)

## License
Expand Down
53 changes: 53 additions & 0 deletions lib/doh.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
defmodule DOH do
def resolve(domain, type \\ :a, doh_server \\ {"https://dns.google/dns-query", []}) do
case query(domain, type, doh_server).anlist do
answers when is_list(answers) and length(answers) > 0 ->
data =
answers
|> Enum.map(& &1.data)
|> Enum.reject(&is_nil/1)

{:ok, data}

_ ->
{:error, :not_found}
end
end

def query(domain, type \\ :a, doh_server \\ {"https://dns.google/dns-query", []}) do
record = %DNS.Record{
header: %DNS.Header{rd: true},
qdlist: [%DNS.Query{domain: to_charlist(domain), type: type, class: :in}]
}

dns_request = DNS.Record.encode(record)

case request(dns_request, doh_server) do
{:ok, response} -> response
{:error, _} -> %DNS.Record{}
end
end

defp request(query, doh_server) do
{http_server, options} = doh_server

case HTTPoison.post(
http_server,
query,
[{"Content-Type", "application/dns-message"}],
options
) do
{:ok, %HTTPoison.Response{status_code: 200, body: response}} ->
{:ok, DNS.Record.decode(response)}

{:ok, %HTTPoison.Response{}} ->
{:error, "query error"}

{:error, :fmt} ->
{:error, "http error"}

{:error, %HTTPoison.Error{reason: reason}} ->
{:error, reason}
end
end
end
3 changes: 2 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ defmodule DNS.Mixfile do
{:socket, "~> 0.3.13"},
{:ex_doc, ">= 0.0.0", only: [:dev]},
{:earmark, ">= 0.0.0", only: [:dev]},
{:credo, "~> 0.9.0-rc1", only: [:dev, :test], runtime: false}
{:credo, "~> 0.9.0-rc1", only: [:dev, :test], runtime: false},
{:httpoison, "~> 1.6"},
]
end

Expand Down
9 changes: 9 additions & 0 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
%{
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
"certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"},
"credo": {:hex, :credo, "0.9.1", "f021affa11b32a94dc2e807a6472ce0914289c9132f99644a97fc84432b202a1", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:poison, ">= 0.0.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
"earmark": {:hex, :earmark, "1.2.4", "99b637c62a4d65a20a9fb674b8cffb8baa771c04605a80c911c4418c69b75439", [:mix], [], "hexpm"},
"ex_doc": {:hex, :ex_doc, "0.18.2", "993e0a95e9fbb790ac54ea58e700b45b299bd48bc44b4ae0404f28161f37a83e", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"},
"hackney": {:hex, :hackney, "1.15.2", "07e33c794f8f8964ee86cebec1a8ed88db5070e52e904b8f12209773c1036085", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.5", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
"httpoison": {:hex, :httpoison, "1.6.1", "2ce5bf6e535cd0ab02e905ba8c276580bab80052c5c549f53ddea52d72e81f33", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"},
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
"socket": {:hex, :socket, "0.3.13", "98a2ab20ce17f95fb512c5cadddba32b57273e0d2dba2d2e5f976c5969d0c632", [:mix], [], "hexpm"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"},
}
15 changes: 15 additions & 0 deletions test/dns_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,21 @@ defmodule DNSTest do
test "responds with error if domain not found" do
assert {:error, :not_found} = DNS.resolve('uifqourefhoqeirhfqeurfhqehfqoerfiuqe.com')
end

test "default DNS-over-HTTPS servers" do
{:ok, results} = DOH.resolve("www.google.com")

assert is_list(results)
assert length(results) > 0
end

test "can query custom DOH server" do
{:ok, results} =
DOH.resolve("www.google.com", :a, {"https://cloudflare-dns.com/dns-query", []})

assert is_list(results)
assert length(results) > 0
end
end

describe "query" do
Expand Down