Files
attachment/attachments/nano_attachment/nano_attachment.c
Granyaa 7884c711e2 Refactor delayed verdict handling and add configurable retries
Extract HandleDelayedVerdict() to eliminate duplication and make
retry count and polling time configurable. Add delayed verdict
handling to SendRequestEnd with unit tests.
2025-12-23 16:01:46 +02:00

646 lines
19 KiB
C
Executable File

#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"
#include "compression_utils.h"
#include "nano_compression.h"
NanoAttachment *
InitNanoAttachment(uint8_t attachment_type, int worker_id, int num_of_workers, int logging_fd)
{
NanoAttachment *attachment = malloc(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->hold_verdict_retries = 10;
attachment->hold_verdict_polling_time = 1;
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;
session_data->response_data.compression_type = NO_COMPRESSION;
session_data->response_data.compression_stream = NULL;
session_data->response_data.decompression_stream = NULL;
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"
);
if (session_data->response_data.compression_stream != NULL) {
finiCompressionStream(session_data->response_data.compression_stream);
session_data->response_data.compression_stream = NULL;
}
if (session_data->response_data.decompression_stream != NULL) {
finiCompressionStream(session_data->response_data.decompression_stream);
session_data->response_data.decompression_stream = NULL;
}
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;
}
///
/// @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 }
};
}
uint16_t
GetResponseCode(AttachmentVerdictResponse *response)
{
WebResponseData *web_response_data = response->web_response_data;
CustomResponseData *custom_response_data;
custom_response_data = (CustomResponseData *) web_response_data->data;
return custom_response_data->response_code;
}
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;
}
HttpBody *
compressBody(NanoAttachment *attachment, HttpSessionData *session_data, HttpBody *bodies)
{
return nano_compress_body(attachment, bodies, session_data);
}
HttpBody *
decompressBody(NanoAttachment *attachment, HttpSessionData *session_data, HttpBody *bodies)
{
return nano_decompress_body(attachment, bodies, session_data);
}
void
freeCompressedBody(NanoAttachment *attachment, HttpSessionData *session_data, HttpBody *bodies)
{
nano_free_compressed_body(attachment, bodies, session_data);
}
uint32_t
GetRequestProcessingTimeout(NanoAttachment *attachment)
{
if (attachment == NULL) {
return 3000;
}
return attachment->req_max_proccessing_ms_time;
}
uint32_t
GetResponseProcessingTimeout(NanoAttachment *attachment)
{
if (attachment == NULL) {
return 3000;
}
return attachment->res_max_proccessing_ms_time;
}