git: d6678851308d - main - libfetch: Apply timeout to SSL handshake

From: Dag-Erling Smørgrav <des_at_FreeBSD.org>
Date: Mon, 02 Nov 2026 12:38:03 UTC
The branch main has been updated by des:

URL: https://cgit.FreeBSD.org/src/commit/?id=d6678851308d3cec767f07b0712a429cdc5895de

commit d6678851308d3cec767f07b0712a429cdc5895de
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2026-11-02 12:27:53 +0000
Commit:     Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2026-11-02 12:28:39 +0000

    libfetch: Apply timeout to SSL handshake
    
    When the SSL handshake cannot proceed because the server is slow to
    respond, poll() with a timeout instead of spinning.
    
    MFC after:      1 week
    PR:             298962
    Event:          Oslo Hackathon 2026
    Reviewed by:    markj
    Differential Revision:  https://reviews.freebsd.org/D60975
---
 lib/libfetch/common.c | 42 +++++++++++++++++++++++++++++++++++++++---
 1 file changed, 39 insertions(+), 3 deletions(-)

diff --git a/lib/libfetch/common.c b/lib/libfetch/common.c
index da0ea6f8aaeb..c15b22a41603 100644
--- a/lib/libfetch/common.c
+++ b/lib/libfetch/common.c
@@ -1219,7 +1219,9 @@ int
 fetch_ssl(conn_t *conn, const struct url *URL, int verbose)
 {
 #ifdef WITH_SSL
-	int ret, ssl_err;
+	struct timeval now, timeout, delta;
+	struct pollfd pfd;
+	int deltams, ret, ssl_err;
 	X509_NAME *name;
 	char *str;
 
@@ -1251,13 +1253,47 @@ fetch_ssl(conn_t *conn, const struct url *URL, int verbose)
 		return (-1);
 	}
 #endif
+	if (fetchTimeout > 0) {
+		gettimeofday(&timeout, NULL);
+		timeout.tv_sec += fetchTimeout;
+	}
+	deltams = INFTIM;
+	pfd.fd = conn->sd;
 	while ((ret = SSL_connect(conn->ssl)) == -1) {
 		ssl_err = SSL_get_error(conn->ssl, ret);
-		if (ssl_err != SSL_ERROR_WANT_READ &&
-		    ssl_err != SSL_ERROR_WANT_WRITE) {
+		switch (ssl_err) {
+		case SSL_ERROR_WANT_READ:
+			pfd.events = POLLIN;
+			break;
+		case SSL_ERROR_WANT_WRITE:
+			pfd.events = POLLOUT;
+			break;
+		default:
 			ERR_print_errors_fp(stderr);
 			return (-1);
 		}
+		/* check what's left of our timeout */
+		if (fetchTimeout > 0) {
+			gettimeofday(&now, NULL);
+			if (!timercmp(&timeout, &now, >)) {
+				errno = ETIMEDOUT;
+				fetch_syserr();
+				return (-1);
+			}
+			timersub(&timeout, &now, &delta);
+			deltams = delta.tv_sec * 1000 +
+			    delta.tv_usec / 1000;
+		}
+		ret = poll(&pfd, 1, deltams);
+		if (ret == 0) {
+			fetch_info("SSL handshake timed out");
+			return (-1);
+		}
+		if (ret < 0) {
+			if (errno == EINTR && fetchRestartCalls)
+				continue;
+			return (-1);
+		}
 	}
 	conn->ssl_cert = SSL_get_peer_certificate(conn->ssl);