Protect against spurious connection events.
authorEtienne Dechamps <etienne@edechamps.fr>
Fri, 27 Jun 2014 18:33:31 +0000 (19:33 +0100)
committerEtienne Dechamps <etienne@edechamps.fr>
Fri, 27 Jun 2014 18:39:30 +0000 (19:39 +0100)
The event loop does not guarantee that spurious write I/O events do not
happen; in fact, they are guaranteed to happen on Windows when
event_flush_output() is called. Because handle_meta_io() does not check
for spurious events, a metaconnection socket might appear connected even
though it's not, and will fail immediately when sending the ID request.

This commit fixes this issue by making handle_meta_io() check the
connection status before assuming the socket is connected. It seems that
the only reliable way to do that is to try to call connect() again and
look at the error code, which will be EISCONN if the socket is
connected, or EALREADY if it's not.

src/net_socket.c
src/utils.h

index 939aa9c..0a4dd9a 100644 (file)
@@ -401,6 +401,17 @@ static void handle_meta_io(void *data, int flags) {
        connection_t *c = data;
 
        if(c->status.connecting) {
+               /* The event loop does not protect against spurious events. Verify that we are actually connected. */
+               if (connect(c->socket, &c->address.sa, sizeof(c->address)) == 0)
+                       logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Error while connecting to %s (%s): redundant connect() unexpectedly succeeded", c->name, c->hostname);
+               else if (!sockisconn(sockerrno)) {
+                       if (!sockalready(sockerrno)) {
+                               logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Error while checking connection status for %s (%s): %s", c->name, c->hostname, sockstrerror(sockerrno));
+                               terminate_connection(c, false);
+                       }
+                       return;
+               }
+
                c->status.connecting = false;
 
                int result;
index d89d077..a6adffb 100644 (file)
@@ -37,6 +37,8 @@ extern const char *winerror(int);
 #define sockmsgsize(x) ((x) == WSAEMSGSIZE)
 #define sockinprogress(x) ((x) == WSAEINPROGRESS || (x) == WSAEWOULDBLOCK)
 #define sockinuse(x) ((x) == WSAEADDRINUSE)
+#define sockalready(x) ((x) == WSAEALREADY || (x) == WSAEINVAL || (x) == WSAEWOULDBLOCK) /* See MSDN for connect() */
+#define sockisconn(x) ((x) == WSAEISCONN)
 #else
 #define sockerrno errno
 #define sockstrerror(x) strerror(x)
@@ -44,6 +46,8 @@ extern const char *winerror(int);
 #define sockmsgsize(x) ((x) == EMSGSIZE)
 #define sockinprogress(x) ((x) == EINPROGRESS)
 #define sockinuse(x) ((x) == EADDRINUSE)
+#define sockalready(x) ((x) == EALREADY)
+#define sockisconn(x) ((x) == EISCONN)
 #endif
 
 extern unsigned int bitfield_to_int(const void *bitfield, size_t size);