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

openSUSE Build Service is sponsored by