summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaimo Niskanen <raimo@erlang.org>2020-02-18 18:04:44 +0100
committerRaimo Niskanen <raimo@erlang.org>2020-02-20 09:35:18 +0100
commit32e1c59cb2dbba5454ec63286a737a3e29e6df3a (patch)
tree738fd9ff8bea168726d3a1b1d8d1501a20f8e471
parentc0dfc57e9f061c018b58e7e0d5975c61bd218b1f (diff)
downloaderlang-32e1c59cb2dbba5454ec63286a737a3e29e6df3a.tar.gz
Keep intermediate error
-rw-r--r--lib/kernel/src/inet_dns.erl6
-rw-r--r--lib/kernel/src/inet_res.erl157
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) ->