diff options
author | Evgeny Grin (Karlson2k) <k2k@narod.ru> | 2023-11-26 13:31:05 +0300 |
---|---|---|
committer | Evgeny Grin (Karlson2k) <k2k@narod.ru> | 2023-12-02 20:14:23 +0300 |
commit | f09bf9eafdcb4b2bad82a62500be79a0b9ce3b22 (patch) | |
tree | b5f3d4ba5eeab36dbd8f14b4e94716bf26b83429 | |
parent | 4e5b65706e1c89d6d2845bd1fe9e1a91569e97d8 (diff) | |
download | libmicrohttpd-f09bf9eafdcb4b2bad82a62500be79a0b9ce3b22.tar.gz libmicrohttpd-f09bf9eafdcb4b2bad82a62500be79a0b9ce3b22.zip |
test_upgrade: added checking of socket shutdown status
Currently enabled only for non-TLS connections
-rw-r--r-- | src/microhttpd/test_upgrade.c | 243 |
1 files changed, 219 insertions, 24 deletions
diff --git a/src/microhttpd/test_upgrade.c b/src/microhttpd/test_upgrade.c index b30ad64b..8264e13b 100644 --- a/src/microhttpd/test_upgrade.c +++ b/src/microhttpd/test_upgrade.c | |||
@@ -172,6 +172,8 @@ _testErrorLog_func (const char *errDesc, const char *funcName, int lineNum) | |||
172 | /* Could be increased to facilitate debugging */ | 172 | /* Could be increased to facilitate debugging */ |
173 | static int test_timeout = 5; | 173 | static int test_timeout = 5; |
174 | 174 | ||
175 | static bool test_tls; | ||
176 | |||
175 | static int verbose = 0; | 177 | static int verbose = 0; |
176 | 178 | ||
177 | static uint16_t global_port; | 179 | static uint16_t global_port; |
@@ -377,6 +379,8 @@ struct wr_socket | |||
377 | } t; | 379 | } t; |
378 | 380 | ||
379 | bool is_nonblocking; | 381 | bool is_nonblocking; |
382 | |||
383 | bool eof_recieved; | ||
380 | #ifdef HTTPS_SUPPORT | 384 | #ifdef HTTPS_SUPPORT |
381 | /** | 385 | /** |
382 | * TLS credentials | 386 | * TLS credentials |
@@ -439,6 +443,7 @@ wr_create_plain_sckt (void) | |||
439 | return NULL; | 443 | return NULL; |
440 | } | 444 | } |
441 | s->t = wr_plain; | 445 | s->t = wr_plain; |
446 | s->eof_recieved = false; | ||
442 | s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); | 447 | s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); |
443 | s->is_nonblocking = false; | 448 | s->is_nonblocking = false; |
444 | if (MHD_INVALID_SOCKET != s->fd) | 449 | if (MHD_INVALID_SOCKET != s->fd) |
@@ -467,6 +472,7 @@ wr_create_tls_sckt (void) | |||
467 | return NULL; | 472 | return NULL; |
468 | } | 473 | } |
469 | s->t = wr_tls; | 474 | s->t = wr_tls; |
475 | s->eof_recieved = false; | ||
470 | s->tls_connected = 0; | 476 | s->tls_connected = 0; |
471 | s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); | 477 | s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); |
472 | s->is_nonblocking = false; | 478 | s->is_nonblocking = false; |
@@ -532,6 +538,7 @@ wr_create_from_plain_sckt (MHD_socket plain_sk) | |||
532 | return NULL; | 538 | return NULL; |
533 | } | 539 | } |
534 | s->t = wr_plain; | 540 | s->t = wr_plain; |
541 | s->eof_recieved = false; | ||
535 | s->fd = plain_sk; | 542 | s->fd = plain_sk; |
536 | s->is_nonblocking = false; /* The actual mode is unknown */ | 543 | s->is_nonblocking = false; /* The actual mode is unknown */ |
537 | wr_make_nonblocking (s); /* Force set mode to have correct status */ | 544 | wr_make_nonblocking (s); /* Force set mode to have correct status */ |
@@ -540,6 +547,19 @@ wr_create_from_plain_sckt (MHD_socket plain_sk) | |||
540 | } | 547 | } |
541 | 548 | ||
542 | 549 | ||
550 | /** | ||
551 | * Check whether shutdown of connection was received from remote | ||
552 | * @param s socket to check | ||
553 | * @return zero if shutdown signal has not been received, | ||
554 | * 1 if shutdown signal was already received | ||
555 | */ | ||
556 | static int | ||
557 | wr_is_eof_received (struct wr_socket *s) | ||
558 | { | ||
559 | return s->eof_recieved ? 1 : 0; | ||
560 | } | ||
561 | |||
562 | |||
543 | enum wr_wait_for_type | 563 | enum wr_wait_for_type |
544 | { | 564 | { |
545 | WR_WAIT_FOR_RECV = 0, | 565 | WR_WAIT_FOR_RECV = 0, |
@@ -575,10 +595,13 @@ wr_wait_socket_ready_noabort_ (struct wr_socket *s, | |||
575 | else | 595 | else |
576 | tmo_ptr = NULL; /* No timeout */ | 596 | tmo_ptr = NULL; /* No timeout */ |
577 | 597 | ||
578 | if (WR_WAIT_FOR_RECV == wait_for) | 598 | do |
579 | sel_res = select (1 + (int) s->fd, &fds, NULL, NULL, tmo_ptr); | 599 | { |
580 | else | 600 | if (WR_WAIT_FOR_RECV == wait_for) |
581 | sel_res = select (1 + (int) s->fd, NULL, &fds, NULL, tmo_ptr); | 601 | sel_res = select (1 + (int) s->fd, &fds, NULL, NULL, tmo_ptr); |
602 | else | ||
603 | sel_res = select (1 + (int) s->fd, NULL, &fds, NULL, tmo_ptr); | ||
604 | } while (0 > sel_res && MHD_SCKT_ERR_IS_EINTR_ (MHD_socket_get_error_ ())); | ||
582 | 605 | ||
583 | if (1 == sel_res) | 606 | if (1 == sel_res) |
584 | return true; | 607 | return true; |
@@ -763,15 +786,19 @@ wr_send_tmo (struct wr_socket *s, | |||
763 | { | 786 | { |
764 | if (wr_plain == s->t) | 787 | if (wr_plain == s->t) |
765 | { | 788 | { |
766 | int err; | 789 | ssize_t res; |
767 | ssize_t res = MHD_send_ (s->fd, buf, len); | 790 | while (! 0) |
768 | if (0 <= res) | 791 | { |
769 | return res; | 792 | int err; |
770 | err = MHD_socket_get_error_ (); | 793 | res = MHD_send_ (s->fd, buf, len); |
771 | if (! MHD_SCKT_ERR_IS_EAGAIN_ (err) && ! MHD_SCKT_ERR_IS_EINTR_ (err)) | 794 | if (0 <= res) |
772 | return res; | 795 | break; /* Success */ |
773 | wr_wait_socket_ready_ (s, timeout_ms, WR_WAIT_FOR_SEND); | 796 | err = MHD_socket_get_error_ (); |
774 | return MHD_send_ (s->fd, buf, len); | 797 | if (! MHD_SCKT_ERR_IS_EAGAIN_ (err) && ! MHD_SCKT_ERR_IS_EINTR_ (err)) |
798 | break; /* Failure */ | ||
799 | wr_wait_socket_ready_ (s, timeout_ms, WR_WAIT_FOR_SEND); | ||
800 | } | ||
801 | return res; | ||
775 | } | 802 | } |
776 | #ifdef HTTPS_SUPPORT | 803 | #ifdef HTTPS_SUPPORT |
777 | else if (wr_tls == s->t) | 804 | else if (wr_tls == s->t) |
@@ -846,15 +873,21 @@ wr_recv_tmo (struct wr_socket *s, | |||
846 | { | 873 | { |
847 | if (wr_plain == s->t) | 874 | if (wr_plain == s->t) |
848 | { | 875 | { |
849 | int err; | 876 | ssize_t res; |
850 | ssize_t res = MHD_recv_ (s->fd, buf, len); | 877 | while (! 0) |
851 | if (0 <= res) | 878 | { |
852 | return res; | 879 | int err; |
853 | err = MHD_socket_get_error_ (); | 880 | res = MHD_recv_ (s->fd, buf, len); |
854 | if (! MHD_SCKT_ERR_IS_EAGAIN_ (err) && ! MHD_SCKT_ERR_IS_EINTR_ (err)) | 881 | if (0 == res) |
855 | return res; | 882 | s->eof_recieved = true; |
856 | wr_wait_socket_ready_ (s, timeout_ms, WR_WAIT_FOR_RECV); | 883 | if (0 <= res) |
857 | return MHD_recv_ (s->fd, buf, len); | 884 | break; /* Success */ |
885 | err = MHD_socket_get_error_ (); | ||
886 | if (! MHD_SCKT_ERR_IS_EAGAIN_ (err) && ! MHD_SCKT_ERR_IS_EINTR_ (err)) | ||
887 | break; /* Failure */ | ||
888 | wr_wait_socket_ready_ (s, timeout_ms, WR_WAIT_FOR_RECV); | ||
889 | } | ||
890 | return res; | ||
858 | } | 891 | } |
859 | #ifdef HTTPS_SUPPORT | 892 | #ifdef HTTPS_SUPPORT |
860 | if (wr_tls == s->t) | 893 | if (wr_tls == s->t) |
@@ -866,6 +899,8 @@ wr_recv_tmo (struct wr_socket *s, | |||
866 | while (1) | 899 | while (1) |
867 | { | 900 | { |
868 | ret = gnutls_record_recv (s->tls_s, buf, len); | 901 | ret = gnutls_record_recv (s->tls_s, buf, len); |
902 | if (0 == ret) | ||
903 | s->eof_recieved = true; | ||
869 | if (ret >= 0) | 904 | if (ret >= 0) |
870 | return ret; | 905 | return ret; |
871 | if ((GNUTLS_E_AGAIN != ret) && (GNUTLS_E_INTERRUPTED != ret)) | 906 | if ((GNUTLS_E_AGAIN != ret) && (GNUTLS_E_INTERRUPTED != ret)) |
@@ -910,6 +945,90 @@ wr_recv (struct wr_socket *s, | |||
910 | 945 | ||
911 | 946 | ||
912 | /** | 947 | /** |
948 | * Shutdown send/write on the socket. | ||
949 | * @param s the socket to shutdown | ||
950 | * @param how the type of shutdown: SHUT_WR or SHUT_RDWR | ||
951 | * @param timeout_ms the maximum wait time in milliseconds to receive the data, | ||
952 | * no limit if negative value is used | ||
953 | * @return zero on succeed, -1 otherwise | ||
954 | */ | ||
955 | static int | ||
956 | wr_shutdown_tmo (struct wr_socket *s, int how, int timeout_ms) | ||
957 | { | ||
958 | switch (how) | ||
959 | { | ||
960 | case SHUT_WR: /* Valid value */ | ||
961 | break; | ||
962 | case SHUT_RDWR: /* Valid value */ | ||
963 | break; | ||
964 | case SHUT_RD: | ||
965 | externalErrorExitDesc ("Unsupported 'how' value"); | ||
966 | break; | ||
967 | default: | ||
968 | externalErrorExitDesc ("Invalid 'how' value"); | ||
969 | break; | ||
970 | } | ||
971 | if (wr_plain == s->t) | ||
972 | { | ||
973 | (void) timeout_ms; /* Unused parameter for plain sockets */ | ||
974 | return shutdown (s->fd, how); | ||
975 | } | ||
976 | #ifdef HTTPS_SUPPORT | ||
977 | if (wr_tls == s->t) | ||
978 | { | ||
979 | ssize_t ret; | ||
980 | if (! s->tls_connected && ! wr_handshake_tmo_ (s, timeout_ms)) | ||
981 | return -1; | ||
982 | |||
983 | while (1) | ||
984 | { | ||
985 | ret = | ||
986 | gnutls_bye (s->tls_s, | ||
987 | (SHUT_WR == how) ? GNUTLS_SHUT_WR : GNUTLS_SHUT_RDWR); | ||
988 | if (GNUTLS_E_SUCCESS == ret) | ||
989 | { | ||
990 | #if 0 /* Disabled to test pure behaviour */ | ||
991 | if (SHUT_RDWR == how) | ||
992 | (void) shutdown (s->fd, how); /* Also shutdown the underlying transport layer */ | ||
993 | #endif | ||
994 | return 0; | ||
995 | } | ||
996 | if ((GNUTLS_E_AGAIN != ret) && (GNUTLS_E_INTERRUPTED != ret)) | ||
997 | break; | ||
998 | wr_wait_socket_ready_ (s, timeout_ms, | ||
999 | gnutls_record_get_direction (s->tls_s) ? | ||
1000 | WR_WAIT_FOR_SEND : WR_WAIT_FOR_RECV); | ||
1001 | } | ||
1002 | |||
1003 | fprintf (stderr, "The error returned by gnutls_bye() is " | ||
1004 | "'%s' ", gnutls_strerror ((int) ret)); | ||
1005 | #if GNUTLS_VERSION_NUMBER >= 0x020600 | ||
1006 | fprintf (stderr, "(%s)\n", gnutls_strerror_name ((int) ret)); | ||
1007 | #else /* GNUTLS_VERSION_NUMBER < 0x020600 */ | ||
1008 | fprintf (stderr, "(%d)\n", (int) ret); | ||
1009 | #endif /* GNUTLS_VERSION_NUMBER < 0x020600 */ | ||
1010 | testErrorLogDesc ("gnutls_bye() failed with hard error"); | ||
1011 | MHD_socket_set_error_ (MHD_SCKT_ECONNABORTED_); /* hard error */ | ||
1012 | return -1; | ||
1013 | } | ||
1014 | #endif /* HTTPS_SUPPORT */ | ||
1015 | return -1; | ||
1016 | } | ||
1017 | |||
1018 | |||
1019 | /** | ||
1020 | * Shutdown the socket. | ||
1021 | * @param s the socket to shutdown | ||
1022 | * @return zero on succeed, -1 otherwise | ||
1023 | */ | ||
1024 | static int | ||
1025 | wr_shutdown (struct wr_socket *s, int how) | ||
1026 | { | ||
1027 | return wr_shutdown_tmo (s, how, test_timeout * 1000); | ||
1028 | } | ||
1029 | |||
1030 | |||
1031 | /** | ||
913 | * Close socket and release allocated resourced | 1032 | * Close socket and release allocated resourced |
914 | * @param s the socket to close | 1033 | * @param s the socket to close |
915 | * @return zero on succeed, -1 otherwise | 1034 | * @return zero on succeed, -1 otherwise |
@@ -1247,6 +1366,74 @@ recv_all (struct wr_socket *sock, | |||
1247 | 1366 | ||
1248 | 1367 | ||
1249 | /** | 1368 | /** |
1369 | * Shutdown write of the connection to signal end of transmission | ||
1370 | * for the remote side | ||
1371 | * @param sock the socket to shutdown | ||
1372 | */ | ||
1373 | static void | ||
1374 | send_eof (struct wr_socket *sock) | ||
1375 | { | ||
1376 | if (0 != wr_shutdown (sock, wr_is_eof_received (sock) ? SHUT_RDWR : SHUT_WR)) | ||
1377 | externalErrorExitDesc ("Failed to shutdown connection"); | ||
1378 | } | ||
1379 | |||
1380 | |||
1381 | /** | ||
1382 | * Receive end of the transmission indication from the remote side | ||
1383 | * @param sock the socket to use | ||
1384 | */ | ||
1385 | static void | ||
1386 | receive_eof (struct wr_socket *sock) | ||
1387 | { | ||
1388 | uint8_t buf[127]; | ||
1389 | ssize_t ret; | ||
1390 | size_t rcvd; | ||
1391 | bool got_eof = false; | ||
1392 | |||
1393 | wr_make_nonblocking (sock); | ||
1394 | for (rcvd = 0; rcvd < sizeof(buf); rcvd += (size_t) ret) | ||
1395 | { | ||
1396 | ret = wr_recv (sock, | ||
1397 | buf + rcvd, | ||
1398 | sizeof(buf) - rcvd); | ||
1399 | if (0 > ret) | ||
1400 | { | ||
1401 | if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ()) || | ||
1402 | MHD_SCKT_ERR_IS_EINTR_ (MHD_socket_get_error_ ())) | ||
1403 | { | ||
1404 | ret = 0; | ||
1405 | continue; | ||
1406 | } | ||
1407 | externalErrorExitDesc ("recv() failed"); | ||
1408 | } | ||
1409 | else if (0 == ret) | ||
1410 | { | ||
1411 | got_eof = true; | ||
1412 | break; | ||
1413 | } | ||
1414 | } | ||
1415 | if (got_eof && (0 == rcvd)) | ||
1416 | return; /* Success */ | ||
1417 | |||
1418 | if (0 != rcvd) | ||
1419 | { | ||
1420 | if (sizeof(buf) == rcvd) | ||
1421 | { | ||
1422 | fprintf (stderr, "Received at least %lu extra bytes while " | ||
1423 | "end-of-file is expected.\n", (unsigned long) sizeof(buf)); | ||
1424 | mhdErrorExit (); | ||
1425 | } | ||
1426 | fprintf (stderr, "Received at %lu extra bytes and then %s" | ||
1427 | "end-of-file marker.\n", (unsigned long) rcvd, | ||
1428 | got_eof ? "" : "NO "); | ||
1429 | mhdErrorExit (); | ||
1430 | } | ||
1431 | if (! got_eof) | ||
1432 | mhdErrorExitDesc ("Failed to receive end-of-file marker."); | ||
1433 | } | ||
1434 | |||
1435 | |||
1436 | /** | ||
1250 | * Main function for the thread that runs the interaction with | 1437 | * Main function for the thread that runs the interaction with |
1251 | * the upgraded socket. | 1438 | * the upgraded socket. |
1252 | * | 1439 | * |
@@ -1263,6 +1450,11 @@ run_usock (void *cls) | |||
1263 | "World"); | 1450 | "World"); |
1264 | send_all_stext (usock, | 1451 | send_all_stext (usock, |
1265 | "Finished"); | 1452 | "Finished"); |
1453 | if (! test_tls) | ||
1454 | { | ||
1455 | send_eof (usock); | ||
1456 | receive_eof (usock); | ||
1457 | } | ||
1266 | MHD_upgrade_action (urh, | 1458 | MHD_upgrade_action (urh, |
1267 | MHD_UPGRADE_ACTION_CLOSE); | 1459 | MHD_UPGRADE_ACTION_CLOSE); |
1268 | free (usock); | 1460 | free (usock); |
@@ -1292,6 +1484,11 @@ run_usock_client (void *cls) | |||
1292 | "World"); | 1484 | "World"); |
1293 | recv_all_stext (sock, | 1485 | recv_all_stext (sock, |
1294 | "Finished"); | 1486 | "Finished"); |
1487 | if (! test_tls) | ||
1488 | { | ||
1489 | receive_eof (sock); | ||
1490 | send_eof (sock); | ||
1491 | } | ||
1295 | wr_close (sock); | 1492 | wr_close (sock); |
1296 | client_done = true; | 1493 | client_done = true; |
1297 | return NULL; | 1494 | return NULL; |
@@ -1634,8 +1831,6 @@ run_mhd_loop (struct MHD_Daemon *daemon, | |||
1634 | } | 1831 | } |
1635 | 1832 | ||
1636 | 1833 | ||
1637 | static bool test_tls; | ||
1638 | |||
1639 | /** | 1834 | /** |
1640 | * Test upgrading a connection. | 1835 | * Test upgrading a connection. |
1641 | * | 1836 | * |