File 3542-Verify-DNS-reply.patch of Package erlang
From 0fdcb294d94f6d8443a4d3d7095cd524f8b128ec Mon Sep 17 00:00:00 2001
From: Raimo Niskanen <raimo@erlang.org>
Date: Thu, 15 Apr 2021 14:00:22 +0200
Subject: [PATCH 2/8] Verify DNS reply
---
lib/kernel/src/inet_res.erl | 103 +++++++++++++++++++++++-------------
1 file changed, 66 insertions(+), 37 deletions(-)
diff --git a/lib/kernel/src/inet_res.erl b/lib/kernel/src/inet_res.erl
index 4e71944492..1b6344f372 100644
--- a/lib/kernel/src/inet_res.erl
+++ b/lib/kernel/src/inet_res.erl
@@ -382,7 +382,7 @@ gethostbyaddr_tm(_,_) -> {error, formerr}.
%% 2. the list of alternative name servers
%%
res_gethostbyaddr(Addr, IP, Timer) ->
- case res_query(Addr, in, ptr, [], Timer) of
+ case res_query(Addr, in, ?S_PTR, [], Timer) of
{ok, Rec} ->
inet_db:res_gethostbyaddr(IP, Rec);
{error,{qfmterror,_}} -> {error,einval};
@@ -649,6 +649,7 @@ make_query(Dname, Class, Type, Opts) ->
make_query(Dname, Class, Type, Options, Edns) ->
Id = inet_db:res_option(next_id),
Recurse = Options#options.recurse,
+ RD = Recurse =:= 1 orelse Recurse =:= true, % (0 | 1 | true | false)
ARList = case Edns of
false -> [];
_ ->
@@ -656,17 +657,18 @@ make_query(Dname, Class, Type, Options, Edns) ->
[#dns_rr_opt{udp_payload_size=PSz,
version=Edns}]
end,
- Msg = #dns_rec{header=#dns_header{id=Id,
+ Msg = #dns_rec{header=#dns_header{id=Id,
+ qr=false,
opcode='query',
- rd=Recurse,
+ rd=RD,
rcode=?NOERROR},
- qdlist=[#dns_query{domain=Dname,
- type=Type,
- class=Class}],
+ qdlist=[#dns_query{domain=Dname,
+ type=Type,
+ class=Class}],
arlist=ARList},
?verbose(Options#options.verbose, "Query: ~p~n", [dns_msg(Msg)]),
Buffer = inet_dns:encode(Msg),
- {Id, Buffer}.
+ {Msg, Buffer}.
%% --------------------------------------------------------------------------
%% socket helpers
@@ -820,13 +822,13 @@ query_nss_edns(
#q{options =
#options{
udp_payload_size = PSz}=Options,
- edns = {Id,Buffer}}=Q,
+ edns = EDNSQuery}=Q,
[NsSpec|NSs], Timer, Retry, I, S_0, Reason, RetryNSs) ->
%%
{IP,Port} = NS = servfail_retry_wait(NsSpec),
{S,Result} =
query_ns(
- S_0, Id, Buffer, IP, Port, Timer, Retry, I, Options, PSz),
+ S_0, EDNSQuery, IP, Port, Timer, Retry, I, Options, PSz),
case Result of
{error,{E,_}}
when E =:= qfmterror;
@@ -848,19 +850,21 @@ query_nss_edns(
end.
query_nss_dns(
- #q{dns = Qdns}=Q_0,
+ #q{dns = DNSQuery_0}=Q_0,
[NsSpec|NSs], Timer, Retry, I, S_0, Reason, RetryNSs) ->
%%
{IP,Port} = NS = servfail_retry_wait(NsSpec),
#q{options = Options,
- dns = {Id,Buffer}}=Q =
+ dns = DNSQuery}=Q =
if
- is_function(Qdns, 0) -> Q_0#q{dns=Qdns()};
- true -> Q_0
+ is_function(DNSQuery_0, 0) ->
+ Q_0#q{dns=DNSQuery_0()};
+ true ->
+ Q_0
end,
{S,Result} =
query_ns(
- S_0, Id, Buffer, IP, Port, Timer, Retry, I, Options, ?PACKETSZ),
+ S_0, DNSQuery, IP, Port, Timer, Retry, I, Options, ?PACKETSZ),
query_nss_result(
Q, NSs, Timer, Retry, I, S, Reason, RetryNSs, NS, Result).
@@ -952,14 +956,14 @@ query_retries_error(#q{options=#options{nxdomain_reply=NxReply}}, S, Reason) ->
end.
-query_ns(S0, Id, Buffer, IP, Port, Timer, Retry, I,
+query_ns(S0, {Msg, Buffer}, IP, Port, Timer, Retry, I,
#options{timeout=Tm,usevc=UseVC,verbose=Verbose},
PSz) ->
case UseVC orelse iolist_size(Buffer) > PSz of
true ->
TcpTimeout = inet:timeout(Tm*5, Timer),
{S0,
- query_tcp(TcpTimeout, Id, Buffer, IP, Port, Verbose)};
+ query_tcp(TcpTimeout, Msg, Buffer, IP, Port, Verbose)};
false ->
case udp_open(S0, IP) of
{ok,S} ->
@@ -967,13 +971,13 @@ query_ns(S0, Id, Buffer, IP, Port, Timer, Retry, I,
inet:timeout( (Tm * (1 bsl I)) div Retry, Timer),
case
query_udp(
- S, Id, Buffer, IP, Port, UdpTimeout, Verbose)
+ S, Msg, Buffer, IP, Port, UdpTimeout, Verbose)
of
{ok,#dns_rec{header=H}} when H#dns_header.tc ->
TcpTimeout = inet:timeout(Tm*5, Timer),
{S,
query_tcp(
- TcpTimeout, Id, Buffer, IP, Port, Verbose)};
+ TcpTimeout, Msg, Buffer, IP, Port, Verbose)};
{error, econnrefused} = Err ->
ok = udp_close(S),
{#sock{}, Err};
@@ -984,9 +988,9 @@ query_ns(S0, Id, Buffer, IP, Port, Timer, Retry, I,
end
end.
-query_udp(_S, _Id, _Buffer, _IP, _Port, 0, _Verbose) ->
+query_udp(_S, _Msg, _Buffer, _IP, _Port, 0, _Verbose) ->
timeout;
-query_udp(S, Id, Buffer, IP, Port, Timeout, Verbose) ->
+query_udp(S, Msg, Buffer, IP, Port, Timeout, Verbose) ->
?verbose(Verbose, "Try UDP server : ~p:~p (timeout=~w)\n",
[IP,Port,Timeout]),
case
@@ -999,7 +1003,7 @@ query_udp(S, Id, Buffer, IP, Port, Timeout, Verbose) ->
Decode =
fun ({RecIP,RecPort,Answer})
when RecIP =:= IP, RecPort =:= Port ->
- case decode_answer(Answer, Id, Verbose) of
+ case decode_answer(Answer, Msg, Verbose) of
{error,badid} ->
false;
Reply ->
@@ -1020,9 +1024,9 @@ query_udp(S, Id, Buffer, IP, Port, Timeout, Verbose) ->
{error,econnrefused}
end.
-query_tcp(0, _Id, _Buffer, _IP, _Port, _Verbose) ->
+query_tcp(0, _Msg, _Buffer, _IP, _Port, _Verbose) ->
timeout;
-query_tcp(Timeout, Id, Buffer, IP, Port, Verbose) ->
+query_tcp(Timeout, Msg, Buffer, IP, Port, Verbose) ->
?verbose(Verbose, "Try TCP server : ~p:~p (timeout=~w)\n",
[IP, Port, Timeout]),
Family = case IP of
@@ -1038,7 +1042,7 @@ query_tcp(Timeout, Id, Buffer, IP, Port, Verbose) ->
case gen_tcp:recv(S, 0, Timeout) of
{ok, Answer} ->
gen_tcp:close(S),
- case decode_answer(Answer, Id, Verbose) of
+ case decode_answer(Answer, Msg, Verbose) of
{ok, _} = OK -> OK;
{error, badid} -> {error, servfail};
Error -> Error
@@ -1062,38 +1066,63 @@ query_tcp(Timeout, Id, Buffer, IP, Port, Verbose) ->
_:_ -> {error, einval}
end.
-decode_answer(Answer, Id, Verbose) ->
+decode_answer(Answer, Q_Msg, Verbose) ->
case inet_dns:decode(Answer) of
- {ok, Msg} ->
+ {ok, #dns_rec{header = H, arlist = ARList} = Msg} ->
?verbose(Verbose, "Got reply: ~p~n", [dns_msg(Msg)]),
- E = case lists:keyfind(dns_rr_opt, 1, Msg#dns_rec.arlist) of
+ E = case lists:keyfind(dns_rr_opt, 1, ARList) of
false -> 0;
#dns_rr_opt{ext_rcode=ExtRCode} -> ExtRCode
end,
- H = Msg#dns_rec.header,
RCode = (E bsl 4) bor H#dns_header.rcode,
case RCode of
- ?NOERROR ->
- if H#dns_header.id =/= Id ->
- {error,badid};
- length(Msg#dns_rec.qdlist) =/= 1 ->
- {error,{noquery,Msg}};
- true ->
- {ok, Msg}
- end;
+ ?NOERROR -> decode_answer_noerror(Q_Msg, Msg, H);
?FORMERR -> {error,{qfmterror,Msg}};
?SERVFAIL -> {error,{servfail,Msg}};
?NXDOMAIN -> {error,{nxdomain,Msg}};
?NOTIMP -> {error,{notimp,Msg}};
?REFUSED -> {error,{refused,Msg}};
?BADVERS -> {error,{badvers,Msg}};
- _ -> {error,{unknown,Msg}}
+ _ -> {error,{unknown,Msg}}
end;
{error, formerr} = Error ->
?verbose(Verbose, "Got reply: decode format error~n", []),
Error
end.
+decode_answer_noerror(
+ #dns_rec{header = Q_H, qdlist = [Q_RR]},
+ #dns_rec{qdlist = QDList} = Msg,
+ H) ->
+ %% Validate the reply
+ if
+ H#dns_header.id =/= Q_H#dns_header.id ->
+ {error,badid};
+ H#dns_header.qr =/= true;
+ H#dns_header.opcode =/= Q_H#dns_header.opcode;
+ H#dns_header.rd =/= Q_H#dns_header.rd ->
+ {error,{unknown,Msg}};
+ true ->
+ case QDList of
+ [RR] ->
+ case
+ (RR#dns_query.class =:= Q_RR#dns_query.class)
+ andalso
+ (RR#dns_query.type =:= Q_RR#dns_query.type)
+ andalso
+ (inet_db:tolower(RR#dns_query.domain) =:=
+ inet_db:tolower(Q_RR#dns_query.domain))
+ of
+ true ->
+ {ok, Msg};
+ false ->
+ {error,{noquery,Msg}}
+ end;
+ _ when is_list(QDList) ->
+ {error,{noquery,Msg}}
+ end
+ end.
+
%%
%% Transform domain name or address
%% 1. "a.b.c" =>
--
2.31.1