Packages

Simple library in pure Elixir for encoding and decoding pcap file data

Current section

Files

Jump to
pcapex lib dump.ex
Raw

lib/dump.ex

defmodule Pcapex.Dump do
alias Pcapex.Packet
@magic_number 0xA1B2C3D4
@version_major 2
@version_minor 4
@max_packet_size 65535
defstruct network_type: nil,
packets: []
@type t :: %__MODULE__{
network_type: pos_integer,
packets: list(Packet.t())
}
@spec linux_sll_network_type() :: integer
def linux_sll_network_type(), do: 113
@spec ethernet_network_type() :: integer
def ethernet_network_type(), do: 1
@spec new(list(Packet.t()), pos_integer) :: t
def new(packets, network_type \\ ethernet_network_type()) do
%__MODULE__{
packets: packets,
network_type: network_type
}
end
@spec to_binary(t) :: binary()
def to_binary(dump) do
dump_header(dump.network_type) <> Enum.map_join(dump.packets, &record/1)
end
# TODO implement better error handling
@spec from_binary(binary) :: t()
def from_binary(data) do
<<
@magic_number::unsigned-32,
@version_major::unsigned-16,
@version_minor::unsigned-16,
_thiszone::signed-32,
_sigfigs::unsigned-32,
_max_packet_size::unsigned-32,
network_type::unsigned-32,
records_data::binary
>> = data
records_data |> parse_records() |> new(network_type)
end
# guint32 magic_number; /* magic number; always 0xa1b2c3d4 */
# guint16 version_major; /* major version number; currently 2 */
# guint16 version_minor; /* minor version number; currently 4 */
# gint32 thiszone; /* GMT to local correction; always 0 */
# guint32 sigfigs; /* accuracy of timestamps; always */
# guint32 snaplen; /* max length of captured packets, in octets; usually 65535 */
# guint32 network; /* data link type */
defp dump_header(network_type) do
<<
@magic_number::unsigned-32,
@version_major::unsigned-16,
@version_minor::unsigned-16,
_thiszone = 0::signed-32,
_sigfigs = 0::unsigned-32,
@max_packet_size::unsigned-32,
network_type::unsigned-32
>>
end
defp record(packet) do
packet_header(packet) <> packet.data
end
@usecs_in_sec 1000 * 1000
# guint32 ts_sec; /* timestamp seconds */
# guint32 ts_usec; /* timestamp microseconds */
# guint32 incl_len; /* number of octets of packet saved in file */
# guint32 orig_len; /* actual length of packet */
defp packet_header(packet) do
ts_sec = trunc(packet.timestamp_usec / @usecs_in_sec)
ts_usec = packet.timestamp_usec - ts_sec * @usecs_in_sec
<<
ts_sec::unsigned-32,
ts_usec::unsigned-32,
_incl_len = byte_size(packet.data)::unsigned-32,
_orig_len = byte_size(packet.data)::unsigned-32
>>
end
defp parse_records(data, results \\ [])
defp parse_records(_data = <<>>, results), do: Enum.reverse(results)
defp parse_records(data, results) do
<<
ts_sec::unsigned-32,
ts_usec::unsigned-32,
incl_len::unsigned-32,
orig_len::unsigned-32,
packet_data::binary-size(incl_len),
rest_data::binary
>> = data
timestamp_usec = ts_sec * @usecs_in_sec + ts_usec
packet = Packet.new(packet_data, timestamp_usec, orig_len)
parse_records(rest_data, [packet | results])
end
end