Hi,

Dovecot ignores EINPROGRESS on connect(2) for non-blocking fd.
This is wrong. After that, read(2) to fd (or write(2) to fd) fails
with ENOTCONN if the connection of fd is not completed.

The attached patch fixes this problem.

-- 
-- Name: SATOH Fumiyasu (fumiyas @ osstech co jp)
-- Business Home: http://www.OSSTech.co.jp/
-- Personal Home: http://www.SFO.jp/blog/
changeset:   12875:43012b126401
branch:      fumiyas
tag:         tip
user:        SATOH Fumiyasu <fumi...@osstech.co.jp>
date:        Sat Jul 30 14:17:06 2011 +0900
summary:     net_connect_*(): Wait for fd to complete connect(2) when fd is non-blocking

diff -r 86c6f58e5756 -r 43012b126401 src/lib/network.c
--- a/src/lib/network.c	Sat Jul 30 01:21:35 2011 +0900
+++ b/src/lib/network.c	Sat Jul 30 14:17:06 2011 +0900
@@ -5,6 +5,7 @@
 #include "fd-set-nonblock.h"
 #include "time-util.h"
 #include "network.h"
+#include "ioloop.h"
 
 #include <stdlib.h>
 #include <unistd.h>
@@ -138,6 +139,11 @@
 	return 0;
 }
 
+static void net_connect_in_progress(struct ioloop *ioloop)
+{
+	io_loop_stop(ioloop);
+}
+
 #ifdef __FreeBSD__
 static int
 net_connect_ip_full_freebsd(const struct ip_addr *ip, unsigned int port,
@@ -207,16 +213,31 @@
 	/* connect */
 	sin_set_ip(&so, ip);
 	sin_set_port(&so, port);
-	ret = connect(fd, &so.sa, SIZEOF_SOCKADDR(so));
+	do {
+		ret = connect(fd, &so.sa, SIZEOF_SOCKADDR(so));
+	} while (ret < 0 && errno == EINTR);
+
+	if (ret >= 0) {
+		return fd;
+	}
 
 #ifndef WIN32
-	if (ret < 0 && errno != EINPROGRESS)
+	if (errno != EINPROGRESS && errno != EALREADY)
 #else
-	if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK)
+	if (WSAGetLastError() != WSAEWOULDBLOCK)
 #endif
 	{
-                close_keep_errno(fd);
+		close_keep_errno(fd);
 		return -1;
+	} else {
+		struct ioloop *ioloop;
+		struct io *io;
+
+		ioloop = io_loop_create();
+		io = io_add(fd, IO_WRITE, net_connect_in_progress, ioloop);
+		io_loop_run(ioloop);
+		io_remove(&io);
+		io_loop_destroy(&ioloop);
 	}
 
 	return fd;
@@ -286,10 +307,26 @@
 	net_set_nonblock(fd, TRUE);
 
 	/* connect */
-	ret = connect(fd, &sa.sa, sizeof(sa));
-	if (ret < 0 && errno != EINPROGRESS) {
-                close_keep_errno(fd);
+	do {
+		ret = connect(fd, &sa.sa, sizeof(sa));
+	} while (ret < 0 && errno == EINTR);
+
+	if (ret >= 0) {
+		return fd;
+	}
+
+	if (errno != EINPROGRESS && errno != EALREADY) {
+		close_keep_errno(fd);
 		return -1;
+	} else {
+		struct ioloop *ioloop;
+		struct io *io;
+
+		ioloop = io_loop_create();
+		io = io_add(fd, IO_WRITE, net_connect_in_progress, ioloop);
+		io_loop_run(ioloop);
+		io_remove(&io);
+		io_loop_destroy(&ioloop);
 	}
 
 	return fd;

Reply via email to