From b057a9d995017b1be50d6dc02edd52382f3231b8 Mon Sep 17 00:00:00 2001
From: Raimo Niskanen <raimo@erlang.org>
Date: Mon, 16 Mar 2026 16:36:57 +0100
Subject: [PATCH] Update after feedback

* Hide the randomized port number -1 internal feature better, so that
  it does not show in the type spec of `gen_udp:open/2`.
  Suppress the Dialyzer warnings that follows from that.

* Mention RFC 5452 in the RFC list.

* Explain in a code comment why we use a deprecated `crypto` function.

* Fix inet:bind_random to actually do a final attempt with port 0.
  Reduce the number of random attempts to 3 since the "always"
  succeed anyway on a "normal" machine.

* Fix SCTP modules to handle port number like the TCP and UDP modules.
---
 lib/kernel/src/gen_udp.erl    |  4 +--
 lib/kernel/src/inet.erl       | 46 +++++++++++++++++++++++------------
 lib/kernel/src/inet6_sctp.erl |  9 ++++---
 lib/kernel/src/inet_db.erl    |  4 +++
 lib/kernel/src/inet_dns.erl   |  1 +
 lib/kernel/src/inet_res.erl   | 12 ++++++++-
 lib/kernel/src/inet_sctp.erl  |  8 +++---
 7 files changed, 58 insertions(+), 26 deletions(-)

Index: otp-OTP-27.1.3/lib/kernel/src/gen_udp.erl
===================================================================
--- otp-OTP-27.1.3.orig/lib/kernel/src/gen_udp.erl
+++ otp-OTP-27.1.3/lib/kernel/src/gen_udp.erl
@@ -183,7 +183,7 @@ The 3-tuple form _isn't_ supported on al
 
 -doc(#{equiv => open(Port, [])}).
 -spec open(Port) -> {ok, Socket} | {error, Reason} when
-      Port   :: inet:port_number() | -1,
+      Port   :: inet:port_number(),
       Socket :: socket(),
       Reason :: system_limit | inet:posix().
 
@@ -297,7 +297,7 @@ can be truncated without warning.
 The default value for the receive buffer option is `{recbuf, 8192}`.
 """.
 -spec open(Port, Opts) -> {ok, Socket} | {error, Reason} when
-      Port   :: inet:port_number() | -1,
+      Port   :: inet:port_number(),
       Opts   :: [inet:inet_backend() | open_option()],
       Socket :: socket(),
       Reason :: system_limit | inet:posix().
Index: otp-OTP-27.1.3/lib/kernel/src/inet.erl
===================================================================
--- otp-OTP-27.1.3.orig/lib/kernel/src/inet.erl
+++ otp-OTP-27.1.3/lib/kernel/src/inet.erl
@@ -3841,7 +3841,7 @@ gethostbyaddr_tm_native(Addr, Timer, Opt
 	      {ip6_address() | 'any' | 'loopback',
 	       port_number()}} |
 	     undefined, % Internal - no bind()
-	   BPort :: port_number() | -1,
+	   BPort :: port_number(),
 	   Opts :: [socket_setopt()],
 	   Protocol :: socket_protocol(),
 	   Family :: address_family(),
@@ -3897,7 +3897,7 @@ open(Fd_or_OpenOpts, BAddr, BPort, Opts,
                    {ip6_address() | 'any' | 'loopback',
                     port_number()}} |
                   undefined, % Internal - translated to 'any'
-                BPort :: port_number() | -1,
+                BPort :: port_number(),
                 Opts :: [socket_setopt()],
                 Protocol :: socket_protocol(),
                 Family :: address_family(),
@@ -4000,24 +4000,38 @@ open_setopts(S, BAddr, BPort, Opts, Modu
 
 
 
-bind(S, Addr, Port) when is_list(Addr), ?port(Port) ->
+bind(S, Addr, Port) when is_list(Addr), is_integer(Port) ->
     bindx(S, Addr, Port);
 bind(S, Addr, -1) ->
-    bind_random(S, Addr, 5);
-bind(S, Addr, Port) when ?port(Port) ->
-    %% ?DBG([{s, S}, {addr, Addr}, {port, Port}]),
-    prim_inet:bind(S, Addr, Port).
-
-bind_random(S, Addr, Cnt) when is_integer(Cnt) ->
+    bind_random(S, Addr);
+bind(S, Addr, Port) when is_integer(Port) ->
+    do_bind(S, Addr, Port).
+
+do_bind(S, Addr, Port) ->
+    Result = prim_inet:bind(S, Addr, Port),
+    %% ?DBG([{s, S}, {addr, Addr}, {port, Port}, Result]),
+    Result.
+
+bind_random(S, Addr) ->
+    Cnt = 3,
+    bind_random(S, Addr, Cnt).
+%%
+bind_random(S, Addr, 0 = _Cnt) ->
+    Port = 0,
+    do_bind(S, Addr, Port);
+bind_random(S, Addr, Cnt) when is_integer(Cnt), 0 < Cnt ->
     Port = inet_db:res_option(random_port),
-    %% ?DBG([{s, S}, {addr, Addr}, {port, Port}]),
+    bind_random(S, Addr, Cnt, Port).
+%%
+bind_random(S, Addr, _Cnt, 0 = Port) ->
+    do_bind(S, Addr, Port);
+bind_random(S, Addr, Cnt, Port) when ?port(Port) ->
     case prim_inet:bind(S, Addr, Port) of
-        {ok, _} = OK                -> OK;
-        {error, _} = Error ->
-            if  Port =:= 0          -> Error;
-                0 < Cnt             -> bind_random(S, Addr, Cnt - 1);
-                true                -> Error
-            end
+        {ok, _} = OK ->
+            %% ?DBG([{s, S}, {addr, Addr}, {port, Port}, OK]),
+            OK;
+        {error, _} ->
+            bind_random(S, Addr, Cnt - 1)
     end.
 
 bindx(S, [Addr], Port0) ->
Index: otp-OTP-27.1.3/lib/kernel/src/inet6_sctp.erl
===================================================================
--- otp-OTP-27.1.3.orig/lib/kernel/src/inet6_sctp.erl
+++ otp-OTP-27.1.3/lib/kernel/src/inet6_sctp.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2007-2024. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2026. 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.
@@ -55,11 +55,12 @@ open(Opts) ->
                        ifaddr = Addr,
                        port   = Port,
                        type   = Type,
-                       opts   = SOs}} ->
+                       opts   = SOs}}
+          when ?port(Port) ->
 	    inet:open_bind(
               Fd, Addr, Port, SOs, ?PROTO, ?FAMILY, Type, ?MODULE);
-	Error ->
-            Error
+        {ok, _} -> {error, badarg};
+	Error   -> Error
     end.
 
 close(S) ->
Index: otp-OTP-27.1.3/lib/kernel/src/inet_db.erl
===================================================================
--- otp-OTP-27.1.3.orig/lib/kernel/src/inet_db.erl
+++ otp-OTP-27.1.3/lib/kernel/src/inet_db.erl
@@ -2107,6 +2107,10 @@ generate_random_port() ->
         undefined                                               -> 0
     end.
 
+%% We use `crypto:rand_uniform/2` here, which is the simplest to use,
+%% but it is deprecated, for outdated reasons.  In really old, now obsolete,
+%% libcrypto versions the function was not cryptographically strong,
+%% but since OpenSSL 1.1.0 that is no longer the case.
 -compile({nowarn_deprecated_function, {crypto,rand_uniform,2}}).
 
 crypto_rand_range(Range) when is_integer(Range), 0 < Range ->
Index: otp-OTP-27.1.3/lib/kernel/src/inet_dns.erl
===================================================================
--- otp-OTP-27.1.3.orig/lib/kernel/src/inet_dns.erl
+++ otp-OTP-27.1.3/lib/kernel/src/inet_dns.erl
@@ -29,6 +29,7 @@
 %% RFC 2181: Clarifications to the DNS Specification
 %% RFC 2782: A DNS RR for specifying the location of services (DNS SRV)
 %% RFC 2915: The Naming Authority Pointer (NAPTR) DNS Resource Rec
+%% RFC 5452: Measures for Making DNS More Resilient against Forged Answers
 %% RFC 5936: DNS Zone Transfer Protocol (AXFR)
 %% RFC 6488: DNS Certification Authority Authorization (CAA) Resource Record
 %% RFC 6762: Multicast DNS
Index: otp-OTP-27.1.3/lib/kernel/src/inet_res.erl
===================================================================
--- otp-OTP-27.1.3.orig/lib/kernel/src/inet_res.erl
+++ otp-OTP-27.1.3/lib/kernel/src/inet_res.erl
@@ -1045,6 +1045,11 @@ udp_connect(#sock{inet=I}, {A,B,C,D}=IP,
   when ?ip(A,B,C,D), ?port(Port), is_boolean(Verbose) ->
     udp_connect_socket(I, IP, Port, Verbose).
 
+%% Port number -1 is an internal feature that randomizes
+%% the bind port more than an ephemeral port, but it is not allowed
+%% by the type spec for gen_udp:open/2, so we have to suppress
+%% the Dialyzer warnings that that causes here...
+-dialyzer({[no_return, no_fail_call], udp_connect_fam/4}).
 udp_connect_fam(Fam, IP, Port, Verbose) ->
     case gen_udp:open(-1, [{active,false},binary,Fam]) of
         {ok, Socket} = OK ->
@@ -1284,7 +1289,6 @@ query_nss_result(Q, NSs, Timer, Retry, I
             %% it will not answer differently on the next retry.
 	    query_nss_retry(Q, NSs, Timer, Retry, I, S, NewReason, RetryNSs);
  	{error,E=NewReason}
-	{error,E=NewReason}
           when E =:= formerr;
                E =:= enetunreach;
                E =:= econnrefused ->
@@ -1368,7 +1372,12 @@ query_ns(S0, {Msg, Buffer}, IP, Port, Ti
 	    end
     end.
 
-
+%% See udp_connect_fam/4 above.  The use of port number -1 causes
+%% a secondary Dialyzer warning here since Dialyzer concludes
+%% that S cannot be 'undefined' because that would cause udp_connect/4
+%% to crash here, so `S =:= undefined` can never be `true`.
+%% This is a suppression for that.
+-dialyzer({no_match, query_udp/7}).
 query_udp(_S, _Msg0, _Buffer, IP, Port, 0, Verbose) ->
     ?verbose(Verbose, "No try UDP server : ~p:~p (overdue)\n",
 	     [IP,Port]),
Index: otp-OTP-27.1.3/lib/kernel/src/inet_sctp.erl
===================================================================
--- otp-OTP-27.1.3.orig/lib/kernel/src/inet_sctp.erl
+++ otp-OTP-27.1.3/lib/kernel/src/inet_sctp.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2007-2024. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2026. 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.
@@ -55,10 +55,12 @@ open(Opts) ->
                        ifaddr = Addr,
                        port   = Port,
                        type   = Type,
-                       opts   = SOs}} ->
+                       opts   = SOs}}
+          when ?port(Port) ->
 	    inet:open_bind(
               Fd, Addr, Port, SOs, ?PROTO, ?FAMILY, Type, ?MODULE);
-	Error -> Error
+        {ok, _} -> {error, badarg};
+	Error   -> Error
     end.
 
 close(S) ->
