Просмотр исходного кода

fix simultaneously connecting to multiple hosts in non-IPv6 builds

we need to deep-copy the struct hostent data, as otherwise the
concurrent connects will overwrite each other's lookup results.

this is a rather hypothetical fix, as the bug currently affects only
channels connecting two IMAP accounts, and only if the first host's
first address asynchronously fails to connect.
Oswald Buddenhagen 4 лет назад
Родитель
Сommit
813ad67c56
2 измененных файлов с 30 добавлено и 24 удалено
  1. 29 23
      src/socket.c
  2. 1 1
      src/socket.h

+ 29 - 23
src/socket.c

@@ -430,6 +430,32 @@ socket_close_internal( conn_t *sock )
 	sock->fd = -1;
 }
 
+#ifndef HAVE_IPV6
+struct addr_info {
+	struct addr_info *ai_next;
+	struct sockaddr_in ai_addr[1];
+};
+
+#define freeaddrinfo(ai) free( ai )
+
+static struct addr_info *
+init_addrinfo( struct hostent *he )
+{
+	uint naddr = 0;
+	for (char **addr = he->h_addr_list; *addr; addr++)
+		naddr++;
+	struct addr_info *caddr = nfcalloc( naddr * sizeof(struct addrinfo) );
+	struct addr_info *ret, **caddrp = &ret;
+	for (char **addr = he->h_addr_list; *addr; addr++, caddr++) {
+		caddr->ai_addr->sin_family = AF_INET;
+		memcpy( &caddr->ai_addr->sin_addr.s_addr, *addr, sizeof(struct in_addr) );
+		*caddrp = caddr;
+		caddrp = &caddr->ai_next;
+	}
+	return ret;
+}
+#endif
+
 void
 socket_connect( conn_t *sock, void (*cb)( int ok, void *aux ) )
 {
@@ -479,8 +505,6 @@ socket_connect( conn_t *sock, void (*cb)( int ok, void *aux ) )
 			return;
 		}
 		info( "\vok\n" );
-
-		sock->curr_addr = sock->addrs;
 #else
 		struct hostent *he;
 
@@ -493,8 +517,9 @@ socket_connect( conn_t *sock, void (*cb)( int ok, void *aux ) )
 		}
 		info( "\vok\n" );
 
-		sock->curr_addr = he->h_addr_list;
+		sock->addrs = init_addrinfo( he );
 #endif
+		sock->curr_addr = sock->addrs;
 		socket_connect_one( sock );
 	}
 }
@@ -506,16 +531,10 @@ socket_connect_one( conn_t *sock )
 #ifdef HAVE_IPV6
 	struct addrinfo *ai;
 #else
-	struct {
-		struct sockaddr_in ai_addr[1];
-	} ai[1];
+	struct addr_info *ai;
 #endif
 
-#ifdef HAVE_IPV6
 	if (!(ai = sock->curr_addr)) {
-#else
-	if (!*sock->curr_addr) {
-#endif
 		error( "No working address found for %s\n", sock->conf->host );
 		socket_connect_bail( sock );
 		return;
@@ -532,11 +551,6 @@ socket_connect_one( conn_t *sock )
 #endif
 	{
 		struct sockaddr_in *in = ((struct sockaddr_in *)ai->ai_addr);
-#ifndef HAVE_IPV6
-		memset( in, 0, sizeof(*in) );
-		in->sin_family = AF_INET;
-		in->sin_addr.s_addr = *((int *)*sock->curr_addr);
-#endif
 		in->sin_port = htons( sock->conf->port );
 		nfasprintf( &sock->name, "%s (%s:%hu)",
 		            sock->conf->host, inet_ntoa( in->sin_addr ), sock->conf->port );
@@ -579,11 +593,7 @@ socket_connect_next( conn_t *conn )
 	sys_error( "Cannot connect to %s", conn->name );
 	free( conn->name );
 	conn->name = 0;
-#ifdef HAVE_IPV6
 	conn->curr_addr = conn->curr_addr->ai_next;
-#else
-	conn->curr_addr++;
-#endif
 	socket_connect_one( conn );
 }
 
@@ -597,12 +607,10 @@ socket_connect_failed( conn_t *conn )
 static void
 socket_connected( conn_t *conn )
 {
-#ifdef HAVE_IPV6
 	if (conn->addrs) {
 		freeaddrinfo( conn->addrs );
 		conn->addrs = 0;
 	}
-#endif
 	conf_notifier( &conn->notify, 0, POLLIN );
 	socket_expect_read( conn, 0 );
 	conn->state = SCK_READY;
@@ -612,12 +620,10 @@ socket_connected( conn_t *conn )
 static void
 socket_cleanup_names( conn_t *conn )
 {
-#ifdef HAVE_IPV6
 	if (conn->addrs) {
 		freeaddrinfo( conn->addrs );
 		conn->addrs = 0;
 	}
-#endif
 	free( conn->name );
 	conn->name = 0;
 }

+ 1 - 1
src/socket.h

@@ -73,7 +73,7 @@ typedef struct {
 #ifdef HAVE_IPV6
 	struct addrinfo *addrs, *curr_addr; /* needed during connect */
 #else
-	char **curr_addr; /* needed during connect */
+	struct addr_info *addrs, *curr_addr; /* needed during connect */
 #endif
 	char *name;
 #ifdef HAVE_LIBSSL