]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
asyn-thread: use c-ares to resolve HTTPS RR
authorDaniel Stenberg <daniel@haxx.se>
Tue, 21 Jan 2025 10:42:20 +0000 (11:42 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Sat, 25 Jan 2025 22:46:14 +0000 (23:46 +0100)
Allow building with c-ares and yet use threaded resolver for the main
host A/AAAA resolving:

  `--with-ares` provides the c-ares install path and defaults to use
  c-ares for name resolving

  `--with-threaded-resolver` still uses c-ares in the build (for HTTPS)
  but uses the threaded resolver for "normal" resolves.

It works similarly for cmake: ENABLE_ARES enables ares, and if
ENABLE_THREADED_RESOLVER also is set, c-ares is used for HTTPS RR and
the threaded resolver for "normal" resolves.

HTTPSRR and c-ares-rr are new features return by curl_version_info() and
thus shown by curl -V.

The c-ares-rr feature bit is there to make it possible to distinguish
between builds using c-ares for all name resolves and builds that use
the threaded resolves for the regular name resolves and c-ares for
HTTPSRR only. "c-ares-rr" means it does not use c-ares for "plain" name
resolves.

HTTPSRR support is EXPERIMENTAL only.

Closes #16054

18 files changed:
.github/scripts/spellcheck.words
CMakeLists.txt
configure.ac
docs/libcurl/curl_version_info.md
lib/asyn-ares.c
lib/asyn-thread.c
lib/asyn.h
lib/curl_setup.h
lib/hostip.h
lib/httpsrr.c
lib/httpsrr.h
lib/version.c
m4/curl-confopts.m4
tests/FILEFORMAT.md
tests/data/test1014
tests/libtest/lib1565.c
tests/runtests.pl
tests/server/disabled.c

index 85714c771d7ed15a6c5fa60471460cdd06c38627..196171551658b0322d7c28ff6f165abcc30019dd 100644 (file)
@@ -346,6 +346,7 @@ httpget
 HttpGet
 HTTPS
 https
+HTTPSRR
 hyper's
 Högskolan
 IANA
@@ -779,10 +780,10 @@ src
 SRP
 SRWLOCK
 SSL
-SSLS
 ssl
 SSLeay
 SSLKEYLOGFILE
+SSLS
 sslv
 SSLv
 SSLVERSION
index f66b7e1fcbe01b8023867776c5aba24a665f538f..1987cb1ac89f955dd842585230a50ec1d525c0bd 100644 (file)
@@ -243,9 +243,15 @@ elseif(DOS OR AMIGA)
 endif()
 option(CURL_LTO "Enable compiler Link Time Optimizations" OFF)
 
-cmake_dependent_option(ENABLE_THREADED_RESOLVER "Enable threaded DNS lookup"
-  ON "NOT ENABLE_ARES;NOT DOS;NOT AMIGA"
-  OFF)
+if(NOT DOS AND NOT AMIGA)
+  # if c-ares is used, default the threaded resolver to OFF
+  if(ENABLE_ARES)
+    set(_enable_threaded_resolver_default OFF)
+  else()
+    set(_enable_threaded_resolver_default ON)
+  endif()
+  option(ENABLE_THREADED_RESOLVER "Enable threaded DNS lookup" ${_enable_threaded_resolver_default})
+endif()
 
 include(PickyWarnings)
 
@@ -1003,7 +1009,10 @@ if(USE_ECH)
     if(NOT HAVE_ECH)
       message(FATAL_ERROR "ECH support missing in OpenSSL/BoringSSL/AWS-LC/wolfSSL")
     else()
-      message(STATUS "ECH enabled.")
+      message(STATUS "ECH enabled")
+      # ECH wants HTTPSRR
+      set(USE_HTTPSRR ON)
+      message(STATUS "HTTPSRR enabled")
     endif()
   else()
     message(FATAL_ERROR "ECH requires ECH-enablded OpenSSL, BoringSSL, AWS-LC or wolfSSL")
@@ -2098,6 +2107,7 @@ curl_add_if("brotli"        HAVE_BROTLI)
 curl_add_if("gsasl"         USE_GSASL)
 curl_add_if("zstd"          HAVE_ZSTD)
 curl_add_if("AsynchDNS"     USE_ARES OR USE_THREADS_POSIX OR USE_THREADS_WIN32)
+curl_add_if("c-ares-rr"     USE_ARES AND ENABLE_THREADED_RESOLVER)
 curl_add_if("IDN"           (HAVE_LIBIDN2 AND HAVE_IDN2_H) OR
                             USE_WIN32_IDN OR
                             USE_APPLE_IDN)
@@ -2128,6 +2138,7 @@ curl_add_if("threadsafe"    HAVE_ATOMIC OR
 curl_add_if("Debug"         ENABLE_DEBUG)
 curl_add_if("TrackMemory"   ENABLE_CURLDEBUG)
 curl_add_if("ECH"           _ssl_enabled AND HAVE_ECH)
+curl_add_if("HTTPSRR"       _ssl_enabled AND USE_HTTPSRR)
 curl_add_if("PSL"           USE_LIBPSL)
 curl_add_if("CAcert"        CURL_CA_EMBED_SET)
 curl_add_if("SSLS-EXPORT"   _ssl_enabled AND USE_SSLS_EXPORT)
index 3c1e64d534b2e630ef680e7b3e4f6203050d9612..a67dc76064de71ddd6918cc31f32f641b9584938 100644 (file)
@@ -4134,74 +4134,26 @@ dnl set variable for use in automakefile(s)
 AM_CONDITIONAL(USE_MANUAL, test x"$USE_MANUAL" = x1)
 
 CURL_CHECK_LIB_ARES
+CURL_CHECK_OPTION_THREADED_RESOLVER
 
-if test "x$want_ares" != xyes; then
-  CURL_CHECK_OPTION_THREADED_RESOLVER
-
-  if test "$ipv6" = yes; then
-    CURL_DARWIN_SYSTEMCONFIGURATION
-  fi
+if test "$ipv6" = yes; then
+  CURL_DARWIN_SYSTEMCONFIGURATION
 fi
 
-dnl ************************************************************
-dnl disable POSIX threads
-dnl
-AC_MSG_CHECKING([whether to use POSIX threads for threaded resolver])
-AC_ARG_ENABLE(pthreads,
-AS_HELP_STRING([--enable-pthreads],
-  [Enable POSIX threads (default for threaded resolver)])
-AS_HELP_STRING([--disable-pthreads],[Disable POSIX threads]),
-[ case "$enableval" in
-  no)
-    AC_MSG_RESULT(no)
-    want_pthreads=no
-    ;;
-  *)
-    AC_MSG_RESULT(yes)
-    want_pthreads=yes
-    ;;
-  esac ], [
-    default_pthreads=1
-    if test "$curl_cv_native_windows" = "yes"; then
-      default_pthreads=0
-    else
-      case $host_os in
-        msdos*)
-          default_pthreads=0
-          ;;
-      esac
-    fi
-    if test "$default_pthreads" = '0'; then
-      AC_MSG_RESULT(no)
-      want_pthreads=no
-    else
-      AC_MSG_RESULT(auto)
-      want_pthreads=auto
-    fi
-  ]
-)
-
 dnl turn off pthreads if rt is disabled
-if test "$want_pthreads" != "no"; then
-  if test "$want_pthreads" = "yes" && test "$dontwant_rt" = "yes"; then
-    AC_MSG_ERROR([options --enable-pthreads and --disable-rt are mutually exclusive])
-  fi
-  if test "$dontwant_rt" != "no"; then
-    dnl if --enable-pthreads was explicit then warn it's being ignored
-    if test "$want_pthreads" = "yes"; then
-      AC_MSG_WARN([--enable-pthreads Ignored since librt is disabled.])
-    fi
-    want_pthreads=no
-  fi
+if test "$want_threaded_resolver" = "yes" && test "$dontwant_rt" = "yes"; then
+  AC_MSG_ERROR([options --enable-pthreads and --disable-rt are mutually exclusive])
 fi
 
-dnl turn off pthreads if no threaded resolver
-if test "$want_pthreads" != "no" && test "$want_thres" != "yes"; then
-  want_pthreads=no
+dnl Windows threaded resolver check
+if test "$want_threaded_resolver" = "yes" && test "$curl_cv_native_windows" = "yes"; then
+  USE_THREADS_WIN32=1
+  AC_DEFINE(USE_THREADS_WIN32, 1, [if you want Win32 threaded DNS lookup])
+  curl_res_msg="Win32 threaded"
 fi
 
 dnl detect pthreads
-if test "$want_pthreads" != "no"; then
+if test "$want_threaded_resolver" = "yes" && test "$USE_THREADS_WIN32" != "1"; then
   AC_CHECK_HEADER(pthread.h,
     [ AC_DEFINE(HAVE_PTHREAD_H, 1, [if you have <pthread.h>])
       save_CFLAGS="$CFLAGS"
@@ -4262,19 +4214,9 @@ if test "$want_pthreads" != "no"; then
   ])
 fi
 
-dnl threaded resolver check
-if test "$want_thres" = "yes" && test "x$USE_THREADS_POSIX" != "x1"; then
-  if test "$want_pthreads" = "yes"; then
-    AC_MSG_ERROR([--enable-pthreads but pthreads was not found])
-  fi
-  dnl If native Windows fallback on Win32 threads since no POSIX threads
-  if test "$curl_cv_native_windows" = "yes"; then
-    USE_THREADS_WIN32=1
-    AC_DEFINE(USE_THREADS_WIN32, 1, [if you want Win32 threaded DNS lookup])
-    curl_res_msg="Win32 threaded"
-  else
-    AC_MSG_ERROR([Threaded resolver enabled but no thread library found])
-  fi
+dnl Did we find a threading option?
+if test "$want_threaded_resolver" != "no" -a "x$USE_THREADS_POSIX" != "x1" -a "x$USE_THREADS_WIN32" != "x1"; then
+  AC_MSG_ERROR([Threaded resolver enabled but no thread library found])
 fi
 
 AC_CHECK_HEADER(dirent.h,
@@ -4873,15 +4815,6 @@ if test "x$hsts" != "xyes"; then
 fi
 
 
-dnl *************************************************************
-dnl check whether HTTPSRR support if desired
-dnl
-if test "x$want_httpsrr" != "xno"; then
-  AC_MSG_RESULT([HTTPSRR support is available])
-  AC_DEFINE(USE_HTTPSRR, 1, [enable HTTPS RR support])
-  experimental="$experimental HTTPSRR"
-fi
-
 dnl *************************************************************
 dnl check whether ECH support, if desired, is actually available
 dnl
@@ -4909,11 +4842,24 @@ if test "x$want_ech" != "xno"; then
     AC_DEFINE(USE_ECH, 1, [if ECH support is available])
     AC_MSG_RESULT($ECH_SUPPORT)
     experimental="$experimental ECH"
+    dnl ECH wants HTTPSRR
+    want_httpsrr="yes"
   else
     AC_MSG_ERROR([--enable-ech ignored: No ECH support found])
   fi
 fi
 
+dnl *************************************************************
+dnl check whether HTTPSRR support if desired
+dnl
+if test "x$want_httpsrr" != "xno"; then
+  AC_MSG_RESULT([HTTPSRR support is enabled])
+  AC_DEFINE(USE_HTTPSRR, 1, [enable HTTPS RR support])
+  experimental="$experimental HTTPSRR"
+  curl_httpsrr_msg="enabled (--disable-httpsrr)"
+fi
+
+
 dnl *************************************************************
 dnl check whether OpenSSL (lookalikes) have SSL_set0_wbio
 dnl
@@ -5071,6 +5017,9 @@ if test "x$USE_ARES" = "x1" -o "x$USE_THREADS_POSIX" = "x1" \
                             -o "x$USE_THREADS_WIN32" = "x1"; then
   SUPPORT_FEATURES="$SUPPORT_FEATURES AsynchDNS"
 fi
+if test "x$USE_ARES" = "x1" -a "$want_threaded_resolver" = "yes"; then
+  SUPPORT_FEATURES="$SUPPORT_FEATURES c-ares-rr"
+fi
 if test "x$IDN_ENABLED" = "x1"; then
   SUPPORT_FEATURES="$SUPPORT_FEATURES IDN"
 fi
@@ -5177,6 +5126,10 @@ if test "x$OPENSSL_ENABLED" = "x1" -o -n "$SSL_ENABLED"; then
   fi
 fi
 
+if test "x$want_httpsrr" != "xno"; then
+  SUPPORT_FEATURES="$SUPPORT_FEATURES HTTPSRR"
+fi
+
 if test "x$SSLS_EXPORT_ENABLED" = "x1"; then
   SUPPORT_FEATURES="$SUPPORT_FEATURES SSLS-EXPORT"
 fi
@@ -5446,6 +5399,7 @@ AC_MSG_NOTICE([Configured to build curl/libcurl:
   HTTP2:            ${curl_h2_msg}
   HTTP3:            ${curl_h3_msg}
   ECH:              ${curl_ech_msg}
+  HTTPS RR:         ${curl_httpsrr_msg}
   SSLS-EXPORT:      ${curl_ssls_export_msg}
   Protocols:        ${SUPPORT_PROTOCOLS_LOWER}
   Features:         ${SUPPORT_FEATURES}
index a112058a6d1f1b83a7aaaa24b370be50fdc1e4b0..ed22a54df955513e9397d0ebad6c9f785028be15 100644 (file)
@@ -151,13 +151,13 @@ entry.
 
 # FEATURES
 
-## alt-svc
+## `alt-svc`
 
 *features* mask bit: CURL_VERSION_ALTSVC
 
 HTTP Alt-Svc parsing and the associated options (Added in 7.64.1)
 
-## AsynchDNS
+## `AsynchDNS`
 
 *features* mask bit: CURL_VERSION_ASYNCHDNS
 
@@ -165,32 +165,40 @@ libcurl was built with support for asynchronous name lookups, which allows
 more exact timeouts (even on Windows) and less blocking when using the multi
 interface. (added in 7.10.7)
 
-## brotli
+## `brotli`
 
 *features* mask bit: CURL_VERSION_BROTLI
 
 supports HTTP Brotli content encoding using libbrotlidec (Added in 7.57.0)
 
-## Debug
+## `c-ares-rr`
+
+*features* mask bit: non-existent
+
+libcurl was built to use c-ares for EXPERIMENTAL HTTPS resource record
+resolves, but uses the threaded resolver for "normal" resolves (Added in
+8.12.0)
+
+## `Debug`
 
 *features* mask bit: CURL_VERSION_DEBUG
 
 libcurl was built with debug capabilities (added in 7.10.6)
 
-## ECH
+## `ECH`
 
 *features* mask bit: non-existent
 
 libcurl was built with ECH support (experimental, added in 8.8.0)
 
-## gsasl
+## `gsasl`
 
 *features* mask bit: CURL_VERSION_GSASL
 
 libcurl was built with libgsasl and thus with some extra SCRAM-SHA
 authentication methods. (added in 7.76.0)
 
-## GSS-API
+## `GSS-API`
 
 *features* mask bit: CURL_VERSION_GSSAPI
 
@@ -199,66 +207,73 @@ functions for Kerberos and SPNEGO authentication. It also allows libcurl
 to use the current user credentials without the app having to pass them on.
 (Added in 7.38.0)
 
-## HSTS
+## `HSTS`
 
 *features* mask bit: CURL_VERSION_HSTS
 
 libcurl was built with support for HSTS (HTTP Strict Transport Security)
 (Added in 7.74.0)
 
-## HTTP2
+## `HTTP2`
 
 *features* mask bit: CURL_VERSION_HTTP2
 
 libcurl was built with support for HTTP2.
 (Added in 7.33.0)
 
-## HTTP3
+## `HTTP3`
 
 *features* mask bit: CURL_VERSION_HTTP3
 
 HTTP/3 and QUIC support are built-in (Added in 7.66.0)
 
-## HTTPS-proxy
+## `HTTPS-proxy`
 
 *features* mask bit: CURL_VERSION_HTTPS_PROXY
 
 libcurl was built with support for HTTPS-proxy.
 (Added in 7.52.0)
 
-## IDN
+## `HTTPSRR`
+
+*features* mask bit: non-existent
+
+libcurl was built with EXPERIMENTAL support for HTTPS resource records (Added
+in 8.12.0)
+
+## `IDN`
 
 *features* mask bit: CURL_VERSION_IDN
 
 libcurl was built with support for IDNA, domain names with international
 letters. (Added in 7.12.0)
 
-## IPv6
+## `IPv6`
 
 *features* mask bit: CURL_VERSION_IPV6
 
 supports IPv6
 
-## Kerberos
+## `Kerberos`
 
 *features* mask bit: CURL_VERSION_KERBEROS5
 
 supports Kerberos V5 authentication for FTP, IMAP, LDAP, POP3, SMTP and
 SOCKSv5 proxy. (Added in 7.40.0)
 
-## Largefile
+## `Largefile`
 
 *features* mask bit: CURL_VERSION_LARGEFILE
 
 libcurl was built with support for large files. (Added in 7.11.1)
 
-## libz
+## `libz`
 
 *features* mask bit: CURL_VERSION_LIBZ
 
 supports HTTP deflate using libz (Added in 7.10)
 
-## MultiSSL
+## `MultiSSL`
 
 *features* mask bit: CURL_VERSION_MULTI_SSL
 
@@ -266,20 +281,20 @@ libcurl was built with multiple SSL backends. For details, see
 curl_global_sslset(3).
 (Added in 7.56.0)
 
-## NTLM
+## `NTLM`
 
 *features* mask bit: CURL_VERSION_NTLM
 
 supports HTTP NTLM (added in 7.10.6)
 
-## NTLM_WB
+## `NTLM_WB`
 
 *features* mask bit: CURL_VERSION_NTLM_WB
 
 libcurl was built with support for NTLM delegation to a winbind helper.
 (Added in 7.22.0) This feature was removed from curl in 8.8.0.
 
-## PSL
+## `PSL`
 
 *features* mask bit: CURL_VERSION_PSL
 
@@ -287,27 +302,27 @@ libcurl was built with support for Mozilla's Public Suffix List. This makes
 libcurl ignore cookies with a domain that is on the list.
 (Added in 7.47.0)
 
-## SPNEGO
+## `SPNEGO`
 
 *features* mask bit: CURL_VERSION_SPNEGO
 
 libcurl was built with support for SPNEGO authentication (Simple and Protected
 GSS-API Negotiation Mechanism, defined in RFC 2478.) (added in 7.10.8)
 
-## SSL
+## `SSL`
 
 *features* mask bit: CURL_VERSION_SSL
 
 supports SSL (HTTPS/FTPS) (Added in 7.10)
 
-## SSLS-EXPORT
+## `SSLS-EXPORT`
 
 *features* mask bit: non-existent
 
 libcurl was built with SSL session import/export support
 (experimental, added in 8.12.0)
 
-## SSPI
+## `SSPI`
 
 *features* mask bit: CURL_VERSION_SSPI
 
@@ -316,42 +331,42 @@ makes libcurl use Windows-provided functions for Kerberos, NTLM, SPNEGO and
 Digest authentication. It also allows libcurl to use the current user
 credentials without the app having to pass them on. (Added in 7.13.2)
 
-## threadsafe
+## `threadsafe`
 
 *features* mask bit: CURL_VERSION_THREADSAFE
 
 libcurl was built with thread-safety support (Atomic or SRWLOCK) to protect
 curl initialization. (Added in 7.84.0) See libcurl-thread(3)
 
-## TLS-SRP
+## `TLS-SRP`
 
 *features* mask bit: CURL_VERSION_TLSAUTH_SRP
 
 libcurl was built with support for TLS-SRP (in one or more of the built-in TLS
 backends). (Added in 7.21.4)
 
-## TrackMemory
+## `TrackMemory`
 
 *features* mask bit: CURL_VERSION_CURLDEBUG
 
 libcurl was built with memory tracking debug capabilities. This is mainly of
 interest for libcurl hackers. (added in 7.19.6)
 
-## Unicode
+## `Unicode`
 
 *features* mask bit: CURL_VERSION_UNICODE
 
 libcurl was built with Unicode support on Windows. This makes non-ASCII
 characters work in filenames and options passed to libcurl. (Added in 7.72.0)
 
-## UnixSockets
+## `UnixSockets`
 
 *features* mask bit: CURL_VERSION_UNIX_SOCKETS
 
 libcurl was built with support for Unix domain sockets.
 (Added in 7.40.0)
 
-## zstd
+## `zstd`
 
 *features* mask bit: CURL_VERSION_ZSTD
 
index 8123226b6e2b917e78e5261827e88c1744c1b6c2..fe2b800f25c906a68c79d5cdf0ed3e4cd0292e19 100644 (file)
 
 #include "curl_setup.h"
 
+#ifdef USE_ARES
+
 /***********************************************************************
  * Only for ares-enabled builds
  * And only for functions that fulfill the asynch resolver backend API
  * as defined in asyn.h, nothing else belongs in this file!
  **********************************************************************/
 
-#ifdef CURLRES_ARES
-
 #include <limits.h>
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
 #include <ares_version.h> /* really old c-ares did not include this by
                              itself */
 
+/*
+ * Curl_ares_getsock() is called when the outside world (using
+ * curl_multi_fdset()) wants to get our fd_set setup and we are talking with
+ * ares. The caller must make sure that this function is only called when we
+ * have a working ares channel.
+ *
+ * Returns: sockets-in-use-bitmap
+ */
+
+int Curl_ares_getsock(struct Curl_easy *data,
+                      ares_channel channel,
+                      curl_socket_t *socks)
+{
+  struct timeval maxtime = { CURL_TIMEOUT_RESOLVE, 0 };
+  struct timeval timebuf;
+  int max = ares_getsock(channel,
+                         (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE);
+  struct timeval *timeout = ares_timeout(channel, &maxtime, &timebuf);
+  timediff_t milli = curlx_tvtoms(timeout);
+  Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
+  return max;
+}
+
+/*
+ * Curl_ares_perform()
+ *
+ * 1) Ask ares what sockets it currently plays with, then
+ * 2) wait for the timeout period to check for action on ares' sockets.
+ * 3) tell ares to act on all the sockets marked as "with action"
+ *
+ * return number of sockets it worked on, or -1 on error
+ */
+
+int Curl_ares_perform(ares_channel channel,
+                      timediff_t timeout_ms)
+{
+  int nfds;
+  int bitmask;
+  ares_socket_t socks[ARES_GETSOCK_MAXNUM];
+  struct pollfd pfd[ARES_GETSOCK_MAXNUM];
+  int i;
+  int num = 0;
+
+  bitmask = ares_getsock(channel, socks, ARES_GETSOCK_MAXNUM);
+
+  for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
+    pfd[i].events = 0;
+    pfd[i].revents = 0;
+    if(ARES_GETSOCK_READABLE(bitmask, i)) {
+      pfd[i].fd = socks[i];
+      pfd[i].events |= POLLRDNORM|POLLIN;
+    }
+    if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
+      pfd[i].fd = socks[i];
+      pfd[i].events |= POLLWRNORM|POLLOUT;
+    }
+    if(pfd[i].events)
+      num++;
+    else
+      break;
+  }
+
+  if(num) {
+    nfds = Curl_poll(pfd, (unsigned int)num, timeout_ms);
+    if(nfds < 0)
+      return -1;
+  }
+  else
+    nfds = 0;
+
+  if(!nfds)
+    /* Call ares_process() unconditionally here, even if we simply timed out
+       above, as otherwise the ares name resolve will not timeout! */
+    ares_process_fd(channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
+  else {
+    /* move through the descriptors and ask for processing on them */
+    for(i = 0; i < num; i++)
+      ares_process_fd(channel,
+                      (pfd[i].revents & (POLLRDNORM|POLLIN)) ?
+                      pfd[i].fd : ARES_SOCKET_BAD,
+                      (pfd[i].revents & (POLLWRNORM|POLLOUT)) ?
+                      pfd[i].fd : ARES_SOCKET_BAD);
+  }
+  return nfds;
+}
+
+#ifdef CURLRES_ARES
+
 #if ARES_VERSION >= 0x010500
 /* c-ares 1.5.0 or later, the callback proto is modified */
 #define HAVE_CARES_CALLBACK_TIMEOUTS 1
 #if ARES_VERSION >= 0x011c00
 /* 1.28.0 and later have ares_query_dnsrec */
 #define HAVE_ARES_QUERY_DNSREC 1
-#else
-#undef USE_HTTPSRR
+#ifdef USE_HTTPSRR
+#define USE_HTTPSRR_ARES 1
+#endif
 #endif
 
 /* The last 3 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 
-struct thread_data {
-  int num_pending; /* number of outstanding c-ares requests */
-  struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares
-                                    parts */
-  int last_status;
-#ifndef HAVE_CARES_GETADDRINFO
-  struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */
-#endif
-#ifdef USE_HTTPSRR
-  struct Curl_https_rrinfo hinfo;
-#endif
-  char hostname[1];
-};
-
 /* How long we are willing to wait for additional parallel responses after
    obtaining a "definitive" one. For old c-ares without getaddrinfo.
 
@@ -292,89 +367,13 @@ static void destroy_async_data(struct Curl_async *async)
 
 /*
  * Curl_resolver_getsock() is called when someone from the outside world
- * (using curl_multi_fdset()) wants to get our fd_set setup and we are talking
- * with ares. The caller must make sure that this function is only called when
- * we have a working ares channel.
- *
- * Returns: sockets-in-use-bitmap
- */
-
-int Curl_resolver_getsock(struct Curl_easy *data,
-                          curl_socket_t *socks)
-{
-  struct timeval maxtime = { CURL_TIMEOUT_RESOLVE, 0 };
-  struct timeval timebuf;
-  int max = ares_getsock((ares_channel)data->state.async.resolver,
-                         (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE);
-  struct timeval *timeout =
-    ares_timeout((ares_channel)data->state.async.resolver, &maxtime, &timebuf);
-  timediff_t milli = curlx_tvtoms(timeout);
-  Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
-  return max;
-}
-
-/*
- * waitperform()
- *
- * 1) Ask ares what sockets it currently plays with, then
- * 2) wait for the timeout period to check for action on ares' sockets.
- * 3) tell ares to act on all the sockets marked as "with action"
- *
- * return number of sockets it worked on, or -1 on error
+ * (using curl_multi_fdset()) wants to get our fd_set setup.
  */
 
-static int waitperform(struct Curl_easy *data, timediff_t timeout_ms)
+int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks)
 {
-  int nfds;
-  int bitmask;
-  ares_socket_t socks[ARES_GETSOCK_MAXNUM];
-  struct pollfd pfd[ARES_GETSOCK_MAXNUM];
-  int i;
-  int num = 0;
-
-  bitmask = ares_getsock((ares_channel)data->state.async.resolver, socks,
-                         ARES_GETSOCK_MAXNUM);
-
-  for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
-    pfd[i].events = 0;
-    pfd[i].revents = 0;
-    if(ARES_GETSOCK_READABLE(bitmask, i)) {
-      pfd[i].fd = socks[i];
-      pfd[i].events |= POLLRDNORM|POLLIN;
-    }
-    if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
-      pfd[i].fd = socks[i];
-      pfd[i].events |= POLLWRNORM|POLLOUT;
-    }
-    if(pfd[i].events)
-      num++;
-    else
-      break;
-  }
-
-  if(num) {
-    nfds = Curl_poll(pfd, (unsigned int)num, timeout_ms);
-    if(nfds < 0)
-      return -1;
-  }
-  else
-    nfds = 0;
-
-  if(!nfds)
-    /* Call ares_process() unconditionally here, even if we simply timed out
-       above, as otherwise the ares name resolve will not timeout! */
-    ares_process_fd((ares_channel)data->state.async.resolver, ARES_SOCKET_BAD,
-                    ARES_SOCKET_BAD);
-  else {
-    /* move through the descriptors and ask for processing on them */
-    for(i = 0; i < num; i++)
-      ares_process_fd((ares_channel)data->state.async.resolver,
-                      (pfd[i].revents & (POLLRDNORM|POLLIN)) ?
-                      pfd[i].fd : ARES_SOCKET_BAD,
-                      (pfd[i].revents & (POLLWRNORM|POLLOUT)) ?
-                      pfd[i].fd : ARES_SOCKET_BAD);
-  }
-  return nfds;
+  return Curl_ares_getsock(data, (ares_channel)data->state.async.resolver,
+                           socks);
 }
 
 /*
@@ -393,7 +392,7 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
   DEBUGASSERT(dns);
   *dns = NULL;
 
-  if(waitperform(data, 0) < 0)
+  if(Curl_ares_perform((ares_channel)data->state.async.resolver, 0) < 0)
     return CURLE_UNRECOVERABLE_POLL;
 
 #ifndef HAVE_CARES_GETADDRINFO
@@ -431,7 +430,7 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
       result = Curl_resolver_error(data);
     else {
       *dns = data->state.async.dns;
-#ifdef USE_HTTPSRR
+#ifdef USE_HTTPSRR_ARES
       {
         struct Curl_https_rrinfo *lhrr =
           Curl_memdup(&res->hinfo, sizeof(struct Curl_https_rrinfo));
@@ -503,7 +502,8 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
     else
       timeout_ms = 1000;
 
-    if(waitperform(data, timeout_ms) < 0)
+    if(Curl_ares_perform((ares_channel)data->state.async.resolver,
+                         timeout_ms) < 0)
       return CURLE_UNRECOVERABLE_POLL;
     result = Curl_resolver_is_resolved(data, entry);
 
@@ -768,72 +768,6 @@ static void addrinfo_cb(void *arg, int status, int timeouts,
 
 #endif
 
-#ifdef USE_HTTPSRR
-static void httpsrr_opt(struct Curl_easy *data,
-                        const ares_dns_rr_t *rr,
-                        ares_dns_rr_key_t key, size_t idx)
-{
-  size_t len = 0;
-  const unsigned char *val = NULL;
-  unsigned short code;
-  struct thread_data *res = data->state.async.tdata;
-
-  code  = ares_dns_rr_get_opt(rr, key, idx, &val, &len);
-
-  switch(code) {
-  case HTTPS_RR_CODE_ALPN: /* str_list */
-    Curl_httpsrr_decode_alpn(val, len, res->hinfo.alpns);
-    infof(data, "HTTPS RR ALPN: %u %u %u %u",
-          res->hinfo.alpns[0], res->hinfo.alpns[1], res->hinfo.alpns[2],
-          res->hinfo.alpns[3]);
-    break;
-  case HTTPS_RR_CODE_NO_DEF_ALPN:
-    infof(data, "HTTPS RR no-def-alpn");
-    break;
-  case HTTPS_RR_CODE_IPV4: /* addr4 list */
-    infof(data, "HTTPS RR IPv4");
-    break;
-  case HTTPS_RR_CODE_ECH:
-    infof(data, "HTTPS RR ECH");
-    break;
-  case HTTPS_RR_CODE_IPV6: /* addr6 list */
-    infof(data, "HTTPS RR IPv6");
-    break;
-  case HTTPS_RR_CODE_PORT:
-    infof(data, "HTTPS RR port");
-    break;
-  default:
-    infof(data, "HTTPS RR unknown code");
-    break;
-  }
-}
-
-static void dnsrec_done_cb(void *arg, ares_status_t status,
-                           size_t timeouts,
-                           const ares_dns_record_t *dnsrec)
-{
-  struct Curl_easy *data = arg;
-  size_t i;
-  struct thread_data *res = data->state.async.tdata;
-  (void)timeouts;
-
-  res->num_pending--;
-  if((ARES_SUCCESS != status) || !dnsrec)
-    return;
-
-  for(i = 0; i < ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER); i++) {
-    size_t opt;
-    const ares_dns_rr_t *rr =
-      ares_dns_record_rr_get_const(dnsrec, ARES_SECTION_ANSWER, i);
-    if(ares_dns_rr_get_type(rr) != ARES_REC_TYPE_HTTPS)
-      continue;
-    for(opt = 0; opt < ares_dns_rr_get_opt_cnt(rr, ARES_RR_HTTPS_PARAMS);
-        opt++)
-      httpsrr_opt(data, rr, ARES_RR_HTTPS_PARAMS, opt);
-  }
-}
-#endif
-
 /*
  * Curl_resolver_getaddrinfo() - when using ares
  *
@@ -916,14 +850,14 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
                          query_completed_cb, data);
     }
 #endif
-#ifdef USE_HTTPSRR
+#ifdef USE_HTTPSRR_ARES
     {
       res->num_pending++; /* one more */
       memset(&res->hinfo, 0, sizeof(struct Curl_https_rrinfo));
       ares_query_dnsrec((ares_channel)data->state.async.resolver,
                         hostname, ARES_CLASS_IN,
                         ARES_REC_TYPE_HTTPS,
-                        dnsrec_done_cb, data, NULL);
+                        Curl_dnsrec_done_cb, data, NULL);
     }
 #endif
     *waitp = 1; /* expect asynchronous response */
@@ -1058,3 +992,5 @@ CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
 #endif
 }
 #endif /* CURLRES_ARES */
+
+#endif /* USE_ARES */
index 32d496b107cb0a0acf11a7ca5a74d84fda58493a..c7e84353cbd81ac6fff599c646ab25b0e59d383d 100644 (file)
 #include "inet_ntop.h"
 #include "curl_threads.h"
 #include "connect.h"
+
+#ifdef USE_ARES
+#include <ares.h>
+#ifdef USE_HTTPSRR
+#define USE_HTTPSRR_ARES 1 /* the combo */
+#endif
+#endif
+
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
@@ -145,32 +153,6 @@ static bool init_resolve_thread(struct Curl_easy *data,
                                 const struct addrinfo *hints);
 
 
-/* Data for synchronization between resolver thread and its parent */
-struct thread_sync_data {
-  curl_mutex_t *mtx;
-  int done;
-  int port;
-  char *hostname;        /* hostname to resolve, Curl_async.hostname
-                            duplicate */
-#ifndef CURL_DISABLE_SOCKETPAIR
-  struct Curl_easy *data;
-  curl_socket_t sock_pair[2]; /* eventfd/pipes/socket pair */
-#endif
-  int sock_error;
-  struct Curl_addrinfo *res;
-#ifdef HAVE_GETADDRINFO
-  struct addrinfo hints;
-#endif
-  struct thread_data *td; /* for thread-self cleanup */
-};
-
-struct thread_data {
-  curl_thread_t thread_hnd;
-  unsigned int poll_interval;
-  timediff_t interval_end;
-  struct thread_sync_data tsd;
-};
-
 static struct thread_sync_data *conn_thread_sync_data(struct Curl_easy *data)
 {
   return &(data->state.async.tdata->tsd);
@@ -220,7 +202,7 @@ int init_thread_sync_data(struct thread_data *td,
   /* Treat the request as done until the thread actually starts so any early
    * cleanup gets done properly.
    */
-  tsd->done = 1;
+  tsd->done = TRUE;
 #ifdef HAVE_GETADDRINFO
   DEBUGASSERT(hints);
   tsd->hints = *hints;
@@ -343,7 +325,7 @@ CURL_STDCALL getaddrinfo_thread(void *arg)
       }
     }
 #endif
-    tsd->done = 1;
+    tsd->done = TRUE;
     Curl_mutex_release(tsd->mtx);
   }
 
@@ -382,7 +364,7 @@ CURL_STDCALL gethostbyname_thread(void *arg)
     free(td);
   }
   else {
-    tsd->done = 1;
+    tsd->done = TRUE;
     Curl_mutex_release(tsd->mtx);
   }
 
@@ -398,19 +380,22 @@ static void destroy_async_data(struct Curl_async *async)
 {
   if(async->tdata) {
     struct thread_data *td = async->tdata;
-    int done;
+    bool done;
 #ifndef CURL_DISABLE_SOCKETPAIR
     curl_socket_t sock_rd = td->tsd.sock_pair[0];
     struct Curl_easy *data = td->tsd.data;
 #endif
 
+#ifdef USE_HTTPSRR_ARES
+    ares_destroy(data->state.async.tdata->channel);
+#endif
     /*
      * if the thread is still blocking in the resolve syscall, detach it and
      * let the thread do the cleanup...
      */
     Curl_mutex_acquire(td->tsd.mtx);
     done = td->tsd.done;
-    td->tsd.done = 1;
+    td->tsd.done = TRUE;
     Curl_mutex_release(td->tsd.mtx);
 
     if(!done) {
@@ -439,6 +424,24 @@ static void destroy_async_data(struct Curl_async *async)
   async->hostname = NULL;
 }
 
+#ifdef USE_HTTPSRR_ARES
+static CURLcode resolve_httpsrr(struct Curl_easy *data,
+                                struct Curl_async *asp)
+{
+  int status = ares_init(&asp->tdata->channel);
+  if(status != ARES_SUCCESS)
+    return CURLE_FAILED_INIT;
+
+  memset(&asp->tdata->hinfo, 0, sizeof(struct Curl_https_rrinfo));
+  ares_query_dnsrec(asp->tdata->channel,
+                    asp->hostname, ARES_CLASS_IN,
+                    ARES_REC_TYPE_HTTPS,
+                    Curl_dnsrec_done_cb, data, NULL);
+
+  return CURLE_OK;
+}
+#endif
+
 /*
  * init_resolve_thread() starts a new thread that performs the actual
  * resolve. This function returns before the resolve is done.
@@ -474,8 +477,8 @@ static bool init_resolve_thread(struct Curl_easy *data,
   if(!asp->hostname)
     goto err_exit;
 
-  /* The thread will set this to 1 when complete. */
-  td->tsd.done = 0;
+  /* The thread will set this TRUE when complete. */
+  td->tsd.done = FALSE;
 
 #ifdef HAVE_GETADDRINFO
   td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
@@ -485,11 +488,14 @@ static bool init_resolve_thread(struct Curl_easy *data,
 
   if(td->thread_hnd == curl_thread_t_null) {
     /* The thread never started, so mark it as done here for proper cleanup. */
-    td->tsd.done = 1;
+    td->tsd.done = TRUE;
     err = errno;
     goto err_exit;
   }
-
+#ifdef USE_HTTPSRR_ARES
+  if(resolve_httpsrr(data, asp))
+    goto err_exit;
+#endif
   return TRUE;
 
 err_exit:
@@ -587,7 +593,7 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
                                    struct Curl_dns_entry **entry)
 {
   struct thread_data *td = data->state.async.tdata;
-  int done = 0;
+  bool done = FALSE;
 
   DEBUGASSERT(entry);
   *entry = NULL;
@@ -596,6 +602,10 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
     DEBUGASSERT(td);
     return CURLE_COULDNT_RESOLVE_HOST;
   }
+#ifdef USE_HTTPSRR_ARES
+  if(Curl_ares_perform(data->state.async.tdata->channel, 0) < 0)
+    return CURLE_UNRECOVERABLE_POLL;
+#endif
 
   Curl_mutex_acquire(td->tsd.mtx);
   done = td->tsd.done;
@@ -643,18 +653,28 @@ int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks)
   timediff_t milli;
   timediff_t ms;
   struct resdata *reslv = (struct resdata *)data->state.async.resolver;
+  int socketi = 0;
 #ifndef CURL_DISABLE_SOCKETPAIR
   struct thread_data *td = data->state.async.tdata;
 #else
   (void)socks;
 #endif
 
+#ifdef USE_HTTPSRR_ARES
+  if(data->state.async.tdata) {
+    ret_val = Curl_ares_getsock(data, data->state.async.tdata->channel, socks);
+    for(socketi = 0; socketi < (MAX_SOCKSPEREASYHANDLE - 1); socketi++)
+      if(!ARES_GETSOCK_READABLE(ret_val, socketi) &&
+         !ARES_GETSOCK_WRITABLE(ret_val, socketi))
+        break;
+  }
+#endif
 #ifndef CURL_DISABLE_SOCKETPAIR
   if(td) {
     /* return read fd to client for polling the DNS resolution status */
-    socks[0] = td->tsd.sock_pair[0];
+    socks[socketi] = td->tsd.sock_pair[0];
     td->tsd.data = data;
-    ret_val = GETSOCK_READSOCK(0);
+    ret_val = GETSOCK_READSOCK(socketi);
   }
   else {
 #endif
index 0ff2048845551e24caad9a6894c20e31d4fb5a2c..c6745412563d8c2999e639d44617be8f1402fe22 100644 (file)
@@ -26,6 +26,7 @@
 
 #include "curl_setup.h"
 #include "curl_addrinfo.h"
+#include "httpsrr.h"
 
 struct addrinfo;
 struct hostent;
@@ -33,6 +34,69 @@ struct Curl_easy;
 struct connectdata;
 struct Curl_dns_entry;
 
+#ifdef CURLRES_THREADED
+#include "curl_threads.h"
+
+/* Data for synchronization between resolver thread and its parent */
+struct thread_sync_data {
+  curl_mutex_t *mtx;
+  bool done;
+  int port;
+  char *hostname;        /* hostname to resolve, Curl_async.hostname
+                            duplicate */
+#ifndef CURL_DISABLE_SOCKETPAIR
+  struct Curl_easy *data;
+  curl_socket_t sock_pair[2]; /* eventfd/pipes/socket pair */
+#endif
+  int sock_error;
+  struct Curl_addrinfo *res;
+#ifdef HAVE_GETADDRINFO
+  struct addrinfo hints;
+#endif
+  struct thread_data *td; /* for thread-self cleanup */
+};
+
+struct thread_data {
+  curl_thread_t thread_hnd;
+  unsigned int poll_interval;
+  timediff_t interval_end;
+  struct thread_sync_data tsd;
+#if defined(USE_HTTPSRR) && defined(USE_ARES)
+  struct Curl_https_rrinfo hinfo;
+  ares_channel channel;
+#endif
+};
+
+#elif defined(CURLRES_ARES) /* CURLRES_THREADED */
+
+struct thread_data {
+  int num_pending; /* number of outstanding c-ares requests */
+  struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares
+                                    parts */
+  int last_status;
+#ifndef HAVE_CARES_GETADDRINFO
+  struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */
+#endif
+#ifdef USE_HTTPSRR
+  struct Curl_https_rrinfo hinfo;
+#endif
+  char hostname[1];
+};
+
+#endif /* CURLRES_ARES */
+
+#ifdef USE_ARES
+#include <ares.h>
+
+/* for HTTPS RR purposes as well */
+int Curl_ares_getsock(struct Curl_easy *data,
+                      ares_channel channel,
+                      curl_socket_t *socks);
+int Curl_ares_perform(ares_channel channel,
+                      timediff_t timeout_ms);
+#endif
+
+
 /*
  * This header defines all functions in the internal asynch resolver interface.
  * All asynch resolvers need to provide these functions.
index c973794a6c9a0b33792692f0ecfff7849930af79..71326c35e6ee5ae23fe9fcc9a4d52df49b071ec4 100644 (file)
 #  define CURL_DISABLE_HTTP_AUTH 1
 #endif
 
-/*
- * ECH requires HTTPSRR.
- */
-
-#if defined(USE_ECH) && !defined(USE_HTTPSRR)
-#  define USE_HTTPSRR
-#endif
-
 /* ================================================================ */
 /* No system header file shall be included in this file before this */
 /* point.                                                           */
 #  define CURLRES_IPV4
 #endif
 
-#ifdef USE_ARES
+#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
+#  define CURLRES_ASYNCH
+#  define CURLRES_THREADED
+#elif defined(USE_ARES)
 #  define CURLRES_ASYNCH
 #  define CURLRES_ARES
 /* now undef the stock libc functions just to avoid them being used */
 #  undef HAVE_GETADDRINFO
 #  undef HAVE_FREEADDRINFO
-#elif defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
-#  define CURLRES_ASYNCH
-#  define CURLRES_THREADED
 #else
 #  define CURLRES_SYNCH
 #endif
index e5e0d32604c2e30332f06f09f5156137616af8de..10f70b2ba82e788eb5b06de21662b3a98231195d 100644 (file)
@@ -29,6 +29,7 @@
 #include "curl_addrinfo.h"
 #include "timeval.h" /* for timediff_t */
 #include "asyn.h"
+#include "httpsrr.h"
 
 #include <setjmp.h>
 
@@ -69,31 +70,6 @@ enum alpnid {
  */
 struct Curl_hash *Curl_global_host_cache_init(void);
 
-#ifdef USE_HTTPSRR
-
-#define CURL_MAXLEN_host_name 253
-#define MAX_HTTPSRR_ALPNS 4
-
-struct Curl_https_rrinfo {
-  /*
-   * Fields from HTTPS RR. The only mandatory fields are priority and target.
-   * See https://datatracker.ietf.org/doc/html/rfc9460#section-14.3.2
-   */
-  char *target;
-  unsigned char *ipv4hints; /* keytag = 4 */
-  size_t ipv4hints_len;
-  unsigned char *echconfiglist; /* keytag = 5 */
-  size_t echconfiglist_len;
-  unsigned char *ipv6hints; /* keytag = 6 */
-  size_t ipv6hints_len;
-  unsigned char alpns[MAX_HTTPSRR_ALPNS]; /* keytag = 1 */
-  /* store parsed alpnid entries in the array, end with ALPN_none */
-  int port; /* -1 means not set */
-  uint16_t priority;
-  bool no_def_alpn; /* keytag = 2 */
-};
-#endif
-
 struct Curl_dns_entry {
   struct Curl_addrinfo *addr;
 #ifdef USE_HTTPSRR
index 6e6b0a51c964790f6a5de24253d3ccc9d2511789..9884b92580b43fa59b6b66dae2081fc6adc16239 100644 (file)
 #include "curl_addrinfo.h"
 #include "httpsrr.h"
 #include "connect.h"
+#include "sendf.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
 
 CURLcode Curl_httpsrr_decode_alpn(const unsigned char *cp, size_t len,
                                   unsigned char *alpns)
@@ -85,4 +91,77 @@ err:
   return CURLE_BAD_CONTENT_ENCODING;
 }
 
+#ifdef USE_ARES
+
+static void httpsrr_opt(struct Curl_easy *data,
+                        const ares_dns_rr_t *rr,
+                        ares_dns_rr_key_t key, size_t idx)
+{
+  size_t len = 0;
+  const unsigned char *val = NULL;
+  unsigned short code;
+  struct thread_data *res = data->state.async.tdata;
+  struct Curl_https_rrinfo *hi = &res->hinfo;
+  code  = ares_dns_rr_get_opt(rr, key, idx, &val, &len);
+
+  switch(code) {
+  case HTTPS_RR_CODE_ALPN: /* str_list */
+    Curl_httpsrr_decode_alpn(val, len, hi->alpns);
+    infof(data, "HTTPS RR ALPN: %u %u %u %u",
+          hi->alpns[0], hi->alpns[1], hi->alpns[2], hi->alpns[3]);
+    break;
+  case HTTPS_RR_CODE_NO_DEF_ALPN:
+    infof(data, "HTTPS RR no-def-alpn");
+    break;
+  case HTTPS_RR_CODE_IPV4: /* addr4 list */
+    infof(data, "HTTPS RR IPv4");
+    break;
+  case HTTPS_RR_CODE_ECH:
+    infof(data, "HTTPS RR ECH");
+    break;
+  case HTTPS_RR_CODE_IPV6: /* addr6 list */
+    infof(data, "HTTPS RR IPv6");
+    break;
+  case HTTPS_RR_CODE_PORT:
+    infof(data, "HTTPS RR port");
+    break;
+  default:
+    infof(data, "HTTPS RR unknown code");
+    break;
+  }
+}
+
+void Curl_dnsrec_done_cb(void *arg, ares_status_t status,
+                         size_t timeouts,
+                         const ares_dns_record_t *dnsrec)
+{
+  struct Curl_easy *data = arg;
+  size_t i;
+#ifdef CURLRES_ARES
+  struct thread_data *res = data->state.async.tdata;
+
+  res->num_pending--;
 #endif
+  (void)timeouts;
+  if((ARES_SUCCESS != status) || !dnsrec)
+    return;
+
+  for(i = 0; i < ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER); i++) {
+    size_t opt;
+    const ares_dns_rr_t *rr =
+      ares_dns_record_rr_get_const(dnsrec, ARES_SECTION_ANSWER, i);
+    if(ares_dns_rr_get_type(rr) != ARES_REC_TYPE_HTTPS)
+      continue;
+    /* When SvcPriority is 0, the SVCB record is in AliasMode. Otherwise, it
+       is in ServiceMode */
+    infof(data, "HTTPS RR priority: %u",
+          ares_dns_rr_get_u16(rr, ARES_RR_HTTPS_PRIORITY));
+    for(opt = 0; opt < ares_dns_rr_get_opt_cnt(rr, ARES_RR_HTTPS_PARAMS);
+        opt++)
+      httpsrr_opt(data, rr, ARES_RR_HTTPS_PARAMS, opt);
+  }
+}
+
+#endif /* USE_ARES */
+
+#endif /* USE_HTTPSRR */
index 26f7a9826a2f36bb32668c39ec5e902eb5d8be57..ade2126f0f1dcb82aee445f37abaf83149ace9bb 100644 (file)
  *
  ***************************************************************************/
 
+#include "curl_setup.h"
+
+#ifdef USE_ARES
+#include <ares.h>
+#endif
+
+#ifdef USE_HTTPSRR
+
+#define CURL_MAXLEN_host_name 253
+#define MAX_HTTPSRR_ALPNS 4
+
+struct Curl_https_rrinfo {
+  /*
+   * Fields from HTTPS RR. The only mandatory fields are priority and target.
+   * See https://datatracker.ietf.org/doc/html/rfc9460#section-14.3.2
+   */
+  char *target;
+  unsigned char *ipv4hints; /* keytag = 4 */
+  size_t ipv4hints_len;
+  unsigned char *echconfiglist; /* keytag = 5 */
+  size_t echconfiglist_len;
+  unsigned char *ipv6hints; /* keytag = 6 */
+  size_t ipv6hints_len;
+  unsigned char alpns[MAX_HTTPSRR_ALPNS]; /* keytag = 1 */
+  /* store parsed alpnid entries in the array, end with ALPN_none */
+  int port; /* -1 means not set */
+  uint16_t priority;
+  bool no_def_alpn; /* keytag = 2 */
+};
+#endif
 
 /*
  * Code points for DNS wire format SvcParams as per RFC 9460
@@ -38,4 +68,9 @@
 CURLcode Curl_httpsrr_decode_alpn(const unsigned char *cp, size_t len,
                                   unsigned char *alpns);
 
+#if defined(USE_ARES) && defined(USE_HTTPSRR)
+void Curl_dnsrec_done_cb(void *arg, ares_status_t status,
+                         size_t timeouts,
+                         const ares_dns_record_t *dnsrec);
+#endif
 #endif /* HEADER_CURL_HTTPSRR_H */
index c5275495bcd526004cf6cbeaf536cfeb5fdda591..0fcfed037961b89dcfa3d0a1fbd703502180e4e8 100644 (file)
@@ -467,11 +467,18 @@ static const struct feat features_table[] = {
 #ifdef HAVE_BROTLI
   FEATURE("brotli",      NULL,                CURL_VERSION_BROTLI),
 #endif
+#if defined(CURLRES_ARES) && defined(CURLRES_THREADED)
+  FEATURE("c-ares-rr", NULL,             0),
+#endif
 #ifdef DEBUGBUILD
   FEATURE("Debug",       NULL,                CURL_VERSION_DEBUG),
 #endif
 #if defined(USE_SSL) && defined(USE_ECH)
   FEATURE("ECH",         ech_present,         0),
+
+#ifndef USE_HTTPSRR
+#error "ECH enabled but not HTTPSRR, must be a config error"
+#endif
 #endif
 #ifdef USE_GSASL
   FEATURE("gsasl",       NULL,                CURL_VERSION_GSASL),
@@ -492,6 +499,9 @@ static const struct feat features_table[] = {
   !defined(CURL_DISABLE_HTTP)
   FEATURE("HTTPS-proxy", https_proxy_present, CURL_VERSION_HTTPS_PROXY),
 #endif
+#if defined(USE_HTTPSRR)
+  FEATURE("HTTPSRR",     NULL,                0),
+#endif
 #if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN)
   FEATURE("IDN",         idn_present,         CURL_VERSION_IDN),
 #endif
index f7b99dffccf68a367b42d2b87e6099d3f65fb536..7da8cb58be1c5ab05aa2a4a5d1a300b0e92f1320 100644 (file)
@@ -29,7 +29,7 @@ dnl CURL_CHECK_OPTION_THREADED_RESOLVER
 dnl -------------------------------------------------
 dnl Verify if configure has been invoked with option
 dnl --enable-threaded-resolver or --disable-threaded-resolver, and
-dnl set shell variable want_thres as appropriate.
+dnl set shell variable want_threaded_resolver as appropriate.
 
 AC_DEFUN([CURL_CHECK_OPTION_THREADED_RESOLVER], [
   AC_MSG_CHECKING([whether to enable the threaded resolver])
@@ -41,25 +41,29 @@ AS_HELP_STRING([--disable-threaded-resolver],[Disable threaded resolver]),
   case "$OPT_THRES" in
     no)
       dnl --disable-threaded-resolver option used
-      want_thres="no"
+      want_threaded_resolver="no"
       ;;
     yes)
       dnl --enable-threaded-resolver option used
-      want_thres="yes"
+      want_threaded_resolver="yes"
       ;;
     *)
       dnl configure option not specified
       case $host_os in
         msdos* | amiga*)
-          want_thres="no"
+          want_threaded_resolver="no"
           ;;
         *)
-          want_thres="yes"
+          if test "$want_ares" = "yes"; then
+            want_threaded_resolver="no"
+          else
+            want_threaded_resolver="yes"
+          fi
           ;;
       esac
       ;;
   esac
-  AC_MSG_RESULT([$want_thres])
+  AC_MSG_RESULT([$want_threaded_resolver])
 ])
 
 dnl CURL_CHECK_OPTION_ARES
@@ -281,53 +285,6 @@ AS_HELP_STRING([--disable-symbol-hiding],[Disable hiding of library internal sym
 ])
 
 
-dnl CURL_CHECK_OPTION_THREADS
-dnl -------------------------------------------------
-dnl Verify if configure has been invoked with option
-dnl --enable-threads or --disable-threads, and
-dnl set shell variable want_threads as appropriate.
-
-dnl AC_DEFUN([CURL_CHECK_OPTION_THREADS], [
-dnl   AC_BEFORE([$0],[CURL_CHECK_LIB_THREADS])dnl
-dnl   AC_MSG_CHECKING([whether to enable threads for DNS lookups])
-dnl   OPT_THREADS="default"
-dnl   AC_ARG_ENABLE(threads,
-dnl AS_HELP_STRING([--enable-threads@<:@=PATH@:>@],[Enable threads for DNS lookups])
-dnl AS_HELP_STRING([--disable-threads],[Disable threads for DNS lookups]),
-dnl   OPT_THREADS=$enableval)
-dnl   case "$OPT_THREADS" in
-dnl     no)
-dnl       dnl --disable-threads option used
-dnl       want_threads="no"
-dnl       AC_MSG_RESULT([no])
-dnl       ;;
-dnl     default)
-dnl       dnl configure option not specified
-dnl       want_threads="no"
-dnl       AC_MSG_RESULT([(assumed) no])
-dnl       ;;
-dnl     *)
-dnl       dnl --enable-threads option used
-dnl       want_threads="yes"
-dnl       want_threads_path="$enableval"
-dnl       AC_MSG_RESULT([yes])
-dnl       ;;
-dnl   esac
-dnl   #
-dnl   if test "$want_ares" = "assume_yes"; then
-dnl     if test "$want_threads" = "yes"; then
-dnl       AC_MSG_CHECKING([whether to ignore c-ares enabling assumed setting])
-dnl       AC_MSG_RESULT([yes])
-dnl       want_ares="no"
-dnl     else
-dnl       want_ares="yes"
-dnl     fi
-dnl   fi
-dnl   if test "$want_threads" = "yes" && test "$want_ares" = "yes"; then
-dnl     AC_MSG_ERROR([options --enable-ares and --enable-threads are mutually exclusive, at most one may be enabled.])
-dnl   fi
-dnl ])
-
 dnl CURL_CHECK_OPTION_RT
 dnl -------------------------------------------------
 dnl Verify if configure has been invoked with option
@@ -612,7 +569,7 @@ AS_HELP_STRING([--disable-httpsrr],[Disable HTTPSRR support]),
     *)
       dnl --enable-httpsrr option used
       want_httpsrr="yes"
-      curl_httpsrr_msg="enabled (--disable-httpsrr)"
+      curl_httpsrr_msg="enabled"
       AC_MSG_RESULT([yes])
       ;;
   esac
index a763f8711ce44a87e11b0a82414abfe1922d92a9..529bbc0196151fe3487ba26b42c7d7fd7f96db03 100644 (file)
@@ -434,7 +434,8 @@ Features testable here are:
 - `AppleIDN`
 - `bearssl`
 - `brotli`
-- `c-ares`
+- `c-ares` - c-ares is used for (all) name resolves
+- `c-ares-rr` - c-ares is used for additional records only
 - `CharConv`
 - `codeset-utf8`. If the running codeset is UTF-8 capable.
 - `cookies`
index a8f0a3b1d2799a59812af0e5a76eda0c92842001..ce113a73b86e3367149f9f50e7eeb42551c2b32d 100644 (file)
@@ -28,7 +28,7 @@ Compare curl --version with curl-config --features
 # Verify data after the test has been "shot"
 <verify>
 <postcheck>
-%SRCDIR/libtest/test1013.pl ../curl-config %LOGDIR/stdout%TESTNUMBER features
+%SRCDIR/libtest/test1013.pl ../curl-config %LOGDIR/stdout%TESTNUMBER features > %LOGDIR/result%TESTNUMBER
 </postcheck>
 <errorcode>
 0
index b6b8ecf12df02ee8f739d88fad64bba15a0eea7c..3acbcb03382a2518c51c3232abe6c61b44aaefa6 100644 (file)
@@ -205,6 +205,6 @@ test_cleanup:
 CURLcode test(char *URL)
 {
   (void)URL;
-  return 0;
+  return CURLE_OK;
 }
 #endif
index 175a843eef3ff23b7089dbd36d5a32b1f093cf67..9ec2b8ca45a05e3cb125f5ee9e62b5bb40bd9a56 100755 (executable)
@@ -698,6 +698,8 @@ sub checksystemfeatures {
             $feature{"Unicode"} = $feat =~ /Unicode/i;
             # Thread-safe init
             $feature{"threadsafe"} = $feat =~ /threadsafe/i;
+            $feature{"HTTPSRR"} = $feat =~ /HTTPSRR/;
+            $feature{"c-ares-rr"} = $feat =~ /c-ares-rr/;
         }
         #
         # Test harness currently uses a non-stunnel server in order to
@@ -822,7 +824,6 @@ sub checksystemfeatures {
     $feature{"large-time"} = 1;
     $feature{"large-size"} = 1;
     $feature{"sha512-256"} = 1;
-    $feature{"HTTPSRR"} = 1;
     $feature{"local-http"} = servers::localhttp();
     $feature{"codeset-utf8"} = lc(langinfo(CODESET())) eq "utf-8";
 
index daf7b5d2a8dc469aa640d7952171eaf5a5b360ea..ef047c54855118cba959425fd712b503c9302469 100644 (file)
@@ -115,9 +115,6 @@ static const char *disabled[]={
 #ifndef CURL_CA_SEARCH_SAFE
   "win32-ca-search-safe",
 #endif
-#endif
-#ifndef USE_HTTPSRR
-  "HTTPSRR",
 #endif
   NULL
 };