diff --git a/mongoose.c b/mongoose.c index ae79bce8..9e22326f 100644 --- a/mongoose.c +++ b/mongoose.c @@ -10720,7 +10720,7 @@ static void mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg, nonce[8] ^= (uint8_t) ((seq >> 24) & 255U); nonce[9] ^= (uint8_t) ((seq >> 16) & 255U); nonce[10] ^= (uint8_t) ((seq >> 8) & 255U); - nonce[11] ^= (uint8_t) ((seq) & 255U); + nonce[11] ^= (uint8_t) ((seq) &255U); mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr)); mg_iobuf_resize(wio, wio->len + encsz); @@ -10799,7 +10799,7 @@ static int mg_tls_recv_record(struct mg_connection *c) { nonce[8] ^= (uint8_t) ((seq >> 24) & 255U); nonce[9] ^= (uint8_t) ((seq >> 16) & 255U); nonce[10] ^= (uint8_t) ((seq >> 8) & 255U); - nonce[11] ^= (uint8_t) ((seq) & 255U); + nonce[11] ^= (uint8_t) ((seq) &255U); #if CHACHA20 { uint8_t *dec = (uint8_t *) calloc(1, msgsz); @@ -11557,7 +11557,7 @@ static int mg_tls_client_recv_cert(struct mg_connection *c) { struct mg_tls_cert certs[8]; int certnum = 0; uint8_t *p = recv_buf + 8; - //uint8_t *endp = recv_buf + tls->recv_len; + // uint8_t *endp = recv_buf + tls->recv_len; uint8_t *endp = recv_buf + cert_chain_len; int found_ca = 0; @@ -11666,7 +11666,8 @@ static int mg_tls_client_recv_cert_verify(struct mg_connection *c) { tls->recv_len - 8); return -1; } - MG_VERBOSE(("certificate verification, algo=%04x, siglen=%d", sigalg, siglen)); + MG_VERBOSE( + ("certificate verification, algo=%04x, siglen=%d", sigalg, siglen)); if (sigalg == 0x0804) { // rsa_pss_rsae_sha256 uint8_t sig2[512]; // 2048 or 4096 bits @@ -11819,7 +11820,9 @@ static void mg_tls_client_handshake(struct mg_connection *c) { c->is_tls_hs = 0; mg_call(c, MG_EV_TLS_HS, NULL); break; - default: mg_error(c, "unexpected client state: %d", tls->state); break; + default: + mg_error(c, "unexpected client state: %d", tls->state); + break; } } @@ -11846,7 +11849,9 @@ static void mg_tls_server_handshake(struct mg_connection *c) { tls->state = MG_TLS_STATE_SERVER_CONNECTED; c->is_tls_hs = 0; return; - default: mg_error(c, "unexpected server state: %d", tls->state); break; + default: + mg_error(c, "unexpected server state: %d", tls->state); + break; } } @@ -11980,15 +11985,22 @@ void mg_tls_free(struct mg_connection *c) { long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) { struct tls_data *tls = (struct tls_data *) c->tls; long n = MG_IO_WAIT; - if (len > MG_IO_SIZE) len = MG_IO_SIZE; - if (len > 16384) len = 16384; - mg_tls_encrypt(c, (const uint8_t *) buf, len, MG_TLS_APP_DATA); + bool was_throttled = c->is_tls_throttled; // see #3074 + if (!was_throttled) { // encrypt new data + if (len > MG_IO_SIZE) len = MG_IO_SIZE; + if (len > 16384) len = 16384; + mg_tls_encrypt(c, (const uint8_t *) buf, len, MG_TLS_APP_DATA); + } // else, resend outstanding encrypted data in tls->send while (tls->send.len > 0 && (n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) { mg_iobuf_del(&tls->send, 0, (size_t) n); } - if (n == MG_IO_ERR || n == MG_IO_WAIT) return n; - return (long) len; + c->is_tls_throttled = (tls->send.len > 0 && n == MG_IO_WAIT); + MG_VERBOSE(("%lu %ld %ld %ld %c %c", c->id, (long) len, (long) tls->send.len, + n, was_throttled ? 'T' : 't', c->is_tls_throttled ? 'T' : 't')); + if (n == MG_IO_ERR) return MG_IO_ERR; + if (was_throttled) return MG_IO_WAIT; // sent throttled data instead + return (long) len; // return len even when throttled, already encripted that } long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { @@ -13609,9 +13621,16 @@ long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) { struct mg_tls *tls = (struct mg_tls *) c->tls; - long n = mbedtls_ssl_write(&tls->ssl, (unsigned char *) buf, len); - if (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE) - return MG_IO_WAIT; + long n; + bool was_throttled = c->is_tls_throttled; // see #3074 + n = was_throttled ? mbedtls_ssl_write(&tls->ssl, tls->throttled_buf, + tls->throttled_len) /* flush old data */ + : mbedtls_ssl_write(&tls->ssl, (unsigned char *) buf, + len); // encrypt current data + c->is_tls_throttled = + (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE); + if (was_throttled) return MG_IO_WAIT; // flushed throttled data instead + if (c->is_tls_throttled) return len; // already encripted that when throttled if (n <= 0) return MG_IO_ERR; return n; } diff --git a/mongoose.h b/mongoose.h index 9bce23c2..8571b4ef 100644 --- a/mongoose.h +++ b/mongoose.h @@ -2224,40 +2224,41 @@ struct mg_mgr { }; struct mg_connection { - struct mg_connection *next; // Linkage in struct mg_mgr :: connections - struct mg_mgr *mgr; // Our container - struct mg_addr loc; // Local address - struct mg_addr rem; // Remote address - void *fd; // Connected socket, or LWIP data - unsigned long id; // Auto-incrementing unique connection ID - struct mg_iobuf recv; // Incoming data - struct mg_iobuf send; // Outgoing data - struct mg_iobuf prof; // Profile data enabled by MG_ENABLE_PROFILE - struct mg_iobuf rtls; // TLS only. Incoming encrypted data - mg_event_handler_t fn; // User-specified event handler function - void *fn_data; // User-specified function parameter - mg_event_handler_t pfn; // Protocol-specific handler function - void *pfn_data; // Protocol-specific function parameter - char data[MG_DATA_SIZE]; // Arbitrary connection data - void *tls; // TLS specific data - unsigned is_listening : 1; // Listening connection - unsigned is_client : 1; // Outbound (client) connection - unsigned is_accepted : 1; // Accepted (server) connection - unsigned is_resolving : 1; // Non-blocking DNS resolution is in progress - unsigned is_arplooking : 1; // Non-blocking ARP resolution is in progress - unsigned is_connecting : 1; // Non-blocking connect is in progress - unsigned is_tls : 1; // TLS-enabled connection - unsigned is_tls_hs : 1; // TLS handshake is in progress - unsigned is_udp : 1; // UDP connection - unsigned is_websocket : 1; // WebSocket connection - unsigned is_mqtt5 : 1; // For MQTT connection, v5 indicator - unsigned is_hexdumping : 1; // Hexdump in/out traffic - unsigned is_draining : 1; // Send remaining data, then close and free - unsigned is_closing : 1; // Close and free the connection immediately - unsigned is_full : 1; // Stop reads, until cleared - unsigned is_resp : 1; // Response is still being generated - unsigned is_readable : 1; // Connection is ready to read - unsigned is_writable : 1; // Connection is ready to write + struct mg_connection *next; // Linkage in struct mg_mgr :: connections + struct mg_mgr *mgr; // Our container + struct mg_addr loc; // Local address + struct mg_addr rem; // Remote address + void *fd; // Connected socket, or LWIP data + unsigned long id; // Auto-incrementing unique connection ID + struct mg_iobuf recv; // Incoming data + struct mg_iobuf send; // Outgoing data + struct mg_iobuf prof; // Profile data enabled by MG_ENABLE_PROFILE + struct mg_iobuf rtls; // TLS only. Incoming encrypted data + mg_event_handler_t fn; // User-specified event handler function + void *fn_data; // User-specified function parameter + mg_event_handler_t pfn; // Protocol-specific handler function + void *pfn_data; // Protocol-specific function parameter + char data[MG_DATA_SIZE]; // Arbitrary connection data + void *tls; // TLS specific data + unsigned is_listening : 1; // Listening connection + unsigned is_client : 1; // Outbound (client) connection + unsigned is_accepted : 1; // Accepted (server) connection + unsigned is_resolving : 1; // Non-blocking DNS resolution is in progress + unsigned is_arplooking : 1; // Non-blocking ARP resolution is in progress + unsigned is_connecting : 1; // Non-blocking connect is in progress + unsigned is_tls : 1; // TLS-enabled connection + unsigned is_tls_hs : 1; // TLS handshake is in progress + unsigned is_udp : 1; // UDP connection + unsigned is_websocket : 1; // WebSocket connection + unsigned is_mqtt5 : 1; // For MQTT connection, v5 indicator + unsigned is_hexdumping : 1; // Hexdump in/out traffic + unsigned is_draining : 1; // Send remaining data, then close and free + unsigned is_closing : 1; // Close and free the connection immediately + unsigned is_full : 1; // Stop reads, until cleared + unsigned is_tls_throttled : 1; // Last TLS write: MG_SOCK_PENDING() was true + unsigned is_resp : 1; // Response is still being generated + unsigned is_readable : 1; // Connection is ready to read + unsigned is_writable : 1; // Connection is ready to write }; void mg_mgr_poll(struct mg_mgr *, int ms); @@ -2425,6 +2426,9 @@ struct mg_tls { #ifdef MBEDTLS_SSL_SESSION_TICKETS mbedtls_ssl_ticket_context ticket; // Session tickets context #endif + // https://github.com/Mbed-TLS/mbedtls/blob/3b3c652d/include/mbedtls/ssl.h#L5071C18-L5076C29 + unsigned char *throttled_buf; // see #3074 + size_t throttled_len; }; #endif diff --git a/src/net.h b/src/net.h index cca92c1e..08d0893c 100644 --- a/src/net.h +++ b/src/net.h @@ -43,40 +43,41 @@ struct mg_mgr { }; struct mg_connection { - struct mg_connection *next; // Linkage in struct mg_mgr :: connections - struct mg_mgr *mgr; // Our container - struct mg_addr loc; // Local address - struct mg_addr rem; // Remote address - void *fd; // Connected socket, or LWIP data - unsigned long id; // Auto-incrementing unique connection ID - struct mg_iobuf recv; // Incoming data - struct mg_iobuf send; // Outgoing data - struct mg_iobuf prof; // Profile data enabled by MG_ENABLE_PROFILE - struct mg_iobuf rtls; // TLS only. Incoming encrypted data - mg_event_handler_t fn; // User-specified event handler function - void *fn_data; // User-specified function parameter - mg_event_handler_t pfn; // Protocol-specific handler function - void *pfn_data; // Protocol-specific function parameter - char data[MG_DATA_SIZE]; // Arbitrary connection data - void *tls; // TLS specific data - unsigned is_listening : 1; // Listening connection - unsigned is_client : 1; // Outbound (client) connection - unsigned is_accepted : 1; // Accepted (server) connection - unsigned is_resolving : 1; // Non-blocking DNS resolution is in progress - unsigned is_arplooking : 1; // Non-blocking ARP resolution is in progress - unsigned is_connecting : 1; // Non-blocking connect is in progress - unsigned is_tls : 1; // TLS-enabled connection - unsigned is_tls_hs : 1; // TLS handshake is in progress - unsigned is_udp : 1; // UDP connection - unsigned is_websocket : 1; // WebSocket connection - unsigned is_mqtt5 : 1; // For MQTT connection, v5 indicator - unsigned is_hexdumping : 1; // Hexdump in/out traffic - unsigned is_draining : 1; // Send remaining data, then close and free - unsigned is_closing : 1; // Close and free the connection immediately - unsigned is_full : 1; // Stop reads, until cleared - unsigned is_resp : 1; // Response is still being generated - unsigned is_readable : 1; // Connection is ready to read - unsigned is_writable : 1; // Connection is ready to write + struct mg_connection *next; // Linkage in struct mg_mgr :: connections + struct mg_mgr *mgr; // Our container + struct mg_addr loc; // Local address + struct mg_addr rem; // Remote address + void *fd; // Connected socket, or LWIP data + unsigned long id; // Auto-incrementing unique connection ID + struct mg_iobuf recv; // Incoming data + struct mg_iobuf send; // Outgoing data + struct mg_iobuf prof; // Profile data enabled by MG_ENABLE_PROFILE + struct mg_iobuf rtls; // TLS only. Incoming encrypted data + mg_event_handler_t fn; // User-specified event handler function + void *fn_data; // User-specified function parameter + mg_event_handler_t pfn; // Protocol-specific handler function + void *pfn_data; // Protocol-specific function parameter + char data[MG_DATA_SIZE]; // Arbitrary connection data + void *tls; // TLS specific data + unsigned is_listening : 1; // Listening connection + unsigned is_client : 1; // Outbound (client) connection + unsigned is_accepted : 1; // Accepted (server) connection + unsigned is_resolving : 1; // Non-blocking DNS resolution is in progress + unsigned is_arplooking : 1; // Non-blocking ARP resolution is in progress + unsigned is_connecting : 1; // Non-blocking connect is in progress + unsigned is_tls : 1; // TLS-enabled connection + unsigned is_tls_hs : 1; // TLS handshake is in progress + unsigned is_udp : 1; // UDP connection + unsigned is_websocket : 1; // WebSocket connection + unsigned is_mqtt5 : 1; // For MQTT connection, v5 indicator + unsigned is_hexdumping : 1; // Hexdump in/out traffic + unsigned is_draining : 1; // Send remaining data, then close and free + unsigned is_closing : 1; // Close and free the connection immediately + unsigned is_full : 1; // Stop reads, until cleared + unsigned is_tls_throttled : 1; // Last TLS write: MG_SOCK_PENDING() was true + unsigned is_resp : 1; // Response is still being generated + unsigned is_readable : 1; // Connection is ready to read + unsigned is_writable : 1; // Connection is ready to write }; void mg_mgr_poll(struct mg_mgr *, int ms); diff --git a/src/tls_builtin.c b/src/tls_builtin.c index 31026b98..4571075d 100644 --- a/src/tls_builtin.c +++ b/src/tls_builtin.c @@ -424,7 +424,7 @@ static void mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg, nonce[8] ^= (uint8_t) ((seq >> 24) & 255U); nonce[9] ^= (uint8_t) ((seq >> 16) & 255U); nonce[10] ^= (uint8_t) ((seq >> 8) & 255U); - nonce[11] ^= (uint8_t) ((seq) & 255U); + nonce[11] ^= (uint8_t) ((seq) &255U); mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr)); mg_iobuf_resize(wio, wio->len + encsz); @@ -503,7 +503,7 @@ static int mg_tls_recv_record(struct mg_connection *c) { nonce[8] ^= (uint8_t) ((seq >> 24) & 255U); nonce[9] ^= (uint8_t) ((seq >> 16) & 255U); nonce[10] ^= (uint8_t) ((seq >> 8) & 255U); - nonce[11] ^= (uint8_t) ((seq) & 255U); + nonce[11] ^= (uint8_t) ((seq) &255U); #if CHACHA20 { uint8_t *dec = (uint8_t *) calloc(1, msgsz); @@ -1261,7 +1261,7 @@ static int mg_tls_client_recv_cert(struct mg_connection *c) { struct mg_tls_cert certs[8]; int certnum = 0; uint8_t *p = recv_buf + 8; - //uint8_t *endp = recv_buf + tls->recv_len; + // uint8_t *endp = recv_buf + tls->recv_len; uint8_t *endp = recv_buf + cert_chain_len; int found_ca = 0; @@ -1370,7 +1370,8 @@ static int mg_tls_client_recv_cert_verify(struct mg_connection *c) { tls->recv_len - 8); return -1; } - MG_VERBOSE(("certificate verification, algo=%04x, siglen=%d", sigalg, siglen)); + MG_VERBOSE( + ("certificate verification, algo=%04x, siglen=%d", sigalg, siglen)); if (sigalg == 0x0804) { // rsa_pss_rsae_sha256 uint8_t sig2[512]; // 2048 or 4096 bits @@ -1523,7 +1524,9 @@ static void mg_tls_client_handshake(struct mg_connection *c) { c->is_tls_hs = 0; mg_call(c, MG_EV_TLS_HS, NULL); break; - default: mg_error(c, "unexpected client state: %d", tls->state); break; + default: + mg_error(c, "unexpected client state: %d", tls->state); + break; } } @@ -1550,7 +1553,9 @@ static void mg_tls_server_handshake(struct mg_connection *c) { tls->state = MG_TLS_STATE_SERVER_CONNECTED; c->is_tls_hs = 0; return; - default: mg_error(c, "unexpected server state: %d", tls->state); break; + default: + mg_error(c, "unexpected server state: %d", tls->state); + break; } } @@ -1684,15 +1689,22 @@ void mg_tls_free(struct mg_connection *c) { long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) { struct tls_data *tls = (struct tls_data *) c->tls; long n = MG_IO_WAIT; - if (len > MG_IO_SIZE) len = MG_IO_SIZE; - if (len > 16384) len = 16384; - mg_tls_encrypt(c, (const uint8_t *) buf, len, MG_TLS_APP_DATA); + bool was_throttled = c->is_tls_throttled; // see #3074 + if (!was_throttled) { // encrypt new data + if (len > MG_IO_SIZE) len = MG_IO_SIZE; + if (len > 16384) len = 16384; + mg_tls_encrypt(c, (const uint8_t *) buf, len, MG_TLS_APP_DATA); + } // else, resend outstanding encrypted data in tls->send while (tls->send.len > 0 && (n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) { mg_iobuf_del(&tls->send, 0, (size_t) n); } - if (n == MG_IO_ERR || n == MG_IO_WAIT) return n; - return (long) len; + c->is_tls_throttled = (tls->send.len > 0 && n == MG_IO_WAIT); + MG_VERBOSE(("%lu %ld %ld %ld %c %c", c->id, (long) len, (long) tls->send.len, + n, was_throttled ? 'T' : 't', c->is_tls_throttled ? 'T' : 't')); + if (n == MG_IO_ERR) return MG_IO_ERR; + if (was_throttled) return MG_IO_WAIT; // sent throttled data instead + return (long) len; // return len even when throttled, already encripted that } long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { diff --git a/src/tls_mbed.c b/src/tls_mbed.c index a0b92048..187b4b1a 100644 --- a/src/tls_mbed.c +++ b/src/tls_mbed.c @@ -190,9 +190,16 @@ long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) { struct mg_tls *tls = (struct mg_tls *) c->tls; - long n = mbedtls_ssl_write(&tls->ssl, (unsigned char *) buf, len); - if (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE) - return MG_IO_WAIT; + long n; + bool was_throttled = c->is_tls_throttled; // see #3074 + n = was_throttled ? mbedtls_ssl_write(&tls->ssl, tls->throttled_buf, + tls->throttled_len) /* flush old data */ + : mbedtls_ssl_write(&tls->ssl, (unsigned char *) buf, + len); // encrypt current data + c->is_tls_throttled = + (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE); + if (was_throttled) return MG_IO_WAIT; // flushed throttled data instead + if (c->is_tls_throttled) return len; // already encripted that when throttled if (n <= 0) return MG_IO_ERR; return n; } diff --git a/src/tls_mbed.h b/src/tls_mbed.h index a2f3b8b4..14fd3e11 100644 --- a/src/tls_mbed.h +++ b/src/tls_mbed.h @@ -27,5 +27,8 @@ struct mg_tls { #ifdef MBEDTLS_SSL_SESSION_TICKETS mbedtls_ssl_ticket_context ticket; // Session tickets context #endif + // https://github.com/Mbed-TLS/mbedtls/blob/3b3c652d/include/mbedtls/ssl.h#L5071C18-L5076C29 + unsigned char *throttled_buf; // see #3074 + size_t throttled_len; }; #endif