Update deps
This commit is contained in:
parent
9047519d2f
commit
09ebfcf747
145
.credo.exs
145
.credo.exs
|
@ -1,145 +0,0 @@
|
|||
# This file contains the configuration for Credo and you are probably reading
|
||||
# this after creating it with `mix credo.gen.config`.
|
||||
#
|
||||
# If you find anything wrong or unclear in this file, please report an
|
||||
# issue on GitHub: https://github.com/rrrene/credo/issues
|
||||
#
|
||||
%{
|
||||
#
|
||||
# You can have as many configs as you like in the `configs:` field.
|
||||
configs: [
|
||||
%{
|
||||
#
|
||||
# Run any exec using `mix credo -C <name>`. If no exec name is given
|
||||
# "default" is used.
|
||||
#
|
||||
name: "default",
|
||||
#
|
||||
# These are the files included in the analysis:
|
||||
files: %{
|
||||
#
|
||||
# You can give explicit globs or simply directories.
|
||||
# In the latter case `**/*.{ex,exs}` will be used.
|
||||
#
|
||||
included: ["lib/", "src/", "web/", "apps/"],
|
||||
excluded: [~r"/_build/", ~r"/deps/"]
|
||||
},
|
||||
#
|
||||
# If you create your own checks, you must specify the source files for
|
||||
# them here, so they can be loaded by Credo before running the analysis.
|
||||
#
|
||||
requires: [],
|
||||
#
|
||||
# If you want to enforce a style guide and need a more traditional linting
|
||||
# experience, you can change `strict` to `true` below:
|
||||
#
|
||||
strict: false,
|
||||
#
|
||||
# If you want to use uncolored output by default, you can change `color`
|
||||
# to `false` below:
|
||||
#
|
||||
color: true,
|
||||
#
|
||||
# You can customize the parameters of any check by adding a second element
|
||||
# to the tuple.
|
||||
#
|
||||
# To disable a check put `false` as second element:
|
||||
#
|
||||
# {Credo.Check.Design.DuplicatedCode, false}
|
||||
#
|
||||
checks: [
|
||||
{Credo.Check.Consistency.ExceptionNames},
|
||||
{Credo.Check.Consistency.LineEndings},
|
||||
{Credo.Check.Consistency.ParameterPatternMatching},
|
||||
{Credo.Check.Consistency.SpaceAroundOperators},
|
||||
{Credo.Check.Consistency.SpaceInParentheses},
|
||||
{Credo.Check.Consistency.TabsOrSpaces},
|
||||
|
||||
# For some checks, like AliasUsage, you can only customize the priority
|
||||
# Priority values are: `low, normal, high, higher`
|
||||
#
|
||||
{Credo.Check.Design.AliasUsage, priority: :low},
|
||||
|
||||
# For others you can set parameters
|
||||
|
||||
# If you don't want the `setup` and `test` macro calls in ExUnit tests
|
||||
# or the `schema` macro in Ecto schemas to trigger DuplicatedCode, just
|
||||
# set the `excluded_macros` parameter to `[:schema, :setup, :test]`.
|
||||
#
|
||||
{Credo.Check.Design.DuplicatedCode, excluded_macros: []},
|
||||
|
||||
# You can also customize the exit_status of each check.
|
||||
# If you don't want TODO comments to cause `mix credo` to fail, just
|
||||
# set this value to 0 (zero).
|
||||
#
|
||||
{Credo.Check.Design.TagTODO, exit_status: 2},
|
||||
{Credo.Check.Design.TagFIXME},
|
||||
|
||||
{Credo.Check.Readability.FunctionNames},
|
||||
{Credo.Check.Readability.LargeNumbers},
|
||||
{Credo.Check.Readability.MaxLineLength, priority: :low, max_length: 120},
|
||||
{Credo.Check.Readability.ModuleAttributeNames},
|
||||
{Credo.Check.Readability.ModuleDoc},
|
||||
{Credo.Check.Readability.ModuleNames},
|
||||
{Credo.Check.Readability.ParenthesesOnZeroArityDefs},
|
||||
{Credo.Check.Readability.ParenthesesInCondition},
|
||||
{Credo.Check.Readability.PredicateFunctionNames},
|
||||
{Credo.Check.Readability.PreferImplicitTry},
|
||||
{Credo.Check.Readability.RedundantBlankLines},
|
||||
{Credo.Check.Readability.StringSigils},
|
||||
{Credo.Check.Readability.TrailingBlankLine},
|
||||
{Credo.Check.Readability.TrailingWhiteSpace},
|
||||
{Credo.Check.Readability.VariableNames},
|
||||
{Credo.Check.Readability.Semicolons},
|
||||
{Credo.Check.Readability.SpaceAfterCommas},
|
||||
|
||||
{Credo.Check.Refactor.DoubleBooleanNegation},
|
||||
{Credo.Check.Refactor.CondStatements},
|
||||
{Credo.Check.Refactor.CyclomaticComplexity},
|
||||
{Credo.Check.Refactor.FunctionArity},
|
||||
{Credo.Check.Refactor.LongQuoteBlocks},
|
||||
{Credo.Check.Refactor.MatchInCondition},
|
||||
{Credo.Check.Refactor.NegatedConditionsInUnless},
|
||||
{Credo.Check.Refactor.NegatedConditionsWithElse},
|
||||
{Credo.Check.Refactor.Nesting},
|
||||
{Credo.Check.Refactor.PipeChainStart},
|
||||
{Credo.Check.Refactor.UnlessWithElse},
|
||||
|
||||
{Credo.Check.Warning.BoolOperationOnSameValues},
|
||||
{Credo.Check.Warning.IExPry},
|
||||
{Credo.Check.Warning.IoInspect},
|
||||
{Credo.Check.Warning.LazyLogging},
|
||||
{Credo.Check.Warning.OperationOnSameValues},
|
||||
{Credo.Check.Warning.OperationWithConstantResult},
|
||||
{Credo.Check.Warning.UnusedEnumOperation},
|
||||
{Credo.Check.Warning.UnusedFileOperation},
|
||||
{Credo.Check.Warning.UnusedKeywordOperation},
|
||||
{Credo.Check.Warning.UnusedListOperation},
|
||||
{Credo.Check.Warning.UnusedPathOperation},
|
||||
{Credo.Check.Warning.UnusedRegexOperation},
|
||||
{Credo.Check.Warning.UnusedStringOperation},
|
||||
{Credo.Check.Warning.UnusedTupleOperation},
|
||||
{Credo.Check.Warning.RaiseInsideRescue},
|
||||
|
||||
# Controversial and experimental checks (opt-in, just remove `, false`)
|
||||
#
|
||||
{Credo.Check.Refactor.ABCSize, false},
|
||||
{Credo.Check.Refactor.AppendSingleItem, false},
|
||||
{Credo.Check.Refactor.VariableRebinding, false},
|
||||
{Credo.Check.Warning.MapGetUnsafePass, false},
|
||||
{Credo.Check.Consistency.MultiAliasImportRequireUse, false},
|
||||
|
||||
# Deprecated checks (these will be deleted after a grace period)
|
||||
#
|
||||
{Credo.Check.Readability.Specs, false},
|
||||
{Credo.Check.Warning.NameRedeclarationByAssignment, false},
|
||||
{Credo.Check.Warning.NameRedeclarationByCase, false},
|
||||
{Credo.Check.Warning.NameRedeclarationByDef, false},
|
||||
{Credo.Check.Warning.NameRedeclarationByFn, false},
|
||||
|
||||
# Custom checks can be created using `mix credo.gen.check`.
|
||||
#
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
3
.formatter.exs
Normal file
3
.formatter.exs
Normal file
|
@ -0,0 +1,3 @@
|
|||
[
|
||||
inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
]
|
|
@ -1,4 +1,11 @@
|
|||
image: elixir:1.5
|
||||
image: elixir:latest
|
||||
|
||||
cache:
|
||||
key: "$CI_JOB_NAME"
|
||||
paths:
|
||||
- deps
|
||||
- _build
|
||||
- /root/.mix
|
||||
|
||||
variables:
|
||||
MIX_ENV: "test"
|
||||
|
@ -11,8 +18,20 @@ before_script:
|
|||
test:
|
||||
script:
|
||||
- mix test
|
||||
- mix credo --strict
|
||||
- mix inch --pedantic
|
||||
|
||||
after_script:
|
||||
- mix inch.report
|
||||
credo:
|
||||
script:
|
||||
- mix credo --strict
|
||||
|
||||
audit:
|
||||
script:
|
||||
- mix hex.audit
|
||||
|
||||
format:
|
||||
script:
|
||||
- mix format --check-formatted
|
||||
|
||||
# inch:
|
||||
# script:
|
||||
# - mix inch --pedantic
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ defimpl Enumerable, for: IP.Prefix do
|
|||
...> |> Enum.count()
|
||||
128
|
||||
"""
|
||||
@spec count(Prefix.t) :: {:ok, non_neg_integer} | {:error, module}
|
||||
@spec count(Prefix.t()) :: {:ok, non_neg_integer} | {:error, module}
|
||||
def count(prefix), do: {:ok, Prefix.space(prefix)}
|
||||
|
||||
@doc """
|
||||
|
@ -32,7 +32,7 @@ defimpl Enumerable, for: IP.Prefix do
|
|||
...> |> Enum.member?(~i(192.0.2.250))
|
||||
true
|
||||
"""
|
||||
@spec member?(Prefix.t, Address.t) :: {:ok, boolean} | {:error, module}
|
||||
@spec member?(Prefix.t(), Address.t()) :: {:ok, boolean} | {:error, module}
|
||||
def member?(prefix, %Address{} = address), do: {:ok, Prefix.contains_address?(prefix, address)}
|
||||
|
||||
@doc """
|
||||
|
@ -45,26 +45,35 @@ defimpl Enumerable, for: IP.Prefix do
|
|||
...> |> Enum.map(fn a -> IP.Address.to_string(a) end)
|
||||
["192.0.2.130", "192.0.2.132", "192.0.2.134"]
|
||||
"""
|
||||
@spec reduce(Prefix.t, Enumerable.acc, Enumerable.reducer) :: \
|
||||
Enumerable.result
|
||||
def reduce(_, {:halt, acc}, _fun), do: {:halted, acc}
|
||||
def reduce({prefix, pos, last}, {:suspend, acc}, fun), do: {:suspended, acc, &reduce({prefix, pos, last}, &1, fun)}
|
||||
@spec reduce(Prefix.t(), Enumerable.acc(), Enumerable.reducer()) :: Enumerable.result()
|
||||
def reduce(_, {:halt, acc}, _fun), do: {:halted, acc}
|
||||
|
||||
def reduce({prefix, pos, last}, {:suspend, acc}, fun),
|
||||
do: {:suspended, acc, &reduce({prefix, pos, last}, &1, fun)}
|
||||
|
||||
def reduce(%Prefix{} = prefix, {:cont, acc}, fun) do
|
||||
first = prefix
|
||||
first =
|
||||
prefix
|
||||
|> Prefix.first()
|
||||
|> Address.to_integer()
|
||||
|
||||
last = prefix
|
||||
last =
|
||||
prefix
|
||||
|> Prefix.last()
|
||||
|> Address.to_integer()
|
||||
|
||||
reduce({prefix, first, last}, {:cont, acc}, fun)
|
||||
end
|
||||
|
||||
def reduce({%Prefix{address: %Address{version: version}} = prefix, pos, last}, {:cont, acc}, fun) do
|
||||
def reduce(
|
||||
{%Prefix{address: %Address{version: version}} = prefix, pos, last},
|
||||
{:cont, acc},
|
||||
fun
|
||||
) do
|
||||
case pos do
|
||||
^last -> {:done, acc}
|
||||
^last ->
|
||||
{:done, acc}
|
||||
|
||||
pos ->
|
||||
pos = pos + 1
|
||||
next = Address.from_integer!(pos, version)
|
||||
|
|
|
@ -17,9 +17,9 @@ defimpl Inspect, for: IP.Address do
|
|||
iex> ~i(2001:db8::1)
|
||||
#IP.Address<2001:db8::1 DOCUMENTATION>
|
||||
"""
|
||||
@spec inspect(Address.t, list) :: binary
|
||||
@spec inspect(Address.t(), list) :: binary
|
||||
def inspect(address, _opts) do
|
||||
scope = Scope.address_scope(address)
|
||||
concat ["#IP.Address<#{address} #{scope}>"]
|
||||
concat(["#IP.Address<#{address} #{scope}>"])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,9 +15,9 @@ defimpl Inspect, for: IP.Prefix do
|
|||
...> |> IP.Address.to_prefix(32)
|
||||
#IP.Prefix<192.0.2.1/32 DOCUMENTATION>
|
||||
"""
|
||||
@spec inspect(Prefix.t, list) :: binary
|
||||
@spec inspect(Prefix.t(), list) :: binary
|
||||
def inspect(%Prefix{} = prefix, _opts) do
|
||||
scope = Scope.prefix_scope(prefix)
|
||||
concat ["#IP.Prefix<#{prefix} #{scope}>"]
|
||||
concat(["#IP.Prefix<#{prefix} #{scope}>"])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
defmodule IP.Address do
|
||||
alias IP.{Address, Prefix}
|
||||
alias IP.Address.{InvalidAddress, Helpers, ULA}
|
||||
alias IP.Address
|
||||
alias IP.Address.{Helpers, InvalidAddress, ULA}
|
||||
alias IP.Prefix
|
||||
defstruct ~w(address version)a
|
||||
import Helpers
|
||||
use Bitwise
|
||||
|
@ -10,10 +11,10 @@ defmodule IP.Address do
|
|||
"""
|
||||
|
||||
@typedoc "Valid IPv4 address - integer between zero and 32 ones."
|
||||
@type ipv4 :: 0..0xffffffff
|
||||
@type ipv4 :: 0..0xFFFFFFFF
|
||||
|
||||
@typedoc "Valid IPv6 address - integer between zero and 128 ones."
|
||||
@type ipv6 :: 0..0xffffffffffffffffffffffffffffffff
|
||||
@type ipv6 :: 0..0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
|
||||
|
||||
@typedoc "Valid IP address"
|
||||
@type ip :: ipv4 | ipv6
|
||||
|
@ -42,8 +43,12 @@ defmodule IP.Address do
|
|||
{:error, "Unable to convert binary to address"}
|
||||
"""
|
||||
@spec from_binary(binary) :: {:ok, t} | {:error, term}
|
||||
def from_binary(<<address :: unsigned-integer-size(32)>>), do: {:ok, %Address{address: address, version: 4}}
|
||||
def from_binary(<<address :: unsigned-integer-size(128)>>), do: {:ok, %Address{address: address, version: 6}}
|
||||
def from_binary(<<address::unsigned-integer-size(32)>>),
|
||||
do: {:ok, %Address{address: address, version: 4}}
|
||||
|
||||
def from_binary(<<address::unsigned-integer-size(128)>>),
|
||||
do: {:ok, %Address{address: address, version: 6}}
|
||||
|
||||
def from_binary(_address), do: {:error, "Unable to convert binary to address"}
|
||||
|
||||
@doc """
|
||||
|
@ -92,7 +97,7 @@ defmodule IP.Address do
|
|||
|
||||
def from_integer(_address, 4), do: {:error, "Supplied address not within IPv4 address space"}
|
||||
def from_integer(_address, 6), do: {:error, "Supplied address not within IPv6 address space"}
|
||||
def from_integer(_address, version), do: {:error, "No such IP version #{inspect version}"}
|
||||
def from_integer(_address, version), do: {:error, "No such IP version #{inspect(version)}"}
|
||||
|
||||
@doc """
|
||||
Convert an integer into an IP address of specified version or raise an
|
||||
|
@ -135,7 +140,9 @@ defmodule IP.Address do
|
|||
@spec from_string(binary) :: {:ok, t} | {:error, term}
|
||||
def from_string(address) when is_binary(address) do
|
||||
case from_string(address, 6) do
|
||||
{:ok, address} -> {:ok, address}
|
||||
{:ok, address} ->
|
||||
{:ok, address}
|
||||
|
||||
{:error, _} ->
|
||||
case from_string(address, 4) do
|
||||
{:ok, address} -> {:ok, address}
|
||||
|
@ -183,28 +190,36 @@ defmodule IP.Address do
|
|||
def from_string(address, 4) when is_binary(address) do
|
||||
case :inet.parse_ipv4strict_address(String.to_charlist(address)) do
|
||||
{:ok, addr} ->
|
||||
addr = addr
|
||||
addr =
|
||||
addr
|
||||
|> Tuple.to_list()
|
||||
|> from_bytes()
|
||||
|
||||
{:ok, %Address{version: 4, address: addr}}
|
||||
{:error, _} -> {:error, "Cannot parse IPv4 address"}
|
||||
|
||||
{:error, _} ->
|
||||
{:error, "Cannot parse IPv4 address"}
|
||||
end
|
||||
end
|
||||
|
||||
def from_string(address, 6) when is_binary(address) do
|
||||
case :inet.parse_ipv6strict_address(String.to_charlist(address)) do
|
||||
{:ok, addr} ->
|
||||
addr = addr
|
||||
addr =
|
||||
addr
|
||||
|> Tuple.to_list()
|
||||
|> from_bytes()
|
||||
|
||||
{:ok, %Address{version: 6, address: addr}}
|
||||
{:error, _} -> {:error, "Cannot parse IPv6 address"}
|
||||
|
||||
{:error, _} ->
|
||||
{:error, "Cannot parse IPv6 address"}
|
||||
end
|
||||
end
|
||||
|
||||
def from_string(_address, 4), do: {:error, "Cannot parse IPv4 address"}
|
||||
def from_string(_address, 6), do: {:error, "Cannot parse IPv6 address"}
|
||||
def from_string(_address, version), do: {:error, "No such IP version #{inspect version}"}
|
||||
def from_string(_address, version), do: {:error, "No such IP version #{inspect(version)}"}
|
||||
|
||||
@doc """
|
||||
Convert a string representation into an IP address of specified version or raise an
|
||||
|
@ -243,24 +258,26 @@ defmodule IP.Address do
|
|||
"""
|
||||
@spec to_string(t) :: binary
|
||||
def to_string(%Address{version: 4, address: addr}) do
|
||||
a = addr >>> 0x18 &&& 0xff
|
||||
b = addr >>> 0x10 &&& 0xff
|
||||
c = addr >>> 0x08 &&& 0xff
|
||||
d = addr &&& 0xff
|
||||
a = addr >>> 0x18 &&& 0xFF
|
||||
b = addr >>> 0x10 &&& 0xFF
|
||||
c = addr >>> 0x08 &&& 0xFF
|
||||
d = addr &&& 0xFF
|
||||
|
||||
{a, b, c, d}
|
||||
|> :inet.ntoa()
|
||||
|> List.to_string()
|
||||
end
|
||||
|
||||
def to_string(%Address{version: 6, address: addr}) do
|
||||
a = addr >>> 0x70 &&& 0xffff
|
||||
b = addr >>> 0x60 &&& 0xffff
|
||||
c = addr >>> 0x50 &&& 0xffff
|
||||
d = addr >>> 0x40 &&& 0xffff
|
||||
e = addr >>> 0x30 &&& 0xffff
|
||||
f = addr >>> 0x20 &&& 0xffff
|
||||
g = addr >>> 0x10 &&& 0xffff
|
||||
h = addr &&& 0xffff
|
||||
a = addr >>> 0x70 &&& 0xFFFF
|
||||
b = addr >>> 0x60 &&& 0xFFFF
|
||||
c = addr >>> 0x50 &&& 0xFFFF
|
||||
d = addr >>> 0x40 &&& 0xFFFF
|
||||
e = addr >>> 0x30 &&& 0xFFFF
|
||||
f = addr >>> 0x20 &&& 0xFFFF
|
||||
g = addr >>> 0x10 &&& 0xFFFF
|
||||
h = addr &&& 0xFFFF
|
||||
|
||||
{a, b, c, d, e, f, g, h}
|
||||
|> :inet.ntoa()
|
||||
|> List.to_string()
|
||||
|
@ -275,7 +292,7 @@ defmodule IP.Address do
|
|||
...> |> IP.Address.to_prefix(32)
|
||||
#IP.Prefix<192.0.2.1/32 DOCUMENTATION>
|
||||
"""
|
||||
@spec to_prefix(t, Prefix.prefix_length) :: Prefix.t
|
||||
@spec to_prefix(t, Prefix.prefix_length()) :: Prefix.t()
|
||||
def to_prefix(%Address{} = address, length), do: Prefix.new(address, length)
|
||||
|
||||
@doc """
|
||||
|
@ -355,8 +372,8 @@ defmodule IP.Address do
|
|||
"""
|
||||
@spec eui_64?(t) :: boolean
|
||||
def eui_64?(%Address{address: address, version: 6})
|
||||
when (address &&& 0x20000fffe000000) == 0x20000fffe000000,
|
||||
do: true
|
||||
when (address &&& 0x20000FFFE000000) == 0x20000FFFE000000,
|
||||
do: true
|
||||
|
||||
def eui_64?(_address), do: false
|
||||
|
||||
|
@ -371,16 +388,18 @@ defmodule IP.Address do
|
|||
"""
|
||||
@spec eui_64_mac(t) :: {:ok, binary} | {:error, term}
|
||||
def eui_64_mac(%Address{address: address, version: 6})
|
||||
when (address &&& 0x20000fffe000000) == 0x20000fffe000000
|
||||
do
|
||||
mac = address &&& 0xffffffffffffffff
|
||||
when (address &&& 0x20000FFFE000000) == 0x20000FFFE000000 do
|
||||
mac = address &&& 0xFFFFFFFFFFFFFFFF
|
||||
head = mac >>> 40
|
||||
tail = mac &&& 0xffffff
|
||||
mac = ((head <<< 24) + tail) ^^^ 0x20000000000
|
||||
<<a::binary-size(4), b::binary-size(4), c::binary-size(4)>> = mac
|
||||
tail = mac &&& 0xFFFFFF
|
||||
mac = ((head <<< 24) + tail) ^^^ 0x20000000000
|
||||
|
||||
<<a::binary-size(4), b::binary-size(4), c::binary-size(4)>> =
|
||||
mac
|
||||
|> Integer.to_string(16)
|
||||
|> String.downcase()
|
||||
|> String.pad_leading(12, "0")
|
||||
|
||||
{:ok, "#{a}.#{b}.#{c}"}
|
||||
end
|
||||
|
||||
|
@ -418,7 +437,8 @@ defmodule IP.Address do
|
|||
"""
|
||||
@spec is_6to4?(t) :: boolean
|
||||
def is_6to4?(%Address{address: address, version: 6})
|
||||
when (address >>> 112) == 0x2002, do: true
|
||||
when address >>> 112 == 0x2002,
|
||||
do: true
|
||||
|
||||
def is_6to4?(_address), do: false
|
||||
|
||||
|
@ -438,9 +458,8 @@ defmodule IP.Address do
|
|||
"""
|
||||
@spec from_6to4(t) :: {:ok, t} | {:error, term}
|
||||
def from_6to4(%Address{address: address, version: 6})
|
||||
when (address >>> 112) == 0x2002
|
||||
do
|
||||
address = (address >>> 80) &&& 0xffffffff
|
||||
when address >>> 112 == 0x2002 do
|
||||
address = address >>> 80 &&& 0xFFFFFFFF
|
||||
Address.from_integer(address, 4)
|
||||
end
|
||||
|
||||
|
@ -457,7 +476,8 @@ defmodule IP.Address do
|
|||
"""
|
||||
@spec is_teredo?(t) :: boolean
|
||||
def is_teredo?(%Address{address: address, version: 6})
|
||||
when (address >>> 96) == 0x20010000, do: true
|
||||
when address >>> 96 == 0x20010000,
|
||||
do: true
|
||||
|
||||
def is_teredo?(_address), do: false
|
||||
|
||||
|
@ -483,13 +503,16 @@ defmodule IP.Address do
|
|||
"""
|
||||
@spec teredo(t) :: {:ok, map} | {:error, term}
|
||||
def teredo(%Address{address: address, version: 6})
|
||||
when (address >>> 96) == 0x20010000 do
|
||||
server = address >>> 64 &&& ((1 <<< 32) - 1)
|
||||
client = address &&& ((1 <<< 32) - 1) &&& ((1 <<< 32) - 1)
|
||||
port = (address >>> 32) &&& ((1 <<< 16) - 1)
|
||||
%{server: Address.from_integer!(server, 4),
|
||||
when address >>> 96 == 0x20010000 do
|
||||
server = address >>> 64 &&& (1 <<< 32) - 1
|
||||
client = address &&& (1 <<< 32) - 1 &&& (1 <<< 32) - 1
|
||||
port = address >>> 32 &&& (1 <<< 16) - 1
|
||||
|
||||
%{
|
||||
server: Address.from_integer!(server, 4),
|
||||
client: Address.from_integer!(client, 4),
|
||||
port: port}
|
||||
port: port
|
||||
}
|
||||
end
|
||||
|
||||
def teredo(_address), do: {:error, "Not a teredo address"}
|
||||
|
@ -506,12 +529,10 @@ defmodule IP.Address do
|
|||
iex> IP.Address.generate_ula("60:f8:1d:ad:d8:90")
|
||||
#IP.Address<fd29:f1ef:86a1::>
|
||||
"""
|
||||
@spec generate_ula(binary, non_neg_integer, boolean) :: \
|
||||
{:ok, t} | {:error, term}
|
||||
@spec generate_ula(binary, non_neg_integer, boolean) :: {:ok, t} | {:error, term}
|
||||
def generate_ula(mac, subnet_id \\ 0, locally_assigned \\ true) do
|
||||
with {:ok, address} <- ULA.generate(mac, subnet_id, locally_assigned),
|
||||
{:ok, address} <- from_integer(address, 6)
|
||||
do
|
||||
{:ok, address} <- from_integer(address, 6) do
|
||||
{:ok, address}
|
||||
end
|
||||
end
|
||||
|
@ -521,7 +542,7 @@ defmodule IP.Address do
|
|||
end
|
||||
|
||||
defp from_bytes([a, b, c, d, e, f, g, h]) do
|
||||
(a <<< 0x70) + (b <<< 0x60) + (c <<< 0x50) + (d <<< 0x40) +
|
||||
(e <<< 0x30) + (f <<< 0x20) + (g <<< 0x10) + h
|
||||
(a <<< 0x70) + (b <<< 0x60) + (c <<< 0x50) + (d <<< 0x40) + (e <<< 0x30) + (f <<< 0x20) +
|
||||
(g <<< 0x10) + h
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,7 +6,7 @@ defmodule IP.Address.Helpers do
|
|||
"""
|
||||
defmacro valid_ipv4_integer?(n) do
|
||||
quote do
|
||||
is_integer(unquote(n)) and unquote(n) >= 0 and unquote(n) <= 0xffffffff
|
||||
is_integer(unquote(n)) and unquote(n) >= 0 and unquote(n) <= 0xFFFFFFFF
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -15,25 +15,24 @@ defmodule IP.Address.Helpers do
|
|||
"""
|
||||
defmacro valid_ipv6_integer?(n) do
|
||||
quote do
|
||||
is_integer(unquote(n))
|
||||
and unquote(n) >= 0
|
||||
and unquote(n) <= 0xffffffffffffffffffffffffffffffff
|
||||
is_integer(unquote(n)) and unquote(n) >= 0 and
|
||||
unquote(n) <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Guard clause macro for "4 or 6"
|
||||
"""
|
||||
defmacro valid_ip_version?(4), do: quote do: true
|
||||
defmacro valid_ip_version?(6), do: quote do: true
|
||||
defmacro valid_ip_version?(_), do: quote do: false
|
||||
defmacro valid_ip_version?(4), do: quote(do: true)
|
||||
defmacro valid_ip_version?(6), do: quote(do: true)
|
||||
defmacro valid_ip_version?(_), do: quote(do: false)
|
||||
|
||||
@doc """
|
||||
Guard clause macro for "between 0 and 0xff"
|
||||
"""
|
||||
defmacro valid_byte?(n) do
|
||||
quote do
|
||||
is_integer(unquote(n)) and unquote(n) >= 0 and unquote(n) <= 0xff
|
||||
is_integer(unquote(n)) and unquote(n) >= 0 and unquote(n) <= 0xFF
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,51 +8,48 @@ defmodule IP.Address.ULA do
|
|||
@doc """
|
||||
Generates an IPv6 Unique Local Address
|
||||
"""
|
||||
@spec generate(binary, non_neg_integer, true | false) :: \
|
||||
{:ok, Address.ipv6} | {:error, term}
|
||||
@spec generate(binary, non_neg_integer, true | false) :: {:ok, Address.ipv6()} | {:error, term}
|
||||
def generate(mac, subnet_id, locally_assigned)
|
||||
when is_binary(mac)
|
||||
and is_integer(subnet_id) and subnet_id >= 0 and subnet_id <= 0xffff
|
||||
and is_boolean(locally_assigned)
|
||||
do
|
||||
when is_binary(mac) and is_integer(subnet_id) and subnet_id >= 0 and subnet_id <= 0xFFFF and
|
||||
is_boolean(locally_assigned) do
|
||||
with %DateTime{} = now <- DateTime.utc_now(),
|
||||
{:ok, ntp_time} <- ntp_time(now),
|
||||
{:ok, eui} <- EUI64.eui_portion(mac),
|
||||
{:ok, digest} <- generate_digest(ntp_time, eui),
|
||||
{:ok, global_id} <- last_40_bits_of_digest(digest),
|
||||
{:ok, prefix} <- generate_address(locally_assigned,
|
||||
subnet_id,
|
||||
global_id)
|
||||
do
|
||||
{:ok, ntp_time} <- ntp_time(now),
|
||||
{:ok, eui} <- EUI64.eui_portion(mac),
|
||||
{:ok, digest} <- generate_digest(ntp_time, eui),
|
||||
{:ok, global_id} <- last_40_bits_of_digest(digest),
|
||||
{:ok, prefix} <-
|
||||
generate_address(
|
||||
locally_assigned,
|
||||
subnet_id,
|
||||
global_id
|
||||
) do
|
||||
{:ok, prefix}
|
||||
end
|
||||
end
|
||||
|
||||
defp ntp_time(%DateTime{} = time) do
|
||||
seconds = DateTime.to_unix(time)
|
||||
seconds = DateTime.to_unix(time)
|
||||
{msec, _} = time.microsecond
|
||||
{:ok, ((seconds + 0x83AA7E80) <<< 32) + msec}
|
||||
end
|
||||
|
||||
defp generate_digest(ntp_time, eui) do
|
||||
with key <- << ntp_time::unsigned-integer-size(64),
|
||||
eui::unsigned-integer-size(64) >>,
|
||||
with key <- <<ntp_time::unsigned-integer-size(64), eui::unsigned-integer-size(64)>>,
|
||||
digest <- :crypto.hash(:sha, key),
|
||||
digest <- :binary.decode_unsigned(digest)
|
||||
do
|
||||
digest <- :binary.decode_unsigned(digest) do
|
||||
{:ok, digest}
|
||||
end
|
||||
end
|
||||
|
||||
defp last_40_bits_of_digest(digest) do
|
||||
{:ok, digest &&& 0xffffffffff}
|
||||
{:ok, digest &&& 0xFFFFFFFFFF}
|
||||
end
|
||||
|
||||
defp generate_address(locally_assigned, subnet_id, global_id) do
|
||||
address = (0xfc <<< 120) +
|
||||
(local_assignment_bit(locally_assigned) <<< 120) +
|
||||
(global_id <<< 80) +
|
||||
((subnet_id &&& 0xffff) <<< 64)
|
||||
address =
|
||||
(0xFC <<< 120) + (local_assignment_bit(locally_assigned) <<< 120) + (global_id <<< 80) +
|
||||
((subnet_id &&& 0xFFFF) <<< 64)
|
||||
|
||||
{:ok, address}
|
||||
end
|
||||
|
||||
|
|
104
lib/ip/prefix.ex
104
lib/ip/prefix.ex
|
@ -1,6 +1,6 @@
|
|||
defmodule IP.Prefix do
|
||||
alias IP.{Prefix, Address}
|
||||
alias IP.Prefix.{Parser, InvalidPrefix, Helpers, EUI64}
|
||||
alias IP.{Address, Prefix}
|
||||
alias IP.Prefix.{EUI64, Helpers, InvalidPrefix, Parser}
|
||||
defstruct ~w(address mask)a
|
||||
use Bitwise
|
||||
import Helpers
|
||||
|
@ -9,8 +9,8 @@ defmodule IP.Prefix do
|
|||
Defines an IP prefix, otherwise known as a subnet.
|
||||
"""
|
||||
|
||||
@ipv4_mask 0xffffffff
|
||||
@ipv6_mask 0xffffffffffffffffffffffffffffffff
|
||||
@ipv4_mask 0xFFFFFFFF
|
||||
@ipv6_mask 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
|
||||
|
||||
@typedoc "Valid IPv4 prefix lengths from 0 to 32."
|
||||
@type ipv4_prefix_length :: 0..32
|
||||
|
@ -22,7 +22,7 @@ defmodule IP.Prefix do
|
|||
@type prefix_length :: ipv4_prefix_length | ipv6_prefix_length
|
||||
|
||||
@typedoc "The main prefix type, contains an address and a mask value."
|
||||
@type t :: %Prefix{address: Address.t, mask: Address.ip}
|
||||
@type t :: %Prefix{address: Address.t(), mask: Address.ip()}
|
||||
|
||||
@doc """
|
||||
Create an IP prefix from an `IP.Address` and `length`.
|
||||
|
@ -35,7 +35,7 @@ defmodule IP.Prefix do
|
|||
iex> IP.Prefix.new(~i(2001:db8::1), 64)
|
||||
#IP.Prefix<2001:db8::/64 DOCUMENTATION>
|
||||
"""
|
||||
@spec new(Address.t, prefix_length) :: t
|
||||
@spec new(Address.t(), prefix_length) :: t
|
||||
def new(%Address{address: address, version: 4}, length) when length >= 0 and length <= 32 do
|
||||
mask = calculate_mask_from_length(length, 32)
|
||||
%Prefix{address: Address.from_integer!(address, 4), mask: mask}
|
||||
|
@ -90,7 +90,7 @@ defmodule IP.Prefix do
|
|||
...> |> IP.Prefix.from_string(4)
|
||||
{:error, "Error parsing IPv4 prefix"}
|
||||
"""
|
||||
@spec from_string(binary, Address.version) :: {:ok, t} | {:error, term}
|
||||
@spec from_string(binary, Address.version()) :: {:ok, t} | {:error, term}
|
||||
def from_string(prefix, version), do: Parser.parse(prefix, version)
|
||||
|
||||
@doc """
|
||||
|
@ -133,7 +133,7 @@ defmodule IP.Prefix do
|
|||
...> |> IP.Prefix.from_string!(4)
|
||||
#IP.Prefix<192.0.2.0/24 DOCUMENTATION>
|
||||
"""
|
||||
@spec from_string!(binary, Address.version) :: t
|
||||
@spec from_string!(binary, Address.version()) :: t
|
||||
def from_string!(prefix, version) do
|
||||
case from_string(prefix, version) do
|
||||
{:ok, prefix} -> prefix
|
||||
|
@ -164,14 +164,12 @@ defmodule IP.Prefix do
|
|||
"""
|
||||
@spec length(t, prefix_length) :: t
|
||||
def length(%Prefix{address: %Address{version: 4}} = prefix, length)
|
||||
when is_number(length) and length >= 0 and length <= 32
|
||||
do
|
||||
when is_number(length) and length >= 0 and length <= 32 do
|
||||
%{prefix | mask: calculate_mask_from_length(length, 32)}
|
||||
end
|
||||
|
||||
def length(%Prefix{address: %Address{version: 6}} = prefix, length)
|
||||
when is_number(length) and length >= 0 and length <= 128
|
||||
do
|
||||
when is_number(length) and length >= 0 and length <= 128 do
|
||||
%{prefix | mask: calculate_mask_from_length(length, 128)}
|
||||
end
|
||||
|
||||
|
@ -184,7 +182,7 @@ defmodule IP.Prefix do
|
|||
...> |> IP.Prefix.mask()
|
||||
0b11111111111111111111111100000000
|
||||
"""
|
||||
@spec mask(t) :: Address.ip
|
||||
@spec mask(t) :: Address.ip()
|
||||
def mask(%Prefix{mask: mask}), do: mask
|
||||
|
||||
@doc """
|
||||
|
@ -196,7 +194,7 @@ defmodule IP.Prefix do
|
|||
...> |> IP.Prefix.subnet_mask()
|
||||
#IP.Address<255.255.255.0 RESERVED>
|
||||
"""
|
||||
@spec subnet_mask(t) :: Address.t
|
||||
@spec subnet_mask(t) :: Address.t()
|
||||
def subnet_mask(%Prefix{mask: mask, address: %Address{version: 4}}) do
|
||||
mask
|
||||
|> Address.from_integer!(4)
|
||||
|
@ -211,7 +209,7 @@ defmodule IP.Prefix do
|
|||
...> |> IP.Prefix.wildcard_mask()
|
||||
#IP.Address<0.0.0.255 CURRENT NETWORK>
|
||||
"""
|
||||
@spec wildcard_mask(t) :: Address.t
|
||||
@spec wildcard_mask(t) :: Address.t()
|
||||
def wildcard_mask(%Prefix{mask: mask, address: %Address{version: 4}}) do
|
||||
mask
|
||||
|> bnot()
|
||||
|
@ -232,7 +230,7 @@ defmodule IP.Prefix do
|
|||
...> |> IP.Prefix.first()
|
||||
#IP.Address<2001:db8:: DOCUMENTATION>
|
||||
"""
|
||||
@spec first(t) :: Address.t
|
||||
@spec first(t) :: Address.t()
|
||||
def first(%Prefix{address: %Address{address: address, version: version}, mask: mask}) do
|
||||
Address.from_integer!(lowest_address(address, mask), version)
|
||||
end
|
||||
|
@ -250,7 +248,7 @@ defmodule IP.Prefix do
|
|||
...> |> IP.Prefix.last()
|
||||
#IP.Address<2001:db8::ffff:ffff:ffff:ffff DOCUMENTATION>
|
||||
"""
|
||||
@spec last(t) :: Address.t
|
||||
@spec last(t) :: Address.t()
|
||||
def last(%Prefix{address: %Address{address: address, version: 4}, mask: mask}) do
|
||||
Address.from_integer!(highest_address(address, mask, 4), 4)
|
||||
end
|
||||
|
@ -287,20 +285,20 @@ defmodule IP.Prefix do
|
|||
true
|
||||
|
||||
"""
|
||||
@spec contains_prefix?(t, Address.t) :: boolean
|
||||
def contains_address?(%Prefix{address: %Address{address: addr0, version: 4}, mask: mask} = _prefix,
|
||||
%Address{address: addr1, version: 4} = _address)
|
||||
when lowest_address(addr0, mask) <= addr1
|
||||
and highest_address(addr0, mask, 4) >= addr1
|
||||
do
|
||||
@spec contains_prefix?(t, Address.t()) :: boolean
|
||||
def contains_address?(
|
||||
%Prefix{address: %Address{address: addr0, version: 4}, mask: mask} = _prefix,
|
||||
%Address{address: addr1, version: 4} = _address
|
||||
)
|
||||
when lowest_address(addr0, mask) <= addr1 and highest_address(addr0, mask, 4) >= addr1 do
|
||||
true
|
||||
end
|
||||
|
||||
def contains_address?(%Prefix{address: %Address{address: addr0, version: 6}, mask: mask} = _prefix,
|
||||
%Address{address: addr1, version: 6} = _address)
|
||||
when lowest_address(addr0, mask) <= addr1
|
||||
and highest_address(addr0, mask, 6) >= addr1
|
||||
do
|
||||
def contains_address?(
|
||||
%Prefix{address: %Address{address: addr0, version: 6}, mask: mask} = _prefix,
|
||||
%Address{address: addr1, version: 6} = _address
|
||||
)
|
||||
when lowest_address(addr0, mask) <= addr1 and highest_address(addr0, mask, 6) >= addr1 do
|
||||
true
|
||||
end
|
||||
|
||||
|
@ -334,19 +332,21 @@ defmodule IP.Prefix do
|
|||
|
||||
"""
|
||||
@spec contains_prefix?(t, t) :: boolean
|
||||
def contains_prefix?(%Prefix{address: %Address{address: oaddr, version: 4}, mask: omask} = _outside,
|
||||
%Prefix{address: %Address{address: iaddr, version: 4}, mask: imask} = _inside)
|
||||
when lowest_address(oaddr, omask) <= lowest_address(iaddr, imask)
|
||||
and highest_address(oaddr, omask, 4) >= highest_address(iaddr, imask, 4)
|
||||
do
|
||||
def contains_prefix?(
|
||||
%Prefix{address: %Address{address: oaddr, version: 4}, mask: omask} = _outside,
|
||||
%Prefix{address: %Address{address: iaddr, version: 4}, mask: imask} = _inside
|
||||
)
|
||||
when lowest_address(oaddr, omask) <= lowest_address(iaddr, imask) and
|
||||
highest_address(oaddr, omask, 4) >= highest_address(iaddr, imask, 4) do
|
||||
true
|
||||
end
|
||||
|
||||
def contains_prefix?(%Prefix{address: %Address{address: oaddr, version: 6}, mask: omask} = _outside,
|
||||
%Prefix{address: %Address{address: iaddr, version: 6}, mask: imask} = _inside)
|
||||
when lowest_address(oaddr, omask) <= lowest_address(iaddr, imask)
|
||||
and highest_address(oaddr, omask, 6) >= highest_address(iaddr, imask, 6)
|
||||
do
|
||||
def contains_prefix?(
|
||||
%Prefix{address: %Address{address: oaddr, version: 6}, mask: omask} = _outside,
|
||||
%Prefix{address: %Address{address: iaddr, version: 6}, mask: imask} = _inside
|
||||
)
|
||||
when lowest_address(oaddr, omask) <= lowest_address(iaddr, imask) and
|
||||
highest_address(oaddr, omask, 6) >= highest_address(iaddr, imask, 6) do
|
||||
true
|
||||
end
|
||||
|
||||
|
@ -364,16 +364,16 @@ defmodule IP.Prefix do
|
|||
...> |> inspect()
|
||||
"{:ok, #IP.Address<2001:db8::62f8:1dff:fead:d890 DOCUMENTATION>}"
|
||||
"""
|
||||
@spec eui_64(t, binary) :: {:ok, Address.t} | {:error, term}
|
||||
def eui_64(%Prefix{address: %Address{version: 6},
|
||||
mask: 0xffffffffffffffff0000000000000000} = prefix, mac)
|
||||
do
|
||||
@spec eui_64(t, binary) :: {:ok, Address.t()} | {:error, term}
|
||||
def eui_64(
|
||||
%Prefix{address: %Address{version: 6}, mask: 0xFFFFFFFFFFFFFFFF0000000000000000} = prefix,
|
||||
mac
|
||||
) do
|
||||
with {:ok, eui_portion} <- EUI64.eui_portion(mac),
|
||||
address <- Prefix.first(prefix),
|
||||
address <- Address.to_integer(address),
|
||||
address <- address + eui_portion,
|
||||
{:ok, address} <- Address.from_integer(address, 6)
|
||||
do
|
||||
address <- Prefix.first(prefix),
|
||||
address <- Address.to_integer(address),
|
||||
address <- address + eui_portion,
|
||||
{:ok, address} <- Address.from_integer(address, 6) do
|
||||
{:ok, address}
|
||||
end
|
||||
end
|
||||
|
@ -389,7 +389,7 @@ defmodule IP.Prefix do
|
|||
...> |> IP.Prefix.eui_64!("60:f8:1d:ad:d8:90")
|
||||
#IP.Address<2001:db8::62f8:1dff:fead:d890 DOCUMENTATION>
|
||||
"""
|
||||
@spec eui_64!(t, binary) :: Address.t
|
||||
@spec eui_64!(t, binary) :: Address.t()
|
||||
def eui_64!(prefix, mac) do
|
||||
case eui_64(prefix, mac) do
|
||||
{:ok, address} -> address
|
||||
|
@ -413,13 +413,13 @@ defmodule IP.Prefix do
|
|||
@spec space(t) :: non_neg_integer
|
||||
def space(%Prefix{address: %Address{address: address, version: 4}, mask: mask}) do
|
||||
first = address &&& mask
|
||||
last = first + (~~~mask &&& @ipv4_mask)
|
||||
last = first + (~~~mask &&& @ipv4_mask)
|
||||
last - first + 1
|
||||
end
|
||||
|
||||
def space(%Prefix{address: %Address{address: address, version: 6}, mask: mask}) do
|
||||
first = address &&& mask
|
||||
last = first + (~~~mask &&& @ipv6_mask)
|
||||
last = first + (~~~mask &&& @ipv6_mask)
|
||||
last - first + 1
|
||||
end
|
||||
|
||||
|
@ -438,8 +438,10 @@ defmodule IP.Prefix do
|
|||
"""
|
||||
@spec usable(t) :: non_neg_integer
|
||||
def usable(%Prefix{address: %Address{version: 4}} = prefix) do
|
||||
space = prefix
|
||||
space =
|
||||
prefix
|
||||
|> Prefix.space()
|
||||
|
||||
space - 2
|
||||
end
|
||||
|
||||
|
|
|
@ -14,11 +14,10 @@ defmodule IP.Prefix.EUI64 do
|
|||
"""
|
||||
@spec eui_portion(binary) :: {:ok, non_neg_integer} | {:error, term}
|
||||
def eui_portion(mac) do
|
||||
with {:ok, mac} <- remove_non_digits(mac),
|
||||
{:ok, mac} <- hex_to_int(mac),
|
||||
with {:ok, mac} <- remove_non_digits(mac),
|
||||
{:ok, mac} <- hex_to_int(mac),
|
||||
{:ok, head, tail} <- split_mac(mac),
|
||||
{:ok, eui} <- generate_eui(head, tail)
|
||||
do
|
||||
{:ok, eui} <- generate_eui(head, tail) do
|
||||
{:ok, eui}
|
||||
else
|
||||
{:error, _} = e -> e
|
||||
|
@ -29,14 +28,14 @@ defmodule IP.Prefix.EUI64 do
|
|||
{:ok, Regex.replace(~r/[^0-9a-f]/i, mac, "")}
|
||||
end
|
||||
|
||||
defp split_mac(mac) when is_integer(mac) and mac >= 0 and mac <= 0xffffffffffff do
|
||||
defp split_mac(mac) when is_integer(mac) and mac >= 0 and mac <= 0xFFFFFFFFFFFF do
|
||||
head = mac >>> 24
|
||||
tail = mac &&& 0xffffff
|
||||
tail = mac &&& 0xFFFFFF
|
||||
{:ok, head, tail}
|
||||
end
|
||||
|
||||
def generate_eui(head, tail) do
|
||||
address = (head <<< 40) + (0xfffe <<< 24) + tail
|
||||
address = (head <<< 40) + (0xFFFE <<< 24) + tail
|
||||
address = address ^^^ 0x0200000000000000
|
||||
{:ok, address}
|
||||
end
|
||||
|
|
|
@ -5,8 +5,8 @@ defmodule IP.Prefix.Helpers do
|
|||
|
||||
@doc false
|
||||
def calculate_mask_from_length(length, mask_length) do
|
||||
pad = mask_length - length - 1
|
||||
mask = n_times_reduce(length, 0, fn (i, mask) -> mask + (1 <<< i) end)
|
||||
pad = mask_length - length - 1
|
||||
mask = n_times_reduce(length, 0, fn i, mask -> mask + (1 <<< i) end)
|
||||
mask <<< pad
|
||||
end
|
||||
|
||||
|
@ -22,6 +22,7 @@ defmodule IP.Prefix.Helpers do
|
|||
end
|
||||
|
||||
defp n_times_reduce(0, acc, _fun), do: acc
|
||||
|
||||
defp n_times_reduce(n, acc, fun) do
|
||||
acc = fun.(n, acc)
|
||||
n_times_reduce(n - 1, acc, fun)
|
||||
|
@ -35,15 +36,14 @@ defmodule IP.Prefix.Helpers do
|
|||
|
||||
defmacro highest_address(addr, mask, 4) do
|
||||
quote do
|
||||
(unquote(addr) &&& unquote(mask)) +
|
||||
(~~~unquote(mask) &&& 0xffffffff)
|
||||
(unquote(addr) &&& unquote(mask)) + (~~~unquote(mask) &&& 0xFFFFFFFF)
|
||||
end
|
||||
end
|
||||
|
||||
defmacro highest_address(addr, mask, 6) do
|
||||
quote do
|
||||
(unquote(addr) &&& unquote(mask)) +
|
||||
(~~~unquote(mask) &&& 0xffffffffffffffffffffffffffffffff)
|
||||
(~~~unquote(mask) &&& 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
defmodule IP.Prefix.Parser do
|
||||
alias IP.{Prefix, Address}
|
||||
alias IP.{Address, Prefix}
|
||||
import IP.Prefix.Helpers
|
||||
|
||||
@moduledoc false
|
||||
|
@ -22,10 +22,12 @@ defmodule IP.Prefix.Parser do
|
|||
...> |> inspect()
|
||||
"{:ok, #IP.Prefix<2001:db8::/64 DOCUMENTATION>}"
|
||||
"""
|
||||
@spec parse(binary) :: {:ok, Prefix.t} | {:error, term}
|
||||
@spec parse(binary) :: {:ok, Prefix.t()} | {:error, term}
|
||||
def parse(prefix) do
|
||||
case parse(prefix, 4) do
|
||||
{:ok, prefix} -> {:ok, prefix}
|
||||
{:ok, prefix} ->
|
||||
{:ok, prefix}
|
||||
|
||||
_ ->
|
||||
case parse(prefix, 6) do
|
||||
{:ok, prefix} -> {:ok, prefix}
|
||||
|
@ -49,12 +51,11 @@ defmodule IP.Prefix.Parser do
|
|||
...> |> inspect()
|
||||
"{:ok, #IP.Prefix<2001:db8::/64 DOCUMENTATION>}"
|
||||
"""
|
||||
@spec parse(binary, Address.version) :: {:ok, Prefix.t} | {:error, term}
|
||||
@spec parse(binary, Address.version()) :: {:ok, Prefix.t()} | {:error, term}
|
||||
def parse(prefix, 4 = _version) do
|
||||
with {:ok, address, mask} <- ensure_contains_slash(prefix),
|
||||
{:ok, address} <- Address.from_string(address, 4),
|
||||
{:ok, mask} <- parse_v4_mask(mask)
|
||||
do
|
||||
{:ok, address} <- Address.from_string(address, 4),
|
||||
{:ok, mask} <- parse_v4_mask(mask) do
|
||||
{:ok, Prefix.new(address, mask)}
|
||||
else
|
||||
_ -> {:error, "Error parsing IPv4 prefix"}
|
||||
|
@ -63,9 +64,8 @@ defmodule IP.Prefix.Parser do
|
|||
|
||||
def parse(prefix, 6 = _version) do
|
||||
with {:ok, address, mask} <- ensure_contains_slash(prefix),
|
||||
{:ok, address} <- Address.from_string(address, 6),
|
||||
{:ok, mask} <- parse_v6_mask(mask)
|
||||
do
|
||||
{:ok, address} <- Address.from_string(address, 6),
|
||||
{:ok, mask} <- parse_v6_mask(mask) do
|
||||
{:ok, Prefix.new(address, mask)}
|
||||
else
|
||||
_ -> {:error, "Error parsing IPv6 prefix"}
|
||||
|
@ -82,10 +82,13 @@ defmodule IP.Prefix.Parser do
|
|||
defp parse_v4_mask(mask) do
|
||||
case Address.from_string(mask, 4) do
|
||||
{:ok, address} ->
|
||||
mask = address
|
||||
|> Address.to_integer()
|
||||
|> calculate_length_from_mask()
|
||||
mask =
|
||||
address
|
||||
|> Address.to_integer()
|
||||
|> calculate_length_from_mask()
|
||||
|
||||
{:ok, mask}
|
||||
|
||||
_ ->
|
||||
{:ok, String.to_integer(mask)}
|
||||
end
|
||||
|
|
104
lib/ip/scope.ex
104
lib/ip/scope.ex
|
@ -1,5 +1,5 @@
|
|||
defmodule IP.Scope do
|
||||
alias IP.{Prefix, Address}
|
||||
alias IP.{Address, Prefix}
|
||||
use Bitwise
|
||||
require IP.Prefix
|
||||
import IP.Prefix.Helpers
|
||||
|
@ -11,40 +11,40 @@ defmodule IP.Scope do
|
|||
"""
|
||||
|
||||
@v4_scopes [
|
||||
{"0.0.0.0/8", "CURRENT NETWORK"},
|
||||
{"10.0.0.0/8", "RFC1918 PRIVATE"},
|
||||
{"127.0.0.0/8", "LOOPBACK"},
|
||||
{"168.254.0.0/16", "AUTOCONF PRIVATE"},
|
||||
{"172.16.0.0/12", "RFC1918 PRIVATE"},
|
||||
{"192.0.0.0/24", "RESERVED (IANA)"},
|
||||
{"192.0.2.0/24", "DOCUMENTATION"},
|
||||
{"192.88.99.0/24", "6to4 ANYCAST"},
|
||||
{"192.168.0.0/16", "RFC1918 PRIVATE"},
|
||||
{"198.18.0.0/15", "NETWORK BENCHMARK TESTS"},
|
||||
{"198.51.100.0/24", "DOCUMENTATION"},
|
||||
{"203.0.113.0/24", "DOCUMENTATION"},
|
||||
{"239.0.0.0/8", "LOCAL MULTICAST"},
|
||||
{"224.0.0.0/4", "GLOBAL MULTICAST"},
|
||||
{"240.0.0.0/4", "RESERVED"},
|
||||
{"0.0.0.0/8", "CURRENT NETWORK"},
|
||||
{"10.0.0.0/8", "RFC1918 PRIVATE"},
|
||||
{"127.0.0.0/8", "LOOPBACK"},
|
||||
{"168.254.0.0/16", "AUTOCONF PRIVATE"},
|
||||
{"172.16.0.0/12", "RFC1918 PRIVATE"},
|
||||
{"192.0.0.0/24", "RESERVED (IANA)"},
|
||||
{"192.0.2.0/24", "DOCUMENTATION"},
|
||||
{"192.88.99.0/24", "6to4 ANYCAST"},
|
||||
{"192.168.0.0/16", "RFC1918 PRIVATE"},
|
||||
{"198.18.0.0/15", "NETWORK BENCHMARK TESTS"},
|
||||
{"198.51.100.0/24", "DOCUMENTATION"},
|
||||
{"203.0.113.0/24", "DOCUMENTATION"},
|
||||
{"239.0.0.0/8", "LOCAL MULTICAST"},
|
||||
{"224.0.0.0/4", "GLOBAL MULTICAST"},
|
||||
{"240.0.0.0/4", "RESERVED"},
|
||||
{"255.255.255.255/32", "GLOBAL BROADCAST"},
|
||||
{"0.0.0.0/0", "GLOBAL UNICAST"}
|
||||
{"0.0.0.0/0", "GLOBAL UNICAST"}
|
||||
]
|
||||
|
||||
@v6_scopes [
|
||||
{"2002::/16", "GLOBAL UNICAST (6to4)"},
|
||||
{"2001::/32", "GLOBAL UNICAST (TEREDO)"},
|
||||
{"2001:10::/28", "ORCHID"},
|
||||
{"2001:db8::/32", "DOCUMENTATION"},
|
||||
{"2000::/3", "GLOBAL UNICAST"},
|
||||
{"::/128", "UNSPECIFIED ADDRESS"},
|
||||
{"::1/128", "LINK LOCAL LOOPBACK"},
|
||||
{"::ffff:0:0/96", "IPv4 MAPPED"},
|
||||
{"::/96", "IPv4 TRANSITION (deprecated)"},
|
||||
{"fc00::/7", "UNIQUE LOCAL UNICAST"},
|
||||
{"fec0::/10", "SITE LOCAL (deprecated)"},
|
||||
{"fe80::/10", "LINK LOCAL UNICAST"},
|
||||
{"ff00::/8", "MULTICAST"},
|
||||
{"::/0", "RESERVED"}
|
||||
{"2002::/16", "GLOBAL UNICAST (6to4)"},
|
||||
{"2001::/32", "GLOBAL UNICAST (TEREDO)"},
|
||||
{"2001:10::/28", "ORCHID"},
|
||||
{"2001:db8::/32", "DOCUMENTATION"},
|
||||
{"2000::/3", "GLOBAL UNICAST"},
|
||||
{"::/128", "UNSPECIFIED ADDRESS"},
|
||||
{"::1/128", "LINK LOCAL LOOPBACK"},
|
||||
{"::ffff:0:0/96", "IPv4 MAPPED"},
|
||||
{"::/96", "IPv4 TRANSITION (deprecated)"},
|
||||
{"fc00::/7", "UNIQUE LOCAL UNICAST"},
|
||||
{"fec0::/10", "SITE LOCAL (deprecated)"},
|
||||
{"fe80::/10", "LINK LOCAL UNICAST"},
|
||||
{"ff00::/8", "MULTICAST"},
|
||||
{"::/0", "RESERVED"}
|
||||
]
|
||||
|
||||
@doc """
|
||||
|
@ -60,26 +60,28 @@ defmodule IP.Scope do
|
|||
...> |> IP.Scope.address_scope()
|
||||
"DOCUMENTATION"
|
||||
"""
|
||||
@spec address_scope(Address.t) :: binary
|
||||
@spec address_scope(Address.t()) :: binary
|
||||
|
||||
Enum.each(@v4_scopes, fn {prefix, description} ->
|
||||
%Prefix{address: %Address{address: addr0}, mask: mask} = prefix
|
||||
%Prefix{address: %Address{address: addr0}, mask: mask} =
|
||||
prefix
|
||||
|> Prefix.from_string!()
|
||||
|
||||
def address_scope(%Address{address: addr1, version: 4})
|
||||
when lowest_address(unquote(addr0), unquote(mask)) <= addr1
|
||||
and highest_address(unquote(addr0), unquote(mask), 4) >= addr1
|
||||
do
|
||||
when lowest_address(unquote(addr0), unquote(mask)) <= addr1 and
|
||||
highest_address(unquote(addr0), unquote(mask), 4) >= addr1 do
|
||||
unquote(description)
|
||||
end
|
||||
end)
|
||||
|
||||
Enum.each(@v6_scopes, fn {prefix, description} ->
|
||||
%Prefix{address: %Address{address: addr0}, mask: mask} = prefix
|
||||
%Prefix{address: %Address{address: addr0}, mask: mask} =
|
||||
prefix
|
||||
|> Prefix.from_string!()
|
||||
|
||||
def address_scope(%Address{address: addr1, version: 6})
|
||||
when lowest_address(unquote(addr0), unquote(mask)) <= addr1
|
||||
and highest_address(unquote(addr0), unquote(mask), 6) >= addr1
|
||||
do
|
||||
when lowest_address(unquote(addr0), unquote(mask)) <= addr1 and
|
||||
highest_address(unquote(addr0), unquote(mask), 6) >= addr1 do
|
||||
unquote(description)
|
||||
end
|
||||
end)
|
||||
|
@ -97,26 +99,30 @@ defmodule IP.Scope do
|
|||
...> |> IP.Scope.prefix_scope()
|
||||
"DOCUMENTATION"
|
||||
"""
|
||||
@spec prefix_scope(Prefix.t) :: binary
|
||||
@spec prefix_scope(Prefix.t()) :: binary
|
||||
|
||||
Enum.each(@v4_scopes, fn {prefix0, description} ->
|
||||
%Prefix{address: %Address{address: addr0}, mask: mask0} = prefix0
|
||||
%Prefix{address: %Address{address: addr0}, mask: mask0} =
|
||||
prefix0
|
||||
|> Prefix.from_string!()
|
||||
|
||||
def prefix_scope(%Prefix{address: %Address{address: addr1, version: 4}, mask: mask1})
|
||||
when lowest_address(unquote(addr0), unquote(mask0)) <= lowest_address(addr1, mask1)
|
||||
and highest_address(unquote(addr0), unquote(mask0), 4) >= highest_address(addr1, mask1, 4)
|
||||
do
|
||||
when lowest_address(unquote(addr0), unquote(mask0)) <= lowest_address(addr1, mask1) and
|
||||
highest_address(unquote(addr0), unquote(mask0), 4) >=
|
||||
highest_address(addr1, mask1, 4) do
|
||||
unquote(description)
|
||||
end
|
||||
end)
|
||||
|
||||
Enum.each(@v6_scopes, fn {prefix0, description} ->
|
||||
%Prefix{address: %Address{address: addr0}, mask: mask0} = prefix0
|
||||
%Prefix{address: %Address{address: addr0}, mask: mask0} =
|
||||
prefix0
|
||||
|> Prefix.from_string!()
|
||||
|
||||
def prefix_scope(%Prefix{address: %Address{address: addr1, version: 6}, mask: mask1})
|
||||
when lowest_address(unquote(addr0), unquote(mask0)) <= lowest_address(addr1, mask1)
|
||||
and highest_address(unquote(addr0), unquote(mask0), 6) >= highest_address(addr1, mask1, 6)
|
||||
do
|
||||
when lowest_address(unquote(addr0), unquote(mask0)) <= lowest_address(addr1, mask1) and
|
||||
highest_address(unquote(addr0), unquote(mask0), 6) >=
|
||||
highest_address(addr1, mask1, 6) do
|
||||
unquote(description)
|
||||
end
|
||||
end)
|
||||
|
|
43
lib/sigil.ex
43
lib/sigil.ex
|
@ -1,5 +1,5 @@
|
|||
defmodule IP.Sigil do
|
||||
alias IP.{Prefix, Address}
|
||||
alias IP.{Address, Prefix}
|
||||
alias IP.Sigil.InvalidValue
|
||||
|
||||
@moduledoc """
|
||||
|
@ -67,36 +67,57 @@ defmodule IP.Sigil do
|
|||
iex> IP.Sigil.sigil_i("2001:db8::/32", 's')
|
||||
#IP.Prefix<2001:db8::/32 DOCUMENTATION>
|
||||
"""
|
||||
@spec sigil_i(binary, [non_neg_integer]) :: Prefix.t | Address.t
|
||||
@spec sigil_i(binary, [non_neg_integer]) :: Prefix.t() | Address.t()
|
||||
def sigil_i(value, '' = _options) do
|
||||
case Prefix.from_string(value) do
|
||||
{:ok, prefix} -> prefix
|
||||
{:ok, prefix} ->
|
||||
prefix
|
||||
|
||||
{:error, _} ->
|
||||
case Address.from_string(value) do
|
||||
{:ok, address} -> address
|
||||
{:error, _} -> raise(InvalidValue, message: "Unable to parse #{inspect value} as an IP address or prefix.")
|
||||
{:ok, address} ->
|
||||
address
|
||||
|
||||
{:error, _} ->
|
||||
raise(InvalidValue,
|
||||
message: "Unable to parse #{inspect(value)} as an IP address or prefix."
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def sigil_i(value, 'f' = _options) do
|
||||
case Prefix.from_string(value, 4) do
|
||||
{:ok, prefix} -> prefix
|
||||
{:ok, prefix} ->
|
||||
prefix
|
||||
|
||||
{:error, _} ->
|
||||
case Address.from_string(value, 4) do
|
||||
{:ok, address} -> address
|
||||
{:error, _} -> raise(InvalidValue, message: "Unable to parse #{inspect value} as an IPv4 address or prefix.")
|
||||
{:ok, address} ->
|
||||
address
|
||||
|
||||
{:error, _} ->
|
||||
raise(InvalidValue,
|
||||
message: "Unable to parse #{inspect(value)} as an IPv4 address or prefix."
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def sigil_i(value, 's' = _options) do
|
||||
case Prefix.from_string(value, 6) do
|
||||
{:ok, prefix} -> prefix
|
||||
{:ok, prefix} ->
|
||||
prefix
|
||||
|
||||
{:error, _} ->
|
||||
case Address.from_string(value, 6) do
|
||||
{:ok, address} -> address
|
||||
{:error, _} -> raise(InvalidValue, message: "Unable to parse #{inspect value} as an IPv6 address or prefix.")
|
||||
{:ok, address} ->
|
||||
address
|
||||
|
||||
{:error, _} ->
|
||||
raise(InvalidValue,
|
||||
message: "Unable to parse #{inspect(value)} as an IPv6 address or prefix."
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
defimpl String.Chars, for: IP.Prefix do
|
||||
alias IP.Prefix
|
||||
|
||||
@moduledoc """
|
||||
Implements `String.Chars` for `IP.Prefix`.
|
||||
"""
|
||||
|
@ -15,7 +16,7 @@ defimpl String.Chars, for: IP.Prefix do
|
|||
"192.0.2.1/32"
|
||||
"""
|
||||
def to_string(%Prefix{} = prefix) do
|
||||
length = Prefix.length(prefix)
|
||||
length = Prefix.length(prefix)
|
||||
address = Prefix.first(prefix)
|
||||
"#{address}/#{length}"
|
||||
end
|
||||
|
|
12
mix.exs
12
mix.exs
|
@ -4,14 +4,14 @@ defmodule IP.Mixfile do
|
|||
@description """
|
||||
Represtations and tools for IP addresses and networks.
|
||||
"""
|
||||
@version "1.0.0"
|
||||
@version "1.1.0"
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :ip,
|
||||
version: @version,
|
||||
elixir: "~> 1.5",
|
||||
start_permanent: Mix.env == :prod,
|
||||
start_permanent: Mix.env() == :prod,
|
||||
package: package(),
|
||||
deps: deps(),
|
||||
description: @description
|
||||
|
@ -20,8 +20,8 @@ defmodule IP.Mixfile do
|
|||
|
||||
def package do
|
||||
[
|
||||
maintainers: [ "James Harton <james@automat.nz>" ],
|
||||
licenses: [ "MIT" ],
|
||||
maintainers: ["James Harton <james@automat.nz>"],
|
||||
licenses: ["MIT"],
|
||||
links: %{
|
||||
"Source" => "https://gitlab.com/jimsy/ip"
|
||||
}
|
||||
|
@ -40,8 +40,8 @@ defmodule IP.Mixfile do
|
|||
[
|
||||
{:ex_doc, ">= 0.0.0", only: :dev},
|
||||
{:earmark, ">= 0.0.0", only: :dev},
|
||||
{:credo, "~> 0.6", only: ~w(dev test)a, runtime: false},
|
||||
{:inch_ex, "~> 0.5", only: ~w(dev test)a, runtime: false},
|
||||
{:credo, "~> 0.10", only: ~w(dev test)a, runtime: false},
|
||||
{:inch_ex, "~> 1.0", only: ~w(dev test)a, runtime: false},
|
||||
{:dialyxir, "~> 0.5", only: ~w(dev test)a, runtime: false}
|
||||
]
|
||||
end
|
||||
|
|
18
mix.lock
18
mix.lock
|
@ -1,7 +1,13 @@
|
|||
%{"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], []},
|
||||
"credo": {:hex, :credo, "0.8.7", "b1aad9cd3aa7acdbaea49765bfc9f1605dc4555023a037dc9ea7a70539615bc8", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, optional: false]}]},
|
||||
%{
|
||||
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
|
||||
"credo": {:hex, :credo, "0.10.0", "66234a95effaf9067edb19fc5d0cd5c6b461ad841baac42467afed96c78e5e9e", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], [], "hexpm"},
|
||||
"earmark": {:hex, :earmark, "1.2.3", "206eb2e2ac1a794aa5256f3982de7a76bf4579ff91cb28d0e17ea2c9491e46a4", [:mix], []},
|
||||
"ex_doc": {:hex, :ex_doc, "0.17.1", "39f777415e769992e6732d9589dc5846ea587f01412241f4a774664c746affbb", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, optional: false]}]},
|
||||
"inch_ex": {:hex, :inch_ex, "0.5.6", "418357418a553baa6d04eccd1b44171936817db61f4c0840112b420b8e378e67", [:mix], [{:poison, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :poison, optional: false]}]},
|
||||
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], []}}
|
||||
"earmark": {:hex, :earmark, "1.2.6", "b6da42b3831458d3ecc57314dff3051b080b9b2be88c2e5aa41cd642a5b044ed", [:mix], [], "hexpm"},
|
||||
"ex_doc": {:hex, :ex_doc, "0.19.1", "519bb9c19526ca51d326c060cb1778d4a9056b190086a8c6c115828eaccea6cf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.7", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"inch_ex": {:hex, :inch_ex, "1.0.0", "18496a900ca4b7542a1ff1159e7f8be6c2012b74ca55ac70de5e805f14cdf939", [:mix], [{:poison, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"jason": {:hex, :jason, "1.1.1", "d3ccb840dfb06f2f90a6d335b536dd074db748b3e7f5b11ab61d239506585eb2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"makeup": {:hex, :makeup, "0.5.1", "966c5c2296da272d42f1de178c1d135e432662eca795d6dc12e5e8787514edf7", [:mix], [{:nimble_parsec, "~> 0.2.2", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"makeup_elixir": {:hex, :makeup_elixir, "0.8.0", "1204a2f5b4f181775a0e456154830524cf2207cf4f9112215c05e0b76e4eca8b", [:mix], [{:makeup, "~> 0.5.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 0.2.2", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"nimble_parsec": {:hex, :nimble_parsec, "0.2.2", "d526b23bdceb04c7ad15b33c57c4526bf5f50aaa70c7c141b4b4624555c68259", [:mix], [], "hexpm"},
|
||||
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue