#include "nano_attachment_io.h" #include #include #include #include #include #include "nano_attachment_common.h" #include "nano_utils.h" #include "nano_attachment_metric.h" #include "nano_attachment_util.h" #include "nano_configuration.h" #define MAX_HEADER_BULK_SIZE 10 #define RESPONSE_CODE_COUNT 3 #define CONTENT_LENGTH_COUNT 3 #define HEADER_DATA_COUNT 4 #define BODY_DATA_COUNT 5 #define END_TRANSACTION_DATA_COUNT 2 #define DELAYED_VERDICT_DATA_COUNT 2 /// @brief Sends a signal to the nano service to notify about new session data to inspect. /// /// This function sends a signal to the nano service to notify it about new session data /// that needs to be inspected. /// /// @param attachment A pointer to a NanoAttachment structure representing the attachment to the nano service. /// @param cur_session_id An unsigned 32-bit integer representing the current session ID. /// /// @return NANO_OK if the signal is sent successfully /// NANO_ERROR if there is an error /// NANO_TIMEOUT if a timeout occurs. /// static NanoCommunicationResult notify_signal_to_service(NanoAttachment *attachment, uint32_t cur_session_id) { int res = 0; unsigned int bytes_written = 0; struct timeval absolute_timeout = get_absolute_timeout_val_sec(1); int failopen_enabled = (attachment->inspection_mode == NON_BLOCKING_THREAD); struct pollfd s_poll; write_dbg( attachment, cur_session_id, DBG_LEVEL_TRACE, "Sending signal to the service to notify about new session data to inspect" ); s_poll.fd = attachment->comm_socket; s_poll.events = POLLOUT; s_poll.revents = 0; res = poll(&s_poll, 1, 0); if (res > 0 && s_poll.revents & POLLHUP) { write_dbg( attachment, cur_session_id, DBG_LEVEL_DEBUG, "Polling communication socket failed" ); return NANO_ERROR; } while (bytes_written < sizeof(cur_session_id)) { if (failopen_enabled && is_absolute_timeout_reached(&absolute_timeout)) { write_dbg( attachment, cur_session_id, DBG_LEVEL_WARNING, "Reached timeout during attempt to signal nano service" ); return NANO_TIMEOUT; } res = write( attachment->comm_socket, ((char *)&cur_session_id) + bytes_written, sizeof(cur_session_id) - bytes_written ); if (res < 0) { write_dbg( attachment, cur_session_id, DBG_LEVEL_WARNING, "Failed to signal nano service, trying to restart communications" ); return NANO_ERROR; } bytes_written += res; } return NANO_OK; } /// /// @brief Signals the service fora possible session data waiting for inspection on /// on the shared mememory (IPC). /// /// @param attachment A pointer to a NanoAttachment structure representing the attachment to the nano service. /// @param cur_session_id An unsigned 32-bit integer representing the current session ID. /// @param chunk_type An enumeration representing the type of HTTP chunk being sent. /// /// @return NANO_OK if the ack response is received and matches the current session ID, /// NANO_ERROR if there is an error in signaling or reading the response, /// NANO_AGAIN if the response indicates an old session ID and polling should be retried, /// NANO_TIMEOUT if a timeout occurs while waiting for the response. /// static NanoCommunicationResult signal_for_session_data(NanoAttachment *attachment, uint32_t cur_session_id, AttachmentDataType chunk_type) { struct pollfd s_poll; NanoCommunicationResult res = NANO_OK; uint32_t reply_from_service; int timeout = attachment->fail_open_timeout; int retry; if (chunk_type == REQUEST_DELAYED_VERDICT) { timeout = attachment->fail_open_delayed_timeout; } if (attachment->inspection_mode != NON_BLOCKING_THREAD) { timeout = -1; } res = notify_signal_to_service(attachment, cur_session_id); if (res != NANO_OK) return res; write_dbg( attachment, cur_session_id, DBG_LEVEL_TRACE, "Successfully signaled to the service! pending to receive ack" ); for (retry = 0; retry < 3; retry++) { s_poll.fd = attachment->comm_socket; s_poll.events = POLLIN; s_poll.revents = 0; res = poll(&s_poll, 1, timeout); if (res < 0) { // Polling from the nano service has failed. write_dbg( attachment, cur_session_id, DBG_LEVEL_TRACE, "Polling from nano service had fail, failure: %d", res); return NANO_ERROR; } if (res == 0) { write_dbg(attachment, cur_session_id, DBG_LEVEL_TRACE, "Polling from nano service reached timeout"); continue; } res = read(attachment->comm_socket, ((char *)&reply_from_service), sizeof(reply_from_service)); if (res <= 0) { write_dbg( attachment, cur_session_id, DBG_LEVEL_WARNING, "Failed to read ack from nano service" ); return NANO_ERROR; } if (reply_from_service == cur_session_id) { // Read was successful and matches the current session Id. write_dbg( attachment, cur_session_id, DBG_LEVEL_TRACE, "Received signal from nano service to the current session. Current session id: %d", cur_session_id ); return NANO_OK; } else if (reply_from_service == CORRUPTED_SESSION_ID) { // Recieved corrupted session ID, returning error. write_dbg( attachment, cur_session_id, DBG_LEVEL_WARNING, "Received signal from nano service regarding a corrupted session. Current session id: %d", cur_session_id ); return NANO_ERROR; } else { // Recieved old session Id, attempting to poll again. write_dbg( attachment, cur_session_id, DBG_LEVEL_DEBUG, "Received signal from nano service regarding a previous session." " Current session id: %d, Signaled session id: %d", cur_session_id, reply_from_service ); return NANO_AGAIN; } } write_dbg(attachment, cur_session_id, DBG_LEVEL_WARNING, "Reached timeout during attempt to signal nano service"); return NANO_TIMEOUT; } /// @brief Receives verdict data from the nano service. /// /// This function attempts to receive verdict data from the nano service IPC channel. /// /// @param attachment A pointer to a NanoAttachment structure representing the attachment to the nano service. /// @param session_id An unsigned 32-bit integer representing the current session ID. /// /// @return A pointer to a HttpReplyFromService structure representing the received verdict data, /// or NULL if the data could not be received after multiple attempts. /// static HttpReplyFromService * receive_data_from_service(NanoAttachment *attachment, uint32_t session_id) { int res, retry; const char *reply_data; uint16_t reply_size; write_dbg(attachment, session_id, DBG_LEVEL_TRACE, "Receiving verdict data from nano service"); for (retry = 0; retry < 5; retry++) { if (!isDataAvailable(attachment->nano_service_ipc)) { write_dbg( attachment, session_id, DBG_LEVEL_TRACE, "Service data is not available - trying again (retry = %d) in 1 u-seconds", retry ); usleep(1); continue; } res = receiveData(attachment->nano_service_ipc, &reply_size, &reply_data); if (res < 0 || reply_data == NULL) { write_dbg( attachment, session_id, DBG_LEVEL_TRACE, "Failed to receive verdict data - trying again (retry = %d) in 1 u-seconds", retry ); usleep(1); continue; } return (HttpReplyFromService *)reply_data; } return NULL; } 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 ) { int attempt_num; NanoCommunicationResult res = NANO_OK; int err_code = 0; write_dbg( attachment, cur_session_id, DBG_LEVEL_TRACE, "Sending session data chunk for inspection" ); for (attempt_num = 1; attempt_num <= 5; attempt_num++) { err_code = sendChunkedData( attachment->nano_service_ipc, fragments_sizes, (const char **)fragments, num_of_data_elem ); write_dbg( attachment, cur_session_id, DBG_LEVEL_DEBUG, "Res code is %d, err code is %d", res, err_code ); if (res == NANO_OK && err_code == 0) { return NANO_OK; } if (err_code != 0) { write_dbg( attachment, cur_session_id, DBG_LEVEL_DEBUG, "Failed to send data for inspection - attempt number %d", attempt_num ); } // Notify the nano service to inspect new session data. // This notification is triggered when chunked data transmission fails. res = signal_for_session_data(attachment, cur_session_id, chunk_type); if (res == NANO_ERROR) { disconnect_communication(attachment); res = restart_communication(attachment); if (res == NANO_ERROR) return res; } } switch(err_code) { case -1: write_dbg( attachment, cur_session_id, DBG_LEVEL_WARNING, "Failed to send data for inspection - Corrupted shared memory" ); break; case -2: write_dbg( attachment, cur_session_id, DBG_LEVEL_WARNING, "Failed to send data for inspection - Requested write size exceeds the write limit" ); break; case -3: write_dbg( attachment, cur_session_id, DBG_LEVEL_WARNING, "Failed to send data for inspection - Cannot write to a full queue" ); break; case -4: write_dbg( attachment, cur_session_id, DBG_LEVEL_WARNING, "Failed to send data for inspection - Attempted write to a location outside the queue" ); break; default: write_dbg( attachment, cur_session_id, DBG_LEVEL_WARNING, "Failed to send data for inspection - Unknown error code %d", err_code ); break; } return NANO_ERROR; } /// /// @brief Allocates memory for a modification buffer and copies the given data into it. /// /// This function allocates memory for a modification buffer of size (data_size + 1) /// bytes, copies the provided data into the buffer, and assigns the pointer to /// the allocated memory to the 'target' parameter. /// /// @param attachment A pointer to a NanoAttachment structure representing the attachment. /// @param session_id The ID of the session. /// @param target A pointer to a pointer that will hold the address of the allocated memory. /// Upon successful allocation, this pointer will be updated to point to the /// allocated memory. /// @param data_size The size of the data to be copied into the modification buffer. /// @param data A pointer to the data to be copied into the modification buffer. /// /// @return NANO_OK if the memory allocation and copying are successful, NANO_ERROR otherwise. /// static NanoCommunicationResult create_modification_buffer( NanoAttachment *attachment, SessionID session_id, char **target, uint16_t data_size, char *data ) { *target = malloc(data_size + 1); if (*target == NULL) { write_dbg( attachment, session_id, DBG_LEVEL_WARNING, "Failed to allocate modification buffer of size: %d", data_size ); return NANO_ERROR; } snprintf(*target, data_size + 1, "%s", data); write_dbg( attachment, session_id, DBG_LEVEL_DEBUG, "Successfully created modification buffer, size: %d", data_size ); return NANO_OK; } /// /// @brief Creates a new Modification List node. /// /// This function allocates memory for a new NanoHttpModificationList node, /// initializes its fields with the provided modification data, and returns /// a pointer to the created node. /// /// @param attachment A pointer to a NanoAttachment structure representing the attachment. /// @param session_id The ID of the session. /// @param modification A pointer to a HttpInjectData structure containing the modification data. /// /// @return A pointer to the newly created NanoHttpModificationList node if successful, /// or NULL if memory allocation fails or if an error occurs during the creation process. /// static NanoHttpModificationList * create_modification_node(NanoAttachment *attachment, SessionID session_id, HttpInjectData *modification) { NanoCommunicationResult res; NanoHttpModificationList *modification_node = malloc(sizeof(NanoHttpModificationList)); if (modification_node == NULL) { write_dbg( attachment, session_id, DBG_LEVEL_WARNING, "Failed to allocate modification node of size: %d", sizeof(NanoHttpModificationList) ); return NULL; } res = create_modification_buffer( attachment, session_id, &modification_node->modification_buffer, modification->injection_size, modification->data ); if (res != NANO_OK) { free(modification_node); return NULL; } modification_node->next = NULL; modification_node->modification.is_header = modification->is_header; modification_node->modification.mod_type = modification->mod_type; modification_node->modification.injection_pos = modification->injection_pos; modification_node->modification.injection_size = modification->injection_size; modification_node->modification.orig_buff_index = modification->orig_buff_index; write_dbg( attachment, session_id, DBG_LEVEL_DEBUG, "Successfully created modification node. " "Is header: %d, " "Injection position: %d, " "Injection size: %d, " "Original buffer index: %d, " "Modification data: %s, " "Should change data: %d", modification_node->modification.is_header, modification_node->modification.injection_pos, modification_node->modification.injection_size, modification_node->modification.orig_buff_index, modification_node->modification_buffer, modification_node->modification.mod_type ); return modification_node; } /// /// @brief Handles the response of an injection request by creating modification nodes /// and updating the modification list. /// /// This function iterates over the injection data and creates modification nodes for each injection, /// updating the modification list accordingly. If an error occurs during node creation, it logs a warning /// and frees the memory associated with previously created modification nodes. /// /// @param attachment A pointer to a NanoAttachment structure representing the attachment. /// @param session_id The ID of the session. /// @param modification_list A pointer to a pointer to the head of the modification list. /// @param inject_data A pointer to an array of HttpInjectData structures containing injection data. /// @param modification_count The number of modifications in the injection data array. /// static void handle_inject_response( NanoAttachment *attachment, SessionID session_id, NanoHttpModificationList **modification_list, HttpInjectData *inject_data, uint8_t modification_count ) { NanoHttpModificationList *new_modification = NULL; NanoHttpModificationList *current_modification = NULL; unsigned int modification_index; for (modification_index = 0; modification_index < modification_count; modification_index++) { // Go over the modifications and create nodes. new_modification = create_modification_node(attachment, session_id, inject_data); write_dbg( attachment, session_id, DBG_LEVEL_DEBUG, "create modification node %d out of %d", modification_index, modification_count ); if (new_modification == NULL) { write_dbg(attachment, session_id, DBG_LEVEL_WARNING, "Failed to create modification node"); while (*modification_list) { current_modification = *modification_list; *modification_list = (*modification_list)->next; free(current_modification->modification_buffer); free(current_modification); } return; } if (*modification_list == NULL) { *modification_list = new_modification; current_modification = *modification_list; } else { while (*modification_list) { current_modification = *modification_list; *modification_list = (*modification_list)->next; } current_modification->next = new_modification; current_modification = current_modification->next; } // Moving the pointer to the next injection. inject_data = (HttpInjectData *)((char *)inject_data + sizeof(HttpInjectData) + inject_data->injection_size); } } /// /// @brief Create a custom web response by the provided data /// /// @param[in] attachment Nano attachment. /// @param[in] session_id Session ID. /// @param[in] web_response_data Web response data. /// @param[in, out] ctx_response_data Web response data to be set. /// static void handle_custom_web_response( NanoAttachment *attachment, SessionID session_id, WebResponseData **ctx_response_data, HttpWebResponseData *web_response_data ) { nano_str_t title = {0, NULL}; nano_str_t body = {0, NULL}; nano_str_t uuid; size_t incident_prefix_size = strlen("Incident Id: "); WebResponseData *new_response_data = NULL; CustomResponseData *custom_response_data = NULL; uuid.len = web_response_data->uuid_size; if (uuid.len >= UUID_SIZE) { write_dbg(attachment, session_id, DBG_LEVEL_WARNING, "Custom response UUID is too long"); return; } title.len = web_response_data->response_data.custom_response_data.title_size; if (title.len >= CUSTOM_RESPONSE_TITLE_SIZE) { write_dbg(attachment, session_id, DBG_LEVEL_WARNING, "Custom response title is too long"); return; } body.len = web_response_data->response_data.custom_response_data.body_size; if (body.len >= CUSTOM_RESPONSE_BODY_SIZE) { write_dbg(attachment, session_id, DBG_LEVEL_WARNING, "Custom response body is too long"); return; } new_response_data = malloc(sizeof(WebResponseData)); if (new_response_data == NULL) { write_dbg(attachment, session_id, DBG_LEVEL_WARNING, "Failed to allocate memory for web response data"); return; } write_dbg(attachment, session_id, DBG_LEVEL_TRACE, "Preparing to set custom web response page"); custom_response_data = malloc(sizeof(CustomResponseData)); if (custom_response_data == NULL) { write_dbg(attachment, session_id, DBG_LEVEL_WARNING, "Failed to allocate memory for custom response data"); free(new_response_data); return; } // Setting custom web response title's data. if (title.len > 0) { title.data = (u_char *)web_response_data->response_data.custom_response_data.data; } // Setting custom web response body's data. if (body.len > 0) { body.data = (u_char *)web_response_data->response_data.custom_response_data.data + title.len; } uuid.data = (u_char *)web_response_data->response_data.custom_response_data.data + title.len + body.len; custom_response_data->response_code = web_response_data->response_data.custom_response_data.response_code; if (title.data != NULL) { memcpy(custom_response_data->title, title.data, title.len); } custom_response_data->title[title.len] = '\0'; if (body.data != NULL) { memcpy(custom_response_data->body, body.data, body.len); } custom_response_data->body[body.len] = '\0'; new_response_data->web_response_type = CUSTOM_WEB_RESPONSE; memcpy(new_response_data->uuid, "Incident Id: ", incident_prefix_size); memcpy(new_response_data->uuid + incident_prefix_size, uuid.data, uuid.len); new_response_data->uuid[incident_prefix_size + uuid.len] = '\0'; new_response_data->data = custom_response_data; *ctx_response_data = new_response_data; } /// /// @brief Create a redirect response by the provided data /// /// @param[in] attachment Nano attachment. /// @param[in] session_id Session ID. /// @param[in] web_response_data Web response data. /// @param[in, out] ctx_response_data Web response data to be set. /// static void handle_redirect_response( NanoAttachment *attachment, SessionID session_id, WebResponseData **ctx_response_data, HttpWebResponseData *web_response_data ) { nano_str_t uuid; nano_str_t redirect_location; WebResponseData *new_response_data = NULL; RedirectData *redirect_data = NULL; uuid.len = web_response_data->uuid_size; if (uuid.len >= UUID_SIZE) { write_dbg(attachment, session_id, DBG_LEVEL_WARNING, "Custom response UUID is too long"); return; } redirect_location.len = web_response_data->response_data.redirect_data.redirect_location_size; if (redirect_location.len >= REDIRECT_RESPONSE_LOCATION_SIZE) { write_dbg(attachment, session_id, DBG_LEVEL_WARNING, "Custom response redirect location is too long"); return; } new_response_data = malloc(sizeof(WebResponseData)); if (new_response_data == NULL) { write_dbg(attachment, session_id, DBG_LEVEL_WARNING, "Failed to allocate memory for web response data"); return; } write_dbg(attachment, session_id, DBG_LEVEL_TRACE, "Preparing to set redirect web response"); redirect_data = malloc(sizeof(RedirectData)); if (redirect_data == NULL) { write_dbg(attachment, session_id, DBG_LEVEL_WARNING, "Failed to allocate memory for custom response data"); free(new_response_data); return; } redirect_location.data = (u_char *)web_response_data->response_data.redirect_data.redirect_location; memcpy(redirect_data->redirect_location, redirect_location.data, redirect_location.len); redirect_data->redirect_location[redirect_location.len] = '\0'; uuid.data = (u_char *)web_response_data->response_data.redirect_data.redirect_location + redirect_location.len; new_response_data->web_response_type = REDIRECT_WEB_RESPONSE; memcpy(new_response_data->uuid, uuid.data, uuid.len); new_response_data->uuid[uuid.len] = '\0'; new_response_data->data = redirect_data; *ctx_response_data = new_response_data; } /// /// @brief Handles drop response received from a service. /// /// This function is responsible for processing different types of web responses /// and taking appropriate actions based on the response type. /// /// @param[in] attachment Pointer to the NanoAttachment structure associated with the request. /// @param[in] session_id The session ID of the request. /// @param[in, out] ctx_response_data Pointer to the pointer of WebResponseData holding context response data. /// @param[in, out] web_response_data Pointer to the HttpWebResponseData containing the web response data. /// static void handle_drop_response( NanoAttachment *attachment, SessionID session_id, WebResponseData **ctx_response_data, HttpWebResponseData *web_response_data ) { switch (web_response_data->web_response_type) { case CUSTOM_WEB_RESPONSE: handle_custom_web_response(attachment, session_id, ctx_response_data, web_response_data); break; case REDIRECT_WEB_RESPONSE: handle_redirect_response(attachment, session_id, ctx_response_data, web_response_data); break; default: write_dbg( attachment, session_id, DBG_LEVEL_WARNING, "Received an unknown web response type %d", web_response_data->web_response_type ); break; } } NanoCommunicationResult service_reply_receiver( NanoAttachment *attachment, HttpSessionData *session_data, WebResponseData **web_response_data, NanoHttpModificationList **modification_list, AttachmentDataType chunk_type ) { HttpReplyFromService *reply_p; NanoHttpModificationList *current_modification = NULL; NanoCommunicationResult res; write_dbg( attachment, session_data->session_id, DBG_LEVEL_TRACE, "Receiving verdict replies for %d chunks of inspected data", session_data->remaining_messages_to_reply ); if (session_data->remaining_messages_to_reply == 0) { session_data->verdict = TRAFFIC_VERDICT_INSPECT; return NANO_OK; } do { res = signal_for_session_data(attachment, session_data->session_id, chunk_type); } while (res == NANO_AGAIN); if (res != NANO_OK) { disconnect_communication(attachment); restart_communication(attachment); return NANO_ERROR; } while (session_data->remaining_messages_to_reply) { // For each expected message, receive the reply from the nano service. reply_p = receive_data_from_service(attachment, session_data->session_id); if (reply_p == NULL) { write_dbg( attachment, session_data->session_id, DBG_LEVEL_WARNING, "Failed to get reply from the nano service" ); return NANO_ERROR; } // If reply isn't reconfiguration, it should be handled as part of session data related replies. // The reason is that the reconfiguration is not related to a specific session but to attachment as a whole. // It is a signal sent by the nano service to let the attachment know that it should reconfigure itself // due to a new configuration being applied and it is broadcasted to all attachment's instances. if (reply_p->verdict != TRAFFIC_VERDICT_RECONF) { if (reply_p->session_id != session_data->session_id) { // Verify if incoming reply is of a correct session. write_dbg( attachment, session_data->session_id, DBG_LEVEL_DEBUG, "Ignoring verdict to an already handled request %d", reply_p->session_id ); popData(attachment->nano_service_ipc); continue; } session_data->remaining_messages_to_reply--; } session_data->verdict = reply_p->verdict; write_dbg( attachment, session_data->session_id, DBG_LEVEL_TRACE, "Verdict %d received", session_data->verdict ); switch(session_data->verdict) { case TRAFFIC_VERDICT_INJECT: { // Verdict inject received from the nano service. write_dbg( attachment, session_data->session_id, DBG_LEVEL_TRACE, "Verdict inject received from the nano service" ); updateMetricField(attachment, INJECT_VERDICTS_COUNT, 1); handle_inject_response( attachment, session_data->session_id, modification_list, reply_p->modify_data->inject_data, reply_p->modification_count ); session_data->verdict = TRAFFIC_VERDICT_INSPECT; break; } case TRAFFIC_VERDICT_DROP: { // After a drop verdict no more replies will be sent, so we can leave the loop write_dbg( attachment, session_data->session_id, DBG_LEVEL_TRACE, "Verdict drop received from the nano service" ); updateMetricField(attachment, DROP_VERDICTS_COUNT, 1); handle_drop_response( attachment, session_data->session_id, web_response_data, reply_p->modify_data->web_response_data ); session_data->remaining_messages_to_reply = 0; while (*modification_list) { current_modification = *modification_list; *modification_list = (*modification_list)->next; free(current_modification->modification.data); free(current_modification); } popData(attachment->nano_service_ipc); return NANO_HTTP_FORBIDDEN; } case TRAFFIC_VERDICT_ACCEPT: { // After an accept verdict no more replies will be sent, so we can leave the loop write_dbg( attachment, session_data->session_id, DBG_LEVEL_TRACE, "Verdict accept received from the nano service" ); updateMetricField(attachment, ACCEPT_VERDICTS_COUNT, 1); session_data->remaining_messages_to_reply = 0; popData(attachment->nano_service_ipc); return NANO_OK; } case TRAFFIC_VERDICT_IRRELEVANT: { // After an irrelevant verdict, ignore the verdict and continue to the next response. write_dbg( attachment, session_data->session_id, DBG_LEVEL_TRACE, "Verdict irrelevant received from the nano service" ); updateMetricField(attachment, IRRELEVANT_VERDICTS_COUNT, 1); break; } case TRAFFIC_VERDICT_RECONF: { // After a reconfiguration verdict, reset attachment config. write_dbg( attachment, session_data->session_id, DBG_LEVEL_TRACE, "Verdict reconf received from the nano service" ); updateMetricField(attachment, RECONF_VERDICTS_COUNT, 1); reset_attachment_config(attachment); break; } case TRAFFIC_VERDICT_INSPECT: { // Inspect verdict, continue to the next response. write_dbg( attachment, session_data->session_id, DBG_LEVEL_TRACE, "Verdict inspect received from the nano service" ); updateMetricField(attachment, INSPECT_VERDICTS_COUNT, 1); break; } case TRAFFIC_VERDICT_DELAYED: { // After a delayed verdict, query the nano agent again to get an updated verdict. write_dbg( attachment, session_data->session_id, DBG_LEVEL_DEBUG, "Verdict delayed received from the nano service" ); updateMetricField(attachment, HOLD_VERDICTS_COUNT, 1); break; } } popData(attachment->nano_service_ipc); } write_dbg( attachment, session_data->session_id, DBG_LEVEL_DEBUG, "No finalized verdict (ACCEPT, DROP) has been received from the nano service" ); return NANO_OK; } /// /// @brief Set meta data fragment element data and size. /// @param[in, out] meta_data_elems Fragments data array. /// @param[in, out] meta_data_sizes Fragments data sizes array. /// @param[in] data Data to set into the meta_data_elems array. /// @param[in] size Size to be set into the meta_data_sizes array. /// @param[in] idx Index of the arrays to set the data and size into. /// static void set_fragment_elem(char **meta_data_elems, uint16_t *meta_data_sizes, void *data, uint16_t size, uint idx) { meta_data_elems[idx] = data; meta_data_sizes[idx] = size; } /// /// @brief Set meta data fragments identifiers. /// @details The data identifiers will be set on the 0 and 1 slots of the array. /// @param[in, out] meta_data_elems Fragments data array. /// @param[in, out] meta_data_sizes Fragments data sizes array. /// @param[in] data_type Data type identifier to be set. /// @param[in] cur_request_id Request's Id. /// static void set_fragments_identifiers( char **meta_data_elems, uint16_t *meta_data_sizes, uint16_t *data_type, uint32_t *cur_request_id ) { set_fragment_elem(meta_data_elems, meta_data_sizes, data_type, sizeof(uint16_t), 0); set_fragment_elem(meta_data_elems, meta_data_sizes, cur_request_id, sizeof(uint32_t), 1); } /// /// @brief Checks if inspection is required for the given source IP address. /// /// @param src_ip The source IP address to check for inspection requirement. /// @return 1 if inspection is required, 0 if inspection can be skipped, -1 on error. /// static int IsInspectionRequiredForSource(NanoAttachment *attachment, SessionID session_id, const nano_str_t *src_ip) { if (!isIPAddress((char *)src_ip->data)) { write_dbg(attachment, session_id, DBG_LEVEL_WARNING, "Input %s is not an IP adress", src_ip->data); return -1; } write_dbg(attachment, session_id, DBG_LEVEL_DEBUG, "IP is relevant: %s", src_ip->data); return !isSkipSource((char *)src_ip->data); } NanoCommunicationResult connect_to_comm_socket(NanoAttachment *attachment) { struct sockaddr_un server; int cur_errno = 0; // temp fix for errno changing during print // Close the old socket if there was one. if (attachment->comm_socket > 0) { close(attachment->comm_socket); attachment->comm_socket = -1; } // Connect a new socket. attachment->comm_socket = socket(AF_UNIX, SOCK_STREAM, 0); if (attachment->comm_socket < 0) { write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Could not create socket, Error: %s", strerror(errno)); return NANO_ERROR; } server.sun_family = AF_UNIX; strncpy(server.sun_path, attachment->shared_verdict_signal_path, sizeof(server.sun_path) - 1); if (connect(attachment->comm_socket, (struct sockaddr *)&server, sizeof(struct sockaddr_un)) != -1) { return NANO_OK; } cur_errno = errno; write_dbg( attachment, 0, DBG_LEVEL_DEBUG, "Could not connect to nano service. Path: %s, Error: %s, Errno: %d", server.sun_path, strerror(errno), cur_errno ); return NANO_ERROR; } NanoCommunicationResult connect_to_registration_socket(NanoAttachment *attachment) { struct sockaddr_un server; int cur_errno = 0; // temp fix for errno changing during print // Connect a new socket. attachment->registration_socket = socket(AF_UNIX, SOCK_STREAM, 0); if (attachment->registration_socket < 0) { write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Could not create socket, Error: %s", strerror(errno)); return NANO_ERROR; } server.sun_family = AF_UNIX; strncpy(server.sun_path, SHARED_REGISTRATION_SIGNAL_PATH, sizeof(server.sun_path) - 1); if (connect(attachment->registration_socket, (struct sockaddr *)&server, sizeof(struct sockaddr_un)) != -1) { return NANO_OK; } cur_errno = errno; write_dbg( attachment, 0, DBG_LEVEL_DEBUG, "Could not connect to nano service. Path: %s, Error: %s, Errno: %d", server.sun_path, strerror(errno), cur_errno ); return NANO_ERROR; } 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 ) { uint16_t chunk_type; NanoCommunicationResult res; char *fragments[META_DATA_COUNT + 2]; uint16_t fragments_sizes[META_DATA_COUNT + 2]; uint8_t meta_data_count = META_DATA_COUNT - 4; write_dbg(attachment, cur_request_id, DBG_LEVEL_TRACE, "Sending request start meta data for inspection"); if(!IsInspectionRequiredForSource(attachment, cur_request_id, &metadata->client_ip)) { write_dbg( attachment, cur_request_id, DBG_LEVEL_DEBUG, "Skipping IP Source" ); ctx->session_data_p->verdict = TRAFFIC_VERDICT_IRRELEVANT; ctx->res = NANO_DECLINED; return; } // Sets the fragments chunk_type = REQUEST_START; set_fragments_identifiers(fragments, fragments_sizes, &chunk_type, &cur_request_id); // Add protocol length to fragments. set_fragment_elem( fragments, fragments_sizes, &metadata->http_protocol.len, sizeof(uint16_t), HTTP_PROTOCOL_SIZE + 2 ); // Add protocol data to fragments. set_fragment_elem( fragments, fragments_sizes, metadata->http_protocol.data, metadata->http_protocol.len, HTTP_PROTOCOL_DATA + 2 ); // Add method data length to fragments. set_fragment_elem(fragments, fragments_sizes, &metadata->method_name.len, sizeof(uint16_t), HTTP_METHOD_SIZE + 2); // Add method data to fragments set_fragment_elem( fragments, fragments_sizes, metadata->method_name.data, metadata->method_name.len, HTTP_METHOD_DATA + 2 ); // Add host data length to the fragments. set_fragment_elem( fragments, fragments_sizes, &metadata->host.len, sizeof(uint16_t), HOST_NAME_SIZE + 2 ); // Add host data to the fragments. set_fragment_elem( fragments, fragments_sizes, metadata->host.data, metadata->host.len, HOST_NAME_DATA + 2 ); // Add listening IP metadata. set_fragment_elem( fragments, fragments_sizes, &metadata->listening_ip.len, sizeof(uint16_t), LISTENING_ADDR_SIZE + 2 ); set_fragment_elem( fragments, fragments_sizes, metadata->listening_ip.data, metadata->listening_ip.len, LISTENING_ADDR_DATA + 2 ); // Add listening port data. set_fragment_elem( fragments, fragments_sizes, &metadata->listening_port, sizeof(uint16_t), LISTENING_PORT + 2 ); // Add URI data. set_fragment_elem(fragments, fragments_sizes, &metadata->uri.len, sizeof(uint16_t), URI_SIZE + 2); set_fragment_elem(fragments, fragments_sizes, metadata->uri.data, metadata->uri.len, URI_DATA + 2); // Add client IP data. set_fragment_elem(fragments, fragments_sizes, &metadata->client_ip.len, sizeof(uint16_t), CLIENT_ADDR_SIZE + 2); set_fragment_elem( fragments, fragments_sizes, metadata->client_ip.data, metadata->client_ip.len, CLIENT_ADDR_DATA + 2 ); // Add client IP port. set_fragment_elem( fragments, fragments_sizes, &metadata->client_port, sizeof(uint16_t), CLIENT_PORT + 2 ); if (metadata->parsed_host.len > 0) { // Add parsed host data. set_fragment_elem( fragments, fragments_sizes, &metadata->parsed_host.len, sizeof(uint16_t), PARSED_HOST_SIZE + 2 ); set_fragment_elem( fragments, fragments_sizes, metadata->parsed_host.data, metadata->parsed_host.len, PARSED_HOST_DATA + 2 ); meta_data_count += 2; } if (metadata->parsed_uri.len > 0) { // Add parsed URI data. set_fragment_elem( fragments, fragments_sizes, &metadata->parsed_uri.len, sizeof(uint16_t), PARSED_URI_SIZE + 2 ); set_fragment_elem( fragments, fragments_sizes, metadata->parsed_uri.data, metadata->parsed_uri.len, PARSED_URI_DATA + 2 ); meta_data_count += 2; } // Sends all the data to the nano service. res = send_session_data_to_service( attachment, fragments, fragments_sizes, meta_data_count + 2, cur_request_id, chunk_type ); if (res == NANO_ERROR) { // Failed to send the metadata to nano service. write_dbg( attachment, cur_request_id, DBG_LEVEL_WARNING, "Failed to send request meta data to the nano service. Session ID: %d", cur_request_id ); ctx->res = NANO_ERROR; return; } if (res == NANO_OK) { *num_of_messages_sent += 1; } if (is_verdict_requested) { ctx->res = service_reply_receiver( attachment, ctx->session_data_p, &ctx->web_response_data, &ctx->modifications, chunk_type ); } } void nano_send_response_code( NanoAttachment *attachment, uint16_t response_code, HttpEventThreadCtx *ctx, uint32_t cur_request_id, unsigned int *num_messages_sent ) { char *fragments[RESPONSE_CODE_COUNT]; uint16_t fragments_sizes[RESPONSE_CODE_COUNT]; uint16_t chunk_type = RESPONSE_CODE; NanoCommunicationResult res; write_dbg(attachment, cur_request_id, DBG_LEVEL_TRACE, "Sending response code (%d) for inspection", response_code); set_fragments_identifiers(fragments, fragments_sizes, &chunk_type, &cur_request_id); set_fragment_elem(fragments, fragments_sizes, &response_code, sizeof(uint16_t), 2); res = send_session_data_to_service( attachment, fragments, fragments_sizes, RESPONSE_CODE_COUNT, cur_request_id, chunk_type ); if (res != NANO_OK) { ctx->res = NANO_ERROR; return; } *num_messages_sent += 1; } void nano_send_response_content_length( NanoAttachment *attachment, uint64_t content_length, HttpEventThreadCtx *ctx, uint32_t cur_request_id, unsigned int *num_messages_sent ) { char *fragments[CONTENT_LENGTH_COUNT]; uint16_t fragments_sizes[CONTENT_LENGTH_COUNT]; uint16_t chunk_type = CONTENT_LENGTH; NanoCommunicationResult res; write_dbg( attachment, cur_request_id, DBG_LEVEL_TRACE, "Sending content length (%ld) to the intaker", content_length ); set_fragments_identifiers(fragments, fragments_sizes, &chunk_type, &cur_request_id); set_fragment_elem(fragments, fragments_sizes, &content_length, sizeof(content_length), 2); res = send_session_data_to_service( attachment, fragments, fragments_sizes, CONTENT_LENGTH_COUNT, cur_request_id, chunk_type ); if (res != NANO_OK) { ctx->res = NANO_ERROR; return; } *num_messages_sent += 1; } /// /// @brief Sends a bulk of headers to the service using a NanoAttachment. /// /// This function takes a NanoAttachment pointer, /// an array of data fragments, an array of data fragment sizes, the number /// of headers in the bulk, a flag indicating if this is the last bulk, the index of the current bulk part, /// and the current request ID. /// /// @param attachment Pointer to the NanoAttachment struct representing the attachment. /// @param data Array of data fragments containing the bulk of headers. /// @param data_sizes Array of sizes corresponding to the data fragments. /// @param header_type Type of the headers (REQUEST_HEADER or RESPONSE_HEADER). /// @param num_headers Number of headers in the bulk. /// @param is_last_part Flag indicating if this is the last bulk. /// @param bulk_part_index Index of the current bulk part. /// @param cur_request_id Current request ID. /// /// @return NanoCommunicationResult NANO_OK if successful, NANO_ERROR otherwise. /// static NanoCommunicationResult send_header_bulk( NanoAttachment *attachment, char **data, uint16_t *data_sizes, AttachmentDataType header_type, const unsigned int num_headers, uint8_t is_last_part, uint8_t bulk_part_index, uint32_t cur_request_id ) { NanoCommunicationResult res; set_fragments_identifiers(data, data_sizes, (uint16_t *)&header_type, &cur_request_id); set_fragment_elem(data, data_sizes, &is_last_part, sizeof(is_last_part), 2); set_fragment_elem(data, data_sizes, &bulk_part_index, sizeof(bulk_part_index), 3); res = send_session_data_to_service( attachment, data, data_sizes, HEADER_DATA_COUNT * num_headers + 4, cur_request_id, REQUEST_HEADER ); if (res != NANO_OK) { write_dbg(attachment, cur_request_id, DBG_LEVEL_TRACE, "Failed to send bulk of %iu headers", num_headers); return NANO_ERROR; } write_dbg( attachment, cur_request_id, DBG_LEVEL_TRACE, "Successfully sent bulk number %ui with %d headers", bulk_part_index, num_headers ); return NANO_OK; } /// /// @brief Adds an HTTP header to a bulk data structure. /// /// This function takes an array of fragments, an array of fragment sizes, /// an HttpHeaderData struct representing the header, /// and an index to determine the position in the bulk data structure where the header should be added. /// /// @param attachment Pointer to the NanoAttachment struct representing the attachment. /// @param session_id The session ID associated with the attachment. /// @param fragments Array of fragments to add the header to. /// @param fragments_sizes Array of sizes corresponding to the fragments. /// @param header Pointer to the HttpHeaderData struct representing the header to add. /// @param index Index indicating where in the bulk data structure to add the header. /// static inline void add_header_to_bulk( NanoAttachment *attachment, uint32_t session_id, char **fragments, uint16_t *fragments_sizes, HttpHeaderData *header, unsigned int index ) { write_dbg( attachment, session_id, DBG_LEVEL_TRACE, "Sending current header (key: '%.*s', value: '%.*s') to inspection", header->key.len, header->key.data, header->value.len, header->value.data ); unsigned int pos = index * HEADER_DATA_COUNT; set_fragment_elem(fragments, fragments_sizes, &header->key.len, sizeof(uint16_t), pos + HEADER_KEY_SIZE + 4); set_fragment_elem(fragments, fragments_sizes, header->key.data, header->key.len, pos + HEADER_KEY_DATA + 4); set_fragment_elem(fragments, fragments_sizes, &header->value.len, sizeof(uint16_t), pos + HEADER_VAL_SIZE + 4); set_fragment_elem(fragments, fragments_sizes, header->value.data, header->value.len, pos + HEADER_VAL_DATA + 4); } 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 ) { int is_final_header = 0; int bulk_index = 0; char *fragments[HEADER_DATA_COUNT * MAX_HEADER_BULK_SIZE + 4]; uint16_t fragments_sizes[HEADER_DATA_COUNT * MAX_HEADER_BULK_SIZE + 4]; size_t header_index = 0; int fragment_index = 0; NanoCommunicationResult send_bulk_result; write_dbg( attachment, cur_request_id, DBG_LEVEL_TRACE, "Sending %s headers for inspection", header_type == REQUEST_HEADER ? "request" : "response" ); for (header_index = 0; header_index < headers->headers_count ; header_index++) { if (header_index == headers->headers_count - 1) { is_final_header = 1; } add_header_to_bulk( attachment, cur_request_id, fragments, fragments_sizes, &headers->data[header_index], fragment_index ); fragment_index++; if (fragment_index < MAX_HEADER_BULK_SIZE && !is_final_header) continue; send_bulk_result = send_header_bulk( attachment, fragments, fragments_sizes, header_type, fragment_index, is_final_header, bulk_index, cur_request_id ); if (send_bulk_result != NANO_OK) { write_dbg( attachment, cur_request_id, DBG_LEVEL_WARNING, "Failed to send request headers to the nano service. Session ID: %d", cur_request_id ); ctx->res = NANO_ERROR; return; } bulk_index++; fragment_index = 0; } *num_messages_sent += bulk_index; write_dbg( attachment, cur_request_id, DBG_LEVEL_TRACE, "Exit after inspection of %d headers", headers->headers_count ); if (is_verdict_requested) { ctx->res = service_reply_receiver( attachment, ctx->session_data_p, &ctx->web_response_data, &ctx->modifications, header_type ); } } void nano_body_sender( NanoAttachment *attachment, HttpBody *bodies, HttpEventThreadCtx *ctx, AttachmentDataType body_type, uint32_t cur_request_id, unsigned int *num_messages_sent ) { char *fragments[BODY_DATA_COUNT]; uint16_t fragments_sizes[BODY_DATA_COUNT]; uint8_t is_final_chunk = 0; uint8_t body_index = 0; nano_str_t *body = NULL; NanoCommunicationResult send_bulk_result; write_dbg( attachment, cur_request_id, DBG_LEVEL_TRACE, "Sending %s bodies for inspection", body_type == REQUEST_BODY ? "request" : "response" ); for (body_index = 0; body_index < bodies->bodies_count ; body_index++) { if (body_index == bodies->bodies_count - 1) { is_final_chunk = 1; } set_fragments_identifiers(fragments, fragments_sizes, (uint16_t *)&body_type, &cur_request_id); set_fragment_elem(fragments, fragments_sizes, &is_final_chunk, sizeof(is_final_chunk), 2); set_fragment_elem(fragments, fragments_sizes, &body_index, sizeof(body_index), 3); body = &bodies->data[body_index]; set_fragment_elem(fragments, fragments_sizes, body->data, body->len, 4); send_bulk_result = send_session_data_to_service( attachment, fragments, fragments_sizes, BODY_DATA_COUNT, cur_request_id, body_type ); if (send_bulk_result != NANO_OK) { write_dbg( attachment, cur_request_id, DBG_LEVEL_WARNING, "Failed to send request body to the nano service. Session ID: %d", cur_request_id ); ctx->res = NANO_ERROR; return; } } *num_messages_sent += body_index; write_dbg( attachment, cur_request_id, DBG_LEVEL_TRACE, "Exit after inspection of %d body chunks", bodies->bodies_count ); ctx->res = service_reply_receiver( attachment, ctx->session_data_p, &ctx->web_response_data, &ctx->modifications, body_type ); } void nano_end_transaction_sender( NanoAttachment *attachment, AttachmentDataType end_transaction_type, HttpEventThreadCtx *ctx, SessionID cur_request_id, unsigned int *num_messages_sent ) { char *fragments[END_TRANSACTION_DATA_COUNT]; uint16_t fragments_sizes[END_TRANSACTION_DATA_COUNT]; NanoCommunicationResult res; write_dbg( attachment, cur_request_id, DBG_LEVEL_TRACE, "Sending end %s event flag for inspection", end_transaction_type == REQUEST_END ? "request" : "response" ); set_fragments_identifiers(fragments, fragments_sizes, (uint16_t *)&end_transaction_type, &cur_request_id); res = send_session_data_to_service( attachment, fragments, fragments_sizes, END_TRANSACTION_DATA_COUNT, cur_request_id, attachment->fail_open_timeout ); if (res != NANO_OK) { write_dbg( attachment, cur_request_id, DBG_LEVEL_TRACE, "Failed to send end %s event flag for inspection", end_transaction_type == REQUEST_END ? "request" : "response" ); return; } *num_messages_sent += 1; ctx->res = service_reply_receiver( attachment, ctx->session_data_p, &ctx->web_response_data, &ctx->modifications, end_transaction_type ); } void nano_request_delayed_verdict( NanoAttachment *attachment, HttpEventThreadCtx *ctx, SessionID cur_request_id, unsigned int *num_messages_sent ) { char *fragments[DELAYED_VERDICT_DATA_COUNT]; uint16_t fragments_sizes[DELAYED_VERDICT_DATA_COUNT]; AttachmentDataType wait_transaction_type = REQUEST_DELAYED_VERDICT; NanoCommunicationResult res; write_dbg( attachment, cur_request_id, DBG_LEVEL_TRACE, "Sending delayed event flag for inspection" ); set_fragments_identifiers(fragments, fragments_sizes, (uint16_t *)&wait_transaction_type, &cur_request_id); res = send_session_data_to_service( attachment, fragments, fragments_sizes, DELAYED_VERDICT_DATA_COUNT, cur_request_id, attachment->fail_open_timeout ); if (res != NANO_OK) { write_dbg( attachment, cur_request_id, DBG_LEVEL_TRACE, "Failed to send delayed event flag for inspection" ); return; } *num_messages_sent += 1; ctx->res = service_reply_receiver( attachment, ctx->session_data_p, &ctx->web_response_data, &ctx->modifications, wait_transaction_type ); } void nano_send_metric_data_sender(NanoAttachment *attachment) { char *fragments; uint16_t fragments_sizes; uint16_t data_size; NanoCommunicationResult res; NanoHttpMetricData metric_data_to_send; write_dbg( attachment, 0, DBG_LEVEL_DEBUG, "Sending metric data to service" ); metric_data_to_send.data_type = METRIC_DATA_FROM_PLUGIN; data_size = METRIC_TYPES_COUNT * sizeof(metric_data_to_send.data[0]); memcpy(metric_data_to_send.data, attachment->metric_data, data_size); fragments = (char *)&metric_data_to_send; fragments_sizes = sizeof(NanoHttpMetricData); res = send_session_data_to_service( attachment, &fragments, &fragments_sizes, 1, 0, attachment->fail_open_timeout ); if (res != NANO_OK) { write_dbg( attachment, 0, DBG_LEVEL_TRACE, "Failed to send metric data to the nano service worker ID" ); return; } reset_metric_data(attachment); }