diff options
author | Raimo Niskanen <raimo@erlang.org> | 2020-02-18 18:04:44 +0100 |
---|---|---|
committer | Raimo Niskanen <raimo@erlang.org> | 2020-02-20 09:35:18 +0100 |
commit | 32e1c59cb2dbba5454ec63286a737a3e29e6df3a (patch) | |
tree | 738fd9ff8bea168726d3a1b1d8d1501a20f8e471 | |
parent | c0dfc57e9f061c018b58e7e0d5975c61bd218b1f (diff) | |
download | erlang-32e1c59cb2dbba5454ec63286a737a3e29e6df3a.tar.gz |
Keep intermediate error
-rw-r--r-- | lib/kernel/src/inet_dns.erl | 6 | ||||
-rw-r--r-- | lib/kernel/src/inet_res.erl | 157 |
2 files changed, 107 insertions, 56 deletions
diff --git a/lib/kernel/src/inet_dns.erl b/lib/kernel/src/inet_dns.erl index 6c98d2aab7..e03f124fe6 100644 --- a/lib/kernel/src/inet_dns.erl +++ b/lib/kernel/src/inet_dns.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2017. All Rights Reserved. +%% Copyright Ericsson AB 1997-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -109,8 +109,8 @@ lists_member(H, [H|_]) -> true; lists_member(H, [_|T]) -> lists_member(H, T). - --define(DECODE_ERROR, fmt). % must match a clause in inet_res:query_nss_e?dns +%% must match a clause in inet_res:query_nss_e?dns +-define(DECODE_ERROR, formerr). %% %% Decode a dns buffer. diff --git a/lib/kernel/src/inet_res.erl b/lib/kernel/src/inet_res.erl index 6454802b04..5d215cb729 100644 --- a/lib/kernel/src/inet_res.erl +++ b/lib/kernel/src/inet_res.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2018. All Rights Reserved. +%% Copyright Ericsson AB 1997-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -758,71 +758,122 @@ udp_close(#sock{inet=I,inet6=I6}) -> %% And that is what the code seems to do, now fixed, hopefully... do_query(_Q, [], _Timer) -> + %% We have no name server to ask, so say nxdomain {error,nxdomain}; do_query(#q{options=#options{retry=Retry}}=Q, NSs, Timer) -> - query_retries(Q, NSs, Timer, Retry, 0, #sock{}). - -query_retries(_Q, _NSs, _Timer, Retry, Retry, S) -> - udp_close(S), - {error,timeout}; -query_retries(_Q, [], _Timer, _Retry, _I, S) -> - udp_close(S), - {error,timeout}; -query_retries(Q, NSs, Timer, Retry, I, S0) -> - case query_nss(Q, NSs, Timer, Retry, I, S0, []) of - {S,{noanswer,ErrNSs}} -> %% remove unreachable nameservers - query_retries(Q, NSs--ErrNSs, Timer, Retry, I+1, S); - {S,Result} -> - udp_close(S), - Result - end. + %% We have at least one name server, + %% so a failure will be a timeout, + %% unless a name server says otherwise + Reason = timeout, + query_retries(Q, NSs, Timer, Retry, 0, #sock{}, Reason). -query_nss(_Q, [], _Timer, _Retry, _I, S, ErrNSs) -> - {S,{noanswer,ErrNSs}}; -query_nss(#q{edns=undefined}=Q, NSs, Timer, Retry, I, S, ErrNSs) -> - query_nss_dns(Q, NSs, Timer, Retry, I, S, ErrNSs); -query_nss(Q, NSs, Timer, Retry, I, S, ErrNSs) -> - query_nss_edns(Q, NSs, Timer, Retry, I, S, ErrNSs). +%% Loop until out of name servers or retries +%% +query_retries(_Q, _NSs, _Timer, Retry, Retry, S, Reason) -> + query_retries_error(S, Reason); +query_retries(_Q, [], _Timer, _Retry, _I, S, Reason) -> + query_retries_error(S, Reason); +query_retries(Q, NSs, Timer, Retry, I, S_0, Reason) -> + query_nss(Q, NSs, Timer, Retry, I, S_0, Reason, NSs). + +%% Loop for all name servers, for each: +%% If EDNS is enabled, try that first, +%% and for selected failures fall back to plain DNS. +%% +query_nss(Q, NSs, Timer, Retry, I, S, Reason, []) -> + %% End of name servers list, do a new retry + query_retries(Q, NSs, Timer, Retry, I+1, S, Reason); +query_nss(#q{edns = undefined}=Q, NSs, Timer, Retry, I, S, Reason, TryNSs) -> + query_nss_dns(Q, NSs, Timer, Retry, I, S, Reason, TryNSs); +query_nss(Q, NSs, Timer, Retry, I, S, Reason, TryNSs) -> + query_nss_edns(Q, NSs, Timer, Retry, I, S, Reason, TryNSs). query_nss_edns( - #q{options=#options{udp_payload_size=PSz}=Options,edns={Id,Buffer}}=Q, - [{IP,Port}=NS|NSs]=NSs0, Timer, Retry, I, S0, ErrNSs) -> - {S,Res}=Reply = - query_ns(S0, Id, Buffer, IP, Port, Timer, Retry, I, Options, PSz), - case Res of - timeout -> {S,{error,timeout}}; % Bailout timeout - {ok,_} -> Reply; - {error,{nxdomain,_}} -> Reply; - {error,{E,_}} when E =:= qfmterror; E =:= notimp; E =:= servfail; - E =:= badvers -> - query_nss_dns(Q, NSs0, Timer, Retry, I, S, ErrNSs); - {error,E} when E =:= fmt; E =:= enetunreach; E =:= econnrefused -> - query_nss(Q, NSs, Timer, Retry, I, S, [NS|ErrNSs]); - _Error -> - query_nss(Q, NSs, Timer, Retry, I, S, ErrNSs) + #q{options = + #options{ + udp_payload_size = PSz}=Options, + edns = {Id,Buffer}}=Q, + NSs, Timer, Retry, I, S_0, Reason, [{IP,Port}=NS|TryNSs]=TryNSs_0) -> + %% + {S,Result} = + query_ns( + S_0, Id, Buffer, IP, Port, Timer, Retry, I, Options, PSz), + case Result of + {error,{E,_}} + when E =:= qfmterror; + E =:= notimp; + E =:= servfail; + E =:= badvers -> + %% The server did not like that, + %% ignore that error and try plain DNS + query_nss_dns( + Q, NSs, Timer, Retry, I, S, Reason, TryNSs_0); + _ -> + query_nss_result( + Q, NSs, Timer, Retry, I, S, Reason, TryNSs, NS, Result) end. query_nss_dns( - #q{dns=Qdns}=Q0, - [{IP,Port}=NS|NSs], Timer, Retry, I, S0, ErrNSs) -> - #q{options=Options,dns={Id,Buffer}}=Q = + #q{dns = Qdns}=Q_0, + NSs, Timer, Retry, I, S_0, Reason, [{IP,Port}=NS|TryNSs]) -> + %% + #q{options = Options, + dns = {Id,Buffer}}=Q = if - is_function(Qdns, 0) -> Q0#q{dns=Qdns()}; - true -> Q0 + is_function(Qdns, 0) -> Q_0#q{dns=Qdns()}; + true -> Q_0 end, - {S,Res}=Reply = + {S,Result} = query_ns( - S0, Id, Buffer, IP, Port, Timer, Retry, I, Options, ?PACKETSZ), - case Res of - timeout -> {S,{error,timeout}}; % Bailout timeout - {ok,_} -> Reply; - {error,{E,_}} when E =:= nxdomain; E =:= qfmterror -> Reply; - {error,E} when E =:= fmt; E =:= enetunreach; E =:= econnrefused -> - query_nss(Q, NSs, Timer, Retry, I, S, [NS|ErrNSs]); - _Error -> - query_nss(Q, NSs, Timer, Retry, I, S, ErrNSs) + S_0, Id, Buffer, IP, Port, Timer, Retry, I, Options, ?PACKETSZ), + query_nss_result( + Q, NSs, Timer, Retry, I, S, Reason, TryNSs, NS, Result). + +query_nss_result(Q, NSs, Timer, Retry, I, S, Reason, TryNSs, NS, Result) -> + case Result of + {ok,_} -> + _ = udp_close(S), + Result; + timeout -> % Out of total time timeout + query_retries_error(S, Reason); % The best reason we have + {error,timeout} -> % Query timeout + %% Try next server, may retry this server later + query_nss(Q, NSs, Timer, Retry, I, S, Reason, TryNSs); + {error,{nxdomain,_}=NewReason} -> + query_retries_error(S, NewReason); % Definite answer + {error,{E,_}=NewReason} + when E =:= qfmterror; + E =:= notimp; + E =:= refused; + E =:= badvers -> + %% The server did not like that. + %% Remove this server from retry list since + %% it will not answer differently on the next retry. + NewNSs = lists:delete(NS, NSs), + query_nss(Q, NewNSs, Timer, Retry, I, S, NewReason, TryNSs); + {error,E=NewReason} + when E =:= formerr; + E =:= enetunreach; + E =:= econnrefused -> + %% Could not decode answer, or network problem. + %% Remove this server from retry list. + NewNSs = lists:delete(NS, NSs), + query_nss(Q, NewNSs, Timer, Retry, I, S, NewReason, TryNSs); + {error,NewReason} -> + %% Try next server, may retry this server later + query_nss(Q, NSs, Timer, Retry, I, S, NewReason, TryNSs) end. +query_retries_error(S, Reason) -> + _ = udp_close(S), + case Reason of + {nxdomain, _} -> + {error, nxdomain}; + _ -> + {error, Reason} + end. + + query_ns(S0, Id, Buffer, IP, Port, Timer, Retry, I, #options{timeout=Tm,usevc=UseVC,verbose=Verbose}, PSz) -> |