]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
ares: use ares_getaddrinfo()
authorDaniel Stenberg <daniel@haxx.se>
Tue, 10 Aug 2021 14:11:51 +0000 (16:11 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Wed, 11 Aug 2021 07:53:06 +0000 (09:53 +0200)
ares_getaddrinfo() is the getaddrinfo() cloned provided by c-ares, introduced
in version 1.16.0.

With older c-ares versions, curl invokes ares_gethostbyname() twice - once for
IPv4 and once for IPv6 to resolve both addresses, and then combines the
returned results.

Reported-by: jjandesmet
Fixes #7364
Closes #7552

lib/asyn-ares.c

index 839fabb86aa323be6351481ecd6ae64c9d44c7d5..0be66139562e05db3fcd4929dffb257293d5e22f 100644 (file)
@@ -86,7 +86,7 @@
 #include "memdebug.h"
 
 struct thread_data {
-  int num_pending; /* number of ares_gethostbyname() requests */
+  int num_pending; /* number of outstanding c-ares requests */
   struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares
                                     parts */
   int last_status;
@@ -490,6 +490,8 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
   return result;
 }
 
+#if ARES_VERSION < 0x011000 /* before 1.16.0 */
+
 /* Connects results to the list */
 static void compound_results(struct thread_data *res,
                              struct Curl_addrinfo *ai)
@@ -620,7 +622,97 @@ static void query_completed_cb(void *arg,  /* (struct connectdata *) */
     }
   }
 }
+#else
+/* c-ares 1.16.0 or later */
 
+/*
+ * ares2addr() converts an address list provided by c-ares to an internal
+ * libcurl compatible list
+ */
+static struct Curl_addrinfo *ares2addr(struct ares_addrinfo_node *node)
+{
+  /* traverse the ares_addrinfo_node list */
+  struct ares_addrinfo_node *ai;
+  struct Curl_addrinfo *cafirst = NULL;
+  struct Curl_addrinfo *calast = NULL;
+  int error = 0;
+
+  for(ai = node; ai != NULL; ai = ai->ai_next) {
+    size_t ss_size;
+    struct Curl_addrinfo *ca;
+    /* ignore elements with unsupported address family, */
+    /* settle family-specific sockaddr structure size.  */
+    if(ai->ai_family == AF_INET)
+      ss_size = sizeof(struct sockaddr_in);
+#ifdef ENABLE_IPV6
+    else if(ai->ai_family == AF_INET6)
+      ss_size = sizeof(struct sockaddr_in6);
+#endif
+    else
+      continue;
+
+    /* ignore elements without required address info */
+    if(!ai->ai_addr || !(ai->ai_addrlen > 0))
+      continue;
+
+    /* ignore elements with bogus address size */
+    if((size_t)ai->ai_addrlen < ss_size)
+      continue;
+
+    ca = malloc(sizeof(struct Curl_addrinfo) + ss_size);
+    if(!ca) {
+      error = EAI_MEMORY;
+      break;
+    }
+
+    /* copy each structure member individually, member ordering, */
+    /* size, or padding might be different for each platform.    */
+
+    ca->ai_flags     = ai->ai_flags;
+    ca->ai_family    = ai->ai_family;
+    ca->ai_socktype  = ai->ai_socktype;
+    ca->ai_protocol  = ai->ai_protocol;
+    ca->ai_addrlen   = (curl_socklen_t)ss_size;
+    ca->ai_addr      = NULL;
+    ca->ai_canonname = NULL;
+    ca->ai_next      = NULL;
+
+    ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
+    memcpy(ca->ai_addr, ai->ai_addr, ss_size);
+
+    /* if the return list is empty, this becomes the first element */
+    if(!cafirst)
+      cafirst = ca;
+
+    /* add this element last in the return list */
+    if(calast)
+      calast->ai_next = ca;
+    calast = ca;
+  }
+
+  /* if we failed, destroy the Curl_addrinfo list */
+  if(error) {
+    Curl_freeaddrinfo(cafirst);
+    cafirst = NULL;
+  }
+
+  return cafirst;
+}
+
+static void addrinfo_cb(void *arg, int status, int timeouts,
+                        struct ares_addrinfo *result)
+{
+  struct Curl_easy *data = (struct Curl_easy *)arg;
+  struct thread_data *res = data->state.async.tdata;
+  (void)timeouts;
+  if(ARES_SUCCESS == status) {
+    res->temp_ai = ares2addr(result->nodes);
+    res->last_status = CURL_ASYNC_SUCCESS;
+  }
+  res->num_pending--;
+}
+
+#endif
 /*
  * Curl_resolver_getaddrinfo() - when using ares
  *
@@ -658,6 +750,27 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
     /* initial status - failed */
     res->last_status = ARES_ENOTFOUND;
 
+#if ARES_VERSION >= 0x010601
+    {
+      struct ares_addrinfo_hints hints;
+      char service[12];
+      int pf = PF_INET;
+      memset(&hints, 0, sizeof(hints));
+#ifdef CURLRES_IPV6
+      if(Curl_ipv6works(data))
+        /* The stack seems to be IPv6-enabled */
+        pf = PF_UNSPEC;
+#endif /* CURLRES_IPV6 */
+      hints.ai_family = pf;
+      hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)?
+        SOCK_STREAM : SOCK_DGRAM;
+      msnprintf(service, sizeof(service), "%d", port);
+      res->num_pending = 1;
+      ares_getaddrinfo((ares_channel)data->state.async.resolver, hostname,
+                       service, &hints, addrinfo_cb, data);
+    }
+#else
+
 #if ARES_VERSION >= 0x010601
     /* IPv6 supported by c-ares since 1.6.1 */
     if(Curl_ipv6works(data)) {
@@ -680,7 +793,7 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
                          hostname, PF_INET,
                          query_completed_cb, data);
     }
-
+#endif
     *waitp = 1; /* expect asynchronous response */
   }
   return NULL; /* no struct yet */