]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
asyn-ares: initial HTTPS resolve support
authorDaniel Stenberg <daniel@haxx.se>
Sat, 18 Jan 2025 21:47:54 +0000 (22:47 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Sun, 19 Jan 2025 10:25:09 +0000 (11:25 +0100)
Gets the ALPN list the same way DoH does. Needs c-ares 1.28.0 or later.

Thanks-to: Brad House
Closes #16039

lib/Makefile.inc
lib/asyn-ares.c
lib/doh.c
lib/doh.h
lib/httpsrr.c [new file with mode: 0644]
lib/httpsrr.h [new file with mode: 0644]

index d2c7b6e8881e70179fa56fefd123c2fcbdae77c7..fc6201e9e62b7e518248542b2d63cfa025684112 100644 (file)
@@ -180,6 +180,7 @@ LIB_CFILES =         \
   http_negotiate.c   \
   http_ntlm.c        \
   http_proxy.c       \
+  httpsrr.c          \
   idn.c              \
   if2ip.c            \
   imap.c             \
@@ -320,6 +321,7 @@ LIB_HFILES =         \
   http_negotiate.h   \
   http_ntlm.h        \
   http_proxy.h       \
+  httpsrr.h          \
   idn.h              \
   if2ip.h            \
   imap.h             \
index 6f81675995af2232204d8bb13c463e88528c87ea..8123226b6e2b917e78e5261827e88c1744c1b6c2 100644 (file)
@@ -59,6 +59,8 @@
 #include "select.h"
 #include "progress.h"
 #include "timediff.h"
+#include "httpsrr.h"
+#include "strdup.h"
 
 #if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) &&   \
   defined(_WIN32)
 #define HAVE_CARES_GETADDRINFO 1
 #endif
 
+#if ARES_VERSION >= 0x011c00
+/* 1.28.0 and later have ares_query_dnsrec */
+#define HAVE_ARES_QUERY_DNSREC 1
+#else
+#undef USE_HTTPSRR
+#endif
+
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
@@ -105,6 +114,9 @@ struct thread_data {
   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];
 };
@@ -417,8 +429,18 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
 
     if(!data->state.async.dns)
       result = Curl_resolver_error(data);
-    else
+    else {
       *dns = data->state.async.dns;
+#ifdef USE_HTTPSRR
+      {
+        struct Curl_https_rrinfo *lhrr =
+          Curl_memdup(&res->hinfo, sizeof(struct Curl_https_rrinfo));
+        if(!lhrr)
+          return CURLE_OUT_OF_MEMORY;
+        (*dns)->hinfo = lhrr;
+      }
+#endif
+    }
 
     destroy_async_data(&data->state.async);
   }
@@ -745,6 +767,73 @@ 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
  *
@@ -826,6 +915,16 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
                          hostname, PF_INET,
                          query_completed_cb, data);
     }
+#endif
+#ifdef USE_HTTPSRR
+    {
+      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);
+    }
 #endif
     *waitp = 1; /* expect asynchronous response */
   }
index d4500b3bd50bd0932bb421ac18071b788303af2d..617a761f9754816f33ca61a1b9bd13c3ffc76782 100644 (file)
--- a/lib/doh.c
+++ b/lib/doh.c
@@ -1064,59 +1064,6 @@ static CURLcode doh_decode_rdata_name(unsigned char **buf, size_t *remaining,
   return CURLE_OK;
 }
 
-static CURLcode doh_decode_rdata_alpn(unsigned char *cp, size_t len,
-                                      unsigned char *alpns)
-{
-  /*
-   * spec here is as per RFC 9460, section-7.1.1
-   * encoding is a concatenated list of strings each preceded by a one
-   * octet length
-   * output is comma-sep list of the strings
-   * implementations may or may not handle quoting of comma within
-   * string values, so we might see a comma within the wire format
-   * version of a string, in which case we will precede that by a
-   * backslash - same goes for a backslash character, and of course
-   * we need to use two backslashes in strings when we mean one;-)
-   */
-  struct dynbuf dval;
-  int idnum = 0;
-
-  Curl_dyn_init(&dval, DYN_DOH_RESPONSE);
-  while(len > 0) {
-    size_t tlen = (size_t) *cp++;
-    size_t i;
-    enum alpnid id;
-    len--;
-    if(tlen > len)
-      goto err;
-    /* add escape char if needed, clunky but easier to read */
-    for(i = 0; i != tlen; i++) {
-      if('\\' == *cp || ',' == *cp) {
-        if(Curl_dyn_addn(&dval, "\\", 1))
-          goto err;
-      }
-      if(Curl_dyn_addn(&dval, cp++, 1))
-        goto err;
-    }
-    len -= tlen;
-
-    /* we only store ALPN ids we know about */
-    id = Curl_alpn2alpnid(Curl_dyn_ptr(&dval), Curl_dyn_len(&dval));
-    if(id != ALPN_none) {
-      if(idnum == MAX_HTTPSRR_ALPNS)
-        break;
-      alpns[idnum++] = (unsigned char)id;
-    }
-    Curl_dyn_reset(&dval);
-  }
-  if(idnum < MAX_HTTPSRR_ALPNS)
-    alpns[idnum] = ALPN_none; /* terminate the list */
-  return CURLE_OK;
-err:
-  Curl_dyn_free(&dval);
-  return CURLE_BAD_CONTENT_ENCODING;
-}
-
 #ifdef DEBUGBUILD
 static CURLcode doh_test_alpn_escapes(void)
 {
@@ -1131,7 +1078,7 @@ static CURLcode doh_test_alpn_escapes(void)
   unsigned char aval[MAX_HTTPSRR_ALPNS] = { 0 };
   static const char expected[2] = { ALPN_h2, ALPN_none };
 
-  if(doh_decode_rdata_alpn(example, example_len, aval) != CURLE_OK)
+  if(Curl_httpsrr_decode_alpn(example, example_len, aval) != CURLE_OK)
     return CURLE_BAD_CONTENT_ENCODING;
   if(memcmp(aval, expected, sizeof(expected)))
     return CURLE_BAD_CONTENT_ENCODING;
@@ -1170,7 +1117,7 @@ static CURLcode doh_resp_decode_httpsrr(unsigned char *cp, size_t len,
     len -= 4;
     switch(pcode) {
     case HTTPS_RR_CODE_ALPN:
-      if(doh_decode_rdata_alpn(cp, plen, lhrr->alpns) != CURLE_OK)
+      if(Curl_httpsrr_decode_alpn(cp, plen, lhrr->alpns) != CURLE_OK)
         goto err;
       break;
     case HTTPS_RR_CODE_NO_DEF_ALPN:
index aae32a65409fe4d32c2fc42054061d84b6bfe649..53644863e6f5b035e2891cdf6eda6881ae40c93b 100644 (file)
--- a/lib/doh.h
+++ b/lib/doh.h
@@ -28,6 +28,7 @@
 #include "curl_addrinfo.h"
 #ifdef USE_HTTPSRR
 # include <stdint.h>
+# include "httpsrr.h"
 #endif
 
 #ifndef CURL_DISABLE_DOH
@@ -123,19 +124,6 @@ struct dohaddr {
 
 #ifdef USE_HTTPSRR
 
-/*
- * These are the code points for DNS wire format SvcParams as
- * per draft-ietf-dnsop-svcb-https
- * Not all are supported now, and even those that are may need
- * more work in future to fully support the spec.
- */
-#define HTTPS_RR_CODE_ALPN            0x01
-#define HTTPS_RR_CODE_NO_DEF_ALPN     0x02
-#define HTTPS_RR_CODE_PORT            0x03
-#define HTTPS_RR_CODE_IPV4            0x04
-#define HTTPS_RR_CODE_ECH             0x05
-#define HTTPS_RR_CODE_IPV6            0x06
-
 /*
  * These may need escaping when found within an ALPN string
  * value.
diff --git a/lib/httpsrr.c b/lib/httpsrr.c
new file mode 100644 (file)
index 0000000..6e6b0a5
--- /dev/null
@@ -0,0 +1,88 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_HTTPSRR
+
+#include "urldata.h"
+#include "curl_addrinfo.h"
+#include "httpsrr.h"
+#include "connect.h"
+
+CURLcode Curl_httpsrr_decode_alpn(const unsigned char *cp, size_t len,
+                                  unsigned char *alpns)
+{
+  /*
+   * spec here is as per RFC 9460, section-7.1.1
+   * encoding is a concatenated list of strings each preceded by a one
+   * octet length
+   * output is comma-sep list of the strings
+   * implementations may or may not handle quoting of comma within
+   * string values, so we might see a comma within the wire format
+   * version of a string, in which case we will precede that by a
+   * backslash - same goes for a backslash character, and of course
+   * we need to use two backslashes in strings when we mean one;-)
+   */
+  struct dynbuf dval;
+  int idnum = 0;
+
+  Curl_dyn_init(&dval, DYN_DOH_RESPONSE);
+  while(len > 0) {
+    size_t tlen = (size_t) *cp++;
+    size_t i;
+    enum alpnid id;
+    len--;
+    if(tlen > len)
+      goto err;
+    /* add escape char if needed, clunky but easier to read */
+    for(i = 0; i != tlen; i++) {
+      if('\\' == *cp || ',' == *cp) {
+        if(Curl_dyn_addn(&dval, "\\", 1))
+          goto err;
+      }
+      if(Curl_dyn_addn(&dval, cp++, 1))
+        goto err;
+    }
+    len -= tlen;
+
+    /* we only store ALPN ids we know about */
+    id = Curl_alpn2alpnid(Curl_dyn_ptr(&dval), Curl_dyn_len(&dval));
+    if(id != ALPN_none) {
+      if(idnum == MAX_HTTPSRR_ALPNS)
+        break;
+      alpns[idnum++] = (unsigned char)id;
+    }
+    Curl_dyn_reset(&dval);
+  }
+  Curl_dyn_free(&dval);
+  if(idnum < MAX_HTTPSRR_ALPNS)
+    alpns[idnum] = ALPN_none; /* terminate the list */
+  return CURLE_OK;
+err:
+  Curl_dyn_free(&dval);
+  return CURLE_BAD_CONTENT_ENCODING;
+}
+
+#endif
diff --git a/lib/httpsrr.h b/lib/httpsrr.h
new file mode 100644 (file)
index 0000000..26f7a98
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef HEADER_CURL_HTTPSRR_H
+#define HEADER_CURL_HTTPSRR_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+
+/*
+ * Code points for DNS wire format SvcParams as per RFC 9460
+ */
+#define HTTPS_RR_CODE_ALPN            0x01
+#define HTTPS_RR_CODE_NO_DEF_ALPN     0x02
+#define HTTPS_RR_CODE_PORT            0x03
+#define HTTPS_RR_CODE_IPV4            0x04
+#define HTTPS_RR_CODE_ECH             0x05
+#define HTTPS_RR_CODE_IPV6            0x06
+
+CURLcode Curl_httpsrr_decode_alpn(const unsigned char *cp, size_t len,
+                                  unsigned char *alpns);
+
+#endif /* HEADER_CURL_HTTPSRR_H */