aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel/src')
-rw-r--r--lib/kernel/src/inet.erl137
-rw-r--r--lib/kernel/src/inet_parse.erl275
2 files changed, 270 insertions, 142 deletions
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index b86aa1839e..77725372c2 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -45,6 +45,7 @@
%% resolve
-export([gethostbyname/1, gethostbyname/2, gethostbyname/3,
gethostbyname_tm/3]).
+-export([gethostbyname_string/2, gethostbyname_self/2]).
-export([gethostbyaddr/1, gethostbyaddr/2,
gethostbyaddr_tm/2]).
@@ -411,7 +412,17 @@ gethostbyname(Name,Family,Timeout) ->
Res.
gethostbyname_tm(Name,Family,Timer) ->
- gethostbyname_tm(Name,Family,Timer,inet_db:res_option(lookup)).
+ Opts0 = inet_db:res_option(lookup),
+ Opts =
+ case (lists:member(native, Opts0) orelse
+ lists:member(string, Opts0) orelse
+ lists:member(nostring, Opts0)) of
+ true ->
+ Opts0;
+ false ->
+ [string|Opts0]
+ end,
+ gethostbyname_tm(Name, Family, Timer, Opts).
-spec gethostbyaddr(Address :: string() | ip_address()) ->
@@ -850,75 +861,61 @@ getaddrs_tm(Address, Family, Timer) ->
%%
%% gethostbyname with option search
%%
-gethostbyname_tm(Name, Type, Timer, [dns | Opts]) ->
- Res = inet_res:gethostbyname_tm(Name, Type, Timer),
- case Res of
- {ok,_} -> Res;
- {error,timeout} -> Res;
- {error,formerr} -> {error,einval};
- {error,_} -> gethostbyname_tm(Name,Type,Timer,Opts)
- end;
-gethostbyname_tm(Name, Type, Timer, [file | Opts]) ->
- case inet_hosts:gethostbyname(Name, Type) of
- {error,formerr} -> {error,einval};
- {error,_} -> gethostbyname_tm(Name,Type,Timer,Opts);
- Result -> Result
- end;
-gethostbyname_tm(Name, Type, Timer, [yp | Opts]) ->
+gethostbyname_tm(Name, Type, Timer, [string|_]=Opts) ->
+ Result = gethostbyname_string(Name, Type),
+ gethostbyname_tm(Name, Type, Timer, Opts, Result);
+gethostbyname_tm(Name, Type, Timer, [dns|_]=Opts) ->
+ Result = inet_res:gethostbyname_tm(Name, Type, Timer),
+ gethostbyname_tm(Name, Type, Timer, Opts, Result);
+gethostbyname_tm(Name, Type, Timer, [file|_]=Opts) ->
+ Result = inet_hosts:gethostbyname(Name, Type),
+ gethostbyname_tm(Name, Type, Timer, Opts, Result);
+gethostbyname_tm(Name, Type, Timer, [yp|_]=Opts) ->
gethostbyname_tm_native(Name, Type, Timer, Opts);
-gethostbyname_tm(Name, Type, Timer, [nis | Opts]) ->
+gethostbyname_tm(Name, Type, Timer, [nis|_]=Opts) ->
gethostbyname_tm_native(Name, Type, Timer, Opts);
-gethostbyname_tm(Name, Type, Timer, [nisplus | Opts]) ->
+gethostbyname_tm(Name, Type, Timer, [nisplus|_]=Opts) ->
gethostbyname_tm_native(Name, Type, Timer, Opts);
-gethostbyname_tm(Name, Type, Timer, [wins | Opts]) ->
+gethostbyname_tm(Name, Type, Timer, [wins|_]=Opts) ->
gethostbyname_tm_native(Name, Type, Timer, Opts);
-gethostbyname_tm(Name, Type, Timer, [native | Opts]) ->
+gethostbyname_tm(Name, Type, Timer, [native|_]=Opts) ->
gethostbyname_tm_native(Name, Type, Timer, Opts);
-gethostbyname_tm(_, _, _, [no_default|_]) ->
- %% If the native resolver has failed, we should not bother
- %% to try to be smarter and parse the IP address here.
- {error,nxdomain};
-gethostbyname_tm(Name, Type, Timer, [_ | Opts]) ->
+gethostbyname_tm(Name, Type, Timer, [_|_]=Opts) ->
gethostbyname_tm(Name, Type, Timer, Opts);
-%% Last resort - parse the hostname as address
-gethostbyname_tm(Name, inet, _Timer, []) ->
- case inet_parse:ipv4_address(Name) of
- {ok,IP4} ->
- {ok,make_hostent(Name, [IP4], [], inet)};
- _ ->
- gethostbyname_self(Name)
- end;
-gethostbyname_tm(Name, inet6, _Timer, []) ->
- case inet_parse:ipv6_address(Name) of
- {ok,IP6} ->
- {ok,make_hostent(Name, [IP6], [], inet6)};
- _ ->
- %% Even if Name is a valid IPv4 address, we can't
- %% assume it's correct to return it on a IPv6
- %% format ( {0,0,0,0,0,16#ffff,?u16(A,B),?u16(C,D)} ).
- %% This host might not support IPv6.
- gethostbyname_self(Name)
+%% Make sure we always can look up our own hostname.
+gethostbyname_tm(Name, Type, Timer, []) ->
+ Result = gethostbyname_self(Name, Type),
+ gethostbyname_tm(Name, Type, Timer, [], Result).
+
+gethostbyname_tm(Name, Type, Timer, Opts, Result) ->
+ case Result of
+ {ok,_} ->
+ Result;
+ {error,formerr} ->
+ {error,einval};
+ {error,_} when Opts =:= [] ->
+ {error,nxdomain};
+ {error,_} ->
+ gethostbyname_tm(Name, Type, Timer, tl(Opts))
end.
gethostbyname_tm_native(Name, Type, Timer, Opts) ->
%% Fixme: add (global) timeout to gethost_native
- case inet_gethost_native:gethostbyname(Name, Type) of
- {error,formerr} -> {error,einval};
- {error,timeout} -> {error,timeout};
- {error,_} -> gethostbyname_tm(Name, Type, Timer, Opts++[no_default]);
- Result -> Result
- end.
+ Result = inet_gethost_native:gethostbyname(Name, Type),
+ gethostbyname_tm(Name, Type, Timer, Opts, Result).
-%% Make sure we always can look up our own hostname.
-gethostbyname_self(Name) ->
- Type = case inet_db:res_option(inet6) of
- true -> inet6;
- false -> inet
- end,
+
+
+gethostbyname_self(Name, Type) when is_atom(Name) ->
+ gethostbyname_self(atom_to_list(Name), Type);
+gethostbyname_self(Name, Type)
+ when is_list(Name), Type =:= inet;
+ is_list(Name), Type =:= inet6 ->
case inet_db:gethostname() of
Name ->
- {ok,make_hostent(Name, [translate_ip(loopback, Type)],
- [], Type)};
+ {ok,make_hostent(Name,
+ [translate_ip(loopback, Type)],
+ [], Type)};
Self ->
case inet_db:res_option(domain) of
"" -> {error,nxdomain};
@@ -931,7 +928,31 @@ gethostbyname_self(Name) ->
_ -> {error,nxdomain}
end
end
- end.
+ end;
+gethostbyname_self(_, _) ->
+ {error,formerr}.
+
+gethostbyname_string(Name, Type) when is_atom(Name) ->
+ gethostbyname_string(atom_to_list(Name), Type);
+gethostbyname_string(Name, Type)
+ when is_list(Name), Type =:= inet;
+ is_list(Name), Type =:= inet6 ->
+ case
+ case Type of
+ inet ->
+ inet_parse:ipv4_address(Name);
+ inet6 ->
+ %% XXX should we really translate IPv4 addresses here
+ %% even if we do not know if this host can do IPv6?
+ inet_parse:ipv6_address(Name)
+ end of
+ {ok,IP} ->
+ {ok,make_hostent(Name, [IP], [], Type)};
+ {error,einval} ->
+ {error,nxdomain}
+ end;
+gethostbyname_string(_, _) ->
+ {error,formerr}.
make_hostent(Name, Addrs, Aliases, Type) ->
#hostent{h_name = Name,
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).