From 4bd20889fc3803b42647eaa4902e5257c508b363 Mon Sep 17 00:00:00 2001 From: Dmitry Karpov Date: Thu, 3 Jun 2021 23:56:37 +0200 Subject: [PATCH] ares: always store IPv6 addresses first Trying dual-stack on some embedded platform, I noticed that quite frequently (20%) libCurl starts from IPv4 regardless the Happy Eyeballs timeout value. After debugging this issue, I noticed that this happens if c-ares resolver response for IPv6 family comes before IPv4 (which was randomly happening in my tests). In such cases, because libCurl puts the last resolver response on top of the address list, when IPv4 resolver response comes after IPv6 one - the IPv4 family starts the connection phase instead of IPv6 family. The solution for this issue is to always put IPv6 addresses on top of the address list, regardless the order of resolver responses. Bug: https://curl.se/mail/lib-2021-06/0003.html Closes #7188 --- lib/asyn-ares.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index 782784735..9ee307beb 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -493,17 +493,31 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data, static void compound_results(struct thread_data *res, struct Curl_addrinfo *ai) { - struct Curl_addrinfo *ai_tail; if(!ai) return; - ai_tail = ai; - while(ai_tail->ai_next) - ai_tail = ai_tail->ai_next; +#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */ + if(res->temp_ai && res->temp_ai->ai_family == PF_INET6) { + /* We have results already, put the new IPv6 entries at the head of the + list. */ + struct Curl_addrinfo *temp_ai_tail = res->temp_ai; - /* Add the new results to the list of old results. */ - ai_tail->ai_next = res->temp_ai; - res->temp_ai = ai; + while(temp_ai_tail->ai_next) + temp_ai_tail = temp_ai_tail->ai_next; + + temp_ai_tail->ai_next = ai; + } + else +#endif /* CURLRES_IPV6 */ + { + /* Add the new results to the list of old results. */ + struct Curl_addrinfo *ai_tail = ai; + while(ai_tail->ai_next) + ai_tail = ai_tail->ai_next; + + ai_tail->ai_next = res->temp_ai; + res->temp_ai = ai; + } } /*