]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
c-ares: really lazy init channel
authorStefan Eissing <stefan@eissing.org>
Thu, 24 Apr 2025 10:18:33 +0000 (12:18 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 24 Apr 2025 12:07:01 +0000 (14:07 +0200)
Only initialize the c-ares channel when we start resolving and not
alreads when the application sets `CURLOPT_DNS_SERVERS` and friends.

Creating an ares channel takes considerable time and when we have the
DNS information for a transfer already cached, we do not need it.

Closes #17167

lib/asyn-ares.c
lib/asyn.h
lib/easy.c
lib/setopt.c

index 58dde9309aea5147ab07ac4fda18214053f86634..6d70976460c66792391d1035dc7d14b85dba6634 100644 (file)
 
 static int ares_ver = 0;
 
+static CURLcode async_ares_set_dns_servers(struct Curl_easy *data,
+                                           bool reset_on_null);
+
 /*
  * Curl_async_global_init() - the generic low-level asynchronous name
  * resolve API. Called from curl_global_init() to initialize global resolver
@@ -159,6 +162,8 @@ static CURLcode async_ares_init(struct Curl_easy *data)
   int status;
   struct ares_options options;
   int optmask = ARES_OPT_SOCK_STATE_CB;
+  CURLcode rc = CURLE_OK;
+
   options.sock_state_cb = sock_state_cb;
   options.sock_state_cb_data = data;
 
@@ -181,25 +186,35 @@ static CURLcode async_ares_init(struct Curl_easy *data)
   status = ares_init_options(&ares->channel, &options, optmask);
   if(status != ARES_SUCCESS) {
     ares->channel = NULL;
-    if(status == ARES_ENOMEM)
-      return CURLE_OUT_OF_MEMORY;
-    else
-      return CURLE_FAILED_INIT;
+    rc = (status == ARES_ENOMEM) ?
+         CURLE_OUT_OF_MEMORY : CURLE_FAILED_INIT;
+    goto out;
   }
-#if defined(CURLDEBUG) && defined(HAVE_CARES_PORTS_CSV)
-  else {
-    const char *env = getenv("CURL_DNS_SERVER");
-    if(env) {
-      int rc = ares_set_servers_ports_csv(ares->channel, env);
-      if(rc)
-        infof(data, "ares_set_servers_ports_csv failed: %d", rc);
-    }
-  }
-#endif
 
-  return CURLE_OK;
-  /* make sure that all other returns from this function should destroy the
-     ares channel before returning error! */
+  rc = async_ares_set_dns_servers(data, FALSE);
+  if(rc && rc != CURLE_NOT_BUILT_IN)
+    goto out;
+
+  rc = Curl_async_ares_set_dns_interface(data);
+  if(rc && rc != CURLE_NOT_BUILT_IN)
+    goto out;
+
+  rc = Curl_async_ares_set_dns_local_ip4(data);
+  if(rc && rc != CURLE_NOT_BUILT_IN)
+    goto out;
+
+  rc = Curl_async_ares_set_dns_local_ip6(data);
+  if(rc && rc != CURLE_NOT_BUILT_IN)
+    goto out;
+
+  rc = CURLE_OK;
+
+out:
+  if(rc && ares->channel) {
+    ares_destroy(ares->channel);
+    ares->channel = NULL;
+  }
+  return rc;
 }
 
 static CURLcode async_ares_init_lazy(struct Curl_easy *data)
@@ -794,43 +809,39 @@ struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
   return NULL; /* no struct yet */
 }
 
-CURLcode Curl_set_dns_servers(struct Curl_easy *data,
-                              char *servers)
+/* Set what DNS server are is to use. This is called in 2 situations:
+ * 1. when the application does `CURLOPT_DNS_SERVERSĀ“ and passing NULL
+ *    means any previous set value should be unset. Which means
+ *    we need to destroy and create the are channel anew, if there is one.
+ * 2. When we lazy init the ares channel and NULL means that there
+ *    are no preferences and we do not reset any existing channel. */
+static CURLcode async_ares_set_dns_servers(struct Curl_easy *data,
+                                           bool reset_on_null)
 {
   struct async_ares_ctx *ares = &data->state.async.ares;
   CURLcode result = CURLE_NOT_BUILT_IN;
-  int ares_result;
+  const char *servers = data->set.str[STRING_DNS_SERVERS];
+  int ares_result = ARES_SUCCESS;
+
+#if defined(CURLDEBUG) && defined(HAVE_CARES_SERVERS_CSV)
+  if(getenv("CURL_DNS_SERVER"))
+    servers = getenv("CURL_DNS_SERVER");
+#endif
 
-  /* If server is NULL, this purges all DNS servers from c-ares. Reset it to
-   * default.
-   */
   if(!servers) {
-    Curl_async_destroy(data);
-    result = async_ares_init_lazy(data);
-    if(!result) {
-      /* this now needs to restore the other options set to c-ares */
-      if(data->set.str[STRING_DNS_INTERFACE])
-        (void)Curl_set_dns_interface(data,
-                                     data->set.str[STRING_DNS_INTERFACE]);
-      if(data->set.str[STRING_DNS_LOCAL_IP4])
-        (void)Curl_set_dns_local_ip4(data,
-                                     data->set.str[STRING_DNS_LOCAL_IP4]);
-      if(data->set.str[STRING_DNS_LOCAL_IP6])
-        (void)Curl_set_dns_local_ip6(data,
-                                     data->set.str[STRING_DNS_LOCAL_IP6]);
+    if(reset_on_null) {
+      Curl_async_destroy(data);
     }
-    return result;
+    return CURLE_OK;
   }
 
 #ifdef HAVE_CARES_SERVERS_CSV
-  result = async_ares_init_lazy(data);
-  if(result)
-    return result;
-
+  /* if channel is not there, this is just a parameter check */
+  if(ares->channel)
 #ifdef HAVE_CARES_PORTS_CSV
-  ares_result = ares_set_servers_ports_csv(ares->channel, servers);
+    ares_result = ares_set_servers_ports_csv(ares->channel, servers);
 #else
-  ares_result = ares_set_servers_csv(ares->channel, servers);
+    ares_result = ares_set_servers_csv(ares->channel, servers);
 #endif
   switch(ares_result) {
   case ARES_SUCCESS:
@@ -854,21 +865,23 @@ CURLcode Curl_set_dns_servers(struct Curl_easy *data,
   return result;
 }
 
-CURLcode Curl_set_dns_interface(struct Curl_easy *data,
-                                const char *interf)
+CURLcode Curl_async_ares_set_dns_servers(struct Curl_easy *data)
+{
+  return async_ares_set_dns_servers(data, TRUE);
+}
+
+CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data)
 {
 #ifdef HAVE_CARES_LOCAL_DEV
   struct async_ares_ctx *ares = &data->state.async.ares;
-  CURLcode result;
+  const char *interf = data->set.str[STRING_DNS_INTERFACE];
 
   if(!interf)
     interf = "";
 
-  result = async_ares_init_lazy(data);
-  if(result)
-    return result;
-
-  ares_set_local_dev(ares->channel, interf);
+  /* if channel is not there, this is just a parameter check */
+  if(ares->channel)
+    ares_set_local_dev(ares->channel, interf);
 
   return CURLE_OK;
 #else /* c-ares version too old! */
@@ -878,13 +891,12 @@ CURLcode Curl_set_dns_interface(struct Curl_easy *data,
 #endif
 }
 
-CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
-                                const char *local_ip4)
+CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data)
 {
 #ifdef HAVE_CARES_SET_LOCAL
   struct async_ares_ctx *ares = &data->state.async.ares;
   struct in_addr a4;
-  CURLcode result;
+  const char *local_ip4 = data->set.str[STRING_DNS_LOCAL_IP4];
 
   if((!local_ip4) || (local_ip4[0] == 0)) {
     a4.s_addr = 0; /* disabled: do not bind to a specific address */
@@ -896,11 +908,9 @@ CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
     }
   }
 
-  result = async_ares_init_lazy(data);
-  if(result)
-    return result;
-
-  ares_set_local_ip4(ares->channel, ntohl(a4.s_addr));
+  /* if channel is not there yet, this is just a parameter check */
+  if(ares->channel)
+    ares_set_local_ip4(ares->channel, ntohl(a4.s_addr));
 
   return CURLE_OK;
 #else /* c-ares version too old! */
@@ -910,13 +920,12 @@ CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
 #endif
 }
 
-CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
-                                const char *local_ip6)
+CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data)
 {
 #if defined(HAVE_CARES_SET_LOCAL) && defined(USE_IPV6)
   struct async_ares_ctx *ares = &data->state.async.ares;
   unsigned char a6[INET6_ADDRSTRLEN];
-  CURLcode result;
+  const char *local_ip6 = data->set.str[STRING_DNS_LOCAL_IP6];
 
   if((!local_ip6) || (local_ip6[0] == 0)) {
     /* disabled: do not bind to a specific address */
@@ -929,11 +938,9 @@ CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
     }
   }
 
-  result = async_ares_init_lazy(data);
-  if(result)
-    return result;
-
-  ares_set_local_ip6(ares->channel, a6);
+  /* if channel is not there, this is just a parameter check */
+  if(ares->channel)
+    ares_set_local_ip6(ares->channel, a6);
 
   return CURLE_OK;
 #else /* c-ares version too old! */
index b3516dc4ebcf151225ae7acfe1909a8da7795ff7..1cc7175bebc17c6ad5bacc02200b274f64fbd8f6 100644 (file)
@@ -154,31 +154,17 @@ struct async_ares_ctx {
 void Curl_async_ares_shutdown(struct Curl_easy *data);
 void Curl_async_ares_destroy(struct Curl_easy *data);
 
-/*
- * Function provided by the resolver backend to set DNS servers to use.
- */
-CURLcode Curl_set_dns_servers(struct Curl_easy *data, char *servers);
+/* Set the DNS server to use by ares, from `data` settings. */
+CURLcode Curl_async_ares_set_dns_servers(struct Curl_easy *data);
 
-/*
- * Function provided by the resolver backend to set
- * outgoing interface to use for DNS requests
- */
-CURLcode Curl_set_dns_interface(struct Curl_easy *data,
-                                const char *interf);
+/* Set the DNS interfacer to use by ares, from `data` settings. */
+CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data);
 
-/*
- * Function provided by the resolver backend to set
- * local IPv4 address to use as source address for DNS requests
- */
-CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
-                                const char *local_ip4);
+/* Set the local ipv4 address to use by ares, from `data` settings. */
+CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data);
 
-/*
- * Function provided by the resolver backend to set
- * local IPv6 address to use as source address for DNS requests
- */
-CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
-                                const char *local_ip6);
+/* Set the local ipv6 address to use by ares, from `data` settings. */
+CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data);
 
 #endif /* CURLRES_ARES */
 
index 8e95fe7311b6cd8c9b25dcb52be4da7d5afe658e..7fc285f7b9d9a3a687fc4e3bb503b352afd469bf 100644 (file)
@@ -1050,28 +1050,6 @@ CURL *curl_easy_duphandle(CURL *d)
   }
 #endif
 
-#ifdef CURLRES_ARES
-  {
-    CURLcode rc;
-
-    rc = Curl_set_dns_servers(outcurl, data->set.str[STRING_DNS_SERVERS]);
-    if(rc && rc != CURLE_NOT_BUILT_IN)
-      goto fail;
-
-    rc = Curl_set_dns_interface(outcurl, data->set.str[STRING_DNS_INTERFACE]);
-    if(rc && rc != CURLE_NOT_BUILT_IN)
-      goto fail;
-
-    rc = Curl_set_dns_local_ip4(outcurl, data->set.str[STRING_DNS_LOCAL_IP4]);
-    if(rc && rc != CURLE_NOT_BUILT_IN)
-      goto fail;
-
-    rc = Curl_set_dns_local_ip6(outcurl, data->set.str[STRING_DNS_LOCAL_IP6]);
-    if(rc && rc != CURLE_NOT_BUILT_IN)
-      goto fail;
-  }
-#endif /* USE_ARES */
-
   outcurl->magic = CURLEASY_MAGIC_NUMBER;
 
   /* we reach this point and thus we are OK */
index 193ef66e53adec3354974fd746f07816e1441bb7..ee3fff8a1f6638101eeaaea5b8649ffd39e3b7e9 100644 (file)
@@ -2540,25 +2540,25 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
     result = Curl_setstropt(&data->set.str[STRING_DNS_SERVERS], ptr);
     if(result)
       return result;
-    return Curl_set_dns_servers(data, data->set.str[STRING_DNS_SERVERS]);
+    return Curl_async_ares_set_dns_servers(data);
 
   case CURLOPT_DNS_INTERFACE:
     result = Curl_setstropt(&data->set.str[STRING_DNS_INTERFACE], ptr);
     if(result)
       return result;
-    return Curl_set_dns_interface(data, data->set.str[STRING_DNS_INTERFACE]);
+    return Curl_async_ares_set_dns_interface(data);
 
   case CURLOPT_DNS_LOCAL_IP4:
     result = Curl_setstropt(&data->set.str[STRING_DNS_LOCAL_IP4], ptr);
     if(result)
       return result;
-    return Curl_set_dns_local_ip4(data, data->set.str[STRING_DNS_LOCAL_IP4]);
+    return Curl_async_ares_set_dns_local_ip4(data);
 
   case CURLOPT_DNS_LOCAL_IP6:
     result = Curl_setstropt(&data->set.str[STRING_DNS_LOCAL_IP6], ptr);
     if(result)
       return result;
-    return Curl_set_dns_local_ip6(data, data->set.str[STRING_DNS_LOCAL_IP6]);
+    return Curl_async_ares_set_dns_local_ip6(data);
 
 #endif
 #ifdef USE_UNIX_SOCKETS