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;