Current section
Files
Jump to
Current section
Files
lib/cidr.ex
defmodule CIDR do
use Bitwise
@moduledoc """
Classless Inter-Domain Routing (CIDR)
"""
defstruct ip: { 0, 0, 0, 0 }, mask: 32
def set_mask(cidr, mask) when mask in 0..32 do
%CIDR{ cidr | mask: mask }
end
def is_cidr(cidr) when is_map(cidr) do
cidr.__struct__ == CIDR
end
def is_cidr(string) when is_bitstring(string) do
string |> to_cidr |> is_cidr
end
def is_cidr(_), do: false
@doc """
Checks if an IP address is in the provided CIDR.
"""
def match(%CIDR{ ip: { a0, b0, c0, d0 }, mask: mask } = cidr, { a1, b1, c1, d1 } = ip) do
cidr_value = (a0 <<< 24) ||| (b0 <<< 16) ||| (c0 <<< 8) ||| d0
ip_value = (a1 <<< 24) ||| (b1 <<< 16) ||| (c1 <<< 8) ||| d1
(cidr_value >>> (32 - mask)) == (ip_value >>> (32 - mask))
end
def to_cidr(string) when string |> is_bitstring do
tokens = String.split(string, "/")
if Enum.count(tokens) == 2 do
result =
tokens
|> List.first
|> String.to_char_list
|> :inet.parse_address
is_ip_valid =
case result do
{ :ok, _ } -> true
{ :error, _ } -> false
end
parsed_netmask =
tokens
|> List.last
|> Integer.parse
netmask =
case parsed_netmask do
{ value, "" } -> value
_ -> false
end
is_netmask_valid_range =
if netmask do
(netmask >= 0) and (netmask <= 32)
else
false
end
if is_ip_valid and is_netmask_valid_range do
{ :ok, ip } = result
%CIDR{ ip: ip, mask: netmask }
else
false
end
else
false
end
end
def to_cidr(string), do: false
defp regex_ip do
~r/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
end
def parse(string) do
if string =~ regex_ip do
to_cidr(string <> "/32")
else
to_cidr(string)
end
end
end