From 8152505ea3793037984a7f3349ac95cc20c4e44c Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Mon, 27 May 2013 16:41:59 +0200 Subject: Improve case (in)sensitivity for host lookups --- lib/kernel/src/inet.erl | 41 +++++++++++++++++---------- lib/kernel/src/inet_db.erl | 65 +++++++++++++++++++++++-------------------- lib/kernel/src/inet_hosts.erl | 9 ++++-- lib/kernel/src/inet_res.erl | 6 ++-- 4 files changed, 71 insertions(+), 50 deletions(-) (limited to 'lib/kernel') 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,_}, _, _) -> []. %% -------------------------------------------------------------------------- -- cgit v1.2.3 From 54240a5829cac17ea59eeeb16f343f3f2817d7b3 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Fri, 7 Jun 2013 12:05:02 +0200 Subject: Add test cases for host lookup case (in)sensitivity --- lib/kernel/test/inet_SUITE.erl | 197 ++++++++++---------- lib/kernel/test/inet_res_SUITE.erl | 198 +++++++++++++++++---- .../inet_res_SUITE_data/otptest/named_inc.conf | 6 +- .../test/inet_res_SUITE_data/otptest/root.zone | 6 +- 4 files changed, 262 insertions(+), 145 deletions(-) (limited to 'lib/kernel') 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 -- cgit v1.2.3