From 61836a2d9d1314a797b72dac8c867e8d600d0ff6 Mon Sep 17 00:00:00 2001 From: Kouya Heika Date: Fri, 24 Apr 2026 23:44:28 -0500 Subject: [PATCH] Re-add asset fetching --- config.ini.example | 2 + src/main.c | 204 +++++++++++++++++++++++++++++---------------- 2 files changed, 133 insertions(+), 73 deletions(-) diff --git a/config.ini.example b/config.ini.example index ecdf345..86e22d1 100644 --- a/config.ini.example +++ b/config.ini.example @@ -22,6 +22,8 @@ stylesheet= banner= # Time in seconds between status page recaching. (default: 15) recache_delay=15 +# Path (absolute or relative to working dir) to root of web. Not recommended. +root= ## SERVICE EXAMPLE # Services are always defined in their own section with their name prepended by "service." diff --git a/src/main.c b/src/main.c index f16ef87..1b41c96 100644 --- a/src/main.c +++ b/src/main.c @@ -21,6 +21,7 @@ uint16_t web_port = 7800; char* web_stylesheet = ""; char* web_banner = ""; uint32_t web_recache = 30; +char* web_root = ""; uint8_t daemon_enabled = 1; mxml_node_t* cached_index; @@ -63,6 +64,9 @@ static int config_handler(void* user, const char* section, const char* name, con strcpy(web_banner, value); } else if (strcmp(name, "recache_delay") == 0) { web_recache = atoi(value); + } else if (strcmp(name, "root") == 0) { + web_root = malloc(strlen(value)+1); + strcpy(web_root, value); } else { printf("Unknown configuration entry \'%s\' in \'%s\'\n", name, section); } @@ -125,78 +129,7 @@ static void init_signals() { sigaction(SIGPIPE, &sa, NULL); } -static enum MHD_Result http_response(void *cls, struct MHD_Connection *conn, const char* uri, const char* method, const char* version, const char* upload_Data, size_t* upload_data_size, void** req_cls) { - const union MHD_ConnectionInfo *info = MHD_get_connection_info(conn, MHD_CONNECTION_INFO_CLIENT_ADDRESS); - const char* client_ip = (char*)malloc(64); - uint16_t client_port = 0; - if (info != NULL) { - const struct sockaddr *addr = info->client_addr; - char ip_str[INET6_ADDRSTRLEN]; - - if (addr->sa_family == AF_INET) { - const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr; - client_ip = inet_ntop(AF_INET, &addr4->sin_addr, ip_str, sizeof(ip_str)); - client_port = ntohs(addr4->sin_port); - } else if (addr->sa_family == AF_INET6) { - const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr; - client_ip = inet_ntop(AF_INET6, &addr6->sin6_addr, ip_str, sizeof(ip_str)); - client_port = ntohs(addr6->sin6_port); - } - } - - uint16_t response_code = 0; - char* page; - size_t page_size = 0; - - if (strcmp(method, "GET") != 0 || strcmp(uri, "/") != 0) - response_code = 400; - - if (response_code != 200) { - mxml_node_t* doc = mxmlNewXML("1.0"); - if (strcmp(uri, "/") == 0 && response_code == 0) { - doc = cached_index; - response_code = 200; - } else { - mxml_node_t* html = mxmlNewElement(doc, "html"); - mxmlElementSetAttr(html, "xmlns", "http://www.w3.org/1999/xhtml"); - mxmlElementSetAttr(html, "xml:lang", "en"); - mxmlElementSetAttr(html, "lang", "en"); - - mxml_node_t* head = mxmlNewElement(html, "head"); - mxml_node_t *title = mxmlNewElement(head, "title"); - mxmlNewText(title, 0, daemon_name); - - mxml_node_t* meta_charset = mxmlNewElement(head, "meta"); - mxmlElementSetAttr(meta_charset, "http-equiv", "Content-Type"); - mxmlElementSetAttr(meta_charset, "content", "text/html; charset=utf-8"); - - mxml_node_t* meta_viewport = mxmlNewElement(head, "meta"); - mxmlElementSetAttr(meta_viewport, "name", "viewport"); - mxmlElementSetAttr(meta_viewport, "content", "width=device-width, initial-scale=1.0"); - - mxml_node_t* body = mxmlNewElement(html, "body"); - - mxml_node_t* center = mxmlNewElement(body, "center"); - - mxml_node_t* error_header = mxmlNewElement(center, "h1"); - char err_str[5]; - snprintf(err_str, sizeof(err_str)-1, "%i", response_code); - mxmlNewText(error_header, 0, err_str); - } - page = mxmlSaveAllocString(doc, MXML_NO_CALLBACK); - page_size = strlen(page); - } - - printf("[%lu] %s %s \"%s\" %s:%u %zu %zu %i\n", time(NULL), method, uri, version, client_ip, client_port, *upload_data_size, page_size, response_code); - fflush(stdout); - - struct MHD_Response* response = MHD_create_response_from_buffer_static(page_size, page); - enum MHD_Result ret = MHD_queue_response(conn, response_code, response); - MHD_destroy_response(response); - return ret; -}; - -mxml_node_t* generate_index() { +static mxml_node_t* generate_index() { mxml_node_t* doc = mxmlNewXML("1.0"); mxml_node_t* html = mxmlNewElement(doc, "html"); mxmlElementSetAttr(html, "xmlns", "http://www.w3.org/1999/xhtml"); @@ -215,7 +148,7 @@ mxml_node_t* generate_index() { mxmlElementSetAttr(meta_viewport, "name", "viewport"); mxmlElementSetAttr(meta_viewport, "content", "width=device-width, initial-scale=1.0"); - if (web_stylesheet != 0 && strcmp(web_stylesheet, "") != 0) { + if (web_stylesheet != 0 && strlen(web_stylesheet) > 0) { mxml_node_t* stylesheet = mxmlNewElement(head, "link"); mxmlElementSetAttr(stylesheet, "rel", "stylesheet"); mxmlElementSetAttr(stylesheet, "href", web_stylesheet); @@ -355,6 +288,131 @@ mxml_node_t* generate_index() { return doc; } +static uint16_t get_asset(char** buffer, const char* path, size_t* size) { + *size = 0; + if (strlen(web_root) + strlen(path) + 2 > PATH_MAX) + return 404; + + char full_path[PATH_MAX] = {}; + snprintf(full_path, sizeof(full_path), "%s%s", web_root, path); + + char resolved_path[PATH_MAX] = {}; + if (realpath(full_path, resolved_path) == NULL) + return 404; + + char resolved_base[PATH_MAX] = {}; + if (realpath(web_root, resolved_base) == NULL) + return 404; + + size_t resolved_base_len = strlen(resolved_base); + if (strncmp(resolved_path, resolved_base, resolved_base_len) != 0) + return 404; + + if (resolved_path[resolved_base_len] != '\0' && resolved_path[resolved_base_len] != '/') + return 404; + + FILE* asset = fopen(resolved_path, "rb"); + if (!asset) return 404; + + fseek(asset, 0, SEEK_END); + *size = ftell(asset); + rewind(asset); + + *buffer = (char*)malloc(*size + 1); + if (!*buffer) { + fclose(asset); + *size = 0; + return 500; + } + + size_t bytes_read = fread(*buffer, 1, *size, asset); + fclose(asset); + + if (bytes_read != *size) { + free(*buffer); + *size = 0; + return 500; + } + + return 200; +}; + +static enum MHD_Result http_response(void *cls, struct MHD_Connection *conn, const char* uri, const char* method, const char* version, const char* upload_Data, size_t* upload_data_size, void** req_cls) { + const union MHD_ConnectionInfo *info = MHD_get_connection_info(conn, MHD_CONNECTION_INFO_CLIENT_ADDRESS); + const char* client_ip = (char*)malloc(64); + uint16_t client_port = 0; + if (info != NULL) { + const struct sockaddr *addr = info->client_addr; + char ip_str[INET6_ADDRSTRLEN]; + + if (addr->sa_family == AF_INET) { + const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr; + client_ip = inet_ntop(AF_INET, &addr4->sin_addr, ip_str, sizeof(ip_str)); + client_port = ntohs(addr4->sin_port); + } else if (addr->sa_family == AF_INET6) { + const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr; + client_ip = inet_ntop(AF_INET6, &addr6->sin6_addr, ip_str, sizeof(ip_str)); + client_port = ntohs(addr6->sin6_port); + } + } + + uint16_t response_code = 0; + char* page; + size_t page_size = 0; + + if (strcmp(method, "GET") != 0) response_code = 400; + else + if (strcmp(uri, "/") != 0) { + if (web_root != 0 && strlen(web_root) > 0) + response_code = get_asset(&page, uri, &page_size); + else response_code = 404; + } + + if (response_code != 200) { + mxml_node_t* doc = mxmlNewXML("1.0"); + if (strcmp(uri, "/") == 0 && response_code == 0) { + doc = cached_index; + response_code = 200; + } else { + mxml_node_t* html = mxmlNewElement(doc, "html"); + mxmlElementSetAttr(html, "xmlns", "http://www.w3.org/1999/xhtml"); + mxmlElementSetAttr(html, "xml:lang", "en"); + mxmlElementSetAttr(html, "lang", "en"); + + mxml_node_t* head = mxmlNewElement(html, "head"); + mxml_node_t *title = mxmlNewElement(head, "title"); + mxmlNewText(title, 0, daemon_name); + + mxml_node_t* meta_charset = mxmlNewElement(head, "meta"); + mxmlElementSetAttr(meta_charset, "http-equiv", "Content-Type"); + mxmlElementSetAttr(meta_charset, "content", "text/html; charset=utf-8"); + + mxml_node_t* meta_viewport = mxmlNewElement(head, "meta"); + mxmlElementSetAttr(meta_viewport, "name", "viewport"); + mxmlElementSetAttr(meta_viewport, "content", "width=device-width, initial-scale=1.0"); + + mxml_node_t* body = mxmlNewElement(html, "body"); + + mxml_node_t* center = mxmlNewElement(body, "center"); + + mxml_node_t* error_header = mxmlNewElement(center, "h1"); + char err_str[5]; + snprintf(err_str, sizeof(err_str)-1, "%i", response_code); + mxmlNewText(error_header, 0, err_str); + } + page = mxmlSaveAllocString(doc, MXML_NO_CALLBACK); + page_size = strlen(page); + } + + printf("[%lu] %s %s \"%s\" %s:%u %zu %zu %i\n", time(NULL), method, uri, version, client_ip, client_port, *upload_data_size, page_size, response_code); + fflush(stdout); + + struct MHD_Response* response = MHD_create_response_from_buffer_static(page_size, page); + enum MHD_Result ret = MHD_queue_response(conn, response_code, response); + MHD_destroy_response(response); + return ret; +}; + int main(int argc, char** argv) { printf("Starting Stethoscope...\n"); fflush(stdout);