adding istio files

This commit is contained in:
wiaamm
2024-12-24 18:32:02 +02:00
parent 3c614f385a
commit 1290c51ab6
60 changed files with 13459 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
add_subdirectory(nano_attachment_util)
include_directories(include)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE -lpthread -Wall")
link_directories(../../core)
link_directories(../../core/shmem_ipc)
include_directories(../../core/include/attachments)
add_library(
nano_attachment
SHARED
nano_attachment.c
nano_configuration.c
nano_initializer.c
nano_utils.c
nano_attachment_io.c
nano_attachment_thread.c
nano_attachment_sender.c
nano_attachment_sender_thread.c
nano_attachment_metric.c
)
target_link_libraries(nano_attachment shmem_ipc_2 nano_attachment_util)
# add_subdirectory(nano_attachment_ut)
# add_subdirectory(manual_testing)
install(TARGETS nano_attachment DESTINATION lib)
install(TARGETS nano_attachment DESTINATION http_transaction_handler_service/lib/ PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ)

View File

@@ -0,0 +1,22 @@
#ifndef __MOCK_ACCESS_H__
#define __MOCK_ACCESS_H__
#include "cmock.h"
extern "C" {
#include <unistd.h> // For the access function
}
class NanoAccessMocker : public CMockMocker<NanoAccessMocker>
{
public:
MOCK_METHOD2(access, int(const char *path, int mode));
};
CMOCK_MOCK_FUNCTION2(
NanoAccessMocker,
access,
int(const char *path, int mode)
);
#endif // __MOCK_ACCESS_H__

View File

@@ -0,0 +1,213 @@
#ifndef __MOCK_NANO_INITIALIZER_IO_H__
#define __MOCK_NANO_INITIALIZER_IO_H__
#include "cmock.h"
#include "nano_attachment_common.h"
extern "C" {
#include "nano_attachment_io.h"
}
class NanoAttachmentIoMocker : public CMockMocker<NanoAttachmentIoMocker>
{
public:
MOCK_METHOD1(
connect_to_comm_socket,
NanoCommunicationResult(NanoAttachment *attachment)
);
MOCK_METHOD1(
connect_to_registration_socket,
NanoCommunicationResult(NanoAttachment *attachment)
);
MOCK_METHOD6(
nano_metadata_sender,
void(
NanoAttachment *attachment,
HttpMetaData *metadata,
HttpEventThreadCtx *ctx,
uint32_t cur_request_id,
unsigned int *num_of_messages_sent,
bool is_verdict_requested
)
);
MOCK_METHOD7(
nano_header_sender,
void(
NanoAttachment *attachment,
HttpHeaders *headers,
HttpEventThreadCtx *ctx,
AttachmentDataType header_type,
uint32_t cur_request_id,
unsigned int *num_messages_sent,
bool is_verdict_requested
)
);
MOCK_METHOD5(
nano_send_response_code,
void(
NanoAttachment *attachment,
uint16_t response_code,
HttpEventThreadCtx *ctx,
uint32_t cur_request_id,
unsigned int *num_messages_sent
)
);
MOCK_METHOD5(
nano_send_response_content_length,
void(
NanoAttachment *attachment,
uint64_t content_length,
HttpEventThreadCtx *ctx,
uint32_t cur_request_id,
unsigned int *num_messages_sent
)
);
MOCK_METHOD6(
nano_body_sender,
void(
NanoAttachment *attachment,
HttpBody *bodies,
HttpEventThreadCtx *ctx,
AttachmentDataType body_type,
uint32_t cur_request_id,
unsigned int *num_messages_sent
)
);
MOCK_METHOD5(
nano_end_transaction_sender,
void(
NanoAttachment *attachment,
AttachmentDataType end_transaction_type,
HttpEventThreadCtx *ctx,
SessionID cur_request_id,
unsigned int *num_messages_sent
)
);
MOCK_METHOD4(
nano_request_delayed_verdict,
void(
NanoAttachment *attachment,
HttpEventThreadCtx *ctx,
SessionID cur_request_id,
unsigned int *num_messages_sent
)
);
MOCK_METHOD1(nano_send_metric_data_sender, void(NanoAttachment *Attachment));
};
CMOCK_MOCK_FUNCTION1(
NanoAttachmentIoMocker,
connect_to_comm_socket,
NanoCommunicationResult(NanoAttachment *attachment)
);
CMOCK_MOCK_FUNCTION1(
NanoAttachmentIoMocker,
connect_to_registration_socket,
NanoCommunicationResult(NanoAttachment *attachment)
);
CMOCK_MOCK_FUNCTION6(
NanoAttachmentIoMocker,
nano_metadata_sender,
void(
NanoAttachment *attachment,
HttpMetaData *metadata,
HttpEventThreadCtx *ctx,
uint32_t cur_request_id,
unsigned int *num_of_messages_sent,
bool is_verdict_requested
)
);
CMOCK_MOCK_FUNCTION7(
NanoAttachmentIoMocker,
nano_header_sender,
void(
NanoAttachment *attachment,
HttpHeaders *headers,
HttpEventThreadCtx *ctx,
AttachmentDataType header_type,
uint32_t cur_request_id,
unsigned int *num_messages_sent,
bool is_verdict_requested
)
);
CMOCK_MOCK_FUNCTION5(
NanoAttachmentIoMocker,
nano_send_response_code,
void(
NanoAttachment *attachment,
uint16_t response_code,
HttpEventThreadCtx *ctx,
uint32_t cur_request_id,
unsigned int *num_messages_sent
)
);
CMOCK_MOCK_FUNCTION5(
NanoAttachmentIoMocker,
nano_send_response_content_length,
void(
NanoAttachment *attachment,
uint64_t content_length,
HttpEventThreadCtx *ctx,
uint32_t cur_request_id,
unsigned int *num_messages_sent
)
);
CMOCK_MOCK_FUNCTION6(
NanoAttachmentIoMocker,
nano_body_sender,
void(
NanoAttachment *attachment,
HttpBody *bodies,
HttpEventThreadCtx *ctx,
AttachmentDataType body_type,
uint32_t cur_request_id,
unsigned int *num_messages_sent
)
);
CMOCK_MOCK_FUNCTION5(
NanoAttachmentIoMocker,
nano_end_transaction_sender,
void(
NanoAttachment *attachment,
AttachmentDataType end_transaction_type,
HttpEventThreadCtx *ctx,
SessionID cur_request_id,
unsigned int *num_messages_sent
)
);
CMOCK_MOCK_FUNCTION4(
NanoAttachmentIoMocker,
nano_request_delayed_verdict,
void(
NanoAttachment *attachment,
HttpEventThreadCtx *ctx,
SessionID cur_request_id,
unsigned int *num_messages_sent
)
);
CMOCK_MOCK_FUNCTION1(
NanoAttachmentIoMocker,
nano_send_metric_data_sender,
void(NanoAttachment *Attachment)
);
#endif // __MOCK_NANO_INITIALIZER_IO_H__

View File

@@ -0,0 +1,72 @@
#ifndef __MOCK_NANO_ATTACHMENT_SENDER_H__
#define __MOCK_NANO_ATTACHMENT_SENDER_H__
#include "cmock.h"
#include "nano_attachment_common.h"
extern "C" {
#include "nano_attachment_sender.h"
}
class NanoAttachmentSenderMocker : public CMockMocker<NanoAttachmentSenderMocker>
{
public:
MOCK_METHOD2(SendMetadata, AttachmentVerdictResponse(NanoAttachment *attachment, AttachmentData *data));
MOCK_METHOD2(SendRequestHeaders, AttachmentVerdictResponse(NanoAttachment *attachment, AttachmentData *data));
MOCK_METHOD2(SendResponseHeaders, AttachmentVerdictResponse(NanoAttachment *attachment, AttachmentData *data));
MOCK_METHOD2(SendRequestBody, AttachmentVerdictResponse(NanoAttachment *attachment, AttachmentData *data));
MOCK_METHOD2(SendResponseBody, AttachmentVerdictResponse(NanoAttachment *attachment, AttachmentData *data));
MOCK_METHOD2(SendRequestEnd, AttachmentVerdictResponse(NanoAttachment *attachment, AttachmentData *data));
MOCK_METHOD2(SendResponseEnd, AttachmentVerdictResponse(NanoAttachment *attachment, AttachmentData *data));
MOCK_METHOD1(SendMetricData, NanoCommunicationResult(NanoAttachment *attachment));
};
CMOCK_MOCK_FUNCTION2(
NanoAttachmentSenderMocker,
SendMetadata,
AttachmentVerdictResponse(NanoAttachment *attachment, AttachmentData *data)
);
CMOCK_MOCK_FUNCTION2(
NanoAttachmentSenderMocker,
SendRequestHeaders,
AttachmentVerdictResponse(NanoAttachment *attachment, AttachmentData *data)
);
CMOCK_MOCK_FUNCTION2(
NanoAttachmentSenderMocker,
SendResponseHeaders,
AttachmentVerdictResponse(NanoAttachment *attachment, AttachmentData *data)
);
CMOCK_MOCK_FUNCTION2(
NanoAttachmentSenderMocker,
SendRequestBody,
AttachmentVerdictResponse(NanoAttachment *attachment, AttachmentData *data)
);
CMOCK_MOCK_FUNCTION2(
NanoAttachmentSenderMocker,
SendResponseBody,
AttachmentVerdictResponse(NanoAttachment *attachment, AttachmentData *data)
);
CMOCK_MOCK_FUNCTION2(
NanoAttachmentSenderMocker,
SendRequestEnd,
AttachmentVerdictResponse(NanoAttachment *attachment, AttachmentData *data)
);
CMOCK_MOCK_FUNCTION2(
NanoAttachmentSenderMocker,
SendResponseEnd,
AttachmentVerdictResponse(NanoAttachment *attachment, AttachmentData *data)
);
CMOCK_MOCK_FUNCTION1(
NanoAttachmentSenderMocker,
SendMetricData,
NanoCommunicationResult(NanoAttachment *attachment)
);
#endif // __MOCK_NANO_ATTACHMENT_SENDER_H__

View File

@@ -0,0 +1,42 @@
#ifndef __MOCK_NANO_ATTACHMENT_THREAD_H__
#define __MOCK_NANO_ATTACHMENT_THREAD_H__
#include "cmock.h"
#include "nano_attachment_common.h"
extern "C" {
#include "nano_attachment_thread.h"
}
class NanoAttachmentThreadMocker : public CMockMocker<NanoAttachmentThreadMocker>
{
public:
MOCK_METHOD7(
NanoRunInThreadTimeout,
int(
NanoAttachment *attachment,
AttachmentData *data,
CpThreadRoutine thread_func,
void *arg,
int timeout_msecs,
char *func_name,
TransactionType transaction_type
)
);
};
CMOCK_MOCK_FUNCTION7(
NanoAttachmentThreadMocker,
NanoRunInThreadTimeout,
int(
NanoAttachment *attachment,
AttachmentData *data,
CpThreadRoutine thread_func,
void *arg,
int timeout_msecs,
char *func_name,
TransactionType transaction_type
)
);
#endif // __MOCK_NANO_ATTACHMENT_THREAD_H__

View File

@@ -0,0 +1,39 @@
#ifndef __MOCK_NANO_CONFIGURATION_H__
#define __MOCK_NANO_CONFIGURATION_H__
#include "cmock.h"
#include "nano_attachment_common.h"
extern "C" {
#include "nano_configuration.h"
}
class NanoConfigurationMocker : public CMockMocker<NanoConfigurationMocker>
{
public:
MOCK_METHOD2(
init_attachment_config,
NanoCommunicationResult(
NanoAttachment *attachment,
const char *conf_path
)
);
MOCK_METHOD1(reset_attachment_config, NanoCommunicationResult(NanoAttachment *attachment));
};
CMOCK_MOCK_FUNCTION2(
NanoConfigurationMocker,
init_attachment_config,
NanoCommunicationResult(
NanoAttachment *attachment,
const char *conf_path
)
);
CMOCK_MOCK_FUNCTION1(
NanoConfigurationMocker,
reset_attachment_config,
NanoCommunicationResult(NanoAttachment *attachment)
);
#endif // __MOCK_NANO_CONFIGURATION_H__

View File

@@ -0,0 +1,52 @@
#ifndef __MOCK_NANO_INITIALIZER_H__
#define __MOCK_NANO_INITIALIZER_H__
#include "cmock.h"
#include "nano_attachment_common.h"
extern "C" {
#include "nano_initializer.h"
}
class NanoInitializerMocker : public CMockMocker<NanoInitializerMocker>
{
public:
MOCK_METHOD1(nano_attachment_init_process, NanoCommunicationResult(NanoAttachment *attachment));
MOCK_METHOD5(
write_to_service,
NanoCommunicationResult(
NanoAttachment *attachment,
int *socket,
void *data,
uint32_t size,
struct timeval *absolute_end_time
)
);
MOCK_METHOD1(handle_shmem_corruption, NanoCommunicationResult(NanoAttachment *attachment));
};
CMOCK_MOCK_FUNCTION1(
NanoInitializerMocker,
nano_attachment_init_process,
NanoCommunicationResult(NanoAttachment *attachment)
);
CMOCK_MOCK_FUNCTION5(
NanoInitializerMocker,
write_to_service,
NanoCommunicationResult(
NanoAttachment *attachment,
int *socket,
void *data,
uint32_t size,
struct timeval *absolute_end_time
)
);
CMOCK_MOCK_FUNCTION1(
NanoInitializerMocker,
handle_shmem_corruption,
NanoCommunicationResult(NanoAttachment *attachment)
);
#endif // __MOCK_NANO_INITIALIZER_H__

View File

@@ -0,0 +1,22 @@
#ifndef __MOCK_NANO_POLL_H__
#define __MOCK_NANO_POLL_H__
#include "cmock.h"
extern "C" {
#include <poll.h>
}
class NanoPollMocker : public CMockMocker<NanoPollMocker>
{
public:
MOCK_METHOD3(poll, int(pollfd *fds, nfds_t nfds, int timeout));
};
CMOCK_MOCK_FUNCTION3(
NanoPollMocker,
poll,
int(pollfd *fds, nfds_t nfds, int timeout)
);
#endif // __MOCK_NANO_POLL_H__

View File

@@ -0,0 +1,23 @@
#ifndef __MOCK_NANO_ATTACHMENT_SENDER_THREAD_H__
#define __MOCK_NANO_ATTACHMENT_SENDER_THREAD_H__
#include "cmock.h"
#include "nano_attachment_common.h"
extern "C" {
#include "nano_attachment_sender_thread.h"
}
class NanoSenderThreadMocker : public CMockMocker<NanoSenderThreadMocker>
{
public:
MOCK_METHOD1(SendRequestEndThread, void *(void *_ctx));
};
CMOCK_MOCK_FUNCTION1(
NanoSenderThreadMocker,
SendRequestEndThread,
void *(void *_ctx)
);
#endif // __MOCK_NANO_ATTACHMENT_SENDER_THREAD_H__

View File

@@ -0,0 +1,50 @@
#ifndef __MOCK_NANO_SOCKET_H__
#define __MOCK_NANO_SOCKET_H__
#include "cmock.h"
extern "C" {
#include <sys/socket.h>
}
class NanoSocketMocker : public CMockMocker<NanoSocketMocker>
{
public:
MOCK_METHOD3(socket, int(int domain, int type, int protocol));
MOCK_METHOD3(connect, int(int sockfd, const struct sockaddr *addr, socklen_t addrlen));
MOCK_METHOD1(close, int(int sockfd));
MOCK_METHOD3(write, ssize_t(int fd, const void *buf, size_t count));
MOCK_METHOD3(read, ssize_t(int fd, void *buf, size_t count));
};
CMOCK_MOCK_FUNCTION3(
NanoSocketMocker,
socket,
int(int domain, int type, int protocol)
);
CMOCK_MOCK_FUNCTION3(
NanoSocketMocker,
connect,
int(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
);
CMOCK_MOCK_FUNCTION1(
NanoSocketMocker,
close,
int(int sockfd)
);
CMOCK_MOCK_FUNCTION3(
NanoSocketMocker,
write,
ssize_t(int fd, const void *buf, size_t count)
);
CMOCK_MOCK_FUNCTION3(
NanoSocketMocker,
read,
ssize_t(int fd, void *buf, size_t count)
);
#endif // __MOCK_NANO_SOCKET_H__

View File

@@ -0,0 +1,22 @@
#ifndef __MOCK_NANO_STAT_H__
#define __MOCK_NANO_STAT_H__
#include "cmock.h"
extern "C" {
#include <sys/stat.h>
}
class NanoStatMocker : public CMockMocker<NanoStatMocker>
{
public:
MOCK_METHOD2(mkdir, int(const char *pathname, mode_t mode));
};
CMOCK_MOCK_FUNCTION2(
NanoStatMocker,
mkdir,
int(const char *pathname, mode_t mode)
);
#endif // __MOCK_NANO_STAT_H__

View File

@@ -0,0 +1,107 @@
#ifndef __MOCK_SHMEM_IPC__
#define __MOCK_SHMEM_IPC__
#include "cmock.h"
#include "cptest.h"
extern "C" {
#include "shmem_ipc_2.h"
}
class NanoShmemIPCMocker : public CMockMocker<NanoShmemIPCMocker>
{
public:
MOCK_METHOD7(
initIpc,
SharedMemoryIPC *(
const char queue_name[32],
uint32_t user_id,
uint32_t group_id,
int is_owner,
uint16_t num_of_queue_elem,
const LoggingData *logging_data,
void (*debug_func)(
const LoggingData *loggin_data,
uint32_t worker_id,
int is_error,
const char *func,
const char *file,
int line_num,
const char *fmt,
...
)
)
);
MOCK_METHOD2(destroyIpc, void(SharedMemoryIPC *ipc, int is_owner));
MOCK_METHOD2(resetIpc, void(SharedMemoryIPC *ipc, uint16_t num_of_data_segments));
MOCK_METHOD3(
sendData,
int(SharedMemoryIPC *ipc, const uint16_t data_to_send_size, const char *data_to_send)
);
MOCK_METHOD4(
sendChunkedData,
int(
SharedMemoryIPC *ipc,
const uint16_t *data_to_send_sizes,
const char **data_elem_to_send,
const uint8_t num_of_data_elem
)
);
MOCK_METHOD3(
receiveData,
int(SharedMemoryIPC *ipc, uint16_t *received_data_size, const char **received_data)
);
MOCK_METHOD1(popData, int(SharedMemoryIPC *ipc));
MOCK_METHOD1(isDataAvailable, int(SharedMemoryIPC *ipc));
MOCK_METHOD2(isCorruptedShmem, int(SharedMemoryIPC *ipc, int));
};
CMOCK_MOCK_FUNCTION7(
NanoShmemIPCMocker,
initIpc,
SharedMemoryIPC *(
const char queue_name[32],
uint32_t user_id,
uint32_t group_id,
int is_owner,
uint16_t num_of_queue_elem,
const LoggingData *logging_data,
void (*debug_func)(
const LoggingData *loggin_data,
uint32_t worker_id,
int is_error,
const char *func,
const char *file,
int line_num,
const char *fmt,
...
)
)
);
CMOCK_MOCK_FUNCTION2(NanoShmemIPCMocker, destroyIpc, void(SharedMemoryIPC *ipc, int is_owner));
CMOCK_MOCK_FUNCTION3(
NanoShmemIPCMocker,
sendData,
int(SharedMemoryIPC *ipc, const uint16_t data_to_send_size, const char *data_to_send)
)
CMOCK_MOCK_FUNCTION4(
NanoShmemIPCMocker,
sendChunkedData,
int(
SharedMemoryIPC *ipc,
const uint16_t *data_to_send_sizes,
const char **data_elem_to_send,
const uint8_t num_of_data_elem
)
);
CMOCK_MOCK_FUNCTION3(
NanoShmemIPCMocker,
receiveData,
int(SharedMemoryIPC *ipc, uint16_t *received_data_size, const char **received_data)
);
CMOCK_MOCK_FUNCTION1(NanoShmemIPCMocker, popData, int(SharedMemoryIPC *ipc));
CMOCK_MOCK_FUNCTION1(NanoShmemIPCMocker, isDataAvailable, int(SharedMemoryIPC *ipc));
CMOCK_MOCK_FUNCTION2(NanoShmemIPCMocker, resetIpc, void(SharedMemoryIPC *ipc, uint16_t num_of_data_segments));
CMOCK_MOCK_FUNCTION2(NanoShmemIPCMocker, isCorruptedShmem, int(SharedMemoryIPC *ipc, int));
#endif // __MOCK_SHMEM_IPC__

View File

@@ -0,0 +1,589 @@
#include "nano_attachment.h"
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <sys/un.h>
#include <sys/socket.h>
#include "nano_attachment_sender.h"
#include "nano_attachment_metric.h"
#include "nano_initializer.h"
#include "nano_configuration.h"
#include "nano_utils.h"
#include "attachment_types.h"
#include "nano_blockpage.h"
NanoAttachment *
InitNanoAttachment(uint8_t attachment_type, int worker_id, int num_of_workers, int logging_fd)
{
// NanoAttachment *attachment = malloc(sizeof(NanoAttachment));
NanoAttachment *attachment = calloc(1, sizeof(NanoAttachment));
if (attachment == NULL) {
return NULL;
}
// memset(attachment, 0, sizeof(NanoAttachment));
attachment->shared_verdict_signal_path[0] = '\0';
attachment->worker_id = worker_id;
attachment->num_of_workers = num_of_workers;
attachment->nano_user_id = getuid();
attachment->nano_group_id = getgid();
attachment->registration_socket = -1;
attachment->registration_state = NOT_REGISTERED;
attachment->attachment_type = attachment_type;
attachment->nano_service_ipc = NULL;
attachment->comm_socket = -1;
attachment->logging_data = NULL;
if (set_docker_id(attachment) == NANO_ERROR) {
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Could not evaluate container id");
close_logging_fd(attachment);
free(attachment);
return NULL;
}
if (set_logging_fd(attachment, logging_fd) == NANO_ERROR) {
free(attachment);
return NULL;
}
attachment->logging_data = initLoggingData(
attachment->logging_fd,
DBG_LEVEL_INFO,
attachment->worker_id
);
if (attachment->logging_data == NULL) {
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Failed to initialize logging data");
return NULL;
}
if (set_unique_id(attachment) == NANO_ERROR) {
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Could not evaluate unique name");
close_logging_fd(attachment);
free(attachment);
return NULL;
}
attachment->is_configuration_updated = NANO_ERROR;
attachment->current_config_version = 0;
attachment->fail_mode_verdict = NANO_OK;
attachment->fail_mode_delayed_verdict = NANO_OK;
attachment->dbg_level = DBG_LEVEL_INFO;
attachment->num_of_connection_attempts = 0;
attachment->fail_open_timeout = 50;
attachment->fail_open_delayed_timeout = 150;
attachment->sessions_per_minute_limit_verdict = ATTACHMENT_VERDICT_ACCEPT;
attachment->max_sessions_per_minute = 0;
attachment->req_max_proccessing_ms_time = 3000;
attachment->res_max_proccessing_ms_time = 3000;
attachment->registration_thread_timeout_msec = 100;
attachment->req_start_thread_timeout_msec = 100;
attachment->req_header_thread_timeout_msec = 100;
attachment->req_body_thread_timeout_msec = 150;
attachment->res_header_thread_timeout_msec = 100;
attachment->res_body_thread_timeout_msec = 150;
attachment->waiting_for_verdict_thread_timeout_msec = 150;
attachment->metric_timeout_timeout = 100;
attachment->inspection_mode = NON_BLOCKING_THREAD;
attachment->num_of_nano_ipc_elements = 200;
attachment->keep_alive_interval_msec = DEFAULT_KEEP_ALIVE_INTERVAL_MSEC;
if (nano_attachment_init_process(attachment) != NANO_OK) {
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Could not initialize nano attachment");
close_logging_fd(attachment);
free(attachment);
return NULL;
}
reset_metric_data(attachment);
return attachment;
};
void
FiniNanoAttachment(NanoAttachment *attachment)
{
close_logging_fd(attachment);
free(attachment);
};
NanoCommunicationResult
RestartAttachmentConfiguration(NanoAttachment *attachment)
{
return reset_attachment_config(attachment);
};
HttpSessionData *
InitSessionData(NanoAttachment *attachment, SessionID session_id)
{
HttpSessionData *session_data = malloc(sizeof(HttpSessionData));
if (session_data == NULL) {
return NULL;
}
write_dbg(
attachment,
session_id,
DBG_LEVEL_TRACE,
"Initiating session data"
);
session_data->was_request_fully_inspected = 0;
session_data->verdict = TRAFFIC_VERDICT_INSPECT;
session_data->session_id = session_id;
session_data->remaining_messages_to_reply = 0;
session_data->req_proccesing_time = 0;
session_data->res_proccesing_time = 0;
session_data->processed_req_body_size = 0;
session_data->processed_res_body_size = 0;
return session_data;
};
void
FiniSessionData(NanoAttachment *attachment, HttpSessionData *session_data)
{
write_dbg(
attachment,
session_data->session_id,
DBG_LEVEL_DEBUG,
"Freeing session data for session_id"
);
free(session_data);
};
void
UpdateMetric(NanoAttachment *attachment, AttachmentMetricType metric_type, uint64_t value)
{
updateMetricField(attachment, metric_type, value);
}
void
SendAccumulatedMetricData(NanoAttachment *attachment)
{
SendMetricData(attachment);
}
AttachmentVerdictResponse
SendDataNanoAttachment(NanoAttachment *attachment, AttachmentData *data)
{
switch (data->chunk_type) {
case HTTP_REQUEST_FILTER: {
return SendRequestFilter(attachment, data);
}
case HTTP_REQUEST_METADATA: {
return SendMetadata(attachment, data);
}
case HTTP_REQUEST_HEADER: {
return SendRequestHeaders(attachment, data);
}
case HTTP_REQUEST_BODY: {
return SendRequestBody(attachment, data);
}
case HTTP_REQUEST_END: {
return SendRequestEnd(attachment, data);
}
case HTTP_RESPONSE_HEADER: {
return SendResponseHeaders(attachment, data);
}
case HTTP_RESPONSE_BODY: {
return SendResponseBody(attachment, data);
}
case HTTP_RESPONSE_END: {
return SendResponseEnd(attachment, data);
}
default:
break;
}
AttachmentVerdictResponse response = {
.verdict = ATTACHMENT_VERDICT_INSPECT,
.session_id = data->session_id,
.modifications = NULL
};
return response;
}
// LCOV_EXCL_START Reason: Simple wrapper.
AttachmentVerdictResponse SendDataNanoAttachmentWrapper(NanoAttachment *attachment, AttachmentData data)
{
return SendDataNanoAttachment(attachment, &data);
}
// LCOV_EXCL_STOP
///
/// @brief Connects to the keep-alive socket.
///
/// @param attachment A pointer to a NanoAttachment struct containing attachment information.
///
/// @return An int representing an opened socket, if failed returns -1.
///
static int
connect_to_keep_alive_socket(NanoAttachment *attachment)
{
struct sockaddr_un server;
int keep_alive_socket;
// Connect a new socket.
keep_alive_socket = socket(AF_UNIX, SOCK_STREAM, 0);
if (keep_alive_socket < 0) {
write_dbg(
attachment,
attachment->worker_id,
DBG_LEVEL_WARNING,
"Could not create socket, Error: %s",
strerror(errno)
);
return keep_alive_socket;
}
server.sun_family = AF_UNIX;
strncpy(server.sun_path, SHARED_KEEP_ALIVE_PATH, sizeof(server.sun_path) - 1);
if (connect(keep_alive_socket, (struct sockaddr *)&server, sizeof(struct sockaddr_un)) != -1 ) {
return keep_alive_socket;
}
write_dbg(
attachment,
attachment->worker_id,
DBG_LEVEL_DEBUG,
"Could not connect to nano service. Path: %s, Error: %s, Errno: %d",
server.sun_path,
strerror(errno),
errno
);
close(keep_alive_socket);
return -1;
}
///
/// @brief Sends the keep-alive signal to the alive socket.
///
/// @param attachment A pointer to a NanoAttachment struct containing attachment information.
/// @param keep_alive_socket Socket descriptor for the keep-alive socket.
///
/// @return A NanoCommunicationResult indicating the result of the communication operation.
///
static NanoCommunicationResult
send_keep_alive_to_alive_socket(NanoAttachment *attachment, int keep_alive_socket)
{
uint8_t container_id_size = strlen(attachment->container_id);
struct timeval timeout = get_absolute_timeout_val_sec(1);
NanoCommunicationResult res;
// Exchanging worker id with the nano service.
res = write_to_service(
attachment,
&keep_alive_socket,
&attachment->worker_id,
sizeof(attachment->worker_id),
&timeout
);
if (res != NANO_OK) {
// Failed to send worker id
write_dbg(attachment, attachment->worker_id, DBG_LEVEL_WARNING, "Failed to send worker id");
return NANO_ERROR;
}
// Exchanging container id size with the nano service.
res = write_to_service(
attachment,
&keep_alive_socket,
&container_id_size,
sizeof(container_id_size),
&timeout
);
if (res != NANO_OK) {
// Failed to send container id size.
write_dbg(attachment, attachment->worker_id, DBG_LEVEL_WARNING, "Failed to send container id size");
return NANO_ERROR;
}
if (container_id_size > 0) {
// Exchanging container id with the nano service.
res = write_to_service(
attachment,
&keep_alive_socket,
attachment->container_id,
container_id_size,
&timeout
);
if (res != NANO_OK) {
// Failed to send container id name.
write_dbg(attachment, attachment->worker_id, DBG_LEVEL_WARNING, "Failed to send container id");
return NANO_ERROR;
}
}
return NANO_OK;
}
void
SendKeepAlive(NanoAttachment *attachment)
{
int keep_alive_socket;
NanoCommunicationResult res;
write_dbg(
attachment,
attachment->worker_id,
DBG_LEVEL_DEBUG,
"Keep alive signal. Family id: %s, UID: %u",
attachment->container_id,
attachment->worker_id
);
keep_alive_socket = connect_to_keep_alive_socket(attachment);
if (keep_alive_socket < 0) {
write_dbg(attachment, attachment->worker_id, DBG_LEVEL_WARNING, "Failed to connect to keep alive socket");
return;
}
write_dbg(
attachment,
attachment->worker_id,
DBG_LEVEL_DEBUG,
"connected to socket: %d. sending keep alive signals"
);
res = send_keep_alive_to_alive_socket(attachment, keep_alive_socket);
if (res == NANO_ERROR) {
write_dbg(attachment, attachment->worker_id, DBG_LEVEL_WARNING, "Failed to send keep alive data");
}
close(keep_alive_socket);
}
int
IsSessionFinalized(NanoAttachment *attachment, HttpSessionData *session_data)
{
if (session_data->verdict == TRAFFIC_VERDICT_INSPECT) {
write_dbg(
attachment,
attachment->worker_id,
DBG_LEVEL_TRACE,
"Inspecting data for session id: %d",
session_data->session_id
);
return 0;
}
write_dbg(
attachment,
attachment->worker_id,
DBG_LEVEL_TRACE,
"Skipping already inspected for session id: %d",
session_data->session_id
);
return 1;
}
NanoWebResponseType
GetWebResponseType(
NanoAttachment *attachment,
HttpSessionData *session_data,
AttachmentVerdictResponse *response
)
{
if (response->web_response_data == NULL) {
write_dbg(
attachment,
session_data->session_id,
DBG_LEVEL_WARNING,
"Trying to get web response with no response object"
);
return NO_WEB_RESPONSE;
}
return response->web_response_data->web_response_type;
}
int
IsResponseWithModification(
NanoAttachment *attachment,
HttpSessionData *session_data,
AttachmentVerdictResponse *response
)
{
int res = response->modifications != NULL;
write_dbg(
attachment,
session_data->session_id,
DBG_LEVEL_TRACE,
"Response %s have modifications",
res ? "does" : "does not"
);
return res;
}
NanoResponseModifications
GetResponseModifications(
NanoAttachment *attachment,
HttpSessionData *session_data,
AttachmentVerdictResponse *response
)
{
if (response == NULL) {
write_dbg(
attachment,
session_data->session_id,
DBG_LEVEL_WARNING,
"Trying to get modifications with no response object"
);
return (NanoResponseModifications) {
.modifications = NULL
};
}
return (NanoResponseModifications) {
.modifications = response->modifications
};
}
BlockPageData
GetBlockPage(NanoAttachment *attachment, HttpSessionData *session_data, AttachmentVerdictResponse *response)
{
WebResponseData *web_response_data = response->web_response_data;
CustomResponseData *custom_response_data;
if (web_response_data->web_response_type != CUSTOM_WEB_RESPONSE) {
write_dbg(
attachment,
session_data->session_id,
DBG_LEVEL_WARNING,
"Trying to generate custom block page with a non custom response object"
);
return (BlockPageData) {
.response_code = 0,
.title_prefix = { .len = 0, .data = NULL },
.title = { .len = 0, .data = NULL },
.body_prefix = { .len = 0, .data = NULL },
.body = { .len = 0, .data = NULL },
.uuid_prefix = { .len = 0, .data = NULL },
.uuid = { .len = 0, .data = NULL },
.uuid_suffix = { .len = 0, .data = NULL }
};
}
write_dbg(
attachment,
session_data->session_id,
DBG_LEVEL_TRACE,
"Getting custom block page"
);
custom_response_data = (CustomResponseData *) web_response_data->data;
return (BlockPageData) {
.response_code = custom_response_data->response_code,
.title_prefix = { .len = strlen(title_prefix), .data = (unsigned char *)title_prefix },
.title = { .len = strlen((char *)custom_response_data->title), .data = custom_response_data->title },
.body_prefix = { .len = strlen(body_prefix), .data = (unsigned char *)body_prefix },
.body = { .len = strlen((char *)custom_response_data->body), .data = custom_response_data->body },
.uuid_prefix = { .len = strlen(uuid_prefix), .data = (unsigned char *)uuid_prefix },
.uuid = { .len = strlen((char *)web_response_data->uuid), .data = web_response_data->uuid },
.uuid_suffix = { .len = strlen(uuid_suffix), .data = (unsigned char *)uuid_suffix }
};
}
RedirectPageData
GetRedirectPage(NanoAttachment *attachment, HttpSessionData *session_data, AttachmentVerdictResponse *response)
{
WebResponseData *web_response_data = response->web_response_data;
RedirectData *redirect_data;
if (web_response_data->web_response_type != REDIRECT_WEB_RESPONSE) {
write_dbg(
attachment,
session_data->session_id,
DBG_LEVEL_WARNING,
"Trying to generate custom block page with a non redirect response object"
);
return (RedirectPageData) {
.redirect_location = { .len = 0, .data = NULL }
};
}
write_dbg(
attachment,
session_data->session_id,
DBG_LEVEL_TRACE,
"Getting redirect data"
);
redirect_data = (RedirectData *) web_response_data->data;
return (RedirectPageData) {
.redirect_location = {
.len = strlen((char*)redirect_data->redirect_location),
.data = redirect_data->redirect_location
}
};
}
void
FreeAttachmentResponseContent(
NanoAttachment *attachment,
HttpSessionData *session_data,
AttachmentVerdictResponse *response
)
{
NanoHttpModificationList *current_modification;
NanoHttpModificationList *modification_list;
if (response == NULL) {
write_dbg(
attachment,
session_data->session_id,
DBG_LEVEL_WARNING,
"Attempting to free NULL response"
);
return;
}
write_dbg(
attachment,
session_data->session_id,
DBG_LEVEL_TRACE,
"Freeing AttachmentResponse object"
);
if (response->web_response_data != NULL) {
write_dbg(
attachment,
session_data->session_id,
DBG_LEVEL_TRACE,
"Freeing custom web response data"
);
free(response->web_response_data->data);
free(response->web_response_data);
response->web_response_data = NULL;
}
if (response->modifications != NULL) {
modification_list = response->modifications;
while (modification_list) {
write_dbg(
attachment,
session_data->session_id,
DBG_LEVEL_TRACE,
"Freeing modifications list"
);
current_modification = modification_list;
modification_list = modification_list->next;
free(current_modification);
}
response->modifications = NULL;
}
return;
}

View File

@@ -0,0 +1,217 @@
#ifndef __NANO_ATTACHMENT_H__
#define __NANO_ATTACHMENT_H__
#include "nano_attachment_common.h"
#include "nano_initializer.h"
///
/// @brief Initializes a NanoAttachment structure.
///
/// This function initializes a NanoAttachment structure with the specified parameters and default values.
///
/// @param attachment_type The type of attachment to initialize.
/// @param worker_id The ID of the worker associated with the attachment.
/// @param num_of_workers The total number of workers.
/// @param logging_fd The file descriptor for logging.
///
/// @return A pointer to the initialized NanoAttachment structure if the function completes, NULL otherwise.
///
NanoAttachment * InitNanoAttachment(uint8_t attachment_type, int worker_id, int num_of_workers, int logging_fd);
///
/// @brief Cleans up resources associated with a NanoAttachment structure and deallocates memory.
///
/// This function performs cleanup operations on a NanoAttachment structure and deallocates
/// the memory associated with it.
/// The function closes the logging file descriptor associated with the NanoAttachment
/// and frees the memory allocated for the structure.
///
/// @param attachment A pointer to the NanoAttachment structure to be cleaned up.
///
void FiniNanoAttachment(NanoAttachment *attachment);
///
/// @brief Restarts the configuration of a NanoAttachment.
///
/// @param attachment A pointer to the NanoAttachment whose configuration is to be restarted.
///
/// @return A NanoCommunicationResult indicating the success or failure of the operation.
NanoCommunicationResult RestartAttachmentConfiguration(NanoAttachment *attachment);
///
/// @brief Initializes a HttpSessionData structure with default values.
///
/// This function dynamically allocates memory for a HttpSessionData structure
/// and initializes its fields with default values.
///
/// @param attachment A pointer to the NanoAttachment structure associated with the session.
/// @param session_id The ID of the session to be initialized.
///
/// @return A pointer to the initialized HttpSessionData structure if the function completes, NULL otherwise.
///
HttpSessionData * InitSessionData(NanoAttachment *attachment, SessionID session_id);
///
/// @brief Cleans up and deallocates resources associated with a HttpSessionData structure.
///
/// This function performs cleanup operations on a HttpSessionData structure and deallocates
/// the memory associated with it. It writes a debug message indicating the session ID being
/// freed, and then frees the memory allocated for the HttpSessionData structure.
///
/// @param attachment A pointer to the NanoAttachment structure associated with the session.
/// @param session_data A pointer to the HttpSessionData structure to be cleaned up.
///
void FiniSessionData(NanoAttachment *attachment, HttpSessionData *session_data);
///
/// @brief Updates a metric associated with a NanoAttachment.
///
/// This function updates a metric associated with a NanoAttachment structure
/// based on the provided metric type and value. It delegates the actual updating
/// of the metric to the helper function updateMetricField.
///
/// @param attachment A pointer to the NanoAttachment structure associated with the metric.
/// @param metric The type of metric to be updated.
/// @param value The value to be incorporated into the metric calculation.
///
void UpdateMetric(NanoAttachment *attachment, AttachmentMetricType metric, uint64_t value);
///
/// @brief Sends metric data that been accumulated in the attachment to the service.
///
/// @param attachment A pointer to the NanoAttachment structure associated with the metric.
///
void SendAccumulatedMetricData(NanoAttachment *attachment);
///
/// @brief Processes and sends attachment data to the appropriate handlers.
///
/// This function processes the attachment data based on its chunk type and sends
/// it to the appropriate handler functions. If the chunk type is not recognized,
/// it sets a default verdict of ATTACHMENT_VERDICT_INSPECT and returns an AttachmentVerdictResponse
/// structure containing the default verdict and the session ID from the provided AttachmentData.
///
/// @param attachment A pointer to the NanoAttachment structure associated with the data.
/// @param data A pointer to the AttachmentData structure containing the data to be processed.
///
/// @return An AttachmentVerdictResponse structure containing the verdict and session ID.
///
AttachmentVerdictResponse SendDataNanoAttachment(NanoAttachment *attachment, AttachmentData *data);
AttachmentVerdictResponse SendDataNanoAttachmentWrapper(NanoAttachment *attachment, AttachmentData data);
///
/// @brief Sends a keep-alive signal using a socket connection.
///
/// @param attachment A pointer to a NanoAttachment struct containing attachment information.
///
void SendKeepAlive(NanoAttachment *attachment);
///
/// @brief Checks if a session is finalized based on the session's verdict.
///
/// @param attachment The NanoAttachment object associated with the session.
/// @param session_data The HttpSessionData object representing the session.
///
/// @return Returns 0 if the session is not finalized, 1 otherwise.
///
int IsSessionFinalized(NanoAttachment *attachment, HttpSessionData *session_data);
///
/// @brief Checks if the response contains modifications.
///
/// This function determines whether the provided response contains modifications.
///
/// @param attachment A pointer to a NanoAttachment structure representing the attachment.
/// @param session_data A pointer to a HttpSessionData structure containing session data.
/// @param response A pointer to an AttachmentVerdictResponse structure representing the response.
///
/// @return 1 if the response contains modifications, 0 otherwise.
///
int IsResponseWithModification(
NanoAttachment *attachment,
HttpSessionData *session_data,
AttachmentVerdictResponse *response
);
///
/// @brief Retrieves response modifications from the given attachment and session data.
///
/// @param attachment Pointer to a NanoAttachment object.
/// @param session_data Pointer to HttpSessionData object containing session information.
/// @param response Pointer to an AttachmentVerdictResponse object.
///
/// @return NanoResponseModifications structure containing response modifications.
///
NanoResponseModifications GetResponseModifications(
NanoAttachment *attachment,
HttpSessionData *session_data,
AttachmentVerdictResponse *response
);
///
/// @brief Retrieves the type of web response associated with the given attachment and session data.
///
/// This function checks if the provided response object contains valid web response data.
/// If the response object is null, it logs a warning and returns NO_WEB_RESPONSE.
/// Otherwise, it returns the type of web response contained in the response object.
///
/// @param attachment Pointer to the NanoAttachment structure associated with the request.
/// @param session_data Pointer to the HttpSessionData structure containing session-related data.
/// @param response Pointer to the AttachmentVerdictResponse structure containing response data.
///
/// @return The type of web response, or NO_WEB_RESPONSE if no response object is provided.
///
NanoWebResponseType GetWebResponseType(
NanoAttachment *attachment,
HttpSessionData *session_data,
AttachmentVerdictResponse *response
);
///
/// @brief Retrieves the block page data for a response.
///
/// @param attachment The NanoAttachment object associated with the session.
/// @param session_data The HttpSessionData object representing the session.
/// @param response The AttachmentVerdictResponse object containing the verdict.
///
/// @return
///
BlockPageData GetBlockPage(
NanoAttachment *attachment,
HttpSessionData *session_data,
AttachmentVerdictResponse *response
);
///
/// @brief Retrieves the redict page data for a response.
///
/// @param attachment The NanoAttachment object associated with the session.
/// @param session_data The HttpSessionData object representing the session.
/// @param response The AttachmentVerdictResponse object containing the verdict.
///
/// @return
///
RedirectPageData GetRedirectPage(
NanoAttachment *attachment,
HttpSessionData *session_data,
AttachmentVerdictResponse *response
);
///
/// @brief Free allocated resources of an AttachmentVerdictResponse.
///
/// This function frees the allocated resources of an AttachmentVerdictResponse.
///
/// @param attachment The NanoAttachment object associated with the session.
/// @param session_data The HttpSessionData object representing the session.
/// @param response The AttachmentVerdictResponse object to be freed.
///
void FreeAttachmentResponseContent(
NanoAttachment *attachment,
HttpSessionData *session_data,
AttachmentVerdictResponse *response
);
#endif // __NANO_ATTACHMENT_H__

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,230 @@
#ifndef __NANO_ATTACHMENT_IO_H__
#define __NANO_ATTACHMENT_IO_H__
#include <unistd.h>
#include "nano_attachment_common.h"
#include "nano_initializer.h"
#include "nano_attachment_sender_thread.h"
#include "shmem_ipc_2.h"
/// @brief Sends session data chunk to a nano service for inspection.
///
/// This function sends the provided data fragments to the nano service for inspection.
///
/// @param attachment A pointer to a NanoAttachment structure representing the attachment to the nano service.
/// @param fragments An array of pointers to character arrays representing the data fragments to send.
/// @param fragments_sizes An array of uint16_t values representing the sizes of the data fragments.
/// @param num_of_data_elem An 8-bit integer representing the number of data elements (fragments) to send.
/// @param cur_session_id An unsigned 32-bit integer representing the current session ID.
/// @param chunk_type An enumeration representing the type of data chunk being sent.
///
/// @return NANO_OK if the data is sent successfully, NANO_ERROR otherwise.
///
NanoCommunicationResult
send_session_data_to_service(
NanoAttachment *attachment,
char **fragments,
const uint16_t *fragments_sizes,
uint8_t num_of_data_elem,
uint32_t cur_session_id,
AttachmentDataType chunk_type
);
///
/// @brief Connect to the communication socket.
///
/// This function creates a new socket and connects it to the verdict
/// Unix domain socket address. If the attachment already has a communication
/// socket open, it is closed before creating a new one.
///
/// @param[in] attachment The NanoAttachment struct containing socket information.
/// @returns A NanoCommunicationResult indicating the success of the operation.
///
NanoCommunicationResult connect_to_comm_socket(NanoAttachment *attachment);
///
/// @brief Create an unix socket and connect to the attachment registration service.
/// @param[in] attachment Points to initiated NanoAttachment struct.
/// @returns NanoCommunicationResult
/// - #NANO_OK
/// - #NANO_ERROR
///
NanoCommunicationResult connect_to_registration_socket(NanoAttachment *attachment);
/// @brief Receives and processes replies from a nano service regarding traffic inspection verdicts.
///
/// This function waits for replies from the service and handles each reply based on the verdict received.
///
/// @param attachment A pointer to a NanoAttachment structure representing the attachment to the nano service.
/// @param session_data A pointer to a HttpSessionData which holds the session data.
/// @param web_response_data A pointer to a WebResponseData structure representing the response data.
/// @param modification_list A pointer to a pointer to a NanoHttpModificationList structure
/// representing a list of HTTP modifications.
///
/// @return NANO_OK if the function completes successfully
/// NANO_ERROR if an error occurs during processing
/// NANO_HTTP_FORBIDDEN if a drop verdict is received.
///
NanoCommunicationResult
service_reply_receiver(
NanoAttachment *attachment,
HttpSessionData *session_data,
WebResponseData **web_response_data,
NanoHttpModificationList **modification_list,
AttachmentDataType chunk_type
);
///
/// @brief Sends request start metadata for inspection to the nano service.
///
/// @param attachment Pointer to the NanoAttachment struct representing the attachment.
/// @param metadata Pointer to the HttpMetaData struct containing the HTTP metadata.
/// @param ctx Pointer to the HttpEventThreadCtx struct containing the HTTP event context.
/// @param cur_request_id The current request ID.
/// @param num_of_messages_sent Pointer to an unsigned int to store the number of messages sent.
/// @param is_verdict_requested Boolean value indicating if a verdict is requested.
///
void
nano_metadata_sender(
NanoAttachment *attachment,
HttpMetaData *metadata,
HttpEventThreadCtx *ctx,
uint32_t cur_request_id,
unsigned int *num_of_messages_sent,
bool is_verdict_requested
);
///
/// @brief Sends a response code for inspection.
///
/// This function sends a response code for inspection to a service.
///
/// @param attachment A pointer to the NanoAttachment structure.
/// @param response_code The response code to send.
/// @param ctx The HttpEventThreadCtx context.
/// @param cur_request_id The current request ID.
/// @param num_messages_sent A pointer to the number of messages sent.
///
void
nano_send_response_code(
NanoAttachment *attachment,
uint16_t response_code,
HttpEventThreadCtx *ctx,
uint32_t cur_request_id,
unsigned int *num_messages_sent
);
///
/// @brief Sends the content length to the intaker.
///
/// This function sends the content length to the intaker for processing.
///
/// @param attachment A pointer to the NanoAttachment structure.
/// @param content_length The content length to send.
/// @param ctx The HttpEventThreadCtx context.
/// @param cur_request_id The current request ID.
/// @param num_messages_sent A pointer to the number of messages sent.
///
void
nano_send_response_content_length(
NanoAttachment *attachment,
uint64_t content_length,
HttpEventThreadCtx *ctx,
uint32_t cur_request_id,
unsigned int *num_messages_sent
);
///
/// @brief Sends HTTP headers for inspection using a NanoAttachment.
///
/// This function takes a NanoAttachment pointer, an HttpHeaders struct containing the headers to send,
/// the type of the headers (request or response), the current request ID, and a pointer to store
/// the number of messages sent.
///
/// @param attachment Pointer to the NanoAttachment struct representing the attachment.
/// @param headers Pointer to the HttpHeaders struct containing the headers to send.
/// @param ctx Pointer to the HttpEventThreadCtx struct containing the context of the current thread.
/// @param header_type Type of the headers (REQUEST_HEADER or RESPONSE_HEADER).
/// @param cur_request_id Current request ID.
/// @param num_messages_sent Pointer to an unsigned int to store the number of messages sent.
///
void
nano_header_sender(
NanoAttachment *attachment,
HttpHeaders *headers,
HttpEventThreadCtx *ctx,
AttachmentDataType header_type,
uint32_t cur_request_id,
unsigned int *num_messages_sent,
bool is_verdict_requested
);
///
/// @brief Sends the body of a request or response for inspection to a nano service.
///
/// This function iterates over the body chunks, creates fragments, and sends them
/// in bulk to the service. It also handles the final chunk and updates the number
/// of messages sent.
///
/// @param attachment Pointer to a NanoAttachment struct representing the attachment/module.
/// @param bodies Pointer to an HttpBody struct containing the HTTP request/response body data.
/// @param ctx Pointer to an HttpEventThreadCtx struct representing the HTTP event thread context.
/// @param body_type Enum value indicating whether the body is a request or response body.
/// @param cur_request_id Current request ID for logging and tracking purposes.
/// @param num_messages_sent Pointer to an unsigned int to track the number of messages sent.
///
void
nano_body_sender(
NanoAttachment *attachment,
HttpBody *bodies,
HttpEventThreadCtx *ctx,
AttachmentDataType body_type,
uint32_t cur_request_id,
unsigned int *num_messages_sent
);
///
/// @brief Sends an end transaction event to a service for inspection.
///
/// @param attachment The NanoAttachment struct representing the attachment/module.
/// @param end_transaction_type The type of end transaction event (REQUEST_END or RESPONSE_END).
/// @param ctx Pointer to the HttpEventThreadCtx struct containing the context of the current thread.
/// @param cur_request_id The ID of the current request.
/// @param num_messages_sent Pointer to an unsigned integer to store the number of messages sent.
/// @return NANO_OK if the end transaction event was sent successfully, NANO_ERROR otherwise.
///
void
nano_end_transaction_sender(
NanoAttachment *attachment,
AttachmentDataType end_transaction_type,
HttpEventThreadCtx *ctx,
SessionID cur_request_id,
unsigned int *num_messages_sent
);
///
/// @brief Sends delayed transaction event to a service for inspection.
///
/// @param attachment The NanoAttachment struct representing the attachment/module.
/// @param ctx Pointer to the HttpEventThreadCtx struct containing the context of the current thread.
/// @param cur_request_id The ID of the current request.
/// @param num_messages_sent Pointer to an unsigned integer to store the number of messages sent.
///
void
nano_request_delayed_verdict(
NanoAttachment *attachment,
HttpEventThreadCtx *ctx,
SessionID cur_request_id,
unsigned int *num_messages_sent
);
///
/// @brief Sends attachment's metric data to the service.
///
/// @param attachment The NanoAttachment struct representing the attachment/module, which contains the metric data.
///
void
nano_send_metric_data_sender(NanoAttachment *attachment);
#endif // __NANO_ATTACHMENT_IO_H__

View File

@@ -0,0 +1,83 @@
#include "nano_attachment_metric.h"
#include "nano_initializer.h"
#include "nano_attachment_common.h"
void
reset_metric_data(NanoAttachment *attachment)
{
int i;
for (i = 0 ; i < METRIC_TYPES_COUNT ; i++) {
attachment->metric_data[i] = 0;
attachment->metric_average_data_divisor[i] = 0;
}
}
static void
updateCounterMetricField(NanoAttachment *attachment, AttachmentMetricType metric_type, uint64_t value)
{
attachment->metric_data[metric_type] += value;
}
static void
updateAverageMetricField(NanoAttachment *attachment, AttachmentMetricType metric_type, uint64_t value)
{
attachment->metric_data[metric_type] =
(((attachment->metric_data[metric_type] * attachment->metric_average_data_divisor[metric_type]) + value) /
(attachment->metric_average_data_divisor[metric_type] + 1));
attachment->metric_average_data_divisor[metric_type] += 1;
}
static void
updateMaxMetricField(NanoAttachment *attachment, AttachmentMetricType metric_type, uint64_t value)
{
if (attachment->metric_data[metric_type] < value) attachment->metric_data[metric_type] = value;
}
static void
updateMinMetricField(NanoAttachment *attachment, AttachmentMetricType metric_type, uint64_t value)
{
if (attachment->metric_data[metric_type] == 0) {
attachment->metric_data[metric_type] = value;
} else if (attachment->metric_data[metric_type] > value) {
attachment->metric_data[metric_type] = value;
}
}
void
updateMetricField(NanoAttachment *attachment, AttachmentMetricType 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(attachment, 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(attachment, 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(attachment, metric_type, value);
break;
}
default:
updateCounterMetricField(attachment, metric_type, value);
}
}

View File

@@ -0,0 +1,33 @@
#ifndef __NANO_ATTACHMENT_METRIC_H__
#define __NANO_ATTACHMENT_METRIC_H__
#include "nano_attachment_common.h"
#include "nano_initializer.h"
///
/// @brief Updates a specified metric field of the NanoAttachment structure.
///
/// This function updates the value of a specified metric field within the NanoAttachment structure.
/// It selects the appropriate update strategy (counter, average, maximum, or minimum) based on the type
/// of the metric provided. For average, maximum, and minimum metrics, the function updates the metric value
/// only if the provided value is non-zero. For counter metrics, the value is always incremented.
///
/// @param attachment A pointer to the NanoAttachment structure.
/// @param metric_type The type of the metric to be updated. This determines the update strategy used.
/// @param value The value to update the metric with. For average, maximum, and minimum metrics,
/// this value must be non-zero to be considered.
///
void updateMetricField(NanoAttachment *attachment, AttachmentMetricType metric_type, uint64_t value);
///
/// @brief Resets all metric data fields of the NanoAttachment structure.
///
/// This function resets the metric data and the average data divisor fields within the NanoAttachment structure
/// to zero. It is typically used to initialize or clear the metric data before starting a new measurement session
/// or after completing an existing session.
///
/// @param attachment A pointer to the NanoAttachment structure.
///
void reset_metric_data(NanoAttachment *attachment);
#endif // __NANO_ATTACHMENT_METRIC_H__

View File

@@ -0,0 +1,795 @@
#include "nano_attachment_sender.h"
#include <time.h>
#include <stdlib.h>
#include "nano_attachment_sender_thread.h"
#include "nano_attachment_thread.h"
#include "nano_utils.h"
#include "nano_attachment_metric.h"
static unsigned char default_uuid[] = "20118dba-81f7-4999-8e94-003cf242f5dd\0";
static const size_t default_uuid_size = 37;
static unsigned char default_title[] = "Default Title\0";
static const size_t default_title_size = 14;
static unsigned char default_body[] = "Default Body\0";
static const size_t default_body_size = 13;
static uint16_t default_response_code = 403;
///
/// @brief Creates a default block page.
///
/// @param attachment Pointer to the NanoAttachment struct representing the attachment.
/// @param session_id The session ID associated with the attachment.
/// @return A pointer to a WebResponseData struct containing the default block page data.
///
static WebResponseData*
CreateDefaultBlockPage(NanoAttachment *attachment, SessionID session_id)
{
WebResponseData *web_response_data = NULL;
CustomResponseData *custom_response_data = NULL;
web_response_data = (WebResponseData *)malloc(sizeof(WebResponseData));
if (web_response_data == NULL) {
write_dbg(
attachment,
session_id,
DBG_LEVEL_WARNING,
"Failed to allocate memory for WebResponseData"
);
return NULL;
}
custom_response_data = (CustomResponseData *)malloc(sizeof(CustomResponseData));
if (custom_response_data == NULL) {
write_dbg(
attachment,
session_id,
DBG_LEVEL_WARNING,
"Failed to allocate memory for CustomResponseData"
);
free(web_response_data);
return NULL;
}
web_response_data->web_response_type = CUSTOM_WEB_RESPONSE;
memcpy(web_response_data->uuid, default_uuid, default_uuid_size);
custom_response_data->response_code = default_response_code;
memcpy(custom_response_data->title, default_title, default_title_size);
memcpy(custom_response_data->body, default_body, default_body_size);
web_response_data->data = (DataBuffer*)custom_response_data;
return web_response_data;
}
///
/// @brief Get a string representation of the AttachmentVerdict enum.
///
/// @param verdict The AttachmentVerdict enum value.
/// @return A string representation of the enum value.
///
static const char*
AttachmentVerdictToString(AttachmentVerdict verdict)
{
switch (verdict) {
case ATTACHMENT_VERDICT_INSPECT:
return "inspect";
case ATTACHMENT_VERDICT_ACCEPT:
return "accept";
case ATTACHMENT_VERDICT_DROP:
return "drop";
case ATTACHMENT_VERDICT_INJECT:
return "inject";
default:
return "unknown";
}
}
///
/// @brief Sends a verdict response for a corrupt memory condition.
///
/// @param attachment Pointer to the NanoAttachment struct representing the attachment.
/// @param session_data_p Pointer to the HttpSessionData struct containing the session data.
/// @return An AttachmentVerdictResponse struct containing the session ID and verdict.
///
static AttachmentVerdictResponse
SendCorruptMemoryVerdict(NanoAttachment *attachment, HttpSessionData *session_data_p)
{
AttachmentVerdictResponse response = {
.session_id = session_data_p->session_id,
.web_response_data = NULL,
.modifications = NULL
};
if (attachment->fail_mode_verdict == NANO_OK) {
updateMetricField(attachment, INSPECTION_OPEN_FAILURES_COUNT, 1);
response.verdict = ATTACHMENT_VERDICT_ACCEPT;
session_data_p->verdict = TRAFFIC_VERDICT_ACCEPT;
} else {
updateMetricField(attachment, INSPECTION_CLOSE_FAILURES_COUNT, 1);
response.verdict = ATTACHMENT_VERDICT_DROP;
session_data_p->verdict = TRAFFIC_VERDICT_DROP;
response.web_response_data = CreateDefaultBlockPage(attachment, session_data_p->session_id);
}
write_dbg(
attachment,
response.session_id,
DBG_LEVEL_DEBUG,
"Shared memory is corrupted, returning default fail mode verdict. Session id: %d, verdict: %s",
response.session_id,
response.verdict == ATTACHMENT_VERDICT_ACCEPT ? "accept" : "drop"
);
return response;
}
///
/// @brief Sends a verdict response for a thread timeout condition.
///
/// @param attachment Pointer to the NanoAttachment struct representing the attachment.
/// @param session_id The session ID associated with the attachment.
/// @param ctx Pointer to the HttpEventThreadCtx struct containing the HTTP event context.
/// @return An AttachmentVerdictResponse struct containing the session ID and verdict.
///
static AttachmentVerdictResponse
SendThreadTimeoutVerdict(NanoAttachment *attachment, SessionID session_id, HttpEventThreadCtx *ctx)
{
AttachmentVerdictResponse response = {
.session_id = session_id,
.web_response_data = NULL,
.modifications = NULL
};
if (attachment->fail_mode_verdict == NANO_OK) {
response.verdict = ATTACHMENT_VERDICT_ACCEPT;
ctx->session_data_p->verdict = TRAFFIC_VERDICT_ACCEPT;
} else {
response.verdict = ATTACHMENT_VERDICT_DROP;
ctx->session_data_p->verdict = TRAFFIC_VERDICT_DROP;
response.web_response_data = CreateDefaultBlockPage(attachment, session_id);
}
write_dbg(
attachment,
response.session_id,
DBG_LEVEL_DEBUG,
"Thread failed, returning fail mode verdict. Session id: %d, verdict: %s",
response.session_id,
response.verdict == ATTACHMENT_VERDICT_ACCEPT ? "accept" : "drop"
);
return response;
}
///
/// @brief Finalizes a successful response by determining the verdict based on the HTTP response code.
///
/// @param attachment Pointer to the NanoAttachment struct representing the attachment.
/// @param session_id The session ID associated with the attachment.
/// @param ctx Pointer to the HttpEventThreadCtx struct containing the HTTP event context.
/// @return An AttachmentVerdictResponse struct containing the session ID and verdict.
///
static AttachmentVerdictResponse
FinalizeSuccessfulResponse(
NanoAttachment *attachment,
SessionID session_id,
HttpEventThreadCtx *ctx
)
{
AttachmentVerdictResponse response = {
.session_id = session_id,
.web_response_data = ctx->web_response_data,
.modifications = ctx->modifications
};
switch (ctx->session_data_p->verdict) {
case TRAFFIC_VERDICT_INSPECT:
response.verdict = ATTACHMENT_VERDICT_INSPECT;
break;
case TRAFFIC_VERDICT_ACCEPT:
response.verdict = ATTACHMENT_VERDICT_ACCEPT;
break;
case TRAFFIC_VERDICT_DROP:
response.verdict = ATTACHMENT_VERDICT_DROP;
break;
case TRAFFIC_VERDICT_INJECT:
// Not yet supported
response.verdict = ATTACHMENT_VERDICT_INSPECT;
break;
default:
write_dbg(
attachment,
session_id,
DBG_LEVEL_WARNING,
"Unknown verdict %d",
ctx->session_data_p->verdict
);
response.verdict = ATTACHMENT_VERDICT_INSPECT;
break;
}
updateMetricField(attachment, INSPECTION_SUCCESSES_COUNT, 1);
write_dbg(
attachment,
response.session_id,
DBG_LEVEL_DEBUG,
"Finalizing successful response to Session id: %d, verdict: %s",
response.session_id,
AttachmentVerdictToString(response.verdict)
);
return response;
}
///
/// @brief Finalizes an irrelevant response by setting the verdict to accept.
///
/// @param attachment Pointer to the NanoAttachment struct representing the attachment.
/// @param session_id The session ID associated with the attachment.
/// @return An AttachmentVerdictResponse struct containing the session ID and verdict.
///
static AttachmentVerdictResponse
FinalizeIrrelevantResponse(NanoAttachment *attachment, SessionID session_id)
{
AttachmentVerdictResponse response = {
.verdict = ATTACHMENT_VERDICT_ACCEPT,
.session_id = session_id,
.web_response_data = NULL,
.modifications = NULL
};
updateMetricField(attachment, IRRELEVANT_VERDICTS_COUNT, 1);
write_dbg(
attachment,
response.session_id,
DBG_LEVEL_TRACE,
"Finalizing irrelevant response to Session id: %d",
response.session_id
);
return response;
}
///
/// @brief Finalizes a failed response by determining the verdict
/// based on the fail mode verdict associated with the attachment.
///
/// @param attachment Pointer to the NanoAttachment struct representing the attachment.
/// @param session_id The session ID associated with the attachment.
/// @param ctx Pointer to the HttpEventThreadCtx struct containing the HTTP event context.
/// @return An AttachmentVerdictResponse struct containing the session ID and verdict.
///
static AttachmentVerdictResponse
FinalizeFailedResponse(NanoAttachment *attachment, SessionID session_id, HttpEventThreadCtx *ctx)
{
AttachmentVerdictResponse response = {
.session_id = session_id,
.web_response_data = NULL,
.modifications = NULL
};
if (attachment->fail_mode_verdict == NANO_OK) {
updateMetricField(attachment, INSPECTION_OPEN_FAILURES_COUNT, 1);
response.verdict = ATTACHMENT_VERDICT_ACCEPT;
ctx->session_data_p->verdict = TRAFFIC_VERDICT_ACCEPT;
} else {
updateMetricField(attachment, INSPECTION_CLOSE_FAILURES_COUNT, 1);
response.verdict = ATTACHMENT_VERDICT_DROP;
ctx->session_data_p->verdict = TRAFFIC_VERDICT_DROP;
response.web_response_data = CreateDefaultBlockPage(attachment, session_id);
}
write_dbg(
attachment,
response.session_id,
DBG_LEVEL_TRACE,
"Handling Failure with fail %s mode",
response.verdict == ATTACHMENT_VERDICT_ACCEPT ? "open" : "close"
);
return response;
}
AttachmentVerdictResponse
SendRequestFilter(NanoAttachment *attachment, AttachmentData *data)
{
HttpEventThreadCtx ctx;
HttpSessionData *session_data_p = data->session_data;
SessionID session_id = session_data_p->session_id;
int res;
write_dbg(
attachment,
session_id,
DBG_LEVEL_DEBUG,
"Request filter handling session ID: %d",
session_id
);
if (handle_shmem_corruption(attachment) == NANO_ERROR) {
return SendCorruptMemoryVerdict(attachment, session_data_p);
}
write_dbg(attachment, session_id, DBG_LEVEL_DEBUG, "spawn SendRequestFilterThread");
res = NanoRunInThreadTimeout(
attachment,
data,
SendRequestFilterThread,
(void *)&ctx,
attachment->req_header_thread_timeout_msec,
"SendRequestFilterThread",
REQUEST
);
if (!res) {
updateMetricField(attachment, REQ_METADATA_THREAD_TIMEOUT, 1);
return SendThreadTimeoutVerdict(attachment, session_id, &ctx);
}
write_dbg(
attachment,
session_id,
DBG_LEVEL_DEBUG,
"finished SendMetadataThread successfully. res=%d",
ctx.res
);
if (ctx.res == NANO_DECLINED) {
return FinalizeIrrelevantResponse(attachment, session_id);
}
if (ctx.res != NANO_HTTP_FORBIDDEN && ctx.res != NANO_OK) {
return FinalizeFailedResponse(attachment, session_id, &ctx);
}
return FinalizeSuccessfulResponse(attachment, session_id, &ctx);
}
AttachmentVerdictResponse
SendMetadata(NanoAttachment *attachment, AttachmentData *data)
{
HttpEventThreadCtx ctx;
HttpSessionData *session_data_p = data->session_data;
SessionID session_id = session_data_p->session_id;
int res;
write_dbg(
attachment,
session_id,
DBG_LEVEL_DEBUG,
"Request start handling session ID: %d",
session_id
);
if (handle_shmem_corruption(attachment) == NANO_ERROR) {
return SendCorruptMemoryVerdict(attachment, session_data_p);
}
write_dbg(attachment, session_id, DBG_LEVEL_DEBUG, "spawn SendMetadataThread");
res = NanoRunInThreadTimeout(
attachment,
data,
SendMetadataThread,
(void *)&ctx,
attachment->req_start_thread_timeout_msec,
"SendMetadataThread",
REQUEST
);
if (!res) {
updateMetricField(attachment, REQ_METADATA_THREAD_TIMEOUT, 1);
return SendThreadTimeoutVerdict(attachment, session_id, &ctx);
}
write_dbg(
attachment,
session_id,
DBG_LEVEL_DEBUG,
"finished SendMetadataThread successfully. res=%d",
ctx.res
);
if (ctx.res == NANO_DECLINED) {
return FinalizeIrrelevantResponse(attachment, session_id);
}
if (ctx.res != NANO_HTTP_FORBIDDEN && ctx.res != NANO_OK) {
return FinalizeFailedResponse(attachment, session_id, &ctx);
}
return FinalizeSuccessfulResponse(attachment, session_id, &ctx);
}
AttachmentVerdictResponse
SendRequestHeaders(NanoAttachment *attachment, AttachmentData *data)
{
HttpEventThreadCtx ctx;
HttpSessionData *session_data_p = data->session_data;
SessionID session_id = session_data_p->session_id;
int res;
write_dbg(
attachment,
session_id,
DBG_LEVEL_DEBUG,
"Request header handling session ID: %d",
session_id
);
if (handle_shmem_corruption(attachment) == NANO_ERROR) {
return SendCorruptMemoryVerdict(attachment, session_data_p);
}
write_dbg(attachment, session_id, DBG_LEVEL_DEBUG, "spawn SendRequestHeadersThread");
res = NanoRunInThreadTimeout(
attachment,
data,
SendRequestHeadersThread,
(void *)&ctx,
attachment->req_header_thread_timeout_msec,
"SendRequestHeadersThread",
REQUEST
);
if (!res) {
updateMetricField(attachment, REQ_HEADER_THREAD_TIMEOUT, 1);
return SendThreadTimeoutVerdict(attachment, session_id, &ctx);
}
write_dbg(
attachment,
session_id,
DBG_LEVEL_DEBUG,
"finished SendRequestHeadersThread successfully. res=%d",
ctx.res
);
if (session_data_p->verdict == TRAFFIC_VERDICT_DELAYED) {
write_dbg(attachment, session_id, DBG_LEVEL_DEBUG, "spawn SendDelayedVerdictRequestThread");
res = NanoRunInThreadTimeout(
attachment,
data,
SendDelayedVerdictRequestThread,
(void *)&ctx,
attachment->waiting_for_verdict_thread_timeout_msec,
"SendDelayedVerdictRequestThread",
REQUEST
);
if (!res) {
updateMetricField(attachment, HOLD_THREAD_TIMEOUT, 1);
return SendThreadTimeoutVerdict(attachment, session_id, &ctx);
}
write_dbg(
attachment,
session_id,
DBG_LEVEL_DEBUG,
"finished SendDelayedVerdictRequestThread successfully. res=%d",
ctx.res
);
}
if (ctx.res != NANO_HTTP_FORBIDDEN && ctx.res != NANO_OK) {
return FinalizeFailedResponse(attachment, session_id, &ctx);
}
return FinalizeSuccessfulResponse(attachment, session_id, &ctx);
}
AttachmentVerdictResponse
SendResponseHeaders(NanoAttachment *attachment, AttachmentData *data)
{
HttpEventThreadCtx ctx;
HttpSessionData *session_data_p = data->session_data;
SessionID session_id = session_data_p->session_id;
int res;
write_dbg(
attachment,
session_id,
DBG_LEVEL_DEBUG,
"Response header handling session ID: %d",
session_id
);
if (handle_shmem_corruption(attachment) == NANO_ERROR) {
return SendCorruptMemoryVerdict(attachment, session_data_p);
}
write_dbg(attachment, session_id, DBG_LEVEL_DEBUG, "spawn SendResponseHeadersThread");
res = NanoRunInThreadTimeout(
attachment,
data,
SendResponseHeadersThread,
(void *)&ctx,
attachment->res_header_thread_timeout_msec,
"SendResponseHeadersThread",
RESPONSE
);
if (!res) {
updateMetricField(attachment, RES_HEADER_THREAD_TIMEOUT, 1);
return SendThreadTimeoutVerdict(attachment, session_id, &ctx);
}
write_dbg(
attachment,
session_id,
DBG_LEVEL_DEBUG,
"finished SendResponseHeadersThread successfully. res=%d",
ctx.res
);
if (ctx.res != NANO_HTTP_FORBIDDEN && ctx.res != NANO_OK) {
return FinalizeFailedResponse(attachment, session_id, &ctx);
}
return FinalizeSuccessfulResponse(attachment, session_id, &ctx);
}
AttachmentVerdictResponse
SendRequestBody(NanoAttachment *attachment, AttachmentData *data)
{
HttpEventThreadCtx ctx;
HttpSessionData *session_data_p = data->session_data;
SessionID session_id = session_data_p->session_id;
int res;
write_dbg(attachment, session_id, DBG_LEVEL_DEBUG, "Request body handling session ID: %d", session_id);
if (handle_shmem_corruption(attachment) == NANO_ERROR) {
return SendCorruptMemoryVerdict(attachment, session_data_p);
}
write_dbg(attachment, session_id, DBG_LEVEL_DEBUG, "spawn SendRequestBodyThread");
res = NanoRunInThreadTimeout(
attachment,
data,
SendRequestBodyThread,
(void *)&ctx,
attachment->req_body_thread_timeout_msec,
"SendRequestBodyThread",
REQUEST
);
if (!res) {
updateMetricField(attachment, REQ_BODY_THREAD_TIMEOUT, 1);
return SendThreadTimeoutVerdict(attachment, session_id, &ctx);
}
write_dbg(
attachment,
session_id,
DBG_LEVEL_DEBUG,
"finished SendRequestBodyThread successfully. res=%d",
ctx.res
);
if (session_data_p->verdict == TRAFFIC_VERDICT_DELAYED) {
write_dbg(attachment, session_id, DBG_LEVEL_DEBUG, "spawn SendDelayedVerdictRequestThread");
res = NanoRunInThreadTimeout(
attachment,
data,
SendDelayedVerdictRequestThread,
(void *)&ctx,
attachment->waiting_for_verdict_thread_timeout_msec,
"SendDelayedVerdictRequestThread",
REQUEST
);
if (!res) {
updateMetricField(attachment, HOLD_THREAD_TIMEOUT, 1);
return SendThreadTimeoutVerdict(attachment, session_id, &ctx);
}
write_dbg(
attachment,
session_id,
DBG_LEVEL_DEBUG,
"finished SendDelayedVerdictRequestThread successfully. res=%d",
ctx.res
);
}
if (ctx.res != NANO_HTTP_FORBIDDEN && ctx.res != NANO_OK) {
return FinalizeFailedResponse(attachment, session_id, &ctx);
}
return FinalizeSuccessfulResponse(attachment, session_id, &ctx);
}
AttachmentVerdictResponse
SendResponseBody(NanoAttachment *attachment, AttachmentData *data)
{
HttpEventThreadCtx ctx;
HttpSessionData *session_data_p = data->session_data;
SessionID session_id = session_data_p->session_id;
int res;
write_dbg(attachment, session_id, DBG_LEVEL_DEBUG, "Response body handling session ID: %d", session_id);
if (handle_shmem_corruption(attachment) == NANO_ERROR) {
return SendCorruptMemoryVerdict(attachment, session_data_p);
}
write_dbg(attachment, session_id, DBG_LEVEL_DEBUG, "spawn SendResponseBodyThread");
res = NanoRunInThreadTimeout(
attachment,
data,
SendResponseBodyThread,
(void *)&ctx,
attachment->res_body_thread_timeout_msec,
"SendResponseBodyThread",
RESPONSE
);
if (!res) {
updateMetricField(attachment, RES_BODY_THREAD_TIMEOUT, 1);
return SendThreadTimeoutVerdict(attachment, session_id, &ctx);
}
write_dbg(
attachment,
session_id,
DBG_LEVEL_DEBUG,
"finished SendResponseBodyThread successfully. res=%d",
ctx.res
);
if (ctx.res != NANO_HTTP_FORBIDDEN && ctx.res != NANO_OK) {
return FinalizeFailedResponse(attachment, session_id, &ctx);
}
return FinalizeSuccessfulResponse(attachment, session_id, &ctx);
}
AttachmentVerdictResponse
SendRequestEnd(NanoAttachment *attachment, AttachmentData *data)
{
HttpEventThreadCtx ctx;
HttpSessionData *session_data_p = data->session_data;
SessionID session_id = session_data_p->session_id;
int res;
write_dbg(
attachment,
session_id,
DBG_LEVEL_DEBUG,
"Request end handling session ID: %d",
session_id
);
if (handle_shmem_corruption(attachment) == NANO_ERROR) {
return SendCorruptMemoryVerdict(attachment, session_data_p);
}
write_dbg(attachment, session_id, DBG_LEVEL_DEBUG, "spawn SendRequestEndThread");
res = NanoRunInThreadTimeout(
attachment,
data,
SendRequestEndThread,
(void *)&ctx,
attachment->req_header_thread_timeout_msec,
"SendRequestEndThread",
REQUEST
);
if (!res) {
updateMetricField(attachment, REQ_END_THREAD_TIMEOUT, 1);
return SendThreadTimeoutVerdict(attachment, session_id, &ctx);
}
write_dbg(
attachment,
session_id,
DBG_LEVEL_DEBUG,
"finished SendRequestEndThread successfully. res=%d",
ctx.res
);
if (ctx.res != NANO_HTTP_FORBIDDEN && ctx.res != NANO_OK) {
return FinalizeFailedResponse(attachment, session_id, &ctx);
}
return FinalizeSuccessfulResponse(attachment, session_id, &ctx);
}
AttachmentVerdictResponse
SendResponseEnd(NanoAttachment *attachment, AttachmentData *data)
{
HttpEventThreadCtx ctx;
HttpSessionData *session_data_p = data->session_data;
SessionID session_id = session_data_p->session_id;
int res;
write_dbg(
attachment,
session_id,
DBG_LEVEL_DEBUG,
"Response end handling session ID: %d",
session_id
);
if (handle_shmem_corruption(attachment) == NANO_ERROR) {
return SendCorruptMemoryVerdict(attachment, session_data_p);
}
write_dbg(attachment, session_id, DBG_LEVEL_DEBUG, "spawn SendResponseEndThread");
res = NanoRunInThreadTimeout(
attachment,
data,
SendResponseEndThread,
(void *)&ctx,
attachment->req_header_thread_timeout_msec,
"SendResponseEndThread",
RESPONSE
);
if (!res) {
updateMetricField(attachment, RES_END_THREAD_TIMEOUT, 1);
return SendThreadTimeoutVerdict(attachment, session_id, &ctx);
}
write_dbg(
attachment,
session_id,
DBG_LEVEL_DEBUG,
"finished SendResponseEndThread successfully. res=%d",
ctx.res
);
if (ctx.res != NANO_HTTP_FORBIDDEN && ctx.res != NANO_OK) {
return FinalizeFailedResponse(attachment, session_id, &ctx);
}
return FinalizeSuccessfulResponse(attachment, session_id, &ctx);
}
NanoCommunicationResult
SendMetricData(NanoAttachment *attachment)
{
HttpEventThreadCtx ctx;
int res;
write_dbg(
attachment,
0,
DBG_LEVEL_DEBUG,
"Sending metric data saved in worker ID: %d",
attachment->worker_id
);
if (handle_shmem_corruption(attachment) == NANO_ERROR) {
write_dbg(
attachment,
0,
DBG_LEVEL_DEBUG,
"Failed to send metric data, shmem corruption Worker ID: %d",
attachment->worker_id
);
return NANO_ERROR;
}
res = NanoRunInThreadTimeout(
attachment,
NULL,
SendMetricToServiceThread,
(void *)&ctx,
attachment->metric_timeout_timeout,
"SendMetricToServiceThread",
METRICS
);
if (!res) {
write_dbg(
attachment,
0,
DBG_LEVEL_DEBUG,
"Thread timeout while sending metric data from worker ID: %d",
attachment->worker_id
);
return NANO_ERROR;
}
return NANO_OK;
}

View File

@@ -0,0 +1,137 @@
#ifndef __NANO_ATTACHMENT_SENDER_H__
#define __NANO_ATTACHMENT_SENDER_H__
#include "nano_attachment_common.h"
#include "nano_initializer.h"
///
/// @brief Sends start request data to the nano service.
///
/// This function handles the sending of starting meta data, request headers and end request to the nano service.
/// It creates a new thread to perform the sending operation, ensuring that the main execution flow
/// is not blocked. It also handles potential errors and timeouts that may occur during
/// the sending process.
///
/// @param attachment A pointer to the NanoAttachment structure.
/// @param data A pointer to AttachmentData structure containing the data to send and the session data.
///
/// @return An AttachmentVerdictResponse structure indicating the outcome of the operation.
///
AttachmentVerdictResponse SendRequestFilter(NanoAttachment *attachment, AttachmentData *data);
///
/// @brief Sends start request data to the nano service.
///
/// This function handles the sending of starting meta data to the nano service. It creates
/// a new thread to perform the sending operation, ensuring that the main execution flow
/// is not blocked. It also handles potential errors and timeouts that may occur during
/// the sending process.
///
/// @param attachment A pointer to the NanoAttachment structure.
/// @param data A pointer to AttachmentData structure containing the data to send and the session data.
///
/// @return An AttachmentVerdictResponse structure indicating the outcome of the operation.
///
AttachmentVerdictResponse SendMetadata(NanoAttachment *attachment, AttachmentData *data);
///
/// @brief Sends request headers to the nano service.
///
/// This function handles the sending of request headers to the nano service. It creates
/// a new thread to perform the sending operation, ensuring that the main execution flow
/// is not blocked. It also handles potential errors and timeouts that may occur during
/// the sending process.
///
/// @param attachment A pointer to the NanoAttachment structure.
/// @param data A pointer to AttachmentData structure containing the headers to send and the session data.
///
/// @return An AttachmentVerdictResponse structure indicating the outcome of the operation.
///
AttachmentVerdictResponse SendRequestHeaders(NanoAttachment *attachment, AttachmentData *data);
///
/// @brief Sends response headers to the nano service.
///
/// This function handles the sending of response headers to the nano service. It creates
/// a new thread to perform the sending operation, ensuring that the main execution flow
/// is not blocked. It also handles potential errors and timeouts that may occur during
/// the sending process.
///
/// @param attachment A pointer to the NanoAttachment structure.
/// @param data A pointer to AttachmentData structure containing the headers to send and the session data.
///
/// @return An AttachmentVerdictResponse structure indicating the outcome of the operation.
///
AttachmentVerdictResponse SendResponseHeaders(NanoAttachment *attachment, AttachmentData *data);
///
/// @brief Sends request body to the nano service.
///
/// This function handles the sending of request body to the nano service. It creates
/// a new thread to perform the sending operation, ensuring that the main execution flow
/// is not blocked. It also handles potential errors and timeouts that may occur during
/// the sending process.
///
/// @param attachment A pointer to the NanoAttachment structure.
/// @param data A pointer to AttachmentData structure containing the body to send and the session data.
///
/// @return An AttachmentVerdictResponse structure indicating the outcome of the operation.
///
AttachmentVerdictResponse SendRequestBody(NanoAttachment *attachment, AttachmentData *data);
///
/// @brief Sends response body to the nano service.
///
/// This function handles the sending of response body to the nano service. It creates
/// a new thread to perform the sending operation, ensuring that the main execution flow
/// is not blocked. It also handles potential errors and timeouts that may occur during
/// the sending process.
///
/// @param attachment A pointer to the NanoAttachment structure.
/// @param data A pointer to AttachmentData structure containing the body to send and the session data.
///
/// @return An AttachmentVerdictResponse structure indicating the outcome of the operation.
///
AttachmentVerdictResponse SendResponseBody(NanoAttachment *attachment, AttachmentData *data);
///
/// @brief Sends end request signal to the nano service.
///
/// This function handles the sending a request end signal to the nano service. It creates
/// a new thread to perform the sending operation, ensuring that the main execution flow
/// is not blocked. It also handles potential errors and timeouts that may occur during
/// the sending process.
///
/// @param attachment A pointer to the NanoAttachment structure.
/// @param data A pointer to AttachmentData structure containing the necessery data to send and the session data.
///
/// @return An AttachmentVerdictResponse structure indicating the outcome of the operation.
///
AttachmentVerdictResponse SendRequestEnd(NanoAttachment *attachment, AttachmentData *data);
///
/// @brief Sends end response signal to the nano service.
///
/// This function handles the sending a response end signal to the nano service. It creates
/// a new thread to perform the sending operation, ensuring that the main execution flow
/// is not blocked. It also handles potential errors and timeouts that may occur during
/// the sending process.
///
/// @param attachment A pointer to the NanoAttachment structure.
/// @param data A pointer to AttachmentData structure containing the necessery data to send and the session data.
///
/// @return An AttachmentVerdictResponse structure indicating the outcome of the operation.
///
AttachmentVerdictResponse SendResponseEnd(NanoAttachment *attachment, AttachmentData *data);
///
/// @brief Sends metric data to the nano service and resets it on the attachment.
///
/// @param attachment A pointer to the NanoAttachment structure that contains metrics data.
///
/// @return An NanoCommunication enum indicating the outcome of the operation.
/// NANO_OK if the operation was successful, NANO_ERROR otherwise.
///
NanoCommunicationResult SendMetricData(NanoAttachment *attachment);
#endif // __NANO_ATTACHMENT_SENDER_H__

View File

@@ -0,0 +1,269 @@
#include "nano_attachment_sender_thread.h"
#include "nano_initializer.h"
#include "nano_attachment_sender.h"
#include "nano_attachment_common.h"
#include "nano_attachment_io.h"
void
init_thread_ctx(HttpEventThreadCtx *ctx, NanoAttachment *attachment, AttachmentData *data)
{
ctx->attachment = attachment;
ctx->data = data;
ctx->session_data_p = (data == NULL) ? NULL : data->session_data;
ctx->res = NANO_OK;
ctx->web_response_data = NULL;
ctx->modifications = NULL;
}
void *
RegistrationCommSocketThread(void *_ctx)
{
HttpEventThreadCtx *ctx = (HttpEventThreadCtx *)_ctx;
NanoAttachment *attachment = ctx->attachment;
ctx->res = connect_to_comm_socket(attachment);
return NULL;
}
void *
RegistrationSocketThread(void *_ctx)
{
HttpEventThreadCtx *ctx = (HttpEventThreadCtx *)_ctx;
NanoAttachment *attachment = ctx->attachment;
ctx->res = connect_to_registration_socket(attachment);
return 0;
}
void *
SendRequestFilterThread(void *_ctx)
{
HttpEventThreadCtx *ctx = (HttpEventThreadCtx *)_ctx;
HttpRequestFilterData *start_data = (HttpRequestFilterData*)ctx->data->data;
HttpMetaData *metadata = start_data->meta_data;
HttpHeaders *headers = start_data->req_headers;
bool contains_body = start_data->contains_body;
NanoAttachment *attachment = ctx->attachment;
HttpSessionData *session_data_p = ctx->session_data_p;
bool is_verdict_requested = false;
nano_metadata_sender(
attachment,
metadata,
ctx,
session_data_p->session_id,
&session_data_p->remaining_messages_to_reply,
is_verdict_requested
);
nano_header_sender(
attachment,
headers,
ctx,
REQUEST_HEADER,
session_data_p->session_id,
&session_data_p->remaining_messages_to_reply,
is_verdict_requested
);
if (!contains_body) {
nano_end_transaction_sender(
attachment,
REQUEST_END,
ctx,
session_data_p->session_id,
&session_data_p->remaining_messages_to_reply
);
}
return NULL;
}
void *
SendMetadataThread(void *_ctx)
{
HttpEventThreadCtx *ctx = (HttpEventThreadCtx *)_ctx;
HttpMetaData *metadata = (HttpMetaData*)ctx->data->data;
NanoAttachment *attachment = ctx->attachment;
HttpSessionData *session_data_p = ctx->session_data_p;
bool is_verdict_requested = false;
nano_metadata_sender(
attachment,
metadata,
ctx,
session_data_p->session_id,
&session_data_p->remaining_messages_to_reply,
is_verdict_requested
);
return NULL;
}
void *
SendRequestHeadersThread(void *_ctx)
{
HttpEventThreadCtx *ctx = (HttpEventThreadCtx *)_ctx;
HttpHeaders *headers = (HttpHeaders*)ctx->data->data;
NanoAttachment *attachment = ctx->attachment;
HttpSessionData *session_data_p = ctx->session_data_p;
bool is_verdict_requested = false;
nano_header_sender(
attachment,
headers,
ctx,
REQUEST_HEADER,
session_data_p->session_id,
&session_data_p->remaining_messages_to_reply,
is_verdict_requested
);
return NULL;
}
void *
SendResponseHeadersThread(void *_ctx)
{
HttpEventThreadCtx *ctx = (HttpEventThreadCtx *)_ctx;
ResHttpHeaders *headers = (ResHttpHeaders*)ctx->data->data;
NanoAttachment *attachment = ctx->attachment;
HttpSessionData *session_data_p = ctx->session_data_p;
HttpHeaders *http_headers = headers->headers;
bool is_verdict_requested = false;
nano_send_response_code(
attachment,
headers->response_code,
ctx,
session_data_p->session_id,
&session_data_p->remaining_messages_to_reply
);
nano_send_response_content_length(
attachment,
headers->content_length,
ctx,
session_data_p->session_id,
&session_data_p->remaining_messages_to_reply
);
nano_header_sender(
attachment,
http_headers,
ctx,
RESPONSE_HEADER,
session_data_p->session_id,
&session_data_p->remaining_messages_to_reply,
is_verdict_requested
);
return NULL;
}
void *
SendRequestBodyThread(void *_ctx)
{
HttpEventThreadCtx *ctx = (HttpEventThreadCtx *)_ctx;
HttpBody *bodies = (HttpBody*)ctx->data->data;
NanoAttachment *attachment = ctx->attachment;
HttpSessionData *session_data_p = ctx->session_data_p;
nano_body_sender(
attachment,
bodies,
ctx,
REQUEST_BODY,
session_data_p->session_id,
&session_data_p->remaining_messages_to_reply
);
return NULL;
}
void *
SendResponseBodyThread(void *_ctx)
{
HttpEventThreadCtx *ctx = (HttpEventThreadCtx *)_ctx;
HttpBody *bodies = (HttpBody*)ctx->data->data;
NanoAttachment *attachment = ctx->attachment;
HttpSessionData *session_data_p = ctx->session_data_p;
nano_body_sender(
attachment,
bodies,
ctx,
RESPONSE_BODY,
session_data_p->session_id,
&session_data_p->remaining_messages_to_reply
);
return NULL;
}
void *
SendRequestEndThread(void *_ctx)
{
HttpEventThreadCtx *ctx = (HttpEventThreadCtx *)_ctx;
NanoAttachment *attachment = ctx->attachment;
HttpSessionData *session_data_p = ctx->session_data_p;
nano_end_transaction_sender(
attachment,
REQUEST_END,
ctx,
session_data_p->session_id,
&session_data_p->remaining_messages_to_reply
);
return NULL;
}
void *
SendResponseEndThread(void *_ctx)
{
HttpEventThreadCtx *ctx = (HttpEventThreadCtx *)_ctx;
NanoAttachment *attachment = ctx->attachment;
HttpSessionData *session_data_p = ctx->session_data_p;
nano_end_transaction_sender(
attachment,
RESPONSE_END,
ctx,
session_data_p->session_id,
&session_data_p->remaining_messages_to_reply
);
return NULL;
}
void *
SendDelayedVerdictRequestThread(void *_ctx)
{
HttpEventThreadCtx *ctx = (HttpEventThreadCtx *)_ctx;
NanoAttachment *attachment = ctx->attachment;
HttpSessionData *session_data_p = ctx->session_data_p;
nano_request_delayed_verdict(
attachment,
ctx,
session_data_p->session_id,
&session_data_p->remaining_messages_to_reply
);
return NULL;
}
void *
SendMetricToServiceThread(void *_data)
{
HttpEventThreadCtx *ctx = (HttpEventThreadCtx *)_data;
NanoAttachment *attachment = ctx->attachment;
nano_send_metric_data_sender(attachment);
return NULL;
}

View File

@@ -0,0 +1,178 @@
#ifndef __NANO_ATTACHMENT_SENDER_THREAD_H__
#define __NANO_ATTACHMENT_SENDER_THREAD_H__
#include "nano_attachment_common.h"
#include "nano_initializer.h"
/// @struct HttpEventThreadCtx
/// @brief Holds all the information needed to communicate with the attachment service.
typedef struct HttpEventThreadCtx
{
NanoAttachment *attachment; ///< NanoAttachment.
AttachmentData *data; ///< Attachment data.
HttpSessionData *session_data_p; ///< Provided session data.
/// Connection results with the attachment service
/// - #NANO_OK
/// - #NANO_ERROR
NanoCommunicationResult res;
WebResponseData *web_response_data; ///< Web response data.
NanoHttpModificationList *modifications; ///< Context's modification.
} HttpEventThreadCtx;
///
/// @brief Initializes a thread context structure for an NANO event thread.
///
/// This function initializes a thread context structure with the provided data
/// and default values for other fields.
///
/// @param ctx A pointer to a struct HttpEventThreadCtx structure representing the thread context to initialize.
/// @param attachment A pointer to a NanoAttachment structure representing the attachment data for the thread.
/// @param data A pointer to an AttachmentData structure representing the attachment data for the thread.
///
void
init_thread_ctx(HttpEventThreadCtx *ctx, NanoAttachment *attachment, AttachmentData *data);
///
/// @brief Connect attachment communication socket to the nano service.
///
/// @param _ctx A pointer to the HttpEventThreadCtx context.
///
/// @return NULL.
///
void * RegistrationCommSocketThread(void *_ctx);
///
/// @brief Connect attachment to registration socket to the nano service.
///
/// @param _ctx A pointer to the HttpEventThreadCtx context.
///
/// @return NULL.
///
void * RegistrationSocketThread(void *_ctx);
///
/// @brief Sends request start data to the nano service.
///
/// This thread function sends metadata to start a request interaction with the nano service.
///
/// @param _ctx A pointer to the HttpEventThreadCtx context.
///
/// @return NULL.
///
void * SendMetadataThread(void *_ctx);
///
/// @brief Sends request headers to the nano service.
///
/// This thread function sends request headers to the nano service using the provided
/// HttpEventThreadCtx context. It updates the session data and handles any
/// errors that occur during the header sending process.
///
/// @param _ctx A pointer to the HttpEventThreadCtx context.
///
/// @return NULL.
///
void * SendRequestHeadersThread(void *_ctx);
///
/// @brief Sends response headers to the nano service.
///
/// This thread function sends response headers to the nano service using the provided
/// HttpEventThreadCtx context. It updates the session data and handles any
/// errors that occur during the header sending process.
///
/// @param _ctx A pointer to the HttpEventThreadCtx context.
///
/// @return NULL.
///
void * SendResponseHeadersThread(void *_ctx);
///
/// @brief Sends request body to the nano service.
///
/// This thread function sends request body to the nano service using the provided
/// HttpEventThreadCtx context. It updates the session data and handles any
/// errors that occur during the body sending process.
///
/// @param _ctx A pointer to the HttpEventThreadCtx context.
///
/// @return NULL.
///
void * SendRequestBodyThread(void *_ctx);
///
/// @brief Sends response body to the nano service.
///
/// This thread function sends response body to the nano service using the provided
/// HttpEventThreadCtx context. It updates the session data and handles any
/// errors that occur during the body sending process.
///
/// @param _ctx A pointer to the HttpEventThreadCtx context.
///
/// @return NULL.
///
void * SendResponseBodyThread(void *_ctx);
///
/// @brief Sends request end data to the nano service.
///
/// This thread function sends signal to the nano service that the response has ended
/// and with it the whole session transaction.
///
/// @param _ctx A pointer to the HttpEventThreadCtx context.
///
/// @return NULL.
///
void * SendRequestEndThread(void *_ctx);
///
/// @brief Sends response end data to the nano service.
///
/// This thread function sends signal to the nano service that the response has ended
/// and with it the whole session transaction.
///
/// @param _ctx A pointer to the HttpEventThreadCtx context.
///
/// @return NULL.
///
void * SendResponseEndThread(void *_ctx);
///
/// @brief Thread function to send request filters.
///
/// This thread function sends metadata, request headers and end request
/// to the nano service using the provided HttpEventThreadCtx context.
/// It updates the session data and handles any errors that occur during the header sending process.
///
/// @param _ctx A pointer to the HttpEventThreadCtx context.
///
/// @return NULL
///
void * SendRequestFilterThread(void *_ctx);
///
/// @brief Send query for requesting delayed data verdict.
///
/// This thread function sends a delayed data query to the service and waits for the response.
///
/// @param _ctx A pointer to the HttpEventThreadCtx context.
///
/// @return NULL.
///
void * SendDelayedVerdictRequestThread(void *_ctx);
///
/// @brief Send data metric fo the service.
///
/// This thread function sends data metric to the service and resets it.
///
/// @param _ctx A pointer to the HttpEventThreadCtx context.
///
/// @return NULL.
///
void * SendMetricToServiceThread(void *_data);
#endif // __NANO_ATTACHMENT_SENDER_THREAD_H__

View File

@@ -0,0 +1,233 @@
#include "nano_attachment_thread.h"
#include <pthread.h>
#include <sched.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
#include "nano_attachment_sender.h"
#include "nano_attachment_sender_thread.h"
#include "nano_utils.h"
#include "nano_attachment_metric.h"
///
/// @brief Calculate the processing time of a transaction and update the session data accordingly.
///
/// @param session_data_p Pointer to the HttpSessionData struct containing session data.
/// @param thread_time_begin Pointer to the timespec struct representing the start time of the transaction.
/// @param transaction_type The type of transaction (REQUEST or RESPONSE).
///
static void
CalcProcessingTime(
HttpSessionData *session_data_p,
struct timespec *thread_time_begin,
TransactionType transaction_type
)
{
struct timespec thread_time_end;
if (transaction_type == START || transaction_type == METRICS) return;
clock_gettime(CLOCK_REALTIME, &thread_time_end);
double begin_usec = (thread_time_begin->tv_sec * 1000 * 1000) + (thread_time_begin->tv_nsec / 1000);
double end_usec = (thread_time_end.tv_sec * 1000 * 1000) + (thread_time_end.tv_nsec / 1000);
double elapsed = end_usec - begin_usec;
if (transaction_type == REQUEST) {
session_data_p->req_proccesing_time += elapsed;
} else {
session_data_p->res_proccesing_time += elapsed;
}
}
///
/// @brief runs the provided routine with the arguments in a non thread and without a timeout.
/// @param[in, out] attachment A pointer to the NanoAttachment struct.
/// @param[in] session_id The session ID.
/// @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
NanoRunInWithoutThreadTimeout(
NanoAttachment *attachment,
SessionID session_id,
CpThreadRoutine thread_func,
void *arg,
char *func_name
)
{
write_dbg(
attachment,
session_id,
DBG_LEVEL_TRACE,
"Executing cb in blocking mode, fn=%s",
func_name
);
thread_func(arg);
return 1;
}
int
NanoRunInThreadTimeout(
NanoAttachment *attachment,
AttachmentData *data,
CpThreadRoutine thread_func,
void *arg,
int timeout_msecs,
char *func_name,
TransactionType transaction_type
)
{
int status = 0;
int ret = 0;
void *res = NULL;
pthread_t thread;
struct timespec ts;
struct timespec thread_time_begin;
uint32_t session_id;
HttpEventThreadCtx *ctx = (HttpEventThreadCtx *)arg;
session_id = (data == NULL) ? attachment->worker_id : data->session_data->session_id;
init_thread_ctx(ctx, attachment, data);
if (attachment->inspection_mode == NO_THREAD) {
return NanoRunInWithoutThreadTimeout(attachment, session_id, thread_func, arg, func_name);
}
if (clock_gettime(CLOCK_REALTIME, &thread_time_begin) == -1) {
updateMetricField(attachment, THREAD_FAILURE, 1);
write_dbg(
attachment,
session_id,
DBG_LEVEL_ERROR,
"clock_gettime(CLOCK_REALTIME) failed. Status: %s",
strerror(errno)
);
return 0;
}
/// Runs the routine in a dedicated thread.
write_dbg(
attachment,
session_id,
DBG_LEVEL_TRACE,
"Executing cb in dedicated thread, fn=%s",
func_name
);
if (pthread_create(&thread, NULL, thread_func, arg) != 0) {
updateMetricField(attachment, THREAD_FAILURE, 1);
write_dbg(
attachment,
session_id,
DBG_LEVEL_TRACE,
"pthread_create failed with errno=%d, fn=%s",
errno,
func_name
);
return 0;
}
if (attachment->inspection_mode == BLOCKING_THREAD) {
// Runs the function in a blocking thread.
status = pthread_join(thread, &res);
write_dbg(
attachment,
session_id,
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(attachment, THREAD_FAILURE, 1);
write_dbg(
attachment,
session_id,
DBG_LEVEL_ERROR,
"clock_gettime(CLOCK_REALTIME) failed. Status: %s",
strerror(errno)
);
return 0;
}
// Convert milliseconds to timespec
ts.tv_sec += timeout_msecs / 1000;
ts.tv_nsec += (timeout_msecs % 1000) * 1000 * 1000;
if (ts.tv_nsec > 1000 * 1000 * 1000) {
ts.tv_nsec -= 1000 * 1000 * 1000;
++ts.tv_sec;
}
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) {
write_dbg(
attachment,
session_id,
status == ETIMEDOUT ? DBG_LEVEL_DEBUG : DBG_LEVEL_WARNING,
"pthread_timejoin_np returns with %d (%s), fn=%s",
status,
strerror(status),
func_name
);
ret = pthread_cancel(thread);
write_dbg(
attachment,
session_id,
DBG_LEVEL_DEBUG,
"pthread_cancel returns with ret=%d, fn=%s",
ret,
func_name
);
ret = pthread_join(thread, &res);
if (ret != 0) {
updateMetricField(attachment, THREAD_FAILURE, 1);
write_dbg(
attachment,
session_id,
DBG_LEVEL_WARNING,
"pthread_join failed while fail open is enabled. RET=%d, fn=%s",
ret,
func_name
);
return ret != 0;
}
updateMetricField(attachment, res == PTHREAD_CANCELED ? THREAD_TIMEOUT : THREAD_FAILURE, 1);
if (res == PTHREAD_CANCELED) {
write_dbg(attachment, session_id, DBG_LEVEL_DEBUG, "thread was canceled, fn=%s", func_name);
}
write_dbg(attachment, session_id, DBG_LEVEL_DEBUG, "pthread_join returns with ret=%d", ret);
} else {
write_dbg(
attachment,
session_id,
DBG_LEVEL_TRACE,
"Successfully executed thread. fn=%s",
func_name
);
}
if (data != NULL) {
CalcProcessingTime(data->session_data, &thread_time_begin, transaction_type);
}
return status == 0;
}

View File

@@ -0,0 +1,46 @@
#ifndef __NANO_ATTACHMENT_THREAD_H__
#define __NANO_ATTACHMENT_THREAD_H__
#include <stdbool.h>
#include "nano_initializer.h"
typedef void *(*CpThreadRoutine)(void *); ///< Func
typedef enum TransactionType
{
START,
REQUEST,
RESPONSE,
METRICS,
REGISTRATION
} TransactionType;
///
/// @brief Runs a function in a thread with a timeout.
///
/// This function runs the specified thread function in a dedicated thread with a timeout.
/// If the function does not complete within the specified timeout, it is cancelled.
///
/// @param[in] attachment The NanoAttachment object.
/// @param[in] data The AttachmentData object.
/// @param[in] thread_func The function to run in the thread.
/// @param[in] arg The argument to pass to the thread function.
/// @param[in] timeout_msecs The timeout value in milliseconds.
/// @param[in] func_name The name of the function for debugging purposes.
/// @param[in] transaction_type The type of transaction (request or response), used for timeout.
///
/// @return Returns 1 if the function completes within the timeout, 0 otherwise.
///
int
NanoRunInThreadTimeout(
NanoAttachment *attachment,
AttachmentData *data,
CpThreadRoutine thread_func,
void *arg,
int timeout_msecs,
char *func_name,
TransactionType transaction_type
);
#endif // __NANO_ATTACHMENT_THREAD_H__

View File

@@ -0,0 +1,51 @@
include_directories(${CMAKE_SOURCE_DIR}/attachments/nano_attachment)
add_unit_test(
nano_attachment_ut
"nano_attachment_ut.cc"
"nano_attachment"
)
if( NOT "${PLATFORM_TYPE}" STREQUAL "alpine")
add_unit_test(
nano_attachment_io_ut
"nano_attachment_io_ut.cc"
"nano_attachment"
)
endif()
add_unit_test(
nano_attachment_sender_ut
"nano_attachment_sender_ut.cc"
"nano_attachment"
)
add_unit_test(
nano_thread_ut
"nano_thread_ut.cc"
"nano_attachment"
)
add_unit_test(
nano_sender_thread_ut
"nano_sender_thread_ut.cc"
"nano_attachment"
)
add_unit_test(
nano_configuration_ut
"nano_configuration_ut.cc"
"nano_attachment"
)
add_unit_test(
nano_initializer_ut
"nano_initializer_ut.cc"
"nano_attachment"
)
add_unit_test(
nano_attachment_metrics_ut
"nano_attachment_metrics_ut.cc"
"nano_attachment"
)

View File

@@ -0,0 +1,975 @@
#include "cptest.h"
#include "nano_attachment_common.h"
#include "attachment_types.h"
#include "mock_nano_initializer.h"
#include "mock_shmem_ipc.h"
#include "mock_nano_socket.h"
#include "mock_nano_poll.h"
extern "C" {
#include "nano_attachment.h"
#include "nano_attachment_io.h"
}
using namespace std;
using namespace testing;
class NanoAttachmentIoTest : public Test
{
public:
void
SetUp() override
{
EXPECT_CALL(
initializer_mocker,
nano_attachment_init_process(_)).WillOnce(Return(NanoCommunicationResult::NANO_OK)
);
setenv("CLOUDGUARD_UID", "Testing", 1);
attachment = InitNanoAttachment(
static_cast<uint8_t>(AttachmentType::NGINX_ATT_ID),
2,
4,
STDOUT_FILENO
);
EXPECT_NE(attachment, nullptr);
attachment->dbg_level = nano_http_cp_debug_level::DBG_LEVEL_TRACE;
session_data = InitSessionData(attachment, 501);
EXPECT_NE(session_data, nullptr);
ctx_data.session_data = session_data;
init_thread_ctx(&ctx, attachment, &ctx_data);
reply_from_service_mock = (HttpReplyFromService *)malloc(
sizeof(HttpReplyFromService) +
sizeof(HttpModifyData) +
sizeof(HttpInjectData) +
sizeof(HttpWebResponseData)
);
reply_from_service_mock->verdict = static_cast<uint16_t>(ServiceVerdict::TRAFFIC_VERDICT_INSPECT);
reply_from_service_mock->session_id = session_data->session_id;
reply_from_service_mock->modification_count = 0;
modify_data_mock = reinterpret_cast<HttpModifyData *>(reply_from_service_mock->modify_data);
inject_data = reinterpret_cast<HttpInjectData *>(
reply_from_service_mock->modify_data + sizeof(HttpModifyData)
);
web_response_data = reinterpret_cast<HttpWebResponseData *>(
reply_from_service_mock->modify_data + sizeof(HttpModifyData)
);
}
void
TearDown() override
{
free(reply_from_service_mock);
FiniSessionData(attachment, session_data);
FiniNanoAttachment(attachment);
}
nano_str_t
create_nano_str(const char *str)
{
nano_str_t nano_str;
nano_str.data = reinterpret_cast<unsigned char *>(const_cast<char *>(str));
nano_str.len = strlen(str);
return nano_str;
}
AttachmentVerdictResponse inspect_response = {
AttachmentVerdict::ATTACHMENT_VERDICT_INSPECT,
1,
NULL,
NULL
};
AttachmentVerdictResponse accept_response = {
AttachmentVerdict::ATTACHMENT_VERDICT_ACCEPT,
1,
NULL,
NULL
};
AttachmentVerdictResponse custom_drop_response = {
AttachmentVerdict::ATTACHMENT_VERDICT_DROP,
1,
NULL,
NULL
};
HttpMetaData http_meta_data = {
create_nano_str("HTTP/1.1"),
create_nano_str("GET"),
create_nano_str("www.nanoattachmentut.com"),
create_nano_str("192.168.1.100"),
80,
create_nano_str("/dogs.html"),
create_nano_str("192.168.1.101"),
253,
create_nano_str("nanoattachmentut.com"),
create_nano_str("/dogs.html")
};
HttpHeaderData http_headers[3] = {
{
create_nano_str("Host"),
create_nano_str("www.nanoattachmentut.com")
},
{
create_nano_str("User-Agent"),
create_nano_str("Mozilla/5.0")
},
{
create_nano_str("Accept"),
create_nano_str("text/html")
}
};
HttpHeaders http_headers_data = {
http_headers,
3
};
nano_str_t body[3] = {
create_nano_str("Hello"),
create_nano_str("World"),
create_nano_str("!")
};
HttpBody http_body_data = {
body,
3
};
AttachmentData ctx_data = {
1,
HttpChunkType::HTTP_REQUEST_METADATA,
session_data,
nullptr
};
HttpReplyFromService *reply_from_service_mock;
HttpModifyData *modify_data_mock;
HttpInjectData *inject_data;
HttpWebResponseData *web_response_data;
uint32_t reply_session_id = -1;
void *reply_session_id_void = &reply_session_id;
const char **replay_data_mock;
NanoAttachment *attachment;
HttpSessionData *session_data;
HttpEventThreadCtx ctx;
StrictMock<NanoInitializerMocker> initializer_mocker;
StrictMock<NanoShmemIPCMocker> mock_shmem_ipc;
StrictMock<NanoSocketMocker> mock_nano_socket;
StrictMock<NanoPollMocker> mock_nano_poll;
};
TEST_F(NanoAttachmentIoTest, ConnectToCommSocket)
{
NanoCommunicationResult res;
EXPECT_CALL(
mock_nano_socket,
socket(AF_UNIX, SOCK_STREAM, 0)
).WillOnce(Return(53));
EXPECT_CALL(
mock_nano_socket,
connect(53, _, _)
).WillOnce(Return(0));
res = connect_to_comm_socket(attachment);
EXPECT_EQ(res, NanoCommunicationResult::NANO_OK);
EXPECT_EQ(attachment->comm_socket, 53);
}
TEST_F(NanoAttachmentIoTest, ConnectToRegistrationSocket)
{
NanoCommunicationResult res;
EXPECT_CALL(
mock_nano_socket,
socket(AF_UNIX, SOCK_STREAM, 0)
).WillOnce(Return(39));
EXPECT_CALL(
mock_nano_socket,
connect(39, _, _)
).WillOnce(Return(0));
res = connect_to_registration_socket(attachment);
EXPECT_EQ(res, NanoCommunicationResult::NANO_OK);
EXPECT_EQ(attachment->registration_socket, 39);
}
TEST_F(NanoAttachmentIoTest, NanoMetadataSender)
{
EXPECT_CALL(
mock_shmem_ipc,
sendChunkedData(_, _, _, _)
).WillOnce(Return(0));
EXPECT_CALL(
mock_shmem_ipc,
isDataAvailable(_)
).WillOnce(Return(1));
EXPECT_CALL(
mock_shmem_ipc,
receiveData(_, _, _)
).WillOnce(
DoAll(
SaveArg<2>(&replay_data_mock),
InvokeWithoutArgs(
[&] () {
*replay_data_mock = reinterpret_cast<const char *>(reply_from_service_mock);
}
),
Return(1)
)
);
EXPECT_CALL(
mock_shmem_ipc,
popData(_)
).WillOnce(Return(1));
EXPECT_CALL(
mock_nano_poll,
poll(_, _, _)
).Times(2)
.WillOnce(Return(1))
.WillOnce(Return(2));
EXPECT_CALL(
mock_nano_socket,
write(_, _, _)
).WillOnce(Return(sizeof(SessionID)));
EXPECT_CALL(
mock_nano_socket,
read(_, _, _)
).WillOnce(
DoAll(
SaveArg<1>(&reply_session_id_void),
InvokeWithoutArgs(
[&] () {
*reinterpret_cast<uint32_t *>(reply_session_id_void) = 501;
}
),
Return(1)
)
);
nano_metadata_sender(
attachment,
&http_meta_data,
&ctx,
session_data->session_id,
&session_data->remaining_messages_to_reply,
true
);
}
TEST_F(NanoAttachmentIoTest, NanoMetadataSenderFailOnce)
{
EXPECT_CALL(
mock_shmem_ipc,
sendChunkedData(_, _, _, _)
).Times(2)
.WillOnce(Return(1))
.WillOnce(Return(0));
EXPECT_CALL(
mock_nano_poll,
poll(_, _, _)
).Times(2)
.WillOnce(Return(1))
.WillOnce(Return(2));
EXPECT_CALL(
mock_nano_socket,
write(_, _, _)
).WillOnce(Return(sizeof(SessionID)));
EXPECT_CALL(
mock_nano_socket,
read(_, _, _)
).WillOnce(
DoAll(
SaveArg<1>(&reply_session_id_void),
InvokeWithoutArgs(
[&] () {
*reinterpret_cast<uint32_t *>(reply_session_id_void) = 501;
}
),
Return(1)
)
);
nano_metadata_sender(
attachment,
&http_meta_data,
&ctx,
session_data->session_id,
&session_data->remaining_messages_to_reply,
false
);
}
TEST_F(NanoAttachmentIoTest, NanoHeadersSender)
{
EXPECT_CALL(
mock_shmem_ipc,
sendChunkedData(_, _, _, _)
).WillOnce(Return(0));
EXPECT_CALL(
mock_shmem_ipc,
isDataAvailable(_)
).WillOnce(Return(1));
EXPECT_CALL(
mock_shmem_ipc,
receiveData(_, _, _)
).WillOnce(
DoAll(
SaveArg<2>(&replay_data_mock),
InvokeWithoutArgs(
[&] () {
*replay_data_mock = (const char *)reply_from_service_mock;
}
),
Return(1)
)
);
EXPECT_CALL(
mock_nano_poll,
poll(_, _, _)
).Times(2)
.WillOnce(Return(1))
.WillOnce(Return(2));
EXPECT_CALL(
mock_nano_socket,
write(_, _, _)
).WillOnce(Return(sizeof(SessionID)));
EXPECT_CALL(
mock_nano_socket,
read(_, _, _)
).WillOnce(
DoAll(
SaveArg<1>(&reply_session_id_void),
InvokeWithoutArgs(
[&] () {
*reinterpret_cast<uint32_t *>(reply_session_id_void) = 501;
}
),
Return(1)
)
);
EXPECT_CALL(
mock_shmem_ipc,
popData(_)
).WillOnce(Return(1));
nano_header_sender(
attachment,
&http_headers_data,
&ctx,
AttachmentDataType::REQUEST_HEADER,
session_data->session_id,
&session_data->remaining_messages_to_reply,
true
);
}
TEST_F(NanoAttachmentIoTest, NanoBodySender)
{
EXPECT_CALL(
mock_shmem_ipc,
sendChunkedData(_, _, _, _)
).Times(3)
.WillOnce(Return(0))
.WillOnce(Return(0))
.WillOnce(Return(0));
EXPECT_CALL(
mock_nano_socket,
read(_, _, _)
).WillRepeatedly(
DoAll(
SaveArg<1>(&reply_session_id_void),
InvokeWithoutArgs(
[&] () {
*reinterpret_cast<uint32_t *>(reply_session_id_void) = 501;
}
),
Return(1)
)
);
EXPECT_CALL(
mock_nano_poll,
poll(_, _, _)
).WillRepeatedly(Return(1));
EXPECT_CALL(
mock_nano_socket,
write(_, _, _)
).WillRepeatedly(Return(sizeof(SessionID)));
EXPECT_CALL(
mock_nano_socket,
close(_)
).WillRepeatedly(Return(0));
EXPECT_CALL(
mock_nano_socket,
connect(_, _, _)
).WillRepeatedly(Return(0));
EXPECT_CALL(
mock_nano_socket,
socket(_, _, _)
).WillRepeatedly(Return(0));
EXPECT_CALL(
initializer_mocker,
write_to_service(attachment, _, _, _, _)
).WillRepeatedly(Return(NanoCommunicationResult::NANO_OK));
EXPECT_CALL(
mock_shmem_ipc,
isDataAvailable(_)
).WillRepeatedly(Return(1));
EXPECT_CALL(
mock_shmem_ipc,
receiveData(_, _, _)
).WillRepeatedly(
DoAll(
SaveArg<2>(&replay_data_mock),
InvokeWithoutArgs(
[&] () {
*replay_data_mock = (const char *)reply_from_service_mock;
}
),
Return(1)
)
);
EXPECT_CALL(
mock_shmem_ipc,
popData(_)
).WillRepeatedly(Return(1));
nano_body_sender(
attachment,
&http_body_data,
&ctx,
AttachmentDataType::REQUEST_BODY,
session_data->session_id,
&session_data->remaining_messages_to_reply
);
}
TEST_F(NanoAttachmentIoTest, NanoSendResponseContentLength)
{
EXPECT_CALL(
mock_shmem_ipc,
sendChunkedData(_, _, _, _)
).WillOnce(Return(0));
nano_send_response_content_length(
attachment,
332,
&ctx,
session_data->session_id,
&session_data->remaining_messages_to_reply
);
EXPECT_EQ(session_data->remaining_messages_to_reply, 1u);
}
TEST_F(NanoAttachmentIoTest, NanoSendResponseCode)
{
EXPECT_CALL(
mock_shmem_ipc,
sendChunkedData(_, _, _, _)
).WillOnce(Return(0));
nano_send_response_code(
attachment,
443,
&ctx,
session_data->session_id,
&session_data->remaining_messages_to_reply
);
EXPECT_EQ(session_data->remaining_messages_to_reply, 1u);
}
TEST_F(NanoAttachmentIoTest, NanoEndTransactionSender)
{
reply_from_service_mock->verdict = static_cast<uint16_t>(ServiceVerdict::TRAFFIC_VERDICT_ACCEPT);
EXPECT_CALL(
mock_shmem_ipc,
sendChunkedData(_, _, _, _)
).WillOnce(Return(0));
EXPECT_CALL(
mock_nano_poll,
poll(_, _, _)
).Times(2)
.WillOnce(Return(1))
.WillOnce(Return(2));
EXPECT_CALL(
mock_nano_socket,
write(_, _, _)
).WillOnce(Return(sizeof(SessionID)));
EXPECT_CALL(
mock_nano_socket,
read(_, _, _)
).WillOnce(
DoAll(
SaveArg<1>(&reply_session_id_void),
InvokeWithoutArgs(
[&] () {
*reinterpret_cast<uint32_t *>(reply_session_id_void) = 501;
}
),
Return(1)
)
);
EXPECT_CALL(
mock_shmem_ipc,
isDataAvailable(_)
).WillOnce(Return(1));
EXPECT_CALL(
mock_shmem_ipc,
receiveData(_, _, _)
).WillOnce(
DoAll(
SaveArg<2>(&replay_data_mock),
InvokeWithoutArgs(
[&] () {
*replay_data_mock = reinterpret_cast<const char *>(reply_from_service_mock);
}
),
Return(1)
)
);
EXPECT_CALL(
mock_shmem_ipc,
popData(_)
).WillOnce(Return(1));
nano_end_transaction_sender(
attachment,
AttachmentDataType::REQUEST_END,
&ctx,
session_data->session_id,
&session_data->remaining_messages_to_reply
);
EXPECT_EQ(session_data->remaining_messages_to_reply, 0u);
EXPECT_EQ(session_data->verdict, ServiceVerdict::TRAFFIC_VERDICT_ACCEPT);
}
TEST_F(NanoAttachmentIoTest, NanoDelayedTransactionSender)
{
reply_from_service_mock->verdict = static_cast<uint16_t>(ServiceVerdict::TRAFFIC_VERDICT_ACCEPT);
EXPECT_CALL(
mock_shmem_ipc,
sendChunkedData(_, _, _, _)
).WillOnce(Return(0));
EXPECT_CALL(
mock_nano_poll,
poll(_, _, _)
).Times(2)
.WillOnce(Return(1))
.WillOnce(Return(2));
EXPECT_CALL(
mock_nano_socket,
write(_, _, _)
).WillOnce(Return(sizeof(SessionID)));
EXPECT_CALL(
mock_nano_socket,
read(_, _, _)
).WillOnce(
DoAll(
SaveArg<1>(&reply_session_id_void),
InvokeWithoutArgs(
[&] () {
*reinterpret_cast<uint32_t *>(reply_session_id_void) = 501;
}
),
Return(1)
)
);
EXPECT_CALL(
mock_shmem_ipc,
isDataAvailable(_)
).WillOnce(Return(1));
EXPECT_CALL(
mock_shmem_ipc,
receiveData(_, _, _)
).WillOnce(
DoAll(
SaveArg<2>(&replay_data_mock),
InvokeWithoutArgs(
[&] () {
*replay_data_mock = reinterpret_cast<const char *>(reply_from_service_mock);
}
),
Return(1)
)
);
EXPECT_CALL(
mock_shmem_ipc,
popData(_)
).WillOnce(Return(1));
nano_request_delayed_verdict(
attachment,
&ctx,
session_data->session_id,
&session_data->remaining_messages_to_reply
);
EXPECT_EQ(session_data->remaining_messages_to_reply, 0u);
EXPECT_EQ(session_data->verdict, ServiceVerdict::TRAFFIC_VERDICT_ACCEPT);
}
TEST_F(NanoAttachmentIoTest, NanoDropResponseBadResponse)
{
web_response_data->web_response_type = static_cast<uint16_t>(NanoWebResponseType::NO_WEB_RESPONSE);
reply_from_service_mock->verdict = static_cast<uint16_t>(ServiceVerdict::TRAFFIC_VERDICT_DROP);
EXPECT_CALL(
mock_shmem_ipc,
sendChunkedData(_, _, _, _)
).WillOnce(Return(0));
EXPECT_CALL(
mock_nano_poll,
poll(_, _, _)
).Times(2)
.WillOnce(Return(1))
.WillOnce(Return(2));
EXPECT_CALL(
mock_nano_socket,
write(_, _, _)
).WillOnce(Return(sizeof(SessionID)));
EXPECT_CALL(
mock_nano_socket,
read(_, _, _)
).WillOnce(
DoAll(
SaveArg<1>(&reply_session_id_void),
InvokeWithoutArgs(
[&] () {
*reinterpret_cast<uint32_t *>(reply_session_id_void) = 501;
}
),
Return(1)
)
);
EXPECT_CALL(
mock_shmem_ipc,
isDataAvailable(_)
).WillOnce(Return(1));
EXPECT_CALL(
mock_shmem_ipc,
receiveData(_, _, _)
).WillOnce(
DoAll(
SaveArg<2>(&replay_data_mock),
InvokeWithoutArgs(
[&] () {
*replay_data_mock = reinterpret_cast<const char *>(reply_from_service_mock);
}
),
Return(1)
)
);
EXPECT_CALL(
mock_shmem_ipc,
popData(_)
).WillOnce(Return(1));
nano_end_transaction_sender(
attachment,
AttachmentDataType::REQUEST_END,
&ctx,
session_data->session_id,
&session_data->remaining_messages_to_reply
);
EXPECT_EQ(session_data->remaining_messages_to_reply, 0u);
EXPECT_EQ(session_data->verdict, ServiceVerdict::TRAFFIC_VERDICT_DROP);
}
TEST_F(NanoAttachmentIoTest, NanoDropCustomResponse)
{
web_response_data->web_response_type = static_cast<uint16_t>(NanoWebResponseType::CUSTOM_WEB_RESPONSE);
reply_from_service_mock->verdict = static_cast<uint16_t>(ServiceVerdict::TRAFFIC_VERDICT_DROP);
EXPECT_CALL(
mock_shmem_ipc,
sendChunkedData(_, _, _, _)
).WillOnce(Return(0));
EXPECT_CALL(
mock_nano_poll,
poll(_, _, _)
).Times(2)
.WillOnce(Return(1))
.WillOnce(Return(2));
EXPECT_CALL(
mock_nano_socket,
write(_, _, _)
).WillOnce(Return(sizeof(SessionID)));
EXPECT_CALL(
mock_nano_socket,
read(_, _, _)
).WillOnce(
DoAll(
SaveArg<1>(&reply_session_id_void),
InvokeWithoutArgs(
[&] () {
*reinterpret_cast<uint32_t *>(reply_session_id_void) = 501;
}
),
Return(1)
)
);
EXPECT_CALL(
mock_shmem_ipc,
isDataAvailable(_)
).WillOnce(Return(1));
EXPECT_CALL(
mock_shmem_ipc,
receiveData(_, _, _)
).WillOnce(
DoAll(
SaveArg<2>(&replay_data_mock),
InvokeWithoutArgs(
[&] () {
*replay_data_mock = reinterpret_cast<const char *>(reply_from_service_mock);
}
),
Return(1)
)
);
EXPECT_CALL(
mock_shmem_ipc,
popData(_)
).WillOnce(Return(1));
nano_end_transaction_sender(
attachment,
AttachmentDataType::REQUEST_END,
&ctx,
session_data->session_id,
&session_data->remaining_messages_to_reply
);
EXPECT_EQ(session_data->remaining_messages_to_reply, 0u);
EXPECT_EQ(session_data->verdict, ServiceVerdict::TRAFFIC_VERDICT_DROP);
}
TEST_F(NanoAttachmentIoTest, NanoDropRedirectResponse)
{
web_response_data->web_response_type = static_cast<uint16_t>(NanoWebResponseType::REDIRECT_WEB_RESPONSE);
reply_from_service_mock->verdict = static_cast<uint16_t>(ServiceVerdict::TRAFFIC_VERDICT_DROP);
EXPECT_CALL(
mock_shmem_ipc,
sendChunkedData(_, _, _, _)
).WillOnce(Return(0));
EXPECT_CALL(
mock_nano_poll,
poll(_, _, _)
).Times(2)
.WillOnce(Return(1))
.WillOnce(Return(2));
EXPECT_CALL(
mock_nano_socket,
write(_, _, _)
).WillOnce(Return(sizeof(SessionID)));
EXPECT_CALL(
mock_nano_socket,
read(_, _, _)
).WillOnce(
DoAll(
SaveArg<1>(&reply_session_id_void),
InvokeWithoutArgs(
[&] () {
*reinterpret_cast<uint32_t *>(reply_session_id_void) = 501;
}
),
Return(1)
)
);
EXPECT_CALL(
mock_shmem_ipc,
isDataAvailable(_)
).WillOnce(Return(1));
EXPECT_CALL(
mock_shmem_ipc,
receiveData(_, _, _)
).WillOnce(
DoAll(
SaveArg<2>(&replay_data_mock),
InvokeWithoutArgs(
[&] () {
*replay_data_mock = reinterpret_cast<const char *>(reply_from_service_mock);
}
),
Return(1)
)
);
EXPECT_CALL(
mock_shmem_ipc,
popData(_)
).WillOnce(Return(1));
nano_end_transaction_sender(
attachment,
AttachmentDataType::REQUEST_END,
&ctx,
session_data->session_id,
&session_data->remaining_messages_to_reply
);
EXPECT_EQ(session_data->remaining_messages_to_reply, 0u);
EXPECT_EQ(session_data->verdict, ServiceVerdict::TRAFFIC_VERDICT_DROP);
}
TEST_F(NanoAttachmentIoTest, NanoInjectResponse)
{
reply_from_service_mock->modification_count = 1;
reply_from_service_mock->verdict = static_cast<uint16_t>(ServiceVerdict::TRAFFIC_VERDICT_INJECT);
EXPECT_CALL(
mock_shmem_ipc,
sendChunkedData(_, _, _, _)
).WillOnce(Return(0));
EXPECT_CALL(
mock_nano_poll,
poll(_, _, _)
).Times(2)
.WillOnce(Return(1))
.WillOnce(Return(2));
EXPECT_CALL(
mock_nano_socket,
write(_, _, _)
).WillOnce(Return(sizeof(SessionID)));
EXPECT_CALL(
mock_nano_socket,
read(_, _, _)
).WillOnce(
DoAll(
SaveArg<1>(&reply_session_id_void),
InvokeWithoutArgs(
[&] () {
*reinterpret_cast<uint32_t *>(reply_session_id_void) = 501;
}
),
Return(1)
)
);
EXPECT_CALL(
mock_shmem_ipc,
isDataAvailable(_)
).WillOnce(Return(1));
EXPECT_CALL(
mock_shmem_ipc,
receiveData(_, _, _)
).WillOnce(
DoAll(
SaveArg<2>(&replay_data_mock),
InvokeWithoutArgs(
[&] () {
*replay_data_mock = reinterpret_cast<const char *>(reply_from_service_mock);
}
),
Return(1)
)
);
EXPECT_CALL(
mock_shmem_ipc,
popData(_)
).WillOnce(Return(1));
nano_end_transaction_sender(
attachment,
AttachmentDataType::REQUEST_END,
&ctx,
session_data->session_id,
&session_data->remaining_messages_to_reply
);
EXPECT_EQ(session_data->remaining_messages_to_reply, 0u);
EXPECT_EQ(session_data->verdict, ServiceVerdict::TRAFFIC_VERDICT_INSPECT);
}
TEST_F(NanoAttachmentIoTest, NanoSendMetricData)
{
reply_from_service_mock->verdict = static_cast<uint16_t>(ServiceVerdict::TRAFFIC_VERDICT_ACCEPT);
EXPECT_CALL(
mock_shmem_ipc,
sendChunkedData(_, _, _, _)
).WillOnce(Return(0));
nano_send_metric_data_sender(attachment);
EXPECT_EQ(session_data->remaining_messages_to_reply, 0u);
}

View File

@@ -0,0 +1,102 @@
#include "cptest.h"
#include "nano_attachment_common.h"
#include "attachment_types.h"
#include "mock_nano_initializer.h"
extern "C" {
#include "nano_attachment.h"
#include "nano_attachment_metric.h"
}
using namespace std;
using namespace testing;
class NanoAttachmentMetricTest : public Test
{
public:
void
SetUp() override
{
EXPECT_CALL(
initializer_mocker,
nano_attachment_init_process(_)
).WillOnce(
Return(NanoCommunicationResult::NANO_OK)
);
setenv("CLOUDGUARD_UID", "Testing", 1);
attachment = InitNanoAttachment(
static_cast<uint8_t>(AttachmentType::NGINX_ATT_ID),
2,
4,
STDOUT_FILENO
);
EXPECT_NE(attachment, nullptr);
}
void
TearDown() override
{
FiniNanoAttachment(attachment);
}
NanoAttachment *attachment;
StrictMock<NanoInitializerMocker> initializer_mocker;
};
TEST_F(NanoAttachmentMetricTest, CheckMetricsFunctions)
{
updateMetricField(attachment, AttachmentMetricType::INJECT_VERDICTS_COUNT, 100u);
updateMetricField(attachment, AttachmentMetricType::DROP_VERDICTS_COUNT, 200u);
updateMetricField(attachment, AttachmentMetricType::ACCEPT_VERDICTS_COUNT, 300u);
updateMetricField(attachment, AttachmentMetricType::IRRELEVANT_VERDICTS_COUNT, 400u);
updateMetricField(attachment, AttachmentMetricType::MAX_RES_PPROCESSING_TIME_UNTIL_VERDICT, 400u);
updateMetricField(attachment, AttachmentMetricType::MAX_RES_PPROCESSING_TIME_UNTIL_VERDICT, 200u);
updateMetricField(attachment, AttachmentMetricType::MIN_RES_PPROCESSING_TIME_UNTIL_VERDICT, 50u);
updateMetricField(attachment, AttachmentMetricType::MIN_RES_PPROCESSING_TIME_UNTIL_VERDICT, 150u);
updateMetricField(attachment, AttachmentMetricType::AVERAGE_RES_PPROCESSING_TIME_UNTIL_VERDICT, 50u);
updateMetricField(attachment, AttachmentMetricType::AVERAGE_RES_PPROCESSING_TIME_UNTIL_VERDICT, 50u);
updateMetricField(attachment, AttachmentMetricType::AVERAGE_RES_PPROCESSING_TIME_UNTIL_VERDICT, 20u);
EXPECT_EQ(attachment->metric_data[static_cast<int>(AttachmentMetricType::INJECT_VERDICTS_COUNT)], 100u);
EXPECT_EQ(attachment->metric_data[static_cast<int>(AttachmentMetricType::DROP_VERDICTS_COUNT)], 200u);
EXPECT_EQ(attachment->metric_data[static_cast<int>(AttachmentMetricType::ACCEPT_VERDICTS_COUNT)], 300u);
EXPECT_EQ(attachment->metric_data[static_cast<int>(AttachmentMetricType::IRRELEVANT_VERDICTS_COUNT)], 400u);
EXPECT_EQ(
attachment->metric_data[static_cast<int>(AttachmentMetricType::MAX_RES_PPROCESSING_TIME_UNTIL_VERDICT)],
400u
);
EXPECT_EQ(
attachment->metric_data[static_cast<int>(AttachmentMetricType::MIN_RES_PPROCESSING_TIME_UNTIL_VERDICT)],
50u
);
EXPECT_EQ(
attachment->metric_data[static_cast<int>(AttachmentMetricType::AVERAGE_RES_PPROCESSING_TIME_UNTIL_VERDICT)],
40u
);
reset_metric_data(attachment);
EXPECT_EQ(attachment->metric_data[static_cast<int>(AttachmentMetricType::INJECT_VERDICTS_COUNT)], 0u);
EXPECT_EQ(attachment->metric_data[static_cast<int>(AttachmentMetricType::DROP_VERDICTS_COUNT)], 0u);
EXPECT_EQ(attachment->metric_data[static_cast<int>(AttachmentMetricType::ACCEPT_VERDICTS_COUNT)], 0u);
EXPECT_EQ(attachment->metric_data[static_cast<int>(AttachmentMetricType::IRRELEVANT_VERDICTS_COUNT)], 0u);
EXPECT_EQ(
attachment->metric_data[static_cast<int>(AttachmentMetricType::MAX_RES_PPROCESSING_TIME_UNTIL_VERDICT)],
0u
);
EXPECT_EQ(
attachment->metric_data[static_cast<int>(AttachmentMetricType::MIN_RES_PPROCESSING_TIME_UNTIL_VERDICT)],
0u
);
EXPECT_EQ(
attachment->metric_data[static_cast<int>(AttachmentMetricType::AVERAGE_RES_PPROCESSING_TIME_UNTIL_VERDICT)],
0u
);
}

View File

@@ -0,0 +1,737 @@
#include "cptest.h"
#include "nano_attachment_common.h"
#include "attachment_types.h"
#include "mock_nano_initializer.h"
#include "mock_nano_attachment_thread.h"
extern "C" {
#include "nano_attachment.h"
#include "nano_attachment_sender.h"
#include "nano_attachment_sender_thread.h"
}
using namespace std;
using namespace testing;
class NanoAttachmentSenderTest : public Test
{
public:
void
SetUp() override
{
EXPECT_CALL(
initializer_mocker,
nano_attachment_init_process(_)
).WillOnce(
Return(NanoCommunicationResult::NANO_OK)
);
setenv("CLOUDGUARD_UID", "Testing", 1);
attachment = InitNanoAttachment(
static_cast<uint8_t>(AttachmentType::NGINX_ATT_ID),
2,
4,
STDOUT_FILENO
);
EXPECT_NE(attachment, nullptr);
attachment->dbg_level = nano_http_cp_debug_level_e::DBG_LEVEL_TRACE;
session_data = InitSessionData(attachment, 1);
EXPECT_NE(session_data, nullptr);
start_data.session_data = session_data;
req_header_data.session_data = session_data;
res_header_data.session_data = session_data;
req_body_data.session_data = session_data;
res_body_data.session_data = session_data;
req_end_data.session_data = session_data;
res_end_data.session_data = session_data;
req_filter_data.session_data = session_data;
}
void
TearDown() override
{
FiniSessionData(attachment, session_data);
FiniNanoAttachment(attachment);
}
nano_str_t
create_nano_str(const char *str)
{
nano_str_t nano_str;
nano_str.data = reinterpret_cast<unsigned char *>(const_cast<char *>(str));
nano_str.len = strlen(str);
return nano_str;
}
void
NanoRunInThreadTimeoutInvoker(
AttachmentData *data,
void *arg,
NanoCommunicationResult com_res,
ServiceVerdict verdict
)
{
HttpEventThreadCtx *ctx = (HttpEventThreadCtx *)arg;
init_thread_ctx(ctx, attachment, data);
ctx->session_data_p = data->session_data;
ctx->res = com_res;
ctx->session_data_p->verdict = verdict;
}
HttpMetaData http_meta_data = {
create_nano_str("HTTP/1.1"),
create_nano_str("GET"),
create_nano_str("www.nanoattachmentut.com"),
create_nano_str("192.168.1.100"),
80,
create_nano_str("/dogs.html"),
create_nano_str("192.168.1.101"),
253,
create_nano_str("nanoattachmentut.com"),
create_nano_str("/dogs.html")
};
HttpHeaderData http_headers[3] = {
{
create_nano_str("Host"),
create_nano_str("www.nanoattachmentut.com")
},
{
create_nano_str("User-Agent"),
create_nano_str("Mozilla/5.0")
},
{
create_nano_str("Accept"),
create_nano_str("text/html")
}
};
HttpHeaders http_headers_data = {
http_headers,
3
};
HttpRequestFilterData request_filter_data = {
&http_meta_data,
&http_headers_data,
false
};
nano_str_t body[3] = {
create_nano_str("Hello"),
create_nano_str("World"),
create_nano_str("!")
};
HttpBody http_body_data = {
body,
3
};
AttachmentData start_data = {
1,
HttpChunkType::HTTP_REQUEST_METADATA,
session_data,
(DataBuffer)&http_meta_data
};
AttachmentData req_filter_data = {
1,
HttpChunkType::HTTP_REQUEST_FILTER,
session_data,
(DataBuffer)&request_filter_data
};
AttachmentData req_header_data = {
1,
HttpChunkType::HTTP_REQUEST_HEADER,
session_data,
(DataBuffer)&http_headers_data
};
AttachmentData res_header_data = {
1,
HttpChunkType::HTTP_RESPONSE_HEADER,
session_data,
(DataBuffer)&http_headers_data
};
AttachmentData req_body_data = {
1,
HttpChunkType::HTTP_REQUEST_BODY,
session_data,
(DataBuffer)&http_body_data
};
AttachmentData res_body_data = {
1,
HttpChunkType::HTTP_RESPONSE_BODY,
session_data,
(DataBuffer)&http_body_data
};
AttachmentData req_end_data = {
1,
HttpChunkType::HTTP_REQUEST_END,
session_data,
NULL
};
AttachmentData res_end_data = {
1,
HttpChunkType::HTTP_RESPONSE_END,
session_data,
NULL
};
NanoAttachment *attachment;
HttpSessionData *session_data;
StrictMock<NanoInitializerMocker> initializer_mocker;
StrictMock<NanoAttachmentThreadMocker> thread_mocker;
};
TEST_F(NanoAttachmentSenderTest, SendRequestCorruptMemory)
{
AttachmentVerdictResponse response;
EXPECT_CALL(
initializer_mocker,
handle_shmem_corruption(attachment)).WillOnce(Return(NanoCommunicationResult::NANO_ERROR)
);
response = SendMetadata(attachment, &start_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_ACCEPT);
EXPECT_EQ(response.web_response_data, nullptr);
EXPECT_EQ(response.modifications, nullptr);
}
TEST_F(NanoAttachmentSenderTest, SendRequestFailOpenDrop)
{
AttachmentVerdictResponse response;
CustomResponseData *custom_response_data;
EXPECT_CALL(
initializer_mocker,
handle_shmem_corruption(attachment)).WillOnce(Return(NanoCommunicationResult::NANO_ERROR)
);
attachment->fail_mode_verdict = static_cast<int>(NanoCommunicationResult::NANO_HTTP_FORBIDDEN);
response = SendMetadata(attachment, &start_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_DROP);
EXPECT_NE(response.web_response_data, nullptr);
EXPECT_EQ(response.web_response_data->web_response_type, NanoWebResponseType::CUSTOM_WEB_RESPONSE);
EXPECT_STREQ(
reinterpret_cast<const char *>(
response.web_response_data->uuid
),
"20118dba-81f7-4999-8e94-003cf242f5dd"
);
EXPECT_NE(response.web_response_data->data, nullptr);
EXPECT_EQ(response.modifications, nullptr);
custom_response_data = (CustomResponseData *)response.web_response_data->data;
EXPECT_EQ(custom_response_data->response_code, 403);
EXPECT_STREQ(reinterpret_cast<const char *>(custom_response_data->title), "Default Title");
EXPECT_STREQ(reinterpret_cast<const char *>(custom_response_data->body), "Default Body");
}
TEST_F(NanoAttachmentSenderTest, SendRequestTimeout)
{
AttachmentVerdictResponse response;
void *_ctx;
AttachmentData *_attachment_data;
EXPECT_CALL(
initializer_mocker,
handle_shmem_corruption(attachment)).WillOnce(Return(NanoCommunicationResult::NANO_OK)
);
EXPECT_CALL(
thread_mocker,
NanoRunInThreadTimeout(
attachment,
&start_data,
_,
_,
_,
_,
TransactionType::REQUEST
)
).WillOnce(DoAll(SaveArg<1>(&_attachment_data), SaveArg<3>(&_ctx),
InvokeWithoutArgs(
[&] () {
NanoRunInThreadTimeoutInvoker(
_attachment_data,
_ctx,
NanoCommunicationResult::NANO_TIMEOUT,
ServiceVerdict::TRAFFIC_VERDICT_INSPECT
);
}
),
Return(0))
);
response = SendMetadata(attachment, &start_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_ACCEPT);
EXPECT_EQ(response.web_response_data, nullptr);
EXPECT_EQ(response.modifications, nullptr);
}
TEST_F(NanoAttachmentSenderTest, SendRequestIrrelevant)
{
AttachmentVerdictResponse response;
void *_ctx;
AttachmentData *_attachment_data;
EXPECT_CALL(
initializer_mocker,
handle_shmem_corruption(attachment)).WillOnce(Return(NanoCommunicationResult::NANO_OK)
);
EXPECT_CALL(
thread_mocker,
NanoRunInThreadTimeout(
attachment,
&start_data,
_,
_,
_,
_,
TransactionType::REQUEST
)
).WillOnce(DoAll(SaveArg<1>(&_attachment_data), SaveArg<3>(&_ctx),
InvokeWithoutArgs(
[&] () {
NanoRunInThreadTimeoutInvoker(
_attachment_data,
_ctx,
NanoCommunicationResult::NANO_DECLINED,
ServiceVerdict::TRAFFIC_VERDICT_ACCEPT
);
}
),
Return(1))
);
response = SendMetadata(attachment, &start_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_ACCEPT);
EXPECT_EQ(response.web_response_data, nullptr);
EXPECT_EQ(response.modifications, nullptr);
}
TEST_F(NanoAttachmentSenderTest, SendRequestFailed)
{
AttachmentVerdictResponse response;
void *_ctx;
AttachmentData *_attachment_data;
EXPECT_CALL(
initializer_mocker,
handle_shmem_corruption(attachment)).WillOnce(Return(NanoCommunicationResult::NANO_OK)
);
EXPECT_CALL(
thread_mocker,
NanoRunInThreadTimeout(
attachment,
&start_data,
_,
_,
_,
_,
TransactionType::REQUEST
)
).WillOnce(DoAll(SaveArg<1>(&_attachment_data), SaveArg<3>(&_ctx),
InvokeWithoutArgs(
[&] () {
NanoRunInThreadTimeoutInvoker(
_attachment_data,
_ctx,
NanoCommunicationResult::NANO_ERROR,
ServiceVerdict::TRAFFIC_VERDICT_INSPECT
);
}
),
Return(1))
);
response = SendMetadata(attachment, &start_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_ACCEPT);
EXPECT_EQ(response.web_response_data, nullptr);
EXPECT_EQ(response.modifications, nullptr);
}
TEST_F(NanoAttachmentSenderTest, SendRequestFilter)
{
AttachmentVerdictResponse response;
void *_ctx;
AttachmentData *_attachment_data;
EXPECT_CALL(
initializer_mocker,
handle_shmem_corruption(attachment)).WillOnce(Return(NanoCommunicationResult::NANO_OK)
);
EXPECT_CALL(
thread_mocker,
NanoRunInThreadTimeout(
attachment,
&req_filter_data,
_,
_,
_,
_,
TransactionType::REQUEST
)
).WillOnce(DoAll(SaveArg<1>(&_attachment_data), SaveArg<3>(&_ctx),
InvokeWithoutArgs(
[&] () {
NanoRunInThreadTimeoutInvoker(
_attachment_data,
_ctx,
NanoCommunicationResult::NANO_OK,
ServiceVerdict::TRAFFIC_VERDICT_INSPECT
);
}
),
Return(1))
);
response = SendRequestFilter(attachment, &req_filter_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_INSPECT);
EXPECT_EQ(response.web_response_data, nullptr);
EXPECT_EQ(response.modifications, nullptr);
}
TEST_F(NanoAttachmentSenderTest, SendMetadata)
{
AttachmentVerdictResponse response;
void *_ctx;
AttachmentData *_attachment_data;
EXPECT_CALL(
initializer_mocker,
handle_shmem_corruption(attachment)).WillOnce(Return(NanoCommunicationResult::NANO_OK)
);
EXPECT_CALL(
thread_mocker,
NanoRunInThreadTimeout(
attachment,
&start_data,
_,
_,
_,
_,
TransactionType::REQUEST
)
).WillOnce(DoAll(SaveArg<1>(&_attachment_data), SaveArg<3>(&_ctx),
InvokeWithoutArgs(
[&] () {
NanoRunInThreadTimeoutInvoker(
_attachment_data,
_ctx,
NanoCommunicationResult::NANO_OK,
ServiceVerdict::TRAFFIC_VERDICT_INSPECT
);
}
),
Return(1))
);
response = SendMetadata(attachment, &start_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_INSPECT);
EXPECT_EQ(response.web_response_data, nullptr);
EXPECT_EQ(response.modifications, nullptr);
}
TEST_F(NanoAttachmentSenderTest, SendRequestHeaders)
{
AttachmentVerdictResponse response;
void *_ctx;
AttachmentData *_attachment_data;
EXPECT_CALL(
initializer_mocker,
handle_shmem_corruption(attachment)).WillOnce(Return(NanoCommunicationResult::NANO_OK)
);
EXPECT_CALL(
thread_mocker,
NanoRunInThreadTimeout(
attachment,
&req_header_data,
_,
_,
_,
_,
TransactionType::REQUEST
)
).WillOnce(DoAll(SaveArg<1>(&_attachment_data), SaveArg<3>(&_ctx),
InvokeWithoutArgs(
[&] () {
NanoRunInThreadTimeoutInvoker(
_attachment_data,
_ctx,
NanoCommunicationResult::NANO_OK,
ServiceVerdict::TRAFFIC_VERDICT_INSPECT
);
}
),
Return(1))
);
response = SendRequestHeaders(attachment, &req_header_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_INSPECT);
EXPECT_EQ(response.web_response_data, nullptr);
EXPECT_EQ(response.modifications, nullptr);
}
TEST_F(NanoAttachmentSenderTest, SendResponseHeaders)
{
AttachmentVerdictResponse response;
void *_ctx;
AttachmentData *_attachment_data;
EXPECT_CALL(
initializer_mocker,
handle_shmem_corruption(attachment)).WillOnce(Return(NanoCommunicationResult::NANO_OK)
);
EXPECT_CALL(
thread_mocker,
NanoRunInThreadTimeout(
attachment,
&res_header_data,
_,
_,
_,
_,
TransactionType::RESPONSE
)
).WillOnce(DoAll(SaveArg<1>(&_attachment_data), SaveArg<3>(&_ctx),
InvokeWithoutArgs(
[&] () {
NanoRunInThreadTimeoutInvoker(
_attachment_data,
_ctx,
NanoCommunicationResult::NANO_OK,
ServiceVerdict::TRAFFIC_VERDICT_INSPECT
);
}
),
Return(1))
);
response = SendResponseHeaders(attachment, &res_header_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_INSPECT);
EXPECT_EQ(response.web_response_data, nullptr);
EXPECT_EQ(response.modifications, nullptr);
}
TEST_F(NanoAttachmentSenderTest, SendRequestBody)
{
AttachmentVerdictResponse response;
void *_ctx;
AttachmentData *_attachment_data;
EXPECT_CALL(
initializer_mocker,
handle_shmem_corruption(attachment)).WillOnce(Return(NanoCommunicationResult::NANO_OK)
);
EXPECT_CALL(
thread_mocker,
NanoRunInThreadTimeout(
attachment,
&req_body_data,
_,
_,
_,
_,
TransactionType::REQUEST
)
).WillOnce(DoAll(SaveArg<1>(&_attachment_data), SaveArg<3>(&_ctx),
InvokeWithoutArgs(
[&] () {
NanoRunInThreadTimeoutInvoker(
_attachment_data,
_ctx,
NanoCommunicationResult::NANO_OK,
ServiceVerdict::TRAFFIC_VERDICT_INSPECT
);
}
),
Return(1))
);
response = SendRequestBody(attachment, &req_body_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_INSPECT);
EXPECT_EQ(response.web_response_data, nullptr);
EXPECT_EQ(response.modifications, nullptr);
}
TEST_F(NanoAttachmentSenderTest, SendResponseBody)
{
AttachmentVerdictResponse response;
void *_ctx;
AttachmentData *_attachment_data;
EXPECT_CALL(
initializer_mocker,
handle_shmem_corruption(attachment)).WillOnce(Return(NanoCommunicationResult::NANO_OK)
);
EXPECT_CALL(
thread_mocker,
NanoRunInThreadTimeout(
attachment,
&res_body_data,
_,
_,
_,
_,
TransactionType::RESPONSE
)
).WillOnce(DoAll(SaveArg<1>(&_attachment_data), SaveArg<3>(&_ctx),
InvokeWithoutArgs(
[&] () {
NanoRunInThreadTimeoutInvoker(
_attachment_data,
_ctx,
NanoCommunicationResult::NANO_OK,
ServiceVerdict::TRAFFIC_VERDICT_INSPECT
);
}
),
Return(1))
);
response = SendResponseBody(attachment, &res_body_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_INSPECT);
EXPECT_EQ(response.web_response_data, nullptr);
EXPECT_EQ(response.modifications, nullptr);
}
TEST_F(NanoAttachmentSenderTest, SendRequestEnd)
{
AttachmentVerdictResponse response;
void *_ctx;
AttachmentData *_attachment_data;
EXPECT_CALL(
initializer_mocker,
handle_shmem_corruption(attachment)).WillOnce(Return(NanoCommunicationResult::NANO_OK)
);
EXPECT_CALL(
thread_mocker,
NanoRunInThreadTimeout(
attachment,
&req_end_data,
_,
_,
_,
_,
TransactionType::REQUEST
)
).WillOnce(DoAll(SaveArg<1>(&_attachment_data), SaveArg<3>(&_ctx),
InvokeWithoutArgs(
[&] () {
NanoRunInThreadTimeoutInvoker(
_attachment_data,
_ctx,
NanoCommunicationResult::NANO_OK,
ServiceVerdict::TRAFFIC_VERDICT_ACCEPT
);
}
),
Return(1))
);
response = SendRequestEnd(attachment, &req_end_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_ACCEPT);
EXPECT_EQ(response.web_response_data, nullptr);
EXPECT_EQ(response.modifications, nullptr);
}
TEST_F(NanoAttachmentSenderTest, SendResponseEnd)
{
AttachmentVerdictResponse response;
void *_ctx;
AttachmentData *_attachment_data;
EXPECT_CALL(
initializer_mocker,
handle_shmem_corruption(attachment)).WillOnce(Return(NanoCommunicationResult::NANO_OK)
);
EXPECT_CALL(
thread_mocker,
NanoRunInThreadTimeout(
attachment,
&res_end_data,
_,
_,
_,
_,
TransactionType::RESPONSE
)
).WillOnce(DoAll(SaveArg<1>(&_attachment_data), SaveArg<3>(&_ctx),
InvokeWithoutArgs(
[&] () {
NanoRunInThreadTimeoutInvoker(
_attachment_data,
_ctx,
NanoCommunicationResult::NANO_OK,
ServiceVerdict::TRAFFIC_VERDICT_ACCEPT
);
}
),
Return(1))
);
response = SendResponseEnd(attachment, &res_end_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_ACCEPT);
EXPECT_EQ(response.web_response_data, nullptr);
EXPECT_EQ(response.modifications, nullptr);
}
TEST_F(NanoAttachmentSenderTest, SendMetricData)
{
EXPECT_CALL(
initializer_mocker,
handle_shmem_corruption(attachment)).WillOnce(Return(NanoCommunicationResult::NANO_OK)
);
EXPECT_CALL(
thread_mocker,
NanoRunInThreadTimeout(
attachment,
_,
_,
_,
_,
_,
TransactionType::METRICS
)
).WillOnce(Return(1));
SendMetricData(attachment);
}

View File

@@ -0,0 +1,452 @@
#include "cptest.h"
#include "nano_attachment_common.h"
#include "attachment_types.h"
#include "mock_nano_socket.h"
#include "mock_nano_initializer.h"
#include "mock_nano_attachment_sender.h"
#include "mock_nano_configuration.h"
extern "C" {
#include "nano_attachment.h"
}
using namespace std;
using namespace testing;
class NanoAttachmentTest : public Test
{
public:
void
SetUp() override
{
EXPECT_CALL(
initializer_mocker,
nano_attachment_init_process(_)
).WillOnce(
Return(NanoCommunicationResult::NANO_OK)
);
setenv("CLOUDGUARD_UID", "Testing", 1);
attachment = InitNanoAttachment(
static_cast<uint8_t>(AttachmentType::NGINX_ATT_ID),
2,
4,
STDOUT_FILENO
);
EXPECT_NE(attachment, nullptr);
session_data = InitSessionData(attachment, 1);
EXPECT_NE(session_data, nullptr);
}
void
TearDown() override
{
FiniSessionData(attachment, session_data);
FiniNanoAttachment(attachment);
}
nano_str_t
create_nano_str(const char *str)
{
nano_str_t nano_str;
nano_str.data = reinterpret_cast<unsigned char *>(const_cast<char *>(str));
nano_str.len = strlen(str);
return nano_str;
}
WebResponseData *
create_custom_drop_response_data()
{
WebResponseData *web_response_data = (WebResponseData *)malloc(sizeof(WebResponseData));
web_response_data->web_response_type = NanoWebResponseType::CUSTOM_WEB_RESPONSE;
memcpy(web_response_data->uuid, "TestThisIsUuidTest\0", 20);
CustomResponseData *custom_response_data = (CustomResponseData *)malloc(sizeof(CustomResponseData));
custom_response_data->response_code = 502;
memcpy(custom_response_data->title, "Woof Woof\0", 10);
memcpy(custom_response_data->body, "This is Ruby's barking\0", 24);
web_response_data->data = custom_response_data;
return web_response_data;
}
WebResponseData *
create_redirect_response_data()
{
WebResponseData *web_response_data = (WebResponseData *)malloc(sizeof(WebResponseData));
web_response_data->web_response_type = NanoWebResponseType::REDIRECT_WEB_RESPONSE;
memcpy(web_response_data->uuid, "TestThisIsUuidTest\0", 20);
RedirectData *redirect_response_data = (RedirectData *)malloc(sizeof(CustomResponseData));
memcpy(redirect_response_data->redirect_location, "Woowwwiee.com\0", 15);
web_response_data->data = redirect_response_data;
return web_response_data;
}
NanoHttpModificationList *
create_modifications_data()
{
NanoHttpModificationList *modification_node =
(NanoHttpModificationList *)malloc(sizeof(NanoHttpModificationList));
modification_node->next = NULL;
modification_node->modification.mod_type = HttpModificationType::APPEND;
modification_node->modification_buffer = NULL;
return modification_node;
}
AttachmentVerdictResponse inspect_response = {
AttachmentVerdict::ATTACHMENT_VERDICT_INSPECT,
1,
NULL,
NULL
};
AttachmentVerdictResponse accept_response = {
AttachmentVerdict::ATTACHMENT_VERDICT_ACCEPT,
1,
NULL,
NULL
};
AttachmentVerdictResponse custom_drop_response = {
AttachmentVerdict::ATTACHMENT_VERDICT_DROP,
1,
NULL,
NULL
};
HttpMetaData http_meta_data = {
create_nano_str("HTTP/1.1"),
create_nano_str("GET"),
create_nano_str("www.nanoattachmentut.com"),
create_nano_str("192.168.1.100"),
80,
create_nano_str("/dogs.html"),
create_nano_str("192.168.1.101"),
253,
create_nano_str("nanoattachmentut.com"),
create_nano_str("/dogs.html")
};
HttpHeaderData http_headers[3] = {
{
create_nano_str("Host"),
create_nano_str("www.nanoattachmentut.com")
},
{
create_nano_str("User-Agent"),
create_nano_str("Mozilla/5.0")
},
{
create_nano_str("Accept"),
create_nano_str("text/html")
}
};
HttpHeaders http_headers_data = {
http_headers,
3
};
nano_str_t body[3] = {
create_nano_str("Hello"),
create_nano_str("World"),
create_nano_str("!")
};
HttpBody http_body_data = {
body,
3
};
AttachmentData start_data = {
1,
HttpChunkType::HTTP_REQUEST_METADATA,
session_data,
(DataBuffer)&http_meta_data
};
AttachmentData req_header_data = {
1,
HttpChunkType::HTTP_REQUEST_HEADER,
session_data,
(DataBuffer)&http_headers_data
};
AttachmentData req_body_data = {
1,
HttpChunkType::HTTP_REQUEST_BODY,
session_data,
(DataBuffer)&http_body_data
};
AttachmentData req_end_data = {
1,
HttpChunkType::HTTP_REQUEST_END,
session_data,
NULL
};
NanoAttachment *attachment;
HttpSessionData *session_data;
StrictMock<NanoAttachmentSenderMocker> sender_mocker;
StrictMock<NanoInitializerMocker> initializer_mocker;
StrictMock<NanoSocketMocker> socket_mocker;
StrictMock<NanoConfigurationMocker> configuration_mocker;
};
TEST_F(NanoAttachmentTest, InitNanoAttachment)
{
EXPECT_EQ(strcmp(attachment->shared_verdict_signal_path, ""), 0);
EXPECT_EQ(attachment->worker_id, 2);
EXPECT_EQ(attachment->num_of_workers, 4);
EXPECT_EQ(attachment->nano_user_id, getuid());
EXPECT_EQ(attachment->nano_group_id, getgid());
EXPECT_EQ(attachment->registration_socket, -1);
EXPECT_EQ(attachment->attachment_type, static_cast<uint8_t>(AttachmentType::NGINX_ATT_ID));
EXPECT_EQ(attachment->comm_socket, -1);
EXPECT_EQ(attachment->logging_fd, STDOUT_FILENO);
EXPECT_EQ(attachment->is_configuration_updated, NanoCommunicationResult::NANO_ERROR);
EXPECT_EQ(attachment->current_config_version, 0u);
EXPECT_EQ(attachment->dbg_level, nano_http_cp_debug_level::DBG_LEVEL_INFO);
EXPECT_EQ(attachment->num_of_connection_attempts, 0);
EXPECT_EQ(attachment->fail_open_timeout, 50u);
EXPECT_EQ(attachment->fail_open_delayed_timeout, 150u);
EXPECT_EQ(attachment->sessions_per_minute_limit_verdict, AttachmentVerdict::ATTACHMENT_VERDICT_ACCEPT);
EXPECT_EQ(attachment->max_sessions_per_minute, 0u);
EXPECT_EQ(attachment->req_max_proccessing_ms_time, 3000u);
EXPECT_EQ(attachment->res_max_proccessing_ms_time, 3000u);
EXPECT_EQ(attachment->registration_thread_timeout_msec, 100u);
EXPECT_EQ(attachment->req_start_thread_timeout_msec, 100u);
EXPECT_EQ(attachment->req_header_thread_timeout_msec, 100u);
EXPECT_EQ(attachment->req_body_thread_timeout_msec, 150u);
EXPECT_EQ(attachment->res_header_thread_timeout_msec, 100u);
EXPECT_EQ(attachment->res_body_thread_timeout_msec, 150u);
EXPECT_EQ(attachment->waiting_for_verdict_thread_timeout_msec, 150u);
EXPECT_EQ(attachment->inspection_mode, NanoHttpInspectionMode::NON_BLOCKING_THREAD);
EXPECT_EQ(attachment->num_of_nano_ipc_elements, 200u);
EXPECT_EQ(attachment->keep_alive_interval_msec, DEFAULT_KEEP_ALIVE_INTERVAL_MSEC);
EXPECT_CALL(
configuration_mocker,
reset_attachment_config(_)
).WillOnce(
Return(NanoCommunicationResult::NANO_OK)
);
RestartAttachmentConfiguration(attachment);
}
TEST_F(NanoAttachmentTest, InitSessionData)
{
EXPECT_NE(session_data, nullptr);
EXPECT_EQ(session_data->was_request_fully_inspected, 0);
EXPECT_EQ(session_data->verdict, ServiceVerdict::TRAFFIC_VERDICT_INSPECT);
EXPECT_EQ(session_data->session_id, 1u);
EXPECT_EQ(session_data->remaining_messages_to_reply, 0u);
EXPECT_EQ(session_data->req_proccesing_time, 0u);
EXPECT_EQ(session_data->res_proccesing_time, 0u);
EXPECT_EQ(session_data->processed_req_body_size, 0u);
EXPECT_EQ(session_data->processed_res_body_size, 0u);
EXPECT_EQ(IsSessionFinalized(attachment, session_data), 0);
}
TEST_F(NanoAttachmentTest, AcceptFlow)
{
EXPECT_CALL(sender_mocker, SendMetadata(attachment, &start_data)).WillOnce(Return(inspect_response));
EXPECT_CALL(sender_mocker, SendRequestHeaders(attachment, &req_header_data)).WillOnce(Return(inspect_response));
EXPECT_CALL(sender_mocker, SendRequestBody(attachment, &req_body_data)).WillOnce(Return(inspect_response));
EXPECT_CALL(sender_mocker, SendRequestEnd(attachment, &req_end_data)).WillOnce(Return(accept_response));
AttachmentVerdictResponse response = SendDataNanoAttachment(attachment, &start_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_INSPECT);
EXPECT_EQ(GetWebResponseType(attachment, session_data, &response), NanoWebResponseType::NO_WEB_RESPONSE);
FreeAttachmentResponseContent(attachment, session_data, &response);
response = SendDataNanoAttachment(attachment, &req_header_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_INSPECT);
EXPECT_EQ(GetWebResponseType(attachment, session_data, &response), NanoWebResponseType::NO_WEB_RESPONSE);
FreeAttachmentResponseContent(attachment, session_data, &response);
response = SendDataNanoAttachment(attachment, &req_body_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_INSPECT);
EXPECT_EQ(GetWebResponseType(attachment, session_data, &response), NanoWebResponseType::NO_WEB_RESPONSE);
FreeAttachmentResponseContent(attachment, session_data, &response);
response = SendDataNanoAttachment(attachment, &req_end_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_ACCEPT);
EXPECT_EQ(GetWebResponseType(attachment, session_data, &response), NanoWebResponseType::NO_WEB_RESPONSE);
FreeAttachmentResponseContent(attachment, session_data, &response);
}
TEST_F(NanoAttachmentTest, DropFlow)
{
AttachmentVerdictResponse response;
BlockPageData block_page_data;
custom_drop_response.web_response_data = create_custom_drop_response_data();
EXPECT_CALL(sender_mocker, SendMetadata(attachment, &start_data)).WillOnce(Return(inspect_response));
EXPECT_CALL(sender_mocker, SendRequestHeaders(attachment, &req_header_data)).WillOnce(Return(inspect_response));
EXPECT_CALL(sender_mocker, SendRequestBody(attachment, &req_body_data)).WillOnce(Return(inspect_response));
EXPECT_CALL(sender_mocker, SendRequestEnd(attachment, &req_end_data)).WillOnce(Return(custom_drop_response));
response = SendDataNanoAttachment(attachment, &start_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_INSPECT);
EXPECT_EQ(GetWebResponseType(attachment, session_data, &response), NanoWebResponseType::NO_WEB_RESPONSE);
FreeAttachmentResponseContent(attachment, session_data, &response);
response = SendDataNanoAttachment(attachment, &req_header_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_INSPECT);
EXPECT_EQ(GetWebResponseType(attachment, session_data, &response), NanoWebResponseType::NO_WEB_RESPONSE);
FreeAttachmentResponseContent(attachment, session_data, &response);
response = SendDataNanoAttachment(attachment, &req_body_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_INSPECT);
EXPECT_EQ(GetWebResponseType(attachment, session_data, &response), NanoWebResponseType::NO_WEB_RESPONSE);
FreeAttachmentResponseContent(attachment, session_data, &response);
response = SendDataNanoAttachment(attachment, &req_end_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_DROP);
EXPECT_EQ(GetWebResponseType(attachment, session_data, &response), NanoWebResponseType::CUSTOM_WEB_RESPONSE);
block_page_data = GetBlockPage(attachment, session_data, &response);
EXPECT_EQ(block_page_data.response_code, 502);
EXPECT_EQ(strcmp((char *)block_page_data.uuid.data, "TestThisIsUuidTest"), 0);
EXPECT_EQ(strcmp((char *)block_page_data.title.data, "Woof Woof"), 0);
EXPECT_EQ(strcmp((char *)block_page_data.body.data, "This is Ruby's barking"), 0);
FreeAttachmentResponseContent(attachment, session_data, &response);
EXPECT_EQ(GetWebResponseType(attachment, session_data, &response), NanoWebResponseType::NO_WEB_RESPONSE);
}
TEST_F(NanoAttachmentTest, RedirectFlow)
{
AttachmentVerdictResponse response;
RedirectPageData redirect_page_data;
custom_drop_response.web_response_data = create_redirect_response_data();
EXPECT_CALL(sender_mocker, SendMetadata(attachment, &start_data)).WillOnce(Return(inspect_response));
EXPECT_CALL(sender_mocker, SendRequestHeaders(attachment, &req_header_data)).WillOnce(Return(inspect_response));
EXPECT_CALL(sender_mocker, SendRequestBody(attachment, &req_body_data)).WillOnce(Return(inspect_response));
EXPECT_CALL(sender_mocker, SendRequestEnd(attachment, &req_end_data)).WillOnce(Return(custom_drop_response));
response = SendDataNanoAttachment(attachment, &start_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_INSPECT);
EXPECT_EQ(GetWebResponseType(attachment, session_data, &response), NanoWebResponseType::NO_WEB_RESPONSE);
FreeAttachmentResponseContent(attachment, session_data, &response);
response = SendDataNanoAttachment(attachment, &req_header_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_INSPECT);
EXPECT_EQ(GetWebResponseType(attachment, session_data, &response), NanoWebResponseType::NO_WEB_RESPONSE);
FreeAttachmentResponseContent(attachment, session_data, &response);
response = SendDataNanoAttachment(attachment, &req_body_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_INSPECT);
EXPECT_EQ(GetWebResponseType(attachment, session_data, &response), NanoWebResponseType::NO_WEB_RESPONSE);
FreeAttachmentResponseContent(attachment, session_data, &response);
response = SendDataNanoAttachment(attachment, &req_end_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_DROP);
EXPECT_EQ(GetWebResponseType(attachment, session_data, &response), NanoWebResponseType::REDIRECT_WEB_RESPONSE);
redirect_page_data = GetRedirectPage(attachment, session_data, &response);
EXPECT_EQ(strcmp((char *)redirect_page_data.redirect_location.data, "Woowwwiee.com"), 0);
FreeAttachmentResponseContent(attachment, session_data, &response);
EXPECT_EQ(GetWebResponseType(attachment, session_data, &response), NanoWebResponseType::NO_WEB_RESPONSE);
}
TEST_F(NanoAttachmentTest, ModificationsFlow)
{
AttachmentVerdictResponse response;
NanoResponseModifications response_modifications;
accept_response.modifications = create_modifications_data();
EXPECT_CALL(sender_mocker, SendMetadata(attachment, &start_data)).WillOnce(Return(inspect_response));
EXPECT_CALL(sender_mocker, SendRequestHeaders(attachment, &req_header_data)).WillOnce(Return(inspect_response));
EXPECT_CALL(sender_mocker, SendRequestBody(attachment, &req_body_data)).WillOnce(Return(inspect_response));
EXPECT_CALL(sender_mocker, SendRequestEnd(attachment, &req_end_data)).WillOnce(Return(accept_response));
response = SendDataNanoAttachment(attachment, &start_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_INSPECT);
EXPECT_EQ(GetWebResponseType(attachment, session_data, &response), NanoWebResponseType::NO_WEB_RESPONSE);
FreeAttachmentResponseContent(attachment, session_data, &response);
response = SendDataNanoAttachment(attachment, &req_header_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_INSPECT);
EXPECT_EQ(GetWebResponseType(attachment, session_data, &response), NanoWebResponseType::NO_WEB_RESPONSE);
FreeAttachmentResponseContent(attachment, session_data, &response);
response = SendDataNanoAttachment(attachment, &req_body_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_INSPECT);
EXPECT_EQ(GetWebResponseType(attachment, session_data, &response), NanoWebResponseType::NO_WEB_RESPONSE);
FreeAttachmentResponseContent(attachment, session_data, &response);
response = SendDataNanoAttachment(attachment, &req_end_data);
EXPECT_EQ(response.session_id, 1u);
EXPECT_EQ(response.verdict, AttachmentVerdict::ATTACHMENT_VERDICT_ACCEPT);
EXPECT_EQ(GetWebResponseType(attachment, session_data, &response), NanoWebResponseType::NO_WEB_RESPONSE);
EXPECT_EQ(IsResponseWithModification(attachment, session_data, &response), 1);
response_modifications = GetResponseModifications(attachment, session_data, &response);
EXPECT_NE(response_modifications.modifications, nullptr);
EXPECT_EQ(response_modifications.modifications->next, nullptr);
EXPECT_EQ(response_modifications.modifications->modification.mod_type, HttpModificationType::APPEND);
FreeAttachmentResponseContent(attachment, session_data, &response);
EXPECT_EQ(GetWebResponseType(attachment, session_data, &response), NanoWebResponseType::NO_WEB_RESPONSE);
EXPECT_EQ(IsResponseWithModification(attachment, session_data, &response), 0);
}
TEST_F(NanoAttachmentTest, SendAlive)
{
EXPECT_CALL(socket_mocker, socket(AF_UNIX, SOCK_STREAM, 0)).WillOnce(Return(34));
EXPECT_CALL(socket_mocker, connect(34, _, _)).WillOnce(Return(0));
EXPECT_CALL(initializer_mocker, write_to_service(attachment, _, _, _, _))
.WillRepeatedly(Return(NanoCommunicationResult::NANO_OK));
EXPECT_CALL(socket_mocker, close(34));
SendKeepAlive(attachment);
}
TEST_F(NanoAttachmentTest, SendAliveFail)
{
EXPECT_CALL(socket_mocker, socket(AF_UNIX, SOCK_STREAM, 0)).WillOnce(Return(34));
EXPECT_CALL(socket_mocker, connect(34, _, _)).WillOnce(Return(-1));
EXPECT_CALL(socket_mocker, close(34));
SendKeepAlive(attachment);
}
TEST_F(NanoAttachmentTest, TestMetricUpdate)
{
EXPECT_EQ(attachment->metric_data[static_cast<int>(AttachmentMetricType::INJECT_VERDICTS_COUNT)], 0u);
UpdateMetric(attachment, AttachmentMetricType::INJECT_VERDICTS_COUNT, 100);
EXPECT_EQ(attachment->metric_data[static_cast<int>(AttachmentMetricType::INJECT_VERDICTS_COUNT)], 100u);
}
TEST_F(NanoAttachmentTest, SendAccumulatedMetricData)
{
EXPECT_CALL(sender_mocker, SendMetricData(attachment)).WillOnce(Return(NanoCommunicationResult::NANO_OK));
SendAccumulatedMetricData(attachment);
}

View File

@@ -0,0 +1,129 @@
#include "cptest.h"
#include "nano_attachment_common.h"
#include "attachment_types.h"
#include "http_configuration.h"
#include <fstream>
extern "C" {
#include "nano_attachment.h"
#include "nano_configuration.h"
#include "nano_utils.h"
}
using namespace std;
using namespace testing;
class NanoConfigurationTest : public Test
{
public:
string
createIPRangesString(const vector<string> &ip_ranges)
{
stringstream ip_ranges_string_stream;
ip_ranges_string_stream << "[";
for (auto iterator = ip_ranges.begin(); iterator < ip_ranges.end() - 1; iterator++) {
ip_ranges_string_stream << "\"" << *iterator << "\"" << ", ";
}
ip_ranges_string_stream << "\"" << ip_ranges.back() << "\"]";
return ip_ranges_string_stream.str();
}
NanoAttachment attachment;
LoggingData logging_data;
const string static_resources_path = "/dev/shm/static_resources/";
const vector<string> ip_ranges = { "8.8.8.8", "9.9.9.9-10.10.10.10", "0:0:0:0:0:0:0:1-0:0:0:0:0:0:0:4"};
const string attachment_configuration_file_name = "cp_nano_http_attachment_conf";
};
TEST_F(NanoConfigurationTest, InitAttachmentConfiguration)
{
NanoCommunicationResult res;
string valid_configuration =
"{\n"
"\"context_values\": {"
"\"clientIp\": \"1.2.3.4\","
"\"listeningIp\": \"5.6.7.8\","
"\"uriPrefix\": \"/abc\","
"\"hostName\": \"test\","
"\"httpMethod\": \"GET\","
"\"listeningPort\": 80"
"},"
"\"is_fail_open_mode_enabled\": 0,\n"
"\"fail_open_timeout\": 1234,\n"
"\"is_fail_open_mode_hold_enabled\": 0,\n"
"\"fail_open_hold_timeout\": 4321,\n"
"\"sessions_per_minute_limit_verdict\": \"Accept\",\n"
"\"max_sessions_per_minute\": 0,\n"
"\"num_of_nginx_ipc_elements\": 200,\n"
"\"keep_alive_interval_msec\": 10000,\n"
"\"dbg_level\": 2,\n"
"\"nginx_inspection_mode\": 1,\n"
"\"operation_mode\": 0,\n"
"\"req_body_thread_timeout_msec\": 155,\n"
"\"req_proccessing_timeout_msec\": 42,\n"
"\"registration_thread_timeout_msec\": 101,\n"
"\"res_proccessing_timeout_msec\": 420,\n"
"\"res_header_thread_timeout_msec\": 1,\n"
"\"res_body_thread_timeout_msec\": 80,\n"
"\"waiting_for_verdict_thread_timeout_msec\": 60,\n"
"\"req_header_thread_timeout_msec\": 10,\n"
"\"ip_ranges\": " + createIPRangesString(ip_ranges) + ",\n"
"\"static_resources_path\": \"" + static_resources_path + "\""
"}\n";
ofstream valid_configuration_file(attachment_configuration_file_name);
valid_configuration_file << valid_configuration;
valid_configuration_file.close();
attachment.shared_verdict_signal_path[0] = '\0';
attachment.worker_id = 2;
attachment.num_of_workers = 4;
attachment.nano_user_id = getuid();
attachment.nano_group_id = getgid();
attachment.registration_socket = -1;
attachment.attachment_type = static_cast<uint8_t>(AttachmentType::NGINX_ATT_ID);
attachment.nano_service_ipc = NULL;
attachment.comm_socket = -1;
attachment.logging_data = &logging_data;
res = set_logging_fd(&attachment, STDOUT_FILENO);
EXPECT_EQ(res, NanoCommunicationResult::NANO_OK);
setenv("CLOUDGUARD_UID", "Testing", 1);
res = set_docker_id(&attachment);
EXPECT_EQ(res, NanoCommunicationResult::NANO_OK);
res = set_unique_id(&attachment);
EXPECT_EQ(res, NanoCommunicationResult::NANO_OK);
attachment.is_configuration_updated = NanoCommunicationResult::NANO_ERROR;
attachment.current_config_version = 0;
attachment.dbg_level = nano_http_cp_debug_level_e::DBG_LEVEL_TRACE;
res = init_attachment_config(&attachment, attachment_configuration_file_name.c_str());
EXPECT_EQ(res, NanoCommunicationResult::NANO_OK);
EXPECT_EQ(attachment.is_configuration_updated, NanoCommunicationResult::NANO_OK);
EXPECT_EQ(attachment.dbg_level, nano_http_cp_debug_level_e::DBG_LEVEL_INFO);
EXPECT_EQ(attachment.fail_mode_verdict, 1);
EXPECT_EQ(attachment.fail_open_timeout, 1234u);
EXPECT_EQ(attachment.fail_mode_delayed_verdict, 1);
EXPECT_EQ(attachment.fail_open_delayed_timeout, 4321u);
EXPECT_EQ(attachment.sessions_per_minute_limit_verdict, AttachmentVerdict::ATTACHMENT_VERDICT_ACCEPT);
EXPECT_EQ(attachment.max_sessions_per_minute, 0u);
EXPECT_EQ(attachment.req_max_proccessing_ms_time, 42u);
EXPECT_EQ(attachment.res_max_proccessing_ms_time, 420u);
EXPECT_EQ(attachment.registration_thread_timeout_msec, 101u);
EXPECT_EQ(attachment.req_header_thread_timeout_msec, 10u);
EXPECT_EQ(attachment.req_body_thread_timeout_msec, 155u);
EXPECT_EQ(attachment.res_header_thread_timeout_msec, 1u);
EXPECT_EQ(attachment.res_body_thread_timeout_msec, 80u);
EXPECT_EQ(attachment.waiting_for_verdict_thread_timeout_msec, 60u);
EXPECT_EQ(attachment.num_of_nano_ipc_elements, 200u);
EXPECT_EQ(attachment.keep_alive_interval_msec, 10000u);
EXPECT_EQ(attachment.inspection_mode, NanoHttpInspectionMode::BLOCKING_THREAD);
res = reset_attachment_config(&attachment);
EXPECT_EQ(res, NanoCommunicationResult::NANO_ERROR);
}

View File

@@ -0,0 +1,373 @@
#include "cptest.h"
#include "nano_attachment_common.h"
#include "attachment_types.h"
#include <poll.h>
#include <stdlib.h>
#include "mock_nano_socket.h"
#include "mock_shmem_ipc.h"
#include "mock_nano_access.h"
#include "mock_nano_poll.h"
#include "mock_nano_configuration.h"
#include "mock_nano_stat.h"
#include "mock_nano_attachment_thread.h"
extern "C" {
#include "nano_initializer.h"
#include "nano_attachment_sender_thread.h"
}
using namespace std;
using namespace testing;
class NanoInitializerTest : public Test
{
public:
void
SetUp() override
{
ipc_holder = make_unique<int>();
mock_ipc = reinterpret_cast<SharedMemoryIPC *>(ipc_holder.get());
set_logging_fd(&attachment, STDOUT_FILENO);
attachment.dbg_level = nano_http_cp_debug_level_e::DBG_LEVEL_TRACE;
}
void
NanoRunInThreadTimeoutInvoker(
AttachmentData *data,
void *arg,
int comm_socket,
NanoCommunicationResult com_res
)
{
HttpEventThreadCtx *ctx = (HttpEventThreadCtx *)arg;
init_thread_ctx(ctx, &attachment, data);
attachment.comm_socket = comm_socket;
attachment.registration_socket = comm_socket;
ctx->res = com_res;
}
unique_ptr<int> ipc_holder;
NanoAttachment attachment;
SharedMemoryIPC *mock_ipc;
void *_ctx;
StrictMock<NanoSocketMocker> socket_mocker;
StrictMock<NanoShmemIPCMocker> ipc_mocker;
StrictMock<NanoAccessMocker> access_mocker;
StrictMock<NanoPollMocker> poll_mocker;
StrictMock<NanoConfigurationMocker> config_mocker;
StrictMock<NanoStatMocker> stat_mocker;
StrictMock<NanoAttachmentThreadMocker> thread_mocker;
};
TEST_F(NanoInitializerTest, InitNanoAttachmentProcess)
{
NanoCommunicationResult res;
struct pollfd mock_s_poll;
struct pollfd *mock_s_poll_ptr = &mock_s_poll;
attachment.registration_state = nano_attachment_registration_state::REGISTERED;
attachment.comm_socket = -1;
attachment.nano_service_ipc = nullptr;
EXPECT_CALL(
poll_mocker,
poll(_, _, _)
).WillRepeatedly(
DoAll(
SaveArg<0>(&mock_s_poll_ptr),
InvokeWithoutArgs(
[&] () {
mock_s_poll_ptr->revents = POLLIN;
}
),
Return(1)
)
);
EXPECT_CALL(
socket_mocker,
write(_, _, _)
).WillRepeatedly(Return(1));
EXPECT_CALL(
socket_mocker,
read(
_,
_,
_
)
).Times(1)
.WillOnce(Return(sizeof(uint8_t)));
EXPECT_CALL(
thread_mocker,
NanoRunInThreadTimeout(
&attachment,
_,
_,
_,
_,
_,
TransactionType::REGISTRATION
)
).WillOnce(DoAll(SaveArg<3>(&_ctx),
InvokeWithoutArgs(
[&] () {
NanoRunInThreadTimeoutInvoker(
nullptr,
_ctx,
32,
NanoCommunicationResult::NANO_OK
);
}
),
Return(1))
);
EXPECT_CALL(
config_mocker,
init_attachment_config(
&attachment,
_
)
).Times(2)
.WillOnce(Return(NanoCommunicationResult::NANO_ERROR))
.WillOnce(Return(NanoCommunicationResult::NANO_OK));
EXPECT_CALL(
access_mocker,
access(
_,
_
)
).WillOnce(Return(0));
EXPECT_CALL(
ipc_mocker,
initIpc(
_,
_,
_,
_,
_,
_,
_
)
).WillOnce(Return(mock_ipc));
res = nano_attachment_init_process(&attachment);
EXPECT_EQ(res, NanoCommunicationResult::NANO_OK);
}
TEST_F(NanoInitializerTest, RegisterToAttachment)
{
NanoCommunicationResult res;
struct pollfd mock_s_poll;
struct pollfd *mock_s_poll_ptr = &mock_s_poll;
attachment.comm_socket = 53;
attachment.registration_socket = 34;
attachment.nano_service_ipc = mock_ipc;
EXPECT_CALL(
socket_mocker,
close(34)
).WillOnce(Return(0));
EXPECT_CALL(
socket_mocker,
close(88)
).WillOnce(Return(0));
EXPECT_CALL(
thread_mocker,
NanoRunInThreadTimeout(
&attachment,
_,
_,
_,
_,
_,
TransactionType::REGISTRATION
)
).WillOnce(DoAll(SaveArg<3>(&_ctx),
InvokeWithoutArgs(
[&] () {
NanoRunInThreadTimeoutInvoker(
nullptr,
_ctx,
88,
NanoCommunicationResult::NANO_OK
);
}
),
Return(1))
);
EXPECT_CALL(
socket_mocker,
write(_, _, _)
).WillRepeatedly(Return(1));
EXPECT_CALL(
poll_mocker,
poll(_, _, _)
).WillRepeatedly(
DoAll(
SaveArg<0>(&mock_s_poll_ptr),
InvokeWithoutArgs(
[&] () {
mock_s_poll_ptr->revents = POLLIN;
}
),
Return(1)
)
);
EXPECT_CALL(
socket_mocker,
read(
_,
_,
_
)
).Times(1)
.WillOnce(Return(sizeof(uint8_t)));
res = register_to_attachments_manager(&attachment);
EXPECT_EQ(res, NanoCommunicationResult::NANO_OK);
}
TEST_F(NanoInitializerTest, DisconnectCommunication)
{
int res;
attachment.comm_socket = 53;
attachment.nano_service_ipc = mock_ipc;
EXPECT_CALL(
socket_mocker,
close(53)
).WillOnce(Return(-1));
EXPECT_CALL(
ipc_mocker,
destroyIpc(mock_ipc, 0)
).WillOnce(Return());
EXPECT_CALL(
ipc_mocker,
isCorruptedShmem(mock_ipc, 0)
).WillOnce(Return(0));
handle_shmem_corruption(&attachment);
disconnect_communication(&attachment);
EXPECT_EQ(attachment.comm_socket, -1);
EXPECT_EQ(attachment.nano_service_ipc, nullptr);
res = isIpcReady(&attachment);
EXPECT_EQ(res, 0);
}
TEST_F(NanoInitializerTest, RestartCommunication)
{
NanoCommunicationResult res;
struct pollfd mock_s_poll;
struct pollfd *mock_s_poll_ptr = &mock_s_poll;
attachment.comm_socket = 53;
attachment.registration_socket = 34;
attachment.nano_service_ipc = mock_ipc;
EXPECT_CALL(
ipc_mocker,
destroyIpc(mock_ipc, 0)
).WillOnce(Return());
EXPECT_CALL(
poll_mocker,
poll(_, _, _)
).WillRepeatedly(
DoAll(
SaveArg<0>(&mock_s_poll_ptr),
InvokeWithoutArgs(
[&] () {
mock_s_poll_ptr->revents = POLLIN;
}
),
Return(1)
)
);
EXPECT_CALL(
socket_mocker,
write(_, _, _)
).WillRepeatedly(Return(1));
EXPECT_CALL(
socket_mocker,
read(
_,
_,
_
)
).Times(1)
.WillOnce(Return(sizeof(uint8_t)));
EXPECT_CALL(
ipc_mocker,
initIpc(
_,
_,
_,
_,
_,
_,
_
)
).WillOnce(Return(mock_ipc));
EXPECT_CALL(
thread_mocker,
NanoRunInThreadTimeout(
&attachment,
_,
_,
_,
_,
_,
TransactionType::REGISTRATION
)
).WillOnce(DoAll(SaveArg<3>(&_ctx),
InvokeWithoutArgs(
[&] () {
NanoRunInThreadTimeoutInvoker(
nullptr,
_ctx,
53,
NanoCommunicationResult::NANO_OK
);
}
),
Return(1))
);
res = restart_communication(&attachment);
EXPECT_EQ(res, NanoCommunicationResult::NANO_OK);
}
TEST_F(NanoInitializerTest, SetLoggingFailure)
{
NanoCommunicationResult res;
EXPECT_CALL(stat_mocker, mkdir(_, _)).WillOnce(Return(-1));
res = set_logging_fd(&attachment, 0);
EXPECT_EQ(res, NanoCommunicationResult::NANO_ERROR);
}

View File

@@ -0,0 +1,466 @@
#include "cptest.h"
#include "nano_attachment_common.h"
#include "attachment_types.h"
#include "mock_nano_initializer.h"
#include "mock_nano_attachment_io.h"
extern "C" {
#include "nano_attachment.h"
#include "nano_attachment_sender_thread.h"
}
using namespace std;
using namespace testing;
class NanoAttachmentSenderThreadTest : public Test
{
public:
void
SetUp() override
{
EXPECT_CALL(
initializer_mocker,
nano_attachment_init_process(_)
).WillOnce(
Return(NanoCommunicationResult::NANO_OK)
);
setenv("CLOUDGUARD_UID", "Testing", 1);
attachment = InitNanoAttachment(
static_cast<uint8_t>(AttachmentType::NGINX_ATT_ID),
2,
4,
STDOUT_FILENO
);
EXPECT_NE(attachment, nullptr);
attachment->dbg_level = nano_http_cp_debug_level::DBG_LEVEL_TRACE;
session_data = InitSessionData(attachment, 1);
EXPECT_NE(session_data, nullptr);
start_data.session_data = session_data;
req_header_data.session_data = session_data;
res_header_data.session_data = session_data;
req_body_data.session_data = session_data;
res_body_data.session_data = session_data;
req_end_data.session_data = session_data;
res_end_data.session_data = session_data;
delayed_verdict_data.session_data = session_data;
req_filter_data.session_data = session_data;
}
void
TearDown() override
{
FiniSessionData(attachment, session_data);
FiniNanoAttachment(attachment);
}
nano_str_t
create_nano_str(const char *str)
{
nano_str_t nano_str;
nano_str.data = reinterpret_cast<unsigned char *>(const_cast<char *>(str));
nano_str.len = strlen(str);
return nano_str;
}
HttpMetaData http_meta_data = {
create_nano_str("HTTP/1.1"),
create_nano_str("GET"),
create_nano_str("www.nanoattachmentut.com"),
create_nano_str("192.168.1.100"),
80,
create_nano_str("/dogs.html"),
create_nano_str("192.168.1.101"),
253,
create_nano_str("nanoattachmentut.com"),
create_nano_str("/dogs.html")
};
HttpHeaderData http_headers[3] = {
{
create_nano_str("Host"),
create_nano_str("www.nanoattachmentut.com")
},
{
create_nano_str("User-Agent"),
create_nano_str("Mozilla/5.0")
},
{
create_nano_str("Accept"),
create_nano_str("text/html")
}
};
HttpHeaders http_headers_data = {
http_headers,
3
};
HttpRequestFilterData request_filter_data = {
&http_meta_data,
&http_headers_data,
false
};
ResHttpHeaders res_http_headers_data = {
&http_headers_data,
static_cast<uint16_t>(522),
static_cast<uint64_t>(300)
};
nano_str_t body[3] = {
create_nano_str("Hello"),
create_nano_str("World"),
create_nano_str("!")
};
HttpBody http_body_data = {
body,
3
};
AttachmentData start_data = {
1,
HttpChunkType::HTTP_REQUEST_METADATA,
session_data,
(DataBuffer)&http_meta_data
};
AttachmentData req_header_data = {
1,
HttpChunkType::HTTP_REQUEST_HEADER,
session_data,
(DataBuffer)&http_headers_data
};
AttachmentData req_filter_data = {
1,
HttpChunkType::HTTP_REQUEST_FILTER,
session_data,
(DataBuffer)&request_filter_data
};
AttachmentData res_header_data = {
1,
HttpChunkType::HTTP_RESPONSE_HEADER,
session_data,
(DataBuffer)&res_http_headers_data
};
AttachmentData req_body_data = {
1,
HttpChunkType::HTTP_REQUEST_BODY,
session_data,
(DataBuffer)&http_body_data
};
AttachmentData res_body_data = {
1,
HttpChunkType::HTTP_RESPONSE_BODY,
session_data,
(DataBuffer)&http_body_data
};
AttachmentData req_end_data = {
1,
HttpChunkType::HTTP_REQUEST_END,
session_data,
NULL
};
AttachmentData res_end_data = {
1,
HttpChunkType::HTTP_RESPONSE_END,
session_data,
NULL
};
AttachmentData delayed_verdict_data = {
1,
HttpChunkType::HOLD_DATA,
session_data,
NULL
};
NanoAttachment *attachment;
HttpSessionData *session_data;
StrictMock<NanoInitializerMocker> initializer_mocker;
StrictMock<NanoAttachmentIoMocker> io_mocker;
};
TEST_F(NanoAttachmentSenderThreadTest, InitThreadCtx)
{
HttpEventThreadCtx ctx;
init_thread_ctx(&ctx, attachment, &req_body_data);
EXPECT_EQ(ctx.attachment, attachment);
EXPECT_EQ(ctx.data, &req_body_data);
EXPECT_EQ(ctx.session_data_p, session_data);
EXPECT_EQ(ctx.res, NanoCommunicationResult::NANO_OK);
EXPECT_EQ(ctx.web_response_data, nullptr);
EXPECT_EQ(ctx.modifications, nullptr);
}
TEST_F(NanoAttachmentSenderThreadTest, RegistrationCommSocketThread)
{
HttpEventThreadCtx ctx;
EXPECT_CALL(
io_mocker,
connect_to_comm_socket(attachment)
);
init_thread_ctx(&ctx, attachment, nullptr);
RegistrationCommSocketThread(&ctx);
}
TEST_F(NanoAttachmentSenderThreadTest, RegistrationSocketThread)
{
HttpEventThreadCtx ctx;
EXPECT_CALL(
io_mocker,
connect_to_registration_socket(attachment)
);
init_thread_ctx(&ctx, attachment, nullptr);
RegistrationSocketThread(&ctx);
}
TEST_F(NanoAttachmentSenderThreadTest, SendMetadataThread)
{
HttpEventThreadCtx ctx;
EXPECT_CALL(
io_mocker,
nano_metadata_sender(
attachment,
(HttpMetaData *)start_data.data,
&ctx,
session_data->session_id,
&session_data->remaining_messages_to_reply,
false
)
);
init_thread_ctx(&ctx, attachment, &start_data);
SendMetadataThread(&ctx);
}
TEST_F(NanoAttachmentSenderThreadTest, SendRequestFilterThread)
{
HttpEventThreadCtx ctx;
EXPECT_CALL(
io_mocker,
nano_metadata_sender(
attachment,
(HttpMetaData *)start_data.data,
&ctx,
session_data->session_id,
&session_data->remaining_messages_to_reply,
false
)
);
EXPECT_CALL(
io_mocker,
nano_header_sender(
attachment,
(HttpHeaders *)req_header_data.data,
&ctx,
AttachmentDataType::REQUEST_HEADER,
session_data->session_id,
&session_data->remaining_messages_to_reply,
false
)
);
EXPECT_CALL(
io_mocker,
nano_end_transaction_sender(
attachment,
AttachmentDataType::REQUEST_END,
&ctx,
session_data->session_id,
&session_data->remaining_messages_to_reply
)
);
init_thread_ctx(&ctx, attachment, &req_filter_data);
SendRequestFilterThread(&ctx);
}
TEST_F(NanoAttachmentSenderThreadTest, SendRequestHeadersThread)
{
HttpEventThreadCtx ctx;
EXPECT_CALL(
io_mocker,
nano_header_sender(
attachment,
(HttpHeaders *)req_header_data.data,
&ctx,
AttachmentDataType::REQUEST_HEADER,
session_data->session_id,
&session_data->remaining_messages_to_reply,
false
)
);
init_thread_ctx(&ctx, attachment, &req_header_data);
SendRequestHeadersThread(&ctx);
}
TEST_F(NanoAttachmentSenderThreadTest, SendResponseHeadersThread)
{
HttpEventThreadCtx ctx;
ResHttpHeaders *res_headers = (ResHttpHeaders *)res_header_data.data;
EXPECT_CALL(
io_mocker,
nano_send_response_code(
attachment,
static_cast<uint16_t>(522),
&ctx,
session_data->session_id,
&session_data->remaining_messages_to_reply
)
);
EXPECT_CALL(
io_mocker,
nano_send_response_content_length(
attachment,
static_cast<uint64_t>(300),
&ctx,
session_data->session_id,
&session_data->remaining_messages_to_reply
)
);
EXPECT_CALL(
io_mocker,
nano_header_sender(
attachment,
(HttpHeaders *)res_headers->headers,
&ctx,
AttachmentDataType::RESPONSE_HEADER,
session_data->session_id,
&session_data->remaining_messages_to_reply,
false
)
);
init_thread_ctx(&ctx, attachment, &res_header_data);
SendResponseHeadersThread(&ctx);
}
TEST_F(NanoAttachmentSenderThreadTest, SendRequestBodyThread)
{
HttpEventThreadCtx ctx;
EXPECT_CALL(
io_mocker,
nano_body_sender(
attachment,
(HttpBody *)req_body_data.data,
&ctx,
AttachmentDataType::REQUEST_BODY,
session_data->session_id,
&session_data->remaining_messages_to_reply
)
);
init_thread_ctx(&ctx, attachment, &req_body_data);
SendRequestBodyThread(&ctx);
}
TEST_F(NanoAttachmentSenderThreadTest, SendResponseBodyThread)
{
HttpEventThreadCtx ctx;
EXPECT_CALL(
io_mocker,
nano_body_sender(
attachment,
(HttpBody *)res_body_data.data,
&ctx,
AttachmentDataType::RESPONSE_BODY,
session_data->session_id,
&session_data->remaining_messages_to_reply
)
);
init_thread_ctx(&ctx, attachment, &res_body_data);
SendResponseBodyThread(&ctx);
}
TEST_F(NanoAttachmentSenderThreadTest, SendRequestEndThread)
{
HttpEventThreadCtx ctx;
EXPECT_CALL(
io_mocker,
nano_end_transaction_sender(
attachment,
AttachmentDataType::REQUEST_END,
&ctx,
session_data->session_id,
&session_data->remaining_messages_to_reply
)
);
init_thread_ctx(&ctx, attachment, &req_end_data);
SendRequestEndThread(&ctx);
}
TEST_F(NanoAttachmentSenderThreadTest, SendResponseEndThread)
{
HttpEventThreadCtx ctx;
EXPECT_CALL(
io_mocker,
nano_end_transaction_sender(
attachment,
AttachmentDataType::RESPONSE_END,
&ctx,
session_data->session_id,
&session_data->remaining_messages_to_reply
)
);
init_thread_ctx(&ctx, attachment, &res_end_data);
SendResponseEndThread(&ctx);
}
TEST_F(NanoAttachmentSenderThreadTest, SendDelayedVerdictRequestThread)
{
HttpEventThreadCtx ctx;
EXPECT_CALL(
io_mocker,
nano_request_delayed_verdict(
attachment,
&ctx,
session_data->session_id,
&session_data->remaining_messages_to_reply
)
);
init_thread_ctx(&ctx, attachment, &delayed_verdict_data);
SendDelayedVerdictRequestThread(&ctx);
}
TEST_F(NanoAttachmentSenderThreadTest, SendMetricToServiceThread)
{
HttpEventThreadCtx ctx;
EXPECT_CALL(
io_mocker,
nano_send_metric_data_sender(attachment)
);
init_thread_ctx(&ctx, attachment, &delayed_verdict_data);
SendMetricToServiceThread(&ctx);
}

View File

@@ -0,0 +1,129 @@
#include "cptest.h"
#include "nano_attachment_common.h"
#include "attachment_types.h"
#include "mock_nano_socket.h"
#include "mock_nano_initializer.h"
#include "mock_nano_sender_thread.h"
extern "C" {
#include "nano_attachment.h"
#include "nano_attachment_thread.h"
#include "nano_attachment_sender_thread.h"
}
using namespace std;
using namespace testing;
class NanoThreadTest : public Test
{
public:
void
SetUp() override
{
EXPECT_CALL(
initializer_mocker,
nano_attachment_init_process(_)
).WillOnce(
Return(NanoCommunicationResult::NANO_OK)
);
setenv("CLOUDGUARD_UID", "Testing", 1);
attachment = InitNanoAttachment(
static_cast<uint8_t>(AttachmentType::NGINX_ATT_ID),
2,
4,
STDOUT_FILENO
);
EXPECT_NE(attachment, nullptr);
attachment->dbg_level = nano_http_cp_debug_level_e::DBG_LEVEL_TRACE;
session_data = InitSessionData(attachment, 1);
EXPECT_NE(session_data, nullptr);
ctx_data.session_data = session_data;
}
void
TearDown() override
{
FiniSessionData(attachment, session_data);
FiniNanoAttachment(attachment);
}
nano_str_t
create_nano_str(const char *str)
{
nano_str_t nano_str;
nano_str.data = reinterpret_cast<unsigned char *>(const_cast<char *>(str));
nano_str.len = strlen(str);
return nano_str;
}
AttachmentVerdictResponse accept_response = {
AttachmentVerdict::ATTACHMENT_VERDICT_ACCEPT,
1,
NULL,
NULL
};
AttachmentData ctx_data = {
1,
HttpChunkType::HTTP_REQUEST_END,
session_data,
NULL
};
HttpEventThreadCtx ctx;
NanoAttachment *attachment;
HttpSessionData *session_data;
StrictMock<NanoSenderThreadMocker> sender_thread_mocker;
StrictMock<NanoInitializerMocker> initializer_mocker;
StrictMock<NanoSocketMocker> socket_mocker;
};
TEST_F(NanoThreadTest, NanoRunInThreadTimeout)
{
int res;
char func_name[] = "SendRequestEndThread";
EXPECT_CALL(
sender_thread_mocker,
SendRequestEndThread(&ctx)
).WillOnce(Return(nullptr));
res = NanoRunInThreadTimeout(
attachment,
&ctx_data,
SendRequestEndThread,
(void *)&ctx,
attachment->req_header_thread_timeout_msec,
func_name,
REQUEST
);
EXPECT_EQ(res, 1);
}
TEST_F(NanoThreadTest, NanoRunInThreadTimeoutNonThread)
{
int res;
char func_name[] = "SendRequestEndThread";
attachment->inspection_mode = NanoHttpInspectionMode::NO_THREAD;
EXPECT_CALL(
sender_thread_mocker,
SendRequestEndThread(&ctx)
).WillOnce(Return(nullptr));
res = NanoRunInThreadTimeout(
attachment,
&ctx_data,
SendRequestEndThread,
(void *)&ctx,
attachment->req_header_thread_timeout_msec,
func_name,
REQUEST
);
EXPECT_EQ(res, 1);
}

View File

@@ -0,0 +1,8 @@
add_definitions(-DUSERSPACE)
add_library(nano_attachment_util SHARED nano_attachment_util.cc)
target_link_libraries(nano_attachment_util http_configuration)
# add_subdirectory(nano_attachment_util_ut)
install(TARGETS nano_attachment_util DESTINATION lib)
install(TARGETS nano_attachment_util DESTINATION http_transaction_handler_service/lib/ PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ)

View File

@@ -0,0 +1,251 @@
// 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 "nano_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);
}
NanoHttpInspectionMode
getInspectionMode()
{
return static_cast<NanoHttpInspectionMode>(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");
}
int
isFailOpenHoldMode()
{
return conf_data.getNumericalValue("is_fail_open_mode_hold_enabled");
}
unsigned int
getFailOpenHoldTimeout()
{
return conf_data.getNumericalValue("fail_open_hold_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");
}
unsigned int
getWaitingForVerdictThreadTimeout()
{
return conf_data.getNumericalValue("waiting_for_verdict_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;
}

View File

@@ -0,0 +1,8 @@
include_directories(${Boost_INCLUDE_DIRS})
include_directories(${CMAKE_SOURCE_DIR}/attachments/nano_attachment/nano_attachment_util)
add_unit_test(
nano_attachment_util_ut
"nano_attachment_util_ut.cc"
"nano_attachment_util"
)

View File

@@ -0,0 +1,125 @@
#include <fstream>
#include <boost/algorithm/string.hpp>
#include <stdlib.h>
#include <arpa/inet.h>
#include "nano_attachment_util.h"
#include "cptest.h"
#include "c_common/ip_common.h"
using namespace std;
using namespace testing;
class HttpAttachmentUtilTest : public Test
{
public:
string
createIPRangesString(const vector<string> &ip_ranges)
{
stringstream ip_ranges_string_stream;
ip_ranges_string_stream << "[";
for (auto iterator = ip_ranges.begin(); iterator < ip_ranges.end() - 1; iterator++) {
ip_ranges_string_stream << "\"" << *iterator << "\"" << ", ";
}
ip_ranges_string_stream << "\"" << ip_ranges.back() << "\"]";
return ip_ranges_string_stream.str();
}
const string attachment_configuration_file_name = "cp_nano_http_attachment_conf";
const vector<string> ip_ranges = { "8.8.8.8", "9.9.9.9-10.10.10.10", "0:0:0:0:0:0:0:2-0:0:0:0:0:0:0:5"};
const string static_resources_path = "/dev/shm/static_resources/";
};
TEST_F(HttpAttachmentUtilTest, GetValidAttachmentConfiguration)
{
string valid_configuration =
"{\n"
"\"context_values\": {"
"\"clientIp\": \"1.2.3.4\","
"\"listeningIp\": \"5.6.7.8\","
"\"uriPrefix\": \"/abc\","
"\"hostName\": \"test\","
"\"httpMethod\": \"GET\","
"\"listeningPort\": 80"
"},"
"\"is_fail_open_mode_enabled\": 0,\n"
"\"fail_open_timeout\": 1234,\n"
"\"is_fail_open_mode_hold_enabled\": 1,\n"
"\"fail_open_hold_timeout\": 4321,\n"
"\"sessions_per_minute_limit_verdict\": \"Accept\",\n"
"\"max_sessions_per_minute\": 0,\n"
"\"num_of_nginx_ipc_elements\": 200,\n"
"\"keep_alive_interval_msec\": 10000,\n"
"\"dbg_level\": 2,\n"
"\"nginx_inspection_mode\": 1,\n"
"\"operation_mode\": 0,\n"
"\"req_body_thread_timeout_msec\": 155,\n"
"\"req_proccessing_timeout_msec\": 42,\n"
"\"registration_thread_timeout_msec\": 101,\n"
"\"res_proccessing_timeout_msec\": 420,\n"
"\"res_header_thread_timeout_msec\": 1,\n"
"\"res_body_thread_timeout_msec\": 0,\n"
"\"waiting_for_verdict_thread_timeout_msec\": 75,\n"
"\"req_header_thread_timeout_msec\": 10,\n"
"\"ip_ranges\": " + createIPRangesString(ip_ranges) + ",\n"
"\"static_resources_path\": \"" + static_resources_path + "\""
"}\n";
ofstream valid_configuration_file(attachment_configuration_file_name);
valid_configuration_file << valid_configuration;
valid_configuration_file.close();
EXPECT_EQ(initAttachmentConfig(attachment_configuration_file_name.c_str()), 1);
EXPECT_EQ(getDbgLevel(), 2u);
EXPECT_EQ(getStaticResourcesPath(), static_resources_path);
EXPECT_EQ(isFailOpenMode(), 0);
EXPECT_EQ(getFailOpenTimeout(), 1234u);
EXPECT_EQ(isFailOpenHoldMode(), 1);
EXPECT_EQ(getFailOpenHoldTimeout(), 4321u);
EXPECT_EQ(isFailOpenOnSessionLimit(), 1);
EXPECT_EQ(getMaxSessionsPerMinute(), 0u);
EXPECT_EQ(getNumOfNginxIpcElements(), 200u);
EXPECT_EQ(getKeepAliveIntervalMsec(), 10000u);
EXPECT_EQ(getResProccessingTimeout(), 420u);
EXPECT_EQ(getReqProccessingTimeout(), 42u);
EXPECT_EQ(getRegistrationThreadTimeout(), 101u);
EXPECT_EQ(getReqHeaderThreadTimeout(), 10u);
EXPECT_EQ(getReqBodyThreadTimeout(), 155u);
EXPECT_EQ(getResHeaderThreadTimeout(), 1u);
EXPECT_EQ(getResBodyThreadTimeout(), 0u);
EXPECT_EQ(getWaitingForVerdictThreadTimeout(), 75u);
EXPECT_EQ(getInspectionMode(), NanoHttpInspectionMode::BLOCKING_THREAD);
EXPECT_EQ(isDebugContext("1.2.3.4", "5.6.7.8", 80, "GET", "test", "/abc"), 1);
EXPECT_EQ(isDebugContext("1.2.3.9", "5.6.7.8", 80, "GET", "test", "/abc"), 0);
EXPECT_EQ(isDebugContext("1.2.3.4", "5.6.7.9", 80, "GET", "test", "/abc"), 0);
EXPECT_EQ(isDebugContext("1.2.3.4", "5.6.7.8", 88, "GET", "test", "/abc"), 0);
EXPECT_EQ(isDebugContext("1.2.3.4", "5.6.7.8", 80, "POST", "test", "/abc"), 0);
EXPECT_EQ(isDebugContext("1.2.3.4", "5.6.7.8", 80, "GET", "est", "/abc"), 0);
EXPECT_EQ(isDebugContext("1.2.3.4", "5.6.7.8", 80, "GET", "test", "/ab"), 0);
EXPECT_EQ(isSkipSource("8.8.8.8"), 1);
EXPECT_EQ(isSkipSource("8.8.8.9"), 0);
EXPECT_EQ(isSkipSource("8.8.8.10"), 0);
EXPECT_EQ(isSkipSource("9.9.9.8"), 0);
EXPECT_EQ(isSkipSource("9.9.9.9"), 1);
EXPECT_EQ(isSkipSource("9.255.0.0"), 1);
EXPECT_EQ(isSkipSource("10.10.10.10"), 1);
EXPECT_EQ(isSkipSource("10.10.10.11"), 0);
EXPECT_EQ(isSkipSource("0:0:0:0:0:0:0:1"), 0);
EXPECT_EQ(isSkipSource("0:0:0:0:0:0:0:2"), 1);
EXPECT_EQ(isSkipSource("0:0:0:0:0:0:0:4"), 1);
EXPECT_EQ(isSkipSource("0:0:0:0:0:0:0:5"), 1);
EXPECT_EQ(isSkipSource("0:0:0:0:0:0:0:6"), 0);
}
TEST_F(HttpAttachmentUtilTest, CheckIPAddrValidity)
{
EXPECT_EQ(isIPAddress("10.0.0.1"), 1);
EXPECT_EQ(isIPAddress("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), 1);
EXPECT_EQ(isIPAddress("333.0.0.1"), 0);
EXPECT_EQ(isIPAddress("2001:0gb8:85a3:0000:0000:8a2e:0370:7334"), 0);
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,144 @@
#include "nano_configuration.h"
#include "nano_attachment_common.h"
#include "nano_utils.h"
#include "nano_attachment_util.h"
#include <unistd.h>
#include <sys/types.h>
NanoCommunicationResult
init_attachment_config(NanoAttachment *attachment, const char *conf_path)
{
int new_dbg_level = DBG_LEVEL_COUNT;
if (access(conf_path, F_OK) != 0) return NANO_ERROR;
if (attachment->is_configuration_updated == NANO_OK) return NANO_OK;
// Initiate attachment using the file in the provided conf_path.
if (!initAttachmentConfig(conf_path)) {
write_dbg(attachment, attachment->worker_id, DBG_LEVEL_WARNING, "Failed to load the configuration");
return NANO_ERROR;
}
new_dbg_level = getDbgLevel();
if (new_dbg_level >= DBG_LEVEL_COUNT) {
write_dbg(
attachment,
attachment->worker_id,
DBG_LEVEL_WARNING,
"Illegal debug level received: %d",
new_dbg_level
);
attachment->is_configuration_updated = NANO_ERROR;
return NANO_ERROR;
}
write_dbg(attachment, attachment->worker_id, DBG_LEVEL_DEBUG, "Setting debug level to: %d", new_dbg_level);
// Setting a new debug level.
attachment->dbg_level = new_dbg_level;
// Setting fail open/close.
attachment->fail_mode_verdict = isFailOpenMode() == 1 ? NANO_OK : NANO_ERROR;
attachment->fail_open_timeout = getFailOpenTimeout();
// Setting fail delayed open/close
attachment->fail_mode_delayed_verdict = isFailOpenHoldMode() == 1 ? NANO_OK : NANO_ERROR;
attachment->fail_open_delayed_timeout = getFailOpenHoldTimeout();
// Setting attachment's variables.
attachment->sessions_per_minute_limit_verdict =
isFailOpenOnSessionLimit() ? ATTACHMENT_VERDICT_ACCEPT : ATTACHMENT_VERDICT_DROP;
attachment->max_sessions_per_minute = getMaxSessionsPerMinute();
attachment->inspection_mode = getInspectionMode();
if (attachment->inspection_mode >= INSPECTION_MODE_COUNT) {
write_dbg(
attachment,
attachment->worker_id,
DBG_LEVEL_WARNING,
"Illegal inspection mode received: %d",
attachment->inspection_mode
);
attachment->is_configuration_updated = NANO_ERROR;
return NANO_ERROR;
}
attachment->req_max_proccessing_ms_time = getReqProccessingTimeout();
attachment->res_max_proccessing_ms_time = getResProccessingTimeout();
attachment->registration_thread_timeout_msec = getRegistrationThreadTimeout();
attachment->req_header_thread_timeout_msec = getReqHeaderThreadTimeout();
attachment->req_body_thread_timeout_msec = getReqBodyThreadTimeout();
attachment->res_header_thread_timeout_msec = getResHeaderThreadTimeout();
attachment->res_body_thread_timeout_msec = getResBodyThreadTimeout();
attachment->waiting_for_verdict_thread_timeout_msec = getWaitingForVerdictThreadTimeout();
attachment->num_of_nano_ipc_elements = getNumOfNginxIpcElements();
attachment->keep_alive_interval_msec = getKeepAliveIntervalMsec();
// set_static_resources_path(getStaticResourcesPath());
attachment->is_configuration_updated = NANO_OK;
attachment->logging_data->dbg_level = attachment->dbg_level;
attachment->logging_data->worker_id = attachment->worker_id;
attachment->logging_data->fd = attachment->logging_fd;
write_dbg(
attachment,
attachment->worker_id,
DBG_LEVEL_INFO,
"Successfully loaded configuration. "
"inspection mode: %d, "
"debug level: %d, "
"failure mode: %s, "
"fail mode timeout: %u msec, "
"failure delayed mode: %s, "
"fail mode delayed timeout: %u msec, "
"sessions per minute limit verdict: %s, "
"max sessions per minute: %u, "
"req max processing time: %u msec, "
"res max processing time: %u msec, "
"registration thread timeout: %u msec, "
"req start thread timeout: %u msec, "
"req header thread timeout: %u msec, "
"req body thread timeout: %u msec, "
"res header thread timeout: %u msec, "
"res body thread timeout: %u msec, "
"delayed thread timeout: %u msec, "
"static resources path: %s, "
"num of nginx ipc elements: %u, "
"keep alive interval msec: %u msec",
attachment->inspection_mode,
attachment->dbg_level,
(attachment->fail_mode_verdict == NANO_OK ? "fail-open" : "fail-close"),
attachment->fail_open_timeout,
(attachment->fail_mode_delayed_verdict == NANO_OK ? "fail-open" : "fail-close"),
attachment->fail_open_delayed_timeout,
attachment->sessions_per_minute_limit_verdict == ATTACHMENT_VERDICT_ACCEPT ? "Accept" : "Drop",
attachment->max_sessions_per_minute,
attachment->req_max_proccessing_ms_time,
attachment->res_max_proccessing_ms_time,
attachment->registration_thread_timeout_msec,
attachment->req_start_thread_timeout_msec,
attachment->req_header_thread_timeout_msec,
attachment->req_body_thread_timeout_msec,
attachment->res_header_thread_timeout_msec,
attachment->res_body_thread_timeout_msec,
attachment->waiting_for_verdict_thread_timeout_msec,
getStaticResourcesPath(),
attachment->num_of_nano_ipc_elements,
attachment->keep_alive_interval_msec
);
return NANO_OK;
}
NanoCommunicationResult
reset_attachment_config(NanoAttachment *attachment)
{
write_dbg(attachment, attachment->worker_id, DBG_LEVEL_INFO, "Resetting attachment configuration");
attachment->is_configuration_updated = NANO_ERROR;
attachment->current_config_version++;
return init_attachment_config(attachment, SHARED_ATTACHMENT_CONF_PATH);
}

View File

@@ -0,0 +1,37 @@
/// @file nano_configuration.h
#ifndef __NANO_CONFIGURATION_H__
#define __NANO_CONFIGURATION_H__
#include <sys/time.h>
#include <assert.h>
#include "nano_attachment_common.h"
#include "nano_initializer.h"
///
/// @brief Initializes the general configuration for a NanoAttachment object.
///
/// This function initializes the general configuration for the specified NanoAttachment object
/// using the configuration file located at the specified path. It updates various configuration
/// parameters such as debug level, fail-open/fail-close mode, session limits, timeouts, and others
/// based on the configuration file.
///
/// @param attachment A pointer to the NanoAttachment object to initialize the configuration for.
/// @param conf_path The path to the configuration file.
/// @return A NanoCommunicationResult indicating the result of the operation.
///
NanoCommunicationResult init_attachment_config(NanoAttachment *attachment, const char *conf_path);
///
/// @brief Resets the configuration of a NanoAttachment object.
///
/// This function resets the configuration of the specified NanoAttachment object by
/// marking it as not updated, incrementing the current configuration version, and
/// reinitializing the general configuration using the specified configuration path.
///
/// @param attachment A pointer to the NanoAttachment object to reset the configuration for.
/// @return A NanoCommunicationResult indicating the result of the operation.
///
NanoCommunicationResult reset_attachment_config(NanoAttachment *attachment);
#endif // __NANO_CONFIGURATION_H__

View File

@@ -0,0 +1,860 @@
// 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 nano_initializer.c
#include "nano_initializer.h"
#include <poll.h>
#include <stdint.h>
#include <dirent.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include "nano_attachment_common.h"
#include "attachment_types.h"
#include "nano_configuration.h"
#include "nano_attachment_io.h"
#include "shmem_ipc_2.h"
#include "nano_utils.h"
#include "nano_attachment_sender_thread.h"
#include "nano_attachment_thread.h"
NanoCommunicationResult
write_to_service(
NanoAttachment *attachment,
int *socket,
void *data,
uint32_t size,
struct timeval *absolute_end_time)
{
int res = 0;
// `remaining_size` and `cur_data_ptr` are used to keep track of where we are in the memory.
// This allows us to write to the socket in parts (if we need to).
uint32_t remaining_size = size;
char *cur_data_ptr = data;
while (remaining_size > 0) {
// If the operation exceeded the allowed time, treat it as a failure.
if (is_absolute_timeout_reached(absolute_end_time)) {
close(*socket);
*socket = -1;
write_dbg(
attachment,
0,
DBG_LEVEL_TRACE,
"Reached timeout while communicating with the socket"
);
return NANO_TIMEOUT;
}
res = write(*socket, (void *)cur_data_ptr, remaining_size);
// `res` is -1 in case of an error: write functions failed or socket wasn't available.
if (res < 0) {
close(*socket);
*socket = -1;
write_dbg(
attachment,
0,
DBG_LEVEL_TRACE,
"Failed to communicate with the socket, Error: %s",
strerror(errno)
);
return NANO_ERROR;
}
remaining_size -= res;
cur_data_ptr += res;
}
return NANO_OK;
}
NanoCommunicationResult
read_from_service(
NanoAttachment *attachment,
int *socket,
void *data,
uint32_t size,
struct timeval *absolute_end_time)
{
int res = 0;
// `remaining_size` and `cur_data_ptr` are used to keep track of where we are in the memory.
// This allows us to read from the socket in parts (if we need to).
uint32_t remaining_size = size;
char *cur_data_ptr = data;
while (remaining_size > 0) {
// If the operation exceeded the allowed time, treat it as a failure.
if (is_absolute_timeout_reached(absolute_end_time)) {
close(*socket);
*socket = -1;
write_dbg(
attachment,
0,
DBG_LEVEL_TRACE,
"Reached timeout while communicating with the socket"
);
return NANO_TIMEOUT;
}
// 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.
struct pollfd s_poll;
s_poll.fd = *socket;
s_poll.events = POLLIN;
s_poll.revents = 0;
res = poll(&s_poll, 1, 3000);
if (res <= 0 || (s_poll.revents & POLLIN) == 0) {
close(*socket);
*socket = -1;
write_dbg(
attachment,
0,
DBG_LEVEL_TRACE,
"Failed to communicate with the socket, Error: %s",
strerror(errno)
);
return NANO_ERROR;
}
res = read(*socket, (void *)cur_data_ptr, remaining_size);
remaining_size -= res;
cur_data_ptr += res;
}
return NANO_OK;
}
///
/// @brief Send communication data to the communication socket.
///
/// This function sends various data (unique identifier, process UID, process GID)
/// to the service through the communication socket. It sends the data in the
/// following 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.
///
/// @param[in] attachment The NanoAttachment struct containing the data to send.
/// @returns A NanoCommunicationResult indicating the success of the operation.
/// - #NANO_OK: The data was successfully sent.
/// - #NANO_ERROR: An error occurred during data transmission.
///
NanoCommunicationResult
send_comm_data_to_comm_socket(NanoAttachment *attachment)
{
NanoCommunicationResult res;
uint8_t uid_size_to_send = strlen(attachment->unique_id);
struct timeval timeout = get_absolute_timeout_val_sec(1);
res = write_to_service(
attachment,
&attachment->comm_socket,
&uid_size_to_send,
sizeof(uid_size_to_send),
&timeout
);
if (res != NANO_OK) {
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Failed to send unique id size");
return NANO_ERROR;
}
res = write_to_service(
attachment,
&attachment->comm_socket,
attachment->unique_id,
uid_size_to_send,
&timeout
);
if (res != NANO_OK) {
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Failed to send unique id %s", attachment->unique_id);
return NANO_ERROR;
}
res = write_to_service(
attachment,
&attachment->comm_socket,
&attachment->nano_user_id,
sizeof(uint32_t),
&timeout
);
if (res != NANO_OK) {
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Failed to send nano user id");
return NANO_ERROR;
}
res = write_to_service(
attachment,
&attachment->comm_socket,
&attachment->nano_group_id,
sizeof(uint32_t),
&timeout
);
if (res != NANO_OK) {
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Failed to send nano group id");
return NANO_ERROR;
}
return NANO_OK;
}
///
/// @brief Initialize the signaling socket for communication.
///
/// This function connects to the communication socket, sends communication data,
/// and waits for an acknowledgment from the service that communication has been
/// established. If any step fails, the function closes the communication socket
/// and returns an error.
///
/// @param[in] attachment The NanoAttachment struct containing socket information.
/// @returns A NanoCommunicationResult indicating the success of the operation.
///
/// - #NANO_OK: Signaling socket initialized successfully.
/// - #NANO_ERROR: An error occurred during initialization.
///
static NanoCommunicationResult
init_signaling_socket(NanoAttachment *attachment)
{
uint8_t initialization_ack;
HttpEventThreadCtx ctx;
int t_res;
NanoCommunicationResult res;
struct timeval timeout = get_absolute_timeout_val_sec(1);
write_dbg(attachment, 0, DBG_LEVEL_DEBUG, "spawn RegistrationCommSocketThread");
t_res = NanoRunInThreadTimeout(
attachment,
NULL,
RegistrationCommSocketThread,
(void *)&ctx,
attachment->registration_thread_timeout_msec,
"RegistrationCommSocketThread",
REGISTRATION
);
if (!t_res || ctx.res != NANO_OK) {
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Failed to connect to connection socket");
close(attachment->comm_socket);
attachment->comm_socket = -1;
if (attachment->registration_state != PENDING) {
attachment->registration_state = NOT_REGISTERED;
}
return NANO_ERROR;
}
res = send_comm_data_to_comm_socket(attachment);
if (res != NANO_OK) {
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Failed to send comm data");
close(attachment->comm_socket);
attachment->comm_socket = -1;
if (attachment->registration_state != PENDING) {
attachment->registration_state = NOT_REGISTERED;
}
return NANO_ERROR;
}
// Get an acknowledgement form the service that communication has been established.
res = read_from_service(
attachment,
&attachment->comm_socket,
&initialization_ack,
sizeof(initialization_ack),
&timeout
);
if (res != NANO_OK) {
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Failed to read communication ack");
close(attachment->comm_socket);
attachment->comm_socket = -1;
if (attachment->registration_state != PENDING) {
attachment->registration_state = NOT_REGISTERED;
}
return NANO_ERROR;
}
write_dbg(
attachment,
0,
DBG_LEVEL_DEBUG,
"Successfully connected on client socket %d",
attachment->comm_socket
);
return NANO_OK;
}
static NanoCommunicationResult
createDirectory(NanoAttachment *attachment, const char *path)
{
struct stat st;
if (stat(path, &st) == 0) {
write_dbg(
attachment,
0,
DBG_LEVEL_DEBUG,
"Nano attachment logging directory already exists"
);
return NANO_OK;
}
if (mkdir(path, 0755) == 0) {
write_dbg(
attachment,
0,
DBG_LEVEL_DEBUG,
"Successfully created logging directory"
);
return NANO_OK;
}
write_dbg(
attachment,
0,
DBG_LEVEL_WARNING,
"Failed to create logging directory"
);
return NANO_ERROR;
}
NanoCommunicationResult
set_docker_id(NanoAttachment *attachment)
{
size_t len = MAX_NGINX_UID_LEN;
char *line = NULL;
char *docker_ptr = NULL;
char *containerd_ptr = NULL;
FILE *file = fopen(CONTAINER_ID_FILE_PATH, "r");
if (file == NULL) {
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Failed to open %s", CONTAINER_ID_FILE_PATH);
return NANO_ERROR;
}
write_dbg(attachment, 0, DBG_LEVEL_DEBUG, "opened file %s", CONTAINER_ID_FILE_PATH);
line = malloc(MAX_NGINX_UID_LEN);
if (line == NULL) {
write_dbg(attachment, 0, DBG_LEVEL_DEBUG, "Failed to allocate memory for reading docker id file");
fclose(file);
return NANO_ERROR;
}
// Reading the file line by line.
bool uid_read = false;
while (getline(&line, &len, file) != -1) {
docker_ptr = strstr(line, "docker/");
containerd_ptr = strstr(line, "cri-containerd-");
if (docker_ptr != NULL)
{
// We've found a line with "docker/" so the identifier will be right after that.
write_dbg(attachment, 0, DBG_LEVEL_DEBUG, "checking for docker/");
docker_ptr += strlen("docker/");
strncpy(attachment->container_id, docker_ptr, MAX_CONTAINER_ID_LEN - 1);
attachment->container_id[MAX_CONTAINER_ID_LEN - 1] = '\0';
uid_read = true;
break;
}
if (containerd_ptr != NULL)
{
write_dbg(attachment, 0, DBG_LEVEL_DEBUG, "checking for cri-containerd-");
containerd_ptr += strlen("cri-containerd-");
strncpy(attachment->container_id, containerd_ptr, MAX_CONTAINER_ID_LEN - 1);
attachment->container_id[MAX_CONTAINER_ID_LEN - 1] = '\0';
uid_read = true;
break;
}
}
if (!uid_read) {
const char *env_var_name = "CLOUDGUARD_UID"; // Replace with your environment variable name
const char *env_value = getenv(env_var_name);
if (env_value) {
strncpy(attachment->container_id, env_value, MAX_CONTAINER_ID_LEN - 1);
attachment->container_id[MAX_CONTAINER_ID_LEN - 1] = '\0';
uid_read = true;
}
}
free(line);
fclose(file);
if (!uid_read) {
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Severe error - failed to get uid!");
return NANO_ERROR;
}
return NANO_OK;
}
///
///@brief Sends registration data to the registration socket.
///
/// This function sends registration data to the registration socket of the given NanoAttachment.
/// It sends the attachment type, worker ID, total number of workers,
/// and the size and content of the container id (docker ID).
/// If any of the send operations fail, it returns NANO_ERROR; otherwise, it returns NANO_OK.
///
///@param attachment The NanoAttachment containing the registration socket and other necessary information.
///@return NanoCommunicationResult Returns NANO_OK if the registration data is successfully sent, otherwise NANO_ERROR.
///
NanoCommunicationResult
send_registration_data_to_registration_socket(NanoAttachment *attachment)
{
uint8_t attachment_type = attachment->attachment_type;
uint8_t worker_id = attachment->worker_id + 1;
uint8_t workers_amount = attachment->num_of_workers;
struct timeval absolute_timeout = get_absolute_timeout_val_sec(1);
uint8_t container_id_size = strlen(attachment->container_id);
NanoCommunicationResult res;
// 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 = write_to_service(
attachment,
&attachment->registration_socket,
&attachment_type,
sizeof(attachment_type),
&absolute_timeout
);
if (res != NANO_OK) {
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Failed to send attachment type");
return NANO_ERROR;
}
res = write_to_service(
attachment,
&attachment->registration_socket,
&worker_id,
sizeof(worker_id),
&absolute_timeout
);
if (res != NANO_OK) {
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Failed to send worker ID");
return NANO_ERROR;
}
res = write_to_service(
attachment,
&attachment->registration_socket,
&workers_amount,
sizeof(workers_amount),
&absolute_timeout
);
if (res != NANO_OK) {
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Failed to send workers amount");
return NANO_ERROR;
}
res = write_to_service(
attachment,
&attachment->registration_socket,
&container_id_size,
sizeof(container_id_size),
&absolute_timeout
);
if (res != NANO_OK) {
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Failed to send container id size");
return NANO_ERROR;
}
if (container_id_size > 0) {
res = write_to_service(
attachment,
&attachment->registration_socket,
attachment->container_id,
container_id_size,
&absolute_timeout
);
if (res != NANO_OK) {
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Failed to send container id");
return NANO_ERROR;
}
}
return NANO_OK;
}
///
/// @brief Reads the verdict signal path from the registration socket.
///
/// This function reads the verdict signal path from the registration socket of the given NanoAttachment.
/// It first reads the length of the signal path, then reads the signal path itself.
/// If the read operations fail or the path length exceeds the maximum allowed length, it returns NANO_ERROR;
/// otherwise, it returns NANO_OK.
///
/// @param attachment The NanoAttachment containing the registration socket and other necessary information.
/// @return NanoCommunicationResult Returns NANO_OK if the signal path is successfully read, otherwise NANO_ERROR.
///
NanoCommunicationResult
read_verdict_signal_path_from_registration_socket(NanoAttachment *attachment)
{
uint8_t path_length;
int registration_socket = attachment->registration_socket;
uint8_t worker_id = attachment->worker_id + 1;
uint8_t workers_amount = attachment->num_of_workers;
NanoCommunicationResult res;
struct timeval absolute_timeout = get_absolute_timeout_val_sec(1);
// Read from the attachment manager:
// 1. The length of signal path.
// 2. The signal path itself.
// If that fails - return an error.
res = read_from_service(
attachment,
&attachment->registration_socket,
&path_length,
sizeof(path_length),
&absolute_timeout
);
if (res != NANO_OK) {
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Failed to read path length");
return NANO_ERROR;
}
if (path_length >= MAX_SHARED_MEM_PATH_LEN) {
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Verdict path length is too long");
return NANO_ERROR;
}
res = read_from_service(
attachment,
&attachment->registration_socket,
attachment->shared_verdict_signal_path,
path_length,
&absolute_timeout
);
if (res != NANO_OK) {
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Failed to read socket path");
return NANO_ERROR;
}
// Successfully go the shared communication path - add null termination and exit.
attachment->shared_verdict_signal_path[path_length] = '\0';
write_dbg(
attachment,
0,
DBG_LEVEL_DEBUG,
"Successfully registered on client. socket: %d, instance ID: %u, instances amount: %u, received path: %s",
registration_socket,
(uint32_t)worker_id,
(uint32_t)workers_amount,
attachment->shared_verdict_signal_path
);
return NANO_OK;
}
NanoCommunicationResult
register_to_attachments_manager(NanoAttachment *attachment)
{
NanoCommunicationResult res;
HttpEventThreadCtx ctx;
int t_res;
// If there was an old socket, close it.
if (attachment->registration_socket > 0) {
close(attachment->registration_socket);
attachment->registration_socket = -1;
}
write_dbg(attachment, 0, DBG_LEVEL_DEBUG, "spawn RegistrationSocketThread");
t_res = NanoRunInThreadTimeout(
attachment,
NULL,
RegistrationSocketThread,
(void *)&ctx,
attachment->registration_thread_timeout_msec,
"RegistrationSocketThread",
REGISTRATION
);
if (!t_res || ctx.res != NANO_OK) {
write_dbg(
attachment,
0,
DBG_LEVEL_WARNING,
"Failed to connect to registration socket"
);
close(attachment->registration_socket);
attachment->registration_socket = -1;
return NANO_ERROR;
}
res = send_registration_data_to_registration_socket(attachment);
if (res != NANO_OK) {
write_dbg(
attachment,
0,
DBG_LEVEL_WARNING,
"Failed to send registration data"
);
close(attachment->registration_socket);
attachment->registration_socket = -1;
return NANO_ERROR;
}
res = read_verdict_signal_path_from_registration_socket(attachment);
if (res != NANO_OK) {
write_dbg(
attachment,
0,
DBG_LEVEL_WARNING,
"Failed to read verdict signal path"
);
close(attachment->registration_socket);
attachment->registration_socket = -1;
return NANO_ERROR;
}
close(attachment->registration_socket);
attachment->registration_socket = -1;
return NANO_OK;
}
NanoCommunicationResult
set_unique_id(NanoAttachment *attachment)
{
unsigned int unique_id_size = 0;
long unsigned int nano_worker_id = attachment->worker_id + 1;
if (strlen(attachment->container_id) > 0) {
unique_id_size += snprintf(
attachment->unique_id,
MAX_NGINX_UID_LEN,
"%s_%lu",
attachment->container_id,
nano_worker_id);
} else {
unique_id_size += snprintf(attachment->unique_id, MAX_NGINX_UID_LEN, "%lu", nano_worker_id);
}
if (unique_id_size <= 0) {
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Failed to set attachment's unique_id");
return NANO_ERROR;
}
if (unique_id_size >= MAX_NGINX_UID_LEN) {
write_dbg(attachment, 0, DBG_LEVEL_INFO, "Unique ID is too long, trancheated to: %s", attachment->unique_id);
}
write_dbg(attachment, 0, DBG_LEVEL_INFO, "Successfully set attachment's unique_id: '%s'", attachment->unique_id);
return NANO_OK;
}
NanoCommunicationResult
set_logging_fd(NanoAttachment *attachment, int logging_fd)
{
char full_logging_path[128];
if (logging_fd > 0) {
attachment->is_default_fd = 0;
attachment->logging_fd = logging_fd;
write_dbg(attachment, 0, DBG_LEVEL_DEBUG, "Successfully set provided logging_fd");
return NANO_OK;
}
if (createDirectory(attachment, LOGGING_DIRECTORY_PATH) != NANO_OK) {
return NANO_ERROR;
}
snprintf(full_logging_path, sizeof(full_logging_path), "%s-%s.dbg", LOGGING_FILE_PATH, attachment->container_id);
attachment->logging_fd = open(full_logging_path, O_WRONLY | O_CREAT | O_APPEND, 0644);
if (attachment->logging_fd < 0) {
return NANO_ERROR;
}
attachment->is_default_fd = 1;
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Successfully opened logging file");
return NANO_OK;
}
void
close_logging_fd(NanoAttachment *attachment)
{
if (attachment->logging_fd > 0 && attachment->is_default_fd) {
close(attachment->logging_fd);
attachment->logging_fd = -1;
}
free(attachment->logging_data);
attachment->logging_data = NULL;
}
NanoCommunicationResult
nano_attachment_init_process(NanoAttachment *attachment)
{
attachment->nano_user_id = getuid();
attachment->nano_group_id = getgid();
attachment->num_of_connection_attempts++;
init_attachment_config(attachment, SHARED_ATTACHMENT_CONF_PATH);
if (access(SHARED_REGISTRATION_SIGNAL_PATH, F_OK) != 0) {
write_dbg(attachment, 0, DBG_LEVEL_TRACE, "Attachment registration manager is turned off");
return NANO_ABORT;
}
if (attachment->registration_state == PENDING) {
write_dbg(attachment, 0, DBG_LEVEL_INFO, "Registration to the Attachments Manager is in process");
return NANO_ERROR;
}
if (attachment->registration_state == NOT_REGISTERED) {
// Register with the attachment manager.
if (register_to_attachments_manager(attachment) == NANO_ERROR) {
write_dbg(attachment, 0, DBG_LEVEL_INFO, "Failed to register to Attachments Manager service");
return NANO_ERROR;
}
attachment->registration_state = REGISTERED;
}
if (init_attachment_config(attachment, SHARED_ATTACHMENT_CONF_PATH) == NANO_ERROR) {
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Failed to initialize attachment's configuration");
return NANO_ERROR;
}
if (attachment->comm_socket < 0) {
// Signal to the service to start communication.
write_dbg(attachment, 0, DBG_LEVEL_DEBUG, "Registering to nano service");
if (init_signaling_socket(attachment) == NANO_ERROR) {
write_dbg(attachment, 0, DBG_LEVEL_DEBUG, "Failed to register to the Nano Service");
return NANO_ERROR;
}
}
// Initalize the the communication channel with the service.
if (attachment->nano_service_ipc == NULL) {
write_dbg(attachment, 0, DBG_LEVEL_INFO, "Initializing IPC channel");
attachment->nano_service_ipc = initIpc(
attachment->unique_id,
attachment->nano_user_id,
attachment->nano_group_id,
0,
attachment->num_of_nano_ipc_elements,
attachment->logging_data,
write_dbg_impl
);
if (attachment->nano_service_ipc == NULL) {
restart_communication(attachment);
write_dbg(attachment, 0, DBG_LEVEL_INFO, "Failed to initialize IPC with nano service");
return NANO_ERROR;
}
}
write_dbg(
attachment,
0,
DBG_LEVEL_INFO,
"NGINX attachment (UID='%s') successfully registered to nano service after %d attempts.",
attachment->unique_id,
attachment->num_of_connection_attempts
);
attachment->num_of_connection_attempts = 0;
return NANO_OK;
}
NanoCommunicationResult
restart_communication(NanoAttachment *attachment)
{
write_dbg(attachment, 0, DBG_LEVEL_TRACE, "Restarting communication channels with nano service");
if (attachment->nano_service_ipc != NULL) {
destroyIpc(attachment->nano_service_ipc, 0);
attachment->nano_service_ipc = NULL;
}
if (init_signaling_socket(attachment) == NANO_ERROR) {
if (register_to_attachments_manager(attachment) == NANO_ERROR) {
write_dbg(attachment, 0, DBG_LEVEL_DEBUG, "Failed to register to Attachments Manager service");
return NANO_ERROR;
}
if (init_signaling_socket(attachment) == NANO_ERROR) {
write_dbg(attachment, 0, DBG_LEVEL_DEBUG, "Failed to init the signaling socket");
return NANO_ERROR;
}
}
attachment->nano_service_ipc = initIpc(
attachment->unique_id,
attachment->nano_user_id,
attachment->nano_group_id,
0,
attachment->num_of_nano_ipc_elements,
attachment->logging_data,
write_dbg_impl
);
if (attachment->nano_service_ipc == NULL) {
write_dbg(attachment, 0, DBG_LEVEL_DEBUG, "Failed to init IPC");
return NANO_ERROR;
}
return NANO_OK;
}
void
disconnect_communication(NanoAttachment *attachment)
{
write_dbg(attachment, 0, DBG_LEVEL_DEBUG, "Disconnecting communication channels with nano service");
if (attachment->comm_socket > 0) {
close(attachment->comm_socket);
attachment->comm_socket = -1;
}
if (attachment->nano_service_ipc != NULL) {
destroyIpc(attachment->nano_service_ipc, 0);
attachment->nano_service_ipc = NULL;
}
}
NanoCommunicationResult
handle_shmem_corruption(NanoAttachment *attachment)
{
NanoCommunicationResult res;
if (attachment->nano_service_ipc == NULL) {
disconnect_communication(attachment);
res = restart_communication(attachment);
if (res != NANO_OK) {
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Failed to restart communication");
return NANO_ERROR;
}
}
if (isCorruptedShmem(attachment->nano_service_ipc, 0)) {
write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Shared memory is corrupted! restarting communication");
disconnect_communication(attachment);
return NANO_ERROR;
}
return NANO_OK;
}
int
isIpcReady(NanoAttachment *attachment)
{
return attachment->nano_service_ipc != NULL && attachment->comm_socket > 0;
}

View File

@@ -0,0 +1,218 @@
// 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 nano_initializer.h
#ifndef __NANO_INITIALIZER_H__
#define __NANO_INITIALIZER_H__
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "nano_attachment_common.h"
#include "shmem_ipc_2.h"
#define LOGGING_DIRECTORY_PATH "/var/log/nano_attachment" ///< Default logging directory path.
#define LOGGING_FILE_NAME "nano_attachment" ///< Default logging file name.
#define LOGGING_FILE_PATH LOGGING_DIRECTORY_PATH "/" LOGGING_FILE_NAME
typedef enum nano_attachment_registration_state {
NOT_REGISTERED,
PENDING,
REGISTERED
} nano_attachment_registration_state; ///< Indicates the current attachment registation stage.
typedef struct NanoAttachment {
char unique_id[MAX_NGINX_UID_LEN]; // Holds the unique identifier for this instance.
char container_id[MAX_NGINX_UID_LEN]; // Holds the container id of the attachment.
char shared_verdict_signal_path[MAX_SHARED_MEM_PATH_LEN]; // Holds the path associating the attachment and service.
uint8_t worker_id; // Holds the worker number of the attachment.
uint8_t num_of_workers; // Holds the number of workers in the attachment.
uint32_t nano_user_id; // Holds the user id of the attachment.
uint32_t nano_group_id; // Holds the group id of the attachment.
int registration_socket; // Holds the file descriptor used for registering the instance.
nano_attachment_registration_state registration_state; // Holds the current attachment registation stage.
uint8_t attachment_type; // Holds the type of the attachment.
SharedMemoryIPC *nano_service_ipc; // Holds the shared memory IPC of the nano service.
int comm_socket; // Holds the communication socket of the attachment.
int is_default_fd; // Holds a value indicating if the logging file descriptor is the default one.
int logging_fd; // Holds the file descriptor for logging.
LoggingData *logging_data; // Holds the logging data of the attachment.
NanoCommunicationResult is_configuration_updated; // Holds the result of the configuration update.
unsigned int current_config_version; // Holds the current configuration version.
int fail_mode_verdict; ///< Fail open verdict incase of a timeout.
int fail_mode_delayed_verdict; ///< Fail open verdict incase of a timeout when waiting for delayed verdict.
nano_http_cp_debug_level_e dbg_level; ///< Default debug level.
int num_of_connection_attempts; ///< Maximum number of attempted connections.
unsigned int fail_open_timeout; ///< Fail open timeout in milliseconds.
unsigned int fail_open_delayed_timeout; ///< Fail open delayed timeout in milliseconds.
AttachmentVerdict sessions_per_minute_limit_verdict; ///< Session per minute limit verdict.
unsigned int max_sessions_per_minute; ///< Masimum session per minute.
unsigned int req_max_proccessing_ms_time; ///< Total Request processing timeout in milliseconds.
unsigned int res_max_proccessing_ms_time; ///< Total Response processing timeout in milliseconds.
unsigned int registration_thread_timeout_msec; ///< Registration timeout in milliseconds.
unsigned int req_start_thread_timeout_msec; ///< Request start processing timeout in milliseconds.
unsigned int req_header_thread_timeout_msec; ///< Request header processing timeout in milliseconds.
unsigned int req_body_thread_timeout_msec; ///< Request body processing timeout in milliseconds.
unsigned int res_header_thread_timeout_msec; ///< Response header processing timeout in milliseconds.
unsigned int res_body_thread_timeout_msec; ///< Response body processing timeout in milliseconds.
unsigned int waiting_for_verdict_thread_timeout_msec; ///< Wait thread processing timeout in milliseconds.
unsigned int metric_timeout_timeout; ///< Metric timeout in milliseconds.
NanoHttpInspectionMode inspection_mode; ///< Default inspection mode.
unsigned int num_of_nano_ipc_elements; ///< Number of NANO IPC elements.
uint64_t keep_alive_interval_msec; ///< Keep alive interval in milliseconds.
#ifdef __cplusplus
uint64_t metric_data[static_cast<int>(AttachmentMetricType::METRIC_TYPES_COUNT)];
uint64_t metric_average_data_divisor[static_cast<int>(AttachmentMetricType::METRIC_TYPES_COUNT)];
#else
uint64_t metric_data[METRIC_TYPES_COUNT];
uint64_t metric_average_data_divisor[METRIC_TYPES_COUNT];
#endif
} NanoAttachment;
///
/// @brief Initialize all the attachments resources and communication channels.
/// @param[in] attachment Points to initiated NanoAttachment struct.
/// @returns NanoCommunicationResult
/// - #NANO_OK
/// - #NANO_ERROR
/// - #NANO_ABORT
///
NanoCommunicationResult nano_attachment_init_process(NanoAttachment *attachment);
///
/// @brief Preforms send information to the service via a socket.
///
/// This function writes data to the socket associated with the given NanoAttachment.
/// It writes data in parts if necessary, keeping track of the remaining data to be written.
/// If the write operation fails or exceeds the allowed timeout, it returns NANO_ERROR;
/// otherwise, it returns NANO_OK.
///
/// @param Points to the NanoAttachment struct.
/// @param socket The socket to write to.
/// @param data The pointer to the data to be written.
/// @param size The size of the data to be written, excluding the null terminator.
/// @param absolute_end_time The absolute till the writing is allowed.
/// @return NanoCommunicationResult Returns NANO_OK if the write operation is successful, otherwise NANO_ERROR.
///
NanoCommunicationResult write_to_service(
NanoAttachment *attachment,
int *socket,
void *data,
uint32_t size,
struct timeval *absolute_end_time);
///
/// @brief Preforms receive information from the service via a socket.
///
/// This function reads data from the socket associated with the given NanoAttachment.
/// It reads data in parts if necessary, keeping track of the remaining data to be read.
/// It checks if the socket has data to be read prior to the read operation to avoid blocking indefinitely.
/// If the read operation fails or exceeds the allowed timeout, it returns NANO_ERROR;
/// otherwise, it returns NANO_OK.
///
/// @param attachment Points to the NanoAttachment struct.
/// @param socket The socket to read from.
/// @param data The pointer to the buffer where the read data will be stored.
/// @param size The size of the data to be read.
/// @param absolute_end_time The absolute till the reading is allowed.
/// @return NanoCommunicationResult Returns NANO_OK if the read operation is successful, otherwise NANO_ERROR.
///
NanoCommunicationResult read_from_service(
NanoAttachment *attachment,
int *socket,
void *data,
uint32_t size,
struct timeval *absolute_end_time);
///
/// @brief Sets a unique identifier for the NanoAttachment based on its container ID and worker ID.
///
/// @param attachment Pointer to the NanoAttachment structure for which to set the unique ID.
/// @return NANO_OK if the unique ID was successfully set, otherwise an error code.
///
NanoCommunicationResult set_unique_id(NanoAttachment *attachment);
///
/// @brief Sets the container ID for the NanoAttachment by reading it from CONTAINER_ID_FILE_PATH value.
///
/// @param attachment Pointer to the NanoAttachment structure for which to set the container ID.
/// @return NANO_OK if the container ID was successfully set, otherwise an error code.
///
NanoCommunicationResult set_docker_id(NanoAttachment *attachment);
///
/// @brief Sets the file descriptor for logging in the NanoAttachment structure.
///
/// If an invalid fd passed, the default logging file descriptor is set to write to LOGGING_FILE_PATH variable.
///
/// @param attachment Pointer to the NanoAttachment struct to set the logging file descriptor.
/// @param logging_fd The file descriptor to set.
/// @return NANO_OK if the logging file descriptor is set successfully, NANO_ERROR otherwise.
///
NanoCommunicationResult set_logging_fd(NanoAttachment *attachment, int logging_fd);
///
/// @brief Closes the logging file descriptor in the NanoAttachment structure.
///
/// @param attachment Pointer to the NanoAttachment struct to close the logging file descriptor.
///
void close_logging_fd(NanoAttachment *attachment);
///
/// @brief Closes any existing communication to the service and tries to open a new one.
/// @param[in] attachment Points to initiated NanoAttachment struct.
/// @returns NanoCommunicationResult
/// - #NANO_OK
/// - #NANO_ERROR
///
NanoCommunicationResult restart_communication(NanoAttachment *attachment);
///
/// @brief Checks that the shared memory with the service isn't corrupted, disconnect if it is.
/// @param[in] attachment Points to initiated NanoAttachment struct.
/// @returns NanoCommunicationResult
/// - #NANO_OK
/// - #NANO_ERROR
///
NanoCommunicationResult handle_shmem_corruption(NanoAttachment *attachment);
///
/// @brief Closes all the communication channels with the service.
/// @param[in] attachment Points to initiated NanoAttachment struct.
///
void disconnect_communication(NanoAttachment *attachment);
///
/// @brief Checks if communication with the service is up and running.
/// @param[in] attachment Points to initiated NanoAttachment struct.
/// @returns NanoCommunicationResult - 1 if communication is active, otherwise 0.
///
int isIpcReady(NanoAttachment *attachment);
///
/// @brief Register the attachment instance with the attachment manager to associate it with a service.
/// @param[in] attachment Points to initiated NanoAttachment struct.
/// @returns NanoCommunicationResult
/// - #NANO_OK
/// - #NANO_ERROR
///
NanoCommunicationResult register_to_attachments_manager(NanoAttachment *attachment);
#endif // __NANO_INITIALIZER_H__

View File

@@ -0,0 +1,131 @@
// 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 nano_utils.c
#include "nano_utils.h"
#include "nano_attachment_common.h"
#include <time.h>
#include <stdarg.h>
#include <math.h>
#include <unistd.h>
#include <stdio.h>
///
/// @brief Gets the current time using a fast, coarse-grained clock.
///
/// This function uses CLOCK_MONOTONIC_COARSE to retrieve the current time,
/// which provides a fast timestamp. The function returns
/// the current time as a struct timeval, which represents seconds and microseconds.
///
/// @return struct timeval The current time as seconds and microseconds.
///
static struct timeval
getCurrTimeFast()
{
struct timeval curr_time;
struct timespec curr_time_mono;
clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_time_mono);
curr_time.tv_sec = curr_time_mono.tv_sec;
curr_time.tv_usec = curr_time_mono.tv_nsec/1000.0;
return curr_time;
}
void
write_dbg_impl(
const LoggingData *logging_data,
uint32_t session_id,
int _dbg_level,
const char *func,
const char *file,
int line_num,
const char *fmt,
...
)
{
if (logging_data == NULL) return;
if (_dbg_level < logging_data->dbg_level) return;
char debug_str[2048] = {0};
char session_id_str[32] = {0};
char unique_id[32] = "uniqueId";
va_list args;
time_t ttime;
int millisec;
struct timeval tv;
char time_stamp[64];
char str_uid[140];
int pid = 0;
time(&ttime);
tv = getCurrTimeFast();
millisec = lrint(tv.tv_usec/1000.0);
if (millisec>=1000) {
// Allow for rounding up to nearest second
millisec -=1000;
tv.tv_sec++;
}
strftime(time_stamp, sizeof(time_stamp), "%FT%T", localtime(&ttime));
if (!pid) pid = getpid();
if (session_id > 0) {
snprintf(session_id_str, sizeof(session_id_str) - 1, "<session id %d> ", session_id);
}
// Prints the debug given all the data and a formatter.
snprintf(
str_uid,
sizeof(str_uid) - 1,
"|%s.%03d: %s@%s:%d [uid %s | pid %d] %s| ",
time_stamp,
millisec,
func,
file,
line_num,
unique_id,
pid,
session_id_str
);
va_start(args, fmt);
vsnprintf(debug_str, sizeof(debug_str) - 1, fmt, args);
va_end(args);
dprintf(logging_data->fd, "%s%s\n", str_uid, debug_str);
}
struct timeval
get_absolute_timeout_val_sec(const int delta_time_in_sec)
{
struct timeval time;
time = getCurrTimeFast();
time.tv_sec += delta_time_in_sec;
return time;
}
int
is_absolute_timeout_reached(struct timeval *timeout)
{
struct timeval curr_time;
curr_time = getCurrTimeFast();
return (timercmp(timeout, &curr_time, <));
}

View File

@@ -0,0 +1,90 @@
// 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 nano_utils.h
#ifndef __NANO_UTILS_H__
#define __NANO_UTILS_H__
#include <sys/time.h>
#include <assert.h>
#include "nano_initializer.h"
typedef struct LoggingData {
int dbg_level;
int worker_id;
int fd;
} LoggingData;
#ifndef __FILENAME__
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#endif
#define write_dbg(attachment, session_id, _dbg_level, fmt, ...) \
{ \
write_dbg_impl( \
attachment->logging_data, \
session_id, \
_dbg_level, \
__func__, \
__FILENAME__, \
__LINE__, \
fmt, \
##__VA_ARGS__ \
); \
if ((_dbg_level) == DBG_LEVEL_ASSERT) assert(0); \
}
///
/// @brief Writing into debug implementation.
/// @param[in] LoggingData Logging data.
/// @param[in] session_id Session ID.
/// @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
write_dbg_impl(
const LoggingData *logging_data,
uint32_t session_id,
int _dbg_level,
const char *func,
const char *file,
int line_num,
const char *fmt,
...
);
///
/// @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_absolute_timeout_val_sec(const int delta_time_in_sec);
///
/// @brief Check if a timeout has been reached.
///
/// This function compares the specified timeout value with the current time
/// to determine if the timeout has been reached.
///
/// @param[in] timeout A pointer to a struct timeval representing the timeout value.
/// @return 1 if the timeout has been reached, 0 otherwise.
///
int is_absolute_timeout_reached(struct timeval *timeout);
#endif // __NANO_UTILS_H__