SOCKET_ACCEPT_RESULT socket_transport_accept()

in win32/src/socket_transport_win32.c [647:804]


SOCKET_ACCEPT_RESULT socket_transport_accept(SOCKET_TRANSPORT_HANDLE socket_transport, SOCKET_TRANSPORT_HANDLE* accepted_socket, uint32_t connection_timeout_ms)
{
    SOCKET_TRANSPORT* accept_result;
    SOCKET_ACCEPT_RESULT result;

    // Codes_SOCKET_TRANSPORT_WIN32_09_067: [ If socket_transport is NULL, socket_transport_accept shall fail and return SOCKET_ACCEPT_ERROR. ]
    if (socket_transport == NULL)
    {
        LogError("Invalid arguments: SOCKET_TRANSPORT_HANDLE socket_transport: %p",
            socket_transport);
        result = SOCKET_ACCEPT_ERROR;
    }
    else
    {
        // Codes_SOCKET_TRANSPORT_WIN32_09_068: [ If the transport type is not SOCKET_BINDING, socket_transport_accept shall fail and return SOCKET_ACCEPT_ERROR. ]
        if (socket_transport->type != SOCKET_BINDING)
        {
            LogError("Invalid socket type for this API expected: SOCKET_BINDING, actual: %" PRI_MU_ENUM, MU_ENUM_VALUE(SOCKET_TYPE, socket_transport->type));
            result = SOCKET_ACCEPT_ERROR;
        }
        else
        {
            // Codes_SOCKET_TRANSPORT_WIN32_09_069: [ socket_transport_accept shall call sm_exec_begin. ]
            SM_RESULT sm_result = sm_exec_begin(socket_transport->sm);

            // Codes_SOCKET_TRANSPORT_WIN32_09_070: [ If sm_exec_begin does not return SM_EXEC_GRANTED, socket_transport_accept shall fail and return SOCKET_ACCEPT_ERROR. ]
            if (sm_result != SM_EXEC_GRANTED)
            {
                LogError("sm_exec_begin failed : %" PRI_MU_ENUM, MU_ENUM_VALUE(SM_RESULT, sm_result));
                result = SOCKET_ACCEPT_ERROR;
            }
            else
            {
                fd_set read_fds;
                int select_result;
                struct timeval timeout;

                read_fds.fd_array[0] = socket_transport->socket;
                read_fds.fd_count = 1;
                timeout.tv_usec = connection_timeout_ms * MILLISECONDS_CONVERSION;
                timeout.tv_sec = 0;

                // Codes_SOCKET_TRANSPORT_WIN32_09_071: [ socket_transport_accept shall call select to determine if the socket is ready to be read passing connection_timeout_ms. ]
                select_result = select(0, &read_fds, NULL, NULL, &timeout);
                if (select_result == SOCKET_ERROR)
                {
                    // Codes_SOCKET_TRANSPORT_WIN32_11_001: [ If select returns SOCKET_ERROR and WSAGetLastError return WSAEINPROGRESS, socket_transport_accept shall return SOCKET_ACCEPT_INPROGRESS. ]
                    DWORD last_error = WSAGetLastError();
                    if (last_error == WSAEINPROGRESS)
                    {
                        LogLastError("socket is in progress");
                        result = SOCKET_ACCEPT_INPROGRESS;
                    }
                    else
                    {
                        // Codes_SOCKET_TRANSPORT_WIN32_11_002: [ If select returns SOCKET_ERROR and WSAGetLastError does not return WSAEINPROGRESS, socket_transport_accept shall return SOCKET_ACCEPT_ERROR. ]
                        LogLastError("Error waiting for socket connections %", select_result);
                        result = SOCKET_ACCEPT_ERROR;
                    }
                }
                else if (select_result > 0)
                {
                    struct sockaddr_in cli_addr;
                    socklen_t client_len = sizeof(cli_addr);

                    // Codes_SOCKET_TRANSPORT_WIN32_09_072: [ socket_transport_accept shall call accept to accept the incoming socket connection. ]
                    SOCKET accepting_socket = accept(socket_transport->socket, (struct sockaddr*)&cli_addr, &client_len);

                    if (accepting_socket == INVALID_SOCKET)
                    {
                        DWORD last_error = WSAGetLastError();
                        // Codes_SOCKET_TRANSPORT_WIN32_11_003: [ If accept returns an INVALID_SOCKET and WSAGetLastError returns WSAENOBUFS, socket_transport_accept shall fail and return SOCKET_ACCEPT_PORT_EXHAUSTION. ]
                        if (last_error == WSAENOBUFS)
                        {
                            LogError("The server socket is experiencing port exhaustion");
                            result = SOCKET_ACCEPT_PORT_EXHAUSTION;
                        }
                        // Codes_SOCKET_TRANSPORT_WIN32_11_004: [ If accept returns an INVALID_SOCKET and WSAGetLastError returns WSAEWOULDBLOCK, socket_transport_accept shall fail and return SOCKET_ACCEPT_NO_CONNECTION. ]
                        else if (last_error == WSAEWOULDBLOCK)
                        {
                            result = SOCKET_ACCEPT_NO_CONNECTION;
                        }
                        // Codes_SOCKET_TRANSPORT_WIN32_09_073: [ If accept returns an INVALID_SOCKET and WSAGetLastError does not return WSAEWOULDBLOCK, socket_transport_accept shall fail and return SOCKET_ACCEPT_ERROR. ]
                        else
                        {
                            LogLastError("Error in accepting socket");
                            result = SOCKET_ACCEPT_ERROR;
                        }
                    }
                    else
                    {
                        // Codes_SOCKET_TRANSPORT_WIN32_09_074: [ socket_transport_accept shall allocate a SOCKET_TRANSPORT for the incoming connection and call sm_create and sm_open on the connection. ]
                        char hostname_addr[256];
                        (void)inet_ntop(AF_INET, (const void*)&cli_addr.sin_addr, hostname_addr, sizeof(hostname_addr));

                        // Create the socket handle
                        // Codes_SOCKET_TRANSPORT_WIN32_09_084: [ If malloc fails, socket_transport_accept shall fail and return SOCKET_ACCEPT_ERROR. ]
                        accept_result = malloc(sizeof(SOCKET_TRANSPORT));
                        if (accept_result == NULL)
                        {
                            LogError("failure allocating SOCKET_TRANSPORT: %zu", sizeof(SOCKET_TRANSPORT));
                            result = SOCKET_ACCEPT_ERROR;
                        }
                        else
                        {
                            // Codes_SOCKET_TRANSPORT_WIN32_09_085: [ If sm_create fails, socket_transport_accept shall close the incoming socket, fail, and return SOCKET_ACCEPT_ERROR. ]
                            accept_result->sm = sm_create("Socket_transport_win32");
                            if (accept_result->sm == NULL)
                            {
                                LogError("Failed calling sm_create in accept, closing incoming socket.");
                                closesocket(accepting_socket);
                                free(accept_result);
                                result = SOCKET_ACCEPT_ERROR;
                            }
                            else
                            {
                                // Codes_SOCKET_TRANSPORT_WIN32_09_086: [ If sm_open_begin fails, socket_transport_accept shall close the incoming socket, fail, and return SOCKET_ACCEPT_ERROR ]
                                SM_RESULT open_result = sm_open_begin(accept_result->sm);
                                if (open_result == SM_EXEC_GRANTED)
                                {
                                    accept_result->type = SOCKET_CLIENT;
                                    accept_result->socket = accepting_socket;
                                    sm_open_end(accept_result->sm, true);
                                    *accepted_socket = accept_result;
                                    result = SOCKET_ACCEPT_OK;
                                }
                                else
                                {
                                    LogError("sm_open_begin failed with %" PRI_MU_ENUM " in accept, closing incoming socket.", MU_ENUM_VALUE(SM_RESULT, open_result));
                                    closesocket(accepting_socket);
                                    sm_destroy(accept_result->sm);
                                    free(accept_result);
                                    result = SOCKET_ACCEPT_ERROR;
                                }
                            }
                        }
                    }
                }
                // Codes_SOCKET_TRANSPORT_WIN32_09_091: [ If select returns zero, socket_transport_accept shall set accepted_socket to NULL and return SOCKET_ACCEPT_NO_CONNECTION. ]
                else if (select_result == 0)
                {
                    result = SOCKET_ACCEPT_NO_CONNECTION;
                }
                else
                {
                    // Codes_SOCKET_TRANSPORT_WIN32_09_076: [ If any failure is encountered, socket_transport_accept shall fail and return SOCKET_ACCEPT_ERROR. ]
                    LogLastError("Failure accepting socket connection");
                    result = SOCKET_ACCEPT_ERROR;
                }
                // Codes_SOCKET_TRANSPORT_WIN32_09_077: [ socket_transport_accept shall call sm_exec_end. ]
                sm_exec_end(socket_transport->sm);
            }
        }
    }

    // Codes_SOCKET_TRANSPORT_WIN32_09_075: [ If successful socket_transport_accept shall return the allocated SOCKET_TRANSPORT. ]
    return result;
}