alias DnsPhrases.Reply
alias DnsPhrases.Message
+ require Logger
+
+ import DnsPhrases.Debug.Log
+
@spec encode(Reply.t()) :: binary()
def encode(%Reply{reply_to: reply_to, phrase: phrase}) do
+ Logger.info(
+ "Encoding DNS message: tid=#{reply_to.tid} name=#{inspect(reply_to.name)} type=#{reply_to.type} class=#{reply_to.class}"
+ )
+
+ (encode_header(reply_to.tid) <>
+ encode_question(reply_to) <>
+ encode_answer(reply_to, phrase))
+ |> and_print("Encoded DNS message")
+ end
+
+ defp encode_header(tid) do
<<
- reply_to.tid::integer-size(16),
- 1::1,
+ tid::integer-size(16)-big,
+ 1::integer-size(1)-big,
0::15,
- 1::16,
- 1::16,
- 0::32
- >> <>
- encode_question(reply_to) <>
- encode_answer(reply_to, phrase)
+ 1::integer-size(16)-big,
+ 1::integer-size(16)-big,
+ 0::16,
+ 0::16
+ >>
+ |> and_print("Encoded header")
end
@spec encode_name(Message.t()) :: binary()
defp encode_name(msg) do
- msg.name
- |> Enum.map(fn part -> <<String.length(part)::integer-size(8)>> <> part <> <<0::8>> end)
- |> Enum.join(".")
+ (msg.name
+ |> Enum.map(fn part -> <<String.length(part)::integer-size(8)-big>> <> part end)
+ |> Enum.join("")) <>
+ <<0::8>>
end
@spec encode_question(Message.t()) :: binary()
defp encode_question(msg) do
- encode_name(msg) <> <<msg.type::integer-size(16), msg.class::integer-size(16)>>
+ (encode_name(msg) <> <<msg.type::integer-size(16)-big, msg.class::integer-size(16)-big>>)
+ |> and_print("Encoded question")
end
@spec encode_answer(Message.t(), binary) :: binary()
defp encode_answer(msg, phrase) do
- encode_name(msg) <> <<16::16, 256::16, 0::32, String.length(phrase)::16>> <> phrase
+ phrase_length = String.length(phrase)
+ data_length = 1 + phrase_length
+
+ (encode_name(msg) <>
+ <<16::integer-size(16)-big, 1::integer-size(16)-big, 60::integer-size(32)-big,
+ data_length::integer-size(16)-big,
+ phrase_length::integer-size(8)-big>> <>
+ phrase)
+ |> and_print("Encoded answer")
end
end
defmodule DnsPhrases.Message.Parser do
+ require Logger
+
alias DnsPhrases.Message
- @spec parse(bitstring()) :: DnsPhrases.Message.t()
- def parse(<<
- tid::integer-size(16),
- 0::1,
- _flags::15,
- 1::integer-size(16),
- _number_of_answers::16,
- _authority_rrs::16,
- _additional_rrs::16,
- data::bitstring
- >>) do
- {name, type, class} = parse_question(data)
-
- %Message{
- name: name,
- type: type,
- class: class,
- tid: tid
- }
+ import DnsPhrases.Debug.Log
+
+ @spec parse(bitstring()) :: {:ok, DnsPhrases.Message.t()} | :error
+ def parse(
+ <<
+ tid::integer-size(16),
+ 0::1,
+ _flags::15,
+ 1::integer-size(16),
+ _number_of_answers::16,
+ _authority_rrs::16,
+ _additional_rrs::16,
+ data::bitstring
+ >> = dns_message
+ ) do
+ Logger.info("Parsing incoming DNS message: #{inspect(dns_message)}")
+
+ {name, type, class} =
+ data
+ |> and_print("Parsing question")
+ |> parse_question()
+
+ Logger.info(
+ "Successfully parsed message: tid=#{tid}, name=#{inspect(name)}, type=#{type}, class=#{class}"
+ )
+
+ {:ok,
+ %Message{
+ name: name,
+ type: type,
+ class: class,
+ tid: tid
+ }}
end
+ def parse(_data), do: :error
+
@spec parse_question(bitstring) :: {list(), integer(), integer()}
def parse_question(data), do: parse_question(data, [])
alias DnsPhrases.Message.Encoder
alias DnsPhrases.Reply
+ require Logger
+
defmodule State do
defstruct([:socket])
end
{:udp, socket, ip, port, data},
%State{socket: socket} = state
) do
- msg = Parser.parse(data)
- ans = Encoder.encode(%Reply{reply_to: msg, phrase: "Hello, world!"})
+ Logger.info("Incoming query from #{inspect(ip)}")
+
+ case Parser.parse(data) do
+ {:ok, msg} ->
+ ans = Encoder.encode(%Reply{reply_to: msg, phrase: "Hello, my bro!"})
+ Logger.info("Successfully replied to #{inspect(ip)}")
+ :gen_udp.send(socket, ip, port, ans)
- case :gen_udp.send(socket, ip, port, ans) do
- {:error, reason} -> IO.puts(reason)
- :ok -> {}
+ :error ->
+ :gen_udp.send(socket, ip, port, "Invalid DNS message")
end
{:noreply, state}