]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
rustls: support ECH w/ DoH lookup for config
authorDaniel McCarney <daniel@binaryparadox.net>
Mon, 24 Mar 2025 16:26:21 +0000 (12:26 -0400)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 27 Mar 2025 07:48:10 +0000 (08:48 +0100)
E.g. `curl --tlsv1.3 --doh-url https://one.one.one.one/dns-query --ech true ...`

Closes #16828

lib/vtls/rustls.c

index 4d2d195a5dee42e65e8e1b7b31daa0898b703110..e73e8679179654ce917bf96fffdd28c2389c6c01 100644 (file)
@@ -900,58 +900,94 @@ cleanup:
 #if defined(USE_ECH)
 static CURLcode
 init_config_builder_ech(struct Curl_easy *data,
+                        const struct ssl_connect_data *connssl,
                         struct rustls_client_config_builder *builder)
 {
   const rustls_hpke *hpke = rustls_supported_hpke();
   unsigned char *ech_config = NULL;
   size_t ech_config_len = 0;
+  struct Curl_dns_entry *dns = NULL;
+  struct Curl_https_rrinfo *rinfo = NULL;
+  CURLcode result = CURLE_OK;
+  rustls_result rr;
 
   if(!hpke) {
     failf(data,
           "rustls: ECH unavailable, rustls-ffi built without "
           "HPKE compatible crypto provider");
-    return CURLE_SSL_CONNECT_ERROR;
+    result = CURLE_SSL_CONNECT_ERROR;
+    goto cleanup;
   }
 
   if(data->set.str[STRING_ECH_PUBLIC]) {
     failf(data, "rustls: ECH outername not supported");
-    return CURLE_SSL_CONNECT_ERROR;
+    result = CURLE_SSL_CONNECT_ERROR;
+    goto cleanup;
   }
 
   if(data->set.tls_ech == CURLECH_GREASE) {
-    rustls_result rr;
     rr = rustls_client_config_builder_enable_ech_grease(builder, hpke);
     if(rr != RUSTLS_RESULT_OK) {
       rustls_failf(data, rr, "rustls: failed to configure ECH GREASE");
-      return CURLE_SSL_CONNECT_ERROR;
+      result = CURLE_SSL_CONNECT_ERROR;
+      goto cleanup;
     }
+    return CURLE_OK;
   }
-  else if(data->set.tls_ech & CURLECH_CLA_CFG
+
+  if(data->set.tls_ech & CURLECH_CLA_CFG
        && data->set.str[STRING_ECH_CONFIG]) {
     const char *b64 = data->set.str[STRING_ECH_CONFIG];
     size_t decode_result;
-    rustls_result rr;
     if(!b64) {
       infof(data, "rustls: ECHConfig from command line empty");
-      return CURLE_SSL_CONNECT_ERROR;
+      result = CURLE_SSL_CONNECT_ERROR;
+      goto cleanup;
     }
     /* rustls-ffi expects the raw TLS encoded ECHConfigList bytes */
     decode_result = Curl_base64_decode(b64, &ech_config, &ech_config_len);
     if(decode_result || !ech_config) {
       infof(data, "rustls: cannot base64 decode ECHConfig from command line");
-      return CURLE_SSL_CONNECT_ERROR;
+      result = CURLE_SSL_CONNECT_ERROR;
+      goto cleanup;
     }
-    rr = rustls_client_config_builder_enable_ech(builder,
-                                                 ech_config,
-                                                 ech_config_len,
-                                                 hpke);
-    if(rr != RUSTLS_RESULT_OK) {
-      rustls_failf(data, rr, "rustls: failed to configure ECH");
-      return CURLE_SSL_CONNECT_ERROR;
+  }
+  else {
+    if(connssl->peer.hostname) {
+      dns = Curl_fetch_addr(
+        data,
+        connssl->peer.hostname,
+        connssl->peer.port);
+    }
+    if(!dns) {
+      failf(data, "rustls: ECH requested but no DNS info available");
+      result = CURLE_SSL_CONNECT_ERROR;
+      goto cleanup;
     }
+    rinfo = dns->hinfo;
+    if(!rinfo || !rinfo->echconfiglist) {
+      failf(data, "rustls: ECH requested but no ECHConfig available");
+      result = CURLE_SSL_CONNECT_ERROR;
+      goto cleanup;
+    }
+    ech_config = rinfo->echconfiglist;
+    ech_config_len = rinfo->echconfiglist_len;
   }
 
-  return CURLE_OK;
+  rr = rustls_client_config_builder_enable_ech(builder,
+                                               ech_config,
+                                               ech_config_len,
+                                               hpke);
+  if(rr != RUSTLS_RESULT_OK) {
+    rustls_failf(data, rr, "rustls: failed to configure ECH");
+    result = CURLE_SSL_CONNECT_ERROR;
+    goto cleanup;
+  }
+cleanup:
+  if(dns) {
+    Curl_resolv_unlink(data, &dns);
+  }
+  return result;
 }
 #endif /* USE_ECH */
 
@@ -1014,7 +1050,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
 
 #if defined(USE_ECH)
   if(ECH_ENABLED(data)) {
-    result = init_config_builder_ech(data, config_builder);
+    result = init_config_builder_ech(data, connssl, config_builder);
     if(result != CURLE_OK && data->set.tls_ech & CURLECH_HARD) {
       rustls_client_config_builder_free(config_builder);
       return result;