attachment/attachments/nano_attachment/nano_attachment_sender.c
2024-12-24 18:32:02 +02:00

796 lines
24 KiB
C

#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;
}