Packages
A PCAP library written in Elixir. This does not wrap a C or Erlang PCAP library. This library parses pcap files, however it does not yet support most protocols that can be contained within a pcap file. The only supported protocols at the moment are: * Ethernet * IPv4 * UDP * DNS
Current section
Files
Jump to
Current section
Files
lib/protocol/dns.ex
defimpl String.Chars, for: Protocol.Dns do
@doc """
Prints a DNS packet to a human readable string
"""
@spec to_string(Protocol.Dns.t) :: String.t
def to_string(dns) do
"""
DNS:
#{dns.header}
Length: #{byte_size(dns.data)}
Parsed:
Questions:
#{dns.parsed |> elem(0) |> Enum.map(&String.Chars.to_string/1) |> Enum.join("\n ")}
Answers:
#{dns.parsed |> elem(1) |> Enum.map(&String.Chars.to_string/1) |> Enum.join("\n ")}
Authorities:
#{dns.parsed |> elem(2) |> Enum.map(&String.Chars.to_string/1) |> Enum.join("\n ")}
Additionals:
#{dns.parsed |> elem(3) |> Enum.map(&String.Chars.to_string/1) |> Enum.join("\n ")}
Raw: #{ExPcap.Binaries.to_raw(dns.data)}
""" |> String.trim
end
end
defimpl String.Chars, for: Protocol.Dns.Header do
@doc """
Prints a DNS packet header to a human readable string
"""
@spec to_string(Protocol.Dns.t) :: String.t
def to_string(dns) do
"""
id: #{ExPcap.Binaries.to_string(dns.id)} #{ExPcap.Binaries.to_hex(dns.id)}
qr: #{ExPcap.Binaries.to_string(dns.qr)} #{Protocol.Dns.Header.qr_name(dns.qr)}
opcode: #{ExPcap.Binaries.to_string(dns.opcode)} #{Protocol.Dns.Header.opcode_name(dns.opcode)}
aa: #{ExPcap.Binaries.to_string(dns.aa)} #{Protocol.Dns.Header.aa_name(dns.aa)}
tc: #{ExPcap.Binaries.to_string(dns.tc)} #{Protocol.Dns.Header.tc_name(dns.tc)}
rd: #{ExPcap.Binaries.to_string(dns.rd)} #{Protocol.Dns.Header.rd_name(dns.rd)}
ra: #{ExPcap.Binaries.to_string(dns.ra)} #{Protocol.Dns.Header.ra_name(dns.ra)}
z: #{ExPcap.Binaries.to_string(dns.z)} #{Protocol.Dns.Header.z_name(dns.z)}
rcode: #{ExPcap.Binaries.to_string(dns.rcode)} #{Protocol.Dns.Header.rcode_name(dns.rcode)}
qdcnt: #{ExPcap.Binaries.to_string(dns.qdcnt)}
ancnt: #{ExPcap.Binaries.to_string(dns.ancnt)}
nscnt: #{ExPcap.Binaries.to_string(dns.nscnt)}
arcnt: #{ExPcap.Binaries.to_string(dns.arcnt)}
""" |> String.trim
end
end
defimpl PayloadType, for: Protocol.Dns do
@doc """
Returns the parser that will parse the body of the DNS packet
"""
@spec payload_parser(Protocol.Dns.t) :: PayloadType.t
def payload_parser(_dns) do
nil
# case dns.header.qr do
# <<0 :: size(1)>> -> Protocol.Dns.Question
# <<1 :: size(1)>> -> Protocol.Dns.ResourceRecord
# end
end
end
defimpl PayloadParser, for: Protocol.Dns do
@doc """
Parses the body of the DNS packet
"""
@spec from_data(binary) :: any
def from_data(data) do
data |> Protocol.Dns.from_data
end
end
defmodule Protocol.Dns.Header do
@moduledoc """
A parsed DNS packet header
"""
defstruct id: <<>>,
qr: <<>>,
opcode: <<>>,
aa: <<>>,
tc: <<>>,
rd: <<>>,
ra: <<>>,
z: <<>>,
rcode: <<>>,
qdcnt: <<>>,
ancnt: <<>>,
nscnt: <<>>,
arcnt: <<>>
@type t :: %Protocol.Dns.Header{
id: binary,
qr: bitstring,
opcode: bitstring,
aa: bitstring,
tc: bitstring,
rd: bitstring,
ra: bitstring,
z: bitstring,
rcode: bitstring,
qdcnt: non_neg_integer,
ancnt: non_neg_integer,
nscnt: non_neg_integer,
arcnt: non_neg_integer
}
@doc """
Is this a query or a response?
"""
@spec qr_name(binary) :: :QUERY | :ANSWER
def qr_name(qr) do
case qr do
<<0 :: size(1)>> -> :QUERY
<<1 :: size(1)>> -> :ANSWER
end
end
@doc """
Is this response authoritative?
"""
@spec aa_name(binary) :: :NOT_AUTHORITATIVE | :AUTHORITATIVE | :""
def aa_name(aa) do
case aa do
<<0 :: size(1)>> -> :NOT_AUTHORITATIVE
<<1 :: size(1)>> -> :AUTHORITATIVE
_ -> :""
end
end
@doc """
Is this response truncated?
"""
@spec tc_name(binary) :: :NOT_TRUNCATED | :TRUNCATED | :""
def tc_name(tc) do
case tc do
<<0 :: size(1)>> -> :NOT_TRUNCATED
<<1 :: size(1)>> -> :TRUNCATED
_ -> :""
end
end
@doc """
Is recursion desired?
"""
@spec rd_name(binary) :: :NO_RECURSION_DESIRED | :RECURSION_DESIRED | :""
def rd_name(rd) do
case rd do
<<0 :: size(1)>> -> :NO_RECURSION_DESIRED
<<1 :: size(1)>> -> :RECURSION_DESIRED
_ -> :""
end
end
@doc """
Is recursion available?
"""
@spec ra_name(binary) :: :NO_RECURSION_AVAILABLE | :RECURSION_AVAILABLE | :""
def ra_name(ra) do
case ra do
<<0 :: size(1)>> -> :NO_RECURSION_AVAILABLE
<<1 :: size(1)>> -> :RECURSION_AVAILABLE
_ -> :""
end
end
@doc """
The first bit is reserved.
The second bit indciates if the response was authenticated or not.
The third bit indciates if the data was authenticated or not.
"""
@spec z_name(binary) :: :atom
def z_name(z) do
case z do
<<0b000 :: size(3)>> -> :"RESERVED - NOT AUTHENTICATED - NON AUTHENTICATED DATA"
<<0b001 :: size(3)>> -> :"RESERVED - NOT AUTHENTICATED - AUTHENTICATED DATA"
<<0b010 :: size(3)>> -> :"RESERVED - AUTHENTICATED - NON AUTHENTICATED DATA"
<<0b011 :: size(3)>> -> :"RESERVED - AUTHENTICATED - AUTHENTICATED DATA"
<<0b100 :: size(3)>> -> :"RESERVED - NOT AUTHENTICATED - NON AUTHENTICATED DATA"
<<0b101 :: size(3)>> -> :"RESERVED - NOT AUTHENTICATED - AUTHENTICATED DATA"
<<0b110 :: size(3)>> -> :"RESERVED - AUTHENTICATED - NON AUTHENTICATED DATA"
<<0b111 :: size(3)>> -> :"RESERVED - AUTHENTICATED - AUTHENTICATED DATA"
end
end
@doc """
What is the op code of this DNS packet?
"""
@spec opcode_name(non_neg_integer) :: :QUERY | :STATUS | :NOTIFY | :UPDATE | :""
def opcode_name(opcode) do
case opcode do
0 -> :QUERY
2 -> :STATUS
4 -> :NOTIFY
5 -> :UPDATE
_ -> :""
end
end
@doc """
What is the r code of this DNS packet?
"""
@spec rcode_name(non_neg_integer) :: :atom
def rcode_name(rcode) do
case rcode do
0 -> :NOERROR
1 -> :FORMERR
2 -> :SERVFAIL
3 -> :NXDOMAIN
4 -> :NOTIMPL
5 -> :REFUSED
6 -> :YXDOMAIN
7 -> :YXRRSET
8 -> :NXRRSET
9 -> :NOTAUTH
10 -> :NOTZONE
16 -> :BADVERS_OR_BADSIG
17 -> :BADKEY
18 -> :BADTIME
19 -> :BADMODE
20 -> :BADNAME
21 -> :BADALG
22 -> :BADTRUNC
_ -> :""
end
end
end
defmodule Protocol.Dns do
@moduledoc """
A parsed DNS packet
"""
@bytes_in_header 12
defstruct header: %Protocol.Dns.Header{},
parsed: {
[%Protocol.Dns.Question{}], # questions
[%Protocol.Dns.ResourceRecord{}], # answers
[%Protocol.Dns.ResourceRecord{}], # authorities
[%Protocol.Dns.ResourceRecord{}], # additionals
<<>> # leftover bytes
},
data: <<>>
@type t :: %Protocol.Dns{
header: Protocol.Dns.Header.t,
parsed: {
[Protocol.Dns.Question.t], # questions
[Protocol.Dns.ResourceRecord.t], # answers
[Protocol.Dns.ResourceRecord.t], # authorities
[Protocol.Dns.ResourceRecord.t], # additionals
binary
},
data: binary
}
@doc """
Parses a DNS header
"""
@spec header(binary) :: Protocol.Dns.Header.t
def header(data) do
<<
id :: bytes-size(2),
qr :: bits-size(1),
opcode :: bits-size(4),
aa :: bits-size(1),
tc :: bits-size(1),
rd :: bits-size(1),
ra :: bits-size(1),
z :: bits-size(3),
rcode :: bits-size(4),
qdcnt :: unsigned-integer-size(16),
ancnt :: unsigned-integer-size(16),
nscnt :: unsigned-integer-size(16),
arcnt :: unsigned-integer-size(16),
_payload :: binary
>> = data
%Protocol.Dns.Header{
id: id,
qr: qr,
opcode: ExPcap.Binaries.to_uint4(opcode),
aa: aa,
tc: tc,
rd: rd,
ra: ra,
z: z,
rcode: ExPcap.Binaries.to_uint4(rcode),
qdcnt: qdcnt,
ancnt: ancnt,
nscnt: nscnt,
arcnt: arcnt
}
end
@doc """
Returns a parsed DNS packet
"""
@spec from_data(binary) :: Protocol.Dns.t
def from_data(data) do
<< _header :: bytes-size(@bytes_in_header), payload :: binary >> = data
header = header(data)
dns = Protocol.Dns.ResourceRecord.read_dns(header, data, payload)
%Protocol.Dns{
header: header,
parsed: dns,
data: payload
}
end
end