mirror of
https://github.com/openappsec/attachment.git
synced 2025-11-18 02:00:41 +03:00
Uploading attachment code
This commit is contained in:
548
attachments/nginx/ngx_module/ngx_cp_compression.c
Normal file
548
attachments/nginx/ngx_module/ngx_cp_compression.c
Normal file
@@ -0,0 +1,548 @@
|
||||
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/// @file ngx_cp_compression.c
|
||||
#include "ngx_cp_compression.h"
|
||||
|
||||
#include "ngx_cp_utils.h"
|
||||
#include "ngx_cp_metric.h"
|
||||
|
||||
static ngx_int_t is_debug_printing_initialized = 0;
|
||||
|
||||
ngx_int_t
|
||||
is_compression_debug_printing_initialized()
|
||||
{
|
||||
return is_debug_printing_initialized;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Writes a debug message at a debug level of Trace.
|
||||
/// @param[in] debug_message message to be written.
|
||||
///
|
||||
static void
|
||||
compression_trace_level_debug_printer(const char *debug_message)
|
||||
{
|
||||
write_dbg(DBG_LEVEL_TRACE, debug_message);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Writes a debug message at a debug level of Debug.
|
||||
/// @param[in] debug_message message to be written.
|
||||
///
|
||||
static void
|
||||
compression_debug_level_debug_printer(const char *debug_message)
|
||||
{
|
||||
write_dbg(DBG_LEVEL_DEBUG, debug_message);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Writes a debug message at a debug level of Info.
|
||||
/// @param[in] debug_message message to be written.
|
||||
///
|
||||
static void
|
||||
compression_info_level_debug_printer(const char *debug_message)
|
||||
{
|
||||
write_dbg(DBG_LEVEL_INFO, debug_message);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Writes a debug message at a debug level of Warning.
|
||||
/// @param[in] debug_message message to be written.
|
||||
///
|
||||
static void
|
||||
compression_warning_level_debug_printer(const char *debug_message)
|
||||
{
|
||||
write_dbg(DBG_LEVEL_WARNING, debug_message);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Writes a debug message at a debug level of Error.
|
||||
/// @param[in] debug_message message to be written.
|
||||
///
|
||||
static void
|
||||
compression_error_level_debug_printer(const char *debug_message)
|
||||
{
|
||||
write_dbg(DBG_LEVEL_ERROR, debug_message);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Writes a debug message at a debug level of Assert.
|
||||
/// @param[in] debug_message message to be written.
|
||||
///
|
||||
static void
|
||||
compression_assert_level_debug_printer(const char *debug_message)
|
||||
{
|
||||
write_dbg(DBG_LEVEL_ASSERT, debug_message);
|
||||
}
|
||||
|
||||
void
|
||||
initialize_compression_debug_printing()
|
||||
{
|
||||
setCompressionDebugFunction(COMPRESSION_DBG_LEVEL_TRACE, compression_trace_level_debug_printer);
|
||||
setCompressionDebugFunction(COMPRESSION_DBG_LEVEL_DEBUG, compression_debug_level_debug_printer);
|
||||
setCompressionDebugFunction(COMPRESSION_DBG_LEVEL_INFO, compression_info_level_debug_printer);
|
||||
setCompressionDebugFunction(COMPRESSION_DBG_LEVEL_WARNING, compression_warning_level_debug_printer);
|
||||
setCompressionDebugFunction(COMPRESSION_DBG_LEVEL_ERROR, compression_error_level_debug_printer);
|
||||
setCompressionDebugFunction(COMPRESSION_DBG_LEVEL_ASSERTION, compression_assert_level_debug_printer);
|
||||
|
||||
is_debug_printing_initialized = 1;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Checks if the compression buffer is valid.
|
||||
/// @param[in] should_compress Checks if buffer can be used for compression.
|
||||
/// - #0 - Buffer is used for decompression.
|
||||
/// - #1 - Buffer is used for compression.
|
||||
/// @param[in] buffer message to be written.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
static ngx_int_t
|
||||
is_valid_compression_buffer(const ngx_int_t should_compress, const ngx_buf_t *buffer)
|
||||
{
|
||||
uint64_t buffer_size = buffer->last - buffer->pos;
|
||||
|
||||
if (buffer_size == 0 && !should_compress) {
|
||||
write_dbg(DBG_LEVEL_WARNING, "Invalid decompression buffer: has size 0");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Gets the NGINX string data from NGINX buffer.
|
||||
/// @param[in, out] buffer_data NGINX string, used as a destination.
|
||||
/// @param[in] buffer NGINX buffer.
|
||||
///
|
||||
static void
|
||||
get_buffer_data(ngx_str_t *buffer_data, const ngx_buf_t *buffer)
|
||||
{
|
||||
if (buffer_data == NULL) {
|
||||
write_dbg(DBG_LEVEL_WARNING, "Passed a null pointer as destination buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
buffer_data->len = buffer->last - buffer->pos;
|
||||
buffer_data->data = buffer->pos;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Sets the buffer from NGINX string to NGINX buffer.
|
||||
/// @param[in, out] buffer NGINX buffer, used as a destination.
|
||||
/// @param[in] buffer_data NGINX string.
|
||||
///
|
||||
static void
|
||||
set_buffer_data(ngx_buf_t *buffer, const ngx_str_t *data)
|
||||
{
|
||||
buffer->start = data->data;
|
||||
buffer->pos = buffer->start;
|
||||
buffer->last = buffer->start + data->len;
|
||||
buffer->end = buffer->last;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Decompresses or compresses the provided data.
|
||||
/// @param[in] should_compress Checks if buffer is used for compression or decompression.
|
||||
/// - #0 - Function will decompression.
|
||||
/// - #1 - Function will compress.
|
||||
/// @param[in, out] compression_stream CompressionStream to compress.
|
||||
/// @param[in] is_last_decompressed_part Flags if the buffer's last part was decompressed.
|
||||
/// @param[in, out] output NGINX string to be used as output.
|
||||
/// @param[in] input NGINX string input to be used as input.
|
||||
/// @param[in] pool NGINX pool.
|
||||
/// @param[in, out] params Holds NGINX compression parameters.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
static ngx_int_t
|
||||
compression_data_filter(
|
||||
const ngx_int_t should_compress,
|
||||
CompressionStream *compression_stream,
|
||||
int *is_last_decompressed_part,
|
||||
ngx_str_t *output,
|
||||
ngx_str_t *input,
|
||||
ngx_pool_t *pool,
|
||||
ngx_cp_http_compression_params *params
|
||||
)
|
||||
{
|
||||
CompressionResult compression_result;
|
||||
|
||||
write_dbg(DBG_LEVEL_TRACE, "Performing %s on buffer data", should_compress ? "compression" : "decompression");
|
||||
|
||||
if (should_compress && params == NULL) {
|
||||
write_dbg(DBG_LEVEL_ASSERT, "Passed a pointer to null as compression parameters");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (should_compress) {
|
||||
// Compressing data.
|
||||
compression_result = compressData(
|
||||
compression_stream,
|
||||
params->compression_type,
|
||||
input->len,
|
||||
input->data,
|
||||
params->is_last_part
|
||||
);
|
||||
} else {
|
||||
// Decompressing data.
|
||||
DecompressionResult decompression_result = decompressData(compression_stream, input->len, input->data);
|
||||
compression_result.ok = decompression_result.ok;
|
||||
compression_result.num_output_bytes = decompression_result.num_output_bytes;
|
||||
compression_result.output = decompression_result.output;
|
||||
*is_last_decompressed_part = decompression_result.is_last_chunk;
|
||||
}
|
||||
if (!compression_result.ok) return NGX_ERROR;
|
||||
|
||||
if (compression_result.output == NULL) {
|
||||
output->len = 0;
|
||||
output->data = (u_char *)"";
|
||||
} else {
|
||||
output->len = compression_result.num_output_bytes;
|
||||
output->data = ngx_palloc(pool, output->len);
|
||||
if (output->data == NULL) {
|
||||
// Failed to allocate a new buffer.
|
||||
write_dbg(DBG_LEVEL_WARNING, "Failed to allocate a new buffer");
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_memcpy(output->data, compression_result.output, output->len);
|
||||
free(compression_result.output);
|
||||
}
|
||||
|
||||
write_dbg(DBG_LEVEL_TRACE, "Successfully %s buffer data", should_compress ? "compressed" : "decompressed");
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Decompresses or compresses the provided buffer.
|
||||
/// @param[in] should_compress Checks if buffer is used for compression or decompression.
|
||||
/// - #0 - Function will decompression.
|
||||
/// - #1 - Function will compress.
|
||||
/// @param[in, out] compression_stream CompressionStream to compress.
|
||||
/// @param[in] is_last_decompressed_part Flags if the buffer's last part was decompressed.
|
||||
/// @param[in, out] dest NGINX buffer used as destination.
|
||||
/// @param[in] src NGINX buffer used as source.
|
||||
/// @param[in] pool NGINX pool.
|
||||
/// @param[in] params Holds NGINX compression parameters.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
static ngx_int_t
|
||||
compression_buffer_filter(
|
||||
const ngx_int_t should_compress,
|
||||
CompressionStream *compression_stream,
|
||||
int *is_last_decompressed_part,
|
||||
ngx_buf_t *dest,
|
||||
ngx_buf_t *src,
|
||||
ngx_pool_t *pool,
|
||||
ngx_cp_http_compression_params *params
|
||||
)
|
||||
{
|
||||
ngx_str_t src_data;
|
||||
ngx_str_t dest_data;
|
||||
ngx_int_t compression_result;
|
||||
|
||||
write_dbg(DBG_LEVEL_TRACE, "Performing %s on buffer", should_compress ? "compression" : "decompression");
|
||||
|
||||
if (is_valid_compression_buffer(should_compress, src) != NGX_OK) {
|
||||
// Invalid buffer provided.
|
||||
write_dbg(DBG_LEVEL_WARNING, "Failed to %s: invalid buffer", should_compress ? "compress" : "decompress");
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (should_compress) {
|
||||
// Preparing data for compression.
|
||||
params->is_last_part = src->last_buf;
|
||||
|
||||
if (params->is_last_part && src->pos == NULL) {
|
||||
src->start = (u_char *)"";
|
||||
src->pos = src->start;
|
||||
src->last = src->start;
|
||||
src->end = src->start;
|
||||
}
|
||||
}
|
||||
|
||||
get_buffer_data(&src_data, src);
|
||||
// Compresses the data
|
||||
compression_result = compression_data_filter(
|
||||
should_compress,
|
||||
compression_stream,
|
||||
is_last_decompressed_part,
|
||||
&dest_data,
|
||||
&src_data,
|
||||
pool,
|
||||
params
|
||||
);
|
||||
if (compression_result != NGX_OK) {
|
||||
// Failed to compress or decompress.
|
||||
write_dbg(DBG_LEVEL_WARNING, "Failed to %s data", should_compress ? "compress" : "decompress");
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_memcpy(dest, src, sizeof(ngx_buf_t));
|
||||
set_buffer_data(dest, &dest_data);
|
||||
|
||||
write_dbg(DBG_LEVEL_TRACE, "Successfully %s buffer", should_compress ? "compressed" : "decompressed");
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Compresses the provided chain.
|
||||
/// @param[in] should_compress Checks if buffer is used for compression or decompression.
|
||||
/// - #0 - Function will decompression.
|
||||
/// - #1 - Function will compress.
|
||||
/// @param[in, out] compression_stream CompressionStream to compress.
|
||||
/// @param[in] is_last_decompressed_part Flags if the buffer's last part was decompressed.
|
||||
/// @param[in, out] body NGINX chain used as destination.
|
||||
/// @param[in] original_body_contents NGINX chain used as source.
|
||||
/// @param[in] pool NGINX pool.
|
||||
/// @param[in] params Holds NGINX cp compression parameters.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
static ngx_int_t
|
||||
compression_chain_filter(
|
||||
const ngx_int_t should_compress,
|
||||
CompressionStream *compression_stream,
|
||||
int *is_last_decompressed_part,
|
||||
ngx_chain_t **body,
|
||||
ngx_chain_t **original_body_contents,
|
||||
ngx_pool_t *pool,
|
||||
ngx_cp_http_compression_params *params
|
||||
)
|
||||
{
|
||||
ngx_int_t compression_result;
|
||||
ngx_buf_t *output_buffer = ngx_calloc_buf(pool);
|
||||
ngx_chain_t *curr_input_link = NULL;
|
||||
ngx_chain_t *curr_original_contents_link = original_body_contents == NULL ? NULL : *original_body_contents;
|
||||
|
||||
if (body == NULL) {
|
||||
// Null body parameter has been passed.
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Failed to %s chain: passed null pointer as output chain",
|
||||
should_compress ? "compress" : "decompress"
|
||||
);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
write_dbg(DBG_LEVEL_TRACE, "Performing %s on chain", should_compress ? "compression" : "decompression");
|
||||
|
||||
for (curr_input_link = *body; curr_input_link != NULL; curr_input_link = curr_input_link->next) {
|
||||
// Decompress or compresses buffer
|
||||
compression_result = compression_buffer_filter(
|
||||
should_compress,
|
||||
compression_stream,
|
||||
is_last_decompressed_part,
|
||||
output_buffer,
|
||||
curr_input_link->buf,
|
||||
pool,
|
||||
params
|
||||
);
|
||||
if (compression_result != NGX_OK) {
|
||||
// Failed to decompress or compress.
|
||||
free_chain(pool, *body);
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (curr_original_contents_link != NULL) {
|
||||
curr_original_contents_link->buf = ngx_calloc_buf(pool);
|
||||
ngx_memcpy(curr_original_contents_link->buf, curr_input_link->buf, sizeof(ngx_buf_t));
|
||||
|
||||
if (curr_input_link->next != NULL) {
|
||||
// Allocates next chain.
|
||||
curr_original_contents_link->next = ngx_alloc_chain_link(pool);
|
||||
ngx_memset(curr_original_contents_link->next, 0, sizeof(ngx_chain_t));
|
||||
curr_original_contents_link = curr_original_contents_link->next;
|
||||
}
|
||||
}
|
||||
|
||||
ngx_memcpy(curr_input_link->buf, output_buffer, sizeof(ngx_buf_t));
|
||||
curr_input_link->buf->memory = 1;
|
||||
}
|
||||
|
||||
write_dbg(DBG_LEVEL_TRACE, "Successfully %s chain", should_compress ? "compressed" : "decompressed");
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Sets the ngx_cp_http_compression_params and calls compression_chain_filter with compression flag.
|
||||
/// @param[in, out] compression_stream CompressionStream to compress.
|
||||
/// @param[in] compression_type Compression type to be used by compressions.
|
||||
/// @param[in] is_last_part Flags if the buffer's last part was compressed.
|
||||
/// @param[in, out] body NGINX chain used as destination.
|
||||
/// @param[in] original_body_contents NGINX chain used as source.
|
||||
/// @param[in] pool NGINX pool.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
static ngx_int_t
|
||||
compress_chain(
|
||||
CompressionStream *compression_stream,
|
||||
const CompressionType compression_type,
|
||||
const int is_last_part,
|
||||
ngx_chain_t **body,
|
||||
ngx_chain_t **original_body_contents,
|
||||
ngx_pool_t *pool
|
||||
)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Sets the ngx_cp_http_compression_params and calls compression_chain_filter with decompression flag.
|
||||
/// @param[in, out] compression_stream CompressionStream to compress.
|
||||
/// @param[in] compression_type Compression type to be used by compressions.
|
||||
/// @param[in] is_last_decompressed_part Flags if the buffer's last part was decompressed.
|
||||
/// @param[in, out] body NGINX chain used as destination.
|
||||
/// @param[in] original_body_contents NGINX chain used as source.
|
||||
/// @param[in] pool NGINX pool.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
static ngx_int_t
|
||||
decompress_chain(
|
||||
CompressionStream *decompress_stream,
|
||||
int *is_last_decompressed_part,
|
||||
ngx_chain_t **body,
|
||||
ngx_chain_t **original_body,
|
||||
ngx_pool_t *pool
|
||||
)
|
||||
{
|
||||
return
|
||||
compression_chain_filter(
|
||||
0,
|
||||
decompress_stream,
|
||||
is_last_decompressed_part,
|
||||
body,
|
||||
original_body,
|
||||
pool,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
|
||||
ngx_int_t
|
||||
decompress_body(
|
||||
CompressionStream *decompression_stream,
|
||||
const ngx_http_chunk_type_e chunk_type,
|
||||
int *is_last_decompressed_part,
|
||||
ngx_chain_t **body,
|
||||
ngx_chain_t **original_body_contents,
|
||||
ngx_pool_t *pool
|
||||
)
|
||||
{
|
||||
char *body_type = chunk_type == REQUEST_BODY ? "request" : "response";
|
||||
|
||||
write_dbg(DBG_LEVEL_TRACE, "Decompressing %s body", body_type);
|
||||
|
||||
ngx_int_t decompress_data_res = decompress_chain(
|
||||
decompression_stream,
|
||||
is_last_decompressed_part,
|
||||
body,
|
||||
original_body_contents,
|
||||
pool
|
||||
);
|
||||
if (decompress_data_res != NGX_OK) {
|
||||
// Failed to decompress the provided data.
|
||||
write_dbg(DBG_LEVEL_WARNING, "Failed to decompress %s body", body_type);
|
||||
updateMetricField(
|
||||
chunk_type == REQUEST_BODY ? REQ_FAILED_DECOMPRESSION_COUNT : RES_FAILED_DECOMPRESSION_COUNT,
|
||||
1
|
||||
);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
write_dbg(DBG_LEVEL_TRACE, "Successfully decompressed %s body", body_type);
|
||||
updateMetricField(
|
||||
chunk_type == REQUEST_BODY ? REQ_SUCCESSFUL_DECOMPRESSION_COUNT : RES_SUCCESSFUL_DECOMPRESSION_COUNT,
|
||||
1
|
||||
);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ngx_int_t
|
||||
compress_body(
|
||||
CompressionStream *compression_stream,
|
||||
const CompressionType compression_type,
|
||||
const ngx_http_chunk_type_e chunk_type,
|
||||
const int is_last_part,
|
||||
ngx_chain_t **body,
|
||||
ngx_chain_t **original_body_contents,
|
||||
ngx_pool_t *pool
|
||||
)
|
||||
{
|
||||
ngx_int_t compress_res;
|
||||
char *body_type;
|
||||
|
||||
if (compression_type == NO_COMPRESSION) {
|
||||
// This function should not be called with a NO_COMPRESSION type.
|
||||
// This if statement serves a case that somewhere throughout the code the data
|
||||
// is set to be compressed but the compression type is wrongly set.
|
||||
write_dbg(DBG_LEVEL_WARNING, "Invalid compression type: NO_COMPRESSION");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
body_type = chunk_type == REQUEST_BODY ? "request" : "response";
|
||||
write_dbg(
|
||||
DBG_LEVEL_TRACE,
|
||||
"Compressing plain-text %s body in the format \"%s\"",
|
||||
body_type,
|
||||
compression_type == GZIP ? "gzip" : "zlib"
|
||||
);
|
||||
// Checks if the compression was successful.
|
||||
compress_res = compress_chain(
|
||||
compression_stream,
|
||||
compression_type,
|
||||
is_last_part,
|
||||
body,
|
||||
original_body_contents,
|
||||
pool
|
||||
);
|
||||
if (compress_res != NGX_OK) {
|
||||
// Failed to compress the body.
|
||||
write_dbg(DBG_LEVEL_WARNING, "Failed to compress %s body", body_type);
|
||||
updateMetricField(
|
||||
chunk_type == REQUEST_BODY ? REQ_FAILED_COMPRESSION_COUNT : RES_FAILED_COMPRESSION_COUNT,
|
||||
1
|
||||
);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
write_dbg(DBG_LEVEL_TRACE, "Successfully compressed %s body", body_type);
|
||||
updateMetricField(
|
||||
chunk_type == REQUEST_BODY ? REQ_SUCCESSFUL_COMPRESSION_COUNT : RES_SUCCESSFUL_COMPRESSION_COUNT,
|
||||
1
|
||||
);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user