aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel
diff options
context:
space:
mode:
authorRaimo Niskanen <raimo@erlang.org>2013-06-10 11:43:15 +0200
committerRaimo Niskanen <raimo@erlang.org>2013-06-10 11:43:15 +0200
commit10671126a75d14977a769d9dfce461482093c5c8 (patch)
treee80ac8aaef51e9e1829623d6088a152a45da661c /lib/kernel
parenta49832f74d364e01d9fb7a98caf3ca942a0a0341 (diff)
parent54240a5829cac17ea59eeeb16f343f3f2817d7b3 (diff)
downloadotp-10671126a75d14977a769d9dfce461482093c5c8.tar.gz
otp-10671126a75d14977a769d9dfce461482093c5c8.tar.bz2
otp-10671126a75d14977a769d9dfce461482093c5c8.zip
Merge branch 'raimo/inet-gethostbyname-lowercase-search/OTP-10689' into maint
* raimo/inet-gethostbyname-lowercase-search/OTP-10689: Add test cases for host lookup case (in)sensitivity Improve case (in)sensitivity for host lookups
Diffstat (limited to 'lib/kernel')
-rw-r--r--lib/kernel/src/inet.erl41
-rw-r--r--lib/kernel/src/inet_db.erl65
-rw-r--r--lib/kernel/src/inet_hosts.erl9
-rw-r--r--lib/kernel/src/inet_res.erl6
-rw-r--r--lib/kernel/test/inet_SUITE.erl197
-rw-r--r--lib/kernel/test/inet_res_SUITE.erl198
-rw-r--r--lib/kernel/test/inet_res_SUITE_data/otptest/named_inc.conf6
-rw-r--r--lib/kernel/test/inet_res_SUITE_data/otptest/root.zone6
8 files changed, 333 insertions, 195 deletions
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index aada1252ad..3ea530a366 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -947,21 +947,34 @@ gethostbyname_self(Name, Type) when is_atom(Name) ->
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)};
- Self ->
+ N = inet_db:tolower(Name),
+ Self = inet_db:gethostname(),
+ %%
+ %% This is the final fallback that pretends /etc/hosts has got
+ %% a line for the hostname on the loopback address.
+ %% Lookups into /etc/hosts are case insensitive and return
+ %% what is in the file. Therefore the letter case may differ between
+ %% the returned hostent record and the hostname that was asked for.
+ %%
+ case inet_db:tolower(Self) of
+ N ->
+ {ok,
+ make_hostent(
+ Self, [translate_ip(loopback, Type)], [], Type)};
+ _ ->
case inet_db:res_option(domain) of
- "" -> {error,nxdomain};
+ "" ->
+ {error,nxdomain};
Domain ->
- case lists:append([Self,".",Domain]) of
- Name ->
- {ok,make_hostent(Name,
- [translate_ip(loopback, Type)],
- [], Type)};
- _ -> {error,nxdomain}
+ FQDN = lists:append([Self,".",Domain]),
+ case inet_db:tolower(FQDN) of
+ N ->
+ {ok,
+ make_hostent(
+ FQDN,
+ [translate_ip(loopback, Type)], [], Type)};
+ _ ->
+ {error,nxdomain}
end
end
end;
diff --git a/lib/kernel/src/inet_db.erl b/lib/kernel/src/inet_db.erl
index d4749b9756..a7679c531b 100644
--- a/lib/kernel/src/inet_db.erl
+++ b/lib/kernel/src/inet_db.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -845,7 +845,8 @@ init([]) ->
process_flag(trap_exit, true),
Db = ets:new(inet_db, [public, named_table]),
reset_db(Db),
- Cache = ets:new(inet_cache, [public, bag, {keypos,2}, named_table]),
+ CacheOpts = [public, bag, {keypos,#dns_rr.domain}, named_table],
+ Cache = ets:new(inet_cache, CacheOpts),
BynameOpts = [protected, bag, named_table, {keypos,1}],
ByaddrOpts = [protected, bag, named_table, {keypos,3}],
HostsByname = ets:new(inet_hosts_byname, BynameOpts),
@@ -901,15 +902,21 @@ reset_db(Db) ->
handle_call(Request, From, #state{db=Db}=State) ->
case Request of
{load_hosts_file,IPNmAs} when is_list(IPNmAs) ->
- NIPs = lists:flatten([ [{N,if tuple_size(IP) =:= 4 -> inet;
- tuple_size(IP) =:= 8 -> inet6
- end,IP} || N <- [Nm|As]]
- || {IP,Nm,As} <- IPNmAs]),
+ NIPs =
+ lists:flatten(
+ [ [{N,
+ if tuple_size(IP) =:= 4 -> inet;
+ tuple_size(IP) =:= 8 -> inet6
+ end,IP} || N <- [Nm|As]]
+ || {IP,Nm,As} <- IPNmAs]),
Byname = State#state.hosts_file_byname,
Byaddr = State#state.hosts_file_byaddr,
ets:delete_all_objects(Byname),
ets:delete_all_objects(Byaddr),
- ets:insert(Byname, NIPs),
+ %% Byname has lowercased names while Byaddr keep the name casing.
+ %% This is to be able to reconstruct the original
+ %% /etc/hosts entry.
+ ets:insert(Byname, [{tolower(N),Type,IP} || {N,Type,IP} <- NIPs]),
ets:insert(Byaddr, NIPs),
{reply, ok, State};
@@ -938,16 +945,14 @@ handle_call(Request, From, #state{db=Db}=State) ->
{reply, ok, State};
{add_rr, RR} when is_record(RR, dns_rr) ->
- RR1 = lower_rr(RR),
- ?dbg("add_rr: ~p~n", [RR1]),
- do_add_rr(RR1, Db, State),
+ ?dbg("add_rr: ~p~n", [RR]),
+ do_add_rr(RR, Db, State),
{reply, ok, State};
{del_rr, RR} when is_record(RR, dns_rr) ->
- RR1 = lower_rr(RR),
%% note. del_rr will handle wildcards !!!
Cache = State#state.cache,
- ets:match_delete(Cache, RR1),
+ ets:match_delete(Cache, RR),
{reply, ok, State};
{lookup_rr, Domain, Class, Type} ->
@@ -1225,15 +1230,18 @@ handle_set_file(ParseFun, Bin, From, State) ->
handle_rc_list(Opts, From, State)
end.
+%% Byname has lowercased names while Byaddr keep the name casing.
+%% This is to be able to reconstruct the original /etc/hosts entry.
+
do_add_host(Byname, Byaddr, Names, Type, IP) ->
do_del_host(Byname, Byaddr, IP),
- NIPs = [{tolower(N),Type,IP} || N <- Names],
- ets:insert(Byname, NIPs),
- ets:insert(Byaddr, NIPs),
+ ets:insert(Byname, [{tolower(N),Type,IP} || N <- Names]),
+ ets:insert(Byaddr, [{N,Type,IP} || N <- Names]),
ok.
do_del_host(Byname, Byaddr, IP) ->
- [ets:delete_object(Byname, NIP) || NIP <- ets:lookup(Byaddr, IP)],
+ [ets:delete_object(Byname, {tolower(Name),Type,Addr}) ||
+ {Name,Type,Addr} <- ets:lookup(Byaddr, IP)],
ets:delete(Byaddr, IP),
ok.
@@ -1369,7 +1377,7 @@ times() ->
%% lookup and remove old entries
do_lookup_rr(Domain, Class, Type) ->
- match_rr(#dns_rr{domain = tolower(Domain), class = Class,type = Type,
+ match_rr(#dns_rr{domain = Domain, class = Class,type = Type,
cnt = '_', tm = '_', ttl = '_',
bm = '_', func = '_', data = '_'}).
@@ -1393,23 +1401,20 @@ filter_rr([], _Time) -> [].
%%
-%% Lower case the domain name before storage
-%%
-lower_rr(RR) ->
- Dn = RR#dns_rr.domain,
- if is_list(Dn) ->
- RR#dns_rr { domain = tolower(Dn) };
- true -> RR
- end.
-
+%% Case fold upper-case to lower-case according to RFC 4343
+%% "Domain Name System (DNS) Case Insensitivity Clarification".
%%
-%% Map upper-case to lower-case
%% NOTE: this code is in kernel and we don't want to relay
-%% to much on stdlib
+%% to much on stdlib. Furthermore string:to_lower/1
+%% does not follow RFC 4343.
%%
tolower([]) -> [];
-tolower([C|Cs]) when C >= $A, C =< $Z -> [(C-$A)+$a|tolower(Cs)];
-tolower([C|Cs]) -> [C|tolower(Cs)].
+tolower([C|Cs]) when is_integer(C) ->
+ if C >= $A, C =< $Z ->
+ [(C-$A)+$a|tolower(Cs)];
+ true ->
+ [C|tolower(Cs)]
+ end.
dn_ip6_int(A,B,C,D,E,F,G,H) ->
dnib(H) ++ dnib(G) ++ dnib(F) ++ dnib(E) ++
diff --git a/lib/kernel/src/inet_hosts.erl b/lib/kernel/src/inet_hosts.erl
index df1d4fc0be..6e9719b4aa 100644
--- a/lib/kernel/src/inet_hosts.erl
+++ b/lib/kernel/src/inet_hosts.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -38,9 +38,12 @@ gethostbyname(_) -> {error, formerr}.
gethostbyname(Name, Type) when is_list(Name), is_atom(Type) ->
- case gethostbyname(Name, Type, inet_hosts_byname, inet_hosts_byaddr) of
+ %% Byname has lowercased names while Byaddr keep the name casing.
+ %% This is to be able to reconstruct the original /etc/hosts entry.
+ N = inet_db:tolower(Name),
+ case gethostbyname(N, Type, inet_hosts_byname, inet_hosts_byaddr) of
false ->
- case gethostbyname(Name, Type,
+ case gethostbyname(N, Type,
inet_hosts_file_byname,
inet_hosts_file_byaddr) of
false -> {error,nxdomain};
diff --git a/lib/kernel/src/inet_res.erl b/lib/kernel/src/inet_res.erl
index 59ba408d7a..94a9f7c64d 100644
--- a/lib/kernel/src/inet_res.erl
+++ b/lib/kernel/src/inet_res.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -181,8 +181,8 @@ lookup(Name, Class, Type, Opts, Timeout) ->
lookup_filter({ok,#dns_rec{anlist=Answers}}, Class, Type) ->
[A#dns_rr.data || A <- Answers,
- A#dns_rr.class =:= Class,
- A#dns_rr.type =:= Type];
+ Class =:= any orelse A#dns_rr.class =:= Class,
+ Type =:= any orelse A#dns_rr.type =:= Type];
lookup_filter({error,_}, _, _) -> [].
%% --------------------------------------------------------------------------
diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl
index 1f7724d0dc..62ba95e1a3 100644
--- a/lib/kernel/test/inet_SUITE.erl
+++ b/lib/kernel/test/inet_SUITE.erl
@@ -183,80 +183,74 @@ t_gethostbyname(Config) when is_list(Config) ->
h_addr_list = [IP]},
?line HEntF_ = HEntF,
?line check_elems([{HEnt#hostent.h_aliases,[[],Aliases]}]),
+ %%
+ ?line FullNameU = toupper(FullName),
+ ?line {ok,HEntU} = inet:gethostbyname(FullNameU),
+ ?line FullNameU = toupper(HEntU#hostent.h_name),
+ ?line #hostent{
+ h_addrtype = inet,
+ h_length = 4,
+ h_addr_list = [IP]} = HEntU,
+ ?line check_elems(
+ [{[toupper(H) || H <- HEntU#hostent.h_aliases],
+ [[],[toupper(A) || A <- Aliases]]}]),
?line {DName, _DFullName, _DIPStr, _DIP, _, _, _} =
ct:get_config(test_dummy_host),
?line {error,nxdomain} = inet:gethostbyname(DName),
- ?line {error,nxdomain} = inet:gethostbyname(IP_46_Str).
+ ?line {error,nxdomain} = inet:gethostbyname(IP_46_Str),
+ ok.
t_gethostbyname_v6() -> required(v6).
t_gethostbyname_v6(doc) -> "Test the inet:gethostbyname/1 inet6 function.";
t_gethostbyname_v6(suite) -> [];
t_gethostbyname_v6(Config) when is_list(Config) ->
- ?line {Name, _, _, _,Aliases,IP_46_Str,IP_46} =
- ct:get_config(test_host_ipv4_only),
+ {Name, FullName, IPStr, IP, Aliases} =
+ ct:get_config(test_host_ipv6_only),
- case {inet:gethostbyname(IP_46_Str, inet6),
- inet:gethostbyname(Name, inet6)} of
- {{ok,HEnt46},{ok,_}} ->
- ?line HEnt46_ = HEnt46#hostent{h_name = IP_46_Str,
- h_addrtype = inet6,
- h_length = 16,
- h_addr_list = [IP_46]},
- ?line HEnt46_ = HEnt46,
- ?line check_elems([{HEnt46#hostent.h_aliases,[[],Aliases]}]),
-
- ?line {Name6, FullName6, IPStr6, IP6, Aliases6} =
- ct:get_config(test_host_ipv6_only),
- ?line {ok,_} = inet:gethostbyname(IPStr6, inet6),
- ?line {ok,HEnt6} = inet:gethostbyname(Name6, inet6),
- ?line {ok,HEnt6} = inet:gethostbyname(list_to_atom(Name6), inet6),
- ?line case HEnt6#hostent.h_addr_list of
- [IP6] -> % ipv6 ok
- ?line HEnt6_ = HEnt6#hostent{h_addrtype = inet6,
- h_length = 16,
- h_addr_list = [IP6]},
- ?line HEnt6_ = HEnt6,
- ?line check_elems([{HEnt6#hostent.h_name,[Name6,FullName6]},
- {HEnt6#hostent.h_aliases,[[],Aliases6]}]);
- _ -> % ipv4 compatible addr
- ?line {ok,HEnt4} = inet:gethostbyname(Name6, inet),
- ?line [IP4] = HEnt4#hostent.h_addr_list,
- ?line {ok,IP46_2} =
- inet_parse:ipv6_address("::ffff:"++inet_parse:ntoa(IP4)),
- ?line HEnt6_ = HEnt6#hostent{h_addrtype = inet6,
- h_length = 16,
- h_addr_list = [IP46_2]},
- ?line HEnt6_ = HEnt6,
- ?line check_elems([{HEnt6#hostent.h_name,[Name6,FullName6]}])
- end,
-
- ?line {ok,HEntF6} = inet:gethostbyname(FullName6, inet6),
- ?line case HEntF6#hostent.h_addr_list of
- [IP6] -> % ipv6 ok
- ?line HEntF6_ = HEntF6#hostent{h_name = FullName6,
- h_addrtype = inet6,
- h_length = 16,
- h_addr_list = [IP6]},
- ?line HEntF6_ = HEntF6,
- ?line check_elems([{HEntF6#hostent.h_aliases,[[],Aliases6]}]);
- _ -> % ipv4 compatible addr
- ?line {ok,HEntF4} = inet:gethostbyname(FullName6, inet),
- ?line [IPF4] = HEntF4#hostent.h_addr_list,
- ?line {ok,IPF46_2} =
- inet_parse:ipv6_address("::ffff:"++inet_parse:ntoa(IPF4)),
- ?line HEntF6_ = HEntF6#hostent{h_addrtype = inet6,
- h_length = 16,
- h_addr_list = [IPF46_2]},
- ?line HEntF6_ = HEntF6,
- ?line check_elems([{HEntF6#hostent.h_name,[Name6,FullName6]}])
- end,
-
- ?line {DName6, _DFullName6, _DIPStr6, _DIP6, _} =
- ct:get_config(test_dummy_ipv6_host),
- ?line {error,nxdomain} = inet:gethostbyname(DName6, inet6),
- ok;
- {_,_} ->
+ case inet:gethostbyname(Name, inet6) of
+ {ok,HEnt} ->
+ {ok,_} = inet:gethostbyname(IPStr, inet6),
+ {ok,HEnt} = inet:gethostbyname(list_to_atom(Name), inet6),
+ case HEnt#hostent.h_addr_list of
+ [IP] -> % IPv6 address
+ #hostent{h_addrtype = inet6,
+ h_length = 16} = HEnt,
+ check_elems(
+ [{HEnt#hostent.h_name,[Name,FullName]},
+ {HEnt#hostent.h_aliases,[[],Aliases]}]);
+ [IP46] -> % IPv4 compatible address
+ {ok,HEnt4} = inet:gethostbyname(Name, inet),
+ #hostent{h_addrtype = inet,
+ h_length = 4,
+ h_addr_list = [IP4]} = HEnt4,
+ {ok,IP46} =
+ inet_parse:ipv6_address(
+ "::ffff:" ++ inet_parse:ntoa(IP4)),
+ check_elems(
+ [{HEnt#hostent.h_name,[Name,FullName]}])
+ end,
+
+ {ok,HEntF} = inet:gethostbyname(FullName, inet6),
+ case HEntF#hostent.h_addr_list of
+ [IP] -> % IPv6 address
+ #hostent{h_name = FullName,
+ h_addrtype = inet6,
+ h_length = 16} = HEntF,
+ check_elems(
+ [{HEnt#hostent.h_aliases,[[],Aliases]}]);
+ [IP46F] -> % IPv4 compatible address
+ {ok,HEnt4F} = inet:gethostbyname(FullName, inet),
+ #hostent{h_addrtype = inet,
+ h_length = 4,
+ h_addr_list = [IP4F]} = HEnt4F,
+ {ok,IP46F} =
+ inet_parse:ipv6_address(
+ "::ffff:" ++ inet_parse:ntoa(IP4F)),
+ check_elems(
+ [{HEntF#hostent.h_name,[Name,FullName]}])
+ end;
+ _ ->
{skip, "IPv6 is not supported on this host"}
end.
@@ -290,47 +284,35 @@ t_getaddr(Config) when is_list(Config) ->
?line {error,nxdomain} = inet:getaddr(DName, inet),
?line {error,nxdomain} = inet:getaddr(DFullName, inet),
?line {ok,DIP} = inet:getaddr(DIPStr, inet),
- ?line {ok,DIP} = inet:getaddr(DIP, inet).
+ ?line {ok,DIP} = inet:getaddr(DIP, inet),
+ ok.
t_getaddr_v6() -> required(v4) ++ required(v6).
t_getaddr_v6(doc) -> "Test the inet:getaddr/2 function.";
t_getaddr_v6(suite) -> [];
t_getaddr_v6(Config) when is_list(Config) ->
- ?line {Name,FullName,IPStr,_IP,_,IP_46_Str,IP46} =
- ct:get_config(test_host_ipv4_only),
- case {inet:getaddr(IP_46_Str, inet6),inet:getaddr(Name, inet6)} of
- {{ok,IP46},{ok,V4Addr}} when V4Addr /= {0,0,0,0,0,0,0,1} ->
- %% Since we suceeded in parsing an IPv6 address string and
- %% look up the name, this computer fully supports IPv6.
- ?line {ok,IP46} = inet:getaddr(IP46, inet6),
- ?line {ok,IP46} = inet:getaddr(Name, inet6),
- ?line {ok,IP46} = inet:getaddr(FullName, inet6),
- ?line {ok,IP46} = inet:getaddr(IPStr, inet6),
-%% ?line IP4toIP6 = inet:getaddr(IPStr, inet6),
-%% ?line case IP4toIP6 of
-%% {ok,IP46} ->
-%% ?line ok;
-%% {error,nxdomain} ->
-%% ?line false =
-%% lists:member(native,
-%% inet_db:res_option(lookup))
-%% end,
- ?line {Name6, FullName6, IPStr6, IP6, _} =
- ct:get_config(test_host_ipv6_only),
- ?line {ok,_} = inet:getaddr(list_to_atom(Name6), inet6),
- ?line {ok,_} = inet:getaddr(Name6, inet6),
- ?line {ok,_} = inet:getaddr(FullName6, inet6),
- ?line {ok,IP6} = inet:getaddr(IP6, inet6),
- ?line {ok,IP6} = inet:getaddr(IPStr6, inet6),
-
- ?line {DName6, DFullName6, DIPStr6, DIP6, _} =
+ {Name,FullName,IPStr,IP,_} =
+ ct:get_config(test_host_ipv6_only),
+
+ case inet:getaddr(Name, inet6) of
+ {ok,Addr} ->
+ IP = Addr,
+ {ok,IP} = inet:getaddr(toupper(Name), inet6),
+ {ok,IP} = inet:getaddr(list_to_atom(Name), inet6),
+ {ok,IP} = inet:getaddr(list_to_atom(toupper(Name)), inet6),
+ {ok,IP} = inet:getaddr(FullName, inet6),
+ {ok,IP} = inet:getaddr(toupper(FullName), inet6),
+ {ok,IP} = inet:getaddr(IP, inet6),
+ {ok,IP} = inet:getaddr(IPStr, inet6),
+ %%
+ {DName,DFullName,DIPStr,DIP,_} =
ct:get_config(test_dummy_ipv6_host),
- ?line {error,nxdomain} = inet:getaddr(DName6, inet6),
- ?line {error,nxdomain} = inet:getaddr(DFullName6, inet6),
- ?line {ok,DIP6} = inet:getaddr(DIPStr6, inet6),
- ?line {ok,DIP6} = inet:getaddr(DIP6, inet6),
+ {error,nxdomain} = inet:getaddr(DName, inet6),
+ {error,nxdomain} = inet:getaddr(DFullName, inet6),
+ {ok,DIP} = inet:getaddr(DIPStr, inet6),
+ {ok,DIP} = inet:getaddr(DIP, inet6),
ok;
- {_,_} ->
+ _ ->
{skip, "IPv6 is not supported on this host"}
end.
@@ -608,8 +590,12 @@ t_parse_address(Func, [String|L]) ->
t_parse_address(Func, L).
parse_strict_address(Config) when is_list(Config) ->
- {ok, Ipv4} = inet:parse_strict_address("127.0.0.1"),
- {ok, Ipv6} = inet:parse_strict_address("c11:0c22:5c33:c440:55c0:c66c:77:0088").
+ {ok, {127,0,0,1}} =
+ inet:parse_strict_address("127.0.0.1"),
+ {ok, {3089,3106,23603,50240,21952,50796,119,136}} =
+ inet:parse_strict_address("c11:0c22:5c33:c440:55c0:c66c:77:0088"),
+ {ok, {3089,3106,23603,50240,0,0,119,136}} =
+ inet:parse_strict_address("c11:0c22:5c33:c440::077:0088").
t_gethostnative(suite) ->[];
t_gethostnative(doc) ->[];
@@ -1102,3 +1088,14 @@ ip_member({127,_,_,_}, [{127,_,_,_}|_]) -> true;
ip_member(K, [K|_]) -> true;
ip_member(K, [_|T]) -> ip_member(K, T);
ip_member(_, []) -> false.
+
+%% Case fold to upper case according to RFC 4343
+%%
+toupper([C|Cs]) when is_integer(C) ->
+ if $a =< C, C =< $z ->
+ [(C - $a + $A)|toupper(Cs)];
+ true ->
+ [C|toupper(Cs)]
+ end;
+toupper([]) ->
+ [].
diff --git a/lib/kernel/test/inet_res_SUITE.erl b/lib/kernel/test/inet_res_SUITE.erl
index f3ba28e4f9..1bc93e3138 100644
--- a/lib/kernel/test/inet_res_SUITE.erl
+++ b/lib/kernel/test/inet_res_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -282,6 +282,7 @@ basic(doc) ->
basic(Config) when is_list(Config) ->
NS = ns(Config),
Name = "ns.otptest",
+ NameC = caseflip(Name),
IP = {127,0,0,254},
%%
%% nslookup
@@ -292,6 +293,17 @@ basic(Config) when is_list(Config) ->
Bin1 = inet_dns:encode(Msg1),
%%io:format("Bin1 = ~w~n", [Bin1]),
{ok,Msg1} = inet_dns:decode(Bin1),
+ %% Now with scrambled case
+ {ok,Msg1b} = inet_res:nslookup(NameC, in, a, [NS]),
+ io:format("~p~n", [Msg1b]),
+ [RR1b] = inet_dns:msg(Msg1b, anlist),
+ IP = inet_dns:rr(RR1b, data),
+ Bin1b = inet_dns:encode(Msg1b),
+ %%io:format("Bin1b = ~w~n", [Bin1b]),
+ {ok,Msg1b} = inet_dns:decode(Bin1b),
+ true =
+ (tolower(inet_dns:rr(RR1, domain))
+ =:= tolower(inet_dns:rr(RR1b, domain))),
%%
%% resolve
{ok,Msg2} = inet_res:resolve(Name, in, a, [{nameservers,[NS]},verbose]),
@@ -301,15 +313,29 @@ basic(Config) when is_list(Config) ->
Bin2 = inet_dns:encode(Msg2),
%%io:format("Bin2 = ~w~n", [Bin2]),
{ok,Msg2} = inet_dns:decode(Bin2),
+ %% Now with scrambled case
+ {ok,Msg2b} = inet_res:resolve(NameC, in, a, [{nameservers,[NS]},verbose]),
+ io:format("~p~n", [Msg2b]),
+ [RR2b] = inet_dns:msg(Msg2b, anlist),
+ IP = inet_dns:rr(RR2b, data),
+ Bin2b = inet_dns:encode(Msg2b),
+ %%io:format("Bin2b = ~w~n", [Bin2b]),
+ {ok,Msg2b} = inet_dns:decode(Bin2b),
+ true =
+ (tolower(inet_dns:rr(RR2, domain))
+ =:= tolower(inet_dns:rr(RR2b, domain))),
%%
%% lookup
[IP] = inet_res:lookup(Name, in, a, [{nameservers,[NS]},verbose]),
+ [IP] = inet_res:lookup(NameC, in, a, [{nameservers,[NS]},verbose]),
%%
%% gethostbyname
{ok,#hostent{h_addr_list=[IP]}} = inet_res:gethostbyname(Name),
+ {ok,#hostent{h_addr_list=[IP]}} = inet_res:gethostbyname(NameC),
%%
%% getbyname
{ok,#hostent{h_addr_list=[IP]}} = inet_res:getbyname(Name, a),
+ {ok,#hostent{h_addr_list=[IP]}} = inet_res:getbyname(NameC, a),
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -317,63 +343,115 @@ basic(Config) when is_list(Config) ->
resolve(doc) ->
["Lookup different records using resolve/2..4"];
resolve(Config) when is_list(Config) ->
+ Class = in,
NS = ns(Config),
Domain = "otptest",
RDomain4 = "0.0.127.in-addr.arpa",
RDomain6 = "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa",
Name = "resolve."++Domain,
- L = [{in,a,Name,[{127,0,0,28}],undefined},
- {in,aaaa,Name,[{0,0,0,0,0,0,32512,28}],undefined},
- {in,cname,"cname."++Name,[Name],undefined},
- {in,a,"cname."++Name,[Name,{127,0,0,28}],undefined},
- {in,ns,"ns."++Name,[],[Name]},
- {in,soa,Domain,[],[{"ns.otptest","lsa.otptest",1,60,10,300,30}]},
+ L = [{a,Name,[{a,{127,0,0,28}}],undefined},
+ {aaaa,Name,[{aaaa,{0,0,0,0,0,0,32512,28}}],undefined},
+ {cname,"cname."++Name,[{cname,Name}],undefined},
+ {a,"cname."++Name,[{cname,Name},{a,{127,0,0,28}}],undefined},
+ {ns,"ns."++Name,[],[{ns,Name}]},
+ {soa,Domain,[],[{soa,{"ns.otptest","lsa.otptest",1,60,10,300,30}}]},
%% WKS: protocol TCP (6), services (bits) TELNET (23) and SMTP (25)
- {in,wks,"wks."++Name,[{{127,0,0,28},6,<<0,0,1,64>>}],undefined},
- {in,ptr,"28."++RDomain4,[Name],undefined},
- {in,ptr,"c.1.0.0.0.0.f.7."++RDomain6,[Name],undefined},
- {in,hinfo,Name,[{"BEAM","Erlang/OTP"}],undefined},
- {in,mx,RDomain4,[{10,"mx."++Domain}],undefined},
- {in,srv,"_srv._tcp."++Name,[{10,3,4711,Name}],undefined},
- {in,naptr,"naptr."++Name,
- [{10,5,"s","http","","_srv._tcp."++Name}],undefined},
- {in,txt,"txt."++Name,
- [["Hej ","du ","glade "],["ta ","en ","spade!"]],undefined},
- {in,mb,"mb."++Name,["mx."++Name],undefined},
- {in,mg,"mg."++Name,["lsa."++Domain],undefined},
- {in,mr,"mr."++Name,["lsa."++Domain],undefined},
- {in,minfo,"minfo."++Name,
- [{"minfo-owner."++Name,"minfo-bounce."++Name}],undefined},
- {in,any,"cname."++Name,[Name],undefined},
- {in,any,Name,[{127,0,0,28},
- {0,0,0,0,0,0,32512,28},
- {"BEAM","Erlang/OTP"}],undefined}
+ {wks,"wks."++Name,[{wks,{{127,0,0,28},6,<<0,0,1,64>>}}],undefined},
+ {ptr,"28."++RDomain4,[{ptr,Name}],undefined},
+ {ptr,"c.1.0.0.0.0.f.7."++RDomain6,[{ptr,Name}],undefined},
+ {hinfo,Name,[{hinfo,{"BEAM","Erlang/OTP"}}],undefined},
+ {mx,RDomain4,[{mx,{10,"mx."++Domain}}],undefined},
+ {srv,"_srv._tcp."++Name,[{srv,{10,3,4711,Name}}],undefined},
+ {naptr,"naptr."++Name,
+ [{naptr,{10,5,"s","http","","_srv._tcp."++Name}}],
+ undefined},
+ {txt,"txt."++Name,
+ [{txt,["Hej ","du ","glade "]},{txt,["ta ","en ","spade!"]}],
+ undefined},
+ {mb,"mb."++Name,[{mb,"mx."++Name}],undefined},
+ {mg,"mg."++Name,[{mg,"Lsa."++Domain}],undefined},
+ {mr,"mr."++Name,[{mr,"LSA."++Domain}],undefined},
+ {minfo,"minfo."++Name,
+ [{minfo,{"minfo-OWNER."++Name,"MinfoBounce."++Name}}],
+ undefined},
+ {any,"cname."++Name,[{cname,Name}],undefined},
+ {any,Name,
+ [{a,{127,0,0,28}},
+ {aaaa,{0,0,0,0,0,0,32512,28}},
+ {hinfo,{"BEAM","Erlang/OTP"}}],
+ undefined}
],
- resolve([{edns,false},{nameservers,[NS]}], L),
- resolve([{edns,0},{nameservers,[NS]}], L).
-
-resolve(_Opts, []) -> ok;
-resolve(Opts, [{Class,Type,Name,Answers,Authority}=Q|Qs]) ->
+ resolve(Class, [{edns,0},{nameservers,[NS]}], L),
+ resolve(Class, [{edns,false},{nameservers,[NS]}], L),
+ %% Again, to see ensure the cache does not mess things up
+ resolve(Class, [{edns,0},{nameservers,[NS]}], L),
+ resolve(Class, [{edns,false},{nameservers,[NS]}], L).
+
+resolve(_Class, _Opts, []) ->
+ ok;
+resolve(Class, Opts, [{Type,Nm,Answers,Authority}=Q|Qs]) ->
io:format("Query: ~p~nOptions: ~p~n", [Q,Opts]),
- {ok,Msg} = inet_res:resolve(Name, Class, Type, Opts),
+ {Name,NameC} =
+ case erlang:phash2(Q) band 4 of
+ 0 ->
+ {Nm,caseflip(Nm)};
+ _ ->
+ {caseflip(Nm),Nm}
+ end,
AnList =
if
Answers =/= undefined ->
- lists:sort(Answers);
+ normalize_answers(Answers);
true ->
undefined
end,
NsList =
if
Authority =/= undefined ->
- lists:sort(Authority);
+ normalize_answers(Authority);
true ->
undefined
end,
- case {lists:sort
- ([inet_dns:rr(RR, data) || RR <- inet_dns:msg(Msg, anlist)]),
- lists:sort
- ([inet_dns:rr(RR, data) || RR <- inet_dns:msg(Msg, nslist)])} of
+ {ok,Msg} = inet_res:resolve(Name, Class, Type, Opts),
+ check_msg(Class, Type, Msg, AnList, NsList),
+ {ok,MsgC} = inet_res:resolve(NameC, Class, Type, Opts),
+ check_msg(Class, Type, MsgC, AnList, NsList),
+ resolve(Class, Opts, Qs).
+
+
+
+normalize_answers(AnList) ->
+ lists:sort([normalize_answer(Answer) || Answer <- AnList]).
+
+normalize_answer({soa,{NS,HM,Ser,Ref,Ret,Exp,Min}}) ->
+ {tolower(NS),tolower_email(HM),Ser,Ref,Ret,Exp,Min};
+normalize_answer({mx,{Prio,DN}}) ->
+ {Prio,tolower(DN)};
+normalize_answer({srv,{Prio,Weight,Port,DN}}) ->
+ {Prio,Weight,Port,tolower(DN)};
+normalize_answer({naptr,{Order,Pref,Flags,Service,RE,Repl}}) ->
+ {Order,Pref,Flags,Service,RE,tolower(Repl)};
+normalize_answer({minfo,{RespM,ErrM}}) ->
+ {tolower_email(RespM),tolower_email(ErrM)};
+normalize_answer({T,MN}) when T =:= mg; T =:= mr ->
+ tolower_email(MN);
+normalize_answer({T,DN}) when T =:= cname; T =:= ns; T =:= ptr; T =:= mb ->
+ tolower(DN);
+normalize_answer(Answer) ->
+ Answer.
+
+check_msg(Class, Type, Msg, AnList, NsList) ->
+ io:format("check_msg Type: ~p, Msg: ~p~n.", [Type,Msg]),
+ case {normalize_answers(
+ [begin
+ Class = inet_dns:rr(RR, class),
+ {inet_dns:rr(RR, type),inet_dns:rr(RR, data)}
+ end || RR <- inet_dns:msg(Msg, anlist)]),
+ normalize_answers(
+ [begin
+ Class = inet_dns:rr(RR, class),
+ {inet_dns:rr(RR, type),inet_dns:rr(RR, data)}
+ end || RR <- inet_dns:msg(Msg, nslist)])} of
{AnList,NsList} ->
ok;
{NsList,AnList} when Type =:= ns ->
@@ -389,7 +467,7 @@ resolve(Opts, [{Class,Type,Name,Answers,Authority}=Q|Qs]) ->
end,
Buf = inet_dns:encode(Msg),
{ok,Msg} = inet_dns:decode(Buf),
- resolve(Opts, Qs).
+ ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -497,6 +575,7 @@ files_monitor(Config) when is_list(Config) ->
do_files_monitor(Config) ->
Dir = ?config(priv_dir, Config),
{ok,Hostname} = inet:gethostname(),
+ io:format("Hostname = ~p.~n", [Hostname]),
FQDN =
case inet_db:res_option(domain) of
"" ->
@@ -504,11 +583,13 @@ do_files_monitor(Config) ->
_ ->
Hostname++"."++inet_db:res_option(domain)
end,
+ io:format("FQDN = ~p.~n", [FQDN]),
HostsFile = filename:join(Dir, "files_monitor_hosts"),
ResolvConf = filename:join(Dir, "files_monitor_resolv.conf"),
ok = inet_db:res_option(resolv_conf, ResolvConf),
ok = inet_db:res_option(hosts_file, HostsFile),
[] = inet_db:res_option(search),
+ %% The inet function will use its final fallback to find this host
{ok,#hostent{h_name = Hostname,
h_addrtype = inet,
h_length = 4,
@@ -521,6 +602,7 @@ do_files_monitor(Config) ->
{error,nxdomain} = inet_res:gethostbyname(FQDN),
{ok,{127,0,0,10}} = inet:getaddr("mx.otptest", inet),
{ok,{0,0,0,0,0,0,32512,28}} = inet:getaddr("resolve.otptest", inet6),
+ %% The inet function will use its final fallback to find this host
{ok,#hostent{h_name = Hostname,
h_addrtype = inet6,
h_length = 16,
@@ -603,3 +685,41 @@ ipv4_to_ipv6() -> inet_SUITE:ipv4_to_ipv6().
ipv4_to_ipv6(Config) -> inet_SUITE:ipv4_to_ipv6(Config).
host_and_addr() -> inet_SUITE:host_and_addr().
host_and_addr(Config) -> inet_SUITE:host_and_addr(Config).
+
+
+
+%% Case flip helper
+
+caseflip([C|Cs]) when is_integer(C), $a =< C, C =< $z ->
+ [(C - $a + $A)|caseflip_skip(Cs)];
+caseflip([C|Cs]) when is_integer(C), $A =< C, C =< $Z ->
+ [(C - $A + $a)|caseflip_skip(Cs)];
+caseflip([C|Cs]) ->
+ [C|caseflip(Cs)];
+caseflip([]) ->
+ [].
+
+caseflip_skip([C|Cs]) when is_integer(C), $a =< C, C =< $z ->
+ [C|caseflip(Cs)];
+caseflip_skip([C|Cs]) when is_integer(C), $A =< C, C =< $Z ->
+ [C|caseflip(Cs)];
+caseflip_skip([C|Cs]) ->
+ [C|caseflip_skip(Cs)];
+caseflip_skip([]) ->
+ [].
+
+tolower_email([$.|Cs]) ->
+ [$.|tolower(Cs)];
+tolower_email([C|Cs]) ->
+ [C|tolower_email(Cs)].
+
+%% Case fold to lower case according to RFC 4343
+%%
+tolower([C|Cs]) when is_integer(C) ->
+ if $A =< C, C =< $Z ->
+ [(C - $A + $a)|tolower(Cs)];
+ true ->
+ [C|tolower(Cs)]
+ end;
+tolower([]) ->
+ [].
diff --git a/lib/kernel/test/inet_res_SUITE_data/otptest/named_inc.conf b/lib/kernel/test/inet_res_SUITE_data/otptest/named_inc.conf
index 0b01b25204..2d68f6e59c 100644
--- a/lib/kernel/test/inet_res_SUITE_data/otptest/named_inc.conf
+++ b/lib/kernel/test/inet_res_SUITE_data/otptest/named_inc.conf
@@ -2,11 +2,11 @@ zone "." in {
type master;
file "root.zone";
};
-zone "0.0.127.in-addr.arpa" in {
+zone "0.0.127.in-addr.arpa." in {
type master;
file "0.0.127.in-addr.arpa.zone";
};
-zone "0.0.0.0.f.7.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa" in {
+zone "0.0.0.0.f.7.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa." in {
type master;
file "0.0.0.0.f.7.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.zone";
-}; \ No newline at end of file
+};
diff --git a/lib/kernel/test/inet_res_SUITE_data/otptest/root.zone b/lib/kernel/test/inet_res_SUITE_data/otptest/root.zone
index 11cba18d45..5a56eac95c 100644
--- a/lib/kernel/test/inet_res_SUITE_data/otptest/root.zone
+++ b/lib/kernel/test/inet_res_SUITE_data/otptest/root.zone
@@ -43,8 +43,8 @@ naptr.resolve.otptest IN NAPTR 10 5 "S" "HTTP" "" _srv._tcp.resolve.otptest
txt.resolve.otptest IN TXT "Hej " "du " "glade "
txt.resolve.otptest IN TXT "ta " "en " "spade!"
mb.resolve.otptest IN MB mx.resolve.otptest
-mg.resolve.otptest IN MG lsa.otptest
-mr.resolve.otptest IN MR lsa.otptest
-minfo.resolve.otptest IN MINFO minfo-owner.resolve.otptest minfo-bounce.resolve.otptest
+mg.resolve.otptest IN MG Lsa.otptest
+mr.resolve.otptest IN MR LSA.otptest
+minfo.resolve.otptest IN MINFO minfo-OWNER.resolve.otptest MinfoBounce.resolve.otptest
ns.otptest IN A 127.0.0.254