adding code to detect port exhaustion (#378)

* adding code to detect port exhaustion

Co-authored-by: Jelani Brandon <jebrando@microsoft.com>
This commit is contained in:
Jelani Brandon 2024-10-02 16:33:55 -07:00 коммит произвёл GitHub
Родитель 21354df004
Коммит 26720d19e2
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
4 изменённых файлов: 143 добавлений и 17 удалений

Просмотреть файл

@ -38,8 +38,10 @@ MU_DEFINE_ENUM(SOCKET_SEND_RESULT, SOCKET_SEND_RESULT_VALUES)
MU_DEFINE_ENUM(SOCKET_RECEIVE_RESULT, SOCKET_RECEIVE_RESULT_VALUES)
#define SOCKET_ACCEPT_RESULT_VALUES \
SOCKET_ACCEPT_OK, \
SOCKET_ACCEPT_ERROR, \
SOCKET_ACCEPT_OK, \
SOCKET_ACCEPT_ERROR, \
SOCKET_ACCEPT_PORT_EXHAUSTION, \
SOCKET_ACCEPT_INPROGRESS, \
SOCKET_ACCEPT_NO_CONNECTION
MU_DEFINE_ENUM(SOCKET_ACCEPT_RESULT, SOCKET_ACCEPT_RESULT_VALUES)

Просмотреть файл

@ -301,7 +301,7 @@ MOCKABLE_FUNCTION(, int, socket_transport_listen, SOCKET_TRANSPORT_HANDLE, socke
### socket_transport_accept
```c
MOCKABLE_FUNCTION(, SOCKET_ACCEPT_RESULT, socket_transport_accept, SOCKET_TRANSPORT_HANDLE, socket_transport, SOCKET_TRANSPORT_HANDLE*, accepted_socket);
MOCKABLE_FUNCTION(, SOCKET_ACCEPT_RESULT, socket_transport_accept, SOCKET_TRANSPORT_HANDLE, socket_transport, SOCKET_TRANSPORT_HANDLE*, accepted_socket, uint32_t, connection_timeout_ms);
```
`socket_transport_accept` accepts the incoming connections.
@ -314,13 +314,21 @@ MOCKABLE_FUNCTION(, SOCKET_ACCEPT_RESULT, socket_transport_accept, SOCKET_TRANSP
**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`. **]**
**SOCKET_TRANSPORT_WIN32_09_071: [** `socket_transport_accept` shall call `select` determine if the socket is ready to be read passing a timeout of 10 milliseconds. **]**
**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`. **]**
**SOCKET_TRANSPORT_WIN32_09_091: [** If `select` returns zero, socket_transport_accept shall set accepted_socket to `NULL` and return `SOCKET_ACCEPT_NO_CONNECTION`. **]**
**SOCKET_TRANSPORT_WIN32_11_001: [** If `select` returns `SOCKET_ERROR` and `WSAGetLastError` return `WSAEINPROGRESS`, `socket_transport_accept` shall return `SOCKET_ACCEPT_INPROGRESS`. **]**
**SOCKET_TRANSPORT_WIN32_11_002: [** If `select` returns `SOCKET_ERROR` and `WSAGetLastError` does not return `WSAEINPROGRESS`, `socket_transport_accept` shall return `SOCKET_ACCEPT_ERROR`. **]**
**SOCKET_TRANSPORT_WIN32_09_091: [** If `select` returns zero, `socket_transport_accept` shall set accepted_socket to `NULL` and return `SOCKET_ACCEPT_NO_CONNECTION`. **]**
**SOCKET_TRANSPORT_WIN32_09_072: [** `socket_transport_accept` shall call `accept` to accept the incoming socket connection. **]**
**SOCKET_TRANSPORT_WIN32_09_073: [** If `accept` returns an INVALID_SOCKET, `socket_transport_accept` shall fail and return `SOCKET_ACCEPT_ERROR`. **]**
**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`. **]**
**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`. **]**
**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`. **]**
**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. **]**

Просмотреть файл

@ -685,12 +685,23 @@ SOCKET_ACCEPT_RESULT socket_transport_accept(SOCKET_TRANSPORT_HANDLE socket_tran
timeout.tv_usec = connection_timeout_ms * MILLISECONDS_CONVERSION;
timeout.tv_sec = 0;
// Codes_SOCKET_TRANSPORT_WIN32_09_071: [ socket_transport_accept shall call select determine if the socket is ready to be read passing a timeout of 10 milliseconds. ]
// 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)
{
LogLastError("Error waiting for socket connections %", select_result);
result = SOCKET_ACCEPT_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)
{
@ -700,14 +711,26 @@ SOCKET_ACCEPT_RESULT socket_transport_accept(SOCKET_TRANSPORT_HANDLE socket_tran
// 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);
// Codes_SOCKET_TRANSPORT_WIN32_09_073: [ If accept returns an INVALID_SOCKET, socket_transport_accept shall fail and return SOCKET_ACCEPT_ERROR. ]
if (accepting_socket == INVALID_SOCKET)
{
if (WSAGetLastError() != WSAEWOULDBLOCK)
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)
{
LogLastError("Error in accepting socket %" PRI_SOCKET "", accepted_socket);
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;
}
result = SOCKET_ACCEPT_ERROR;
}
else
{

Просмотреть файл

@ -1453,7 +1453,7 @@ TEST_FUNCTION(socket_transport_listen_fail)
// socket_transport_accept
// Tests_SOCKET_TRANSPORT_WIN32_09_069: [ socket_transport_accept shall call sm_exec_begin. ]
// Tests_SOCKET_TRANSPORT_WIN32_09_071: [ socket_transport_accept shall call select determine if the socket is ready to be read passing a timeout of 10 milliseconds. ]
// Tests_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. ]
// Tests_SOCKET_TRANSPORT_WIN32_09_072: [ socket_transport_accept shall call accept to accept the incoming socket connection. ]
// Tests_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. ]
// Tests_SOCKET_TRANSPORT_WIN32_09_075: [ If successful socket_transport_accept shall return SOCKET_ACCEPT_OK. ]
@ -1552,7 +1552,7 @@ TEST_FUNCTION(socket_transport_accept_smexecbegin_fail)
socket_transport_destroy(socket_handle);
}
// Tests_SOCKET_TRANSPORT_WIN32_09_071: [ socket_transport_accept shall call select determine if the socket is ready to be read passing a timeout of 10 milliseconds. ]
// Tests_SOCKET_TRANSPORT_WIN32_11_002: [ If select returns SOCKET_ERROR and WSAGetLastError does not return WSAEINPROGRESS, socket_transport_accept shall return SOCKET_ACCEPT_ERROR. ]
TEST_FUNCTION(socket_transport_accept_select_fail)
{
//arrange
@ -1565,6 +1565,7 @@ TEST_FUNCTION(socket_transport_accept_select_fail)
STRICT_EXPECTED_CALL(sm_exec_begin(IGNORED_ARG));
STRICT_EXPECTED_CALL(select(IGNORED_ARG, IGNORED_ARG, IGNORED_ARG, IGNORED_ARG, IGNORED_ARG))
.SetReturn(SOCKET_ERROR);
STRICT_EXPECTED_CALL(WSAGetLastError());
STRICT_EXPECTED_CALL(sm_exec_end(IGNORED_ARG));
//act
@ -1580,6 +1581,36 @@ TEST_FUNCTION(socket_transport_accept_select_fail)
socket_transport_destroy(socket_handle);
}
// Tests_SOCKET_TRANSPORT_WIN32_11_001: [ If select returns SOCKET_ERROR and WSAGetLastError return WSAEINPROGRESS, socket_transport_accept shall return SOCKET_ACCEPT_INPROGRESS. ]
TEST_FUNCTION(socket_transport_accept_select_in_progress_fail)
{
//arrange
SOCKET_TRANSPORT_HANDLE socket_handle = socket_transport_create_server();
ASSERT_IS_NOT_NULL(socket_handle);
ASSERT_ARE_EQUAL(int, 0, socket_transport_listen(socket_handle, TEST_PORT));
umock_c_reset_all_calls();
umock_c_negative_tests_snapshot();
STRICT_EXPECTED_CALL(sm_exec_begin(IGNORED_ARG));
STRICT_EXPECTED_CALL(select(IGNORED_ARG, IGNORED_ARG, IGNORED_ARG, IGNORED_ARG, IGNORED_ARG))
.SetReturn(SOCKET_ERROR);
STRICT_EXPECTED_CALL(WSAGetLastError())
.SetReturn(WSAEINPROGRESS);
STRICT_EXPECTED_CALL(sm_exec_end(IGNORED_ARG));
//act
SOCKET_TRANSPORT_HANDLE accept_socket_handle;
SOCKET_ACCEPT_RESULT accept_result = socket_transport_accept(socket_handle, &accept_socket_handle, TEST_CONNECTION_TIMEOUT);
//assert
ASSERT_ARE_EQUAL(SOCKET_ACCEPT_RESULT, SOCKET_ACCEPT_INPROGRESS, accept_result);
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
//cleanup
socket_transport_disconnect(socket_handle);
socket_transport_destroy(socket_handle);
}
// Tests_SOCKET_TRANSPORT_WIN32_09_091: [ If select returns zero, socket_transport_accept shall set accepted_socket to NULL and return SOCKET_ACCEPT_NO_CONNECTION. ]
TEST_FUNCTION(socket_transport_accept_select_returns_zero)
{
@ -1608,7 +1639,7 @@ TEST_FUNCTION(socket_transport_accept_select_returns_zero)
socket_transport_destroy(socket_handle);
}
// Tests_SOCKET_TRANSPORT_WIN32_09_073: [ If accept returns an INVALID_SOCKET, socket_transport_accept shall fail and return SOCKET_ACCEPT_ERROR. ]
// Tests_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. ]
TEST_FUNCTION(socket_transport_accept_accept_fail)
{
//arrange
@ -1623,7 +1654,7 @@ TEST_FUNCTION(socket_transport_accept_accept_fail)
STRICT_EXPECTED_CALL(accept(IGNORED_ARG, IGNORED_ARG, IGNORED_ARG))
.SetReturn((SOCKET)SOCKET_ERROR);
STRICT_EXPECTED_CALL(WSAGetLastError())
.SetReturn(0);
.SetReturn(WSAENOTSOCK);
STRICT_EXPECTED_CALL(sm_exec_end(IGNORED_ARG));
//act
@ -1639,6 +1670,68 @@ TEST_FUNCTION(socket_transport_accept_accept_fail)
socket_transport_destroy(socket_handle);
}
// Tests_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. ]
TEST_FUNCTION(socket_transport_accept_accept_no_buffer_fail)
{
//arrange
SOCKET_TRANSPORT_HANDLE socket_handle = socket_transport_create_server();
ASSERT_IS_NOT_NULL(socket_handle);
ASSERT_ARE_EQUAL(int, 0, socket_transport_listen(socket_handle, TEST_PORT));
umock_c_reset_all_calls();
umock_c_negative_tests_snapshot();
STRICT_EXPECTED_CALL(sm_exec_begin(IGNORED_ARG));
STRICT_EXPECTED_CALL(select(IGNORED_ARG, IGNORED_ARG, IGNORED_ARG, IGNORED_ARG, IGNORED_ARG));
STRICT_EXPECTED_CALL(accept(IGNORED_ARG, IGNORED_ARG, IGNORED_ARG))
.SetReturn((SOCKET)SOCKET_ERROR);
STRICT_EXPECTED_CALL(WSAGetLastError())
.SetReturn(WSAENOBUFS);
STRICT_EXPECTED_CALL(sm_exec_end(IGNORED_ARG));
//act
SOCKET_TRANSPORT_HANDLE accept_socket_handle;
SOCKET_ACCEPT_RESULT accept_result = socket_transport_accept(socket_handle, &accept_socket_handle, TEST_CONNECTION_TIMEOUT);
//assert
ASSERT_ARE_EQUAL(SOCKET_ACCEPT_RESULT, SOCKET_ACCEPT_PORT_EXHAUSTION, accept_result);
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
//cleanup
socket_transport_disconnect(socket_handle);
socket_transport_destroy(socket_handle);
}
// Tests_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. ]
TEST_FUNCTION(socket_transport_accept_accept_would_block_no_connection)
{
//arrange
SOCKET_TRANSPORT_HANDLE socket_handle = socket_transport_create_server();
ASSERT_IS_NOT_NULL(socket_handle);
ASSERT_ARE_EQUAL(int, 0, socket_transport_listen(socket_handle, TEST_PORT));
umock_c_reset_all_calls();
umock_c_negative_tests_snapshot();
STRICT_EXPECTED_CALL(sm_exec_begin(IGNORED_ARG));
STRICT_EXPECTED_CALL(select(IGNORED_ARG, IGNORED_ARG, IGNORED_ARG, IGNORED_ARG, IGNORED_ARG));
STRICT_EXPECTED_CALL(accept(IGNORED_ARG, IGNORED_ARG, IGNORED_ARG))
.SetReturn((SOCKET)SOCKET_ERROR);
STRICT_EXPECTED_CALL(WSAGetLastError())
.SetReturn(WSAEWOULDBLOCK);
STRICT_EXPECTED_CALL(sm_exec_end(IGNORED_ARG));
//act
SOCKET_TRANSPORT_HANDLE accept_socket_handle;
SOCKET_ACCEPT_RESULT accept_result = socket_transport_accept(socket_handle, &accept_socket_handle, TEST_CONNECTION_TIMEOUT);
//assert
ASSERT_ARE_EQUAL(SOCKET_ACCEPT_RESULT, SOCKET_ACCEPT_NO_CONNECTION, accept_result);
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
//cleanup
socket_transport_disconnect(socket_handle);
socket_transport_destroy(socket_handle);
}
// Tests_SOCKET_TRANSPORT_WIN32_09_084: [ If malloc fails, socket_transport_accept shall fail and return SOCKET_ACCEPT_ERROR. ]
TEST_FUNCTION(socket_transport_accept_malloc_fail)
{