blob: 0b32a4fde24202cbbd78dcdb4fb89e25a26feea9 [file] [log] [blame]
From a4af60bede7bd3dbf201e73b9a06c8823f49083b Mon Sep 17 00:00:00 2001
From: Avery Pennarun <apenwarr@gmail.com>
Date: Wed, 23 Apr 2014 00:09:16 -0400
Subject: [PATCH 1/2] tlsdate-helper: connect to all IP addresses for the host
in parallel.
For example, if a given host has both IPv4 and IPv6 addresses, and your
client device thinks it has a valid IPv6 route but that route doesn't work,
tlsdate-helper could delay for a very long time as it failed on the IPv6
addresses one by one. This way it will try the IPv4 addresses at the same
time, and just use the first socket that connects successfully, so there
is no delay.
---
src/tlsdate-helper.c | 144 ++++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 124 insertions(+), 20 deletions(-)
diff --git a/src/tlsdate-helper.c b/src/tlsdate-helper.c
index c96fe5e..1086625 100644
--- a/src/tlsdate-helper.c
+++ b/src/tlsdate-helper.c
@@ -95,9 +95,11 @@ know:
#include "polarssl/ctr_drbg.h"
#include "polarssl/ssl.h"
#else
+#include <fcntl.h>
+#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
-#include <netdb.h>
+#include <unistd.h>
#endif
static void
@@ -183,13 +185,131 @@ setup_proxy(BIO *ssl)
BIO_push(ssl, bio);
}
+static const char *
+sockaddr_to_str(void *_sa) {
+ static char out[128];
+ struct sockaddr_in *sin = _sa;
+ return inet_ntop(sin->sin_family, &sin->sin_addr, out, sizeof(out));
+}
+
+static const char *
+sock_peername(int sock) {
+ struct sockaddr_storage sa;
+ socklen_t salen = sizeof(sa);
+
+ if (getpeername(sock, (struct sockaddr *)&sa, &salen) < 0) {
+ perror("getpeername");
+ return "(unknown)";
+ }
+ return sockaddr_to_str((struct sockaddr *)&sa);
+}
+
+static int
+parallel_connect(struct addrinfo *ai) {
+ struct addrinfo *cai;
+ int socks[64], nsocks = 0, connsock = -1, i;
+
+ // connect a socket for each possible address
+ for (cai = ai; cai; cai = cai->ai_next) {
+ int sock = -1;
+ if (nsocks >= sizeof(socks)/sizeof(socks[0])) break;
+
+ socks[nsocks++] = sock = socket(cai->ai_family, SOCK_STREAM, 0);
+ if (sock < 0) {
+ perror("socket");
+ nsocks--;
+ continue;
+ }
+
+ if (fcntl(sock, F_SETFL, (long)O_NONBLOCK) < 0) {
+ perror("fcntl(O_NONBLOCK)");
+ close(sock);
+ nsocks--;
+ continue;
+ }
+
+ verb("V: connecting fd#%d to %s\n", sock, sockaddr_to_str(cai->ai_addr));
+ if (!connect(sock, cai->ai_addr, cai->ai_addrlen)) {
+ // instant success!
+ connsock = sock;
+ goto done;
+ }
+ if (errno != EINPROGRESS) {
+ perror("connect");
+ close(sock);
+ nsocks--;
+ continue;
+ }
+ }
+
+ // wait for the sockets to finish connectin
+ while (nsocks > 0) {
+ int fd_max = 0, readyfds;
+ fd_set wfd;
+ struct timeval tv = { .tv_sec = 10, .tv_usec = 0 };
+
+ verb("V: Waiting for %d server IP addresses...\n", nsocks);
+
+ FD_ZERO(&wfd);
+ for (i = 0; i < nsocks; i++) {
+ if (socks[i] > fd_max) fd_max = socks[i];
+ FD_SET(socks[i], &wfd);
+ }
+
+ readyfds = select(fd_max + 1, NULL, &wfd, NULL, &tv);
+ if (readyfds < 0) {
+ perror("select(connect)");
+ break;
+ } else if (readyfds == 0) {
+ fprintf(stderr, "timed out waiting for TCP connection\n");
+ break;
+ }
+
+ for (i = 0; i < nsocks; i++) {
+ if (FD_ISSET(socks[i], &wfd)) {
+ int v = 0;
+ socklen_t vlen = sizeof(v);
+ if (getsockopt(socks[i], SOL_SOCKET, SO_ERROR, &v, &vlen) < 0) {
+ perror("getsockopt(SO_ERROR)");
+ close(socks[i]);
+ socks[i] = socks[--nsocks];
+ break;
+ }
+ if (v) {
+ fprintf(stderr, "connect(fd#%d): %s\n", socks[i], strerror(v));
+ close(socks[i]);
+ socks[i] = socks[--nsocks];
+ } else {
+ verb("V: connected fd#%d to %s\n",
+ socks[i], sock_peername(socks[i]));
+ connsock = socks[i];
+ goto done;
+ }
+ break;
+ }
+ }
+ }
+
+done:
+ if (connsock >= 0 && fcntl(connsock, F_SETFL, 0) < 0) {
+ perror("fcntl(!O_NONBLOCK)");
+ connsock = -1;
+ }
+ for (i = 0; i < nsocks; i++) {
+ if (socks[i] != connsock) {
+ close(socks[i]);
+ }
+ }
+ return connsock;
+}
+
static BIO *
make_ssl_bio(SSL_CTX *ctx, const char *host, const char *port)
{
BIO *con = NULL;
BIO *ssl = NULL;
int err, sock = -1;
- struct addrinfo *ai = NULL, *cai = NULL;
+ struct addrinfo *ai = NULL;
struct addrinfo hints = {
.ai_flags = AI_ADDRCONFIG,
.ai_family = AF_UNSPEC,
@@ -200,24 +320,8 @@ make_ssl_bio(SSL_CTX *ctx, const char *host, const char *port)
fprintf(stderr, "getaddrinfo(%s): %s\n", host, gai_strerror(err));
goto error;
}
-
- for (cai = ai; cai; cai = cai->ai_next) {
- sock = socket(cai->ai_family, SOCK_STREAM, 0);
- if (sock < 0) {
- perror("socket");
- continue;
- }
-
- if (connect(sock, cai->ai_addr, cai->ai_addrlen) != 0) {
- perror("connect");
- close(sock);
- sock = -1;
- continue;
- }
-
- break;
- }
- if (ai) freeaddrinfo(ai);
+ sock = parallel_connect(ai);
+ freeaddrinfo(ai);
if (sock < 0) goto error;
if (!(con = BIO_new_fd(sock, 1)))
--
1.9.1.423.g4596e3a