test_upgrade: added checking of socket shutdown status

Currently enabled only for non-TLS connections
This commit is contained in:
Evgeny Grin (Karlson2k)
2023-12-02 20:14:23 +03:00
parent 4e5b65706e
commit f09bf9eafd
+219 -24
View File
@@ -172,6 +172,8 @@ _testErrorLog_func (const char *errDesc, const char *funcName, int lineNum)
/* Could be increased to facilitate debugging */
static int test_timeout = 5;
static bool test_tls;
static int verbose = 0;
static uint16_t global_port;
@@ -377,6 +379,8 @@ struct wr_socket
} t;
bool is_nonblocking;
bool eof_recieved;
#ifdef HTTPS_SUPPORT
/**
* TLS credentials
@@ -439,6 +443,7 @@ wr_create_plain_sckt (void)
return NULL;
}
s->t = wr_plain;
s->eof_recieved = false;
s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
s->is_nonblocking = false;
if (MHD_INVALID_SOCKET != s->fd)
@@ -467,6 +472,7 @@ wr_create_tls_sckt (void)
return NULL;
}
s->t = wr_tls;
s->eof_recieved = false;
s->tls_connected = 0;
s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
s->is_nonblocking = false;
@@ -532,6 +538,7 @@ wr_create_from_plain_sckt (MHD_socket plain_sk)
return NULL;
}
s->t = wr_plain;
s->eof_recieved = false;
s->fd = plain_sk;
s->is_nonblocking = false; /* The actual mode is unknown */
wr_make_nonblocking (s); /* Force set mode to have correct status */
@@ -540,6 +547,19 @@ wr_create_from_plain_sckt (MHD_socket plain_sk)
}
/**
* Check whether shutdown of connection was received from remote
* @param s socket to check
* @return zero if shutdown signal has not been received,
* 1 if shutdown signal was already received
*/
static int
wr_is_eof_received (struct wr_socket *s)
{
return s->eof_recieved ? 1 : 0;
}
enum wr_wait_for_type
{
WR_WAIT_FOR_RECV = 0,
@@ -575,10 +595,13 @@ wr_wait_socket_ready_noabort_ (struct wr_socket *s,
else
tmo_ptr = NULL; /* No timeout */
if (WR_WAIT_FOR_RECV == wait_for)
sel_res = select (1 + (int) s->fd, &fds, NULL, NULL, tmo_ptr);
else
sel_res = select (1 + (int) s->fd, NULL, &fds, NULL, tmo_ptr);
do
{
if (WR_WAIT_FOR_RECV == wait_for)
sel_res = select (1 + (int) s->fd, &fds, NULL, NULL, tmo_ptr);
else
sel_res = select (1 + (int) s->fd, NULL, &fds, NULL, tmo_ptr);
} while (0 > sel_res && MHD_SCKT_ERR_IS_EINTR_ (MHD_socket_get_error_ ()));
if (1 == sel_res)
return true;
@@ -763,15 +786,19 @@ wr_send_tmo (struct wr_socket *s,
{
if (wr_plain == s->t)
{
int err;
ssize_t res = MHD_send_ (s->fd, buf, len);
if (0 <= res)
return res;
err = MHD_socket_get_error_ ();
if (! MHD_SCKT_ERR_IS_EAGAIN_ (err) && ! MHD_SCKT_ERR_IS_EINTR_ (err))
return res;
wr_wait_socket_ready_ (s, timeout_ms, WR_WAIT_FOR_SEND);
return MHD_send_ (s->fd, buf, len);
ssize_t res;
while (! 0)
{
int err;
res = MHD_send_ (s->fd, buf, len);
if (0 <= res)
break; /* Success */
err = MHD_socket_get_error_ ();
if (! MHD_SCKT_ERR_IS_EAGAIN_ (err) && ! MHD_SCKT_ERR_IS_EINTR_ (err))
break; /* Failure */
wr_wait_socket_ready_ (s, timeout_ms, WR_WAIT_FOR_SEND);
}
return res;
}
#ifdef HTTPS_SUPPORT
else if (wr_tls == s->t)
@@ -846,15 +873,21 @@ wr_recv_tmo (struct wr_socket *s,
{
if (wr_plain == s->t)
{
int err;
ssize_t res = MHD_recv_ (s->fd, buf, len);
if (0 <= res)
return res;
err = MHD_socket_get_error_ ();
if (! MHD_SCKT_ERR_IS_EAGAIN_ (err) && ! MHD_SCKT_ERR_IS_EINTR_ (err))
return res;
wr_wait_socket_ready_ (s, timeout_ms, WR_WAIT_FOR_RECV);
return MHD_recv_ (s->fd, buf, len);
ssize_t res;
while (! 0)
{
int err;
res = MHD_recv_ (s->fd, buf, len);
if (0 == res)
s->eof_recieved = true;
if (0 <= res)
break; /* Success */
err = MHD_socket_get_error_ ();
if (! MHD_SCKT_ERR_IS_EAGAIN_ (err) && ! MHD_SCKT_ERR_IS_EINTR_ (err))
break; /* Failure */
wr_wait_socket_ready_ (s, timeout_ms, WR_WAIT_FOR_RECV);
}
return res;
}
#ifdef HTTPS_SUPPORT
if (wr_tls == s->t)
@@ -866,6 +899,8 @@ wr_recv_tmo (struct wr_socket *s,
while (1)
{
ret = gnutls_record_recv (s->tls_s, buf, len);
if (0 == ret)
s->eof_recieved = true;
if (ret >= 0)
return ret;
if ((GNUTLS_E_AGAIN != ret) && (GNUTLS_E_INTERRUPTED != ret))
@@ -909,6 +944,90 @@ wr_recv (struct wr_socket *s,
}
/**
* Shutdown send/write on the socket.
* @param s the socket to shutdown
* @param how the type of shutdown: SHUT_WR or SHUT_RDWR
* @param timeout_ms the maximum wait time in milliseconds to receive the data,
* no limit if negative value is used
* @return zero on succeed, -1 otherwise
*/
static int
wr_shutdown_tmo (struct wr_socket *s, int how, int timeout_ms)
{
switch (how)
{
case SHUT_WR: /* Valid value */
break;
case SHUT_RDWR: /* Valid value */
break;
case SHUT_RD:
externalErrorExitDesc ("Unsupported 'how' value");
break;
default:
externalErrorExitDesc ("Invalid 'how' value");
break;
}
if (wr_plain == s->t)
{
(void) timeout_ms; /* Unused parameter for plain sockets */
return shutdown (s->fd, how);
}
#ifdef HTTPS_SUPPORT
if (wr_tls == s->t)
{
ssize_t ret;
if (! s->tls_connected && ! wr_handshake_tmo_ (s, timeout_ms))
return -1;
while (1)
{
ret =
gnutls_bye (s->tls_s,
(SHUT_WR == how) ? GNUTLS_SHUT_WR : GNUTLS_SHUT_RDWR);
if (GNUTLS_E_SUCCESS == ret)
{
#if 0 /* Disabled to test pure behaviour */
if (SHUT_RDWR == how)
(void) shutdown (s->fd, how); /* Also shutdown the underlying transport layer */
#endif
return 0;
}
if ((GNUTLS_E_AGAIN != ret) && (GNUTLS_E_INTERRUPTED != ret))
break;
wr_wait_socket_ready_ (s, timeout_ms,
gnutls_record_get_direction (s->tls_s) ?
WR_WAIT_FOR_SEND : WR_WAIT_FOR_RECV);
}
fprintf (stderr, "The error returned by gnutls_bye() is "
"'%s' ", gnutls_strerror ((int) ret));
#if GNUTLS_VERSION_NUMBER >= 0x020600
fprintf (stderr, "(%s)\n", gnutls_strerror_name ((int) ret));
#else /* GNUTLS_VERSION_NUMBER < 0x020600 */
fprintf (stderr, "(%d)\n", (int) ret);
#endif /* GNUTLS_VERSION_NUMBER < 0x020600 */
testErrorLogDesc ("gnutls_bye() failed with hard error");
MHD_socket_set_error_ (MHD_SCKT_ECONNABORTED_); /* hard error */
return -1;
}
#endif /* HTTPS_SUPPORT */
return -1;
}
/**
* Shutdown the socket.
* @param s the socket to shutdown
* @return zero on succeed, -1 otherwise
*/
static int
wr_shutdown (struct wr_socket *s, int how)
{
return wr_shutdown_tmo (s, how, test_timeout * 1000);
}
/**
* Close socket and release allocated resourced
* @param s the socket to close
@@ -1246,6 +1365,74 @@ recv_all (struct wr_socket *sock,
#define recv_all_stext(sk,st) recv_all(sk,st,MHD_STATICSTR_LEN_(st))
/**
* Shutdown write of the connection to signal end of transmission
* for the remote side
* @param sock the socket to shutdown
*/
static void
send_eof (struct wr_socket *sock)
{
if (0 != wr_shutdown (sock, wr_is_eof_received (sock) ? SHUT_RDWR : SHUT_WR))
externalErrorExitDesc ("Failed to shutdown connection");
}
/**
* Receive end of the transmission indication from the remote side
* @param sock the socket to use
*/
static void
receive_eof (struct wr_socket *sock)
{
uint8_t buf[127];
ssize_t ret;
size_t rcvd;
bool got_eof = false;
wr_make_nonblocking (sock);
for (rcvd = 0; rcvd < sizeof(buf); rcvd += (size_t) ret)
{
ret = wr_recv (sock,
buf + rcvd,
sizeof(buf) - rcvd);
if (0 > ret)
{
if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ()) ||
MHD_SCKT_ERR_IS_EINTR_ (MHD_socket_get_error_ ()))
{
ret = 0;
continue;
}
externalErrorExitDesc ("recv() failed");
}
else if (0 == ret)
{
got_eof = true;
break;
}
}
if (got_eof && (0 == rcvd))
return; /* Success */
if (0 != rcvd)
{
if (sizeof(buf) == rcvd)
{
fprintf (stderr, "Received at least %lu extra bytes while "
"end-of-file is expected.\n", (unsigned long) sizeof(buf));
mhdErrorExit ();
}
fprintf (stderr, "Received at %lu extra bytes and then %s"
"end-of-file marker.\n", (unsigned long) rcvd,
got_eof ? "" : "NO ");
mhdErrorExit ();
}
if (! got_eof)
mhdErrorExitDesc ("Failed to receive end-of-file marker.");
}
/**
* Main function for the thread that runs the interaction with
* the upgraded socket.
@@ -1263,6 +1450,11 @@ run_usock (void *cls)
"World");
send_all_stext (usock,
"Finished");
if (! test_tls)
{
send_eof (usock);
receive_eof (usock);
}
MHD_upgrade_action (urh,
MHD_UPGRADE_ACTION_CLOSE);
free (usock);
@@ -1292,6 +1484,11 @@ run_usock_client (void *cls)
"World");
recv_all_stext (sock,
"Finished");
if (! test_tls)
{
receive_eof (sock);
send_eof (sock);
}
wr_close (sock);
client_done = true;
return NULL;
@@ -1634,8 +1831,6 @@ run_mhd_loop (struct MHD_Daemon *daemon,
}
static bool test_tls;
/**
* Test upgrading a connection.
*