From 89b3617aeedf752270e91790d6284832f8cd36c4 Mon Sep 17 00:00:00 2001
From: Dmitry Karpov <dkarpov@roku.com>
Date: Fri, 16 Sep 2022 13:45:36 -0700
Subject: [PATCH] Implemented curl_global_init_ipv6() global function allowing
 to specify a callback telling libcurl if IPv6 works on the system.

---
 include/curl/curl.h | 29 +++++++++++++++++++++++++++++
 lib/easy.c          | 39 +++++++++++++++++++++++++++++++++++++++
 lib/hostip.c        | 39 ++++++++++++++++++++++++++++-----------
 lib/hostip.h        |  5 +++++
 4 files changed, 101 insertions(+), 11 deletions(-)

diff --git a/include/curl/curl.h b/include/curl/curl.h
index da2bf3e42..8b655759f 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -2637,6 +2637,35 @@ CURL_EXTERN CURLcode curl_global_init_mem(long flags,
                                           curl_strdup_callback s,
                                           curl_calloc_callback c);
 
+/*
+ * callback function for checking if IPv6 is enabled on the system or
+ * for the application.
+ *
+ */
+typedef enum {
+  CURLIPV6_NO = 0, /* ipv6 is disabled and shouldn't be used. */
+  CURLIPV6_YES,    /* ipv6 is enabled and can be used. */
+  CURLIPV6_AUTO    /* use default libcurl mechanism to detect if IPv6 works */
+} CURLipv6;
+
+typedef CURLipv6(*curl_ipv6works_callback)();
+
+/*
+ * NAME curl_global_init_ipv6()
+ *
+ * DESCRIPTION
+ *
+ * curl_global_init() or curl_global_init_ipv6() should be invoked exactly once
+ * for each application that uses libcurl.  This function can be used to
+ * initialize libcurl and set user defined callback checking if IPv6 works
+ * on the system.
+ * Users can implement IPv6 checking routines to tell libcurl if it can use
+ * IPv6 in its transfers.
+ */
+
+CURL_EXTERN CURLcode curl_global_init_ipv6(long flags,
+  curl_ipv6works_callback ipv6_works_cb);
+
 /*
  * NAME curl_global_cleanup()
  *
diff --git a/lib/easy.c b/lib/easy.c
index 704a59df6..4d003a891 100644
--- a/lib/easy.c
+++ b/lib/easy.c
@@ -274,6 +274,45 @@ CURLcode curl_global_init_mem(long flags, curl_malloc_callback m,
   return result;
 }
 
+
+CURLcode curl_global_init_ipv6(long flags,
+                               curl_ipv6works_callback ipv6_works_cb)
+{
+#ifdef ENABLE_IPV6
+  CURLcode result;
+
+  /* Invalid input, return immediately */
+  if (!ipv6_works_cb)
+    return CURLE_FAILED_INIT;
+
+  global_init_lock();
+
+  if (initialized) {
+    /* Already initialized, don't do it again, but bump the variable anyway to
+       work like curl_global_init() and require the same amount of cleanup
+       calls. */
+    initialized++;
+    global_init_unlock();
+    return CURLE_OK;
+  }
+
+  /* Call the actual init function */
+  result = global_init(flags, TRUE);
+
+  /* set global IPv6 works check function */
+  Curl_ipv6_works = ipv6_works_cb;
+
+  global_init_unlock();
+
+  return result;
+#else
+  (void)ipv6_works_cb;
+  // IPv6 not enabled in libcurl.
+  return curl_global_init(flags);
+#endif
+}
+
+
 /**
  * curl_global_cleanup() globally cleanups curl, uses the value of
  * "init_flags" to determine what needs to be cleaned up and what doesn't.
diff --git a/lib/hostip.c b/lib/hostip.c
index 04f917675..6180bb8f5 100644
--- a/lib/hostip.c
+++ b/lib/hostip.c
@@ -558,6 +558,27 @@ static struct Curl_addrinfo *get_localhost(int port)
 }
 
 #ifdef ENABLE_IPV6
+bool curl_ipv6_check()
+{
+  /* probe to see if we have a working IPv6 stack */
+  curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0);
+  if (s == CURL_SOCKET_BAD) {
+    /* an IPv6 address was requested but we can't get/use one */
+    return FALSE;
+  }
+
+  sclose(s);
+  return TRUE;
+}
+
+CURLipv6 curl_ipv6works()
+{
+  return CURLIPV6_AUTO;
+}
+
+curl_ipv6works_callback Curl_ipv6_works =
+  (curl_ipv6works_callback)curl_ipv6works;
+
 /*
  * Curl_ipv6works() returns TRUE if IPv6 seems to work.
  */
@@ -572,17 +593,13 @@ bool Curl_ipv6works(struct Curl_easy *data)
     return data->multi->ipv6_works;
   }
   else {
-    int ipv6_works = -1;
-    /* probe to see if we have a working IPv6 stack */
-    curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0);
-    if(s == CURL_SOCKET_BAD)
-      /* an IPv6 address was requested but we can't get/use one */
-      ipv6_works = 0;
-    else {
-      ipv6_works = 1;
-      sclose(s);
-    }
-    return (ipv6_works>0)?TRUE:FALSE;
+    DEBUGASSERT(Curl_ipv6_works);
+    CURLipv6 curl_ipv6 = Curl_ipv6_works();
+
+    if (curl_ipv6 == CURLIPV6_AUTO)
+      return curl_ipv6_check();
+
+    return (curl_ipv6 == CURLIPV6_YES)?TRUE:FALSE;
   }
 }
 #endif /* ENABLE_IPV6 */
diff --git a/lib/hostip.h b/lib/hostip.h
index 4b603378b..ee009ee52 100644
--- a/lib/hostip.h
+++ b/lib/hostip.h
@@ -100,6 +100,11 @@ enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
                                    timediff_t timeoutms);
 
 #ifdef ENABLE_IPV6
+/*
+* Global callback probing if IPv6 works on the system.
+*/
+extern curl_ipv6works_callback Curl_ipv6_works;
+
 /*
  * Curl_ipv6works() returns TRUE if IPv6 seems to work.
  */
-- 
2.37.1.windows.1

