From 4bc7339708bd7f64afcf2e773b5aed76bb5741fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francis=20Brosnan=20Bl=C3=A1zquez?= Date: Sun, 15 May 2016 23:03:44 +0200 Subject: [PATCH] nopoll: * [fix] Several updates to detect when __nopoll_conn_get_ssl_context fails to report a valid SSL_CTX (fixed updated test_19). * [fix] Updated header read handling to support all cases when a splitted/broken header is received. Added regression tests test_31, test_32, test_33, test_34 and test_35 to simulate and support different scenarios where part of the header is received and then the rest... --- src/nopoll_conn.c | 45 ++++++++++++----- test/nopoll-regression-client.c | 85 +++++++++++++++++++++++++++++---- 2 files changed, 109 insertions(+), 21 deletions(-) diff --git a/src/nopoll_conn.c b/src/nopoll_conn.c index 81e3216..ee6aa18 100644 --- a/src/nopoll_conn.c +++ b/src/nopoll_conn.c @@ -602,6 +602,7 @@ nopoll_bool __nopoll_conn_set_ssl_client_options (noPollCtx * ctx, noPollConn * } /* end if */ /* enable default verification paths */ + /* printf ("conn = %p, conn->ssl_ctx = %p\n", conn, conn->ssl_ctx); */ if (SSL_CTX_set_default_verify_paths (conn->ssl_ctx) != 1) { nopoll_log (ctx, NOPOLL_LEVEL_CRITICAL, "Unable to configure default verification paths, SSL_CTX_set_default_verify_paths () failed"); return nopoll_false; @@ -774,10 +775,15 @@ noPollConn * __nopoll_conn_new_common (noPollCtx * ctx, if (enable_tls) { /* found TLS connection request, enable it */ conn->ssl_ctx = __nopoll_conn_get_ssl_context (ctx, conn, options, nopoll_true); + if (conn->ssl_ctx == NULL) { + nopoll_log (ctx, NOPOLL_LEVEL_CRITICAL, "Unable to enable TLS, internal __nopoll_conn_get_ssl_context (ctx=%p, conn=%p, options=%p, nopoll_true) failed", + ctx, conn, options); + goto fail_ssl_connection; + } /* end if */ /* check for client side SSL configuration */ if (! __nopoll_conn_set_ssl_client_options (ctx, conn, options)) { - nopoll_log (ctx, NOPOLL_LEVEL_CRITICAL, "Unable to configure additional SSL options, unable to continue", + nopoll_log (ctx, NOPOLL_LEVEL_CRITICAL, "Unable to configure additional SSL options, unable to continue, conn->ssl_ctx=%p, conn->ssl=%p", conn->ssl_ctx, conn->ssl); goto fail_ssl_connection; } /* end if */ @@ -2885,11 +2891,21 @@ noPollMsg * nopoll_conn_get_msg (noPollConn * conn) unsigned integer */ bytes = __nopoll_conn_receive (conn, buffer + 2, 2); if (bytes != 2) { - nopoll_log (conn->ctx, NOPOLL_LEVEL_WARNING, "Failed to get next 2 bytes to read header from the wire, failed to received content, shutting down id=%d the connection, errno=%d (%s)", conn->id, errno, strerror (errno)); - if (errno == NOPOLL_EWOULDBLOCK) { + nopoll_log (conn->ctx, NOPOLL_LEVEL_WARNING, "Failed to get next 2 bytes to read header from the wire, but received=%d, failed to received content, shutting down id=%d the connection, errno=%d (%s)", + bytes, conn->id, errno, strerror (errno)); + if (errno == NOPOLL_EWOULDBLOCK || errno == 0) { /* connection is not ready at this point */ conn->previous_msg = msg; conn->read_pending_header = nopoll_true; + + /* check amount of bytes to reuse them */ + nopoll_log (conn->ctx, NOPOLL_LEVEL_WARNING, "Detected broken WebSocket peer sending header content using different frames, trying to save and resume later"); + if (bytes > 0) { + /* ok, store content read into the pending buffer for next call */ + memcpy (conn->pending_buf + conn->pending_buf_bytes, buffer + 2, bytes); + conn->pending_buf_bytes += bytes; + } + return NULL; } @@ -2902,6 +2918,8 @@ noPollMsg * nopoll_conn_get_msg (noPollConn * conn) header_size += bytes; msg->payload_size = nopoll_get_16bit (buffer + 2); + + nopoll_log (conn->ctx, NOPOLL_LEVEL_DEBUG, "Received (%d) bytes in header (size %d) for payload size indication, which finally is: %d", bytes, header_size,(int) msg->payload_size); } else if (msg->payload_size == 127) { #if defined(NOPOLL_64BIT_PLATFORM) @@ -3054,7 +3072,7 @@ read_payload: /* flag that this message doesn't have FIN = 0 because * we wasn't able to read it entirely */ - msg->has_fin = 0; + /* msg->has_fin = 0; */ } /* end if */ /* flag the message was being a fragment according to previous flag */ @@ -3844,21 +3862,22 @@ int nopoll_conn_send_frame (noPollConn * conn, nopoll_bool fin, nopoll_bool mask desp = 0; tries = 0; - /***** BEGIN INTERNAL debug code for test_30 : nopoll-regression-client.c ******/ - if (conn->__force_stop_after_header) { - nopoll_log (conn->ctx, NOPOLL_LEVEL_WARNING, "Sending broken header and implement a pause on purpose..."); + /***** BEGIN INTERNAL debug code for test_30, test_31, test_32, test_33, test_34, test_35 : nopoll-regression-client.c ******/ + if ((conn->__force_stop_after_header > 0) && (conn->__force_stop_after_header < (length + header_size))) { + + nopoll_log (conn->ctx, NOPOLL_LEVEL_WARNING, "Sending broken header (just %d bytes) and implement a pause on purpose...", conn->__force_stop_after_header); /* send just 2 bytes for the header and then implement a very long pause */ - bytes_written = conn->send (conn, send_buffer, 2); - desp = 2; - if (bytes_written != 2) { - nopoll_log (conn->ctx, NOPOLL_LEVEL_WARNING, "Requested to write 2 bytes for the header but %d were written", - bytes_written); + bytes_written = conn->send (conn, send_buffer, conn->__force_stop_after_header); + desp = conn->__force_stop_after_header; + if (bytes_written != conn->__force_stop_after_header) { + nopoll_log (conn->ctx, NOPOLL_LEVEL_WARNING, "Requested to write %d bytes for the header but %d were written", + conn->__force_stop_after_header, bytes_written); desp = 0; } /* end if */ /* sleep after header ... */ - nopoll_sleep (5000); /* 5 seconds */ + nopoll_sleep (5000000); /* 5 seconds */ } /* end if */ /****** END INTERNAL debug code for test_30 : nopoll-regression-client.c ******/ diff --git a/test/nopoll-regression-client.c b/test/nopoll-regression-client.c index 8507116..bca6016 100644 --- a/test/nopoll-regression-client.c +++ b/test/nopoll-regression-client.c @@ -2415,7 +2415,7 @@ nopoll_bool test_29 (void) { relases ****/ #include -nopoll_bool test_30 (void) { +nopoll_bool test_30_common_header_stop (const char * label, int bytes_to_send_before_stop) { noPollConn * conn; noPollCtx * ctx; @@ -2434,16 +2434,19 @@ nopoll_bool test_30 (void) { return nopoll_false; } /* end if */ - printf ("Test 30: waiting until connection is ready..\n"); + printf ("Test %s: waiting until connection is ready..\n", label); /* wait until it is connected */ nopoll_conn_wait_until_connection_ready (conn, 5); - printf ("Test 30: ok..\n"); + printf ("Test %s: ok..\n", label); /* send a message to request connection close with a particular message */ - conn->__force_stop_after_header = nopoll_true; + conn->__force_stop_after_header = bytes_to_send_before_stop; - printf ("Test 30: sending first message..\n"); length = strlen (msg); + + printf ("Test %s: sending first message (of %d bytes, sending broken header of %d, pausing then, and then sending the rest..)\n", + label, length, bytes_to_send_before_stop); + if (nopoll_conn_send_text (conn, msg, length) != length) { printf ("ERROR: failed to send message.."); return nopoll_false; @@ -2455,7 +2458,7 @@ nopoll_bool test_30 (void) { return nopoll_false; } /* end if */ - printf ("Test 30: getting reply to the message..\n"); + printf ("Test %s: getting reply to the message..\n", label); tries = 10; while (tries > 0 ) { /* get message */ @@ -2480,13 +2483,16 @@ nopoll_bool test_30 (void) { /* release message */ nopoll_msg_unref (msg_ref); - printf ("Test 30: send second message..\n"); + + printf ("Test %s: sending second message (of %d bytes, sending broken header of %d, pausing then, and then sending the rest..)\n", + label, length, bytes_to_send_before_stop); + if (nopoll_conn_send_text (conn, msg, length) != length) { printf ("ERROR: failed to send message.."); return nopoll_false; } /* end while */ - printf ("Test 30: getting reply to the message (to the second message)..\n"); + printf ("Test %s: getting reply to the message (to the second message)..\n", label); tries = 10; while (tries > 0 ) { /* get message */ @@ -2520,7 +2526,35 @@ nopoll_bool test_30 (void) { return nopoll_true; } +nopoll_bool test_30 (void) { + /* call to test send 1 byte and stop */ + return test_30_common_header_stop ("30", 2); +} +nopoll_bool test_31 (void) { + /* call to test send 1 byte and stop */ + return test_30_common_header_stop ("31", 1); +} + +nopoll_bool test_32 (void) { + /* call to test send 1 byte and stop */ + return test_30_common_header_stop ("32", 3); +} + +nopoll_bool test_33 (void) { + /* call to test send 1 byte and stop */ + return test_30_common_header_stop ("33", 4); +} + +nopoll_bool test_34 (void) { + /* call to test send 1 byte and stop */ + return test_30_common_header_stop ("34", 5); +} + +nopoll_bool test_35 (void) { + /* call to test send 1 byte and stop */ + return test_30_common_header_stop ("35", 8); +} int main (int argc, char ** argv) { @@ -2832,6 +2866,41 @@ int main (int argc, char ** argv) return -1; } /* end if */ + if (test_31 ()) { + printf ("Test 31: simulate stop in the middle of the header send (II) [ OK ]\n"); + } else { + printf ("Test 31: simulate stop in the middle of the header send (II) [ FAILED ]\n"); + return -1; + } /* end if */ + + if (test_32 ()) { + printf ("Test 32: simulate stop in the middle of the header send (III) [ OK ]\n"); + } else { + printf ("Test 32: simulate stop in the middle of the header send (III) [ FAILED ]\n"); + return -1; + } /* end if */ + + if (test_33 ()) { + printf ("Test 33: simulate stop in the middle of the header send (IV) [ OK ]\n"); + } else { + printf ("Test 33: simulate stop in the middle of the header send (IV) [ FAILED ]\n"); + return -1; + } /* end if */ + + if (test_34 ()) { + printf ("Test 34: simulate stop in the middle of the header send (V) [ OK ]\n"); + } else { + printf ("Test 34: simulate stop in the middle of the header send (V) [ FAILED ]\n"); + return -1; + } /* end if */ + + if (test_35 ()) { + printf ("Test 35: simulate stop in the middle of the header send (VI) [ OK ]\n"); + } else { + printf ("Test 35: simulate stop in the middle of the header send (VI) [ FAILED ]\n"); + return -1; + } /* end if */ + /* add support to reply with redirect 301 to an opening * request: page 19 and 22 */