Current section
Files
Jump to
Current section
Files
lib/radius/dict/parser.ex.exs
defmodule Radius.Dict.Parser do
@moduledoc false
def parse(binary) do
{:ok, _, "", ctx, _, _} =
parse_bin(binary, context: [attributes: [], values: [], prepend_key: []])
ctx
end
def parse_index(binary) do
includes =
binary
|> String.split("\n")
|> Enum.filter(&String.starts_with?(&1, "$INCLUDE dictionary."))
|> Enum.map(fn include ->
String.replace_leading(include, "$INCLUDE dictionary.", "")
end)
parsed = parse(binary)
{includes, Map.get(parsed, :attributes), Map.get(parsed, :values)}
end
# parsec:Radius.Dict.Parser
import NimbleParsec
separator = ascii_string([?\s, ?\t], min: 1)
name = ascii_string([not: ?\t, not: ?\n, not: ?\s], min: 1)
vendor = string("VENDOR") |> replace(:vendor)
attribute = string("ATTRIBUTE") |> replace(:attribute)
value = string("VALUE") |> replace(:value)
hex_identifier =
ignore(string("0x"))
|> choice([
ascii_string([?a..?f, ?A..?F, ?0..?9], 8),
ascii_string([?a..?f, ?A..?F, ?0..?9], 4),
ascii_string([?a..?f, ?A..?F, ?0..?9], 3),
ascii_string([?a..?f, ?A..?F, ?0..?9], 2),
ascii_string([?a..?f, ?A..?F, ?0..?9], 1)
])
|> reduce(:hex_to_integer)
identifier =
choice([
hex_identifier,
integer(10),
integer(5),
integer(4),
integer(3),
integer(2),
integer(1)
])
attribute_type =
choice([
string("string") |> replace(:string),
string("octets") |> replace(:octets),
string("ipaddr") |> replace(:ipaddr),
string("integer") |> replace(:integer),
string("signed") |> replace(:signed),
string("date") |> replace(:date),
string("ifid") |> replace(:ifid),
string("ipv6addr") |> replace(:ipv6addr),
string("ipv6prefix") |> replace(:ipv6prefix),
string("ether") |> replace(:ether),
string("abinary") |> replace(:abinary),
string("byte") |> replace(:byte),
string("short") |> replace(:short)
])
comment =
optional(ignore(separator))
|> string("#")
|> optional(ascii_string([not: ?\n], min: 1))
include_line =
string("$INCLUDE")
|> ignore(separator)
|> ignore(string("dictionary."))
|> concat(name)
begin_line =
string("BEGIN-VENDOR")
|> ignore(separator)
|> concat(name)
end_line =
string("END-VENDOR")
|> ignore(separator)
|> concat(name)
attribute_line =
attribute
|> ignore(separator)
|> concat(name)
|> ignore(separator)
|> concat(identifier)
|> ignore(separator)
|> concat(attribute_type)
|> optional(
ignore(separator)
|> optional(string("has_tag") |> replace(true) |> unwrap_and_tag(:has_tag))
|> optional(ignore(string(",")))
|> optional(ignore(string("encrypt=")) |> integer(1) |> unwrap_and_tag(:encrypt))
|> tag(:opts)
)
|> post_traverse(:store_attribute)
value_line =
value
|> ignore(separator)
|> concat(name)
|> ignore(separator)
|> concat(name)
|> ignore(separator)
|> concat(identifier)
|> post_traverse(:store_value)
vendor_line =
vendor
|> ignore(separator)
|> concat(name)
|> ignore(separator)
|> concat(identifier)
|> optional(
ignore(separator)
|> ignore(string("format="))
|> integer(1)
|> ignore(string(","))
|> integer(1)
|> optional(ignore(string(",c")))
|> tag(:format)
)
|> post_traverse(:store_vendor)
vendor_chapter =
ignore(begin_line)
|> post_traverse({:prepend_store_key, [[:vendor]]})
|> repeat(
choice([
ignore(string("\n")),
attribute_line,
value_line,
ignore(comment)
])
)
|> ignore(end_line)
|> post_traverse({:prepend_store_key, [[]]})
defparsecp(
:parse_bin,
choice([
ignore(string("\n")),
attribute_line,
value_line,
vendor_line,
vendor_chapter,
ignore(include_line),
ignore(comment)
])
|> repeat()
)
# parsec:Radius.Dict.Parser
defp prepend_store_key(_rest, _, context, _line, _offset, key) do
{[], Map.put(context, :prepend_key, key)}
end
defp store_vendor(_rest, vendor, context, _line, _offset) do
[:vendor, name, id | opts] = Enum.reverse(vendor)
{[], Map.put(context, :vendor, %{id: id, name: name, opts: opts, attributes: [], values: []})}
end
defp store_attribute(_rest, attr, context, _line, _offset) do
[:attribute, name, id, type | rest] = Enum.reverse(attr)
opts = Keyword.get(rest, :opts, [])
attribute = %{name: name, id: id, type: type, opts: opts}
{[],
update_in(context, context.prepend_key ++ [:attributes], fn attrs -> [attribute | attrs] end)}
end
defp store_value(_rest, [value, name, attr_name, :value], context, _line, _offset) do
value = %{attr: attr_name, value: value, name: name}
{[], update_in(context, context.prepend_key ++ [:values], fn values -> [value | values] end)}
end
defp hex_to_integer([hex]) do
String.to_integer(hex, 16)
end
end