From 9294108f1349fcc7e5cbd89eb1f826f119bb0d62 Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Fri, 6 Sep 2024 17:30:37 +0900 Subject: [PATCH] Impl. print of specific headers arg/option TODO: Maybe refactor this to use a hash-map or something similar to speed up the process. If N = number-of-headers-to-print and M = size-of-request-buffer, then currrently this will take O(N*M) time. --- src/arg_parse.c | 30 ++++++++++++++++++++- src/arg_parse.h | 7 +++++ src/http.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++ src/http.h | 5 ++++ src/main.c | 34 +++++++++++++++++++++-- src/test.c | 20 ++++++++++++++ 6 files changed, 164 insertions(+), 3 deletions(-) diff --git a/src/arg_parse.c b/src/arg_parse.c index 4bc3e3b..4e07d33 100644 --- a/src/arg_parse.c +++ b/src/arg_parse.c @@ -26,14 +26,18 @@ void print_usage(void) { puts(" -p | --port "); puts(" --config="); puts(" --disable-peer-addr-print"); + puts(" --req-header-to-print=
(can be used multiple times)"); + puts(" For example: --req-header-to-print=User-Agent"); } Args parse_args(int argc, char **argv) { --argc; ++argv; + __attribute__((cleanup(c_simple_http_free_args))) Args args; memset(&args, 0, sizeof(Args)); + args.list_of_headers_to_log = simple_archiver_list_init(); while (argc > 0) { if ((strcmp(argv[0], "-p") == 0 || strcmp(argv[0], "--port") == 0) @@ -48,6 +52,19 @@ Args parse_args(int argc, char **argv) { args.config_file = argv[0] + 9; } else if (strcmp(argv[0], "--disable-peer-addr-print") == 0) { args.flags |= 1; + } else if (strncmp(argv[0], "--req-header-to-print=", 22) == 0 + && strlen(argv[0]) > 22) { + size_t arg_size = strlen(argv[0]) + 1 - 22; + char *header_buf = malloc(arg_size); + memcpy(header_buf, argv[0] + 22, arg_size); + if (simple_archiver_list_add( + args.list_of_headers_to_log, header_buf, NULL) + != 0) { + fprintf( + stderr, "ERROR Failed to parse \"--req-header-to-print=...\" !\n"); + free(header_buf); + exit(1); + } } else { puts("ERROR: Invalid args!\n"); print_usage(); @@ -58,7 +75,18 @@ Args parse_args(int argc, char **argv) { ++argv; } - return args; + // Prevent freeing of Args due to successful parsing. + Args to_return = args; + args.list_of_headers_to_log = NULL; + return to_return; +} + +void c_simple_http_free_args(Args *args) { + if (args) { + if (args->list_of_headers_to_log) { + simple_archiver_list_free(&args->list_of_headers_to_log); + } + } } // vim: ts=2 sts=2 sw=2 diff --git a/src/arg_parse.h b/src/arg_parse.h index 641c29b..f9b2d17 100644 --- a/src/arg_parse.h +++ b/src/arg_parse.h @@ -17,6 +17,9 @@ #ifndef SEODISPARATE_COM_C_SIMPLE_HTTP_ARG_PARSE_H_ #define SEODISPARATE_COM_C_SIMPLE_HTTP_ARG_PARSE_H_ +// Third party includes. +#include + typedef struct Args { // xxxx xxx0 - enable peer addr print. // xxxx xxx1 - disable peer addr print. @@ -24,12 +27,16 @@ typedef struct Args { unsigned short port; // Does not need to be free'd, this should point to a string in argv. const char *config_file; + // Needs to be free'd. + SDArchiverLinkedList *list_of_headers_to_log; } Args; void print_usage(void); Args parse_args(int argc, char **argv); +void c_simple_http_free_args(Args *args); + #endif // vim: ts=2 sts=2 sw=2 diff --git a/src/http.c b/src/http.c index b448f93..d9cb3fd 100644 --- a/src/http.c +++ b/src/http.c @@ -209,4 +209,75 @@ char *c_simple_http_strip_path(const char *path, size_t path_size) { return stripped_path; } +char *c_simple_http_filter_request_header( + const char *request, size_t request_size, const char *header) { + if (!request) { + fprintf(stderr, "ERROR filter_request_header: request is NULL!\n"); + return NULL; + } else if (request_size == 0) { + fprintf(stderr, "ERROR filter_request_header: request_size is zero!\n"); + return NULL; + } else if (!header) { + fprintf(stderr, "ERROR filter_request_header: header is NULL!\n"); + return NULL; + } + + // xxxx xxx0 - Start of line. + // xxxx xxx1 - In middle of line. + // xxxx xx0x - Header NOT found. + // xxxx xx1x - Header was found. + unsigned int flags = 0; + size_t idx = 0; + size_t line_start_idx = 0; + size_t header_idx = 0; + for(; idx < request_size && request[idx] != 0; ++idx) { + if ((flags & 1) == 0) { + if (request[idx] == '\n') { + continue; + } + line_start_idx = idx; + flags |= 1; + if (request[idx] == header[header_idx]) { + ++header_idx; + if (header[header_idx] == 0) { + flags |= 2; + break; + } + } else { + header_idx = 0; + } + } else { + if (header_idx != 0) { + if (request[idx] == header[header_idx]) { + ++header_idx; + if (header[header_idx] == 0) { + flags |= 2; + break; + } + } else { + header_idx = 0; + } + } + + if (request[idx] == '\n') { + flags &= 0xFFFFFFFE; + } + } + } + + if ((flags & 2) != 0) { + // Get line end starting from line_start_idx. + for ( + idx = line_start_idx; + idx < request_size && request[idx] != 0 && request[idx] != '\n'; + ++idx); + char *line_buf = malloc(idx - line_start_idx + 1); + memcpy(line_buf, request + line_start_idx, idx - line_start_idx); + line_buf[idx - line_start_idx] = 0; + return line_buf; + } + + return NULL; +} + // vim: ts=2 sts=2 sw=2 diff --git a/src/http.h b/src/http.h index 115e8e1..0c6eebe 100644 --- a/src/http.h +++ b/src/http.h @@ -56,6 +56,11 @@ char *c_simple_http_request_response( /// Must be free'd if returns non-NULL. char *c_simple_http_strip_path(const char *path, size_t path_size); +/// Returns a line from the request that starts with the "header" C-string. +/// If returns non-NULL, must be free'd. +char *c_simple_http_filter_request_header( + const char *request, size_t request_size, const char *header); + #endif // vim: ts=2 sts=2 sw=2 diff --git a/src/main.c b/src/main.c index bac21d6..4c84bc6 100644 --- a/src/main.c +++ b/src/main.c @@ -39,6 +39,11 @@ #include "constants.h" #include "http.h" +typedef struct C_SIMPLE_HTTP_INTERNAL_Header_Check_Ctx { + const unsigned char *recv_buf; + ssize_t recv_buf_size; +} C_SIMPLE_HTTP_INTERNAL_Header_Check_Ctx; + #define CHECK_ERROR_WRITE(write_expr) \ if (write_expr < 0) { \ close(connection_fd); \ @@ -46,8 +51,24 @@ continue; \ } +int c_simple_http_headers_check_print(void *data, void *ud) { + C_SIMPLE_HTTP_INTERNAL_Header_Check_Ctx *ctx = ud; + const char *header_c_str = data; + + __attribute__((cleanup(simple_archiver_helper_cleanup_c_string))) + char *matching_line = c_simple_http_filter_request_header( + (const char*)ctx->recv_buf, + (size_t)ctx->recv_buf_size, + header_c_str); + if (matching_line) { + printf("Printing header line: %s\n", matching_line); + } + return 0; +} + int main(int argc, char **argv) { - const Args args = parse_args(argc, argv); + __attribute__((cleanup(c_simple_http_free_args))) + Args args = parse_args(argc, argv); if (!args.config_file) { fprintf(stderr, "ERROR Config file not specified!\n"); @@ -131,8 +152,8 @@ int main(int argc, char **argv) { "WARNING Failed to read from new connection, closing...\n"); continue; } - // DEBUG print received buf. #ifndef NDEBUG + // DEBUG print received buf. for (unsigned int idx = 0; idx < C_SIMPLE_HTTP_RECV_BUF_SIZE && idx < read_ret; ++idx) { @@ -145,6 +166,15 @@ int main(int argc, char **argv) { } puts(""); #endif + { + C_SIMPLE_HTTP_INTERNAL_Header_Check_Ctx ctx; + ctx.recv_buf = recv_buf; + ctx.recv_buf_size = read_ret; + simple_archiver_list_get( + args.list_of_headers_to_log, + c_simple_http_headers_check_print, + &ctx); + } size_t response_size = 0; enum C_SIMPLE_HTTP_ResponseCode response_code; diff --git a/src/test.c b/src/test.c index 8aab8d8..2cb3e8f 100644 --- a/src/test.c +++ b/src/test.c @@ -6,6 +6,7 @@ // Local includes. #include "config.h" #include "http_template.h" +#include "http.h" // Third party includes. #include @@ -444,6 +445,25 @@ int main(void) { simple_archiver_helper_cleanup_c_string(&buf); } + // Test http. + { + const char *request = + "GET /path HTTP/1.1\nA-Header: Something\nAnother-Header: Other\n" + "Different-Header: Different\n"; + size_t request_size = 92; + + __attribute__((cleanup(simple_archiver_helper_cleanup_c_string))) + char *header_line_buf = c_simple_http_filter_request_header( + request, request_size, "Another-Header"); + ASSERT_TRUE(header_line_buf); + ASSERT_STREQ(header_line_buf, "Another-Header: Other"); + + simple_archiver_helper_cleanup_c_string(&header_line_buf); + header_line_buf = c_simple_http_filter_request_header( + request, request_size, "Non-Existant-Header"); + ASSERT_FALSE(header_line_buf); + } + RETURN() }