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.
|
%% license that can be found in the LICENSE file.
|
||||||
%%
|
%%
|
||||||
%% @doc
|
%% @doc
|
||||||
%% ip2region utils
|
%% ip2region utils: IPv4/IPv6 parsing and version detection.
|
||||||
%% @end
|
%% @end
|
||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(ip2region_util).
|
-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;
|
-spec ip_version(Ip :: tuple() | list() | binary() | integer()) ->
|
||||||
ipv4_to_n({A, B, C, D}) ->
|
ipv4 | ipv6 | {error, atom()}.
|
||||||
<<N:32>> = <<A, B, C, D>>,
|
ip_version(Ip) when is_integer(Ip) -> ipv4;
|
||||||
N;
|
ip_version({_, _, _, _}) -> ipv4;
|
||||||
ipv4_to_n(Ip) when is_binary(Ip) ->
|
ip_version({_, _, _, _, _, _, _, _}) -> ipv6;
|
||||||
ipv4_to_n(binary_to_list(Ip));
|
ip_version(Ip) when is_binary(Ip) ->
|
||||||
ipv4_to_n(Ip) when is_list(Ip) ->
|
ip_version(binary_to_list(Ip));
|
||||||
|
ip_version(Ip) when is_list(Ip) ->
|
||||||
case inet_parse:address(Ip) of
|
case inet_parse:address(Ip) of
|
||||||
{ok, Addr} ->
|
{ok, {_, _, _, _}} -> ipv4;
|
||||||
ipv4_to_n(Addr);
|
{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}
|
{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