From 81a736a5acad5df8196f606305d8ec8aadaee85f Mon Sep 17 00:00:00 2001
From: Duncaen <mail@duncano.de>
Date: Thu, 20 Jun 2019 12:14:32 +0200
Subject: [PATCH] lib/fetch: cleanup happy eyeballs and add verbose logging

---
 lib/fetch/common.c | 60 +++++++++++++++++++++++++---------------------
 1 file changed, 33 insertions(+), 27 deletions(-)

diff --git a/lib/fetch/common.c b/lib/fetch/common.c
index 5716b7a5..526be86b 100644
--- a/lib/fetch/common.c
+++ b/lib/fetch/common.c
@@ -452,7 +452,7 @@ fetch_socks5(conn_t *conn, struct url *url, struct url *socks, int verbose)
 #define UNREACH_IPV6 0x01
 #define UNREACH_IPV4 0x10
 static int
-happy_eyeballs_connect(struct addrinfo *res0)
+happy_eyeballs_connect(struct addrinfo *res0, int verbose)
 {
 	static int unreach = 0;
 	struct pollfd *pfd;
@@ -474,26 +474,25 @@ happy_eyeballs_connect(struct addrinfo *res0)
 		case AF_INET: n4++; break;
 		}
 
-	if (n4+n6 == 0 || !(pfd = calloc(n4+n6, sizeof (struct pollfd))))
-		return -1;
-
 #ifdef FULL_DEBUG
 	fetch_info("got %d A and %d AAAA records", n4, n6);
 #endif
 
 	i4 = i6 = 0;
-	if (getenv("FORCE_IPV4"))
+	if (unreach & UNREACH_IPV6 || getenv("FORCE_IPV4"))
 		i6 = n6;
-	if (getenv("FORCE_IPV6"))
+	if (unreach & UNREACH_IPV4 || getenv("FORCE_IPV6"))
 		i4 = n4;
 
-	if (unreach & UNREACH_IPV6)
-		i6 = n6;
-	if (unreach & UNREACH_IPV4)
-		i4 = n4;
+	if (n6+n4 == 0 || i6+i4 == n6+n4) {
+		netdb_seterr(EAI_FAIL);
+		return -1;
+	}
 
-	if (i6+i4 == n6+n4)
-		goto error;
+	if (!(pfd = calloc(n4+n6, sizeof (struct pollfd)))) {
+		fetch_syserr();
+		return -1;
+	}
 
 	res = NULL;
 	for (;;) {
@@ -502,8 +501,9 @@ happy_eyeballs_connect(struct addrinfo *res0)
 		unsigned short family = 0;
 
 #ifdef FULL_DEBUG
-		fetch_info("happy eyeballs state: i4=%u n4=%u i6=%u n6=%u"
-		    " attempts=%u waiting=%u", i4, n4, i6, n6, attempts, waiting);
+		if (verbose)
+		    fetch_info("happy eyeballs state: i4=%u n4=%u i6=%u n6=%u"
+		        " attempts=%u waiting=%u", i4, n4, i6, n6, attempts, waiting);
 #endif
 
 		if (i6+i4 < n6+n4) {
@@ -523,9 +523,8 @@ happy_eyeballs_connect(struct addrinfo *res0)
 			}
 		} else {
 			/* no more connections to try */
-#ifdef FULL_DEBUG
-			fetch_info("attempted to connect to all addresses, waiting...");
-#endif
+			if (verbose)
+				fetch_info("attempted to connect to all addresses, waiting...");
 			timeout = fetchConnTimeout;
 			done = 1;
 			goto wait;
@@ -545,11 +544,13 @@ happy_eyeballs_connect(struct addrinfo *res0)
 				i++;
 			}
 		}
-		if (res == NULL)
-			goto error;
+		if (res == NULL) {
+			netdb_seterr(EAI_FAIL);
+			goto out;
+		}
 
 		if ((sd = socket(res->ai_family, res->ai_socktype | SOCK_NONBLOCK,
-			 res->ai_protocol)) == -1)
+		    res->ai_protocol)) == -1)
 			continue;
 
 		if (bindaddr != NULL && *bindaddr != '\0' &&
@@ -559,11 +560,14 @@ happy_eyeballs_connect(struct addrinfo *res0)
 			continue;
 		}
 
-		{
+		if (verbose) {
 			char hbuf[1025];
 			if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf), NULL,
-						0, NI_NUMERICHOST) == 0)
-				fetch_info("connecting to %s", hbuf);
+			    0, NI_NUMERICHOST) == 0)
+				fetch_info("connecting to %s:%d", hbuf,
+				    htons(res->ai_family == AF_INET
+					? ((struct sockaddr_in *)(res->ai_addr))->sin_port
+					: ((struct sockaddr_in6 *)(res->ai_addr))->sin6_port));
 		}
 
 		if (connect(sd, res->ai_addr, res->ai_addrlen) == -1) {
@@ -595,10 +599,9 @@ happy_eyeballs_connect(struct addrinfo *res0)
 		waiting++;
 wait:
 		if (!attempts) {
-error:
 			netdb_seterr(EAI_FAIL);
-			free(pfd);
-			return -1;
+			rv = -1;
+			goto out;
 		}
 		for (i = 0; i < attempts; i++) {
 			pfd[i].revents = pfd[i].events = 0;
@@ -637,8 +640,11 @@ error:
 		}
 		if (!waiting)
 			break;
+		else if (done)
+			goto wait;
 	}
 
+out:
 	for (i = 0; i < attempts; i++)
 		if ((rv == -1 || rv != pfd[i].fd) && pfd[i].fd != -1)
 			close(pfd[i].fd);
@@ -703,7 +709,7 @@ fetch_connect(struct url *url, int af, int verbose)
 	if (verbose)
 		fetch_info("connecting to %s:%d", connurl->host, connurl->port);
 
-	sd = happy_eyeballs_connect(res0);
+	sd = happy_eyeballs_connect(res0, verbose);
 	freeaddrinfo(res0);
 	if (sd == -1)
 		return (NULL);