From a5db1bbbc6126de4184c64b1f1ca2b7b3a7828f4 Mon Sep 17 00:00:00 2001 From: Daniel-Eisenberg <59121493+Daniel-Eisenberg@users.noreply.github.com> Date: Sun, 10 Aug 2025 13:23:10 +0300 Subject: [PATCH] sync code (#43) Co-authored-by: Ned Wright --- .../nano_attachment_sender_thread.c | 8 +- .../nano_attachment/nano_initializer.c | 2 +- .../nginx/ngx_module/ngx_cp_compression.c | 57 +++++- .../nginx/ngx_module/ngx_cp_compression.h | 1 + .../nginx/ngx_module/ngx_cp_custom_response.c | 12 +- .../nginx/ngx_module/ngx_cp_custom_response.h | 2 +- .../nginx/ngx_module/ngx_cp_hook_threads.c | 166 ++++++++++++++---- .../nginx/ngx_module/ngx_cp_hook_threads.h | 62 +++---- attachments/nginx/ngx_module/ngx_cp_hooks.c | 88 ++++++++-- attachments/nginx/ngx_module/ngx_cp_hooks.h | 1 + .../nginx/ngx_module/ngx_cp_http_parser.c | 13 +- .../nginx/ngx_module/ngx_cp_http_parser.h | 7 + attachments/nginx/ngx_module/ngx_cp_io.c | 121 +++++++------ attachments/nginx/ngx_module/ngx_cp_io.h | 9 +- .../ngx_http_cp_attachment_module.c | 2 +- core/include/attachments/compression_utils.h | 3 +- .../attachments/nginx_attachment_common.h | 5 +- 17 files changed, 402 insertions(+), 157 deletions(-) diff --git a/attachments/nano_attachment/nano_attachment_sender_thread.c b/attachments/nano_attachment/nano_attachment_sender_thread.c index 1f573ec..42c4a0d 100644 --- a/attachments/nano_attachment/nano_attachment_sender_thread.c +++ b/attachments/nano_attachment/nano_attachment_sender_thread.c @@ -14,7 +14,7 @@ static HttpHeaderData * get_http_header(HttpHeaders *http_headers, const char *header_name) { size_t i; for (i = 0; i < http_headers->headers_count; ++i) { - if (strcmp((char*)http_headers->data[i].key.data, header_name) == 0) { + if (strcasecmp((char*)http_headers->data[i].key.data, header_name) == 0) { return &http_headers->data[i]; } } @@ -42,11 +42,11 @@ set_response_content_encoding( return; } - if (strcmp((char*)content_encoding->value.data, "gzip") == 0) { + if (strcasecmp((char*)content_encoding->value.data, "gzip") == 0) { session_data_p->response_data.compression_type = GZIP; - } else if (strcmp((char*)content_encoding->value.data, "deflate") == 0) { + } else if (strcasecmp((char*)content_encoding->value.data, "deflate") == 0) { session_data_p->response_data.compression_type = ZLIB; - } else if (strcmp((char*)content_encoding->value.data, "identity") == 0) { + } else if (strcasecmp((char*)content_encoding->value.data, "identity") == 0) { session_data_p->response_data.compression_type = NO_COMPRESSION; } else { write_dbg( diff --git a/attachments/nano_attachment/nano_initializer.c b/attachments/nano_attachment/nano_initializer.c index 65e5fd4..eaab6a0 100644 --- a/attachments/nano_attachment/nano_initializer.c +++ b/attachments/nano_attachment/nano_initializer.c @@ -385,7 +385,7 @@ set_docker_id(NanoAttachment *attachment) } if (!uid_read) { - const char *env_var_name = "OPENAPPSEC_UID"; // Replace with your environment variable name + const char *env_var_name = "CLOUDGUARD_UID"; // Replace with your environment variable name const char *env_value = getenv(env_var_name); if (env_value) { diff --git a/attachments/nginx/ngx_module/ngx_cp_compression.c b/attachments/nginx/ngx_module/ngx_cp_compression.c index 99a91a7..3678386 100644 --- a/attachments/nginx/ngx_module/ngx_cp_compression.c +++ b/attachments/nginx/ngx_module/ngx_cp_compression.c @@ -153,6 +153,47 @@ set_buffer_data(ngx_buf_t *buffer, const ngx_str_t *data) buffer->end = buffer->last; } +/// +/// @brief Removes empty chunks from the specified NGINX chain. +/// @param[in,out] chain Pointer to the start of the chain to modify. +/// @param[in] pool NGINX pool used for allocating and freeing chain links. +/// @returns ngx_int_t +/// - #NGX_OK +/// - #NGX_ERROR +/// +static ngx_int_t +ngx_chain_remove_empty_chunks(ngx_chain_t **chain, ngx_pool_t *pool) +{ + ngx_chain_t *prev = NULL; + ngx_chain_t *curr = *chain; + size_t chunk_num = 0; + + while (curr != NULL) { + size_t size = curr->buf->last - curr->buf->pos; + if (size == 0) { + write_dbg(DBG_LEVEL_WARNING, "Removing empty chunk from the chain, chunk number: %d", chunk_num); + if (prev == NULL) { + *chain = curr->next; + } else { + prev->next = curr->next; + } + ngx_chain_t *tmp = curr; + curr = curr->next; + ngx_free_chain(pool, tmp); + continue; + } + prev = curr; + curr = curr->next; + chunk_num++; + } + + if (chunk_num == 0) { + write_dbg(DBG_LEVEL_WARNING, "Empty chain after removing empty chunks"); + return NGX_ERROR; + } + return NGX_OK; +} + /// /// @brief Decompresses or compresses the provided data. /// @param[in] should_compress Checks if buffer is used for compression or decompression. @@ -432,11 +473,17 @@ compress_chain( ngx_pool_t *pool ) { + ngx_int_t compression_result; ngx_cp_http_compression_params params; params.compression_type = compression_type; params.is_last_part = is_last_part; - return compression_chain_filter(1, compression_stream, NULL, body, original_body_contents, pool, ¶ms); + compression_result = compression_chain_filter(1, compression_stream, NULL, body, original_body_contents, pool, ¶ms); + // remove empty chunks from the chain to prevent getting nginx alert: "zero size buf in writer" down the line + if (compression_result == NGX_OK) { + compression_result = ngx_chain_remove_empty_chunks(body, pool); + } + return compression_result; } /// @@ -534,6 +581,14 @@ compress_body( return NGX_ERROR; } + if (compression_type == BROTLI) { + // Brotli compression is not supported. + // This if statement serves a case that the compression type is set to BROTLI + // For now, we should not reach inside this function with a compression type of BROTLI. + write_dbg(DBG_LEVEL_WARNING, "Brotli compression is not supported"); + return NGX_ERROR; + } + body_type = chunk_type == REQUEST_BODY ? "request" : "response"; write_dbg( DBG_LEVEL_TRACE, diff --git a/attachments/nginx/ngx_module/ngx_cp_compression.h b/attachments/nginx/ngx_module/ngx_cp_compression.h index 3360c5a..21fbe57 100644 --- a/attachments/nginx/ngx_module/ngx_cp_compression.h +++ b/attachments/nginx/ngx_module/ngx_cp_compression.h @@ -73,6 +73,7 @@ decompress_body( /// @param[in] compression_type Compression type. /// - #GZIP /// - #ZLIB +/// - #BROTLI /// - #NO_COMPRESSION - Serves as a sanity check in case this function is called /// on a compression type of data that isn't defined and will return NGX_ERROR. /// @param[in] chunk_type Body chunk type: diff --git a/attachments/nginx/ngx_module/ngx_cp_custom_response.c b/attachments/nginx/ngx_module/ngx_cp_custom_response.c index 7999c96..6a59416 100644 --- a/attachments/nginx/ngx_module/ngx_cp_custom_response.c +++ b/attachments/nginx/ngx_module/ngx_cp_custom_response.c @@ -205,11 +205,11 @@ ngx_add_event_id_to_header(ngx_http_request_t *request) } ngx_int_t -ngx_http_cp_finalize_rejected_request(ngx_http_request_t *request) +ngx_http_cp_finalize_rejected_request(ngx_http_request_t *request, int is_response_phase) { static u_char text_html[] = {'t', 'e', 'x', 't', '/', 'h', 't', 'm', 'l'}; static size_t size_of_text_html = sizeof(text_html); - ngx_int_t http_res_code, rc; + ngx_int_t http_res_code, rc = NGX_OK; ngx_table_elt_t *location_header; ngx_chain_t out_chain[7]; // http://lxr.nginx.org/source/src/http/ngx_http_special_response.c#0772 int send_response_custom_body = 1; @@ -235,6 +235,12 @@ ngx_http_cp_finalize_rejected_request(ngx_http_request_t *request) goto CUSTOM_RES_OUT; } + if (is_response_phase) { + write_dbg(DBG_LEVEL_DEBUG, "Closing connection in response phase"); + rc = NGX_HTTP_CLOSE; + goto CUSTOM_RES_OUT; + } + if (get_response_code() == NGX_HTTP_TEMPORARY_REDIRECT) { // Handling redirect web response. write_dbg( @@ -791,7 +797,7 @@ ngx_http_cp_body_modifier( if (curr_modification == NULL) return NGX_OK; if (curr_modification->modification.orig_buff_index != cur_body_chunk) continue; - cur_chunk_size = body_chain->buf->last - body_chain->buf->pos; + cur_chunk_size = chain_iter->buf->last - chain_iter->buf->pos; if (cur_chunk_size == 0) { write_dbg(DBG_LEVEL_TRACE, "No need to modify body chunk of size 0. Chunk index: %d", cur_body_chunk); continue; diff --git a/attachments/nginx/ngx_module/ngx_cp_custom_response.h b/attachments/nginx/ngx_module/ngx_cp_custom_response.h index 3ffd797..85ecc8d 100644 --- a/attachments/nginx/ngx_module/ngx_cp_custom_response.h +++ b/attachments/nginx/ngx_module/ngx_cp_custom_response.h @@ -81,7 +81,7 @@ ngx_http_cp_file_response_sender( /// - #NGX_OK /// - #NGX_ERROR /// -ngx_int_t ngx_http_cp_finalize_rejected_request(ngx_http_request_t *request); +ngx_int_t ngx_http_cp_finalize_rejected_request(ngx_http_request_t *request, int is_response_phase); /// /// @brief Modifies headers with the provided modifiers. diff --git a/attachments/nginx/ngx_module/ngx_cp_hook_threads.c b/attachments/nginx/ngx_module/ngx_cp_hook_threads.c index 6cfb03c..46d6b88 100644 --- a/attachments/nginx/ngx_module/ngx_cp_hook_threads.c +++ b/attachments/nginx/ngx_module/ngx_cp_hook_threads.c @@ -165,6 +165,7 @@ end_req_header_handler( return ngx_http_cp_reply_receiver( &session_data_p->remaining_messages_to_reply, &session_data_p->verdict, + &session_data_p->response_data.inspect_all_response_headers, session_data_p->session_id, request, modifications, @@ -189,7 +190,7 @@ ngx_http_cp_req_header_handler_thread(void *_ctx) ngx_uint_t num_messages_sent = 0; ngx_int_t send_header_result; - send_meta_data_result = ngx_http_cp_meta_data_sender(request, session_data_p->session_id, &num_messages_sent); + send_meta_data_result = ngx_http_cp_meta_data_sender(request, session_data_p->session_id, &num_messages_sent, &ctx->waf_tag); if (send_meta_data_result == inspection_irrelevant) { // Ignoring irrelevant requests. session_data_p->verdict = TRAFFIC_VERDICT_IRRELEVANT; @@ -223,8 +224,7 @@ ngx_http_cp_req_header_handler_thread(void *_ctx) &(request->headers_in.headers.part), REQUEST_HEADER, session_data_p->session_id, - &num_messages_sent, - &ctx->waf_tag + &num_messages_sent ); if (send_header_result != NGX_OK) { write_dbg( @@ -281,6 +281,7 @@ ngx_http_cp_req_body_filter_thread(void *_ctx) ctx->res = ngx_http_cp_reply_receiver( &session_data_p->remaining_messages_to_reply, &session_data_p->verdict, + &session_data_p->response_data.inspect_all_response_headers, session_data_p->session_id, request, &ctx->modifications, @@ -322,6 +323,7 @@ ngx_http_cp_req_end_transaction_thread(void *_ctx) ctx->res = ngx_http_cp_reply_receiver( &session_data_p->remaining_messages_to_reply, &session_data_p->verdict, + &session_data_p->response_data.inspect_all_response_headers, session_data_p->session_id, request, &ctx->modifications, @@ -406,35 +408,37 @@ ngx_http_cp_res_header_filter_thread(void *_ctx) THREAD_CTX_RETURN(NGX_HTTP_FORBIDDEN); } session_data_p->response_data.new_compression_type = session_data_p->response_data.original_compression_type; - - // Sends response headers to the nano service. - num_messages_sent = 0; - send_header_result = ngx_http_cp_header_sender( - &request->headers_out.headers.part, - RESPONSE_HEADER, - session_data_p->session_id, - &num_messages_sent, - &ctx->waf_tag - ); - if (send_header_result != NGX_OK) { - write_dbg( - DBG_LEVEL_WARNING, - "Failed to send response headers to the nano service. Session ID: %d", - session_data_p->session_id + + if (session_data_p->response_data.inspect_all_response_headers) { + // Sends response headers to the nano service. + num_messages_sent = 0; + send_header_result = ngx_http_cp_header_sender( + &request->headers_out.headers.part, + RESPONSE_HEADER, + session_data_p->session_id, + &num_messages_sent ); - handle_inspection_failure(inspection_failure_weight, fail_mode_verdict, session_data_p); - if (fail_mode_verdict == NGX_OK) { - THREAD_CTX_RETURN_NEXT_FILTER(); + if (send_header_result != NGX_OK) { + write_dbg( + DBG_LEVEL_WARNING, + "Failed to send response headers to the nano service. Session ID: %d", + session_data_p->session_id + ); + handle_inspection_failure(inspection_failure_weight, fail_mode_verdict, session_data_p); + if (fail_mode_verdict == NGX_OK) { + THREAD_CTX_RETURN_NEXT_FILTER(); + } + THREAD_CTX_RETURN(NGX_HTTP_FORBIDDEN); } - THREAD_CTX_RETURN(NGX_HTTP_FORBIDDEN); - } - session_data_p->remaining_messages_to_reply += num_messages_sent; + session_data_p->remaining_messages_to_reply += num_messages_sent; + } // Fetch nano services' results. ctx->res = ngx_http_cp_reply_receiver( &session_data_p->remaining_messages_to_reply, &session_data_p->verdict, + &session_data_p->response_data.inspect_all_response_headers, session_data_p->session_id, request, &ctx->modifications, @@ -455,7 +459,13 @@ ngx_http_cp_res_body_filter_thread(void *_ctx) ngx_uint_t num_messages_sent = 0; ngx_int_t is_last_response_part = 0; - // Send response body data to the nano service. + write_dbg( + DBG_LEVEL_DEBUG, + "Starting response body filter thread for session ID: %d, current verdict: %d", + session_data_p->session_id, + session_data_p->verdict + ); + send_body_result = ngx_http_cp_body_sender( ctx->chain, RESPONSE_BODY, @@ -464,6 +474,16 @@ ngx_http_cp_res_body_filter_thread(void *_ctx) &num_messages_sent, &ctx->chain ); + + write_dbg( + DBG_LEVEL_DEBUG, + "Body sender result: %d, num_messages_sent: %d, is_last_response_part: %d for session ID: %d", + send_body_result, + num_messages_sent, + is_last_response_part, + session_data_p->session_id + ); + if (send_body_result != NGX_OK) { write_dbg( DBG_LEVEL_WARNING, @@ -478,9 +498,48 @@ ngx_http_cp_res_body_filter_thread(void *_ctx) } session_data_p->remaining_messages_to_reply += num_messages_sent; + ctx->res = ngx_http_cp_reply_receiver( + &session_data_p->remaining_messages_to_reply, + &session_data_p->verdict, + &session_data_p->response_data.inspect_all_response_headers, + session_data_p->session_id, + request, + &ctx->modifications, + RESPONSE_BODY, + session_data_p->processed_res_body_size + ); + + write_dbg( + DBG_LEVEL_DEBUG, + "Reply receiver returned: %d, got verdict: %d, remaining messages: %d for session ID: %d", + ctx->res, + session_data_p->verdict, + session_data_p->remaining_messages_to_reply, + session_data_p->session_id + ); + + + if (session_data_p->verdict == TRAFFIC_VERDICT_WAIT) { + if (!ngx_http_cp_hold_verdict(ctx)) { + session_data_p->verdict = fail_mode_hold_verdict == NGX_OK ? TRAFFIC_VERDICT_ACCEPT : TRAFFIC_VERDICT_DROP; + updateMetricField(HOLD_THREAD_TIMEOUT, 1); + handle_inspection_failure(inspection_failure_weight, fail_mode_verdict, session_data_p); + if (fail_mode_verdict == NGX_OK) { + THREAD_CTX_RETURN_NEXT_FILTER(); + } + THREAD_CTX_RETURN(NGX_HTTP_FORBIDDEN); + } + } + num_messages_sent = 0; if (is_last_response_part) { // Signals the nano service that the transaction reached the end. + write_dbg( + DBG_LEVEL_DEBUG, + "This is the last response part, sending RESPONSE_END signal for session ID: %d", + session_data_p->session_id + ); + if (ngx_http_cp_end_transaction_sender(RESPONSE_END, session_data_p->session_id, &num_messages_sent) != NGX_OK) { write_dbg( DBG_LEVEL_WARNING, @@ -493,19 +552,51 @@ ngx_http_cp_res_body_filter_thread(void *_ctx) } THREAD_CTX_RETURN(NGX_HTTP_FORBIDDEN); } - session_data_p->remaining_messages_to_reply++; - } - // Fetch nano services' results. - ctx->res = ngx_http_cp_reply_receiver( - &session_data_p->remaining_messages_to_reply, - &session_data_p->verdict, - session_data_p->session_id, - request, - &ctx->modifications, - RESPONSE_BODY, - session_data_p->processed_res_body_size - ); + if ( + session_data_p->verdict == TRAFFIC_VERDICT_ACCEPT || + session_data_p->verdict == TRAFFIC_VERDICT_DROP + ) { + write_dbg( + DBG_LEVEL_DEBUG, + "Will not wait for verdict after RESPONSE_END as we already have verdict %d for session ID %d", + session_data_p->session_id, + session_data_p->verdict + ); + + return NULL; + } + + session_data_p->remaining_messages_to_reply += num_messages_sent; + ctx->res = ngx_http_cp_reply_receiver( + &session_data_p->remaining_messages_to_reply, + &session_data_p->verdict, + &session_data_p->response_data.inspect_all_response_headers, + session_data_p->session_id, + request, + &ctx->modifications, + RESPONSE_END, + session_data_p->processed_res_body_size + ); + write_dbg( + DBG_LEVEL_DEBUG, + "Received verdict %d after RESPONSE_END for session ID %d, will proceed normally", + session_data_p->verdict, + session_data_p->session_id + ); + + if (session_data_p->verdict == TRAFFIC_VERDICT_WAIT) { + if (!ngx_http_cp_hold_verdict(ctx)) { + session_data_p->verdict = fail_mode_hold_verdict == NGX_OK ? TRAFFIC_VERDICT_ACCEPT : TRAFFIC_VERDICT_DROP; + updateMetricField(HOLD_THREAD_TIMEOUT, 1); + handle_inspection_failure(inspection_failure_weight, fail_mode_verdict, session_data_p); + if (fail_mode_verdict == NGX_OK) { + THREAD_CTX_RETURN_NEXT_FILTER(); + } + THREAD_CTX_RETURN(NGX_HTTP_FORBIDDEN); + } + } + } return NULL; } @@ -537,6 +628,7 @@ ngx_http_cp_hold_verdict_thread(void *_ctx) ctx->res = ngx_http_cp_reply_receiver( &session_data_p->remaining_messages_to_reply, &session_data_p->verdict, + &session_data_p->response_data.inspect_all_response_headers, session_data_p->session_id, request, &ctx->modifications, diff --git a/attachments/nginx/ngx_module/ngx_cp_hook_threads.h b/attachments/nginx/ngx_module/ngx_cp_hook_threads.h index 5043ffc..a7de641 100644 --- a/attachments/nginx/ngx_module/ngx_cp_hook_threads.h +++ b/attachments/nginx/ngx_module/ngx_cp_hook_threads.h @@ -47,23 +47,23 @@ struct ngx_http_cp_event_thread_ctx_t ngx_str_t waf_tag; ///< WAF tag value for the location block. }; -/// +/// /// @brief Modifies already_registered value. /// already_registered is a value that symbolize a successful registeration of the thread context with the nano service. /// @param[in] value set registration with the nano service: /// - #0 Thread is not registered with the nano service. /// - #1 Thread is registered with the nano service. /// @returns current registration status. -/// +/// void set_already_registered(ngx_int_t value); -/// +/// /// @brief Returns already_registered value. /// already_registered is a value that symbolize a successful registeration of the thread context with the nano service. /// @returns ngx_in_t get registration value with the nano service: /// - #0 Thread is not registered with the nano service. /// - #1 Thread is registered with the nano service. -/// +/// ngx_int_t get_already_registered(void); /// @@ -86,17 +86,17 @@ void reset_registration_timeout(void); /// ngx_int_t is_registration_timeout_reached(void); -/// +/// /// @brief Initates ngx_http_cp_event_thread_ctx_t struct. /// @param[in, out] ctx struct to initiate. /// @param[in] request /// @param[in] session_data_p /// @param[in] chain -/// Modifies _ctx res to the following values: +/// Modifies _ctx res to the following values: /// - #NGX_OK /// - #NGX_ERROR /// @return NULL. -/// +/// void init_thread_ctx( struct ngx_http_cp_event_thread_ctx_t *ctx, @@ -105,102 +105,102 @@ init_thread_ctx( ngx_chain_t *chain ); -/// +/// /// @brief Registers the context against the nano agent. /// @note _ctx needs to be properly initialized by init_thread_ctx(). /// @param[in, out] _ctx is of type ngx_http_cp_event_thread_ctx_t. -/// Modifies _ctx res to the following values: +/// Modifies _ctx res to the following values: /// - #NGX_OK /// - #NGX_ERROR /// @return NULL. -/// +/// void * ngx_http_cp_registration_thread(void *_ctx); -/// +/// /// @brief Sends request headers to the attachment's service. /// @details Communicates with the attachment service by sending request headers to the attachment's service /// and modifies _ctx by the received response. /// @note _ctx needs to be properly initialized by init_thread_ctx(). /// @param[in, out] _ctx is of type ngx_http_cp_event_thread_ctx_t. -/// Modifies _ctx res to the following values: +/// Modifies _ctx res to the following values: /// - #NGX_OK /// - #NGX_ERROR /// @return NULL. -/// +/// void * ngx_http_cp_req_header_handler_thread(void *_ctx); -/// +/// /// @brief Sends request body to the attachment's service. /// @details Communicates with the attachment service by sending request body to the attachment's service /// and modifies _ctx by the received response. /// @note _ctx needs to be properly initialized by init_thread_ctx() and ngx_chain_t needs of not NULL. /// @param[in, out] _ctx is of type ngx_http_cp_event_thread_ctx_t. -/// Modifies _ctx res to the following values: +/// Modifies _ctx res to the following values: /// - #NGX_OK /// - #NGX_ERROR /// @return NULL. -/// +/// void * ngx_http_cp_req_body_filter_thread(void *_ctx); -/// +/// /// @brief Sends end request transmission to the attachment's service. /// @details Communicates with the attachment service by sending request body to the attachment's service /// and modifies _ctx by the received response. /// @note _ctx needs to be properly initialized by init_thread_ctx() and ngx_chain_t needs of not NULL. /// @param[in, out] _ctx is of type ngx_http_cp_event_thread_ctx_t. -/// Modifies _ctx res to the following values: +/// Modifies _ctx res to the following values: /// - #NGX_OK /// - #NGX_ERROR /// @return NULL. -/// +/// void * ngx_http_cp_req_end_transaction_thread(void *_ctx); -/// +/// /// @brief Sends response headers to the attachment's service. /// @details Communicates with the attachment service by sending response headers to the attachment's service /// and modifies _ctx by the received response. /// @note _ctx needs to be properly initialized by init_thread_ctx(). /// @param[in, out] _ctx is of type ngx_http_cp_event_thread_ctx_t. -/// Modifies _ctx res to the following values: +/// Modifies _ctx res to the following values: /// - #NGX_OK /// - #NGX_ERROR /// @return NULL. -/// +/// void * ngx_http_cp_res_header_filter_thread(void *_ctx); -/// +/// /// @brief Sends response body to the attachment's service. /// @details Communicates with the attachment service by sending response bodies to the attachment's service /// and modifies _ctx by the received response. /// @note _ctx needs to be properly initialized by init_thread_ctx() and ngx_chain_t needs to be defined. /// @param[in, out] _ctx is of type ngx_http_cp_event_thread_ctx_t. -/// Modifies _ctx res to the following values: +/// Modifies _ctx res to the following values: /// - #NGX_OK /// - #NGX_ERROR /// @return NULL. -/// +/// void * ngx_http_cp_res_body_filter_thread(void *_ctx); -/// +/// /// @brief Sends a request to the attachment's service to update the earlier provided "WAIT" verdict. /// @details Communicates with the attachment service by sending a HOLD_DATA request to the attachment's service /// and modifies _ctx by the received response. -/// @note _ctx needs to be properly initialized by init_thread_ctx() and +/// @note _ctx needs to be properly initialized by init_thread_ctx() and /// be called after another call returned wait verdict. /// @param[in, out] _ctx is of type ngx_http_cp_event_thread_ctx_t. -/// Modifies _ctx res to the following values: +/// Modifies _ctx res to the following values: /// - #NGX_OK /// - #NGX_ERROR /// Modifies _ctx session data with an updated verdict. /// @return NULL. -/// +/// void * ngx_http_cp_hold_verdict_thread(void *_ctx); -/// +/// /// @brief Check if transaction contains headers. /// @param[in] headers ngx_http_headers_in_t struct. /// @returns 1 if the transaction contains headers, otherwise 0. -/// +/// ngx_int_t does_contain_body(ngx_http_headers_in_t *headers); #endif // __NGX_CP_HOOK_THREADS_H__ diff --git a/attachments/nginx/ngx_module/ngx_cp_hooks.c b/attachments/nginx/ngx_module/ngx_cp_hooks.c index 2ecca65..7ea7eee 100644 --- a/attachments/nginx/ngx_module/ngx_cp_hooks.c +++ b/attachments/nginx/ngx_module/ngx_cp_hooks.c @@ -78,6 +78,7 @@ init_cp_session_data(ngx_http_request_t *request) session_data->response_data.response_data_status = NGX_OK; session_data->response_data.original_compressed_body = NULL; session_data->response_data.request_pool = NULL; + session_data->response_data.inspect_all_response_headers = 1; if (!metric_timeout.tv_sec) { metric_timeout = get_timeout_val_sec(METRIC_TIMEOUT_VAL); } @@ -86,6 +87,7 @@ init_cp_session_data(ngx_http_request_t *request) session_data->res_proccesing_time = 0; session_data->processed_req_body_size = 0; session_data->processed_res_body_size = 0; + session_data->is_res_body_inspected = 0; ngx_http_set_ctx(request, session_data, ngx_http_cp_attachment_module); @@ -334,7 +336,7 @@ ngx_http_cp_finalize_request_headers_hook( if (final_res == NGX_HTTP_FORBIDDEN) { handle_inspection_success(session_data_p); - return ngx_http_cp_finalize_rejected_request(request); + return ngx_http_cp_finalize_rejected_request(request, 0); } if (final_res != NGX_OK) { @@ -715,6 +717,15 @@ ngx_http_cp_req_body_filter(ngx_http_request_t *request, ngx_chain_t *request_bo return fail_mode_verdict == NGX_OK ? ngx_http_next_request_body_filter(request, request_body_chain) : NGX_HTTP_FORBIDDEN; } + if (session_data_p->verdict == TRAFFIC_VERDICT_WAIT) { + write_dbg(DBG_LEVEL_DEBUG, "spawn ngx_http_cp_hold_verdict"); + res = ngx_http_cp_hold_verdict(&ctx); + if (!res) { + write_dbg(DBG_LEVEL_DEBUG, "ngx_http_cp_hold_verdict failed"); + updateMetricField(HOLD_THREAD_TIMEOUT, 1); + } + } + write_dbg( DBG_LEVEL_DEBUG, "finished ngx_http_cp_req_end_transaction_thread successfully. return=%d next_filter=%d res=%d", @@ -754,7 +765,7 @@ ngx_http_cp_req_body_filter(ngx_http_request_t *request, ngx_chain_t *request_bo if (final_res == NGX_HTTP_FORBIDDEN) { handle_inspection_success(session_data_p); - return ngx_http_cp_finalize_rejected_request(request); + return ngx_http_cp_finalize_rejected_request(request, 0); } if (final_res != NGX_OK) { @@ -799,7 +810,7 @@ ngx_http_cp_req_body_filter(ngx_http_request_t *request, ngx_chain_t *request_bo /// /// @brief Removes the "Server" header from the HTTP response. -/// +/// /// This function modifies the `headers_out` structure of the given HTTP request /// to remove the "Server" header. If the header is already removed, it returns `NGX_OK`. /// Otherwise, it allocates a new header entry, sets its key to "Server" and its value to an @@ -845,7 +856,7 @@ ngx_http_cp_res_header_filter(ngx_http_request_t *request) set_current_session_id(0); session_data_p = recover_cp_session_data(request); - + if (remove_res_server_header) remove_server_header(request); if (session_data_p == NULL) return ngx_http_next_response_header_filter(request); @@ -934,7 +945,7 @@ ngx_http_cp_res_header_filter(ngx_http_request_t *request) if (final_res == NGX_HTTP_FORBIDDEN) { handle_inspection_success(session_data_p); - return ngx_http_cp_finalize_rejected_request(request); + return ngx_http_cp_finalize_rejected_request(request, 0); } if (final_res != NGX_OK) { @@ -961,7 +972,7 @@ ngx_http_cp_res_header_filter(ngx_http_request_t *request) return NGX_ERROR; } } - + handle_inspection_success(session_data_p); return ngx_http_next_response_header_filter(request); } @@ -1005,23 +1016,48 @@ ngx_http_cp_res_body_filter(ngx_http_request_t *request, ngx_chain_t *body_chain if (session_data_p->response_data.response_data_status != NGX_OK) { write_dbg(DBG_LEVEL_WARNING, "skipping session with corrupted compression"); updateMetricField(CORRUPTED_ZIP_SKIPPED_SESSION_COUNT, 1); - if (session_data_p->verdict == TRAFFIC_VERDICT_DROP) request->keepalive = 0; + if (session_data_p->verdict == TRAFFIC_VERDICT_DROP) { + request->keepalive = 0; + } + + if (session_data_p->verdict == TRAFFIC_VERDICT_DROP && session_data_p->is_res_body_inspected) { + write_dbg( + DBG_LEVEL_DEBUG, + "Session with corrupted compression has DROP verdict, returning HTTP_FORBIDDEN. Session ID: %d", + session_data_p->session_id + ); + return NGX_HTTP_FORBIDDEN; + } return ngx_http_next_response_body_filter(request, body_chain); } if ( session_data_p->verdict != TRAFFIC_VERDICT_INSPECT && + session_data_p->verdict != TRAFFIC_VERDICT_WAIT && ( session_data_p->verdict != TRAFFIC_VERDICT_ACCEPT || session_data_p->response_data.new_compression_type == NO_COMPRESSION || + session_data_p->response_data.new_compression_type == BROTLI || session_data_p->response_data.num_body_chunk == 0 ) ) { write_dbg(DBG_LEVEL_TRACE, "skipping already inspected session"); - if (session_data_p->verdict == TRAFFIC_VERDICT_DROP) request->keepalive = 0; + if (session_data_p->verdict == TRAFFIC_VERDICT_DROP) { + request->keepalive = 0; + } + + if (session_data_p->verdict == TRAFFIC_VERDICT_DROP && session_data_p->is_res_body_inspected) { + write_dbg( + DBG_LEVEL_DEBUG, + "Session has DROP verdict, returning HTTP_FORBIDDEN instead of streaming. Session ID: %d", + session_data_p->session_id + ); + return NGX_HTTP_FORBIDDEN; + } return ngx_http_next_response_body_filter(request, body_chain); } + session_data_p->is_res_body_inspected = 1; session_data_p->response_data.num_body_chunk++; if (body_chain == NULL) { @@ -1033,7 +1069,7 @@ ngx_http_cp_res_body_filter(ngx_http_request_t *request, ngx_chain_t *body_chain return ngx_http_next_response_body_filter(request, body_chain); } - if (body_chain->buf->pos != NULL && session_data_p->response_data.new_compression_type != NO_COMPRESSION) { + if (body_chain->buf->pos != NULL && session_data_p->response_data.new_compression_type != NO_COMPRESSION && session_data_p->response_data.new_compression_type != BROTLI) { write_dbg(DBG_LEVEL_TRACE, "Decompressing response body"); if (init_cp_session_original_body(session_data_p, request->pool) == NGX_OK) { if (session_data_p->response_data.decompression_stream == NULL) { @@ -1208,7 +1244,7 @@ ngx_http_cp_res_body_filter(ngx_http_request_t *request, ngx_chain_t *body_chain if (final_res == NGX_HTTP_FORBIDDEN) { handle_inspection_success(session_data_p); - return ngx_http_cp_finalize_rejected_request(request); + return ngx_http_cp_finalize_rejected_request(request, 1); } if (final_res != NGX_OK) { @@ -1224,7 +1260,7 @@ ngx_http_cp_res_body_filter(ngx_http_request_t *request, ngx_chain_t *body_chain return NGX_HTTP_FORBIDDEN; } - if (ctx.modifications) { + if (ctx.modifications && session_data_p->response_data.new_compression_type != BROTLI) { write_dbg(DBG_LEVEL_TRACE, "Handling response body modification"); if (ngx_http_cp_body_modifier(body_chain, ctx.modifications, request->pool) != NGX_OK) { write_dbg(DBG_LEVEL_WARNING, "Failed to modify response body"); @@ -1236,6 +1272,16 @@ ngx_http_cp_res_body_filter(ngx_http_request_t *request, ngx_chain_t *body_chain return NGX_HTTP_FORBIDDEN; } } + + if (ctx.modifications && session_data_p->response_data.new_compression_type == BROTLI) { + ngx_http_cp_modification_list *mod = ctx.modifications; + while (mod != NULL) { + ngx_http_cp_modification_list *next_mod = mod->next; + ngx_pfree(request->pool, mod); + mod = next_mod; + } + ctx.modifications = NULL; + } if ( session_data_p->verdict == TRAFFIC_VERDICT_ACCEPT && @@ -1249,7 +1295,7 @@ ngx_http_cp_res_body_filter(ngx_http_request_t *request, ngx_chain_t *body_chain return ngx_http_next_response_body_filter(request, body_chain); } - if (session_data_p->response_data.new_compression_type != NO_COMPRESSION) { + if (session_data_p->response_data.new_compression_type != NO_COMPRESSION && session_data_p->response_data.new_compression_type != BROTLI) { if (session_data_p->response_data.compression_stream == NULL) { session_data_p->response_data.compression_stream = initCompressionStream(); } @@ -1275,5 +1321,23 @@ ngx_http_cp_res_body_filter(ngx_http_request_t *request, ngx_chain_t *body_chain print_buffer_chain(body_chain, "outgoing", 32, DBG_LEVEL_TRACE); + // Check final verdict before streaming data to client + // This prevents malicious data from reaching client when verdict is DROP + if (session_data_p->verdict == TRAFFIC_VERDICT_DROP) { + write_dbg( + DBG_LEVEL_DEBUG, + "Final verdict is DROP, blocking stream to client. Session ID: %d", + session_data_p->session_id + ); + return NGX_HTTP_FORBIDDEN; + } + + write_dbg( + DBG_LEVEL_DEBUG, + "Final verdict is %d, streaming to client. Session ID: %d", + session_data_p->verdict, + session_data_p->session_id + ); + return ngx_http_next_response_body_filter(request, body_chain); } diff --git a/attachments/nginx/ngx_module/ngx_cp_hooks.h b/attachments/nginx/ngx_module/ngx_cp_hooks.h index 72d036f..49e7737 100644 --- a/attachments/nginx/ngx_module/ngx_cp_hooks.h +++ b/attachments/nginx/ngx_module/ngx_cp_hooks.h @@ -45,6 +45,7 @@ typedef struct ngx_http_cp_session_data { double res_proccesing_time; ///< Holds session's response processing time. uint64_t processed_req_body_size; ///< Holds session's request body's size. uint64_t processed_res_body_size; ///< Holds session's response body's size'. + ngx_int_t is_res_body_inspected; ///< Holds if the response body was inspected } ngx_http_cp_session_data; /// diff --git a/attachments/nginx/ngx_module/ngx_cp_http_parser.c b/attachments/nginx/ngx_module/ngx_cp_http_parser.c index 0f61410..0d3a303 100644 --- a/attachments/nginx/ngx_module/ngx_cp_http_parser.c +++ b/attachments/nginx/ngx_module/ngx_cp_http_parser.c @@ -19,24 +19,31 @@ static const char *gzip_encoding_string = "gzip"; static const char *zlib_encoding_string = "deflate"; +static const char *brotli_encoding_string = "br"; static const char *identity_encoding_string = "identity"; ngx_int_t parse_content_encoding(CompressionType *response_encoding, const ngx_str_t *content_encoding_header_value) { - if (ngx_strcmp(content_encoding_header_value->data, gzip_encoding_string) == 0) { + if (ngx_strcasecmp(content_encoding_header_value->data, (u_char*)gzip_encoding_string) == 0) { /// Sets GZIP encoding. *response_encoding = GZIP; return NGX_OK; } - if (ngx_strcmp(content_encoding_header_value->data, zlib_encoding_string) == 0) { + if (ngx_strcasecmp(content_encoding_header_value->data, (u_char*)zlib_encoding_string) == 0) { /// Sets GZIP encoding. *response_encoding = ZLIB; return NGX_OK; } - if (ngx_strcmp(content_encoding_header_value->data, identity_encoding_string) == 0) { + if (ngx_strcasecmp(content_encoding_header_value->data, (u_char*)brotli_encoding_string) == 0) { + /// Sets Brotli encoding. + *response_encoding = BROTLI; + return NGX_OK; + } + + if (ngx_strcasecmp(content_encoding_header_value->data, (u_char*)identity_encoding_string) == 0) { /// Sets NO_COMPRESSION encoding. *response_encoding = NO_COMPRESSION; return NGX_OK; diff --git a/attachments/nginx/ngx_module/ngx_cp_http_parser.h b/attachments/nginx/ngx_module/ngx_cp_http_parser.h index a93312a..bc36fee 100644 --- a/attachments/nginx/ngx_module/ngx_cp_http_parser.h +++ b/attachments/nginx/ngx_module/ngx_cp_http_parser.h @@ -30,6 +30,11 @@ typedef struct { /// - #NGX_ERROR ngx_int_t response_data_status; + /// This flag determines whether all response headers should be sent to a service for inspection. + /// - If set to 1, all response headers will be sent. + /// - If set to 0, only the Content-Length, response code, and encoding checks for the response body will be sent. + ngx_int_t inspect_all_response_headers; + /// Original compression type, can hold the following values: /// - #GZIP /// - #ZLIB @@ -58,6 +63,7 @@ typedef struct { /// @param[in, out] response_encoding Returns value of one of the supported encoding: /// - #GZIP /// - #ZLIB +/// - #BROTLI /// - #NO_COMPRESSION /// @param[in, out] content_encoding_header_value Encoded value. /// @return ngx_int_t @@ -75,6 +81,7 @@ parse_content_encoding( /// @param[in, out] content_encoding Returns variable of one of the supported encoding: /// - #GZIP /// - #ZLIB +/// - #BROTLI /// - #NO_COMPRESSION /// @param[in, out] content_encoding_header NGINX table Encoding header. /// @return ngx_int_t diff --git a/attachments/nginx/ngx_module/ngx_cp_io.c b/attachments/nginx/ngx_module/ngx_cp_io.c index 55e3003..526e126 100644 --- a/attachments/nginx/ngx_module/ngx_cp_io.c +++ b/attachments/nginx/ngx_module/ngx_cp_io.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "ngx_cp_utils.h" #include "ngx_cp_initializer.h" @@ -452,6 +453,7 @@ ngx_int_t ngx_http_cp_reply_receiver( ngx_int_t *expected_replies, ngx_http_cp_verdict_e *verdict, + ngx_int_t *inspect_all_response_headers, uint32_t cur_session_id, ngx_http_request_t *request, ngx_http_cp_modification_list **modification_list, @@ -492,7 +494,7 @@ ngx_http_cp_reply_receiver( return NGX_ERROR; } - if (reply_p->verdict != TRAFFIC_VERDICT_RECONF) { + if (reply_p->verdict != TRAFFIC_VERDICT_RECONF && reply_p->verdict != LIMIT_RESPONSE_HEADERS) { // Handling reconfiguration verdict. if (reply_p->session_id != cur_session_id) { write_dbg(DBG_LEVEL_DEBUG, "Ignoring verdict to an already handled request %d", reply_p->session_id); @@ -600,6 +602,12 @@ ngx_http_cp_reply_receiver( updateMetricField(HOLD_VERDICTS_COUNT, 1); break; } + + case LIMIT_RESPONSE_HEADERS: { + write_dbg(DBG_LEVEL_DEBUG, "Verdict ignore response headers received from the nano service"); + *inspect_all_response_headers = 0; + break; + } } free_data_from_service(); @@ -662,7 +670,7 @@ convert_sock_addr_to_string(const struct sockaddr *sa, char *ip_addr) } ngx_int_t -ngx_http_cp_meta_data_sender(ngx_http_request_t *request, uint32_t cur_request_id, ngx_uint_t *num_messages_sent) +ngx_http_cp_meta_data_sender(ngx_http_request_t *request, uint32_t cur_request_id, ngx_uint_t *num_messages_sent, ngx_str_t *waf_tag) { char client_ip[INET6_ADDRSTRLEN]; char listening_ip[INET6_ADDRSTRLEN]; @@ -788,6 +796,16 @@ ngx_http_cp_meta_data_sender(ngx_http_request_t *request, uint32_t cur_request_i set_fragment_elem(fragments, fragments_sizes, &parsed_uri.len, sizeof(uint16_t), PARSED_URI_SIZE + 2); set_fragment_elem(fragments, fragments_sizes, parsed_uri.data, parsed_uri.len, PARSED_URI_DATA + 2); + // Add WAF tag data if provided + if (waf_tag != NULL && waf_tag->len > 0) { + set_fragment_elem(fragments, fragments_sizes, &waf_tag->len, sizeof(uint16_t), WAF_TAG_SIZE + 2); + set_fragment_elem(fragments, fragments_sizes, waf_tag->data, waf_tag->len, WAF_TAG_DATA + 2); + } else { + uint16_t zero = 0; + set_fragment_elem(fragments, fragments_sizes, &zero, sizeof(uint16_t), WAF_TAG_SIZE + 2); + set_fragment_elem(fragments, fragments_sizes, "", 0, WAF_TAG_DATA + 2); + } + // Sends all the data to the nano service. res = ngx_http_cp_send_data_to_service(fragments, fragments_sizes, META_DATA_COUNT + 2, cur_request_id, NULL, fail_open_timeout, min_retries_for_verdict); if (res != NGX_OK) { @@ -1008,8 +1026,7 @@ ngx_http_cp_header_sender( ngx_list_part_t *headers_list, ngx_http_chunk_type_e header_type, uint32_t cur_request_id, - ngx_uint_t *num_messages_sent, - ngx_str_t *waf_tag + ngx_uint_t *num_messages_sent ) { ngx_uint_t header_idx = 0; @@ -1025,7 +1042,6 @@ ngx_http_cp_header_sender( const ngx_uint_t max_bulk_size = 10; char *fragments[HEADER_DATA_COUNT * max_bulk_size + 4]; uint16_t fragments_sizes[HEADER_DATA_COUNT * max_bulk_size + 4]; - ngx_flag_t waf_tag_found = 0; write_dbg( DBG_LEVEL_TRACE, @@ -1036,38 +1052,6 @@ ngx_http_cp_header_sender( // Sets fragments identifier to the provided body type. set_fragments_identifiers(fragments, fragments_sizes, (uint16_t *)&header_type, &cur_request_id); - // If waf_tag is provided and valid, check for existing x-waf-tag headers - if (waf_tag != NULL && waf_tag->len > 0) { - for (headers_iter = headers_list; headers_iter; headers_iter = headers_iter->next) { - headers_to_inspect = headers_iter->elts; - for (header_idx = 0; header_idx < headers_iter->nelts; ++header_idx) { - header = headers_to_inspect + header_idx; - if (header->key.len == 9 && ngx_strncasecmp(header->key.data, (u_char *)"x-waf-tag", 9) == 0) { - // Found existing x-waf-tag header, override its value - // header->value = *waf_tag; - waf_tag_found = 1; - write_dbg(DBG_LEVEL_DEBUG, "Overriding existing x-waf-tag header with value: %.*s", waf_tag->len, waf_tag->data); - break; - } - } - if (waf_tag_found) break; - } - - // If no existing x-waf-tag header found, add a new one - if (!waf_tag_found) { - ngx_table_elt_t waf_header; - waf_header.hash = 1; - ngx_str_set(&waf_header.key, "x-waf-tag"); - waf_header.value = *waf_tag; - waf_header.lowcase_key = NULL; // Not needed for sending to agent - - add_header_to_bulk(fragments, fragments_sizes, &waf_header, idx_in_bulk); - idx_in_bulk++; - part_count++; - write_dbg(DBG_LEVEL_DEBUG, "Adding new x-waf-tag header with value: %.*s", waf_tag->len, waf_tag->data); - } - } - for (headers_iter = headers_list; headers_iter ; headers_iter = headers_iter->next) { // Going over the header list. for (header_idx = 0 ; header_idx < headers_iter->nelts ; ++header_idx) { @@ -1085,16 +1069,7 @@ ngx_http_cp_header_sender( is_last_part = (headers_iter->next == NULL && header_idx + 1 == headers_iter->nelts) ? 1 : 0; // Create a header bulk to send. - if (waf_tag_found && header->key.len == 9 && ngx_strncasecmp(header->key.data, (u_char *)"x-waf-tag", 9) == 0) { - ngx_table_elt_t waf_header; - waf_header.hash = 1; - ngx_str_set(&waf_header.key, "x-waf-tag"); - waf_header.value = *waf_tag; - waf_header.lowcase_key = NULL; - add_header_to_bulk(fragments, fragments_sizes, &waf_header, idx_in_bulk); - } else { - add_header_to_bulk(fragments, fragments_sizes, header, idx_in_bulk); - } + add_header_to_bulk(fragments, fragments_sizes, header, idx_in_bulk); idx_in_bulk++; part_count++; @@ -1152,16 +1127,20 @@ ngx_http_cp_body_sender( ngx_int_t res = NGX_ERROR; uint8_t is_last_chunk; uint8_t part_count; + size_t buf_size; ngx_int_t tout_retries = min_retries_for_verdict; char *fragments[num_body_chunk_fragments]; uint16_t fragments_sizes[num_body_chunk_fragments]; int was_waiting = 0; - + int max_chunks_to_process = (body_type == RESPONSE_BODY) ? 1 : INT_MAX; + int chunks_processed = 0; + write_dbg( - DBG_LEVEL_TRACE, - "Sending %s body chunk from session id %d for inspection", + DBG_LEVEL_DEBUG, + "Started %s body sender for session ID %d, max_chunks_to_process: %d", body_type == REQUEST_BODY ? "request" : "response", - session_data->session_id + session_data->session_id, + max_chunks_to_process ); // Sets fragments identifier to the provided body type. @@ -1169,13 +1148,24 @@ ngx_http_cp_body_sender( num_parts_sent = 0; part_count = 0; - for (chain_iter = input; chain_iter; chain_iter = chain_iter->next) { + + for (chain_iter = input; chain_iter && chunks_processed < max_chunks_to_process; chain_iter = chain_iter->next) { // For each NGINX buffer, fragment the buffer and then send the fragments to the nano service. buf = chain_iter->buf; is_last_chunk = buf->last_buf ? 1 : 0; - write_dbg(DBG_LEVEL_TRACE, "Sending last_buf: %d, part_count: %d", buf->last_buf ? 1: 0, part_count); + buf_size = buf->last - buf->pos; + + write_dbg( + DBG_LEVEL_DEBUG, + "Processing %s body chunk %d of size: %d, last_chunk: %d for session ID: %d", + body_type == REQUEST_BODY ? "request" : "response", + part_count, + buf_size, + is_last_chunk, + session_data->session_id + ); - if (buf->last - buf->pos > 0 || is_last_chunk) { + if (buf_size > 0 || is_last_chunk) { // Setting the fragments, including in the case of the last chunk. set_fragment_elem(fragments, fragments_sizes, &is_last_chunk, sizeof(is_last_chunk), 2); set_fragment_elem(fragments, fragments_sizes, &part_count, sizeof(part_count), 3); @@ -1191,16 +1181,32 @@ ngx_http_cp_body_sender( tout_retries = max_retries_for_verdict; } // Sending the data to the nano service. - res = ngx_http_cp_send_data_to_service(fragments, fragments_sizes, num_body_chunk_fragments, session_data->session_id, - &was_waiting, fail_open_timeout, tout_retries); + res = ngx_http_cp_send_data_to_service( + fragments, + fragments_sizes, + num_body_chunk_fragments, + session_data->session_id, + &was_waiting, + fail_open_timeout, + tout_retries + ); if (res != NGX_OK) { // Failed to send the fragments to the nano service. return NGX_ERROR; } + write_dbg( + DBG_LEVEL_DEBUG, + "Successfully sent %s body chunk %d to service for session ID: %d", + body_type == REQUEST_BODY ? "request" : "response", + part_count, + session_data->session_id + ); + num_parts_sent++; is_empty_chain = 0; + chunks_processed++; } part_count++; @@ -1213,7 +1219,7 @@ ngx_http_cp_body_sender( *is_last_part = is_last_chunk; *num_messages_sent = num_parts_sent; - *next_elem_to_inspect = chain_iter; + *next_elem_to_inspect = chain_iter; return (!is_empty_chain && num_parts_sent == 0) ? NGX_ERROR : NGX_OK; } @@ -1239,3 +1245,4 @@ ngx_http_cp_metric_data_sender() reset_metric_data(); return res; } + diff --git a/attachments/nginx/ngx_module/ngx_cp_io.h b/attachments/nginx/ngx_module/ngx_cp_io.h index 071bf30..b24ddff 100644 --- a/attachments/nginx/ngx_module/ngx_cp_io.h +++ b/attachments/nginx/ngx_module/ngx_cp_io.h @@ -58,6 +58,7 @@ ngx_int_t ngx_http_cp_reply_receiver( ngx_int_t *expected_replies, ngx_http_cp_verdict_e *verdict, + ngx_int_t *inspect_all_response_headers, uint32_t cur_session_id, ngx_http_request_t *request, ngx_http_cp_modification_list **modification_list, @@ -70,6 +71,7 @@ ngx_http_cp_reply_receiver( /// @param[in, out] request NGINX request. /// @param[in] cur_request_id Request session's Id. /// @param[in, out] num_messages_sent Number of messages sent will be saved onto this parameter. +/// @param[in] waf_tag WAF tag to be sent. /// @returns ngx_int_t /// - #NGX_OK /// - #NGX_ERROR @@ -78,7 +80,8 @@ ngx_int_t ngx_http_cp_meta_data_sender( ngx_http_request_t *request, uint32_t cur_request_id, - ngx_uint_t *num_messages_sent + ngx_uint_t *num_messages_sent, + ngx_str_t *waf_tag ); /// @@ -138,7 +141,6 @@ ngx_http_cp_content_length_sender( /// - #RESPONSE_HEADER /// @param[in] cur_request_id Request session's Id. /// @param[in, out] num_messages_sent Number of messages sent will be saved onto this parameter. -/// @param[in, out] waf_tag WAF tag to be sent. /// @returns ngx_int_t /// - #NGX_OK /// - #NGX_ERROR @@ -148,8 +150,7 @@ ngx_http_cp_header_sender( ngx_list_part_t *headers, ngx_http_chunk_type_e header_type, uint32_t cur_request_id, - ngx_uint_t *num_messages_sent, - ngx_str_t *waf_tag + ngx_uint_t *num_messages_sent ); /// diff --git a/attachments/nginx/ngx_module/ngx_http_cp_attachment_module.c b/attachments/nginx/ngx_module/ngx_http_cp_attachment_module.c index a4256da..d4a7454 100644 --- a/attachments/nginx/ngx_module/ngx_http_cp_attachment_module.c +++ b/attachments/nginx/ngx_module/ngx_http_cp_attachment_module.c @@ -473,7 +473,7 @@ ngx_cp_attachment_fini_worker(ngx_cycle_t *cycle) // only worker number 0 (always exists since it is worker number 1 is allowed to create // the single instance of the timer and destroy it) if (ngx_worker != 0) return; - + reset_attachment_registration(); (void)cycle; diff --git a/core/include/attachments/compression_utils.h b/core/include/attachments/compression_utils.h index 5a25420..b867c3f 100644 --- a/core/include/attachments/compression_utils.h +++ b/core/include/attachments/compression_utils.h @@ -45,7 +45,8 @@ typedef enum CompressionType { NO_COMPRESSION, GZIP, - ZLIB + ZLIB, + BROTLI } CompressionType; typedef struct CompressionResult diff --git a/core/include/attachments/nginx_attachment_common.h b/core/include/attachments/nginx_attachment_common.h index 7f6353f..9ab8305 100644 --- a/core/include/attachments/nginx_attachment_common.h +++ b/core/include/attachments/nginx_attachment_common.h @@ -146,7 +146,8 @@ typedef enum ngx_http_cp_verdict TRAFFIC_VERDICT_INJECT, TRAFFIC_VERDICT_IRRELEVANT, TRAFFIC_VERDICT_RECONF, - TRAFFIC_VERDICT_WAIT + TRAFFIC_VERDICT_WAIT, + LIMIT_RESPONSE_HEADERS } ngx_http_cp_verdict_e; #ifdef __cplusplus @@ -190,6 +191,8 @@ typedef enum ngx_http_meta_data PARSED_HOST_DATA, PARSED_URI_SIZE, PARSED_URI_DATA, + WAF_TAG_SIZE, + WAF_TAG_DATA, META_DATA_COUNT } ngx_http_meta_data_e;