Use blocking sockets in freerdp_tcp_connect_multi
authorArmin Novak <armin.novak@thincast.com>
Mon, 12 Nov 2018 11:37:33 +0000 (12:37 +0100)
committerArmin Novak <armin.novak@thincast.com>
Thu, 22 Nov 2018 10:11:31 +0000 (11:11 +0100)
The non blocking connect in freerdp_tcp_connect_multi did not
work reliably.
Fall back to blocking connect (which might take longer until the connection
is established) instead of not being able to connect at all.

libfreerdp/core/tcp.c

index 646a09a..6b07b6b 100644 (file)
@@ -881,32 +881,42 @@ fail:
        return rc;
 }
 
+typedef struct
+{
+       SOCKET s;
+       struct addrinfo* addr;
+       struct addrinfo* result;
+} t_peer;
+
+static BOOL peer_free(t_peer* peer)
+{
+       if (peer->s != INVALID_SOCKET)
+               closesocket(peer->s);
+
+       freeaddrinfo(peer->addr);
+       memset(peer, 0, sizeof(t_peer));
+       peer->s = INVALID_SOCKET;
+}
+
 static int freerdp_tcp_connect_multi(rdpContext* context, char** hostnames,
                                      UINT32* ports, UINT32 count, int port,
                                      int timeout)
 {
        UINT32 index;
-       int sindex;
-       int status;
-       SOCKET sockfd = (SOCKET) - 1;
-       SOCKET* sockfds;
+       UINT32 sindex = count;
+       int status = -1;
+       SOCKET sockfd = INVALID_SOCKET;
        HANDLE* events;
-       DWORD waitStatus;
        struct addrinfo* addr;
        struct addrinfo* result;
-       struct addrinfo** addrs;
-       struct addrinfo** results;
-       sockfds = (SOCKET*) calloc(count, sizeof(SOCKET));
+       t_peer* peers;
        events = (HANDLE*) calloc(count + 1, sizeof(HANDLE));
-       addrs = (struct addrinfo**) calloc(count, sizeof(struct addrinfo*));
-       results = (struct addrinfo**) calloc(count, sizeof(struct addrinfo*));
+       peers = (t_peer*)calloc(count, sizeof(t_peer));
 
-       if (!sockfds || !events || !addrs || !results)
+       if (!peers || !events || (count < 1))
        {
-               free(sockfds);
+               free(peers);
                free(events);
-               free(addrs);
-               free(results);
                return -1;
        }
 
@@ -936,104 +946,48 @@ static int freerdp_tcp_connect_multi(rdpContext* context, char** hostnames,
                                addr = result;
                }
 
-               sockfds[index] = _socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
+               peers[index].s = _socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
 
-               if (sockfds[index] == INVALID_SOCKET)
+               if (peers[index].s == INVALID_SOCKET)
                {
                        freeaddrinfo(result);
-                       sockfds[index] = 0;
                        continue;
                }
 
-               addrs[index] = addr;
-               results[index] = result;
+               peers[index].addr = addr;
+               peers[index].result = result;
        }
 
        for (index = 0; index < count; index++)
        {
-               if (!sockfds[index])
-                       continue;
-
-               sockfd = sockfds[index];
-               addr = addrs[index];
-               /* set socket in non-blocking mode */
-               events[index] = WSACreateEvent();
-
-               if (!events[index])
-               {
-                       WLog_ERR(TAG, "WSACreateEvent returned 0x%08X", WSAGetLastError());
+               if (peers[index].s == INVALID_SOCKET)
                        continue;
-               }
 
-               if (WSAEventSelect(sockfd, events[index], FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE))
-               {
-                       WLog_ERR(TAG, "WSAEventSelect returned 0x%08X", WSAGetLastError());
-                       continue;
-               }
-
-               /* non-blocking tcp connect */
+               sockfd = peers[index].s;
+               addr = peers[index].addr;
+               /* blocking tcp connect */
                status = _connect(sockfd, addr->ai_addr, addr->ai_addrlen);
 
                if (status >= 0)
                {
                        /* connection success */
+                       sindex = index;
                        break;
                }
        }
 
-       events[count] = context->abortEvent;
-       waitStatus = WaitForMultipleObjects(count + 1, events, FALSE, timeout * 1000);
-       sindex = waitStatus - WAIT_OBJECT_0;
-
-       for (index = 0; index < count; index++)
+       if (sindex < count)
        {
-               const char* host = hostnames[index];
-               UINT32 curPort = port;
-               u_long arg = 0;
-
-               if (ports)
-                       curPort = ports[index];
-
-               if (!sockfds[index])
-                       continue;
-
-               sockfd = sockfds[index];
-
-               /* set socket in blocking mode */
-               if (WSAEventSelect(sockfd, events[index], 0))
-               {
-                       WLog_ERR(TAG, "[%s:%"PRId32"] WSAEventSelect returned 0x%08X", host, port, WSAGetLastError());
-                       continue;
-               }
-
-               if (_ioctlsocket(sockfd, FIONBIO, &arg))
-               {
-                       WLog_ERR(TAG, "[%s:%"PRId32"] _ioctlsocket failed", host, port);
-               }
+               sockfd = peers[sindex].s;
+               peers[sindex].s = INVALID_SOCKET;
        }
-
-       if ((sindex >= 0) && (sindex < count))
-       {
-               sockfd = sockfds[sindex];
-       }
-
-       if (sindex == count)
+       else
                freerdp_set_last_error(context, FREERDP_ERROR_CONNECT_CANCELLED);
 
        for (index = 0; index < count; index++)
-       {
-               if (results[index])
-                       freeaddrinfo(results[index]);
-
-               if (sindex != index)
-                       closesocket(sockfds[index]);
-
-               CloseHandle(events[index]);
-       }
+               peer_free(&peers[index]);
 
-       free(addrs);
-       free(results);
-       free(sockfds);
+       free(peers);
        free(events);
        return sockfd;
 }