diff options
author | Raimo Niskanen <[email protected]> | 2010-01-28 17:01:00 +0100 |
---|---|---|
committer | Björn Gustavsson <[email protected]> | 2010-02-09 13:41:42 +0100 |
commit | cc90d8c3642afa626b483b92c53964365d72b35a (patch) | |
tree | 64f07e3fe207469ce40a2b55d3e545c213db0091 /lib/kernel/src/inet_parse.erl | |
parent | 51bb4c6ae1e4d61304aee5b4bf77279d838da84a (diff) | |
download | otp-cc90d8c3642afa626b483b92c53964365d72b35a.tar.gz otp-cc90d8c3642afa626b483b92c53964365d72b35a.tar.bz2 otp-cc90d8c3642afa626b483b92c53964365d72b35a.zip |
inet:gethostbyname improved to parse IP strings and look up own hostname
Now inet:gethostbyname tries to parse the hostname as an IP string
first if the 'native' lookup method is not used. One can also
make the IP string parsing explicit using the new 'string'
lookup method, or avoid it using the new pseudo lookup
method 'nostring'.
In R13B04 a bug was introduced when the gethostbyname code
was rewritten, so if the native resolver was used and
misconfigured to not be able to look up the own hostname,
inet:gethostbyname also failed. This is now fixed.
Diffstat (limited to 'lib/kernel/src/inet_parse.erl')
-rw-r--r-- | lib/kernel/src/inet_parse.erl | 275 |
1 files changed, 191 insertions, 84 deletions
diff --git a/lib/kernel/src/inet_parse.erl b/lib/kernel/src/inet_parse.erl index 62d44fb723..f44a5f1800 100644 --- a/lib/kernel/src/inet_parse.erl +++ b/lib/kernel/src/inet_parse.erl @@ -34,6 +34,7 @@ -export([nsswitch_conf/1, nsswitch_conf/2]). -export([ipv4_address/1, ipv6_address/1]). +-export([ipv4strict_address/1, ipv6strict_address/1]). -export([address/1]). -export([visible_string/1, domain/1]). -export([ntoa/1, dots/1]). @@ -456,17 +457,15 @@ is_dom2(_) -> %% -%% Test ipv4 address or ipv6 address +%% Parse ipv4 address or ipv6 address %% Return {ok, Address} | {error, Reason} %% address(Cs) when is_list(Cs) -> case ipv4_address(Cs) of - {ok,IP} -> {ok,IP}; + {ok,IP} -> + {ok,IP}; _ -> - case ipv6_address(Cs) of - {ok, IP} -> {ok, IP}; - Error -> Error - end + ipv6strict_address(Cs) end; address(_) -> {error, einval}. @@ -477,49 +476,145 @@ address(_) -> %% d1.d2.d4 %% d1.d4 %% d4 +%% Any d may be octal, hexadecimal or decimal by C language standards. +%% d4 fills all LSB bytes. This is legacy behaviour from Solaris +%% and FreeBSD. And partly Linux that behave the same except +%% it does not accept hexadecimal. %% %% Return {ok, IP} | {error, einval} %% ipv4_address(Cs) -> - case catch ipv4_addr(Cs) of - {'EXIT',_} -> {error,einval}; - Addr -> {ok,Addr} + try ipv4_addr(Cs) of + Addr -> + {ok,Addr} + catch + error:badarg -> + {error,einval} end. -ipv4_addr(Cs) -> - ipv4_addr(d3(Cs), []). +ipv4_addr(Cs) -> + case ipv4_addr(Cs, []) of + [D] when D < (1 bsl 32) -> + <<D1,D2,D3,D4>> = <<D:32>>, + {D1,D2,D3,D4}; + [D,D1] when D < (1 bsl 24), D1 < 256 -> + <<D2,D3,D4>> = <<D:24>>, + {D1,D2,D3,D4}; + [D,D2,D1] when D < (1 bsl 16), (D2 bor D1) < 256 -> + <<D3,D4>> = <<D:16>>, + {D1,D2,D3,D4}; + [D4,D3,D2,D1] when (D4 bor D3 bor D2 bor D1) < 256 -> + {D1,D2,D3,D4}; + _ -> + erlang:error(badarg) + end. + +ipv4_addr([_|_], [_,_,_,_]) -> + %% Early bailout for extra characters + erlang:error(badarg); +ipv4_addr("0x"++Cs, Ds) -> + ipv4_addr(strip0(Cs), Ds, [], 16, 8); +ipv4_addr("0X"++Cs, Ds) -> + ipv4_addr(strip0(Cs), Ds, [], 16, 8); +ipv4_addr("0"++Cs, Ds) -> + ipv4_addr(strip0(Cs), Ds, [$0], 8, 11); +ipv4_addr(Cs, Ds) when is_list(Cs) -> + ipv4_addr(Cs, Ds, [], 10, 10). + +ipv4_addr(Cs0, Ds, Rs, Base, N) -> + case ipv4_field(Cs0, N, Rs, Base) of + {D,""} -> + [D|Ds]; + {D,[$.|[_|_]=Cs]} -> + ipv4_addr(Cs, [D|Ds]); + {_,_} -> + erlang:error(badarg) + end. + +strip0("0"++Cs) -> + strip0(Cs); +strip0(Cs) when is_list(Cs) -> + Cs. + -ipv4_addr({Cs0,[]}, A) when length(A) =< 3 -> - case [tod(Cs0)|A] of - [D4,D3,D2,D1] -> +%% +%% Parse IPv4 strict dotted decimal address, no leading zeros: +%% d1.d2.d3.d4 +%% +%% Return {ok, IP} | {error, einval} +%% +ipv4strict_address(Cs) -> + try ipv4strict_addr(Cs) of + Addr -> + {ok,Addr} + catch + error:badarg -> + {error,einval} + end. + +ipv4strict_addr(Cs) -> + case ipv4strict_addr(Cs, []) of + [D4,D3,D2,D1] when (D4 bor D3 bor D2 bor D1) < 256 -> {D1,D2,D3,D4}; - [D4,D2,D1] -> - {D1,D2,0,D4}; - [D4,D1] -> - {D1,0,0,D4}; - [D4] -> - {0,0,0,D4} - end; -ipv4_addr({Cs0,"."++Cs1}, A) when length(A) =< 2 -> - ipv4_addr(d3(Cs1), [tod(Cs0)|A]). + _ -> + erlang:error(badarg) + end. -d3(Cs) -> d3(Cs, []). +ipv4strict_addr([_|_], [_,_,_,_]) -> + %% Early bailout for extra characters + erlang:error(badarg); +ipv4strict_addr("0", Ds) -> + [0|Ds]; +ipv4strict_addr("0."++Cs, Ds) -> + ipv4strict_addr(Cs, [0|Ds]); +ipv4strict_addr(Cs0, Ds) when is_list(Cs0) -> + case ipv4_field(Cs0, 3, [], 10) of + {D,""} -> + [D|Ds]; + {D,[$.|[_|_]=Cs]} -> + ipv4strict_addr(Cs, [D|Ds]); + {_,_} -> + erlang:error(badarg) + end. + + + +ipv4_field("", _, Rs, Base) -> + {ipv4_field(Rs, Base),""}; +ipv4_field("."++_=Cs, _, Rs, Base) -> + {ipv4_field(Rs, Base),Cs}; +ipv4_field("0"++_, _, [], _) -> + erlang:error(badarg); +ipv4_field([C|Cs], N, Rs, Base) when N > 0 -> + ipv4_field(Cs, N-1, [C|Rs], Base); +ipv4_field(Cs, _, _, _) when is_list(Cs) -> + erlang:error(badarg). + +ipv4_field(Rs, Base) -> + V = erlang:list_to_integer(lists:reverse(Rs), Base), + if V < 0 -> + erlang:error(badarg); + true -> + V + end. -d3([C|Cs], R) when C >= $0, C =< $9, length(R) =< 2 -> - d3(Cs, [C|R]); -d3(Cs, [_|_]=R) -> - {lists:reverse(R),Cs}. -tod(Cs) -> - case erlang:list_to_integer(Cs) of - D when D >= 0, D =< 255 -> - D; + +%% +%% Forgiving IPv6 address +%% +%% Accepts IPv4 address and returns it as a IPv4 compatible IPv6 address +%% +ipv6_address(Cs) -> + case ipv4_address(Cs) of + {ok,{D1,D2,D3,D4}} -> + {ok,{0,0,0,0,0,16#ffff,(D1 bsl 8) bor D2,(D3 bsl 8) bor D4}}; _ -> - erlang:error(badarg, [Cs]) + ipv6strict_address(Cs) end. %% -%% Parse IPv6 address: +%% Parse IPv6 address according to RFC 4291: %% x1:x2:x3:x4:x5:x6:x7:x8 %% x1:x2::x7:x8 %% ::x7:x8 @@ -530,77 +625,89 @@ tod(Cs) -> %% ::x5:x6:d7a.d7b.d8a.d8b %% x1:x2::d7a.d7b.d8a.d8b %% ::d7a.d7b.d8a.d8b +%% etc %% %% Return {ok, IP} | {error, einval} %% -ipv6_address(Cs) -> - case catch ipv6_addr(Cs) of - {'EXIT',_} -> {error,einval}; - Addr -> {ok,Addr} +ipv6strict_address(Cs) -> + try ipv6_addr(Cs) of + Addr -> + {ok,Addr} + catch + error:badarg -> + {error,einval} end. ipv6_addr("::") -> - ipv6_addr_done([], []); + ipv6_addr_done([], [], 0); ipv6_addr("::"++Cs) -> - ipv6_addr(x4(Cs), [], []); + ipv6_addr(hex(Cs), [], [], 0); ipv6_addr(Cs) -> - ipv6_addr(x4(Cs), []). + ipv6_addr(hex(Cs), [], 0). %% Before "::" -ipv6_addr({Cs0,[]}, A) when length(A) =:= 7 -> - ipv6_addr_done([tox(Cs0)|A]); -ipv6_addr({Cs0,"::"}, A) when length(A) =< 6 -> - ipv6_addr_done([tox(Cs0)|A], []); -ipv6_addr({Cs0,"::"++Cs1}, A) when length(A) =< 5 -> - ipv6_addr(x4(Cs1), [tox(Cs0)|A], []); -ipv6_addr({Cs0,":"++Cs1}, A) when length(A) =< 6 -> - ipv6_addr(x4(Cs1), [tox(Cs0)|A]); -ipv6_addr({Cs0,"."++Cs1}, A) when length(A) =:= 6 -> - ipv6_addr(d3(Cs1), A, [], [tod(Cs0)]). +ipv6_addr({Cs0,[]}, A, N) when N == 7 -> + ipv6_addr_done([hex_to_int(Cs0)|A]); +ipv6_addr({Cs0,"::"}, A, N) when N =< 6 -> + ipv6_addr_done([hex_to_int(Cs0)|A], [], N+1); +ipv6_addr({Cs0,"::"++Cs1}, A, N) when N =< 5 -> + ipv6_addr(hex(Cs1), [hex_to_int(Cs0)|A], [], N+1); +ipv6_addr({Cs0,":"++Cs1}, A, N) when N =< 6 -> + ipv6_addr(hex(Cs1), [hex_to_int(Cs0)|A], N+1); +ipv6_addr({Cs0,"."++_=Cs1}, A, N) when N == 6 -> + ipv6_addr_done(A, [], N, ipv4strict_addr(Cs0++Cs1)); +ipv6_addr(_, _, _) -> + erlang:error(badarg). %% After "::" -ipv6_addr({Cs0,[]}, A, B) when length(A)+length(B) =< 6 -> - ipv6_addr_done(A, [tox(Cs0)|B]); -ipv6_addr({Cs0,":"++Cs1}, A, B) when length(A)+length(B) =< 5 -> - ipv6_addr(x4(Cs1), A, [tox(Cs0)|B]); -ipv6_addr({Cs0,"."++Cs1}, A, B) when length(A)+length(B) =< 5 -> - ipv6_addr(x4(Cs1), A, B, [tod(Cs0)]). - -%% After "." -ipv6_addr({Cs0,[]}, A, B, C) when length(C) =:= 3 -> - ipv6_addr_done(A, B, [tod(Cs0)|C]); -ipv6_addr({Cs0,"."++Cs1}, A, B, C) when length(C) =< 2 -> - ipv6_addr(d3(Cs1), A, B, [tod(Cs0)|C]). +ipv6_addr({Cs0,[]}, A, B, N) when N =< 6 -> + ipv6_addr_done(A, [hex_to_int(Cs0)|B], N+1); +ipv6_addr({Cs0,":"++Cs1}, A, B, N) when N =< 5 -> + ipv6_addr(hex(Cs1), A, [hex_to_int(Cs0)|B], N+1); +ipv6_addr({Cs0,"."++_=Cs1}, A, B, N) when N =< 5 -> + ipv6_addr_done(A, B, N, ipv4strict_addr(Cs0++Cs1)); +ipv6_addr(_, _, _, _) -> + erlang:error(badarg). -ipv6_addr_done(Ar, Br, [D4,D3,D2,D1]) -> - ipv6_addr_done(Ar, [((D3 bsl 8) bor D4),((D1 bsl 8) bor D2)|Br]). +ipv6_addr_done(Ar, Br, N, {D1,D2,D3,D4}) -> + ipv6_addr_done(Ar, [((D3 bsl 8) bor D4),((D1 bsl 8) bor D2)|Br], N+2). -ipv6_addr_done(Ar, Br) -> - ipv6_addr_done(Br++dup(8-length(Ar)-length(Br), 0, Ar)). +ipv6_addr_done(Ar, Br, N) -> + ipv6_addr_done(Br++dup(8-N, 0, Ar)). ipv6_addr_done(Ar) -> list_to_tuple(lists:reverse(Ar)). -x4(Cs) -> x4(Cs, []). - -x4([C|Cs], R) when C >= $0, C =< $9, length(R) =< 3 -> - x4(Cs, [C|R]); -x4([C|Cs], R) when C >= $a, C =< $f, length(R) =< 3 -> - x4(Cs, [C|R]); -x4([C|Cs], R) when C >= $A, C =< $F, length(R) =< 3 -> - x4(Cs, [C|R]); -x4(Cs, [_|_]=R) -> - {lists:reverse(R),Cs}. - -tox(Cs) -> - erlang:list_to_integer(Cs, 16). +%% Collect Hex digits +hex(Cs) -> hex(Cs, []). +%% +hex([C|Cs], R) when C >= $0, C =< $9 -> + hex(Cs, [C|R]); +hex([C|Cs], R) when C >= $a, C =< $f -> + hex(Cs, [C|R]); +hex([C|Cs], R) when C >= $A, C =< $F -> + hex(Cs, [C|R]); +hex(Cs, [_|_]=R) when is_list(Cs) -> + {lists:reverse(R),Cs}; +hex(_, _) -> + erlang:error(badarg). + +%% Hex string to integer +hex_to_int(Cs0) -> + case strip0(Cs0) of + Cs when length(Cs) =< 4 -> + erlang:list_to_integer("0"++Cs, 16); + _ -> + erlang:error(badarg) + end. +%% Dup onto head of existing list dup(0, _, L) -> L; dup(N, E, L) when is_integer(N), N >= 1 -> - dup(N-1, E, [E|L]); -dup(N, E, L) -> - erlang:error(badarg, [N,E,L]). + dup(N-1, E, [E|L]). + + %% Convert IPv4 adress to ascii %% Convert IPv6 / IPV4 adress to ascii (plain format) @@ -674,7 +781,7 @@ separate(_E, [H], R) -> lists:reverse(R, [H]). %% convert to A.B decimal form -dig_to_dec(0) -> [$0,$.,$0]; +dig_to_dec(0) -> "0.0"; dig_to_dec(X) -> integer_to_list((X bsr 8) band 16#ff) ++ "." ++ integer_to_list(X band 16#ff). |