← Commit list

3341da7dc2

Joshua Rogers
Joshua Rogers
2026-09-11
SUCCESS Duration: 402s

Subject

happy eyeballs: bound work from many duplicate/failing addresses

Binary

No binary (build failed)

Architecture & Compiler Flags

Architecture: x86_64-linux-gnu Compiler: gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0

—

Build Log

(log yok)

Commit Diff GitHub →

lib/cf-ip-happy.c+34 -13
@@ -267,6 +267,7 @@ static CURLcode cf_ip_attempt_connect(struct cf_ip_attempt *a,
struct cf_ip_ballers {
struct cf_ip_attempt *running;
+ struct cf_ip_attempt **running_tail; /* &running or &(last->next) */
struct cf_ip_attempt *winner;
struct cf_ai_iter addr_iter;
#ifdef USE_IPV6
@@ -278,10 +279,12 @@ struct cf_ip_ballers {
cf_ip_connect_create *cf_create; /* for creating cf */
struct curltime last_attempt_started;
timediff_t attempt_delay_ms;
+ CURLcode last_dead_result; /* result of last discarded hard failure */
int last_attempt_ai_family;
uint32_t max_concurrent;
uint8_t transport_peer;
uint8_t tunnel_transport;
+ BIT(had_dead); /* a hard failure was discarded */
};
static CURLcode cf_ip_attempt_restart(struct cf_ip_attempt *a,
@@ -322,6 +325,7 @@ static void cf_ip_ballers_clear(struct Curl_easy *data,
bs->running = a->next;
cf_ip_attempt_free(a, data);
}
+ bs->running_tail = &bs->running;
cf_ip_attempt_free(bs->winner, data);
bs->winner = NULL;
Curl_peer_unlink(&bs->origin);
@@ -340,6 +344,7 @@ static CURLcode cf_ip_ballers_init(struct cf_ip_ballers *bs,
uint32_t max_concurrent)
{
memset(bs, 0, sizeof(*bs));
+ bs->running_tail = &bs->running;
bs->cf_create = get_cf_create(transport_peer, !!tunnel_peer);
if(!bs->cf_create) {
failf(data, "unsupported transport type %u%s",
@@ -375,6 +380,8 @@ static void cf_ip_ballers_prune(struct cf_ip_ballers *bs,
a = *panchor;
if(!a->result && !a->connected) {
*panchor = a->next;
+ if(!a->next)
+ bs->running_tail = panchor;
a->next = NULL;
cf_ip_attempt_free(a, data);
--ongoing;
@@ -404,11 +411,17 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs,
return CURLE_OK;
evaluate:
+ if(Curl_timeleft_now_ms(data, Curl_pgrs_now(data)) < 0) {
+ failf(data, "Connection timeout after %" FMT_OFF_T " ms",
+ Curl_pgrs_since_ms(data, NULL, TIMER_STARTSINGLE));
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+
ongoing = inconclusive = 0;
/* check if a running baller connects now */
VERBOSE(i = -1);
- for(panchor = &bs->running; *panchor; panchor = &(*panchor)->next) {
+ for(panchor = &bs->running; *panchor;) {
VERBOSE(++i);
a = *panchor;
a->result = cf_ip_attempt_connect(a, data, connected);
@@ -425,13 +438,27 @@ evaluate:
bs->running = a->next;
cf_ip_attempt_free(a, data);
}
+ bs->running_tail = &bs->running;
return CURLE_OK;
}
/* still running */
++ongoing;
+ panchor = &(*panchor)->next;
}
- else if(a->inconclusive) /* failed, but inconclusive */
+ else if(a->inconclusive) { /* failed, but inconclusive */
++inconclusive;
+ panchor = &(*panchor)->next;
+ }
+ else {
+ /* hard failure, discard right away */
+ *panchor = a->next;
+ if(!a->next)
+ bs->running_tail = panchor;
+ a->next = NULL;
+ bs->last_dead_result = a->result;
+ bs->had_dead = TRUE;
+ cf_ip_attempt_free(a, data);
+ }
}
if(bs->running)
CURL_TRC_CF(data, cf, "checked connect attempts: "
@@ -505,10 +532,9 @@ evaluate:
DEBUGASSERT(a);
/* append to running list */
- panchor = &bs->running;
- while(*panchor)
- panchor = &(*panchor)->next;
- *panchor = a;
+ *bs->running_tail = a;
+ a->next = NULL;
+ bs->running_tail = &a->next;
bs->last_attempt_started = *pnow;
bs->last_attempt_ai_family = ai_family;
/* and run everything again */
@@ -548,13 +574,8 @@ evaluate:
else if(!ongoing && dns_resolved) {
/* no more addresses, no inconclusive attempts */
CURL_TRC_CF(data, cf, "no more attempts to try");
- result = CURLE_COULDNT_CONNECT;
- VERBOSE(i = 0);
- for(a = bs->running; a; a = a->next) {
- CURL_TRC_CF(data, cf, "baller %d: result=%d", i, (int)a->result);
- if(a->result)
- result = a->result;
- }
+ DEBUGASSERT(!bs->running);
+ result = bs->had_dead ? bs->last_dead_result : CURLE_COULDNT_CONNECT;
}
}
lib/vdns/asyn-ares.c+23 -1
@@ -401,6 +401,22 @@ CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id,
return result;
}
+#define CARES_MAX_ADDRS 256
+
+static bool async_ares_addr_seen(const struct Curl_addrinfo *cafirst,
+ int family, size_t ss_size,
+ const void *addr)
+{
+ const struct Curl_addrinfo *ca;
+ for(ca = cafirst; ca; ca = ca->ai_next) {
+ if((ca->ai_family == family) &&
+ ((size_t)ca->ai_addrlen == ss_size) &&
+ !memcmp(ca->ai_addr, addr, ss_size))
+ return TRUE;
+ }
+ return FALSE;
+}
+
/*
* async_ares_node2addr() converts an address list provided by c-ares
* to an internal libcurl compatible list.
@@ -413,8 +429,9 @@ static struct Curl_addrinfo *async_ares_node2addr(
struct Curl_addrinfo *cafirst = NULL;
struct Curl_addrinfo *calast = NULL;
int error = 0;
+ unsigned int naddrs = 0;
- for(ai = node; ai; ai = ai->ai_next) {
+ for(ai = node; ai && (naddrs < CARES_MAX_ADDRS); ai = ai->ai_next) {
size_t ss_size;
struct Curl_addrinfo *ca;
/* ignore elements with unsupported address family,
@@ -436,6 +453,10 @@ static struct Curl_addrinfo *async_ares_node2addr(
if((size_t)ai->ai_addrlen < ss_size)
continue;
+ /* ignore duplicate addresses already collected */
+ if(async_ares_addr_seen(cafirst, ai->ai_family, ss_size, ai->ai_addr))
+ continue;
+
ca = curlx_malloc(sizeof(struct Curl_addrinfo) + ss_size);
if(!ca) {
error = EAI_MEMORY;
@@ -465,6 +486,7 @@ static struct Curl_addrinfo *async_ares_node2addr(
if(calast)
calast->ai_next = ca;
calast = ca;
+ ++naddrs;
}
/* if we failed, destroy the Curl_addrinfo list */