diff options
| author | frosty <gabriel@bwaaa.monster> | 2026-08-11 03:20:47 -0400 |
|---|---|---|
| committer | frosty <gabriel@bwaaa.monster> | 2026-08-11 03:20:47 -0400 |
| commit | 59b76fd601e5840043b9868ae2ca2f06b4b4e7a4 (patch) | |
| tree | e26f511b2cab0f3f3fc363c9ae435e0fd82c6695 /src/routing.c | |
| parent | b6951c18912c82e7863ebd6c6c77b891ad9aea0c (diff) | |
| download | beaker-59b76fd601e5840043b9868ae2ca2f06b4b4e7a4.tar.gz | |
feat: complete logging overhaul
Diffstat (limited to 'src/routing.c')
| -rw-r--r-- | src/routing.c | 373 |
1 files changed, 193 insertions, 180 deletions
diff --git a/src/routing.c b/src/routing.c index 00886c8..9894768 100644 --- a/src/routing.c +++ b/src/routing.c @@ -1,12 +1,12 @@ -#include "../beaker.h" -#include "beaker_globals.h" -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <unistd.h> +#include "../beaker.h" +#include "beaker_globals.h" #include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <unistd.h> void set_handler(const char *path, RequestHandler handler) { @@ -20,9 +20,10 @@ void set_handler(const char *path, RequestHandler handler) { handler_count++; } else { - fprintf(stderr, - "[WARNING] set_handler: Maximum number of handlers reached. Cannot register handler for '%s'.\n", - path); + beaker_log("WARN", + "set_handler: Maximum number of handlers reached. Cannot " + "register handler for '%s'.\n", + path); } } @@ -30,9 +31,9 @@ const char *get_mime_type(const char *file_path) { const char *ext = strrchr(file_path, '.'); if (!ext) { - return "application/octet-stream"; + return "application/octet-stream"; } - ext++; + ext++; if (strcmp(ext, "html") == 0 || strcmp(ext, "htm") == 0) return "text/html"; @@ -60,211 +61,215 @@ const char *get_mime_type(const char *file_path) { return "application/octet-stream"; } -static int canonicalize_path(char *canonical, const char *path, size_t max_len) { - char components[MAX_URL_PARAMS][MAX_KEY_LEN]; - int component_count = 0; +static int canonicalize_path(char *canonical, const char *path, + size_t max_len) { + char components[MAX_URL_PARAMS][MAX_KEY_LEN]; + int component_count = 0; - char *path_copy = strdup(path); - if (!path_copy) { - return -1; - } + char *path_copy = strdup(path); + if (!path_copy) { + return -1; + } - char *token = strtok(path_copy, "/"); - while (token) { - if (strcmp(token, ".") == 0) { + char *token = strtok(path_copy, "/"); + while (token) { + if (strcmp(token, ".") == 0) { - } else if (strcmp(token, "..") == 0) { + } else if (strcmp(token, "..") == 0) { - if (component_count > 0) { - component_count--; - } else { + if (component_count > 0) { + component_count--; + } else { - fprintf(stderr, "[SECURITY] Path traversal attempt: %s\n", path); - free(path_copy); - return -1; - } - } else if (strlen(token) > 0) { + beaker_log("SECURITY", "Path traversal attempt: %s\n", path); + free(path_copy); + return -1; + } + } else if (strlen(token) > 0) { - if (component_count < MAX_URL_PARAMS) { - strncpy(components[component_count], token, MAX_KEY_LEN - 1); - components[component_count][MAX_KEY_LEN - 1] = '\0'; - component_count++; - } - } - token = strtok(NULL, "/"); + if (component_count < MAX_URL_PARAMS) { + strncpy(components[component_count], token, MAX_KEY_LEN - 1); + components[component_count][MAX_KEY_LEN - 1] = '\0'; + component_count++; + } } + token = strtok(NULL, "/"); + } - free(path_copy); + free(path_copy); - canonical[0] = '\0'; - for (int i = 0; i < component_count; i++) { - if (strlen(canonical) + strlen(components[i]) + 2 > max_len) { - fprintf(stderr, "[ERROR] Canonical path too long\n"); - return -1; - } - strcat(canonical, "/"); - strcat(canonical, components[i]); + canonical[0] = '\0'; + for (int i = 0; i < component_count; i++) { + if (strlen(canonical) + strlen(components[i]) + 2 > max_len) { + beaker_log("ERROR", "Canonical path too long\n"); + return -1; } + strcat(canonical, "/"); + strcat(canonical, components[i]); + } - if (canonical[0] == '\0') { - strcpy(canonical, "/"); - } + if (canonical[0] == '\0') { + strcpy(canonical, "/"); + } - return 0; + return 0; } static bool is_safe_path_component(const char *component) { - if (strstr(component, "..") || - strstr(component, "//")) { - return false; - } + if (strstr(component, "..") || strstr(component, "//")) { + return false; + } - for (size_t i = 0; component[i]; i++) { - if (component[i] < 32 && component[i] != '\t') { - return false; - } + for (size_t i = 0; component[i]; i++) { + if (component[i] < 32 && component[i] != '\t') { + return false; } + } - return true; + return true; } static int url_decode(char *dst, const char *src, size_t dst_size) { - size_t i = 0, j = 0; + size_t i = 0, j = 0; - while (src[i] && j < dst_size - 1) { - if (src[i] == '%') { + while (src[i] && j < dst_size - 1) { + if (src[i] == '%') { - if (src[i+1] && src[i+2]) { - char hex[3] = {src[i+1], src[i+2], '\0'}; - char *endptr; - long value = strtol(hex, &endptr, 16); + if (src[i + 1] && src[i + 2]) { + char hex[3] = {src[i + 1], src[i + 2], '\0'}; + char *endptr; + long value = strtol(hex, &endptr, 16); - if (*endptr != '\0') { - fprintf(stderr, "[SECURITY] Invalid URL encoding: %%%s\n", hex); - return -1; - } + if (*endptr != '\0') { + beaker_log("SECURITY", "Invalid URL encoding: %%%s\n", hex); + return -1; + } - if (value == 0) { - fprintf(stderr, "[SECURITY] Null byte in URL encoding\n"); - return -1; - } + if (value == 0) { + beaker_log("SECURITY", "Null byte in URL encoding\n"); + return -1; + } - dst[j++] = (char)value; - i += 3; - } else { - fprintf(stderr, "[SECURITY] Incomplete URL encoding at end\n"); - return -1; - } - } else if (src[i] == '+') { + dst[j++] = (char)value; + i += 3; + } else { + beaker_log("SECURITY", "Incomplete URL encoding at end\n"); + return -1; + } + } else if (src[i] == '+') { - dst[j++] = ' '; - i++; - } else { - dst[j++] = src[i++]; - } + dst[j++] = ' '; + i++; + } else { + dst[j++] = src[i++]; } + } - dst[j] = '\0'; - return 0; + dst[j] = '\0'; + return 0; } char *parse_request_url(const char *request_line, UrlParams *params) { - char method[16]; - char raw_url_full[MAX_PATH_LEN]; - char http_version[16]; + char method[16]; + char raw_url_full[MAX_PATH_LEN]; + char http_version[16]; - if (sscanf(request_line, "%15s %255s %15s", method, raw_url_full, - http_version) != 3) { - fprintf(stderr, "[ERROR] parse_request_url: Malformed request line\n"); - return NULL; - } + if (sscanf(request_line, "%15s %255s %15s", method, raw_url_full, + http_version) != 3) { + beaker_log("ERROR", "parse_request_url: Malformed request line\n"); + return NULL; + } - params->count = 0; + params->count = 0; - char *working_raw = strdup(raw_url_full); - if (!working_raw) { - perror("Failed to allocate memory for URL copy"); - return NULL; - } + char *working_raw = strdup(raw_url_full); + if (!working_raw) { + beaker_log_errno("Failed to allocate memory for URL copy"); + return NULL; + } - char *query_start = strchr(working_raw, '?'); - if (query_start) { - *query_start = '\0'; + char *query_start = strchr(working_raw, '?'); + if (query_start) { + *query_start = '\0'; + } - } + char decoded_path[MAX_PATH_LEN]; + if (url_decode(decoded_path, working_raw, sizeof(decoded_path)) != 0) { + beaker_log("SECURITY", "Invalid URL encoding in path\n"); + free(working_raw); + return NULL; + } - char decoded_path[MAX_PATH_LEN]; - if (url_decode(decoded_path, working_raw, sizeof(decoded_path)) != 0) { - fprintf(stderr, "[SECURITY] Invalid URL encoding in path\n"); - free(working_raw); - return NULL; - } + char canonical_path[MAX_PATH_LEN]; + if (canonicalize_path(canonical_path, decoded_path, sizeof(canonical_path)) != + 0) { + beaker_log("SECURITY", "Path canonicalization failed\n"); + free(working_raw); + return NULL; + } - char canonical_path[MAX_PATH_LEN]; - if (canonicalize_path(canonical_path, decoded_path, sizeof(canonical_path)) != 0) { - fprintf(stderr, "[SECURITY] Path canonicalization failed\n"); + char *path_check = strdup(canonical_path); + if (path_check) { + char *token = strtok(path_check, "/"); + while (token) { + if (!is_safe_path_component(token)) { + beaker_log("SECURITY", "Unsafe path component: %s\n", token); + free(path_check); free(working_raw); return NULL; + } + token = strtok(NULL, "/"); } + free(path_check); + } - char *path_check = strdup(canonical_path); - if (path_check) { - char *token = strtok(path_check, "/"); - while (token) { - if (!is_safe_path_component(token)) { - fprintf(stderr, "[SECURITY] Unsafe path component: %s\n", token); - free(path_check); - free(working_raw); - return NULL; - } - token = strtok(NULL, "/"); - } - free(path_check); - } - - if (query_start) { - char *query_string = query_start + 1; + if (query_start) { + char *query_string = query_start + 1; - char *pair; - char *saveptr; + char *pair; + char *saveptr; - pair = strtok_r(query_string, "&", &saveptr); - while (pair && params->count < MAX_URL_PARAMS) { - char *equals = strchr(pair, '='); - if (equals) { - *equals = '\0'; + pair = strtok_r(query_string, "&", &saveptr); + while (pair && params->count < MAX_URL_PARAMS) { + char *equals = strchr(pair, '='); + if (equals) { + *equals = '\0'; - char *raw_key = pair; - char *raw_val = equals + 1; + char *raw_key = pair; + char *raw_val = equals + 1; - if (url_decode(params->params[params->count].key, raw_key, MAX_KEY_LEN) == 0 && - url_decode(params->params[params->count].value, raw_val, MAX_VALUE_LEN) == 0) { - params->count++; - } - } - pair = strtok_r(NULL, "&", &saveptr); + if (url_decode(params->params[params->count].key, raw_key, + MAX_KEY_LEN) == 0 && + url_decode(params->params[params->count].value, raw_val, + MAX_VALUE_LEN) == 0) { + params->count++; } + } + pair = strtok_r(NULL, "&", &saveptr); } + } - char *final_path = strdup(canonical_path); - free(working_raw); - return final_path; + char *final_path = strdup(canonical_path); + free(working_raw); + return final_path; } -bool serve_static_file_with_mime(const char *request_path_relative_to_static, const char *mime_type) { +bool serve_static_file_with_mime(const char *request_path_relative_to_static, + const char *mime_type) { char full_static_path[MAX_PATH_LEN]; - if (request_path_relative_to_static == NULL || strlen(request_path_relative_to_static) == 0) { - fprintf(stderr, "[ERROR] serve_static_file_with_mime: Empty path provided\n"); + if (request_path_relative_to_static == NULL || + strlen(request_path_relative_to_static) == 0) { + beaker_log("ERROR", "serve_static_file_with_mime: Empty path provided\n"); return false; } if (strstr(request_path_relative_to_static, "..") != NULL || strstr(request_path_relative_to_static, "//") != NULL || request_path_relative_to_static[0] == '/') { - fprintf(stderr, "[SECURITY] Attempted directory traversal: %s\n", - request_path_relative_to_static); + beaker_log("SECURITY", "Attempted directory traversal: %s\n", + request_path_relative_to_static); send_status("403 Forbidden"); return true; } @@ -274,24 +279,27 @@ bool serve_static_file_with_mime(const char *request_path_relative_to_static, co FILE *fp = fopen(full_static_path, "rb"); if (fp == NULL) { - fprintf(stderr, - "[ERROR] serve_static_file_with_mime: File '%s' not found or could not be opened. %s\n", - full_static_path, strerror(errno)); + beaker_log("ERROR", + "serve_static_file_with_mime: File '%s' not found or could not " + "be opened. %s\n", + full_static_path, strerror(errno)); return false; } struct stat st; if (fstat(fileno(fp), &st) < 0) { - perror("fstat error"); - fprintf(stderr, "[ERROR] serve_static_file_with_mime: fstat failed for '%s'.\n", - full_static_path); + beaker_log_errno_format( + "ERROR", "serve_static_file_with_mime: fstat failed for '%s'.\n", + full_static_path); fclose(fp); send_status("500 Internal Server Error"); return true; } long file_size = st.st_size; + current_response_status = 200; + current_response_size = file_size; if (!mime_type || mime_type[0] == '\0') { mime_type = get_mime_type(full_static_path); @@ -308,9 +316,10 @@ bool serve_static_file_with_mime(const char *request_path_relative_to_static, co mime_type, file_size); if (send(current_client_socket, http_header, strlen(http_header), 0) < 0) { - perror("Error sending static file header"); - fprintf(stderr, "[ERROR] serve_static_file_with_mime: Failed to send header for '%s'.\n", - full_static_path); + beaker_log_errno_format( + "ERROR", + "serve_static_file_with_mime: Failed to send header for '%s'.\n", + full_static_path); fclose(fp); return true; } @@ -319,19 +328,21 @@ bool serve_static_file_with_mime(const char *request_path_relative_to_static, co size_t bytes_read; bool send_error = false; - while (!send_error && !feof(fp) && !ferror(fp) && (bytes_read = fread(file_buffer, 1, sizeof(file_buffer), fp)) > 0) { + while (!send_error && !feof(fp) && !ferror(fp) && + (bytes_read = fread(file_buffer, 1, sizeof(file_buffer), fp)) > 0) { if (send(current_client_socket, file_buffer, bytes_read, 0) < 0) { - perror("Error sending static file content"); - fprintf(stderr, "[ERROR] serve_static_file_with_mime: Failed to send content for '%s'.\n", - full_static_path); + beaker_log_errno_format( + "ERROR", + "serve_static_file_with_mime: Failed to send content for '%s'.\n", + full_static_path); send_error = true; } } if (ferror(fp)) { - perror("Error reading static file"); - fprintf(stderr, "[ERROR] serve_static_file_with_mime: Failed to read '%s'.\n", - full_static_path); + beaker_log_errno_format( + "ERROR", "serve_static_file_with_mime: Failed to read '%s'.\n", + full_static_path); fclose(fp); return true; } @@ -347,16 +358,19 @@ bool serve_static_file(const char *request_path_relative_to_static) { bool serve_data(const char *data, size_t size, const char *mime_type) { if (current_client_socket == -1) { - fprintf(stderr, "[ERROR] serve_data: No client socket set. Cannot send data.\n"); + beaker_log("ERROR", + "serve_data: No client socket set. Cannot send data.\n"); return false; } if (data == NULL || size == 0) { - fprintf(stderr, "[ERROR] serve_data: Invalid data or size.\n"); + beaker_log("ERROR", "serve_data: Invalid data or size.\n"); return false; } char http_header[BUFFER_SIZE]; + current_response_status = 200; + current_response_size = size; snprintf(http_header, sizeof(http_header), "HTTP/1.1 200 OK\r\n" @@ -367,17 +381,16 @@ bool serve_data(const char *data, size_t size, const char *mime_type) { mime_type, size); if (send(current_client_socket, http_header, strlen(http_header), 0) < 0) { - perror("Error sending data header"); - fprintf(stderr, "[ERROR] serve_data: Failed to send header.\n"); + beaker_log_errno_format("ERROR", "serve_data: Failed to send header.\n"); return false; } size_t bytes_sent = 0; while (bytes_sent < size) { - size_t chunk = (size - bytes_sent > BUFFER_SIZE) ? BUFFER_SIZE : (size - bytes_sent); + size_t chunk = + (size - bytes_sent > BUFFER_SIZE) ? BUFFER_SIZE : (size - bytes_sent); if (send(current_client_socket, data + bytes_sent, chunk, 0) < 0) { - perror("Error sending data content"); - fprintf(stderr, "[ERROR] serve_data: Failed to send content.\n"); + beaker_log_errno_format("ERROR", "serve_data: Failed to send content.\n"); return false; } bytes_sent += chunk; |
