From 69950390b2d8063b6540dc75941a30ea56d11918 Mon Sep 17 00:00:00 2001
From: Ben Noordhuis <info@bnoordhuis.nl>
Date: Thu, 25 May 2023 21:31:33 +0200
Subject: [PATCH] rand: add support for getrandom() (#526)

glibc provides arc4random_buf() but musl does not and /dev/urandom is
not always available.

Conflict: include/ares_build.h.cmake -> ares_build.h.cmake
src/lib/ares_config.h.cmake -> ares_config.h.cmake
src/lib/ares_rand.c -> ares_rand.c
src/lib/setup_once.h -> setup_once.h
context adapt
Reference: https://github.com/c-ares/c-ares/commit/69950390b2d8063b6540dc75941a30ea56d11918
---
 CMakeLists.txt        |   6 +++
 ares_build.h.cmake    |   5 +++
 ares_config.h.cmake   |   3 ++
 ares_rand.c           |  17 ++++++-
 configure.ac          |   1 +
 m4/cares-functions.m4 | 102 ++++++++++++++++++++++++++++++++++++++++++
 setup_once.h          |   4 ++
 7 files changed, 137 insertions(+), 1 deletion(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index f2ceae8..ddab7ae 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -153,6 +153,7 @@ CARES_FUNCTION_IN_LIBRARY (clock_gettime rt HAVE_LIBRT)
 
 # Look for necessary includes
 CHECK_INCLUDE_FILES (sys/types.h           HAVE_SYS_TYPES_H)
+CHECK_INCLUDE_FILES (sys/random.h          HAVE_SYS_RANDOM_H)
 CHECK_INCLUDE_FILES (sys/socket.h          HAVE_SYS_SOCKET_H)
 CHECK_INCLUDE_FILES (arpa/inet.h           HAVE_ARPA_INET_H)
 CHECK_INCLUDE_FILES (arpa/nameser_compat.h HAVE_ARPA_NAMESER_COMPAT_H)
@@ -259,6 +260,7 @@ CARES_EXTRAINCLUDE_IFSET (HAVE_STDLIB_H       stdlib.h)
 CARES_EXTRAINCLUDE_IFSET (HAVE_STRING_H       string.h)
 CARES_EXTRAINCLUDE_IFSET (HAVE_STRINGS_H      strings.h)
 CARES_EXTRAINCLUDE_IFSET (HAVE_SYS_IOCTL_H    sys/ioctl.h)
+CARES_EXTRAINCLUDE_IFSET (HAVE_SYS_RANDOM_H   sys/random.h)
 CARES_EXTRAINCLUDE_IFSET (HAVE_SYS_SELECT_H   sys/select.h)
 CARES_EXTRAINCLUDE_IFSET (HAVE_SYS_SOCKET_H   sys/socket.h)
 CARES_EXTRAINCLUDE_IFSET (HAVE_SYS_TIME_H     sys/time.h)
@@ -342,6 +344,7 @@ CHECK_SYMBOL_EXISTS (gethostbyaddr   "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_GETHOST
 CHECK_SYMBOL_EXISTS (gethostbyname   "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_GETHOSTBYNAME)
 CHECK_SYMBOL_EXISTS (gethostname     "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_GETHOSTNAME)
 CHECK_SYMBOL_EXISTS (getnameinfo     "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_GETNAMEINFO)
+CHECK_SYMBOL_EXISTS (getrandom       "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_GETRANDOM)
 CHECK_SYMBOL_EXISTS (getservbyport_r "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_GETSERVBYPORT_R)
 CHECK_SYMBOL_EXISTS (getservbyname_r "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_GETSERVBYNAME_R)
 CHECK_SYMBOL_EXISTS (gettimeofday    "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_GETTIMEOFDAY)
@@ -537,6 +540,9 @@ ENDIF ()
 IF (HAVE_SYS_TYPES_H)
 	SET (CARES_HAVE_SYS_TYPES_H 1)
 ENDIF ()
+IF (HAVE_SYS_RANDOM_H)
+	SET (CARES_HAVE_SYS_RANDOM_H 1)
+ENDIF()
 IF (HAVE_SYS_SOCKET_H)
 	SET (CARES_HAVE_SYS_SOCKET_H 1)
 ENDIF()
diff --git a/ares_build.h.cmake b/ares_build.h.cmake
index ac32d47..862bfe4 100644
--- a/ares_build.h.cmake
+++ b/ares_build.h.cmake
@@ -8,6 +8,7 @@
  * files.  We need to include some dependent headers that may be system specific
  * for C-Ares */
 #cmakedefine CARES_HAVE_SYS_TYPES_H
+#cmakedefine CARES_HAVE_SYS_RANDOM_H
 #cmakedefine CARES_HAVE_SYS_SOCKET_H
 #cmakedefine CARES_HAVE_WINDOWS_H
 #cmakedefine CARES_HAVE_WS2TCPIP_H
@@ -18,6 +19,10 @@
 #  include <sys/types.h>
 #endif
 
+#ifdef CARES_HAVE_SYS_RANDOM_H
+#  include <sys/random.h>
+#endif
+
 #ifdef CARES_HAVE_SYS_SOCKET_H
 #  include <sys/socket.h>
 #endif
diff --git a/ares_config.h.cmake b/ares_config.h.cmake
index 441d065..669572f 100644
--- a/ares_config.h.cmake
+++ b/ares_config.h.cmake
@@ -123,6 +123,9 @@
 /* Define to 1 if you have the getnameinfo function. */
 #cmakedefine HAVE_GETNAMEINFO
 
+/* Define to 1 if you have the getrandom function. */
+#cmakedefine HAVE_GETRANDOM
+
 /* Define to 1 if you have the getservbyport_r function. */
 #cmakedefine HAVE_GETSERVBYPORT_R
 
diff --git a/ares_rand.c b/ares_rand.c
index a564bc2..dbdfef7 100644
--- a/ares_rand.c
+++ b/ares_rand.c
@@ -149,7 +149,7 @@ static int ares__init_rand_engine(ares_rand_state *state)
 {
   memset(state, 0, sizeof(*state));
 
-#if defined(HAVE_ARC4RANDOM_BUF) || defined(_WIN32)
+#if defined(HAVE_ARC4RANDOM_BUF) || defined(HAVE_GETRANDOM) || defined(_WIN32)
   state->type = ARES_RAND_OS;
   return 1;
 #elif defined(CARES_RANDOM_FILE)
@@ -236,6 +236,21 @@ static void ares__rand_bytes(ares_rand_state *state, unsigned char *buf, size_t
 #elif defined(HAVE_ARC4RANDOM_BUF)
         arc4random_buf(buf, len);
         return;
+#elif defined(HAVE_GETRANDOM)
+        while (1) {
+          size_t n = len - bytes_read;
+          /* getrandom() on Linux always succeeds and is never
+           * interrupted by a signal when requesting <= 256 bytes.
+           */
+          ssize_t rv = getrandom(buf + bytes_read, n > 256 ? 256 : n, 0);
+          if (rv <= 0)
+            continue; /* Just retry. */
+
+          bytes_read += rv;
+          if (bytes_read == len)
+            return;
+        }
+        break;
 #else
         /* Shouldn't be possible to be here */
         break;
diff --git a/configure.ac b/configure.ac
index e1aaa4e..201abdf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -589,6 +589,7 @@ CARES_CHECK_FUNC_GETENV
 CARES_CHECK_FUNC_GETHOSTBYADDR
 CARES_CHECK_FUNC_GETHOSTBYNAME
 CARES_CHECK_FUNC_GETHOSTNAME
+CARES_CHECK_FUNC_GETRANDOM
 CARES_CHECK_FUNC_GETSERVBYPORT_R
 CARES_CHECK_FUNC_INET_NET_PTON
 CARES_CHECK_FUNC_INET_NTOP
diff --git a/m4/cares-functions.m4 b/m4/cares-functions.m4
index d4f4f99..ce8d2f9 100644
--- a/m4/cares-functions.m4
+++ b/m4/cares-functions.m4
@@ -186,6 +186,24 @@ cares_includes_stropts="\
 ])
 
 
+dnl CARES_INCLUDES_SYS_RANDOM
+dnl -------------------------------------------------
+dnl Set up variable with list of headers that must be
+dnl included when sys/random.h is to be included.
+
+AC_DEFUN([CARES_INCLUDES_SYS_RANDOM], [
+cares_includes_sys_random="\
+/* includes start */
+#ifdef HAVE_SYS_RANDOM_H
+#  include <sys/random.h>
+#endif
+/* includes end */"
+  AC_CHECK_HEADERS(
+    sys/random.h,
+    [], [], [$cares_includes_sys_random])
+])
+
+
 dnl CARES_INCLUDES_SYS_SOCKET
 dnl -------------------------------------------------
 dnl Set up variable with list of headers that must be
@@ -1520,6 +1538,90 @@ AC_DEFUN([CARES_CHECK_FUNC_GETHOSTNAME], [
   fi
 ])
 
+dnl CARES_CHECK_FUNC_GETRANDOM
+dnl -------------------------------------------------
+dnl Verify if getrandom is available, prototyped, and
+dnl can be compiled. If all of these are true, and
+dnl usage has not been previously disallowed with
+dnl shell variable cares_disallow_getrandom, then
+dnl HAVE_GETRANDOM will be defined.
+
+AC_DEFUN([CARES_CHECK_FUNC_GETRANDOM], [
+  AC_REQUIRE([CARES_INCLUDES_SYS_RANDOM])dnl
+  #
+  tst_links_getrandom="unknown"
+  tst_proto_getrandom="unknown"
+  tst_compi_getrandom="unknown"
+  tst_allow_getrandom="unknown"
+  #
+  AC_MSG_CHECKING([if getrandom can be linked])
+  AC_LINK_IFELSE([
+    AC_LANG_FUNC_LINK_TRY([getrandom])
+  ],[
+    AC_MSG_RESULT([yes])
+    tst_links_getrandom="yes"
+  ],[
+    AC_MSG_RESULT([no])
+    tst_links_getrandom="no"
+  ])
+  #
+  if test "$tst_links_getrandom" = "yes"; then
+    AC_MSG_CHECKING([if getrandom is prototyped])
+    AC_EGREP_CPP([getrandom],[
+      $cares_includes_sys_random
+    ],[
+      AC_MSG_RESULT([yes])
+      tst_proto_getrandom="yes"
+    ],[
+      AC_MSG_RESULT([no])
+      tst_proto_getrandom="no"
+    ])
+  fi
+  #
+  if test "$tst_proto_getrandom" = "yes"; then
+    AC_MSG_CHECKING([if getrandom is compilable])
+    AC_COMPILE_IFELSE([
+      AC_LANG_PROGRAM([[
+        $cares_includes_sys_random
+      ]],[[
+        if(0 != getrandom(0, 0, 0))
+          return 1;
+      ]])
+    ],[
+      AC_MSG_RESULT([yes])
+      tst_compi_getrandom="yes"
+    ],[
+      AC_MSG_RESULT([no])
+      tst_compi_getrandom="no"
+    ])
+  fi
+  #
+  if test "$tst_compi_getrandom" = "yes"; then
+    AC_MSG_CHECKING([if getrandom usage allowed])
+    if test "x$cares_disallow_getrandom" != "xyes"; then
+      AC_MSG_RESULT([yes])
+      tst_allow_getrandom="yes"
+    else
+      AC_MSG_RESULT([no])
+      tst_allow_getrandom="no"
+    fi
+  fi
+  #
+  AC_MSG_CHECKING([if getrandom might be used])
+  if test "$tst_links_getrandom" = "yes" &&
+     test "$tst_proto_getrandom" = "yes" &&
+     test "$tst_compi_getrandom" = "yes" &&
+     test "$tst_allow_getrandom" = "yes"; then
+    AC_MSG_RESULT([yes])
+    AC_DEFINE_UNQUOTED(HAVE_GETRANDOM, 1,
+      [Define to 1 if you have the getrandom function.])
+    ac_cv_func_getrandom="yes"
+  else
+    AC_MSG_RESULT([no])
+    ac_cv_func_getrandom="no"
+  fi
+])
+
 
 dnl CARES_CHECK_FUNC_GETSERVBYPORT_R
 dnl -------------------------------------------------
diff --git a/setup_once.h b/setup_once.h
index a8cfe6b..4b0f9ce 100644
--- a/setup_once.h
+++ b/setup_once.h
@@ -91,6 +91,10 @@
 #  endif
 #endif
 
+#ifdef HAVE_SYS_RANDOM_H
+#include <sys/random.h>
+#endif
+
 #ifdef HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
 #endif
-- 
2.27.0

