feat(erlang): add dual-stack IP parsing utilities
This commit is contained in:
parent
c1db56c369
commit
2f0dc5f33f
|
|
@ -4,23 +4,63 @@
|
|||
%% license that can be found in the LICENSE file.
|
||||
%%
|
||||
%% @doc
|
||||
%% ip2region utils
|
||||
%% ip2region utils: IPv4/IPv6 parsing and version detection.
|
||||
%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(ip2region_util).
|
||||
-export([ipv4_to_n/1]).
|
||||
|
||||
-export([ipv4_to_n/1, ip_version/1, ip_to_bytes/1]).
|
||||
|
||||
ipv4_to_n(IntIp) when is_integer(IntIp) -> IntIp;
|
||||
ipv4_to_n({A, B, C, D}) ->
|
||||
<<N:32>> = <<A, B, C, D>>,
|
||||
N;
|
||||
ipv4_to_n(Ip) when is_binary(Ip) ->
|
||||
ipv4_to_n(binary_to_list(Ip));
|
||||
ipv4_to_n(Ip) when is_list(Ip) ->
|
||||
-spec ip_version(Ip :: tuple() | list() | binary() | integer()) ->
|
||||
ipv4 | ipv6 | {error, atom()}.
|
||||
ip_version(Ip) when is_integer(Ip) -> ipv4;
|
||||
ip_version({_, _, _, _}) -> ipv4;
|
||||
ip_version({_, _, _, _, _, _, _, _}) -> ipv6;
|
||||
ip_version(Ip) when is_binary(Ip) ->
|
||||
ip_version(binary_to_list(Ip));
|
||||
ip_version(Ip) when is_list(Ip) ->
|
||||
case inet_parse:address(Ip) of
|
||||
{ok, Addr} ->
|
||||
ipv4_to_n(Addr);
|
||||
{ok, {_, _, _, _}} -> ipv4;
|
||||
{ok, {_, _, _, _, _, _, _, _}} -> ipv6;
|
||||
_ -> {error, bad_ip_format}
|
||||
end;
|
||||
ip_version(_) ->
|
||||
{error, bad_ip_format}.
|
||||
|
||||
-spec ip_to_bytes(Ip :: tuple() | list() | binary() | integer()) ->
|
||||
{ok, ipv4 | ipv6, binary()} | {error, atom()}.
|
||||
ip_to_bytes(Ip) when is_integer(Ip) ->
|
||||
{ok, ipv4, <<Ip:32>>};
|
||||
ip_to_bytes({A, B, C, D}) ->
|
||||
{ok, ipv4, <<A, B, C, D>>};
|
||||
ip_to_bytes({A, B, C, D, E, F, G, H}) ->
|
||||
{ok, ipv6, <<A:16, B:16, C:16, D:16, E:16, F:16, G:16, H:16>>};
|
||||
ip_to_bytes(Ip) when is_binary(Ip) ->
|
||||
ip_to_bytes(binary_to_list(Ip));
|
||||
ip_to_bytes(Ip) when is_list(Ip) ->
|
||||
case inet_parse:address(Ip) of
|
||||
{ok, {A, B, C, D}} ->
|
||||
{ok, ipv4, <<A, B, C, D>>};
|
||||
{ok, {A, B, C, D, E, F, G, H}} ->
|
||||
{ok, ipv6, <<A:16, B:16, C:16, D:16, E:16, F:16, G:16, H:16>>};
|
||||
_ ->
|
||||
{error, bad_ip_format}
|
||||
end.
|
||||
end;
|
||||
ip_to_bytes(_) ->
|
||||
{error, bad_ip_format}.
|
||||
|
||||
-spec ipv4_to_n(Ip :: tuple() | list() | binary() | integer()) ->
|
||||
non_neg_integer() | {error, atom()}.
|
||||
ipv4_to_n(IntIp) when is_integer(IntIp) -> IntIp;
|
||||
ipv4_to_n({A, B, C, D}) ->
|
||||
<<N:32>> = <<A, B, C, D>>,
|
||||
N;
|
||||
ipv4_to_n(Ip) when is_binary(Ip) ->
|
||||
ipv4_to_n(binary_to_list(Ip));
|
||||
ipv4_to_n(Ip) when is_list(Ip) ->
|
||||
case ip_to_bytes(Ip) of
|
||||
{ok, ipv4, <<N:32>>} -> N;
|
||||
_ -> {error, bad_ip_format}
|
||||
end;
|
||||
ipv4_to_n(_) ->
|
||||
{error, bad_ip_format}.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
-module(ip2region_util_test).
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
ipv4_inputs_test_() ->
|
||||
[
|
||||
?_assertEqual({ok, ipv4, <<1,0,8,0>>}, ip2region_util:ip_to_bytes("1.0.8.0")),
|
||||
?_assertEqual({ok, ipv4, <<1,0,8,0>>}, ip2region_util:ip_to_bytes(<<"1.0.8.0">>)),
|
||||
?_assertEqual({ok, ipv4, <<1,0,8,0>>}, ip2region_util:ip_to_bytes({1,0,8,0})),
|
||||
?_assertEqual({ok, ipv4, <<1,0,8,0>>}, ip2region_util:ip_to_bytes(16779264)),
|
||||
?_assertEqual(ipv4, ip2region_util:ip_version("1.0.8.0")),
|
||||
?_assertEqual(ipv4, ip2region_util:ip_version({1,0,8,0}))
|
||||
].
|
||||
|
||||
ipv6_inputs_test_() ->
|
||||
[
|
||||
?_assertEqual({ok, ipv6, <<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1>>},
|
||||
ip2region_util:ip_to_bytes("::1")),
|
||||
?_assertEqual({ok, ipv6, <<0,0,0,0,0,0,0,0,0,0,255,255,1,0,8,0>>},
|
||||
ip2region_util:ip_to_bytes("::ffff:1.0.8.0")),
|
||||
?_assertEqual({ok, ipv6, <<0,0,0,0,0,0,0,0,0,0,255,255,1,0,8,0>>},
|
||||
ip2region_util:ip_to_bytes(<<"::ffff:1.0.8.0">>)),
|
||||
?_assertEqual({ok, ipv6, <<0,0,0,0,0,0,0,0,0,0,255,255,1,0,8,0>>},
|
||||
ip2region_util:ip_to_bytes({0,0,0,0,0,65535,256,2048})),
|
||||
?_assertEqual(ipv6, ip2region_util:ip_version("::1")),
|
||||
?_assertEqual(ipv6, ip2region_util:ip_version({0,0,0,0,0,0,0,1}))
|
||||
].
|
||||
|
||||
invalid_inputs_test_() ->
|
||||
[
|
||||
?_assertEqual({error, bad_ip_format}, ip2region_util:ip_to_bytes("xxx.0.8.0")),
|
||||
?_assertEqual({error, bad_ip_format}, ip2region_util:ip_to_bytes("::ggg")),
|
||||
?_assertEqual({error, bad_ip_format}, ip2region_util:ip_to_bytes({1,2,3})),
|
||||
?_assertEqual({error, bad_ip_format}, ip2region_util:ip_to_bytes(atom))
|
||||
].
|
||||
|
||||
legacy_ipv4_to_n_test_() ->
|
||||
[
|
||||
?_assertEqual(16779264, ip2region_util:ipv4_to_n("1.0.8.0")),
|
||||
?_assertEqual(16779264, ip2region_util:ipv4_to_n(<<"1.0.8.0">>)),
|
||||
?_assertEqual(16779264, ip2region_util:ipv4_to_n({1,0,8,0})),
|
||||
?_assertEqual(16779264, ip2region_util:ipv4_to_n(16779264))
|
||||
].
|
||||
Loading…
Reference in New Issue