mirror of
https://github.com/openappsec/attachment.git
synced 2025-09-29 19:24:26 +03:00
Uploading attachment code
This commit is contained in:
1
attachments/CMakeLists.txt
Normal file
1
attachments/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
add_subdirectory(nginx)
|
1
attachments/nginx/CMakeLists.txt
Normal file
1
attachments/nginx/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
add_subdirectory(nginx_attachment_util)
|
5
attachments/nginx/nginx_attachment_util/CMakeLists.txt
Normal file
5
attachments/nginx/nginx_attachment_util/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
add_definitions(-DUSERSPACE)
|
||||
|
||||
add_library(osrc_nginx_attachment_util SHARED nginx_attachment_util.cc)
|
||||
|
||||
install(TARGETS osrc_nginx_attachment_util DESTINATION lib)
|
233
attachments/nginx/nginx_attachment_util/nginx_attachment_util.cc
Normal file
233
attachments/nginx/nginx_attachment_util/nginx_attachment_util.cc
Normal file
@@ -0,0 +1,233 @@
|
||||
// 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.
|
||||
|
||||
#include "nginx_attachment_util.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "http_configuration.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static HttpAttachmentConfiguration conf_data;
|
||||
|
||||
int
|
||||
initAttachmentConfig(c_str conf_file)
|
||||
{
|
||||
return conf_data.init(conf_file);
|
||||
}
|
||||
|
||||
ngx_http_inspection_mode_e
|
||||
getInspectionMode()
|
||||
{
|
||||
return static_cast<ngx_http_inspection_mode_e>(conf_data.getNumericalValue("nginx_inspection_mode"));
|
||||
}
|
||||
|
||||
unsigned int
|
||||
getNumOfNginxIpcElements()
|
||||
{
|
||||
return conf_data.getNumericalValue("num_of_nginx_ipc_elements");
|
||||
}
|
||||
|
||||
unsigned int
|
||||
getKeepAliveIntervalMsec()
|
||||
{
|
||||
return conf_data.getNumericalValue("keep_alive_interval_msec");
|
||||
}
|
||||
|
||||
unsigned int
|
||||
getDbgLevel()
|
||||
{
|
||||
return conf_data.getNumericalValue("dbg_level");
|
||||
}
|
||||
|
||||
int
|
||||
isDebugContext(c_str client, c_str server, unsigned int port, c_str method, c_str host , c_str uri)
|
||||
{
|
||||
auto &ctx = conf_data.getDebugContext();
|
||||
return
|
||||
(ctx.client == "" || ctx.client == client) &&
|
||||
(ctx.server == "" || ctx.server == server) &&
|
||||
(ctx.port == 0 || ctx.port == port) &&
|
||||
(ctx.method == "" || ctx.method == method) &&
|
||||
(ctx.host == "" || ctx.host == host) &&
|
||||
(ctx.uri == "" || ctx.uri == uri);
|
||||
}
|
||||
|
||||
c_str
|
||||
getStaticResourcesPath()
|
||||
{
|
||||
return conf_data.getStringValue("static_resources_path").c_str();
|
||||
}
|
||||
|
||||
int
|
||||
isFailOpenMode()
|
||||
{
|
||||
return conf_data.getNumericalValue("is_fail_open_mode_enabled");
|
||||
}
|
||||
|
||||
unsigned int
|
||||
getFailOpenTimeout()
|
||||
{
|
||||
return conf_data.getNumericalValue("fail_open_timeout");
|
||||
}
|
||||
|
||||
unsigned int
|
||||
getMaxSessionsPerMinute()
|
||||
{
|
||||
return conf_data.getNumericalValue("max_sessions_per_minute");
|
||||
}
|
||||
|
||||
int
|
||||
isFailOpenOnSessionLimit()
|
||||
{
|
||||
return conf_data.getStringValue("sessions_per_minute_limit_verdict") == "Accept";
|
||||
}
|
||||
|
||||
unsigned int
|
||||
getRegistrationThreadTimeout()
|
||||
{
|
||||
return conf_data.getNumericalValue("registration_thread_timeout_msec");
|
||||
}
|
||||
|
||||
unsigned int
|
||||
getReqProccessingTimeout()
|
||||
{
|
||||
return conf_data.getNumericalValue("req_proccessing_timeout_msec");
|
||||
}
|
||||
|
||||
unsigned int
|
||||
getReqHeaderThreadTimeout()
|
||||
{
|
||||
return conf_data.getNumericalValue("req_header_thread_timeout_msec");
|
||||
}
|
||||
|
||||
unsigned int
|
||||
getReqBodyThreadTimeout()
|
||||
{
|
||||
return conf_data.getNumericalValue("req_body_thread_timeout_msec");
|
||||
}
|
||||
|
||||
unsigned int
|
||||
getResProccessingTimeout()
|
||||
{
|
||||
return conf_data.getNumericalValue("res_proccessing_timeout_msec");
|
||||
}
|
||||
|
||||
unsigned int
|
||||
getResHeaderThreadTimeout()
|
||||
{
|
||||
return conf_data.getNumericalValue("res_header_thread_timeout_msec");
|
||||
}
|
||||
|
||||
unsigned int
|
||||
getResBodyThreadTimeout()
|
||||
{
|
||||
return conf_data.getNumericalValue("res_body_thread_timeout_msec");
|
||||
}
|
||||
|
||||
int
|
||||
isIPAddress(c_str ip_str)
|
||||
{
|
||||
int address_family = AF_INET;
|
||||
for (int i = 0; ip_str[i]; ++i) {
|
||||
if (ip_str[i] == ':') address_family = AF_INET6;
|
||||
}
|
||||
|
||||
char placeholder[16];
|
||||
return inet_pton(address_family, ip_str, placeholder);
|
||||
}
|
||||
|
||||
struct IpAddress
|
||||
{
|
||||
union {
|
||||
struct in_addr ipv4;
|
||||
struct in6_addr ipv6;
|
||||
} ip;
|
||||
bool is_ipv4;
|
||||
|
||||
bool
|
||||
operator<(const IpAddress &other) const
|
||||
{
|
||||
if (is_ipv4 != other.is_ipv4) return is_ipv4 < other.is_ipv4;
|
||||
if (is_ipv4) return memcmp(&ip.ipv4, &other.ip.ipv4, sizeof(struct in_addr)) < 0;
|
||||
return memcmp(&ip.ipv6, &other.ip.ipv6, sizeof(struct in6_addr)) < 0;
|
||||
}
|
||||
|
||||
bool
|
||||
operator<=(const IpAddress &other) const
|
||||
{
|
||||
return !(other < *this);
|
||||
}
|
||||
};
|
||||
|
||||
static IpAddress
|
||||
createIPAddress(c_str ip_str)
|
||||
{
|
||||
IpAddress res;
|
||||
|
||||
for (int i = 0; ip_str[i]; ++i) {
|
||||
if (ip_str[i] == ':') {
|
||||
res.is_ipv4 = false;
|
||||
inet_pton(AF_INET6, ip_str, &res.ip.ipv6);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
res.is_ipv4 = true;
|
||||
inet_pton(AF_INET, ip_str, &res.ip.ipv4);
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool
|
||||
isIPInRange(const IpAddress &ip, const IpAddress &start, const IpAddress &end)
|
||||
{
|
||||
if (ip.is_ipv4 != start.is_ipv4 || ip.is_ipv4 != end.is_ipv4) return false;
|
||||
return start <= ip && ip <= end;
|
||||
}
|
||||
|
||||
static bool
|
||||
isIPInRange(const IpAddress &ip, const string &range)
|
||||
{
|
||||
auto delimiter = range.find('-');
|
||||
|
||||
if (delimiter == string::npos) {
|
||||
if (!isIPAddress(range.c_str())) return false;
|
||||
auto address = createIPAddress(range.c_str());
|
||||
return isIPInRange(ip, address, address);
|
||||
}
|
||||
|
||||
auto start_str = range.substr(0, delimiter);
|
||||
if (!isIPAddress(start_str.c_str())) return false;
|
||||
auto start_addr = createIPAddress(start_str.c_str());
|
||||
|
||||
auto end_str = range.substr(delimiter + 1);
|
||||
if (!isIPAddress(end_str.c_str())) return false;
|
||||
auto end_addr = createIPAddress(end_str.c_str());
|
||||
|
||||
return isIPInRange(ip, start_addr, end_addr);
|
||||
}
|
||||
|
||||
int
|
||||
isSkipSource(c_str ip_str)
|
||||
{
|
||||
if (!isIPAddress(ip_str)) return 0;
|
||||
auto ip = createIPAddress(ip_str);
|
||||
|
||||
for (auto &range : conf_data.getExcludeSources()) {
|
||||
if (isIPInRange(ip, range)) return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
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;
|
||||
}
|
100
attachments/nginx/ngx_module/ngx_cp_compression.h
Normal file
100
attachments/nginx/ngx_module/ngx_cp_compression.h
Normal file
@@ -0,0 +1,100 @@
|
||||
// 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.h
|
||||
#ifndef __NGX_CP_COMPRESSION_H__
|
||||
#define __NGX_CP_COMPRESSION_H__
|
||||
|
||||
#include <ngx_core.h>
|
||||
|
||||
#include "nginx_attachment_common.h"
|
||||
#include "compression_utils.h"
|
||||
|
||||
/// @struct ngx_cp_http_compression_params
|
||||
/// @brief Holds all the information regarding NGINX compression.
|
||||
typedef struct {
|
||||
ngx_int_t is_last_part;
|
||||
CompressionType compression_type;
|
||||
} ngx_cp_http_compression_params;
|
||||
|
||||
///
|
||||
/// @brief Returns compression debug printing initialization status.
|
||||
/// @returns ngx_int_t;
|
||||
/// - #0 Debug printing is not initialized.
|
||||
/// - #1 Debug printing is initialized.
|
||||
///
|
||||
ngx_int_t is_compression_debug_printing_initialized();
|
||||
|
||||
///
|
||||
/// @brief Initialize compression debug printing.
|
||||
///
|
||||
void initialize_compression_debug_printing();
|
||||
|
||||
///
|
||||
/// @brief Decompress the provided body stream.
|
||||
/// @param[in, out] decompression_stream CompressionStream to decompress.
|
||||
/// @param[in] chunk_type Body chunk type:
|
||||
/// - #REQUEST_BODY
|
||||
/// - #RESPONSE_BODY
|
||||
/// @param[in, out] is_last_decompressed_part Flags if the buffer's last part was decompressed.
|
||||
/// @param[in] body NGINX chain, serves as an output.
|
||||
/// @param[in] original_body_contents NGINX chain, serves as an input to be decompressed.
|
||||
/// @param[in, out] pool NGINX pool.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
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
|
||||
);
|
||||
|
||||
///
|
||||
/// @brief Compresses the provided body stream.
|
||||
/// @details Provided by the body, body type (Response/Request) and stream.
|
||||
/// Compresses the provided body by using the provided compression stream.
|
||||
/// @param[in, out] decompression_stream CompressionStream to compress.
|
||||
/// @param[in] compression_type Compression type.
|
||||
/// - #GZIP
|
||||
/// - #ZLIB
|
||||
/// - #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:
|
||||
/// - #REQUEST_BODY
|
||||
/// - #RESPONSE_BODY
|
||||
/// @param[in, out] is_last_part Saves the value if last part was compressed.
|
||||
/// @param[in] body NGINX chain.
|
||||
/// @param[in] original_body_contents NGINX chain, serves as an input to be compressed.
|
||||
/// @param[in, out] pool NGINX pool.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
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
|
||||
);
|
||||
|
||||
#endif // __NGX_CP_COMPRESSION_H__
|
852
attachments/nginx/ngx_module/ngx_cp_custom_response.c
Normal file
852
attachments/nginx/ngx_module/ngx_cp_custom_response.c
Normal file
@@ -0,0 +1,852 @@
|
||||
// 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_custom_response.c
|
||||
#include "ngx_cp_custom_response.h"
|
||||
|
||||
#include "ngx_cp_utils.h"
|
||||
|
||||
///
|
||||
/// @brief Pushes a key and value into a header list.
|
||||
/// @param[in, out] headers_list Headers list to push the new head into.
|
||||
/// @param[in] key_data_size Key data size to be pushed.
|
||||
/// @param[in, out] key_data Key data to be pushed.
|
||||
/// @param[in] value_data_size Value data size to be pushed.
|
||||
/// @param[in, out] value_data Value data to be pushed.
|
||||
/// @return ngx_table_elt_t
|
||||
/// - #A pointer to the pushed NGINX header element.
|
||||
/// - #NULL if failed.
|
||||
///
|
||||
static ngx_table_elt_t *
|
||||
push_header_to_list(
|
||||
ngx_list_t *headers_list,
|
||||
int16_t key_data_size,
|
||||
u_char *key_data,
|
||||
int16_t value_data_size,
|
||||
u_char *value_data
|
||||
);
|
||||
|
||||
///
|
||||
/// @brief Allocates a file buffer to the provided file.
|
||||
/// @param[in, out] memory_pool NGINX pool.
|
||||
/// @param[in, out] open_file_info NGINX file info - file information.
|
||||
/// @param[in, out] is_last_buffer Symbolize if the newly allocated buffer is the last buffer.
|
||||
/// @param[in, out] file_path NGINX string.
|
||||
/// @param[in, out] log NGINX log.
|
||||
/// @returns ngx_buf_t
|
||||
/// - #A valid pointer to NGINX buffer.
|
||||
/// - #NULL
|
||||
///
|
||||
static ngx_buf_t *
|
||||
allocate_file_buffer(
|
||||
ngx_pool_t *memory_pool,
|
||||
ngx_open_file_info_t *open_file_info,
|
||||
ngx_int_t is_last_buffer,
|
||||
ngx_str_t *file_path,
|
||||
ngx_log_t *log
|
||||
)
|
||||
{
|
||||
ngx_buf_t *file_buffer = ngx_calloc_buf(memory_pool);
|
||||
if (file_buffer == NULL) {
|
||||
write_dbg(DBG_LEVEL_WARNING, "Failed to allocate file buffer: could not allocate memory for the buffer");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
file_buffer->file = ngx_pcalloc(memory_pool, sizeof(ngx_file_t));
|
||||
if (file_buffer->file == NULL) {
|
||||
write_dbg(DBG_LEVEL_WARNING, "Failed to allocate file buffer's file descriptor");
|
||||
|
||||
ngx_pfree(memory_pool, file_buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
file_buffer->file_pos = 0;
|
||||
file_buffer->file_last = open_file_info->size;
|
||||
file_buffer->in_file = file_buffer->file_last ? 1: 0;
|
||||
file_buffer->last_buf = is_last_buffer;
|
||||
file_buffer->last_in_chain = 1;
|
||||
file_buffer->file->fd = open_file_info->fd;
|
||||
file_buffer->file->name = *file_path;
|
||||
file_buffer->file->log = log;
|
||||
file_buffer->file->directio = open_file_info->is_directio;
|
||||
|
||||
return file_buffer;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Deletes the provided header list.
|
||||
/// @details Iterates over the header list and frees all the nodes' memory back to the NGX pool.
|
||||
/// @param[in, out] headers_list
|
||||
///
|
||||
static void
|
||||
delete_headers_list(ngx_list_t *headers_list)
|
||||
{
|
||||
ngx_list_part_t *headers_iter = headers_list->part.next;
|
||||
ngx_list_part_t *header_to_del;
|
||||
|
||||
while (headers_iter) {
|
||||
header_to_del = headers_iter;
|
||||
headers_iter = headers_iter->next;
|
||||
ngx_pfree(headers_list->pool, header_to_del->elts);
|
||||
header_to_del->elts = NULL;
|
||||
header_to_del->nelts = 0;
|
||||
ngx_pfree(headers_list->pool, header_to_del);
|
||||
}
|
||||
|
||||
headers_list->part.nelts = 0;
|
||||
headers_list->last = &headers_list->part;
|
||||
headers_list->part.next = NULL;
|
||||
}
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_cp_response_headers_sender(
|
||||
ngx_http_request_t *request,
|
||||
const ngx_uint_t response_code,
|
||||
const off_t content_length,
|
||||
const time_t last_modified_time,
|
||||
const unsigned int allow_ranges,
|
||||
const unsigned int keepalive
|
||||
)
|
||||
{
|
||||
write_dbg(
|
||||
DBG_LEVEL_DEBUG,
|
||||
"Generating response headers: response code: %ui, content length: %O, last modified time: %T",
|
||||
response_code,
|
||||
content_length,
|
||||
last_modified_time
|
||||
);
|
||||
|
||||
// Writes the custom response data onto the response headers.
|
||||
request->headers_out.status = response_code;
|
||||
request->headers_out.content_length_n = content_length;
|
||||
request->headers_out.last_modified_time = last_modified_time;
|
||||
request->allow_ranges = allow_ranges;
|
||||
request->keepalive = keepalive;
|
||||
|
||||
if (ngx_http_set_content_type(request) != NGX_OK) {
|
||||
// Failed to get the header's type.
|
||||
write_dbg(DBG_LEVEL_WARNING, "Failed to set content type header");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
write_dbg(DBG_LEVEL_DEBUG,"Successfully generated response headers, sending response headers");
|
||||
return ngx_http_send_header(request);
|
||||
}
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_cp_file_response_sender(
|
||||
ngx_http_request_t *request,
|
||||
ngx_str_t *file_path,
|
||||
ngx_open_file_info_t *open_file_info,
|
||||
ngx_int_t is_main_request,
|
||||
ngx_log_t *log,
|
||||
ngx_pool_t *memory_pool
|
||||
)
|
||||
{
|
||||
ngx_int_t is_last_buffer;
|
||||
ngx_buf_t *file_buffer;
|
||||
ngx_int_t send_output_chain_result;
|
||||
ngx_chain_t *output_chain;
|
||||
|
||||
write_dbg(DBG_LEVEL_DEBUG, "Trying to send file: %.*s", file_path->len, file_path->data);
|
||||
|
||||
is_last_buffer = is_main_request ? 1: 0;
|
||||
|
||||
// Allocates file's buffer and NGINX chain.
|
||||
file_buffer = allocate_file_buffer(memory_pool, open_file_info, is_last_buffer, file_path, log);
|
||||
|
||||
output_chain = ngx_alloc_chain_link(memory_pool);
|
||||
output_chain->buf = file_buffer;
|
||||
output_chain->next = NULL;
|
||||
|
||||
send_output_chain_result = ngx_http_output_filter(request, output_chain);
|
||||
|
||||
ngx_pfree(memory_pool, file_buffer->file);
|
||||
ngx_pfree(memory_pool, file_buffer);
|
||||
|
||||
return send_output_chain_result;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Adds event ID to the provided NGINX request.
|
||||
/// @param[in, out] request NGINX request.
|
||||
///
|
||||
void
|
||||
ngx_add_event_id_to_header(ngx_http_request_t *request)
|
||||
{
|
||||
u_char *uuid = (u_char *)get_web_response_uuid();
|
||||
ngx_uint_t uuid_size = get_web_response_uuid_size();
|
||||
static u_char uuid_key[] = { 'x', '_', 'e', 'v', 'e', 'n', 't', '_', 'i', 'd' };
|
||||
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Adding instance ID to header. Incident ID: %s, Incident ID size: %d",
|
||||
uuid,
|
||||
uuid_size
|
||||
);
|
||||
push_header_to_list(
|
||||
&(request->headers_out.headers),
|
||||
sizeof(uuid_key),
|
||||
uuid_key,
|
||||
uuid_size,
|
||||
uuid
|
||||
);
|
||||
}
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_cp_finalize_rejected_request(ngx_http_request_t *request)
|
||||
{
|
||||
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 res_code, res;
|
||||
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;
|
||||
|
||||
write_dbg(DBG_LEVEL_TRACE, "Finalizing rejecting request");
|
||||
|
||||
request->keepalive = 0;
|
||||
|
||||
res_code = get_response_code();
|
||||
request->headers_out.status = res_code;
|
||||
request->headers_out.status_line.len = 0;
|
||||
|
||||
if (res_code == 0) {
|
||||
// Response code was not provided, setting it to NGX_HTTP_CLOSE.
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Response code was not provided. Returning default response: %d (NGX_HTTP_CLOSE)",
|
||||
NGX_HTTP_CLOSE
|
||||
);
|
||||
res_code = NGX_HTTP_CLOSE;
|
||||
request->headers_out.status = res_code;
|
||||
|
||||
goto CUSTOM_RES_OUT;
|
||||
}
|
||||
|
||||
if (get_response_code() == NGX_HTTP_TEMPORARY_REDIRECT) {
|
||||
// Handling redirect web response.
|
||||
write_dbg(
|
||||
DBG_LEVEL_DEBUG,
|
||||
"Sending Redirect web response"
|
||||
);
|
||||
|
||||
static u_char location_key[] = {'L', 'o', 'c', 'a', 't', 'i', 'o', 'n'};
|
||||
location_header = push_header_to_list(
|
||||
&(request->headers_out.headers),
|
||||
sizeof(location_key),
|
||||
location_key,
|
||||
get_redirect_location_size(),
|
||||
get_redirect_location()
|
||||
);
|
||||
if (location_header == NULL) {
|
||||
// Failed to allocate header.
|
||||
write_dbg(DBG_LEVEL_ERROR, "Failed to allocate header");
|
||||
res_code = NGX_HTTP_CLOSE;
|
||||
goto CUSTOM_RES_OUT;
|
||||
}
|
||||
|
||||
if (get_add_event_id()) {
|
||||
// Add event ID into the header.
|
||||
ngx_add_event_id_to_header(request);
|
||||
}
|
||||
|
||||
request->keepalive = 1;
|
||||
goto CUSTOM_RES_OUT;
|
||||
}
|
||||
|
||||
ngx_add_event_id_to_header(request);
|
||||
|
||||
if (get_response_page_length() == 0) {
|
||||
// Page details were not provided.
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Web response page details were not provided. Returning %d response code",
|
||||
get_response_code()
|
||||
);
|
||||
send_response_custom_body = 0;
|
||||
}
|
||||
|
||||
// Writes the finalized rejected data into the headers.
|
||||
request->headers_out.content_type.len = size_of_text_html;
|
||||
request->headers_out.content_type_len = request->headers_out.content_type.len;
|
||||
request->headers_out.content_type.data = text_html;
|
||||
request->headers_out.content_length_n = get_response_page_length();
|
||||
|
||||
delete_headers_list(&request->headers_out.headers);
|
||||
|
||||
write_dbg(DBG_LEVEL_TRACE, "Sending response headers for rejected request");
|
||||
res = ngx_http_send_header(request);
|
||||
if (res == NGX_ERROR || res > NGX_OK) {
|
||||
// Failed to send response headers.
|
||||
write_dbg(
|
||||
DBG_LEVEL_DEBUG,
|
||||
"Failed to send response headers (result: %d). Returning response code: %d",
|
||||
res,
|
||||
res_code
|
||||
);
|
||||
goto CUSTOM_RES_OUT;
|
||||
}
|
||||
write_dbg(
|
||||
DBG_LEVEL_TRACE,
|
||||
"Successfully sent response headers for rejected request."
|
||||
" Generating web response page for rejected request."
|
||||
);
|
||||
|
||||
if (send_response_custom_body) {
|
||||
// Sending response custom body.
|
||||
if (get_response_page(request, &out_chain) != NGX_OK) {
|
||||
// Failed to generate custom response page.
|
||||
write_dbg(
|
||||
DBG_LEVEL_DEBUG,
|
||||
"Failed to generate web response page. Returning response code: %d",
|
||||
get_response_code()
|
||||
);
|
||||
goto CUSTOM_RES_OUT;
|
||||
}
|
||||
write_dbg(DBG_LEVEL_TRACE, "Successfully generated web response page for rejected request");
|
||||
write_dbg(DBG_LEVEL_TRACE, "Sending web response body");
|
||||
ngx_int_t output_filter_result = ngx_http_output_filter(request, out_chain);
|
||||
if (output_filter_result != NGX_OK) {
|
||||
// Failed to send response body.
|
||||
write_dbg(DBG_LEVEL_WARNING, "Failed to send web response body");
|
||||
} else {
|
||||
write_dbg(DBG_LEVEL_TRACE, "Successfully sent web response body");
|
||||
}
|
||||
} else {
|
||||
out_chain[0].buf = ngx_calloc_buf(request->pool);
|
||||
if (out_chain[0].buf == NULL) {
|
||||
// Failed to send web response.
|
||||
write_dbg(DBG_LEVEL_WARNING, "Failed to send web response");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
out_chain[0].buf->last_buf = 1;
|
||||
out_chain[0].next = NULL;
|
||||
return ngx_http_output_filter(request, &out_chain[0]);
|
||||
}
|
||||
|
||||
CUSTOM_RES_OUT:
|
||||
ngx_http_finalize_request(request, res_code);
|
||||
return res_code;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Frees modification list.
|
||||
/// @param[in, out] modification_list NGINX modifications.
|
||||
/// @param[in, out] pool NGINX pool.
|
||||
///
|
||||
static void
|
||||
free_modifications_list(ngx_http_cp_modification_list *modification_list, ngx_pool_t *pool)
|
||||
{
|
||||
ngx_http_cp_modification_list *next_modification;
|
||||
|
||||
write_dbg(DBG_LEVEL_DEBUG, "Freeing modification list");
|
||||
|
||||
while (modification_list) {
|
||||
next_modification = modification_list->next;
|
||||
ngx_pfree(pool, modification_list->modification_buffer);
|
||||
ngx_pfree(pool, modification_list);
|
||||
modification_list = next_modification;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Injects the provided buffer at the provided position into the original_buffer.
|
||||
/// @param[in, out] original_buffer NGINX string that new data will be injected to.
|
||||
/// @param[in] injection_pos Injection position on the original buffer.
|
||||
/// @param[in] injected_buffer_size Injected buffer size.
|
||||
/// @param[in, out] injection_buffer Injected buffer.
|
||||
/// @param[in, out] pool NGINX pool.
|
||||
/// @return ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
static ngx_int_t
|
||||
inject_buffer(
|
||||
ngx_str_t *original_buffer,
|
||||
const size_t injection_pos,
|
||||
const size_t injected_buffer_size,
|
||||
u_char *injection_buffer,
|
||||
ngx_pool_t *pool
|
||||
)
|
||||
{
|
||||
size_t new_buffer_len;
|
||||
|
||||
if (injection_pos > original_buffer->len) {
|
||||
// Injection position is after original buffer's end position.
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Injection position is after original buffer's end. Injection position: %u, buffer's size: %u",
|
||||
injection_pos,
|
||||
original_buffer->len
|
||||
);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
// Allocates memory for a new buffer.
|
||||
new_buffer_len = original_buffer->len + injected_buffer_size;
|
||||
u_char *new_buffer_value = ngx_palloc(pool, new_buffer_len);
|
||||
if (new_buffer_value == NULL) {
|
||||
// Failed to allocate memory for a new buffer.
|
||||
write_dbg(DBG_LEVEL_WARNING, "Failed to allocate memory for a new buffer, size: %u", new_buffer_len);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
// Copies the injected data onto the original buffer.
|
||||
ngx_memcpy(new_buffer_value, original_buffer->data, injection_pos);
|
||||
ngx_memcpy(new_buffer_value + injection_pos, injection_buffer, injected_buffer_size);
|
||||
if (injection_pos < original_buffer->len) {
|
||||
ngx_memcpy(
|
||||
new_buffer_value + injection_pos + injected_buffer_size,
|
||||
original_buffer->data + injection_pos,
|
||||
original_buffer->len - injection_pos
|
||||
);
|
||||
}
|
||||
|
||||
original_buffer->len = new_buffer_len;
|
||||
|
||||
ngx_pfree(pool, original_buffer->data);
|
||||
original_buffer->data = new_buffer_value;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Inject modification into a header's value.
|
||||
/// @param[in, out] header NGINX string that the modifications will be injected into.
|
||||
/// @param[in] modification Modifications to inject.
|
||||
/// @param[in, out] header_pool NGINX pool.
|
||||
/// @return ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
static ngx_int_t
|
||||
inject_header_value(
|
||||
ngx_table_elt_t *header,
|
||||
ngx_http_cp_modification_list *modification,
|
||||
ngx_pool_t *header_pool
|
||||
)
|
||||
{
|
||||
if (modification->modification.injection_pos < 0) {
|
||||
// Injection position is after original buffer's end position.
|
||||
write_dbg(DBG_LEVEL_ASSERT, "Negative injection position: %d", modification->modification.injection_pos);
|
||||
}
|
||||
|
||||
write_dbg(
|
||||
DBG_LEVEL_DEBUG,
|
||||
"Injecting buffer into header's value. "
|
||||
"Header's index: %ui, original header's data: '%.*s' (size: %u): '%.*s' (size: %u), "
|
||||
"injection position: %ui, injected buffer: %s (size: %u)",
|
||||
modification->modification.orig_buff_index,
|
||||
header->key.len,
|
||||
header->key.data,
|
||||
header->key.len,
|
||||
header->value.len,
|
||||
header->value.data,
|
||||
header->value.len,
|
||||
modification->modification.injection_pos,
|
||||
modification->modification_buffer,
|
||||
modification->modification.injection_size
|
||||
);
|
||||
|
||||
// Inject the modification's buffer into the header.
|
||||
ngx_int_t inject_buffer_result = inject_buffer(
|
||||
&header->value,
|
||||
modification->modification.injection_pos,
|
||||
modification->modification.injection_size,
|
||||
(u_char *)modification->modification_buffer,
|
||||
header_pool
|
||||
);
|
||||
if (inject_buffer_result != NGX_OK) return NGX_ERROR;
|
||||
|
||||
write_dbg(
|
||||
DBG_LEVEL_DEBUG,
|
||||
"Successfully injected header value. Header's value after injection: %.*s (size: %u)",
|
||||
header->value.len,
|
||||
header->value.data,
|
||||
header->value.len
|
||||
);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
static ngx_table_elt_t *
|
||||
push_header_to_list(
|
||||
ngx_list_t *headers_list,
|
||||
int16_t key_data_size,
|
||||
u_char *key_data,
|
||||
int16_t value_data_size,
|
||||
u_char *value_data
|
||||
)
|
||||
{
|
||||
ngx_table_elt_t *header = ngx_list_push(headers_list);
|
||||
if (header == NULL) {
|
||||
// Failed to allocate header.
|
||||
write_dbg(DBG_LEVEL_WARNING, "Failed to allocate header");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
header->hash = 1;
|
||||
header->key.data = key_data;
|
||||
header->key.len = key_data_size;
|
||||
header->value.data = value_data;
|
||||
header->value.len = value_data_size;
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Validate header modification.
|
||||
/// @details Modifications come in two nodes: The first one handles the key, the other handles the value.
|
||||
/// This function validates that the second node (which is the next) that holds data exists and if their
|
||||
/// data is valid.
|
||||
/// @param[in, out] modification
|
||||
/// @return ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
static ngx_int_t
|
||||
validate_append_header_modification(ngx_http_cp_modification_list *modification)
|
||||
{
|
||||
write_dbg(DBG_LEVEL_DEBUG, "Validating append header modification data");
|
||||
|
||||
if (modification->next == NULL) {
|
||||
// Modification data value for the provided modification is missing.
|
||||
write_dbg(DBG_LEVEL_WARNING, "Error: Append header modification is missing modification data for value");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
if (!modification->modification.is_header || !modification->next->modification.is_header) {
|
||||
// Modification key or value are not of type header and therefor aren't a proper header modification.
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Error: Append header modification is missing modification data for %s",
|
||||
!modification->modification.is_header ? "key" : "value"
|
||||
);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
write_dbg(DBG_LEVEL_DEBUG, "Append header modification data is valid");
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Append modification into the headers list.
|
||||
/// @param[in, out] headers_list NGINX list.
|
||||
/// @param[in, out] modification modification to append into the header list.
|
||||
/// @return ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
static ngx_int_t
|
||||
append_header(ngx_list_t *headers_list, ngx_http_cp_modification_list *modification)
|
||||
{
|
||||
ngx_http_cp_modification_list *key_modification = modification;
|
||||
ngx_http_cp_modification_list *value_modification = modification->next;
|
||||
|
||||
write_dbg(
|
||||
DBG_LEVEL_DEBUG,
|
||||
"Appending header: '%s' (size: %ui) : '%s' (size: %ui)",
|
||||
key_modification->modification_buffer,
|
||||
key_modification->modification.injection_size,
|
||||
value_modification->modification_buffer,
|
||||
value_modification->modification.injection_size
|
||||
);
|
||||
// Appending the header.
|
||||
ngx_table_elt_t *new_header = push_header_to_list(
|
||||
headers_list,
|
||||
key_modification->modification.injection_size,
|
||||
(u_char *)key_modification->modification_buffer,
|
||||
value_modification->modification.injection_size,
|
||||
(u_char *)value_modification->modification_buffer
|
||||
);
|
||||
if (new_header == NULL) return NGX_ERROR;
|
||||
|
||||
write_dbg(
|
||||
DBG_LEVEL_TRACE,
|
||||
"Successfully appended header. Key: '%.*s' (size: %u), value: '%.*s' (size: %u)",
|
||||
new_header->key.len,
|
||||
new_header->key.data,
|
||||
new_header->key.len,
|
||||
new_header->value.len,
|
||||
new_header->value.data,
|
||||
new_header->value.len
|
||||
);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Modifies a header using the modification list.
|
||||
/// @param[in, out] request NGINX request.
|
||||
/// @param[in, out] headers headers NGINX list.
|
||||
/// @param[in, out] headers_iterator NGINX CP header iterator.
|
||||
/// @param[in, out] modification Modifications list.
|
||||
/// @param[in] type Modification type.
|
||||
/// @param[in] is_content_length A flag if the header is content length.
|
||||
/// @return ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
static ngx_int_t
|
||||
perform_header_modification(
|
||||
ngx_http_request_t *request,
|
||||
ngx_list_t *headers,
|
||||
ngx_http_cp_list_iterator *headers_iterator,
|
||||
ngx_http_cp_modification_list *modification,
|
||||
ngx_http_modification_type_e type,
|
||||
ngx_flag_t is_content_length
|
||||
)
|
||||
{
|
||||
ngx_table_elt_t *injected_header;
|
||||
|
||||
switch (type) {
|
||||
case APPEND: {
|
||||
// Appends a modification into the header.
|
||||
if (append_header(headers, modification) != NGX_OK) {
|
||||
// Failed to append the modification.
|
||||
write_dbg(DBG_LEVEL_ERROR, "Failed to append header");
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case INJECT: {
|
||||
// Injects a modification into the header.
|
||||
injected_header = get_list_element(headers_iterator, modification->modification.orig_buff_index);
|
||||
if (injected_header == NULL) {
|
||||
// No header found with the index.
|
||||
write_dbg(
|
||||
DBG_LEVEL_ASSERT,
|
||||
"No header found with index %ui",
|
||||
modification->modification.orig_buff_index
|
||||
)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (inject_header_value(injected_header, modification, headers->pool) != NGX_OK) {
|
||||
// Failed to inject a header value.
|
||||
write_dbg(DBG_LEVEL_ERROR, "Failed to inject header value");
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case REPLACE: {
|
||||
if (is_content_length == 1) {
|
||||
// Replacing Content-Length.
|
||||
write_dbg(DBG_LEVEL_DEBUG, "Content-Length will be replaced")
|
||||
ngx_http_clear_content_length(request);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Failed to get a known modificatino type.
|
||||
write_dbg(DBG_LEVEL_ASSERT, "Unknown modification type: %d", type);
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Get the next modification from modification list.
|
||||
/// @param[in, out] modification Modification list to get the next header from.
|
||||
/// @param[in, out] type Modification type.
|
||||
/// @return ngx_http_cp_modification_list
|
||||
/// - #ngx_http_cp_modification_list pointer to the next element.
|
||||
/// - #NULL if failed to get the next element.
|
||||
///
|
||||
static ngx_http_cp_modification_list *
|
||||
get_next_header_modification(ngx_http_cp_modification_list *modification, ngx_http_modification_type_e type)
|
||||
{
|
||||
switch (type) {
|
||||
case APPEND:
|
||||
return modification->next->next;
|
||||
case INJECT:
|
||||
case REPLACE:
|
||||
return modification->next;
|
||||
default:
|
||||
write_dbg(DBG_LEVEL_ASSERT, "Unknown modification type: %d", type);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Free modifications.
|
||||
/// @param[in, out] modification Modification to free.
|
||||
/// @param[in, out] type Modification type.
|
||||
/// @param[in, out] pool NGINX pool.
|
||||
///
|
||||
static void
|
||||
free_header_modification(
|
||||
ngx_http_cp_modification_list *modification,
|
||||
ngx_http_modification_type_e type,
|
||||
ngx_pool_t *pool
|
||||
)
|
||||
{
|
||||
write_dbg(DBG_LEVEL_DEBUG, "Freeing header modification");
|
||||
|
||||
switch (type) {
|
||||
case APPEND: {
|
||||
ngx_pfree(pool, modification->next->modification_buffer);
|
||||
ngx_pfree(pool, modification->next);
|
||||
ngx_pfree(pool, modification->modification_buffer);
|
||||
ngx_pfree(pool, modification);
|
||||
break;
|
||||
}
|
||||
case INJECT:
|
||||
case REPLACE: {
|
||||
ngx_pfree(pool, modification->modification_buffer);
|
||||
ngx_pfree(pool, modification);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
write_dbg(DBG_LEVEL_ASSERT, "Unknown modification type: %d", type);
|
||||
}
|
||||
}
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_cp_header_modifier(
|
||||
ngx_list_t *headers,
|
||||
ngx_http_cp_modification_list *modifications,
|
||||
ngx_http_request_t *request,
|
||||
ngx_flag_t is_content_length
|
||||
)
|
||||
{
|
||||
ngx_http_modification_type_e type;
|
||||
ngx_http_cp_modification_list *next_modification;
|
||||
ngx_http_cp_list_iterator headers_iterator;
|
||||
init_list_iterator(headers, &headers_iterator);
|
||||
|
||||
while (modifications != NULL) {
|
||||
// Check if modification is a header.
|
||||
if (!modifications->modification.is_header) return NGX_OK;
|
||||
|
||||
type = modifications->modification.mod_type;
|
||||
|
||||
write_dbg(
|
||||
DBG_LEVEL_DEBUG,
|
||||
type == APPEND ?
|
||||
"Appending header" :
|
||||
type == REPLACE ?
|
||||
"Changing header's value" :
|
||||
"Injecting into header's value");
|
||||
|
||||
if (type == APPEND && validate_append_header_modification(modifications) != NGX_OK) {
|
||||
// Modification is not of a valid append type.
|
||||
free_modifications_list(modifications, request->pool);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (perform_header_modification(
|
||||
request,
|
||||
headers,
|
||||
&headers_iterator,
|
||||
modifications,
|
||||
type,
|
||||
is_content_length
|
||||
) != NGX_OK) {
|
||||
// Failed to perform modification on header
|
||||
write_dbg(DBG_LEVEL_WARNING, "Failed to perform modification on header");
|
||||
|
||||
free_modifications_list(modifications, request->pool);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
next_modification = get_next_header_modification(modifications, type);
|
||||
free_header_modification(modifications, type, request->pool);
|
||||
modifications = next_modification;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_cp_body_modifier(
|
||||
ngx_chain_t *body_chain,
|
||||
ngx_http_cp_modification_list *curr_modification,
|
||||
ngx_pool_t *pool
|
||||
)
|
||||
{
|
||||
ngx_http_cp_modification_list *next_modification;
|
||||
ngx_uint_t cur_body_chunk = 0;
|
||||
ngx_chain_t *chain_iter;
|
||||
ngx_chain_t *injected_chain_elem;
|
||||
ngx_uint_t num_appended_elements;
|
||||
size_t cur_chunk_size = 0;
|
||||
|
||||
for (chain_iter = body_chain; chain_iter; chain_iter = chain_iter->next, cur_body_chunk++) {
|
||||
// Iterates of the body chains
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
write_dbg(
|
||||
DBG_LEVEL_DEBUG,
|
||||
"Handling current modification. "
|
||||
"Injection position: %d, injection size: %d, original buffer index: %d, modification buffer: %s",
|
||||
curr_modification->modification.injection_pos,
|
||||
curr_modification->modification.injection_size,
|
||||
curr_modification->modification.orig_buff_index,
|
||||
curr_modification->modification_buffer
|
||||
);
|
||||
// Create a chain element.
|
||||
injected_chain_elem = create_chain_elem(
|
||||
curr_modification->modification.injection_size,
|
||||
curr_modification->modification_buffer,
|
||||
pool
|
||||
);
|
||||
|
||||
if (injected_chain_elem == NULL) {
|
||||
free_modifications_list(curr_modification, pool);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
write_dbg(DBG_LEVEL_DEBUG, "Handling modification of chain element number %d", cur_body_chunk);
|
||||
// Handling modification of a chain element.
|
||||
if (curr_modification->modification.injection_pos == 0) {
|
||||
// Pre appends chain element.
|
||||
prepend_chain_elem(chain_iter, injected_chain_elem);
|
||||
chain_iter = chain_iter->next;
|
||||
num_appended_elements = 0;
|
||||
} else if (curr_modification->modification.injection_pos == chain_iter->buf->last - chain_iter->buf->pos + 1) {
|
||||
// Prepend a chain element.
|
||||
append_chain_elem(chain_iter, injected_chain_elem);
|
||||
chain_iter = chain_iter->next;
|
||||
num_appended_elements = 1;
|
||||
} else {
|
||||
if (split_chain_elem(chain_iter, curr_modification->modification.injection_pos, pool) != NGX_OK) {
|
||||
// Failed to iterate over the modification.
|
||||
free_modifications_list(curr_modification, pool);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
append_chain_elem(chain_iter, injected_chain_elem);
|
||||
chain_iter = chain_iter->next->next;
|
||||
num_appended_elements = 2;
|
||||
}
|
||||
|
||||
// Moves to the next modification element and frees the modifier.
|
||||
next_modification = curr_modification->next;
|
||||
ngx_pfree(pool, curr_modification);
|
||||
curr_modification = next_modification;
|
||||
|
||||
cur_body_chunk += num_appended_elements;
|
||||
}
|
||||
return NGX_OK;
|
||||
}
|
120
attachments/nginx/ngx_module/ngx_cp_custom_response.h
Normal file
120
attachments/nginx/ngx_module/ngx_cp_custom_response.h
Normal file
@@ -0,0 +1,120 @@
|
||||
// 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_custom_response.h
|
||||
#ifndef __NGX_CP_CUSTOM_RESPONSE_H__
|
||||
#define __NGX_CP_CUSTOM_RESPONSE_H__
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
#include "nginx_attachment_common.h"
|
||||
|
||||
/// @struct ngx_http_cp_modification_list
|
||||
/// @brief A node that holds all the information regarding modifications.
|
||||
typedef struct ngx_http_cp_modification_list {
|
||||
struct ngx_http_cp_modification_list *next; ///< Next node.
|
||||
ngx_http_cp_inject_data_t modification; ///< Modification data.
|
||||
char *modification_buffer; ///< Modification buffer used to store extra needed data.
|
||||
} ngx_http_cp_modification_list;
|
||||
|
||||
///
|
||||
/// @brief Generates and sends a response headers.
|
||||
/// @param[in, out] request NGINX request.
|
||||
/// @param[in] response_code Response code.
|
||||
/// @param[in] content_length Content length.
|
||||
/// @param[in] last_modified_time Last modification time.
|
||||
/// @param[in] allow_ranges Allowed ranges.
|
||||
/// @param[in] keepalive Keep alive metadata.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
ngx_int_t
|
||||
ngx_http_cp_response_headers_sender(
|
||||
ngx_http_request_t *request,
|
||||
const ngx_uint_t response_code,
|
||||
const off_t content_length,
|
||||
const time_t last_modified_time,
|
||||
const unsigned int allow_ranges,
|
||||
const unsigned int keepalive
|
||||
);
|
||||
|
||||
///
|
||||
/// @brief Generates and sends a response file.
|
||||
/// @param[in, out] request NGINX request.
|
||||
/// @param[in, out] file_path NGINX string.
|
||||
/// @param[in, out] open_file_info NGINX file info - file information.
|
||||
/// @param[in] is_main_request Flags if the file is the main request.
|
||||
/// @param[in] log NGINX log.
|
||||
/// @param[in] memory_pool NGINX pool.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
ngx_int_t
|
||||
ngx_http_cp_file_response_sender(
|
||||
ngx_http_request_t *request,
|
||||
ngx_str_t *file_path,
|
||||
ngx_open_file_info_t *open_file_info,
|
||||
ngx_int_t is_main_request,
|
||||
ngx_log_t *log,
|
||||
ngx_pool_t *memory_pool
|
||||
);
|
||||
|
||||
///
|
||||
/// @brief Finalizing a rejected request.
|
||||
/// @param[in, out] request NGINX request.
|
||||
/// @return ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
ngx_int_t ngx_http_cp_finalize_rejected_request(ngx_http_request_t *request);
|
||||
|
||||
///
|
||||
/// @brief Modifies headers with the provided modifiers.
|
||||
/// @param[in, out] headers NGINX headers list.
|
||||
/// @param[in] modifications CP modification list.
|
||||
/// @param[in, out] request NGINX request.
|
||||
/// @param[in] is_content_length Flag that signals if the header is of content length.
|
||||
/// @return ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
ngx_int_t
|
||||
ngx_http_cp_header_modifier(
|
||||
ngx_list_t *headers,
|
||||
ngx_http_cp_modification_list *modifications,
|
||||
ngx_http_request_t *request,
|
||||
ngx_flag_t is_content_length
|
||||
);
|
||||
|
||||
///
|
||||
/// @brief Modifies body chain with the provided modifiers.
|
||||
/// @param[in, out] body_chain NGINX body chain.
|
||||
/// @param[in] modifications CP modification list.
|
||||
/// @param[in, out] modification_pool NGINX pool for modifications.
|
||||
/// @return ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
ngx_int_t
|
||||
ngx_http_cp_body_modifier(
|
||||
ngx_chain_t *body_chain,
|
||||
ngx_http_cp_modification_list *modifications,
|
||||
ngx_pool_t *modification_pool
|
||||
);
|
||||
|
||||
#endif // __NGX_CP_CUSTOM_RESPONSE_H__
|
238
attachments/nginx/ngx_module/ngx_cp_failing_state.c
Normal file
238
attachments/nginx/ngx_module/ngx_cp_failing_state.c
Normal file
@@ -0,0 +1,238 @@
|
||||
// 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_failing_state.c
|
||||
#include "ngx_cp_failing_state.h"
|
||||
|
||||
#include "ngx_cp_utils.h"
|
||||
#include "ngx_cp_initializer.h"
|
||||
#include "ngx_cp_metric.h"
|
||||
|
||||
#define FIVE_ERRORS_PER_FIFTEEN_SECONDS 0
|
||||
|
||||
///
|
||||
/// @struct failure_state
|
||||
/// @brief Holds failure state data.
|
||||
///
|
||||
typedef struct failure_state {
|
||||
ngx_uint_t max_allowed_failed_requests; ///< Maximum allowed failed requests.
|
||||
ngx_uint_t failing_interval_boundry; ///< Intervals between each failure.
|
||||
ngx_uint_t transparent_period_sec; ///< Transperancy mode period.
|
||||
} failure_state;
|
||||
|
||||
///
|
||||
/// @struct ngx_http_cp_periodic_failure
|
||||
/// @brief Holds NGINX periodic failure data.
|
||||
///
|
||||
typedef struct ngx_http_cp_periodic_failure {
|
||||
ngx_uint_t max_allowed_failed_requests; ///< Maximum allowed failed requests.
|
||||
ngx_uint_t current_failed_requests; ///< Current failed requests.
|
||||
ngx_flag_t is_transparent_mode_active; ///< Transparent mode flag.
|
||||
ngx_uint_t transparent_interval_boundry; ///< Transparent internval boundary.
|
||||
struct timeval transparent_interval; ///< Transparent time interval.
|
||||
struct timeval failing_interval; ///< Falling data time interval.
|
||||
} ngx_http_cp_periodic_failure;
|
||||
|
||||
/// Failure state session monitors.
|
||||
static const failure_state failed_sessions_monitor[] = {{5, 20, 60}, {5, 20, 300}, {5, 20, 600}};
|
||||
static const ngx_uint_t failed_sessions_monitor_length = 3;
|
||||
|
||||
static ngx_http_cp_periodic_failure current_periodic_failure = {
|
||||
.max_allowed_failed_requests = 0,
|
||||
.current_failed_requests = 0,
|
||||
.is_transparent_mode_active = 0,
|
||||
.transparent_interval_boundry = 0,
|
||||
.transparent_interval = {0, 0},
|
||||
.failing_interval = {0, 0}
|
||||
};
|
||||
|
||||
static ngx_uint_t current_fail_state = FIVE_ERRORS_PER_FIFTEEN_SECONDS;
|
||||
static ngx_flag_t should_update_timeout = 0;
|
||||
|
||||
///
|
||||
/// @brief Resetting current failure state.
|
||||
///
|
||||
static void
|
||||
reset_failure_state()
|
||||
{
|
||||
write_dbg(
|
||||
DBG_LEVEL_DEBUG,
|
||||
"Resetting failing interval to default. Interval: %u seconds",
|
||||
failed_sessions_monitor[current_fail_state].failing_interval_boundry
|
||||
);
|
||||
|
||||
current_periodic_failure.max_allowed_failed_requests =
|
||||
failed_sessions_monitor[current_fail_state].max_allowed_failed_requests;
|
||||
|
||||
current_periodic_failure.failing_interval =
|
||||
get_timeout_val_sec(failed_sessions_monitor[current_fail_state].failing_interval_boundry);
|
||||
}
|
||||
|
||||
ngx_int_t
|
||||
is_in_transparent_mode()
|
||||
{
|
||||
static int transparent_mode_is_active = 0;
|
||||
if (current_periodic_failure.is_transparent_mode_active && !is_timeout_reached(¤t_periodic_failure.transparent_interval)) {
|
||||
if (!transparent_mode_is_active) {
|
||||
write_dbg(
|
||||
DBG_LEVEL_INFO,
|
||||
"NGINX is in transparent mode. Transparent timeout: %u seconds",
|
||||
current_periodic_failure.transparent_interval_boundry
|
||||
);
|
||||
|
||||
updateMetricField(TOTAL_TRANSPARENTS_TIME, (uint64_t)current_periodic_failure.transparent_interval_boundry);
|
||||
}
|
||||
|
||||
transparent_mode_is_active = 1;
|
||||
return 1;
|
||||
}
|
||||
transparent_mode_is_active = 0;
|
||||
|
||||
current_periodic_failure.is_transparent_mode_active = 0;
|
||||
current_periodic_failure.current_failed_requests = 0;
|
||||
reset_failure_state();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
reset_transparent_mode()
|
||||
{
|
||||
current_fail_state = 0;
|
||||
should_update_timeout = 0;
|
||||
memset(¤t_periodic_failure, 0, sizeof(current_periodic_failure));
|
||||
|
||||
current_periodic_failure.max_allowed_failed_requests = failed_sessions_monitor[current_fail_state].max_allowed_failed_requests;
|
||||
current_periodic_failure.current_failed_requests = 0;
|
||||
current_periodic_failure.is_transparent_mode_active = 0;
|
||||
current_periodic_failure.transparent_interval_boundry = failed_sessions_monitor[current_fail_state].transparent_period_sec;
|
||||
current_periodic_failure.failing_interval = get_timeout_val_sec(failed_sessions_monitor[current_fail_state].failing_interval_boundry);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Activate transparent mode.
|
||||
///
|
||||
static void
|
||||
activate_transparent_mode()
|
||||
{
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Activating transparency mode. Transparency period: %u seconds",
|
||||
failed_sessions_monitor[current_fail_state].transparent_period_sec
|
||||
);
|
||||
current_periodic_failure.is_transparent_mode_active = 1;
|
||||
current_periodic_failure.transparent_interval = get_timeout_val_sec(failed_sessions_monitor[current_fail_state].transparent_period_sec);
|
||||
current_periodic_failure.transparent_interval_boundry = failed_sessions_monitor[current_fail_state].transparent_period_sec;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Reports the provided session's metric.
|
||||
/// @param[in, out] state Session's data to report the time metric of.
|
||||
///
|
||||
void
|
||||
ngx_http_cp_report_time_metrics(ngx_http_cp_session_data *state)
|
||||
{
|
||||
struct timespec session_end_time;
|
||||
clock_gettime(CLOCK_REALTIME, &session_end_time);
|
||||
|
||||
double begin_usec = (state->session_start_time.tv_sec * 1000000) + (state->session_start_time.tv_nsec / 1000);
|
||||
double end_usec = (session_end_time.tv_sec * 1000000) + (session_end_time.tv_nsec / 1000);
|
||||
double overall_process_time = end_usec - begin_usec;
|
||||
|
||||
updateMetricField(AVERAGE_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT, overall_process_time);
|
||||
updateMetricField(MAX_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT, overall_process_time);
|
||||
updateMetricField(MIN_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT, overall_process_time);
|
||||
|
||||
updateMetricField(AVERAGE_REQ_PPROCESSING_TIME_UNTIL_VERDICT, state->req_proccesing_time);
|
||||
updateMetricField(MAX_REQ_PPROCESSING_TIME_UNTIL_VERDICT, state->req_proccesing_time);
|
||||
updateMetricField(MIN_REQ_PPROCESSING_TIME_UNTIL_VERDICT, state->req_proccesing_time);
|
||||
|
||||
updateMetricField(AVERAGE_RES_PPROCESSING_TIME_UNTIL_VERDICT, state->res_proccesing_time);
|
||||
updateMetricField(MAX_RES_PPROCESSING_TIME_UNTIL_VERDICT, state->res_proccesing_time);
|
||||
updateMetricField(MIN_RES_PPROCESSING_TIME_UNTIL_VERDICT, state->res_proccesing_time);
|
||||
}
|
||||
|
||||
void
|
||||
handle_inspection_failure(int weight, ngx_int_t fail_mode_verdict, ngx_http_cp_session_data *state)
|
||||
{
|
||||
if (state->verdict != TRAFFIC_VERDICT_INSPECT) {
|
||||
// Skipping already inspected data.
|
||||
write_dbg(DBG_LEVEL_TRACE, "Skipping already inspected data");
|
||||
return;
|
||||
}
|
||||
|
||||
ngx_http_cp_report_time_metrics(state);
|
||||
|
||||
if (fail_mode_verdict == NGX_OK) {
|
||||
// Increase open fail counter.
|
||||
updateMetricField(INSPECTION_OPEN_FAILURES_COUNT, 1);
|
||||
state->verdict = TRAFFIC_VERDICT_ACCEPT;
|
||||
} else {
|
||||
// Increase close fail counter.
|
||||
updateMetricField(INSPECTION_CLOSE_FAILURES_COUNT, 1);
|
||||
state->verdict = TRAFFIC_VERDICT_DROP;
|
||||
}
|
||||
|
||||
current_periodic_failure.current_failed_requests += weight;
|
||||
|
||||
if (is_timeout_reached(¤t_periodic_failure.failing_interval)) {
|
||||
// Enough time had passed without errors. Limits can be reset
|
||||
reset_failure_state();
|
||||
return;
|
||||
}
|
||||
|
||||
if (current_periodic_failure.current_failed_requests <= current_periodic_failure.max_allowed_failed_requests) {
|
||||
write_dbg(
|
||||
DBG_LEVEL_TRACE,
|
||||
"Failure count did not reach maximum allowed limit. Current failure count: %u, Max failure limit: %u",
|
||||
current_periodic_failure.current_failed_requests,
|
||||
current_periodic_failure.max_allowed_failed_requests
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
activate_transparent_mode();
|
||||
|
||||
if (current_fail_state < failed_sessions_monitor_length) {
|
||||
// Setting new transparent interval.
|
||||
current_fail_state++;
|
||||
|
||||
write_dbg(
|
||||
DBG_LEVEL_DEBUG,
|
||||
"Setting new transparent interval. New interval: %u seconds",
|
||||
failed_sessions_monitor[current_fail_state].transparent_period_sec
|
||||
);
|
||||
} else {
|
||||
// Reached impossible fail state, setting highest level of state.
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Reached impossible fail state - Setting highest level instead. Current fail state: %u, New fail state: %u",
|
||||
current_fail_state,
|
||||
failed_sessions_monitor_length - 1
|
||||
);
|
||||
current_fail_state = failed_sessions_monitor_length - 1;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
handle_inspection_success(ngx_http_cp_session_data *state)
|
||||
{
|
||||
updateMetricField(INSPECTION_SUCCESSES_COUNT, 1);
|
||||
ngx_http_cp_report_time_metrics(state);
|
||||
if (!is_timeout_reached(¤t_periodic_failure.failing_interval)) return;
|
||||
if (current_periodic_failure.current_failed_requests != 0 || should_update_timeout == 1) return;
|
||||
|
||||
current_fail_state = 0;
|
||||
}
|
59
attachments/nginx/ngx_module/ngx_cp_failing_state.h
Normal file
59
attachments/nginx/ngx_module/ngx_cp_failing_state.h
Normal file
@@ -0,0 +1,59 @@
|
||||
// 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_failing_state.h
|
||||
#ifndef __NGX_CP_FAILING_STATE_H__
|
||||
#define __NGX_CP_FAILING_STATE_H__
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
|
||||
#include "nginx_attachment_common.h"
|
||||
#include "ngx_cp_hooks.h"
|
||||
|
||||
///
|
||||
/// @brief Returns if transparent mode is activated.
|
||||
/// @returns ngx_int_t
|
||||
/// - #0 if transparent mode is off.
|
||||
/// - #1 if transparent mode is on.
|
||||
///
|
||||
ngx_int_t is_in_transparent_mode(void);
|
||||
|
||||
///
|
||||
/// @brief Handles inspection failure.
|
||||
/// @details Updates metric fields with the provided data.
|
||||
/// Metric fields included such as:
|
||||
/// - #INSPECTION_OPEN_FAILURES_COUNT
|
||||
/// - #INSPECTION_CLOSE_FAILURES_COUNT
|
||||
/// @param[in, out] weight Failure's weight.
|
||||
/// @param[in, out] fail_mode_verdict Fail mode verdict.
|
||||
/// @param[in, out] state NGINX session data.
|
||||
///
|
||||
void handle_inspection_failure(int weight, ngx_int_t fail_mode_verdict, ngx_http_cp_session_data *state);
|
||||
|
||||
///
|
||||
/// @brief Handles inspection success.
|
||||
/// @details The function updates "INSPECTION_SUCCESSES_COUNT" metric.
|
||||
/// Furthermore the function updates time verdicts metrics.
|
||||
/// @param[in, out] state NGINX session data.
|
||||
///
|
||||
void handle_inspection_success(ngx_http_cp_session_data *state);
|
||||
|
||||
///
|
||||
/// @brief Resets transparent mode.
|
||||
/// @details Reset transparent mode to 0, and all the related parameters.
|
||||
///
|
||||
void reset_transparent_mode(void);
|
||||
|
||||
#endif // __NGX_CP_FAILING_STATE_H__
|
453
attachments/nginx/ngx_module/ngx_cp_hook_threads.c
Normal file
453
attachments/nginx/ngx_module/ngx_cp_hook_threads.c
Normal file
@@ -0,0 +1,453 @@
|
||||
// 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_hook_threads.c
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
#include <ngx_files.h>
|
||||
#include <ngx_string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "nginx_attachment_util.h"
|
||||
#include "shmem_ipc.h"
|
||||
#include "compression_utils.h"
|
||||
#include "nginx_attachment_common.h"
|
||||
#include "ngx_cp_io.h"
|
||||
#include "ngx_cp_utils.h"
|
||||
#include "ngx_cp_initializer.h"
|
||||
#include "ngx_http_cp_attachment_module.h"
|
||||
#include "ngx_cp_static_content.h"
|
||||
#include "ngx_cp_compression.h"
|
||||
#include "ngx_cp_http_parser.h"
|
||||
#include "ngx_cp_hook_threads.h"
|
||||
#include "ngx_cp_failing_state.h"
|
||||
#include "ngx_cp_metric.h"
|
||||
#include "ngx_cp_thread.h"
|
||||
#include "ngx_cp_hooks.h"
|
||||
|
||||
///
|
||||
/// @brief THREAD_CTX_RETURN, sets this session's context to _X value and set the context
|
||||
/// to return without going to the next filter.
|
||||
///
|
||||
#define THREAD_CTX_RETURN(_X) do { ctx->res = (_X); ctx->should_return = 1; return NULL; } while (0);
|
||||
|
||||
///
|
||||
/// @brief THREAD_CTX_RETURN_NEXT_FILTER, sets this session's context to _X value and set the context
|
||||
/// to be scanned by the next filter.
|
||||
///
|
||||
#define THREAD_CTX_RETURN_NEXT_FILTER() do { ctx->should_return_next_filter = 1; return NULL; } while (0);
|
||||
|
||||
static ngx_int_t already_registered = 0; ///< Registration status with the nano service.
|
||||
static const ngx_int_t inspection_irrelevant = INSPECTION_IRRELEVANT;
|
||||
extern struct timeval metric_timeout; ///< Holds per-session metric timeout.
|
||||
|
||||
void
|
||||
set_already_registered(ngx_int_t value)
|
||||
{
|
||||
already_registered = value;
|
||||
}
|
||||
|
||||
ngx_int_t
|
||||
get_already_registered()
|
||||
{
|
||||
return already_registered;
|
||||
}
|
||||
|
||||
void
|
||||
init_thread_ctx(
|
||||
struct ngx_http_cp_event_thread_ctx_t *ctx,
|
||||
ngx_http_request_t *request,
|
||||
ngx_http_cp_session_data *session_data_p,
|
||||
ngx_chain_t *chain)
|
||||
{
|
||||
ctx->request = request;
|
||||
ctx->session_data_p = session_data_p;
|
||||
ctx->res = NGX_OK;
|
||||
ctx->should_return = 0;
|
||||
ctx->should_return_next_filter = 0;
|
||||
ctx->chain = chain;
|
||||
ctx->modifications = NULL;
|
||||
}
|
||||
|
||||
void *
|
||||
ngx_http_cp_registration_thread(void *_ctx)
|
||||
{
|
||||
struct ngx_http_cp_event_thread_ctx_t *ctx = (struct ngx_http_cp_event_thread_ctx_t*)_ctx;
|
||||
ngx_int_t res = ngx_cp_attachment_init_process();
|
||||
if (res == NGX_ABORT && already_registered) {
|
||||
already_registered = 0;
|
||||
disconnect_communication();
|
||||
reset_transparent_mode();
|
||||
}
|
||||
if (res != NGX_OK) {
|
||||
// failed to register to the attachment service.
|
||||
if (already_registered) handle_inspection_failure(registration_failure_weight, fail_mode_verdict, ctx->session_data_p);
|
||||
write_dbg(DBG_LEVEL_DEBUG, "Communication with nano service is not ready yet");
|
||||
THREAD_CTX_RETURN(NGX_OK);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// @brief Sends end request header to the attachment's service.
|
||||
/// @details Communicates with the attachment service by sending end request header
|
||||
/// to the attachment's service and returns verdict.
|
||||
/// @param[in, out] session_data_p If the function returns NGX_OK, session data will be modified.
|
||||
/// @param[in, out] request NGINX Request.
|
||||
/// @param[in] modifications A list of this session's data modifications.
|
||||
/// @return ngx_int_t of the following values:
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
static ngx_int_t
|
||||
end_req_header_handler(
|
||||
ngx_http_cp_session_data *session_data_p,
|
||||
ngx_http_request_t *request,
|
||||
ngx_http_cp_modification_list **modifications)
|
||||
{
|
||||
ngx_uint_t num_messages_sent = 0;
|
||||
|
||||
if (!does_contain_body(&(request->headers_in))) {
|
||||
if (ngx_http_cp_end_transaction_sender(REQUEST_END, session_data_p->session_id, &num_messages_sent) != NGX_OK) {
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Failed to send request end data to the nano service. Session ID: %d",
|
||||
session_data_p->session_id
|
||||
);
|
||||
handle_inspection_failure(inspection_failure_weight, fail_mode_verdict, session_data_p);
|
||||
return fail_mode_verdict;
|
||||
}
|
||||
session_data_p->remaining_messages_to_reply += num_messages_sent;
|
||||
}
|
||||
// Fetch nano services' results.
|
||||
return ngx_http_cp_reply_receiver(
|
||||
&session_data_p->remaining_messages_to_reply,
|
||||
&session_data_p->verdict,
|
||||
session_data_p->session_id,
|
||||
request,
|
||||
modifications
|
||||
);
|
||||
}
|
||||
|
||||
ngx_int_t
|
||||
does_contain_body(ngx_http_headers_in_t *headers)
|
||||
{
|
||||
return headers->chunked || (headers->content_length_n != (off_t)(-1) && headers->content_length_n > 0);
|
||||
}
|
||||
|
||||
void *
|
||||
ngx_http_cp_req_header_handler_thread(void *_ctx)
|
||||
{
|
||||
struct ngx_http_cp_event_thread_ctx_t *ctx = (struct ngx_http_cp_event_thread_ctx_t*)_ctx;
|
||||
ngx_http_request_t *request = ctx->request;
|
||||
ngx_http_cp_session_data *session_data_p = ctx->session_data_p;
|
||||
ngx_int_t send_meta_data_result;
|
||||
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);
|
||||
if (send_meta_data_result == inspection_irrelevant) {
|
||||
// Ignoring irrelevant requests.
|
||||
session_data_p->verdict = TRAFFIC_VERDICT_IRRELEVANT;
|
||||
write_dbg(DBG_LEVEL_DEBUG, "Ignoring non-interesting request. Session ID: %d", session_data_p->session_id);
|
||||
THREAD_CTX_RETURN(NGX_OK);
|
||||
}
|
||||
|
||||
if (send_meta_data_result != NGX_OK) {
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Failed to send request meta data to the nano service. Session ID: %d",
|
||||
session_data_p->session_id
|
||||
);
|
||||
handle_inspection_failure(inspection_failure_weight, fail_mode_verdict, session_data_p);
|
||||
THREAD_CTX_RETURN(fail_mode_verdict);
|
||||
}
|
||||
session_data_p->remaining_messages_to_reply += num_messages_sent;
|
||||
|
||||
if (is_timeout_reached(&metric_timeout)) {
|
||||
// Thread task was timed out.
|
||||
set_metric_cpu_usage();
|
||||
set_metric_memory_usage();
|
||||
if (ngx_http_cp_metric_data_sender() != NGX_OK) {
|
||||
write_dbg(DBG_LEVEL_DEBUG, "Failed to send metric data from the plugin to the service");
|
||||
}
|
||||
metric_timeout = get_timeout_val_sec(METRIC_TIMEOUT_VAL);
|
||||
}
|
||||
|
||||
num_messages_sent = 0;
|
||||
send_header_result = ngx_http_cp_header_sender(
|
||||
&(request->headers_in.headers.part),
|
||||
REQUEST_HEADER,
|
||||
session_data_p->session_id,
|
||||
&num_messages_sent
|
||||
);
|
||||
if (send_header_result != NGX_OK) {
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Failed to send request 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);
|
||||
THREAD_CTX_RETURN(fail_mode_verdict);
|
||||
}
|
||||
session_data_p->remaining_messages_to_reply += num_messages_sent;
|
||||
|
||||
// Notify the nano service that we've reached the end of the request headers.
|
||||
ctx->res = end_req_header_handler(session_data_p, request, &ctx->modifications);
|
||||
|
||||
// The caller function will continue and apply the modified ctx->modifications
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *
|
||||
ngx_http_cp_req_body_filter_thread(void *_ctx)
|
||||
{
|
||||
struct ngx_http_cp_event_thread_ctx_t *ctx = (struct ngx_http_cp_event_thread_ctx_t*)_ctx;
|
||||
ngx_http_request_t *request = ctx->request;
|
||||
ngx_http_cp_session_data *session_data_p = ctx->session_data_p;
|
||||
ngx_int_t is_last_part;
|
||||
ngx_int_t send_body_result;
|
||||
ngx_uint_t num_messages_sent = 0;
|
||||
|
||||
send_body_result = ngx_http_cp_body_sender(
|
||||
ctx->chain,
|
||||
REQUEST_BODY,
|
||||
session_data_p,
|
||||
&is_last_part,
|
||||
&num_messages_sent,
|
||||
&ctx->chain
|
||||
);
|
||||
|
||||
if (send_body_result != NGX_OK) {
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Failed to send request body data 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_ERROR);
|
||||
}
|
||||
session_data_p->remaining_messages_to_reply += num_messages_sent;
|
||||
|
||||
num_messages_sent = 0;
|
||||
if (is_last_part) {
|
||||
// Signals the nano service that the transaction reached the end.
|
||||
if (ngx_http_cp_end_transaction_sender(REQUEST_END, session_data_p->session_id, &num_messages_sent) != NGX_OK) {
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Failed to send request end data 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_ERROR);
|
||||
}
|
||||
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
|
||||
);
|
||||
|
||||
if (is_last_part) session_data_p->was_request_fully_inspected = 1;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *
|
||||
ngx_http_cp_res_header_filter_thread(void *_ctx)
|
||||
{
|
||||
struct ngx_http_cp_event_thread_ctx_t *ctx = (struct ngx_http_cp_event_thread_ctx_t*)_ctx;
|
||||
ngx_http_request_t *request = ctx->request;
|
||||
ngx_http_cp_session_data *session_data_p = ctx->session_data_p;
|
||||
ngx_int_t set_response_content_encoding_res;
|
||||
ngx_int_t send_res_code_result;
|
||||
ngx_int_t send_content_length_result;
|
||||
ngx_int_t send_header_result;
|
||||
ngx_uint_t num_messages_sent = 0;
|
||||
|
||||
// Sends response code to the nano service.
|
||||
send_res_code_result = ngx_http_cp_res_code_sender(
|
||||
request->headers_out.status,
|
||||
session_data_p->session_id,
|
||||
&num_messages_sent
|
||||
);
|
||||
if (send_res_code_result != NGX_OK) {
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Failed to send response meta data 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_ERROR);
|
||||
}
|
||||
|
||||
session_data_p->remaining_messages_to_reply += num_messages_sent;
|
||||
num_messages_sent = 0;
|
||||
|
||||
// Sends response content length to the nano service.
|
||||
send_content_length_result = ngx_http_cp_content_length_sender(
|
||||
request->headers_out.content_length_n,
|
||||
session_data_p->session_id,
|
||||
&num_messages_sent
|
||||
);
|
||||
if (send_content_length_result != NGX_OK) {
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Failed to send headers content length 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_ERROR);
|
||||
}
|
||||
|
||||
session_data_p->remaining_messages_to_reply += num_messages_sent;
|
||||
|
||||
// Sets response body's content encoding.
|
||||
set_response_content_encoding_res = set_response_content_encoding(
|
||||
&session_data_p->response_data.original_compression_type,
|
||||
request->headers_out.content_encoding
|
||||
);
|
||||
if (set_response_content_encoding_res != NGX_OK) {
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Failed to set response body's content encoding. 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_ERROR);
|
||||
}
|
||||
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
|
||||
);
|
||||
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_ERROR);
|
||||
}
|
||||
|
||||
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->session_id,
|
||||
request,
|
||||
&ctx->modifications
|
||||
);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *
|
||||
ngx_http_cp_res_body_filter_thread(void *_ctx)
|
||||
{
|
||||
struct ngx_http_cp_event_thread_ctx_t *ctx = (struct ngx_http_cp_event_thread_ctx_t*)_ctx;
|
||||
ngx_http_request_t *request = ctx->request;
|
||||
ngx_http_cp_session_data *session_data_p = ctx->session_data_p;
|
||||
ngx_int_t send_body_result;
|
||||
ngx_uint_t num_messages_sent = 0;
|
||||
ngx_int_t is_last_response_part = 0;
|
||||
|
||||
// Send response body data to the nano service.
|
||||
send_body_result = ngx_http_cp_body_sender(
|
||||
ctx->chain,
|
||||
RESPONSE_BODY,
|
||||
session_data_p,
|
||||
&is_last_response_part,
|
||||
&num_messages_sent,
|
||||
&ctx->chain
|
||||
);
|
||||
if (send_body_result != NGX_OK) {
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Failed to send response body data 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_ERROR);
|
||||
}
|
||||
session_data_p->remaining_messages_to_reply += num_messages_sent;
|
||||
|
||||
num_messages_sent = 0;
|
||||
if (is_last_response_part) {
|
||||
// Signals the nano service that the transaction reached the end.
|
||||
if (ngx_http_cp_end_transaction_sender(RESPONSE_END, session_data_p->session_id, &num_messages_sent) != NGX_OK) {
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Failed to send response end data 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_ERROR);
|
||||
}
|
||||
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
|
||||
);
|
||||
|
||||
return NULL;
|
||||
}
|
157
attachments/nginx/ngx_module/ngx_cp_hook_threads.h
Normal file
157
attachments/nginx/ngx_module/ngx_cp_hook_threads.h
Normal file
@@ -0,0 +1,157 @@
|
||||
// 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_hook_threads.h
|
||||
#ifndef __NGX_CP_HOOK_THREADS_H__
|
||||
#define __NGX_CP_HOOK_THREADS_H__
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
#include <unistd.h>
|
||||
|
||||
typedef struct ngx_http_cp_session_data ngx_http_cp_session_data; ///< Holds per-session data.
|
||||
typedef struct ngx_http_cp_modification_list ngx_http_cp_modification_list; ///< Holds per-session data modifications.
|
||||
|
||||
/// @struct ngx_http_cp_event_thread_ctx_t
|
||||
/// @brief Holds all the information needed to communicate with the attachment service.
|
||||
struct ngx_http_cp_event_thread_ctx_t
|
||||
{
|
||||
ngx_http_request_t *request; ///< NGINX request.
|
||||
ngx_http_cp_session_data *session_data_p; ///< Provided session data.
|
||||
ngx_chain_t *chain; ///< only relevant to body filters
|
||||
|
||||
/// Connection results with the attachment service
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
ngx_int_t res;
|
||||
|
||||
/// Sets if the context should return and not continue to the next filter.
|
||||
int should_return;
|
||||
|
||||
/// Should context continue to the next filter.
|
||||
int should_return_next_filter;
|
||||
|
||||
ngx_http_cp_modification_list *modifications; ///< Context's modification.
|
||||
};
|
||||
|
||||
///
|
||||
/// @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);
|
||||
|
||||
///
|
||||
/// @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:
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
/// @return NULL.
|
||||
///
|
||||
void
|
||||
init_thread_ctx(
|
||||
struct ngx_http_cp_event_thread_ctx_t *ctx,
|
||||
ngx_http_request_t *request,
|
||||
ngx_http_cp_session_data *session_data_p,
|
||||
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:
|
||||
/// - #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:
|
||||
/// - #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:
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
/// @return NULL.
|
||||
///
|
||||
void * ngx_http_cp_req_body_filter_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:
|
||||
/// - #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:
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
/// @return NULL.
|
||||
///
|
||||
void * ngx_http_cp_res_body_filter_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__
|
1030
attachments/nginx/ngx_module/ngx_cp_hooks.c
Normal file
1030
attachments/nginx/ngx_module/ngx_cp_hooks.c
Normal file
File diff suppressed because it is too large
Load Diff
119
attachments/nginx/ngx_module/ngx_cp_hooks.h
Normal file
119
attachments/nginx/ngx_module/ngx_cp_hooks.h
Normal file
@@ -0,0 +1,119 @@
|
||||
// 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_hooks.h
|
||||
#ifndef __NGX_CP_HOOKS_H__
|
||||
#define __NGX_CP_HOOKS_H__
|
||||
|
||||
#include <time.h>
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ngx_cp_http_parser.h"
|
||||
#include "nginx_attachment_common.h"
|
||||
|
||||
static const int registration_failure_weight = 2; ///< Registration failure weight.
|
||||
static const int inspection_failure_weight = 1; ///< Inspection failure weight.
|
||||
static const ngx_int_t METRIC_TIMEOUT_VAL = METRIC_PERIODIC_TIMEOUT;
|
||||
|
||||
/// @struct ngx_http_cp_session_data
|
||||
/// @brief Holds all the session's information needed to communicate with the nano service.
|
||||
/// @details Such as to save verdict and session ID between the request and the response
|
||||
typedef struct ngx_http_cp_session_data {
|
||||
ngx_int_t was_request_fully_inspected; ///< Holds if the request fully inspected.
|
||||
ngx_http_cp_verdict_e verdict; ///< Holds the session's verdict from the Nano Service.
|
||||
uint32_t session_id; ///< Current session's Id.
|
||||
ngx_int_t remaining_messages_to_reply; ///< Remaining messages left for the agent to respond to.
|
||||
ngx_http_response_data response_data; ///< Holds session's response data.
|
||||
struct timespec session_start_time; ///< Holds session's start time.
|
||||
double req_proccesing_time; ///< Holds session's request processing time.
|
||||
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_http_cp_session_data;
|
||||
|
||||
///
|
||||
/// @brief Sends response body to the nano service.
|
||||
/// @details Initiates all the needed context data and session data and calls the relevant threads
|
||||
/// to communicate the response bodies to the nano service and fetch the response back to the NGINX.
|
||||
/// @param[in, out] request NGINX request.
|
||||
/// @param[in, out] input NGINX body chain.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_HTTP_FORBIDDEN
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
ngx_int_t ngx_http_cp_res_body_filter(ngx_http_request_t *request, ngx_chain_t *input);
|
||||
|
||||
///
|
||||
/// @brief Sends request body to the nano service.
|
||||
/// @details Initiates all the needed context data and session data and calls the relevant threads
|
||||
/// to communicate the request bodies to the nano service and fetch the response back to the NGINX.
|
||||
/// @param[in, out] request NGINX request.
|
||||
/// @param[in, out] input_body_chain NGINX body chain.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_HTTP_FORBIDDEN
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
ngx_int_t ngx_http_cp_req_body_filter(ngx_http_request_t *request, ngx_chain_t *input_body_chain);
|
||||
|
||||
///
|
||||
/// @brief Sends response headers to the nano service.
|
||||
/// @details Initiates all the needed context data and session data and calls the relevant threads
|
||||
/// to communicate the response headers to the nano service and fetch the response back to the NGINX.
|
||||
/// @param[in, out] request NGINX request.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_HTTP_FORBIDDEN
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
ngx_int_t ngx_http_cp_res_header_filter(ngx_http_request_t *request);
|
||||
|
||||
///
|
||||
/// @brief Sends request headers to the nano service.
|
||||
/// @details Initiates all the needed context data and session data and calls the relevant threads
|
||||
/// to communicate the request headers to the nano service and fetch the response back to the NGINX.
|
||||
/// @param[in, out] request NGINX request.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_HTTP_FORBIDDEN
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
ngx_int_t ngx_http_cp_req_header_handler(ngx_http_request_t *request);
|
||||
|
||||
///
|
||||
/// @brief Checks if transaction was timed out.
|
||||
/// @param[in, out] ctx
|
||||
/// @note ctx needs to be properly intialized.
|
||||
/// @returns ngx_int_t
|
||||
/// - #0 - Timed out did not occure.
|
||||
/// - #1 - Timed out occured.
|
||||
///
|
||||
ngx_int_t was_transaction_timedout(ngx_http_cp_session_data *ctx);
|
||||
|
||||
|
||||
///
|
||||
/// @brief Enforces the sessions rate.
|
||||
/// @returns ngx_http_cp_verdict_e
|
||||
/// - #TRAFFIC_VERDICT_INSPECT
|
||||
/// - #TRAFFIC_VERDICT_ACCEPT
|
||||
/// - #TRAFFIC_VERDICT_DROP
|
||||
///
|
||||
ngx_http_cp_verdict_e enforce_sessions_rate();
|
||||
|
||||
#endif // __NGX_CP_HOOKS_H__
|
88
attachments/nginx/ngx_module/ngx_cp_http_parser.c
Normal file
88
attachments/nginx/ngx_module/ngx_cp_http_parser.c
Normal file
@@ -0,0 +1,88 @@
|
||||
// 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_http_parser.c
|
||||
#include "ngx_cp_http_parser.h"
|
||||
|
||||
#include "ngx_cp_utils.h"
|
||||
|
||||
static const char *gzip_encoding_string = "gzip";
|
||||
static const char *zlib_encoding_string = "deflate";
|
||||
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) {
|
||||
/// Sets GZIP encoding.
|
||||
*response_encoding = GZIP;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (ngx_strcmp(content_encoding_header_value->data, 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) {
|
||||
/// Sets NO_COMPRESSION encoding.
|
||||
*response_encoding = NO_COMPRESSION;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Unsupported response content encoding: %.*s",
|
||||
content_encoding_header_value->len,
|
||||
content_encoding_header_value->data
|
||||
);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_int_t
|
||||
set_response_content_encoding(CompressionType *content_encoding, const ngx_table_elt_t *content_encoding_header)
|
||||
{
|
||||
ngx_int_t parse_content_encoding_result;
|
||||
|
||||
write_dbg(DBG_LEVEL_TRACE, "Determining response body's content encoding");
|
||||
|
||||
if (content_encoding_header == NULL) {
|
||||
*content_encoding = NO_COMPRESSION;
|
||||
write_dbg(DBG_LEVEL_TRACE, "Response body is not encoded");
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
write_dbg(
|
||||
DBG_LEVEL_TRACE,
|
||||
"Detected Content-Encoding header: key: %.*s, value: %.*s",
|
||||
content_encoding_header->key.len,
|
||||
content_encoding_header->key.data,
|
||||
content_encoding_header->value.len,
|
||||
content_encoding_header->value.data
|
||||
);
|
||||
/// Parses content header's value into content_encoding variable.
|
||||
parse_content_encoding_result = parse_content_encoding(content_encoding, &content_encoding_header->value);
|
||||
if (parse_content_encoding_result != NGX_OK) return NGX_ERROR;
|
||||
|
||||
write_dbg(
|
||||
DBG_LEVEL_TRACE,
|
||||
"Parsed content encoding: %.*s",
|
||||
content_encoding_header->value.len,
|
||||
content_encoding_header->value.data
|
||||
);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
84
attachments/nginx/ngx_module/ngx_cp_http_parser.h
Normal file
84
attachments/nginx/ngx_module/ngx_cp_http_parser.h
Normal file
@@ -0,0 +1,84 @@
|
||||
// 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_http_parser.h
|
||||
#ifndef __NGX_CP_HTTP_PARSER_H__
|
||||
#define __NGX_CP_HTTP_PARSER_H__
|
||||
|
||||
#include <ngx_core.h>
|
||||
|
||||
#include "compression_utils.h"
|
||||
|
||||
/// @struct ngx_http_response_data
|
||||
/// Holds all the data for NGINX CP http response data.
|
||||
typedef struct {
|
||||
uint32_t num_body_chunk; ///< Number of body chunks.
|
||||
|
||||
/// NGINX Response data status
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
ngx_int_t response_data_status;
|
||||
|
||||
/// Original compression type, can hold the following values:
|
||||
/// - #GZIP
|
||||
/// - #ZLIB
|
||||
CompressionType original_compression_type;
|
||||
|
||||
/// A new compression type, can hold the following values:
|
||||
/// - #GZIP
|
||||
/// - #ZLIB
|
||||
CompressionType new_compression_type;
|
||||
|
||||
/// Compression stream
|
||||
CompressionStream *compression_stream;
|
||||
|
||||
/// Decompression stream
|
||||
CompressionStream *decompression_stream;
|
||||
} ngx_http_response_data;
|
||||
|
||||
///
|
||||
/// @brief Parses content encoding and returns it in response_encoding.
|
||||
/// @param[in, out] response_encoding Returns value of one of the supported encoding:
|
||||
/// - #GZIP
|
||||
/// - #ZLIB
|
||||
/// - #NO_COMPRESSION
|
||||
/// @param[in, out] content_encoding_header_value Encoded value.
|
||||
/// @return ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR - Unsupported encoding.
|
||||
///
|
||||
ngx_int_t
|
||||
parse_content_encoding(
|
||||
CompressionType *response_encoding,
|
||||
const ngx_str_t *content_encoding_header_value
|
||||
);
|
||||
|
||||
///
|
||||
/// @brief Sets the content encoding type of the provided encoding header.
|
||||
/// @param[in, out] content_encoding Returns variable of one of the supported encoding:
|
||||
/// - #GZIP
|
||||
/// - #ZLIB
|
||||
/// - #NO_COMPRESSION
|
||||
/// @param[in, out] content_encoding_header NGINX table Encoding header.
|
||||
/// @return ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR - Unsupported encoding.
|
||||
///
|
||||
ngx_int_t
|
||||
set_response_content_encoding(
|
||||
CompressionType *content_encoding,
|
||||
const ngx_table_elt_t *content_encoding_header
|
||||
);
|
||||
|
||||
#endif // __NGX_CP_HTTP_PARSER_H__
|
496
attachments/nginx/ngx_module/ngx_cp_http_usercheck.h
Normal file
496
attachments/nginx/ngx_module/ngx_cp_http_usercheck.h
Normal file
@@ -0,0 +1,496 @@
|
||||
// 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_http_usercheck.h
|
||||
/// \brief This file contains the string for the block-page returned instead of the normal server response
|
||||
|
||||
"<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n"
|
||||
"\t\t<meta name=\"robots\" content=\"noindex,nofollow\">\n"
|
||||
"\t\t<meta content=\"yes\" name=\"apple-mobile-web-app-capable\">\n"
|
||||
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=0.75\">\t\n"
|
||||
" <div id=\"portal_top_pane_div\" class=\"usercheck_header_class\" >\n"
|
||||
"<img class=\"usercheck_company_logo_class\" id=\"usercheck_company_logo\"><table height=\"108px\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" class=\"usercheck_header_table_class usercheck_content_table_class\">\n"
|
||||
"\t\t<img style='display:block; width:75px;height:50px;' class='usercheck_company_logo_class' id='usercheck_company_logo' \n"
|
||||
" src='data:image/jpeg;base64, iVBORw0KGgoAAAANSUhEUgAAAJMAAABuCAYAAAAj6SHSAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAEFkb2JlIEltYWdlUmVhZHlxyWU8AAADIWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkFCRkI4ODI2MzM2RDExRTg5MDg5RjkxMTgwQTA1QTZDIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkFCRkI4ODI3MzM2RDExRTg5MDg5RjkxMTgwQTA1QTZDIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QUJGQjg4MjQzMzZEMTFFODkwODlGOTExODBBMDVBNkMiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QUJGQjg4MjUzMzZEMTFFODkwODlGOTExODBBMDVBNkMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94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' /> \t\t\n"
|
||||
" <p style=\"font-size: 22px;font-family: 'Segoe UI'\">Check Point Infinity Next</p>\n"
|
||||
"</table>\n"
|
||||
" </div>\n"
|
||||
" <style>\n"
|
||||
"p\n"
|
||||
"{\n"
|
||||
"\tmargin: 0px;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"/* SDB_PREVIEW_EXCLUDE_BEGIN */\n"
|
||||
".portalMessage_title\n"
|
||||
"{\n"
|
||||
"\tfont-size: 18px;\n"
|
||||
"\tfont-weight: bold;\n"
|
||||
"\tcolor: #FFFFFF;\n"
|
||||
"\tbackground: url('/UserCheck/img/pop_up_banner.png') repeat-x center center;\n"
|
||||
" height: 60px;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".portalMessage_title_td\n"
|
||||
"{\n"
|
||||
" padding-top: 13px;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".portalMessageTableContainer\n"
|
||||
"{\n"
|
||||
"\tfont-family: \"Segoe UI\";\n"
|
||||
"\twidth: 100%;\n"
|
||||
"\tcursor: default;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"/* SDB_PREVIEW_EXCLUDE_END */\n"
|
||||
"\n"
|
||||
".usercheck_body_background_class\n"
|
||||
"{\n"
|
||||
"\tbackground: #f7f7f7 repeat scroll center center;\t\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_main_background_class\n"
|
||||
"{\n"
|
||||
"\tbackground: repeat-y scroll center center;\t\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_content_table_class\n"
|
||||
"{\n"
|
||||
"\tposition: relative;\n"
|
||||
"\twidth:550px;\n"
|
||||
"\ttable-layout: fixed;\n"
|
||||
"\ttext-wrap: suppress;\n"
|
||||
"\tmargin-left:auto;\n"
|
||||
"\tmargin-right:auto;\n"
|
||||
"\tmargin-top: 35px;\n"
|
||||
"\ttext-align:center;\t\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_buttons_table_class\n"
|
||||
"{\t\n"
|
||||
"\tposition: relative;\n"
|
||||
"\tmargin-top: -15px;\n"
|
||||
"\twidth:400px;\n"
|
||||
"\ttable-layout: fixed;\n"
|
||||
"\ttext-wrap: suppress;\n"
|
||||
"\tmargin-left:auto;\n"
|
||||
"\tmargin-right:auto;\n"
|
||||
"\ttext-align:center;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_title_class\n"
|
||||
"{\n"
|
||||
"\tposition: relative;\n"
|
||||
"\tmargin-top: 20px;\n"
|
||||
"\tmargin-left:auto;\n"
|
||||
"\tmargin-right:auto;\n"
|
||||
"\tfont-family: \"Segoe UI\";\n"
|
||||
"\tfont-size: 40px;\n"
|
||||
"\tfont-weight: 100;\n"
|
||||
"\tcolor: #333333;\n"
|
||||
"\tword-wrap:break-word;\n"
|
||||
"\twidth: expression( document.body.clientWidth > 599 ? \"600px\" : \"auto\" ); /* sets max-width for IE */\n"
|
||||
"\tmax-width: 600px; /* this sets the max-width value for all standards-compliant browsers */\n"
|
||||
"\ttext-align: center;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_message_icon_class\n"
|
||||
"{\t\n"
|
||||
"\tmargin-right: auto;\n"
|
||||
"\tmargin-left: auto;\n"
|
||||
"\tposition: relative;\n"
|
||||
"\tmargin-top: 35px;\n"
|
||||
"\twidth: auto;\n"
|
||||
"\theight: auto;\n"
|
||||
"\ttext-align: center;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_message_icon_column\n"
|
||||
"{\n"
|
||||
"\twidth:70px;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_red_highlight\n"
|
||||
"{\n"
|
||||
"\tborder: 1px red solid;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_red_no_highlight\n"
|
||||
"{\n"
|
||||
"\tborder: auto;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_logo_img_div_class\n"
|
||||
"{\n"
|
||||
"\tbackground: transparent url('cpnewlogo_1.png') no-repeat scroll;\n"
|
||||
"\tbackground-position: center;\n"
|
||||
"\tdisplay: block;\n"
|
||||
"\t/*width: auto; \n"
|
||||
"\theight: auto; \n"
|
||||
"\tmargin: 0;\n"
|
||||
"\tpadding: 0;*/\n"
|
||||
"\tposition: relative;\n"
|
||||
"\tmargin-top: 30px;\n"
|
||||
"\tmargin-right: auto;\n"
|
||||
"\tmargin-left: auto;\n"
|
||||
"\theight: 30px;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".smartField incident_id\n"
|
||||
"{\n"
|
||||
"\tcolor: #7c7c7c;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".smartField orig_url\n"
|
||||
"{\n"
|
||||
"\tcolor: 1b81ff; \n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_message_class\n"
|
||||
"{\n"
|
||||
"\tmargin-right: auto;\n"
|
||||
"\tmargin-left: auto;\n"
|
||||
"\tposition: relative;\n"
|
||||
"\tmargin-top: 0px;\n"
|
||||
"\tfont-family: \"Segoe UI\";\n"
|
||||
"\tfont-size: 17px;\n"
|
||||
"\tcolor: #333333;\n"
|
||||
"\tline-height: 20px;\n"
|
||||
"\ttext-align: center;\n"
|
||||
"\twidth: expression( document.body.clientWidth > 499 ? \"550px\" : \"auto\" ); /* sets max-width for IE */\n"
|
||||
"\tmax-width: 550px; /* this sets the max-width value for all standards-compliant browsers */\n"
|
||||
"\tmin-width: 550px; /* this sets the max-width value for all standards-compliant browsers */\n"
|
||||
"\ttext-wrap: suppress;\n"
|
||||
"\tword-wrap: break-word;\n"
|
||||
"\twhite-space: nowrap;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"p.instance_id_uuid {\n"
|
||||
"\tfont-size: 12;\n"
|
||||
"}\n"
|
||||
".usercheck_url_class\n"
|
||||
"{\n"
|
||||
"\tfont-family: \"Segoe UI\";\n"
|
||||
"\tfont-size: 17px;\n"
|
||||
"\t/* padding: 10px 15px 0px 15px; */\n"
|
||||
"\tline-height: 20px;\n"
|
||||
"\ttext-align: left;\n"
|
||||
"\twidth: expression( document.body.clientWidth > 334 ? \"335px\" : \"auto\" ); /* sets max-width for IE */\n"
|
||||
"\tmax-width: 335px; /* this sets the max-width value for all standards-compliant browsers */\n"
|
||||
"\ttext-decoration: underline;\n"
|
||||
"\tcolor: #3778FF;\n"
|
||||
"\ttext-wrap: suppress;\n"
|
||||
" word-wrap: break-word;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_url_class:hover\n"
|
||||
"{\n"
|
||||
"\tcolor: #1B2F5B;\n"
|
||||
"\ttext-decoration: underline;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_disabled_url_class\n"
|
||||
"{\n"
|
||||
"\tcolor: #1B2F5B;\n"
|
||||
"\ttext-decoration: none;\n"
|
||||
"\tcursor: text;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_disabled_url_class:hover\n"
|
||||
"{\n"
|
||||
"\tcolor: #1B2F5B;\n"
|
||||
"\ttext-decoration: none;\n"
|
||||
"\tcursor: text;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_textual_input_class\n"
|
||||
"{\n"
|
||||
"\t\n"
|
||||
"\tline-height: normal;\n"
|
||||
" padding: 2px 2px 2px 2px;\n"
|
||||
"\tmargin-left: -1px;\n"
|
||||
"\tmargin-top: 30px;\n"
|
||||
" width: 328px;\n"
|
||||
"\tmax-width: 328px;\n"
|
||||
"\tmin-width: 328px;\n"
|
||||
"\toverflow-y: auto;\n"
|
||||
"\toverflow-x: hidden;\n"
|
||||
"\ttext-align: left;\n"
|
||||
"\t\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_textual_input_class_required\n"
|
||||
"{\n"
|
||||
"\tcolor: #8F8F8F;\n"
|
||||
"\t/*border: 1px solid #92979b;*/\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_header_class\n"
|
||||
"{\n"
|
||||
"\tvertical-align: middle;\n"
|
||||
"\ttext-align: center;\n"
|
||||
"\tbackground-color: #ffffff;\n"
|
||||
" height: 108px;\n"
|
||||
"\tborder-bottom: 1px solid #c7c7c7;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_header_table_class\n"
|
||||
"{\n"
|
||||
"\tmargin-left:auto;\n"
|
||||
"\tmargin-right:auto;\n"
|
||||
"\tmargin-top: 0px;\n"
|
||||
"\twidth: 100%;\n"
|
||||
"\theight: 60px;\n"
|
||||
"\t/*direction:ltr;*/\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_company_logo_class\n"
|
||||
"{\n"
|
||||
"\tposition:relative;\n"
|
||||
"\tmargin-left: auto;\n"
|
||||
"\tmargin-right: auto;\n"
|
||||
"\tmargin-bottom: 6px;\n"
|
||||
"\tmargin-top: 3px;\n"
|
||||
"\ttext-align: center;\n"
|
||||
"\tmax-width: 300px;\n"
|
||||
"\tmax-height: 300px;\n"
|
||||
" font-size: 22px;\n"
|
||||
"\t/* SDB_PREVIEW_EXCLUDE_BEGIN */\n"
|
||||
"\twidth: expression( this.width > 175 ? ( this.width / this.height > 176/52 ? \"176px\" : \"auto\" ) : \"auto\" ); /* sets max-width for IE6 */\n"
|
||||
"\theight: expression( this.height > 51 ? ( this.width / this.height > 176/52 ? \"auto\" : \"52px\" ) : \"auto\" ); /* sets max-height for IE6 */\n"
|
||||
"\t/* SDB_PREVIEW_EXCLUDE_END */\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_header_left_class\n"
|
||||
"{\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_header_center_class\n"
|
||||
"{\t\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_header_right_class\n"
|
||||
"{\t\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_header_right_empty_class\n"
|
||||
"{\t\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_page_title_class\n"
|
||||
"{\n"
|
||||
"\tposition: relative;\n"
|
||||
"\tmargin-left:auto;\n"
|
||||
"\tmargin-right:auto;\n"
|
||||
"\t/*width: 1100px;*/\n"
|
||||
"\ttext-align: left;\n"
|
||||
"\tfont-family: \"Segoe UI\";\n"
|
||||
"\tfont-size: 23px;\n"
|
||||
"\tfont-weight: 100;\n"
|
||||
"\tcolor: #FFFFFF;\n"
|
||||
"\ttop: -12px;\n"
|
||||
"\t\n"
|
||||
"\t-webkit-user-select: text;\n"
|
||||
"\t-khtml-user-select: text;\n"
|
||||
"\t-moz-user-select: text;\n"
|
||||
"\t-o-user-select: text;\n"
|
||||
"\tuser-select: text;\n"
|
||||
"\t\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_footer_table_class\n"
|
||||
"{\n"
|
||||
"\tmargin-left:auto;\n"
|
||||
"\tmargin-right:auto;\n"
|
||||
"\twidth: 100%;\n"
|
||||
"\twidth: 100%;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".footer_logo\n"
|
||||
"{\t\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".footer_no_logo\n"
|
||||
"{\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_footer_height_class\n"
|
||||
"{\n"
|
||||
"\theight: 40px;\n"
|
||||
"\tvertical-align: bottom;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_footer_class\n"
|
||||
"{\t\n"
|
||||
"\tposition: relative;\n"
|
||||
"\tmargin-top: 80px;\n"
|
||||
"\tmargin-bottom: 10px;\n"
|
||||
"\tvertical-align: bottom;\n"
|
||||
"\ttext-align: right;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_lang_select_div_class\n"
|
||||
"{\n"
|
||||
"\tmargin-left: 60%;\n"
|
||||
"\tfont-family: \"Segoe UI\";\n"
|
||||
"\tfont-size: 17px;\n"
|
||||
"\tcolor: #333333;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_lang_select_class\n"
|
||||
"{\t\n"
|
||||
"\tfloat: right;\n"
|
||||
"\tfont-family: \"Segoe UI\";\n"
|
||||
"\tfont-size: 17px;\n"
|
||||
"\tcolor: #333333;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_ok_button_div_class\n"
|
||||
"{\n"
|
||||
"\twidth: 110px;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".usercheck_middle_button_div_class\n"
|
||||
"{\n"
|
||||
"\twidth: 110px;\n"
|
||||
"\t/*margin-left:auto;*/\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"/* Buttons section - split into several portions for IE support. */\n"
|
||||
"\n"
|
||||
"a.button {\n"
|
||||
"\tbackground: #4a4a4a;\n"
|
||||
" display: block;\n"
|
||||
" height: 28px;\n"
|
||||
"\tmargin-top: 15px;\n"
|
||||
" padding-right: 14px; /* Sliding doors padding. */\n"
|
||||
" text-decoration: none;\n"
|
||||
"\tcursor: pointer;\n"
|
||||
"\ttext-align: center;\n"
|
||||
"\tborder: solid 1px #2c2c2c;\n"
|
||||
"\tborder-radius: 2px;\n"
|
||||
"\t/* Normal state right image position. */\n"
|
||||
"\tbackground-position: right 0px;\n"
|
||||
"}\n"
|
||||
"a.button span {\n"
|
||||
"\tbackground: #4a4a4a;\n"
|
||||
"\tdisplay: block;\n"
|
||||
" line-height: 28px;\n"
|
||||
" padding: 0px 22px 0px 35px;\n"
|
||||
"\t/* Font styling. */\n"
|
||||
"\tfont-family: \"Segoe UI\";\n"
|
||||
"\tfont-weight: 600;\n"
|
||||
"\tfont-size: 17px;\n"
|
||||
"\tcolor: #FFFFFF;\n"
|
||||
"\t/* Cursor styling. */\n"
|
||||
"\tcursor: pointer;\n"
|
||||
"\t/* Normal state left image position. */\n"
|
||||
"\tbackground-position: left 0px;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"a.cancel_button span {\n"
|
||||
" padding: 0px 20px 0px 30px;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"a.buttonHover {\n"
|
||||
"\t/* Normal state right image position. */\n"
|
||||
"\tbackground-position: right -41px;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"a.buttonHover span {\n"
|
||||
"\t/* Normal state left image position. */\n"
|
||||
"\tbackground-position: left -41px;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"a.buttonActive {\n"
|
||||
"\t/* Normal state right image position. */\n"
|
||||
"\tbackground-position: right -82px;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"a.buttonActive span {\n"
|
||||
"\t/* Normal state left image position. */\n"
|
||||
"\tbackground-position: left -82px;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"a.buttonDisabled {\n"
|
||||
"\t/* Normal state right image position. */\n"
|
||||
"\tbackground-position: right -123px;\n"
|
||||
"\tcursor: default;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"a.buttonDisabled span {\n"
|
||||
"\t/* Text color. */\n"
|
||||
"\tcolor: #BABABA;\n"
|
||||
"\tdisplay: block;\n"
|
||||
"\tline-height: 41px;\n"
|
||||
"\tpadding: 0px 22px 0px 25px;\n"
|
||||
"\t/* Cursor styling. */\n"
|
||||
"\tcursor: default;\n"
|
||||
"\t/* Normal state left image position. */\n"
|
||||
"\tbackground-position: left -123px;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"a.buttonDisabled:hover {\n"
|
||||
"\tcolor: #BABABA;\n"
|
||||
"}\n"
|
||||
" </style>\n"
|
||||
"\t</head>\n"
|
||||
"\t<body class=\"usercheck_body_background_class\" unselectable=\"on\">\n"
|
||||
"\t\t<div id=\"portal_main_view\" class=\"\" unselectable=\"on\">\n"
|
||||
"\t\t\t<table height=\"100%\" width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" class=\"\">\n"
|
||||
"\t\t\t\t<tbody><tr>\n"
|
||||
"\t\t\t\t\t<td id=\"portal_center_div\" valign=\"top\" unselectable=\"on\" class=\"usercheck_main_background_class\">\n"
|
||||
"\t\t\t\t\t\t\n"
|
||||
"\t\t\t\t\t\t<div class=\"usercheck_message_icon_class\" id=\"usercheck_img_div\" unselectable=\"on\">\n"
|
||||
" <img style='display:block; width:60px;height:60px;' class=\"usercheck_message_icon_class\" id='usercheck_img_div' \n"
|
||||
" src='data:image/jpeg;base64, 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' />\n"
|
||||
" </div>\n"
|
||||
"\t\t\t\t\t\t<div class=\"locale_message usercheck_title_class\" data-locale-message-constnat=\"LOCALE_PHISHING_TITLE\" unselectable=\"on\" style=\"color: rgb(237, 85, 82);font-size: 31px;\"><!-- CHECK_POINT_USERCHECK_TITLE_PLACEHOLDER--></div>\t\t\t\t\t\t\n"
|
||||
"\t\t\t\t\t\t<div style=\"text-align: center;\" unselectable=\"on\">\n"
|
||||
"\t\t\t\t\t\t\t<table cellpadding=\"0\" cellspacing=\"0\" class=\"usercheck_content_table_class\">\n"
|
||||
"\t\t\t\t\t\t\t\t<tbody><tr>\n"
|
||||
"\t\t\t\t\t\t\t\t\t<td>\n"
|
||||
"\t\t\t\t\t\t\t\t\t\t<div class=\"usercheck_message_class\" unselectable=\"on\" style=\"FONT-FAMILY: 'Arial', 'sans-serif'\"><span class=\"locale_message\" data-locale-message-constnat=\"LOCALE_PHISHING_DOMAINS\" ><p><!-- CHECK_POINT_USERCHECK_BODY_PLACEHOLDER--></p> <p class=\"instance_id_uuid\"><br><!-- CHECK_POINT_USERCHECK_UUID_PLACEHOLDER--></p></span>\n"
|
||||
"</body></html>";
|
664
attachments/nginx/ngx_module/ngx_cp_initializer.c
Normal file
664
attachments/nginx/ngx_module/ngx_cp_initializer.c
Normal file
@@ -0,0 +1,664 @@
|
||||
// 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_initializer.c
|
||||
#include "ngx_cp_initializer.h"
|
||||
|
||||
#include <poll.h>
|
||||
#include <stdint.h>
|
||||
#include <dirent.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <ngx_log.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_string.h>
|
||||
#include <ngx_files.h>
|
||||
|
||||
#include "nginx_attachment_common.h"
|
||||
#include "ngx_cp_io.h"
|
||||
#include "ngx_cp_utils.h"
|
||||
#include "ngx_cp_static_content.h"
|
||||
#include "ngx_cp_compression.h"
|
||||
#include "attachment_types.h"
|
||||
#include "ngx_http_cp_attachment_module.h"
|
||||
|
||||
typedef enum ngx_cp_attachment_registration_state {
|
||||
SET_UNIQUE_ID,
|
||||
RESGISTER_TO_NODE,
|
||||
LOAD_CONFIG,
|
||||
LOAD_IPC,
|
||||
DONE
|
||||
} ngx_cp_attachment_registration_state_e; ///< Indicates the current initialization stage.
|
||||
|
||||
char unique_id[MAX_NGINX_UID_LEN] = ""; // Holds the unique identifier for this instance.
|
||||
char shared_verdict_signal_path[128]; // Holds the path associating the attachment and service.
|
||||
|
||||
int registration_socket = -1; // Holds the file descriptor used for registering the instance.
|
||||
|
||||
struct sockaddr_un server;
|
||||
|
||||
uint32_t nginx_user_id, nginx_group_id; // Hold the process UID and GID respectively.
|
||||
|
||||
int
|
||||
exchange_communication_data_with_service(
|
||||
int socket,
|
||||
void *data,
|
||||
uint32_t size,
|
||||
ngx_cp_comm_direction_e direction,
|
||||
struct timeval *remaining_timeout)
|
||||
{
|
||||
int res = 0;
|
||||
ngx_int_t retry;
|
||||
// `remaining_size` and `cur_data_ptr` are used to keep track of where we are in the memory.
|
||||
// This allows us to read to\write from the socket in parts (if we need to).
|
||||
int remaining_size = size;
|
||||
char *cur_data_ptr = data;
|
||||
|
||||
while (remaining_size > 0) {
|
||||
if (direction == WRITE_TO_SOCKET){
|
||||
res = write(socket, (void *)cur_data_ptr, remaining_size);
|
||||
} else {
|
||||
// The read operation must not block the attachment indefinitely.
|
||||
// To avoid that we check whether the socket has data to be read prior to the read operation.
|
||||
// If the socket doesn't have data to be read from within a reasonable time, we treat this as an error.
|
||||
for (retry = 0; retry < 3; retry++) {
|
||||
struct pollfd s_poll;
|
||||
s_poll.fd = socket;
|
||||
s_poll.events = POLLIN;
|
||||
s_poll.revents = 0;
|
||||
res = poll(&s_poll, 1, 1000);
|
||||
if (res > 0 && (s_poll.revents & POLLIN) != 0) break; // Socket is ready to be read from
|
||||
res = -1;
|
||||
}
|
||||
|
||||
if (res != -1) {
|
||||
res = read(socket, (void *)cur_data_ptr, remaining_size);
|
||||
}
|
||||
}
|
||||
|
||||
// `res` is -1 in case of an error: either write or read functions failed or socket wasn't available.
|
||||
if (res < 0) {
|
||||
close(socket);
|
||||
socket = -1;
|
||||
write_dbg_if_needed(DBG_LEVEL_TRACE, "Failed to communicate with the socket");
|
||||
break;
|
||||
}
|
||||
|
||||
remaining_size -= res;
|
||||
cur_data_ptr += res;
|
||||
|
||||
// If the operation exceeded the allowed time, treat it as a failure.
|
||||
if (is_timeout_reached(remaining_timeout)) {
|
||||
close(socket);
|
||||
socket = -1;
|
||||
write_dbg_if_needed(DBG_LEVEL_TRACE, "Reached timeout while communicating with the socket");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Initialize socket communication with the serive.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
static ngx_int_t
|
||||
init_signaling_socket()
|
||||
{
|
||||
uint8_t initialization_ack;
|
||||
int res = 0;
|
||||
uint8_t uid_size_to_send = strlen(unique_id);
|
||||
struct timeval timeout = get_timeout_val_sec(1);
|
||||
|
||||
// Close the old socket if there was one.
|
||||
if (comm_socket > 0) {
|
||||
close(comm_socket);
|
||||
comm_socket = -1;
|
||||
}
|
||||
|
||||
// Setup a new socket
|
||||
comm_socket = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (comm_socket < 0) {
|
||||
write_dbg_if_needed(DBG_LEVEL_WARNING, "Could not create socket");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
server.sun_family = AF_UNIX;
|
||||
strncpy(server.sun_path, shared_verdict_signal_path, sizeof(server.sun_path) - 1);
|
||||
|
||||
if (connect(comm_socket, (struct sockaddr *)&server, sizeof(struct sockaddr_un)) == -1) {
|
||||
close(comm_socket);
|
||||
comm_socket = -1;
|
||||
write_dbg_if_needed(
|
||||
DBG_LEVEL_DEBUG,
|
||||
"Could not connect to nano service. Path: %s, Error: %s",
|
||||
server.sun_path,
|
||||
strerror(errno)
|
||||
);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
// Pass the following information to the service (in this order):
|
||||
// 1. The length of the unique identifier for this instance.
|
||||
// 2. The unique identifier for this instance.
|
||||
// 3. The process UID.
|
||||
// 4. The process GID.
|
||||
// If any of them fail - return an error.
|
||||
res = exchange_communication_data_with_service(
|
||||
comm_socket,
|
||||
&uid_size_to_send,
|
||||
sizeof(uid_size_to_send),
|
||||
WRITE_TO_SOCKET,
|
||||
&timeout
|
||||
);
|
||||
if (res <= 0) {
|
||||
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to send unique id size");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
res = exchange_communication_data_with_service(
|
||||
comm_socket,
|
||||
unique_id,
|
||||
uid_size_to_send,
|
||||
WRITE_TO_SOCKET,
|
||||
&timeout
|
||||
);
|
||||
if (res <= 0) {
|
||||
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to send unique id %s", unique_id);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
res = exchange_communication_data_with_service(comm_socket, &nginx_user_id, sizeof(uint32_t), WRITE_TO_SOCKET, &timeout);
|
||||
if (res <= 0) {
|
||||
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to send nginx user id");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
res = exchange_communication_data_with_service(
|
||||
comm_socket,
|
||||
&nginx_group_id,
|
||||
sizeof(uint32_t),
|
||||
WRITE_TO_SOCKET,
|
||||
&timeout
|
||||
);
|
||||
if (res <= 0) {
|
||||
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to send nginx group id");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
// Get an acknowledgement form the service that communication has been established.
|
||||
timeout = get_timeout_val_sec(1);
|
||||
res = exchange_communication_data_with_service(
|
||||
comm_socket,
|
||||
&initialization_ack,
|
||||
sizeof(initialization_ack),
|
||||
READ_FROM_SOCKET,
|
||||
&timeout
|
||||
);
|
||||
if (res <= 0) {
|
||||
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to read registration ack");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
write_dbg_if_needed(DBG_LEVEL_DEBUG, "Successfully connected on client socket %d", comm_socket);
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
#define MAX_CONTAINER_LEN 12 // Maximum size for container identifier
|
||||
|
||||
ngx_int_t
|
||||
get_docker_id(char **_docker_id)
|
||||
{
|
||||
// We keep the container ID as a static variable so we won't have to read it multiple times.
|
||||
// The `already_evaluated` variable indicate if we already have the identifier.
|
||||
static char docker_id[MAX_CONTAINER_LEN + 1];
|
||||
static int already_evaluated = 0;
|
||||
const char *container_id_file_path = "/proc/self/cgroup";
|
||||
if (already_evaluated) {
|
||||
// Already found the identifier before, just return the answer.
|
||||
*_docker_id = docker_id;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
docker_id[0] = '\0';
|
||||
|
||||
FILE *file = fopen(container_id_file_path, "r");
|
||||
if (file == NULL) {
|
||||
write_dbg(DBG_LEVEL_WARNING, "Failed to open %s", container_id_file_path);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
write_dbg(DBG_LEVEL_DEBUG, "opened file %s", container_id_file_path);
|
||||
|
||||
// Reading the file line by line.
|
||||
char *line = NULL;
|
||||
size_t len = 0;
|
||||
while (getline(&line, &len, file) != -1) {
|
||||
char *docker_ptr = strstr(line, "docker/");
|
||||
if (docker_ptr == NULL) continue;
|
||||
|
||||
// We've found a line with "docker/" so the identifier will be right after that.
|
||||
docker_ptr += strlen("docker/");
|
||||
snprintf(docker_id, MAX_CONTAINER_LEN + 1, "%s", docker_ptr);
|
||||
break;
|
||||
}
|
||||
free(line);
|
||||
fclose(file);
|
||||
|
||||
// Return the answer and set the indication so we won't have to
|
||||
*_docker_id = docker_id;
|
||||
already_evaluated = 1;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Register the attachment instance with the attachment manager to associate it with a service.
|
||||
/// @param[in] request Points to an HTTP request, needed to get the number of workers.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
static ngx_int_t
|
||||
register_to_attachments_manager(ngx_http_request_t *request)
|
||||
{
|
||||
uint8_t path_length;
|
||||
int res = 0;
|
||||
uint8_t family_name_size = strlen(unique_id);
|
||||
uint8_t attachment_type = NGINX_ATT_ID;
|
||||
uint8_t worker_id = ngx_worker + 1;
|
||||
uint8_t workers_amount = get_num_of_workers(request);
|
||||
char *family_name = NULL;
|
||||
int cur_errno = 0; // temp fix for errno changing during print
|
||||
struct timeval timeout = get_timeout_val_sec(1);
|
||||
|
||||
if (get_docker_id(&family_name) == NGX_ERROR) {
|
||||
write_dbg(DBG_LEVEL_WARNING, "Could not evaluate family name");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
family_name_size = strlen(family_name);
|
||||
|
||||
// If there was an old socket, close it.
|
||||
if (registration_socket > 0) {
|
||||
close(registration_socket);
|
||||
registration_socket = -1;
|
||||
}
|
||||
|
||||
// Connect a new socket.
|
||||
registration_socket = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (registration_socket < 0) {
|
||||
write_dbg(DBG_LEVEL_WARNING, "Could not create socket");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
server.sun_family = AF_UNIX;
|
||||
strncpy(server.sun_path, SHARED_REGISTRATION_SIGNAL_PATH, sizeof(server.sun_path) - 1);
|
||||
|
||||
if (connect(registration_socket, (struct sockaddr *)&server, sizeof(struct sockaddr_un)) == -1) {
|
||||
cur_errno = errno;
|
||||
close(registration_socket);
|
||||
registration_socket = -1;
|
||||
write_dbg_if_needed(
|
||||
DBG_LEVEL_DEBUG,
|
||||
"Could not connect to nano service. Path: %s, Error: %s, Errno: %d",
|
||||
server.sun_path,
|
||||
strerror(errno),
|
||||
cur_errno
|
||||
);
|
||||
if (cur_errno == ENOENT) {
|
||||
strncpy(shared_verdict_signal_path, SHARED_VERDICT_SIGNAL_PATH, 128);
|
||||
return NGX_OK;
|
||||
}
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
// Send to the attachment manager the following details:
|
||||
// 1. The type of the attachment (fixed NGINX).
|
||||
// 2. The number of this worker.
|
||||
// 3. The total amount of workers.
|
||||
// 4. The size of the docker ID.
|
||||
// 5. If the docker ID isn't empty (size 0), the docker id itself.
|
||||
// If any of these fail - return an error.
|
||||
res = exchange_communication_data_with_service(
|
||||
registration_socket,
|
||||
&attachment_type,
|
||||
sizeof(attachment_type),
|
||||
WRITE_TO_SOCKET,
|
||||
&timeout
|
||||
);
|
||||
if (res <= 0) {
|
||||
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to send attachment type");
|
||||
close(registration_socket);
|
||||
registration_socket = -1;
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
res = exchange_communication_data_with_service(
|
||||
registration_socket,
|
||||
&worker_id,
|
||||
sizeof(worker_id),
|
||||
WRITE_TO_SOCKET,
|
||||
&timeout
|
||||
);
|
||||
if (res <= 0) {
|
||||
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to send worker ID");
|
||||
close(registration_socket);
|
||||
registration_socket = -1;
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
res = exchange_communication_data_with_service(
|
||||
registration_socket,
|
||||
&workers_amount,
|
||||
sizeof(workers_amount),
|
||||
WRITE_TO_SOCKET,
|
||||
&timeout
|
||||
);
|
||||
if (res <= 0) {
|
||||
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to send workers amount");
|
||||
close(registration_socket);
|
||||
registration_socket = -1;
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
res = exchange_communication_data_with_service(
|
||||
registration_socket,
|
||||
&family_name_size,
|
||||
sizeof(family_name_size),
|
||||
WRITE_TO_SOCKET,
|
||||
&timeout
|
||||
);
|
||||
if (res <= 0) {
|
||||
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to send family name size");
|
||||
close(registration_socket);
|
||||
registration_socket = -1;
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (family_name_size > 0) {
|
||||
res = exchange_communication_data_with_service(
|
||||
registration_socket,
|
||||
family_name,
|
||||
family_name_size,
|
||||
WRITE_TO_SOCKET,
|
||||
&timeout
|
||||
);
|
||||
if (res <= 0) {
|
||||
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to send family name");
|
||||
close(registration_socket);
|
||||
registration_socket = -1;
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// Read from the attachment manager:
|
||||
// 1. The length of signal path.
|
||||
// 2. The signal path itsel.
|
||||
// If that fails - return an error.
|
||||
timeout = get_timeout_val_sec(1);
|
||||
res = exchange_communication_data_with_service(
|
||||
registration_socket,
|
||||
&path_length,
|
||||
sizeof(path_length),
|
||||
READ_FROM_SOCKET,
|
||||
&timeout
|
||||
);
|
||||
|
||||
if (res <= 0) {
|
||||
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to read path length");
|
||||
close(registration_socket);
|
||||
registration_socket = -1;
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
res = exchange_communication_data_with_service(
|
||||
registration_socket,
|
||||
shared_verdict_signal_path,
|
||||
path_length,
|
||||
READ_FROM_SOCKET,
|
||||
&timeout
|
||||
);
|
||||
if (res <= 0) {
|
||||
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to read socket path");
|
||||
close(registration_socket);
|
||||
registration_socket = -1;
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
// Successfully go the shared communication path - add null termination and exit.
|
||||
shared_verdict_signal_path[path_length] = '\0';
|
||||
int32_t dbg_id = worker_id;
|
||||
int32_t dbg_size = workers_amount_to_send;
|
||||
write_dbg_if_needed(
|
||||
DBG_LEVEL_DEBUG,
|
||||
"Successfully registered on client. socket: %d, instance ID: %d, instances amount: %d, received path: %s",
|
||||
registration_socket,
|
||||
dbg_id,
|
||||
dbg_size,
|
||||
shared_verdict_signal_path
|
||||
);
|
||||
close(registration_socket);
|
||||
registration_socket = -1;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
const char *
|
||||
get_unique_id()
|
||||
{
|
||||
return unique_id;
|
||||
}
|
||||
|
||||
static ngx_int_t
|
||||
set_unique_id()
|
||||
{
|
||||
int is_container_env = 0;
|
||||
static const int max_container_id_len = 12;
|
||||
const char *container_id_file_path = "/proc/self/cgroup";
|
||||
|
||||
unsigned int unique_id_size = 0;
|
||||
if (strlen(unique_id) > 0) return NGX_OK;
|
||||
|
||||
FILE *file = fopen(container_id_file_path, "r");
|
||||
if (file == NULL) {
|
||||
write_dbg(DBG_LEVEL_WARNING, "Failed to open %s", container_id_file_path);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
char *line = NULL;
|
||||
char docker_id[max_container_id_len + 1];
|
||||
memset(docker_id, '\0', max_container_id_len + 1);
|
||||
size_t len = 0;
|
||||
while (getline(&line, &len, file) != -1) {
|
||||
char *docker_ptr = strstr(line, "docker/");
|
||||
if (docker_ptr == NULL) continue;
|
||||
|
||||
is_container_env = 1;
|
||||
docker_ptr += strlen("docker/");
|
||||
snprintf(docker_id, max_container_id_len + 1, "%s", docker_ptr);
|
||||
break;
|
||||
}
|
||||
free(line);
|
||||
fclose(file);
|
||||
long unsigned int ngx_worker_id = ngx_worker + 1;
|
||||
if (is_container_env) {
|
||||
unique_id_size += strlen(docker_id) + 1 + get_number_of_digits(ngx_worker_id) + 1;
|
||||
snprintf(unique_id, unique_id_size, "%s_%lu", docker_id, ngx_worker_id);
|
||||
} else {
|
||||
unique_id_size += get_number_of_digits(ngx_worker_id) + 1;
|
||||
snprintf(unique_id, unique_id_size, "%lu", ngx_worker_id);
|
||||
}
|
||||
|
||||
write_dbg(DBG_LEVEL_INFO, "Successfully set attachment's unique_id: '%s'", unique_id);
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ngx_int_t
|
||||
ngx_cp_attachment_init_process(ngx_http_request_t *request)
|
||||
{
|
||||
ngx_pool_t *memory_pool;
|
||||
nginx_user_id = getuid();
|
||||
nginx_group_id = getgid();
|
||||
static int need_registration = 1;
|
||||
num_of_connection_attempts++;
|
||||
|
||||
// Best-effort attempt to read the configuration before we start.
|
||||
init_general_config(SHARED_ATTACHMENT_CONF_PATH);
|
||||
|
||||
// Initalizing the various elements of the system (if needed):
|
||||
// 1. Get the unique identifier.
|
||||
// 2. Register with the attachment manager.
|
||||
// 3. Signal to the service to start communication.
|
||||
// 4. Make sure that the configuration is up-to-date.
|
||||
if (access(SHARED_REGISTRATION_SIGNAL_PATH, F_OK) != 0) {
|
||||
write_dbg(DBG_LEVEL_TRACE, "Attachment registration manager is turned off");
|
||||
return NGX_ABORT;
|
||||
}
|
||||
|
||||
if (strncmp(unique_id, "", 1) == 0) {
|
||||
write_dbg(DBG_LEVEL_DEBUG, "Setting attachment's unique id");
|
||||
if (set_unique_id() == NGX_ERROR) {
|
||||
write_dbg(DBG_LEVEL_INFO, "Failed to set attachment's unique_id");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (need_registration) {
|
||||
if (register_to_attachments_manager(request) == NGX_ERROR) {
|
||||
write_dbg_if_needed(DBG_LEVEL_INFO, "Failed to register to Attachments Manager service");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
need_registration = 0;
|
||||
}
|
||||
|
||||
if (comm_socket < 0) {
|
||||
write_dbg_if_needed(DBG_LEVEL_DEBUG, "Registering to nano service");
|
||||
if (init_signaling_socket() == NGX_ERROR) {
|
||||
write_dbg_if_needed(DBG_LEVEL_DEBUG, "Failed to register to the Nano Service");
|
||||
need_registration = 1;
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (init_general_config(SHARED_ATTACHMENT_CONF_PATH) == NGX_ERROR) {
|
||||
write_dbg_if_needed(DBG_LEVEL_INFO, "Failed to initialize attachment's configuration");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
// Initalize the the communication channel with the service.
|
||||
// If we encounter repeated failures - we will restart the whole communication.
|
||||
static const int max_ipc_init_retry_count = 10;
|
||||
static int max_retry_count = max_ipc_init_retry_count;
|
||||
if (nano_service_ipc == NULL) {
|
||||
write_dbg_if_needed(DBG_LEVEL_INFO, "Initializing IPC channel");
|
||||
nano_service_ipc = initIpc(
|
||||
unique_id,
|
||||
nginx_user_id,
|
||||
nginx_group_id,
|
||||
0,
|
||||
num_of_nginx_ipc_elements,
|
||||
write_dbg_impl
|
||||
);
|
||||
if (nano_service_ipc == NULL) {
|
||||
if (max_retry_count-- == 0) {
|
||||
restart_communication(request);
|
||||
max_retry_count = max_ipc_init_retry_count;
|
||||
}
|
||||
write_dbg_if_needed(DBG_LEVEL_INFO, "Failed to initialize IPC with nano service");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
max_retry_count = max_ipc_init_retry_count;
|
||||
|
||||
// Initialize internal resources.
|
||||
if (!is_static_resources_table_initialized()) {
|
||||
memory_pool = get_memory_pool();
|
||||
if (memory_pool == NULL) {
|
||||
write_dbg(DBG_LEVEL_WARNING, "Cannot initialize static resources. No memory pool has been allocated.");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
write_dbg(DBG_LEVEL_DEBUG, "Initializing static resources");
|
||||
if (init_static_resources(memory_pool) != NGX_OK) {
|
||||
write_dbg(DBG_LEVEL_WARNING, "Failed to initialize static resources");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_compression_debug_printing_initialized()) {
|
||||
write_dbg(DBG_LEVEL_DEBUG, "Initializing compression debug message printing");
|
||||
initialize_compression_debug_printing();
|
||||
}
|
||||
|
||||
// we want to indicate about successful registration only once in default level
|
||||
write_dbg(dbg_is_needed ? DBG_LEVEL_DEBUG : DBG_LEVEL_INFO, "NGINX attachment (UID='%s') successfully registered to nano service after %d attempts.", unique_id, num_of_connection_attempts);
|
||||
|
||||
dbg_is_needed = 1;
|
||||
num_of_connection_attempts = 0;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
int
|
||||
restart_communication(ngx_http_request_t *request)
|
||||
{
|
||||
write_dbg_if_needed(DBG_LEVEL_TRACE, "Restarting communication channels with nano service");
|
||||
if (nano_service_ipc != NULL) {
|
||||
destroyIpc(nano_service_ipc, 0);
|
||||
nano_service_ipc = NULL;
|
||||
}
|
||||
|
||||
if (init_signaling_socket() == NGX_ERROR) {
|
||||
if (register_to_attachments_manager(request) == NGX_ERROR) {
|
||||
write_dbg_if_needed(DBG_LEVEL_DEBUG, "Failed to register to Attachments Manager service");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (init_signaling_socket() == NGX_ERROR) return -1;
|
||||
}
|
||||
nano_service_ipc = initIpc(unique_id, nginx_user_id, nginx_group_id, 0, num_of_nginx_ipc_elements, write_dbg_impl);
|
||||
if (nano_service_ipc == NULL) return -2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
disconnect_communication()
|
||||
{
|
||||
if (comm_socket > 0) {
|
||||
close(comm_socket);
|
||||
comm_socket = -1;
|
||||
}
|
||||
if (nano_service_ipc != NULL) {
|
||||
destroyIpc(nano_service_ipc, 0);
|
||||
nano_service_ipc = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ngx_int_t
|
||||
handle_shmem_corruption()
|
||||
{
|
||||
if (nano_service_ipc == NULL) return NGX_OK;
|
||||
|
||||
if (isCorruptedShmem(nano_service_ipc, 0)) {
|
||||
write_dbg(DBG_LEVEL_WARNING, "Shared memory is corrupted! restarting communication");
|
||||
disconnect_communication();
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ngx_int_t
|
||||
isIpcReady()
|
||||
{
|
||||
return nano_service_ipc != NULL && comm_socket > 0;
|
||||
}
|
96
attachments/nginx/ngx_module/ngx_cp_initializer.h
Normal file
96
attachments/nginx/ngx_module/ngx_cp_initializer.h
Normal file
@@ -0,0 +1,96 @@
|
||||
// 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_initializer.h
|
||||
#ifndef __NGX_CP_INITIALIZER_H__
|
||||
#define __NGX_CP_INITIALIZER_H__
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
typedef enum ngx_cp_comm_direction {
|
||||
READ_FROM_SOCKET,
|
||||
WRITE_TO_SOCKET
|
||||
} ngx_cp_comm_direction_e; ///< Indicate whether communication exchange is to read or to write from a socket.
|
||||
|
||||
///
|
||||
/// @brief Initialize all the attachments resources and communication channels.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
/// - #NGX_ABORT
|
||||
///
|
||||
ngx_int_t ngx_cp_attachment_init_process();
|
||||
|
||||
///
|
||||
/// @brief Preforms send\receive information to\from the service via a socket.
|
||||
/// @param[in] socket The file descriptor of the socket to work with.
|
||||
/// @param[in, out] data An allocated memory which data will be read from (if sending) or written to (if recieving).
|
||||
/// @param[in] size The number of bytes in the allocated memoty.
|
||||
/// @param[in] direction Sets whether we read from the socket or write to it:
|
||||
/// - #READ_FROM_SOCKET
|
||||
/// - #WRITE_TO_SOCKET
|
||||
/// @param[in] remaining_timeout Points to the maximal time point the function is allowed to reach.
|
||||
/// @return int - positive if successful, other values indicate an error.
|
||||
///
|
||||
int exchange_communication_data_with_service(
|
||||
int socket,
|
||||
void *data,
|
||||
uint32_t size,
|
||||
ngx_cp_comm_direction_e direction,
|
||||
struct timeval *remaining_timeout);
|
||||
|
||||
///
|
||||
/// @brief Get an identifier that distinguish between different instances running on the same machine.
|
||||
/// @return A null-terminated string that is unique to this instance.
|
||||
///
|
||||
const char * get_unique_id();
|
||||
|
||||
///
|
||||
/// @brief Get an identifier for the current docker instance.
|
||||
/// @param[out] _docker_id Will point to the null terminated string (which shouldn't be freed) indentifier
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
ngx_int_t get_docker_id(char **_docker_id);
|
||||
|
||||
///
|
||||
/// @brief Closes any existing communication to the service and tries to open a new one.
|
||||
/// @param[in] request Points to an HTTP request, needed to get the number of workers.
|
||||
/// @returns int - 0 on success, negative number on failure.
|
||||
///
|
||||
int restart_communication(ngx_http_request_t *request);
|
||||
|
||||
///
|
||||
/// @brief Checks that the shared memory with the service isn't corrupted, disconnect if it is.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
ngx_int_t handle_shmem_corruption();
|
||||
|
||||
///
|
||||
/// @brief Closes all the communication channels with the service.
|
||||
///
|
||||
void disconnect_communication();
|
||||
|
||||
///
|
||||
/// @brief Checks if communication with the service is up and running.
|
||||
/// @returns ngx_int_t - 1 if communication is active, otherwise 0.
|
||||
///
|
||||
ngx_int_t isIpcReady();
|
||||
|
||||
#endif // __NGX_CP_INITIALIZER_H__
|
1097
attachments/nginx/ngx_module/ngx_cp_io.c
Normal file
1097
attachments/nginx/ngx_module/ngx_cp_io.c
Normal file
File diff suppressed because it is too large
Load Diff
201
attachments/nginx/ngx_module/ngx_cp_io.h
Normal file
201
attachments/nginx/ngx_module/ngx_cp_io.h
Normal file
@@ -0,0 +1,201 @@
|
||||
// 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_io.h
|
||||
#ifndef __NGX_CP_IO_H__
|
||||
#define __NGX_CP_IO_H__
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "shmem_ipc.h"
|
||||
#include "nginx_attachment_common.h"
|
||||
#include "ngx_cp_custom_response.h"
|
||||
#include "ngx_cp_hooks.h"
|
||||
|
||||
#define INSPECTION_IRRELEVANT NGX_DECLINED
|
||||
|
||||
extern SharedMemoryIPC *nano_service_ipc; ///< Nano service's IPC.
|
||||
extern int comm_socket; ///< Communication socket.
|
||||
|
||||
///
|
||||
/// @brief Receives nano service's response.
|
||||
/// @details The function awaits for the expected_replies of replies from the nano service.
|
||||
/// The recieved verdict is saved onto the verdict argument and depends on the reply
|
||||
/// one of the ngx_int_t returns.
|
||||
/// @param[in, out] expected_replies Amount of expected replies.
|
||||
/// @param[in, out] verdict Value to save the verdict onto:
|
||||
/// - #TRAFFIC_VERDICT_INSPECT
|
||||
/// - #TRAFFIC_VERDICT_INJECT
|
||||
/// - #TRAFFIC_VERDICT_DROP
|
||||
/// - #TRAFFIC_VERDICT_ACCEPT
|
||||
/// - #TRAFFIC_VERDICT_IRRELEVANT
|
||||
/// - #TRAFFIC_VERDICT_RECONF
|
||||
/// @param[in] cur_session_id Session's Id.
|
||||
/// @param[in, out] request NGINX request.
|
||||
/// @param[in] modification_list
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_HTTP_FORBIDDEN
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
ngx_int_t
|
||||
ngx_http_cp_reply_receiver(
|
||||
ngx_int_t *expected_replies,
|
||||
ngx_http_cp_verdict_e *verdict,
|
||||
uint32_t cur_session_id,
|
||||
ngx_http_request_t *request,
|
||||
ngx_http_cp_modification_list **modification_list
|
||||
);
|
||||
|
||||
///
|
||||
/// @brief Sends meta data to the nano service.
|
||||
/// @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.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
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
|
||||
);
|
||||
|
||||
///
|
||||
/// @brief Sends end of a transaction to the nano service.
|
||||
/// @param[in] end_transaction_type Sets the transaction type, can be of the values:
|
||||
/// - #REQUEST_END
|
||||
/// - #RESPONSE_END
|
||||
/// @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.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
ngx_int_t
|
||||
ngx_http_cp_end_transaction_sender(
|
||||
ngx_http_chunk_type_e end_transaction_type,
|
||||
uint32_t cur_request_id,
|
||||
ngx_uint_t *num_messages_sent
|
||||
);
|
||||
|
||||
///
|
||||
/// @brief Sends response code to the nano service.
|
||||
/// @param[in] response_code response code to send.
|
||||
/// @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.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
ngx_int_t ngx_http_cp_res_code_sender(
|
||||
uint16_t response_code,
|
||||
uint32_t cur_request_id,
|
||||
ngx_uint_t *num_messages_sent
|
||||
);
|
||||
|
||||
///
|
||||
/// @brief Sends content length to the nano service.
|
||||
/// @param[in] content_length_n content length to send.
|
||||
/// @param[in] cur_req_id Request session's Id.
|
||||
/// @param[in, out] num_messages_sent Number of messages sent will be saved onto this parameter.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
ngx_int_t
|
||||
ngx_http_cp_content_length_sender(
|
||||
uint64_t content_length_n,
|
||||
uint32_t cur_req_id,
|
||||
ngx_uint_t *num_messages_sent
|
||||
);
|
||||
|
||||
///
|
||||
/// @brief Sends request/response headers to the nano service.
|
||||
/// @param[in] headers Headers to be sent.
|
||||
/// @param[in, out] header_type Sets the header type, can be of the values:
|
||||
/// - #REQUEST_HEADER
|
||||
/// - #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.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
ngx_int_t
|
||||
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
|
||||
);
|
||||
|
||||
///
|
||||
/// @brief Sends request/response bodies to the nano service.
|
||||
/// @param[in] input NGX chain.
|
||||
/// @param[in] body_type Sets the body type, can be of the values:
|
||||
/// - #REQUEST_BODY
|
||||
/// - #RESPONSE_BODY
|
||||
/// @param[in, out] session_data Session's data.
|
||||
/// @param[in, out] is_last_part If the last part will be saved onto this parameter.
|
||||
/// @param[in, out] num_messages_sent Number of messages sent will be saved onto this parameter.
|
||||
/// @param[in, out] next_elem_to_inspect Next NGX chain to inspect.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
ngx_int_t
|
||||
ngx_http_cp_body_sender(
|
||||
ngx_chain_t *input,
|
||||
ngx_http_chunk_type_e body_type,
|
||||
ngx_http_cp_session_data *session_data,
|
||||
ngx_int_t *is_last_part,
|
||||
ngx_uint_t *num_messages_sent,
|
||||
ngx_chain_t **next_elem_to_inspect
|
||||
);
|
||||
|
||||
///
|
||||
/// @brief Checks if reconf is needed and reconfigs if necessary.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
ngx_int_t ngx_http_cp_is_reconf_needed();
|
||||
|
||||
///
|
||||
/// @brief Sends metric data to the server.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
ngx_int_t ngx_http_cp_metric_data_sender();
|
||||
|
||||
///
|
||||
/// @brief Updates session related metric fields.
|
||||
/// @param[in] session_start_time Session's start time.
|
||||
/// @param[in] req_proccesing_time Session's request processing time.
|
||||
/// @param[in] res_proccesing_time Session's response processing time.
|
||||
///
|
||||
void ngx_http_cp_report_time_metrics(
|
||||
clock_t session_start_time,
|
||||
double req_proccesing_time,
|
||||
double res_proccesing_time
|
||||
);
|
||||
|
||||
#endif // __NGX_CP_IO_H__
|
121
attachments/nginx/ngx_module/ngx_cp_metric.c
Normal file
121
attachments/nginx/ngx_module/ngx_cp_metric.c
Normal file
@@ -0,0 +1,121 @@
|
||||
// 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_metric.c
|
||||
#include <ngx_config.h>
|
||||
|
||||
#include "ngx_cp_metric.h"
|
||||
#include "ngx_cp_utils.h"
|
||||
|
||||
uint64_t metric_data[METRIC_TYPES_COUNT];
|
||||
uint64_t metric_average_data_divisor[METRIC_TYPES_COUNT];
|
||||
|
||||
void
|
||||
reset_metric_data()
|
||||
{
|
||||
int i;
|
||||
for (i = 0 ; i < METRIC_TYPES_COUNT ; i++) {
|
||||
metric_data[i] = 0;
|
||||
metric_average_data_divisor[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Increment the provided metric type by the value.
|
||||
/// @param[in] metric_type Metric type.
|
||||
/// @param[in] value Value to increment the metric type.
|
||||
///
|
||||
static void
|
||||
updateCounterMetricField(ngx_http_plugin_metric_type_e metric_type, uint64_t value)
|
||||
{
|
||||
metric_data[metric_type] += value;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Update the average metric field.
|
||||
/// @param[in] metric_type Metric type.
|
||||
/// @param[in] value Value to add to the average metric.
|
||||
///
|
||||
static void
|
||||
updateAverageMetricField(ngx_http_plugin_metric_type_e metric_type, uint64_t value)
|
||||
{
|
||||
metric_data[metric_type] =
|
||||
(((metric_data[metric_type] * metric_average_data_divisor[metric_type]) + value) / (metric_average_data_divisor[metric_type] + 1));
|
||||
metric_average_data_divisor[metric_type] += 1;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Sets the value as metric if it is higher than the current metric's value.
|
||||
/// @param[in] metric_type Metric type to set the value in.
|
||||
/// @param[in] value Value to set.
|
||||
///
|
||||
static void
|
||||
updateMaxMetricField(ngx_http_plugin_metric_type_e metric_type, uint64_t value)
|
||||
{
|
||||
if (metric_data[metric_type] < value) metric_data[metric_type] = value;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Sets the value as metric if it is lower than the current metric's value.
|
||||
/// @param[in] metric_type Metric type to set the value in.
|
||||
/// @param[in] value Value to set.
|
||||
///
|
||||
static void
|
||||
updateMinMetricField(ngx_http_plugin_metric_type_e metric_type, uint64_t value)
|
||||
{
|
||||
if (metric_data[metric_type] == 0) {
|
||||
metric_data[metric_type] = value;
|
||||
} else if (metric_data[metric_type] > value) {
|
||||
metric_data[metric_type] = value;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
updateMetricField(ngx_http_plugin_metric_type_e metric_type, uint64_t value)
|
||||
{
|
||||
switch (metric_type) {
|
||||
case CPU_USAGE:
|
||||
case AVERAGE_VM_MEMORY_USAGE:
|
||||
case AVERAGE_RSS_MEMORY_USAGE:
|
||||
case AVERAGE_REQ_BODY_SIZE_UPON_TIMEOUT:
|
||||
case AVERAGE_RES_BODY_SIZE_UPON_TIMEOUT:
|
||||
case AVERAGE_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT:
|
||||
case AVERAGE_REQ_PPROCESSING_TIME_UNTIL_VERDICT:
|
||||
case AVERAGE_RES_PPROCESSING_TIME_UNTIL_VERDICT: {
|
||||
if (value != 0) updateAverageMetricField(metric_type, value);
|
||||
break;
|
||||
}
|
||||
case MAX_VM_MEMORY_USAGE:
|
||||
case MAX_RSS_MEMORY_USAGE:
|
||||
case MAX_REQ_BODY_SIZE_UPON_TIMEOUT:
|
||||
case MAX_RES_BODY_SIZE_UPON_TIMEOUT:
|
||||
case MAX_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT:
|
||||
case MAX_REQ_PPROCESSING_TIME_UNTIL_VERDICT:
|
||||
case MAX_RES_PPROCESSING_TIME_UNTIL_VERDICT: {
|
||||
if (value != 0) updateMaxMetricField(metric_type, value);
|
||||
break;
|
||||
}
|
||||
case MIN_REQ_BODY_SIZE_UPON_TIMEOUT:
|
||||
case MIN_RES_BODY_SIZE_UPON_TIMEOUT:
|
||||
case MIN_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT:
|
||||
case MIN_REQ_PPROCESSING_TIME_UNTIL_VERDICT:
|
||||
case MIN_RES_PPROCESSING_TIME_UNTIL_VERDICT: {
|
||||
if (value != 0) updateMinMetricField(metric_type, value);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
updateCounterMetricField(metric_type, value);
|
||||
}
|
||||
}
|
||||
|
35
attachments/nginx/ngx_module/ngx_cp_metric.h
Normal file
35
attachments/nginx/ngx_module/ngx_cp_metric.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// 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_metric.h
|
||||
#ifndef __NGX_CP_METRIC_H__
|
||||
#define __NGX_CP_METRIC_H__
|
||||
|
||||
#include <nginx_attachment_common.h>
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
|
||||
///
|
||||
/// @brief Depending on the metric type, set the provided value in the metric.
|
||||
/// @param[in] metric_type Metric type to update.
|
||||
/// @param[in] value Value to set.
|
||||
///
|
||||
void updateMetricField(ngx_http_plugin_metric_type_e metric_type, uint64_t value);
|
||||
|
||||
///
|
||||
/// @brief Goes over all the metrics and resets them to 0.
|
||||
///
|
||||
void reset_metric_data();
|
||||
|
||||
#endif // __NGX_CP_METRIC_H__
|
546
attachments/nginx/ngx_module/ngx_cp_static_content.c
Normal file
546
attachments/nginx/ngx_module/ngx_cp_static_content.c
Normal file
@@ -0,0 +1,546 @@
|
||||
// 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.
|
||||
|
||||
#include "ngx_cp_static_content.h"
|
||||
|
||||
#include <ngx_hash.h>
|
||||
|
||||
#include "ngx_cp_utils.h"
|
||||
#include "ngx_cp_custom_response.h"
|
||||
|
||||
ngx_hash_t static_resources_hash_table; ///< Holds all the static resources data.
|
||||
ngx_int_t is_static_resources_table_initialized_var = 0; ///< A flag if static resources hash table was initialized.
|
||||
|
||||
static const ngx_int_t not_a_static_resource = NOT_A_STATIC_RESOURCE; ///< Not a static resource variable.
|
||||
static const unsigned int static_resource_res_headers_allow_ranges = 1; ///< Static resource result allow ranges.
|
||||
static const unsigned int static_resource_res_headers_keepalive = 1; ///< Static resource result headers keep alive configuration.
|
||||
static const ngx_int_t static_resource_res_headers_response_code = NGX_HTTP_OK; ///< Static resource good result response.
|
||||
static const ngx_int_t default_max_hash_table_size = 512; ///< Maximum hash table size configuration.
|
||||
static const ngx_int_t default_hash_table_bucket_size = 64;
|
||||
static const ngx_int_t default_part_items_num = 100; ///< Default number of elements to be allocated for a static resources list during static resources initialization.
|
||||
static const ngx_int_t initial_hash_table_data_value = 1; ///< Initial value of initiated table data.
|
||||
|
||||
static ngx_hash_init_t static_resources_hash_initializer; ///< NGINX Hash initialization settings.
|
||||
|
||||
///
|
||||
/// @brief Get the static resources into the static_resources and return the number of resources.
|
||||
/// @details Given a memory poll and static sources list. Attempts to open the static resource directory.
|
||||
/// for each static data in the directory, allocate a new element in the static_resources list and load the
|
||||
/// data into the new element.
|
||||
/// @param[in, out] static_resources NGINX list of the static allocated resources.
|
||||
/// @param[in, out] memory_pool NGINX pool used to allocate data into.
|
||||
/// @return ngx_int_t - The number of allocated static resources.
|
||||
///
|
||||
static ngx_uint_t
|
||||
get_static_resources(ngx_list_t *static_resources, ngx_pool_t *memory_pool)
|
||||
{
|
||||
size_t num_static_resources = 0;
|
||||
struct dirent *current_entry = NULL;
|
||||
ngx_str_t *current_resource = NULL;
|
||||
const char *static_resources_path = get_static_resources_path();
|
||||
|
||||
DIR *static_resources_directory = opendir(static_resources_path);
|
||||
if (static_resources_directory == NULL) {
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Failed to open the static resources directory. Path: %s, error: %s",
|
||||
static_resources_path,
|
||||
strerror(errno)
|
||||
);
|
||||
return -1;
|
||||
}
|
||||
|
||||
write_dbg(
|
||||
DBG_LEVEL_TRACE,
|
||||
"Successfully opened the static resources' directory: %s",
|
||||
static_resources_path
|
||||
);
|
||||
while ((current_entry = readdir (static_resources_directory)) != NULL) {
|
||||
// Iterates over the files in the static resources directory.
|
||||
// Allocates a new element to the list and initialize it given the resources data.
|
||||
if (strcmp(current_entry->d_name, ".") == 0 || strcmp(current_entry->d_name, "..") == 0) continue;
|
||||
|
||||
current_resource = ngx_list_push(static_resources);
|
||||
if (current_resource == NULL) {
|
||||
write_dbg(
|
||||
DBG_LEVEL_ERROR,
|
||||
"Failed to allocate memory for a static resource path. Path: %s",
|
||||
static_resources_path
|
||||
);
|
||||
return -1;
|
||||
}
|
||||
write_dbg(DBG_LEVEL_TRACE, "Found static resource: %s", current_entry->d_name);
|
||||
|
||||
// Load the read data from the file onto the current resource element.
|
||||
current_resource->len = strlen(current_entry->d_name);
|
||||
current_resource->data = ngx_palloc(memory_pool, current_resource->len + 1);
|
||||
ngx_memcpy(current_resource->data, current_entry->d_name, current_resource->len);
|
||||
current_resource->data[current_resource->len] = '\0';
|
||||
num_static_resources++;
|
||||
}
|
||||
closedir(static_resources_directory);
|
||||
|
||||
return num_static_resources;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Initiates static resources hash table with the provided data in static_resources.
|
||||
/// @details Takes the provided variables and initiates the static hash table that
|
||||
/// is used throughout the attachment.
|
||||
/// @param[in, out] static_resources NGINX list - data to be initiated
|
||||
/// @param[in] num_static_resources The number of provided resources in static_resources.
|
||||
/// @param[in, out] memory_pool NGINX pool used to allocate data into.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
static ngx_int_t
|
||||
init_static_web_resources_hash_table(
|
||||
ngx_list_t *static_resources,
|
||||
const size_t num_static_resources,
|
||||
ngx_pool_t *memory_pool
|
||||
)
|
||||
{
|
||||
ngx_int_t init_hash_table_result;
|
||||
|
||||
write_dbg(
|
||||
DBG_LEVEL_TRACE,
|
||||
"Received %u static web resources. Initializing the static resources table.",
|
||||
num_static_resources
|
||||
);
|
||||
|
||||
// initiate the static hash table with the provided data in static_resources.
|
||||
init_hash_table_result = init_hash_table(
|
||||
memory_pool,
|
||||
&static_resources_hash_initializer,
|
||||
&static_resources_hash_table,
|
||||
"static resources",
|
||||
default_max_hash_table_size,
|
||||
default_hash_table_bucket_size,
|
||||
static_resources,
|
||||
&initial_hash_table_data_value,
|
||||
sizeof(initial_hash_table_data_value)
|
||||
);
|
||||
if (init_hash_table_result != NGX_OK) {
|
||||
write_dbg(DBG_LEVEL_WARNING, "Failed to initialize static resources table");
|
||||
free_list_from_pool(memory_pool, static_resources);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
is_static_resources_table_initialized_var = 1;
|
||||
write_dbg(DBG_LEVEL_TRACE, "Successfully initialized the static resources table");
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ngx_int_t
|
||||
init_static_resources(ngx_pool_t *memory_pool)
|
||||
{
|
||||
size_t num_static_resources;
|
||||
ngx_list_t *static_resources = ngx_list_create(memory_pool, default_part_items_num, sizeof(ngx_str_t));
|
||||
if (static_resources == NULL) {
|
||||
write_dbg(DBG_LEVEL_WARNING, "Failed to allocate the list of static resource paths");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
// Read the static data saved into static_resources.
|
||||
num_static_resources = get_static_resources(static_resources, memory_pool);
|
||||
if (num_static_resources == (size_t)-1) {
|
||||
free_list_from_pool(memory_pool, static_resources);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
if (num_static_resources == 0) {
|
||||
free_list_from_pool(memory_pool, static_resources);
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
// Initiate the static_resources_hash_table with static_resources data.
|
||||
if (init_static_web_resources_hash_table(static_resources, num_static_resources, memory_pool) != NGX_OK) {
|
||||
free_list_from_pool(memory_pool, static_resources);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
free_list_from_pool(memory_pool, static_resources);
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ngx_int_t
|
||||
is_static_resources_table_initialized(void)
|
||||
{
|
||||
return is_static_resources_table_initialized_var;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief
|
||||
/// @param[in] null_terminated_uri Null terminated uri that holds the resource name.
|
||||
/// @param[in, out] static_resource_name A variable to save data and len of the extracted resource name.
|
||||
/// @returns ngx_int_t
|
||||
/// - #1 Successed in getting a static resource name.
|
||||
/// - #0 Failed to get static resource name.
|
||||
///
|
||||
ngx_int_t
|
||||
get_static_resource_name(const ngx_str_t *null_terminated_uri, ngx_str_t *static_resource_name)
|
||||
{
|
||||
size_t uri_prefix_length;
|
||||
u_char *last_uri_separator;
|
||||
|
||||
// Skip past last "/"
|
||||
last_uri_separator = reverse_strnchr(null_terminated_uri->data, '/', null_terminated_uri->len);
|
||||
if (last_uri_separator == NULL) {
|
||||
write_dbg(DBG_LEVEL_WARNING, "Invalid URI in HTTP request, URI: %s", null_terminated_uri->data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uri_prefix_length = last_uri_separator - null_terminated_uri->data + 1;
|
||||
static_resource_name->data = last_uri_separator + 1;
|
||||
static_resource_name->len = null_terminated_uri->len - uri_prefix_length;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Validates that the provided static resource name is a valid.
|
||||
/// @details The function checks if the static resources table has been properly intiliazed.
|
||||
/// If it has, it initates a hash key and searchs for it in the static resources hash table.
|
||||
/// If it finds it, returns 1, in any other case returns 0.
|
||||
/// @param[in, out] static_resource_name NGINX string - resource name to be checked to be valid.
|
||||
/// @returns ngx_int_t
|
||||
/// - #1 Successed in getting a static resource.
|
||||
/// - #0 Failed to get static resource.
|
||||
///
|
||||
static ngx_int_t
|
||||
is_static_resource_request(ngx_str_t *static_resource_name)
|
||||
{
|
||||
char *data;
|
||||
ngx_uint_t key;
|
||||
|
||||
if (!is_static_resources_table_initialized()) {
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Cannot determine whether request is for a static resource: static resources' table is not initialized"
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
write_dbg(
|
||||
DBG_LEVEL_TRACE,
|
||||
"Checking whether requested resource %s (name length: %u) is a static resource",
|
||||
static_resource_name->data, static_resource_name->len
|
||||
);
|
||||
|
||||
key = ngx_hash_key(static_resource_name->data, static_resource_name->len);
|
||||
data = ngx_hash_find(&static_resources_hash_table, key, static_resource_name->data, static_resource_name->len);
|
||||
if (data == NULL) {
|
||||
write_dbg(DBG_LEVEL_TRACE, "Requested resource %s is not a static resource", static_resource_name->data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
set_location_conf_root(
|
||||
ngx_http_core_loc_conf_t *location_conf,
|
||||
const ngx_str_t *root_name,
|
||||
ngx_array_t *root_lengths,
|
||||
ngx_array_t *root_values
|
||||
)
|
||||
{
|
||||
location_conf->root.len = root_name->len;
|
||||
location_conf->root.data = root_name->data;
|
||||
location_conf->root_lengths = root_lengths;
|
||||
location_conf->root_values = root_values;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Sends a static content response header.
|
||||
/// @param[in, out] request
|
||||
/// @param[in] static_resource_size
|
||||
/// @param[in] static_resource_last_modified_time
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
static ngx_int_t
|
||||
send_static_content_response_headers(
|
||||
ngx_http_request_t *request,
|
||||
const off_t static_resource_size,
|
||||
const time_t static_resource_last_modified_time
|
||||
)
|
||||
{
|
||||
ngx_int_t send_headers_res = ngx_http_cp_response_headers_sender(
|
||||
request,
|
||||
static_resource_res_headers_response_code,
|
||||
static_resource_size,
|
||||
static_resource_last_modified_time,
|
||||
static_resource_res_headers_allow_ranges,
|
||||
static_resource_res_headers_keepalive
|
||||
);
|
||||
if (send_headers_res != NGX_OK) {
|
||||
write_dbg(DBG_LEVEL_WARNING, "Failed to send headers for static content response");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Open a cached file at the location configuration.
|
||||
/// @param[in] location_conf Location configuration regarding the file to be handled.
|
||||
/// @param[in, out] open_files_cache A cache of opened files.
|
||||
/// @param[in, out] file_path File path to the file to be read.
|
||||
/// @param[in, out] open_file_info Information regarding the file to be read.
|
||||
/// @param[in, out] memory_pool NGINX pool.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
static ngx_int_t
|
||||
open_cached_file(
|
||||
ngx_http_core_loc_conf_t *location_conf,
|
||||
ngx_open_file_cache_t *open_files_cache,
|
||||
ngx_str_t *file_path,
|
||||
ngx_open_file_info_t *open_file_info,
|
||||
ngx_pool_t *memory_pool
|
||||
)
|
||||
{
|
||||
open_file_info->read_ahead = location_conf->read_ahead;
|
||||
open_file_info->directio = location_conf->directio;
|
||||
open_file_info->valid = location_conf->open_file_cache_valid;
|
||||
open_file_info->min_uses = location_conf->open_file_cache_min_uses;
|
||||
open_file_info->errors = location_conf->open_file_cache_errors;
|
||||
open_file_info->events = location_conf->open_file_cache_events;
|
||||
|
||||
return ngx_open_cached_file(open_files_cache, file_path, open_file_info, memory_pool);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Sends the static resource.
|
||||
/// @param[in, out] request NGINX request static resource request to be sent.
|
||||
/// @param[in, out] static_resource_name A path to the static resource.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
static ngx_int_t
|
||||
send_static_resource(ngx_http_request_t *request, ngx_str_t *static_resource_name)
|
||||
{
|
||||
const char *static_resources_root_path = get_static_resources_path();
|
||||
|
||||
ngx_int_t open_file_res;
|
||||
ngx_int_t send_headers_res;
|
||||
ngx_open_file_info_t open_file_info;
|
||||
ngx_str_t static_resource_path;
|
||||
ngx_http_core_loc_conf_t *core_module_loc_conf = ngx_http_get_module_loc_conf(request, ngx_http_core_module);
|
||||
|
||||
ngx_str_t old_root = core_module_loc_conf->root;
|
||||
ngx_array_t *old_root_lengths = core_module_loc_conf->root_lengths;
|
||||
ngx_array_t *old_root_values = core_module_loc_conf->root_values;
|
||||
ngx_str_t old_uri = request->uri;
|
||||
|
||||
ngx_str_t new_root = { strlen(static_resources_root_path), (u_char *)static_resources_root_path };
|
||||
set_location_conf_root(core_module_loc_conf, &new_root, NULL, NULL);
|
||||
request->uri = *static_resource_name;
|
||||
|
||||
// Map static_resource_path URI to file path.
|
||||
if (ngx_http_map_uri_to_path(request, &static_resource_path, &new_root.len, 0) == NULL) {
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Failed to map static resource's URI to file path. URI: %.*s",
|
||||
request->uri.len,
|
||||
request->uri.data
|
||||
);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
static_resource_path.len = new_root.len + static_resource_name->len;
|
||||
|
||||
ngx_memzero(&open_file_info, sizeof(ngx_open_file_info_t));
|
||||
|
||||
// Open static resource's file.
|
||||
open_file_res = open_cached_file(
|
||||
core_module_loc_conf,
|
||||
core_module_loc_conf->open_file_cache,
|
||||
&static_resource_path,
|
||||
&open_file_info,
|
||||
request->pool
|
||||
);
|
||||
if (open_file_res != NGX_OK) {
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Failed to open static resource's file. File path: %.*s",
|
||||
static_resource_path.len,
|
||||
static_resource_path.data
|
||||
);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
// Send static content response headers.
|
||||
send_headers_res = send_static_content_response_headers(request, open_file_info.size, open_file_info.mtime);
|
||||
if (send_headers_res != NGX_OK) return send_headers_res;
|
||||
if (request != request->main && open_file_info.size == 0) {
|
||||
write_dbg(DBG_LEVEL_DEBUG, "Tried to send empty file, sent only headers");
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
set_location_conf_root(core_module_loc_conf, &old_root, old_root_lengths, old_root_values);
|
||||
request->uri = old_uri;
|
||||
|
||||
return ngx_http_cp_file_response_sender(
|
||||
request,
|
||||
&static_resource_path,
|
||||
&open_file_info,
|
||||
request == request->main,
|
||||
request->connection->log,
|
||||
request->pool
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief
|
||||
/// @param[in, out] request NGINX request.
|
||||
/// @param[in, out] null_terminated_uri Null terminated Uri.
|
||||
/// @param[in, out] static_resource_name Static resource name.
|
||||
/// @param[in] handle_static_resource_result Results in handling the static resource request.
|
||||
///
|
||||
void
|
||||
finalize_static_resource_response(
|
||||
ngx_http_request_t *request,
|
||||
ngx_str_t *null_terminated_uri,
|
||||
ngx_str_t *static_resource_name,
|
||||
const ngx_int_t handle_static_resource_result
|
||||
)
|
||||
{
|
||||
ngx_int_t finalize_request_response_code;
|
||||
|
||||
// Frees null_terminated_uri data.
|
||||
if (null_terminated_uri->data != NULL) {
|
||||
ngx_pfree(request->pool, null_terminated_uri->data);
|
||||
null_terminated_uri->data = NULL;
|
||||
}
|
||||
|
||||
if (handle_static_resource_result == not_a_static_resource) {
|
||||
write_dbg(
|
||||
DBG_LEVEL_DEBUG,
|
||||
"Request is not for a static resource. Request's URI: %.*s",
|
||||
request->uri.len,
|
||||
request->uri.data
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Debug printing of request result response.
|
||||
switch (handle_static_resource_result) {
|
||||
case NGX_OK:
|
||||
case NGX_DONE: {
|
||||
write_dbg(
|
||||
DBG_LEVEL_DEBUG,
|
||||
"Successfully sent requested static resource: %.*s",
|
||||
static_resource_name->len,
|
||||
static_resource_name->data
|
||||
);
|
||||
break;
|
||||
}
|
||||
case NGX_AGAIN: {
|
||||
write_dbg(
|
||||
DBG_LEVEL_DEBUG,
|
||||
"Failed to finish sending requested static resource, retrying. Static resource: %.*s",
|
||||
static_resource_name->len,
|
||||
static_resource_name->data
|
||||
);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING, "Failed to send requested static resource: %.*s",
|
||||
static_resource_name->len,
|
||||
static_resource_name->data
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize response of the static response request.
|
||||
if (
|
||||
handle_static_resource_result == NGX_OK ||
|
||||
handle_static_resource_result == NGX_DONE ||
|
||||
handle_static_resource_result == NGX_AGAIN
|
||||
) {
|
||||
finalize_request_response_code = handle_static_resource_result;
|
||||
} else {
|
||||
finalize_request_response_code = NGX_HTTP_FORBIDDEN;
|
||||
}
|
||||
ngx_http_finalize_request(request, finalize_request_response_code);
|
||||
}
|
||||
|
||||
ngx_int_t
|
||||
handle_static_resource_request(uint32_t session_id, ngx_http_cp_verdict_e *verdict, ngx_http_request_t *request)
|
||||
{
|
||||
ngx_str_t null_terminated_uri;
|
||||
ngx_str_t static_resource_name;
|
||||
ngx_int_t send_static_resource_res;
|
||||
|
||||
write_dbg(
|
||||
DBG_LEVEL_TRACE,
|
||||
"Trying to serve requested resource as static content. URI: %.*s",
|
||||
request->uri.len,
|
||||
request->uri.data
|
||||
);
|
||||
|
||||
if (duplicate_ngx_string(&null_terminated_uri, &request->uri, request->pool) != NGX_OK) {
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Failed to create a null terminated duplicate of URI. URI: %.*s",
|
||||
request->uri.len,
|
||||
request->uri.data
|
||||
);
|
||||
finalize_static_resource_response(request, &null_terminated_uri, &static_resource_name, NGX_ERROR);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
// Get static resource name in static_resource_name.
|
||||
if (!get_static_resource_name(&null_terminated_uri, &static_resource_name)) {
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Failed to create a null terminated duplicate of URI. URI: %.*s",
|
||||
request->uri.len,
|
||||
request->uri.data
|
||||
);
|
||||
finalize_static_resource_response(request, &null_terminated_uri, &static_resource_name, NGX_ERROR);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
// Validates that static_resource_name is a valid request.
|
||||
if (!is_static_resource_request(&static_resource_name)) {
|
||||
finalize_static_resource_response(
|
||||
request,
|
||||
&null_terminated_uri,
|
||||
&static_resource_name,
|
||||
not_a_static_resource
|
||||
);
|
||||
return not_a_static_resource;
|
||||
}
|
||||
|
||||
*verdict = TRAFFIC_VERDICT_IRRELEVANT;
|
||||
write_dbg(
|
||||
DBG_LEVEL_DEBUG,
|
||||
"Request is for a static resource, inspection is not needed (session ID = %d)",
|
||||
session_id
|
||||
);
|
||||
|
||||
// Sends the static request.
|
||||
send_static_resource_res = send_static_resource(request, &static_resource_name);
|
||||
finalize_static_resource_response(request, &null_terminated_uri, &static_resource_name, send_static_resource_res);
|
||||
|
||||
return NGX_DONE;
|
||||
}
|
62
attachments/nginx/ngx_module/ngx_cp_static_content.h
Normal file
62
attachments/nginx/ngx_module/ngx_cp_static_content.h
Normal file
@@ -0,0 +1,62 @@
|
||||
// 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_static_content.h
|
||||
#ifndef __NGX_CP_STATIC_CONTENT_H__
|
||||
#define __NGX_CP_STATIC_CONTENT_H__
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
#include "nginx_attachment_common.h"
|
||||
|
||||
#define NOT_A_STATIC_RESOURCE NGX_DECLINED
|
||||
|
||||
///
|
||||
/// @brief Initiates the static resources hash table.
|
||||
/// @details Read the data from the static resources directory, load it into static_resources_hash_table.
|
||||
/// @param[in, out] memory_pool NGINX pool used to allocate data into.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
ngx_int_t init_static_resources(ngx_pool_t *memory_pool);
|
||||
|
||||
///
|
||||
/// @brief Returns if static resources hash has been initialized.
|
||||
/// @returns ngx_int_t
|
||||
/// - #1 Is intialized.
|
||||
/// - #0 Is not intialized.
|
||||
///
|
||||
ngx_int_t is_static_resources_table_initialized(void);
|
||||
|
||||
///
|
||||
/// @brief Handles a resource request.
|
||||
/// @details Recieves a static resource name, get the data out of the static resource hash table and sends it.
|
||||
/// @param[in] session_id Session ID, used for debug message.
|
||||
/// @param[in, out] verdict Verdict to be returned back to the callee.
|
||||
/// - #TRAFFIC_VERDICT_IRRELEVANT If the function returns a static resource.
|
||||
/// @param[in, out] request NGINX request.
|
||||
/// @return ngx_int_t
|
||||
/// - #NOT_A_STATIC_RESOURCE
|
||||
/// - #NGX_DONE
|
||||
///
|
||||
ngx_int_t handle_static_resource_request(
|
||||
uint32_t session_id,
|
||||
ngx_http_cp_verdict_e *verdict,
|
||||
ngx_http_request_t *request
|
||||
);
|
||||
|
||||
#endif // __NGX_CP_STATIC_CONTENT_H__
|
133
attachments/nginx/ngx_module/ngx_cp_thread.c
Normal file
133
attachments/nginx/ngx_module/ngx_cp_thread.c
Normal file
@@ -0,0 +1,133 @@
|
||||
// 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_thread.c
|
||||
#include "ngx_cp_thread.h"
|
||||
#include "ngx_cp_utils.h"
|
||||
#include "ngx_cp_hook_threads.h"
|
||||
#include "ngx_cp_failing_state.h"
|
||||
#include "ngx_cp_hooks.h"
|
||||
#include "ngx_cp_metric.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int success_count = 0;
|
||||
///
|
||||
/// @brief runs the provided routine with the arguments in a non thread and without a timeout.
|
||||
/// @param[in, out] thread_func A pointer to the provided routine to run in a thread.
|
||||
/// @param[in, out] arg Routine's arguments.
|
||||
/// @param[in, out] func_name Called thread timeout.
|
||||
/// @returns 1
|
||||
///
|
||||
int
|
||||
ngx_cp_run_without_thread_timeout(CpThreadRoutine thread_func, void *arg, char *func_name)
|
||||
{
|
||||
write_dbg(DBG_LEVEL_TRACE, "Executing cb in blocking mode, fn=%s", func_name);
|
||||
|
||||
thread_func(arg);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
ngx_cp_run_in_thread_timeout(CpThreadRoutine thread_func, void *arg, int timeout_msecs, char *func_name)
|
||||
{
|
||||
int status = 0;
|
||||
int ret = 0;
|
||||
void *res = NULL;
|
||||
pthread_t thread;
|
||||
struct timespec ts;
|
||||
struct ngx_http_cp_event_thread_ctx_t *ctx = (struct ngx_http_cp_event_thread_ctx_t*)arg;
|
||||
|
||||
if (inspection_mode == NO_THREAD) return ngx_cp_run_without_thread_timeout(thread_func, arg, func_name);
|
||||
|
||||
/// Runs the routine in a dedicated thread.
|
||||
write_dbg(DBG_LEVEL_TRACE, "Executing cb in dedicated thread, fn=%s", func_name);
|
||||
if (pthread_create(&thread, NULL, thread_func, arg) != 0) {
|
||||
updateMetricField(THREAD_FAILURE, 1);
|
||||
write_dbg(DBG_LEVEL_TRACE, "pthread_create failed with errno=%d, fn=%s", errno, func_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (inspection_mode == BLOCKING_THREAD) {
|
||||
// Runs the function in a blocking thread.
|
||||
status = pthread_join(thread, &res);
|
||||
write_dbg(DBG_LEVEL_TRACE, "pthread_join returned from blocking call. status=%d, fn=%s", status, func_name);
|
||||
return status == 0;
|
||||
}
|
||||
|
||||
if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
|
||||
updateMetricField(THREAD_FAILURE, 1);
|
||||
write_dbg(
|
||||
DBG_LEVEL_ERROR,
|
||||
"clock_gettime(CLOCK_REALTIME) failed. Status: %s",
|
||||
strerror(errno)
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Convert milliseconds to timespec
|
||||
long long tv_nsec = ts.tv_nsec + (timeout_msecs % 1000) * 1000000;
|
||||
|
||||
ts.tv_sec += timeout_msecs / 1000 + tv_nsec / 1000000000;
|
||||
ts.tv_nsec = tv_nsec % 1000000000;
|
||||
|
||||
status = pthread_timedjoin_np(thread, NULL, &ts);
|
||||
|
||||
if (status != 0) {
|
||||
/// Handling failed thread.
|
||||
handle_inspection_failure(inspection_failure_weight, fail_mode_verdict, ctx->session_data_p);
|
||||
write_dbg(
|
||||
status == ETIMEDOUT ? DBG_LEVEL_DEBUG : DBG_LEVEL_WARNING,
|
||||
"pthread_timejoin_np returns with %d (%s), successes so far %d, fn=%s",
|
||||
status,
|
||||
strerror(status),
|
||||
success_count,
|
||||
func_name
|
||||
);
|
||||
|
||||
ret = pthread_cancel(thread);
|
||||
write_dbg(DBG_LEVEL_DEBUG, "pthread_cancel returns with ret=%d, fn=%s", ret, func_name);
|
||||
|
||||
ret = pthread_join(thread, &res);
|
||||
if (ret != 0) {
|
||||
updateMetricField(THREAD_FAILURE, 1);
|
||||
write_dbg(DBG_LEVEL_WARNING, "pthread_join failed while fail open is enabled. RET=%d, fn=%s", ret, func_name);
|
||||
return ret != 0;
|
||||
}
|
||||
|
||||
if (res == PTHREAD_CANCELED) {
|
||||
updateMetricField(THREAD_TIMEOUT, 1);
|
||||
write_dbg(DBG_LEVEL_DEBUG, "thread was canceled, fn=%s", func_name);
|
||||
} else {
|
||||
updateMetricField(THREAD_FAILURE, 1);
|
||||
}
|
||||
|
||||
write_dbg(DBG_LEVEL_DEBUG, "pthread_join returns with ret=%d", ret);
|
||||
}
|
||||
else {
|
||||
write_dbg(
|
||||
DBG_LEVEL_TRACE,
|
||||
"Successfully executed thread. successes so far=%d, fn=%s",
|
||||
success_count,
|
||||
func_name
|
||||
);
|
||||
success_count++;
|
||||
}
|
||||
|
||||
return status == 0;
|
||||
}
|
38
attachments/nginx/ngx_module/ngx_cp_thread.h
Normal file
38
attachments/nginx/ngx_module/ngx_cp_thread.h
Normal file
@@ -0,0 +1,38 @@
|
||||
// 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_thread.h
|
||||
#ifndef __NGX_CP_THREAD_H__
|
||||
#define __NGX_CP_THREAD_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef void *(*CpThreadRoutine)(void *); ///< Func
|
||||
|
||||
///
|
||||
/// @brief Runs the provided routine with the provided arguments as a thread
|
||||
/// @details Runs the provided routine as thread_func(args) in a thread. Depending on the inspection
|
||||
/// mode runs it with a timeout.
|
||||
/// This provided routine updates metrics if needed such as THREAD_TIMEOUT and THREAD_FAILURE.
|
||||
/// @param[in, out] thread_func A pointer to the provided routine to run in a thread.
|
||||
/// @param[in, out] arg Routine's arguments.
|
||||
/// @param[in, out] timeout_msecs Called thread timeout.
|
||||
/// @param[in, out] func_name The name of the provided routine.
|
||||
/// @return int
|
||||
/// - #0 Thread success.
|
||||
/// - #1 Thread fail.
|
||||
///
|
||||
int ngx_cp_run_in_thread_timeout(CpThreadRoutine thread_func, void *arg, int timeout_msecs, char *);
|
||||
|
||||
#endif // __NGX_CP_THREAD_H__
|
1086
attachments/nginx/ngx_module/ngx_cp_utils.c
Normal file
1086
attachments/nginx/ngx_module/ngx_cp_utils.c
Normal file
File diff suppressed because it is too large
Load Diff
429
attachments/nginx/ngx_module/ngx_cp_utils.h
Normal file
429
attachments/nginx/ngx_module/ngx_cp_utils.h
Normal file
@@ -0,0 +1,429 @@
|
||||
// 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_utils.h
|
||||
#ifndef __NGX_CP_UTILS_H__
|
||||
#define __NGX_CP_UTILS_H__
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "nginx_attachment_common.h"
|
||||
|
||||
#ifndef __FILENAME__
|
||||
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
|
||||
#endif
|
||||
|
||||
#define write_dbg(_dbg_level, fmt, ...) \
|
||||
{ \
|
||||
write_dbg_impl(_dbg_level, __func__, __FILENAME__, __LINE__, fmt, ##__VA_ARGS__); \
|
||||
if ((_dbg_level) == DBG_LEVEL_ASSERT) assert(0); \
|
||||
}
|
||||
|
||||
#define write_dbg_if_needed(_dbg_level, fmt, ...) \
|
||||
{ \
|
||||
if ((dbg_is_needed) == 0) { \
|
||||
write_dbg_impl(DBG_LEVEL_TRACE, __func__, __FILENAME__, __LINE__, fmt, ##__VA_ARGS__); \
|
||||
} else { \
|
||||
write_dbg_impl(_dbg_level, __func__, __FILENAME__, __LINE__, fmt, ##__VA_ARGS__); \
|
||||
} \
|
||||
if ((_dbg_level) == DBG_LEVEL_ASSERT) assert(0); \
|
||||
}
|
||||
|
||||
extern ngx_int_t fail_mode_verdict;
|
||||
extern ngx_int_t dbg_is_needed;
|
||||
extern ngx_int_t num_of_connection_attempts;
|
||||
extern ngx_uint_t content_length_would_change;
|
||||
extern ngx_uint_t fail_open_timeout;
|
||||
extern ngx_uint_t req_max_proccessing_ms_time;
|
||||
extern ngx_uint_t res_max_proccessing_ms_time;
|
||||
extern ngx_uint_t registration_thread_timeout_msec;
|
||||
extern ngx_uint_t req_header_thread_timeout_msec;
|
||||
extern ngx_uint_t req_body_thread_timeout_msec;
|
||||
extern ngx_uint_t res_header_thread_timeout_msec;
|
||||
extern ngx_uint_t res_body_thread_timeout_msec;
|
||||
extern ngx_http_inspection_mode_e inspection_mode;
|
||||
extern ngx_uint_t num_of_nginx_ipc_elements;
|
||||
|
||||
///
|
||||
/// @struct ngx_http_cp_list_iterator
|
||||
/// @brief NGINX list iterator's data.
|
||||
///
|
||||
typedef struct {
|
||||
ngx_list_part_t *current_part; ///< Iterator's current node.
|
||||
size_t current_part_element_index; ///< Current part index.
|
||||
size_t current_list_element_index; ///< Current list index.
|
||||
size_t list_element_size; ///< The total size of the list that the iterator belongs to.
|
||||
} ngx_http_cp_list_iterator;
|
||||
|
||||
///
|
||||
/// @struct ngx_http_cp_sessions_per_minute_limit
|
||||
/// @brief Holds sessions per minute related limitations.
|
||||
///
|
||||
typedef struct {
|
||||
/// After more than a minute has passed, we reset all session monitoring data.
|
||||
/// sessions_per_second array helps keeping track with sessions that need to be closed.
|
||||
unsigned int sessions_per_second[60];
|
||||
unsigned int last_minute_sessions_sum; ///< Sum of all the last minutes sessions' sum.
|
||||
unsigned int last_session_time; ///< The length of the latest session.
|
||||
} ngx_http_cp_sessions_per_minute_limit;
|
||||
|
||||
///
|
||||
/// @brief Set debug context.
|
||||
/// @param[in, out] client_ip Client IP to set in the debug.
|
||||
/// @param[in, out] listening_ip Listening IP to set in the debug.
|
||||
/// @param[in, out] uri_prefix Uri prefix to set in the debug.
|
||||
/// @param[in, out] hostname Hostname to set in the debug.
|
||||
/// @param[in, out] method Method to set in the debug.
|
||||
/// @param[in] listening_port Listening port to set in the debug.
|
||||
///
|
||||
void set_dbg_by_ctx(
|
||||
char *client_ip,
|
||||
char *listening_ip,
|
||||
char *uri_prefix,
|
||||
char *hostname,
|
||||
char *method,
|
||||
unsigned int listening_port);
|
||||
|
||||
///
|
||||
/// @brief Reset debug context.
|
||||
///
|
||||
void reset_dbg_ctx();
|
||||
|
||||
///
|
||||
/// @brief Initiate list iterator of the provided list.
|
||||
/// @param[in, out] list to get the iterator of.
|
||||
/// @param[in, out] iterator the iterator to be initiated
|
||||
///
|
||||
void init_list_iterator(ngx_list_t *list, ngx_http_cp_list_iterator *iterator);
|
||||
|
||||
///
|
||||
/// @brief Get list element
|
||||
/// @param[in, out] iterator the iterator to be initiated.
|
||||
/// @param[in] index.
|
||||
/// @returns void*
|
||||
/// - #A pointer to the element.
|
||||
/// - #NULL if failed to get or reached the end of the list.
|
||||
///
|
||||
void *get_list_element(ngx_http_cp_list_iterator *iterator, const size_t index);
|
||||
|
||||
///
|
||||
/// @brief Free a list from NGINX pool.
|
||||
/// @param[in, out] memory_pool NGINX pool.
|
||||
/// @param[in, out] list A pointer to a list to free.
|
||||
/// @returns void*
|
||||
/// - #NGX_OK.
|
||||
/// - #NGX_ERROR.
|
||||
///
|
||||
ngx_int_t free_list_from_pool(ngx_pool_t *memory_pool, ngx_list_t *list);
|
||||
|
||||
///
|
||||
/// @brief Initiate a provided hash table with the provided values.
|
||||
/// @param[in, out] memory_pool NGINX pool.
|
||||
/// @param[in, out] hash_table_initializer NGINX hash initializator.
|
||||
/// @param[in, out] hash_table Hash table to init.
|
||||
/// @param[in] hash_table_name Hash table name.
|
||||
/// @param[in] max_size Maximum size to set the hash table.
|
||||
/// @param[in] bucket_size Bucket size to set in the hash table.
|
||||
/// @param[in, out] keys Keys initiate and put into the hash_table.
|
||||
/// @param[in, out] initial_value Initial hash value.
|
||||
/// @param[in, out] initial_value_size Initial hash value's size.
|
||||
/// @return ngx_int_t
|
||||
/// - #NGX_OK.
|
||||
/// - #NGX_ERROR.
|
||||
///
|
||||
ngx_int_t init_hash_table(
|
||||
ngx_pool_t *memory_pool,
|
||||
ngx_hash_init_t *hash_table_initializer,
|
||||
ngx_hash_t *hash_table,
|
||||
char *hash_table_name,
|
||||
ngx_uint_t max_size,
|
||||
ngx_uint_t bucket_size,
|
||||
ngx_list_t *keys,
|
||||
const void *initial_value,
|
||||
const size_t initial_value_size
|
||||
);
|
||||
|
||||
///
|
||||
/// @brief Copy the src buffer to the dest.
|
||||
/// @param[in, out] dest NGINX chain to be copied into.
|
||||
/// @param[in] src NGINX chain to come from.
|
||||
///
|
||||
void copy_chain_buffers(ngx_chain_t *dest, ngx_chain_t *src);
|
||||
|
||||
///
|
||||
/// @brief Adds a new chain element before current list element.
|
||||
/// @param[in, out] current_elem NGINX chain to be copied into.
|
||||
/// @param[in] new_elem NGINX chain to come from.
|
||||
///
|
||||
void prepend_chain_elem(ngx_chain_t *current_elem, ngx_chain_t *new_elem);
|
||||
|
||||
///
|
||||
/// @brief Adds a new chain element after current list element.
|
||||
/// @param[in, out] current_elem NGINX chain to be copied into.
|
||||
/// @param[in] new_elem NGINX chain to come from.
|
||||
///
|
||||
void append_chain_elem(ngx_chain_t *current_elem, ngx_chain_t *new_elem);
|
||||
|
||||
///
|
||||
/// @brief Split chain element.
|
||||
/// @param[in, out] elem NGINX chain to be split.
|
||||
/// @param[in, out] split_index Index to split from.
|
||||
/// @param[in, out] pool NGINX pool.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK.
|
||||
/// - #NGX_ERROR.
|
||||
///
|
||||
ngx_int_t split_chain_elem(ngx_chain_t *elem, uint16_t split_index, ngx_pool_t *pool);
|
||||
|
||||
///
|
||||
/// @brief Create chain element
|
||||
/// @param[in, out] elem NGINX chain to be split.
|
||||
/// @param[in, out] split_index Index to split from.
|
||||
/// @param[in, out] pool NGINX pool.
|
||||
/// @returns ngx_chain_t
|
||||
/// - #A valid pointer to a ngx_chain_t.
|
||||
/// - #NULL if failed to create a chain element.
|
||||
///
|
||||
ngx_chain_t * create_chain_elem(uint32_t data_size, char *data, ngx_pool_t *pool);
|
||||
|
||||
///
|
||||
/// @brief Free a NGINX chain.
|
||||
/// @param[in, out] pool NGINX pool that free the resources into.
|
||||
/// @param[in, out] chain NGINX chain to free.
|
||||
/// @returns ngx_chain_t
|
||||
///
|
||||
void free_chain(ngx_pool_t *pool, ngx_chain_t *chain);
|
||||
|
||||
///
|
||||
/// @brief Get currently set response uuid.
|
||||
/// @returns char * of set web_response_uuid variable.
|
||||
///
|
||||
const char *get_web_response_uuid(void);
|
||||
|
||||
///y
|
||||
/// @brief Get currently set response code.
|
||||
/// @returns Returns the size of web_response_uuid variable.
|
||||
///
|
||||
ngx_uint_t get_web_response_uuid_size(void);
|
||||
|
||||
///
|
||||
/// @brief Sets a custom response page by modifying web_response_title/body/uuid variables.
|
||||
/// @param[in] title Sets the web response title.
|
||||
/// @param[in] message Sets the response body.
|
||||
/// @param[in] uuid Sets the uuid of the custom response.
|
||||
/// @param[in, out] response_code Sets the response code of the custom response.
|
||||
///
|
||||
void set_custom_response(const ngx_str_t *title, const ngx_str_t *message, const ngx_str_t *uuid, ngx_uint_t response_code);
|
||||
|
||||
///
|
||||
/// @brief Sets a redirect response by modifying redirect triggers, redirect_location and web_response_uuid.
|
||||
/// @param[in] location Redirect location to set to.
|
||||
/// @param[in] uuid Redirection's response uuid to set.
|
||||
/// @param[in, out] add_event_id_to_header Event ID to add to the response header.
|
||||
///
|
||||
void set_redirect_response(const ngx_str_t *location, const ngx_str_t *uuid, uint add_event_id_to_header);
|
||||
|
||||
///
|
||||
/// @brief Get the redirect location.
|
||||
/// @returns redirect_location variable.
|
||||
///
|
||||
u_char *get_redirect_location();
|
||||
|
||||
///
|
||||
/// @brief Get the redirect location.
|
||||
/// @returns redirect_location_size variable.
|
||||
///
|
||||
ngx_uint_t get_redirect_location_size();
|
||||
|
||||
///
|
||||
/// @brief Get the redirect location.
|
||||
/// @returns add_event_id variable.
|
||||
///
|
||||
ngx_uint_t get_add_event_id();
|
||||
|
||||
///
|
||||
/// @brief Returns if timeout has been reached.
|
||||
/// @param[in, out] timeout NGINX pool that free the resources into.
|
||||
/// @returns Returns 1 it timeout reached, otherwise 0.
|
||||
///
|
||||
int is_timeout_reached(struct timeval *timeout);
|
||||
|
||||
///
|
||||
/// @brief Get delta current time + delta_time_in_sec value in seconds.
|
||||
/// @param[in] delta_time_in_sec Delta time to return
|
||||
/// @returns timeval struct with tv_sec value of += delta_time_in_sec.
|
||||
///
|
||||
struct timeval get_timeout_val_sec(const int delta_time_in_sec);
|
||||
|
||||
///
|
||||
/// @brief Get delta current time + delta_time_in_usec value in seconds.
|
||||
/// @param[in] delta_time_in_usec Delta time to return
|
||||
/// @returns timeval struct with tv_sec value of += delta_time_in_usec.
|
||||
///
|
||||
struct timeval get_timeout_val_usec(const int delta_time_in_usec);
|
||||
|
||||
///
|
||||
/// @brief Get the currently set response page.
|
||||
/// @param[in, out] request NGINX request, used to get the NGINX pool to allocate buffer needed for out_chain.
|
||||
/// @param[in, out] out_chain NGINX chain that the response page data will be written to.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK.
|
||||
/// - #NGX_ERROR_ERR.
|
||||
///
|
||||
ngx_int_t get_response_page(ngx_http_request_t *request, ngx_chain_t (*out_chain)[7]);
|
||||
|
||||
///
|
||||
/// @brief Get currently set response page length.
|
||||
/// @returns ngx_uint_t length of the response page.
|
||||
///
|
||||
ngx_uint_t get_response_page_length(void);
|
||||
|
||||
///
|
||||
/// @brief Get currently set response code.
|
||||
/// @returns ngx_uint_t web_triggers_response_code variable.
|
||||
///
|
||||
ngx_uint_t get_response_code(void);
|
||||
|
||||
///
|
||||
/// @brief Get currently set static resource path.
|
||||
/// @returns char * get static_resources_path variable.
|
||||
///
|
||||
const char * get_static_resources_path(void);
|
||||
|
||||
///
|
||||
/// @brief Get currently set memory_pool.
|
||||
/// @returns ngx_pool_t * get memory_pool.
|
||||
///
|
||||
ngx_pool_t *get_memory_pool(void);
|
||||
|
||||
///
|
||||
/// @brief Set memory_pool.
|
||||
/// @param[in, out] new_memory_pool A new NGINX pool to be set.
|
||||
///
|
||||
void set_memory_pool(ngx_pool_t *new_memory_pool);
|
||||
|
||||
///
|
||||
/// @brief Get number of digits of the provided num variable.
|
||||
/// @param[in] num The number variable to get the number of digits from.
|
||||
/// @returns Returns the number of digits.
|
||||
///
|
||||
unsigned int get_number_of_digits(int num);
|
||||
|
||||
///
|
||||
/// @brief Get sessions per minute limit verdict.
|
||||
/// @returns ngx_http_cp_verdict_e sessions_per_minute_limit_verdict variable.
|
||||
///
|
||||
ngx_http_cp_verdict_e get_sessions_per_minute_limit_verdict(void);
|
||||
|
||||
///
|
||||
/// @brief Get maximum sessions per minute.
|
||||
/// @returns unsigned int max_sessions_per_minute variable.
|
||||
///
|
||||
unsigned int get_max_sessions_per_minute(void);
|
||||
|
||||
///
|
||||
/// @brief Get periodic session limit info..
|
||||
/// @returns ngx_http_cp_sessions_per_minute_limit * Session per minute limit info.
|
||||
///
|
||||
ngx_http_cp_sessions_per_minute_limit *get_periodic_sessions_limit_info(void);
|
||||
|
||||
///
|
||||
/// @brief Writing into debug implementation.
|
||||
/// @param[in] _dbg_level Debug level to write into.
|
||||
/// @param[in] func Function name from which the write debug was called from.
|
||||
/// @param[in] file File from which the debug function was called from.
|
||||
/// @param[in] line_num Line number of the write debug was called on.
|
||||
/// @param[in] fmt Debug formatter.
|
||||
/// @param[in] ... Extra values to write into the debug using the formatter.
|
||||
///
|
||||
void ngx_cdecl write_dbg_impl(int _dbg_level, const char *func, const char *file, int line_num, const char *fmt, ...);
|
||||
|
||||
///
|
||||
/// @brief Sets a new debug level.
|
||||
/// @param[in] _dbg_level New debug level to be set.
|
||||
///
|
||||
void set_cp_ngx_attachment_debug_level(int _dbg_level);
|
||||
|
||||
///
|
||||
/// @brief Sets a new session ID.
|
||||
/// @param[in] _dbg_level New session ID to be set.
|
||||
///
|
||||
void set_current_session_id(uint32_t cur_session_id);
|
||||
|
||||
///
|
||||
/// @brief Checks if inspection required for a provided source IP.
|
||||
/// @param[in] src_ip Provided source IP to be checked.
|
||||
/// @returns 1 if inspection required, otherwise 0.
|
||||
///
|
||||
int is_inspection_required_for_source(const char *src_ip);
|
||||
|
||||
///
|
||||
/// @brief Initiates general configuration with the provided file path.
|
||||
/// @param[in] conf_path Configuration path to a file of general configuration to initiate.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK.
|
||||
/// - #NGX_ERROR.
|
||||
///
|
||||
ngx_int_t init_general_config(const char *conf_path);
|
||||
|
||||
///
|
||||
/// @brief Resets attachment configuration and loads them again from the file path in SHARED_ATTACMENT_CONF_PATH.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK.
|
||||
/// - #NGX_ERROR.
|
||||
///
|
||||
ngx_int_t reset_attachment_config(void);
|
||||
|
||||
///
|
||||
/// @brief Resets attachment configuration and loads them again from the file path in SHARED_ATTACMENT_CONF_PATH.
|
||||
/// @param[in] null_terminated_string null terminated string that the original string will be copied into.
|
||||
/// @param[in] original_string String to be copied into the null_terminated_string.
|
||||
/// @param[in] memory_pool NGINX pool for allocation the needed buffer for null_terminated_string.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK.
|
||||
/// - #NGX_ERROR.
|
||||
///
|
||||
ngx_int_t duplicate_ngx_string(ngx_str_t *null_terminated_string, ngx_str_t *original_string, ngx_pool_t *memory_pool);
|
||||
|
||||
///
|
||||
/// @brief Reverse implementation to strnchr - finding a character in a length limited string from the end.
|
||||
/// @param[in] string
|
||||
/// @param[in] char_to_find
|
||||
/// @param[in] string_length
|
||||
/// @returns u_char* pointer to the first u_char that was found.
|
||||
///
|
||||
u_char *reverse_strnchr(u_char *string, const u_char char_to_find, const size_t string_length);
|
||||
|
||||
///
|
||||
/// @brief Get keep alive internal milliseconds.
|
||||
/// @returns ngx_msec_t keep_alive_interval_msec variable.
|
||||
///
|
||||
ngx_msec_t get_keep_alive_interval_msec(void);
|
||||
|
||||
///
|
||||
/// @brief Update CPU's max, average metrics and time usage metric.
|
||||
///
|
||||
void set_metric_cpu_usage(void);
|
||||
|
||||
///
|
||||
/// @brief Update memory's max, average metrics and time usage metric.
|
||||
///
|
||||
void set_metric_memory_usage(void);
|
||||
|
||||
#endif // __NGX_CP_UTILS_H__
|
503
attachments/nginx/ngx_module/ngx_http_cp_attachment_module.c
Normal file
503
attachments/nginx/ngx_module/ngx_http_cp_attachment_module.c
Normal file
@@ -0,0 +1,503 @@
|
||||
// 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_http_cp_attachment_module.c
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
#include <ngx_files.h>
|
||||
|
||||
#include "ngx_cp_hooks.h"
|
||||
#include "ngx_cp_utils.h"
|
||||
#include "ngx_cp_initializer.h"
|
||||
#include "ngx_http_cp_attachment_module.h"
|
||||
#include "nginx_attachment_common.h"
|
||||
|
||||
extern ngx_uint_t current_config_version; ///< NGINX configuration version.
|
||||
typedef struct {
|
||||
ngx_flag_t enable; ///< Flags if the configuration enabled.
|
||||
ngx_int_t num_of_workers; ///< Number of workers.
|
||||
ngx_uint_t current_loc_config_version; ///< NGINX configuration version.
|
||||
} ngx_cp_attachment_conf_t;
|
||||
|
||||
///
|
||||
/// @brief Creates NGINX cp attachment configuration.
|
||||
/// @param[in, out] conf NGINX configuration.
|
||||
/// @return
|
||||
/// - #ngx_cp_attachment_conf_t if successed to create conf.
|
||||
/// - #NULL if failed to create conf.
|
||||
///
|
||||
static void * ngx_cp_attachment_create_conf(ngx_conf_t *conf);
|
||||
|
||||
///
|
||||
/// @brief Sets attachment's module configuration in NGINX configuration chain.
|
||||
/// @param[in, out] configure NGINX configuration.
|
||||
/// @param[in] curr ngx_cp_attachment_conf_t Pointer to the configuration.
|
||||
/// @param[in] next ngx_cp_attachment_conf_t Pointer to the configuration.
|
||||
/// @return NGX_CONF_OK.
|
||||
///
|
||||
static char * ngx_cp_attachment_merge_conf(ngx_conf_t *conf, void *curr, void *next);
|
||||
|
||||
///
|
||||
/// @brief Sets numbers of workers configuration.
|
||||
/// @param[in, out] cf NGINX configuration.
|
||||
/// @param[in, out] cmd NGINX command.
|
||||
/// @param[in, out] conf NGINX CP configuration.
|
||||
/// @return NGX_CONF_OK
|
||||
///
|
||||
static char * ngx_cp_attachment_set_num_workers_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
|
||||
|
||||
///
|
||||
/// @brief Inits NGINX CP attachment.
|
||||
/// @param[in] conf NGINX configuration.
|
||||
/// @returns ngx_int_t
|
||||
/// - #NGX_OK
|
||||
/// - #NGX_ERROR
|
||||
///
|
||||
static ngx_int_t ngx_cp_attachment_init(ngx_conf_t *conf);
|
||||
|
||||
///
|
||||
/// @brief Creates NGINX cp attachment configuration.
|
||||
/// @param[in, out] cf NGINX configuration.
|
||||
/// @return ngx_cp_attachment_conf_t
|
||||
/// - #ngx_cp_attachment_conf_t pointer if successed.
|
||||
/// - #NULL if failed.
|
||||
///
|
||||
static void * ngx_cp_attachment_create_main_conf(ngx_conf_t *cf);
|
||||
|
||||
///
|
||||
/// @brief Inits a NGINX CP worker.
|
||||
/// @param[in] cycle NGINX cycle.
|
||||
/// @returns NGX_OK.
|
||||
///
|
||||
static ngx_int_t ngx_cp_attachment_init_worker(ngx_cycle_t *cycle);
|
||||
|
||||
///
|
||||
/// @brief Finis a NGINX CP worker.
|
||||
/// @param[in] cycle NGINX cycle.
|
||||
///
|
||||
static void ngx_cp_attachment_fini_worker(ngx_cycle_t *cycle);
|
||||
|
||||
ngx_http_output_header_filter_pt ngx_http_next_response_header_filter; ///< NGINX response header filter.
|
||||
ngx_http_request_body_filter_pt ngx_http_next_request_body_filter; ///< NGINX request body filter.
|
||||
ngx_http_output_body_filter_pt ngx_http_next_response_body_filter; ///< NGINX output body filter.
|
||||
|
||||
struct sockaddr_un attachment_server; ///< NGINX CP attachments server socket.
|
||||
|
||||
static ngx_event_t ngx_keep_alive_event;
|
||||
static ngx_int_t is_timer_active = 0;
|
||||
static ngx_connection_t dumb_connection;
|
||||
static ngx_msec_t keep_alive_interval_msec = 0;
|
||||
static ngx_msec_t timer_interval_msec = 10000;
|
||||
|
||||
/// NGINX CP attachment command array.
|
||||
static ngx_command_t ngx_cp_attachment_commands[] = {
|
||||
{
|
||||
ngx_string("cp-nano-nginx-attachment"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
|
||||
ngx_conf_set_flag_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_cp_attachment_conf_t, enable),
|
||||
NULL
|
||||
},
|
||||
{
|
||||
ngx_string("cp_worker_processes"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
|
||||
ngx_cp_attachment_set_num_workers_conf,
|
||||
NGX_HTTP_MAIN_CONF_OFFSET,
|
||||
offsetof(ngx_cp_attachment_conf_t, num_of_workers),
|
||||
NULL
|
||||
},
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
/// NGINX CP attachment module context.
|
||||
static ngx_http_module_t ngx_cp_attachment_module_ctx = {
|
||||
NULL,
|
||||
ngx_cp_attachment_init,
|
||||
ngx_cp_attachment_create_main_conf,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
ngx_cp_attachment_create_conf,
|
||||
ngx_cp_attachment_merge_conf
|
||||
};
|
||||
|
||||
/// NGINX attachment module.
|
||||
ngx_module_t ngx_http_cp_attachment_module = {
|
||||
NGX_MODULE_V1, ///< NGINX CP module version.
|
||||
&ngx_cp_attachment_module_ctx, ///< module context.
|
||||
ngx_cp_attachment_commands, ///< module directives.
|
||||
NGX_HTTP_MODULE, ///< module type.
|
||||
NULL,
|
||||
NULL,
|
||||
ngx_cp_attachment_init_worker, ///< init process.
|
||||
NULL,
|
||||
NULL,
|
||||
ngx_cp_attachment_fini_worker, ///< exit process.
|
||||
NULL,
|
||||
NGX_MODULE_V1_PADDING
|
||||
};
|
||||
|
||||
int workers_amount_to_send = 0;
|
||||
|
||||
static void *
|
||||
ngx_cp_attachment_create_main_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_cp_attachment_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool, sizeof(ngx_cp_attachment_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
static char *
|
||||
ngx_cp_attachment_set_num_workers_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
(void) cmd;
|
||||
ngx_str_t *value;
|
||||
ngx_cp_attachment_conf_t *ccf;
|
||||
ccf = (ngx_cp_attachment_conf_t *)conf;
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
if (ngx_strcmp(value[1].data, "auto") == 0) {
|
||||
ccf->num_of_workers = ngx_ncpu;
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
ccf->num_of_workers = ngx_atoi(value[1].data, value[1].len);
|
||||
|
||||
if (ccf->num_of_workers == NGX_ERROR) {
|
||||
ccf->num_of_workers = 0;
|
||||
return "invalid value";
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
static void *
|
||||
ngx_cp_attachment_create_conf(ngx_conf_t *conf)
|
||||
{
|
||||
ngx_cp_attachment_conf_t *module_conf;
|
||||
|
||||
if (conf == NULL) {
|
||||
write_dbg(DBG_LEVEL_WARNING, "Failed to create attachment module configuration: input argument is NULL");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
module_conf = ngx_pcalloc(conf->pool, sizeof(ngx_cp_attachment_conf_t));
|
||||
if (module_conf == NULL) {
|
||||
write_dbg(DBG_LEVEL_WARNING, "Failed to allocate attachment module configuration");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
module_conf->enable = NGX_CONF_UNSET;
|
||||
module_conf->num_of_workers = 0;
|
||||
module_conf->current_loc_config_version = current_config_version;
|
||||
write_dbg(DBG_LEVEL_TRACE, "Successfully created attachment module configuration");
|
||||
return module_conf;
|
||||
}
|
||||
|
||||
|
||||
ngx_uint_t
|
||||
get_num_of_workers(ngx_http_request_t *request)
|
||||
{
|
||||
if (workers_amount_to_send > 0) return workers_amount_to_send;
|
||||
|
||||
ngx_cp_attachment_conf_t *conf = ngx_http_get_module_main_conf(request, ngx_http_cp_attachment_module);
|
||||
if (conf == NULL) {
|
||||
write_dbg(DBG_LEVEL_WARNING, "Failed to fetch the local NGINX attachment state");
|
||||
return 0;
|
||||
}
|
||||
write_dbg(DBG_LEVEL_INFO, "num_of_workers=%d", conf->num_of_workers);
|
||||
|
||||
workers_amount_to_send = conf->num_of_workers;
|
||||
return conf->num_of_workers;
|
||||
}
|
||||
|
||||
ngx_int_t
|
||||
is_ngx_cp_attachment_disabled(ngx_http_request_t *request)
|
||||
{
|
||||
ngx_cp_attachment_conf_t *conf = ngx_http_get_module_loc_conf(request, ngx_http_cp_attachment_module);
|
||||
if (conf == NULL) {
|
||||
write_dbg(DBG_LEVEL_WARNING, "Failed to fetch the local NGINX attachment state");
|
||||
return NGX_CONF_UNSET;
|
||||
}
|
||||
if (conf->current_loc_config_version != current_config_version) {
|
||||
conf->current_loc_config_version = current_config_version;
|
||||
write_dbg(DBG_LEVEL_INFO, "Reconfiguring the local NGINX attachment state");
|
||||
}
|
||||
write_dbg(DBG_LEVEL_TRACE, "NGINX attachment state: %s", conf->enable ? "enabled" : "disabled");
|
||||
return !conf->enable;
|
||||
}
|
||||
|
||||
void
|
||||
ngx_cp_set_module_loc_conf(ngx_http_request_t *request, ngx_flag_t new_state)
|
||||
{
|
||||
ngx_cp_attachment_conf_t *conf;
|
||||
conf = ngx_http_get_module_loc_conf(request, ngx_http_cp_attachment_module);
|
||||
conf->enable = new_state;
|
||||
write_dbg(DBG_LEVEL_INFO, "Configuration set to be %s", conf->enable ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
static char *
|
||||
ngx_cp_attachment_merge_conf(ngx_conf_t *configure, void *curr, void *next)
|
||||
{
|
||||
(void)configure;
|
||||
ngx_cp_attachment_conf_t *prev = curr;
|
||||
ngx_cp_attachment_conf_t *conf = next;
|
||||
|
||||
ngx_conf_merge_value(conf->enable, prev->enable, NGX_CONF_UNSET);
|
||||
ngx_conf_merge_value(conf->num_of_workers, prev->num_of_workers, ngx_ncpu);
|
||||
|
||||
write_dbg(DBG_LEVEL_TRACE, "Successfully set attachment module configuration in nginx configuration chain");
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
///
|
||||
/// @brief Sends keep alive to the nano agent.
|
||||
/// @param[in] event NGINX event.
|
||||
///
|
||||
static void
|
||||
ngx_send_keep_alive(ngx_event_t *event)
|
||||
{
|
||||
char *family_name = NULL;
|
||||
uint8_t family_name_size = 0;
|
||||
uint8_t worker_id = 0;
|
||||
int keep_alive_socket = -1;
|
||||
struct timeval timeout = get_timeout_val_sec(1);
|
||||
int res = 0;
|
||||
int connected = 0;
|
||||
static ngx_msec_t keep_alive_wait_period = 0;
|
||||
|
||||
if (ngx_exiting) {
|
||||
is_timer_active = 0;
|
||||
write_dbg(DBG_LEVEL_INFO, "exiting without re-sched of ngx_send_keep_alive . ngx_exiting=%d", ngx_exiting);
|
||||
return;
|
||||
}
|
||||
|
||||
keep_alive_interval_msec = get_keep_alive_interval_msec();
|
||||
if (keep_alive_interval_msec <= 0) {
|
||||
// Received invalid interval size, using default.
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Received invalid interval size, using default value instead. Received value: %d, Default value: %u",
|
||||
keep_alive_interval_msec,
|
||||
DEFAULT_KEEP_ALIVE_INTERVAL_MSEC
|
||||
);
|
||||
keep_alive_interval_msec = DEFAULT_KEEP_ALIVE_INTERVAL_MSEC;
|
||||
}
|
||||
|
||||
keep_alive_wait_period += timer_interval_msec;
|
||||
if (keep_alive_wait_period < keep_alive_interval_msec) {
|
||||
// Wait alive period is still within interval size.
|
||||
goto keep_alive_handler_out;
|
||||
}
|
||||
|
||||
get_docker_id(&family_name);
|
||||
family_name_size = strlen(family_name);
|
||||
write_dbg(DBG_LEVEL_DEBUG, "Keep alive signal. Family id: %s, UID: %u", family_name, worker_id);
|
||||
|
||||
if (keep_alive_socket > 0) {
|
||||
close(keep_alive_socket);
|
||||
keep_alive_socket = -1;
|
||||
}
|
||||
|
||||
// Attempting to create a socket.
|
||||
keep_alive_socket = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (keep_alive_socket < 0) {
|
||||
// Failed to create a socket.
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Could not create socket due to error. Socket number: %d, error: %s, errno: %d",
|
||||
keep_alive_socket,
|
||||
strerror(errno),
|
||||
errno
|
||||
);
|
||||
goto keep_alive_handler_out;
|
||||
}
|
||||
attachment_server.sun_family = AF_UNIX;
|
||||
strncpy(attachment_server.sun_path, SHARED_KEEP_ALIVE_PATH, sizeof(attachment_server.sun_path) - 1);
|
||||
|
||||
// Attempting to connect to the nano service.
|
||||
connected = connect(keep_alive_socket, (struct sockaddr *)&attachment_server, sizeof(struct sockaddr_un));
|
||||
if (connected == -1) {
|
||||
// Failed to connect to the nano service.
|
||||
write_dbg(
|
||||
DBG_LEVEL_DEBUG,
|
||||
"Could not connect to nano service. Path: %s, Error: %s, Errno: %d",
|
||||
attachment_server.sun_path,
|
||||
strerror(errno),
|
||||
errno
|
||||
);
|
||||
goto keep_alive_handler_out;
|
||||
}
|
||||
write_dbg(DBG_LEVEL_DEBUG, "connected to socket: %d. sending keep alive signals");
|
||||
|
||||
// Exchanging worker id with the nano service.
|
||||
res = exchange_communication_data_with_service(
|
||||
keep_alive_socket,
|
||||
&worker_id,
|
||||
sizeof(worker_id),
|
||||
WRITE_TO_SOCKET,
|
||||
&timeout
|
||||
);
|
||||
if (res <= 0) {
|
||||
// Failed to send worker id
|
||||
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to send worker id");
|
||||
goto keep_alive_handler_out;
|
||||
}
|
||||
|
||||
// Exchanging family name size with the nano service.
|
||||
res = exchange_communication_data_with_service(
|
||||
keep_alive_socket,
|
||||
&family_name_size,
|
||||
sizeof(family_name_size),
|
||||
WRITE_TO_SOCKET,
|
||||
&timeout
|
||||
);
|
||||
if (res <= 0) {
|
||||
// Failed to send family name size.
|
||||
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to send family name size");
|
||||
goto keep_alive_handler_out;
|
||||
}
|
||||
|
||||
if (family_name_size > 0) {
|
||||
// Exchanging family name with the nano service.
|
||||
res = exchange_communication_data_with_service(
|
||||
keep_alive_socket,
|
||||
family_name,
|
||||
family_name_size,
|
||||
WRITE_TO_SOCKET,
|
||||
&timeout
|
||||
);
|
||||
if (res <= 0) {
|
||||
// Failed to send family name.
|
||||
write_dbg_if_needed(DBG_LEVEL_WARNING, "Failed to send family name");
|
||||
goto keep_alive_handler_out;
|
||||
}
|
||||
}
|
||||
keep_alive_wait_period = 0;
|
||||
|
||||
keep_alive_handler_out:
|
||||
// Sends another signal.
|
||||
write_dbg(DBG_LEVEL_DEBUG, "send signal again in %u sec", (timer_interval_msec / 1000));
|
||||
ngx_add_timer(event, timer_interval_msec);
|
||||
if (keep_alive_socket > 0) {
|
||||
close(keep_alive_socket);
|
||||
keep_alive_socket = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static ngx_int_t
|
||||
ngx_cp_attachment_init_worker(ngx_cycle_t *cycle)
|
||||
{
|
||||
ngx_core_conf_t *core_main_conf;
|
||||
ngx_cp_attachment_conf_t *attachment_conf;
|
||||
|
||||
write_dbg(DBG_LEVEL_INFO, "entering init worker. ngx_exiting=%d", ngx_exiting);
|
||||
|
||||
attachment_conf = (ngx_cp_attachment_conf_t *)ngx_get_conf(cycle->conf_ctx, ngx_http_cp_attachment_module);
|
||||
if (attachment_conf && attachment_conf->num_of_workers) workers_amount_to_send = attachment_conf->num_of_workers;
|
||||
if (!workers_amount_to_send) {
|
||||
core_main_conf = (ngx_core_conf_t *)ngx_get_conf(cycle->conf_ctx, ngx_core_module);
|
||||
workers_amount_to_send = core_main_conf->worker_processes;
|
||||
}
|
||||
// Worker number 0 will always exist.
|
||||
// Therefore the single instance of the timer will be created and destroyed by it.
|
||||
if (ngx_worker == 0) {
|
||||
write_dbg(DBG_LEVEL_INFO, "Configured workers amount: %d", workers_amount_to_send);
|
||||
ngx_keep_alive_event.handler = ngx_send_keep_alive;
|
||||
ngx_keep_alive_event.log = cycle->log;
|
||||
ngx_keep_alive_event.data = &dumb_connection;
|
||||
dumb_connection.fd = (ngx_socket_t) -1;
|
||||
keep_alive_interval_msec = get_keep_alive_interval_msec();
|
||||
if (keep_alive_interval_msec == 0) {
|
||||
write_dbg(DBG_LEVEL_WARNING, "Invalid interval size: %u, set to default value: %d ", keep_alive_interval_msec, DEFAULT_KEEP_ALIVE_INTERVAL_MSEC);
|
||||
keep_alive_interval_msec = DEFAULT_KEEP_ALIVE_INTERVAL_MSEC;
|
||||
}
|
||||
|
||||
ngx_add_timer(&ngx_keep_alive_event, timer_interval_msec);
|
||||
is_timer_active = 1;
|
||||
write_dbg(
|
||||
DBG_LEVEL_INFO,
|
||||
"Timer successfully added. Keep alive interval: %d, timer interval: %d",
|
||||
keep_alive_interval_msec,
|
||||
timer_interval_msec
|
||||
);
|
||||
}
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
ngx_cp_attachment_fini_worker(ngx_cycle_t *cycle)
|
||||
{
|
||||
write_dbg(DBG_LEVEL_INFO, "entering fini worker. is_timer_active=%d, ngx_exiting=%d", is_timer_active, ngx_exiting);
|
||||
|
||||
// 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;
|
||||
|
||||
(void)cycle;
|
||||
if (is_timer_active) ngx_del_timer(&ngx_keep_alive_event);
|
||||
write_dbg(DBG_LEVEL_INFO, "Timer successfully deleted");
|
||||
is_timer_active = 0;
|
||||
}
|
||||
|
||||
static ngx_int_t
|
||||
ngx_cp_attachment_init(ngx_conf_t *conf)
|
||||
{
|
||||
ngx_http_handler_pt *handler;
|
||||
ngx_http_core_main_conf_t *http_core_main_conf;
|
||||
write_dbg(DBG_LEVEL_TRACE, "Setting the memory pool used in the current context");
|
||||
if (conf->pool == NULL) {
|
||||
write_dbg(
|
||||
DBG_LEVEL_WARNING,
|
||||
"Failed to set memory pool in the current context, "
|
||||
"no memory pool has been allocated for the current configuration"
|
||||
);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
set_memory_pool(conf->pool);
|
||||
write_dbg(
|
||||
DBG_LEVEL_TRACE,
|
||||
"Successfully set the memory pool in the current context. Setting attachment module's hooks."
|
||||
);
|
||||
|
||||
http_core_main_conf = ngx_http_conf_get_module_main_conf(conf, ngx_http_core_module);
|
||||
handler = ngx_array_push(&http_core_main_conf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
|
||||
|
||||
if (handler == NULL) {
|
||||
write_dbg(DBG_LEVEL_WARNING, "Failed to set HTTP request headers' handler");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
*handler = ngx_http_cp_req_header_handler;
|
||||
|
||||
ngx_http_next_response_header_filter = ngx_http_top_header_filter;
|
||||
ngx_http_top_header_filter = ngx_http_cp_res_header_filter;
|
||||
|
||||
ngx_http_next_request_body_filter = ngx_http_top_request_body_filter;
|
||||
ngx_http_top_request_body_filter = ngx_http_cp_req_body_filter;
|
||||
|
||||
ngx_http_next_response_body_filter = ngx_http_top_body_filter;
|
||||
ngx_http_top_body_filter = ngx_http_cp_res_body_filter;
|
||||
|
||||
write_dbg(DBG_LEVEL_TRACE, "Successfully set attachment module's hooks");
|
||||
|
||||
return NGX_OK;
|
||||
}
|
53
attachments/nginx/ngx_module/ngx_http_cp_attachment_module.h
Normal file
53
attachments/nginx/ngx_module/ngx_http_cp_attachment_module.h
Normal file
@@ -0,0 +1,53 @@
|
||||
// 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_http_cp_attachment_module.h
|
||||
#ifndef __NGX_HTTP_CP_ATTACHMENT_MODULE_H__
|
||||
#define __NGX_HTTP_CP_ATTACHMENT_MODULE_H__
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
extern ngx_http_output_header_filter_pt ngx_http_next_response_header_filter; ///< NGINX response header filter.
|
||||
|
||||
extern ngx_http_request_body_filter_pt ngx_http_next_request_body_filter; ///< NGINX request body filter.
|
||||
extern ngx_http_output_body_filter_pt ngx_http_next_response_body_filter; ///< NGINX output body filter.
|
||||
|
||||
extern ngx_module_t ngx_http_cp_attachment_module; ///< NGINX Module.
|
||||
|
||||
///
|
||||
/// @brief Returns if NGINX CP attachment is disabled.
|
||||
/// @param[in] request NGINX request.
|
||||
/// @returns ngx_int_t
|
||||
/// - #0 attachment is enabled.
|
||||
/// - #1 attachment is disabled.
|
||||
///
|
||||
ngx_int_t is_ngx_cp_attachment_disabled(ngx_http_request_t *request);
|
||||
|
||||
///
|
||||
/// @brief Get the number of workers.
|
||||
/// @param[in] request NGINX request.
|
||||
/// @returns ngx_uint_t returns number of workers.
|
||||
///
|
||||
ngx_uint_t get_num_of_workers(ngx_http_request_t *request);
|
||||
|
||||
///
|
||||
/// @brief Set module config.
|
||||
/// @param[in] request NGINX request.
|
||||
/// @param[in] new_state NGINX flag to set.
|
||||
///
|
||||
void ngx_cp_set_module_loc_conf(ngx_http_request_t *request, ngx_flag_t new_state);
|
||||
|
||||
#endif // __NGX_HTTP_CP_ATTACHMENT_MODULE_H__
|
Reference in New Issue
Block a user