From 291c72c2a5135c7432cf2411ba0871aef5ba6199 Mon Sep 17 00:00:00 2001 From: overcuriousity Date: Tue, 2 Sep 2025 23:40:35 +0200 Subject: [PATCH] progress --- src/main.c | 306 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 285 insertions(+), 21 deletions(-) diff --git a/src/main.c b/src/main.c index 5787f5f..e82f561 100644 --- a/src/main.c +++ b/src/main.c @@ -126,6 +126,13 @@ int is_space(char c) { return (c == ' ' || c == '\t'); } +//Hilfsfunktion zum Prüfen, ob ein String mit einem bestimmten Präfix beginnt. Nötig für das Parsen der Filter in der main()-Funktion +int starts_with(char* str, char* prefix) { + //positiver Vergleich gibt bei strncmp 0 aus - daher prüfen ob == 0, um 1 bei positiver Prüfung zu erhalten + // strncmp statt strcmp, da nur die ersten Bytes beider Strings geprüft werden (Anzahl: drittes Argument) + return strncmp(str, prefix, strlen(prefix)) == 0; +} + // Hilfsfunktion zum Überspringen von Leerzeichen, gibt den Pointer für das nächste nicht-Leerzeichen zurück. Nötig für Parser. char* skip_spaces(char* str) { while (is_space(*str)) { @@ -230,8 +237,8 @@ void mem_expand_dynamically() { struct log_entry *new_ptr = realloc(all_entries, max_entries * sizeof(struct log_entry)); if (new_ptr == NULL) { - printf("ERROR: Speicher konnte nicht auf %d Einträge erweitert werden, ..\n", max_entries); - printf("ERROR: Benötigter Speicher: %lu Bytes\n", (unsigned long)(max_entries * sizeof(struct log_entry))); + printf("FEHLER: Speicher konnte nicht auf %d Einträge erweitert werden, ..\n", max_entries); + printf("FEHLER: Benötigter Speicher: %lu Bytes\n", (unsigned long)(max_entries * sizeof(struct log_entry))); cleanup_and_exit(); } @@ -245,8 +252,8 @@ void allocate_initial_memory() { all_entries = malloc(max_entries * sizeof(struct log_entry)); if (all_entries == NULL) { - printf("ERROR: Konnte %d Einträge nicht allozieren, ..\n", max_entries); - printf("ERROR: %lu Bytes\n", (unsigned long)(max_entries * sizeof(struct log_entry))); + printf("FEHLER: Konnte %d Einträge nicht allozieren, ..\n", max_entries); + printf("FEHLER: %lu Bytes\n", (unsigned long)(max_entries * sizeof(struct log_entry))); exit(1); // cleanup_and_exit() nicht nötig, da der Speicherbereich nicht beschrieben wurde - use-after-free unproblematisch } @@ -397,7 +404,7 @@ int parse_simple_log_line(char* line, int entry_index, char* source_file) { // N // 107.170.27.248 - - [31/Aug/2025:00:11:42 +0000] "GET /.git/config HTTP/1.1" 400 255 "-" "Mozilla/5.0; Keydrop.io/1.0(onlyscans.com/about);" "-" // ^ } else { - printf("ERROR: Unerwartetes Log-Format. Lediglich mit standard-nginx-accesslog kompatibel.\nDer Fehler ist beim Prüfen des Timestamps aufgetreten, dieser sollte folgendes Format haben:\n[DD/MMM/YYYY:HH:MM:SS +0000]\n\n"); + printf("FEHLER: Unerwartetes Log-Format. Lediglich mit standard-nginx-accesslog kompatibel.\nDer Fehler ist beim Prüfen des Timestamps aufgetreten, dieser sollte folgendes Format haben:\n[DD/MMM/YYYY:HH:MM:SS +0000]\n\n"); cleanup_and_exit(); } @@ -472,7 +479,7 @@ int parse_simple_log_line(char* line, int entry_index, char* source_file) { // N // 107.170.27.248 - - [31/Aug/2025:00:11:42 +0000] "GET /.git/config HTTP/1.1" 400 255 "-" "Mozilla/5.0; Keydrop.io/1.0(onlyscans.com/about);" "-" // ^ } else { - printf("ERROR: Unerwartetes Log-Format. Lediglich mit standard-nginx-accesslog kompatibel.\nDer Fehler ist beim Prüfen der HTTP-Methode aufgetreten. Diese steht innerhalb eines Strings zusammen mit dem URL-Pfad:\n\"GET /.git/config HTTP/1.1\"\n\n"); + printf("FEHLER: Unerwartetes Log-Format. Lediglich mit standard-nginx-accesslog kompatibel.\nDer Fehler ist beim Prüfen der HTTP-Methode aufgetreten. Diese steht innerhalb eines Strings zusammen mit dem URL-Pfad:\n\"GET /.git/config HTTP/1.1\"\n\n"); cleanup_and_exit(); } @@ -506,7 +513,7 @@ int parse_simple_log_line(char* line, int entry_index, char* source_file) { // N } if (*current_pos == '"') current_pos++; // schließendes Anführungszeichen überspringen } else { - printf("ERROR: Unerwartetes Log-Format. Lediglich mit standard-nginx-accesslog kompatibel.\nDer Fehler ist beim Prüfen des Referrer-Feldes aufgetreten.\n\n"); + printf("FEHLER: Unerwartetes Log-Format. Lediglich mit standard-nginx-accesslog kompatibel.\nDer Fehler ist beim Prüfen des Referrer-Feldes aufgetreten.\n\n"); cleanup_and_exit(); } @@ -523,7 +530,7 @@ int parse_simple_log_line(char* line, int entry_index, char* source_file) { // N all_entries[entry_index].user_agent[i] = '\0'; if (*current_pos == '"') current_pos++; } else { - printf("ERROR: Unerwartetes Log-Format. Lediglich mit standard-nginx-accesslog kompatibel.\nDer Fehler ist beim Prüfen des User-Agent aufgetreten. Dieser steht innerhalb eines Strings:\n\"Mozilla/5.0; Keydrop.io/1.0(onlyscans.com/about);\"\n\n"); + printf("FEHLER: Unerwartetes Log-Format. Lediglich mit standard-nginx-accesslog kompatibel.\nDer Fehler ist beim Prüfen des User-Agent aufgetreten. Dieser steht innerhalb eines Strings:\n\"Mozilla/5.0; Keydrop.io/1.0(onlyscans.com/about);\"\n\n"); cleanup_and_exit(); } get_current_timestamp(all_entries[entry_index].parsing_timestamp, sizeof(all_entries[entry_index].parsing_timestamp)); @@ -537,7 +544,7 @@ int parse_simple_log_line(char* line, int entry_index, char* source_file) { // N void load_regular_file(char* filename) { FILE* file = fopen(filename, "r"); if (file == NULL) { - printf("ERROR: Kann Datei '%s' nicht öffnen!\n", filename); + printf("FEHLER: Kann Datei '%s' nicht öffnen!\n", filename); return; } printf("INFO: Lade Datei: %s\n", filename); @@ -592,7 +599,7 @@ void load_log_file(char* path) { closedir(dir); if (files_found == 0) { - printf("WARNING: Keine .log Dateien im Verzeichnis gefunden.\n"); + printf("WARNUNG: Keine .log Dateien im Verzeichnis gefunden.\n"); printf(" Tipp: Für .gz Dateien verwenden Sie 'gunzip *.gz' zum Dekomprimieren\n"); } else { printf("INFO: Insgesamt %d .log Dateien verarbeitet.\n", files_found); @@ -1000,7 +1007,7 @@ void export_filtered_entries(char *filepath) { printf("\nINFO: Schreibe Datei %s...\n", filename); FILE* file = fopen(filename, "w"); if (file == NULL) { - printf("ERROR: Kann Datei '%s' nicht erstellen!\n", filename); + printf("FEHLER: Kann Datei '%s' nicht erstellen!\n", filename); return; } @@ -1202,7 +1209,7 @@ void show_status() { printf(" %d Logzeilen in Datenstruktur\n", total_entries); printf(" Speicherbelegung: %lu Bytes\n", (unsigned long)(max_entries * sizeof(struct log_entry))); } else { - printf(" ERROR: Keine Einträge in Datenstruktur!\n"); + printf(" FEHLER: Keine Einträge in Datenstruktur!\n"); } printf("\n Aktive Filter:\n"); @@ -1472,30 +1479,34 @@ int handle_menu_shortcuts(int choice) { return choice; } +// überall wo integer aus String-Input gelesen werden müssen. basiert auf strtol, was gegen Buffer Overflow sicher sein soll int safe_read_integer(const char* prompt, int min_val, int max_val) { char input[50]; int value; char *endptr; - + // endlos while (1) { printf("%s", prompt); + // scanf liest den Input in einen pointer ein, daher nicht &input. Die Usereingabe ist ein String, also ein Array if (scanf("%49s", input) != 1) { clear_input_buffer(); printf("FEHLER: Ungültige Eingabe. Bitte erneut versuchen.\n"); continue; } clear_input_buffer(); - + // Standard Rückgabewerte für die Menünavigation, die Werte werden für die choice-Variable genutzt if (strcmp(input, "b") == 0 || strcmp(input, "B") == 0) return -2; if (strcmp(input, "m") == 0 || strcmp(input, "M") == 0) return -3; if (strcmp(input, "q") == 0 || strcmp(input, "Q") == 0) return -4; + // Konvertierung der Eingabe in einen Long-Integer der Basis 10. Der endptr speichert einen Pointer auf das erste ungültige Zeichen nach einlesen des Lon-Integer value = strtol(input, &endptr, 10); + // wenn der endptr der Nullterminator ist, handelte es sich bei der Eingabe sicher um einen Long-Integer. if (*endptr != '\0') { printf("FEHLER: '%s' ist keine gültige Zahl. Bitte erneut versuchen.\n", input); continue; } - + // Prüfen, ob sich der Wert im Erwartungsbereich befindet if (value < min_val || value > max_val) { printf("FEHLER: Wert muss zwischen %d und %d liegen. Bitte erneut versuchen.\n", min_val, max_val); continue; @@ -2169,6 +2180,227 @@ void menu_show_entries() { } } +// Funktion zum Parsen der Filter-Werte, die mit --= übergeben werden. +// values_str sind die Werte hinter dem =, filter_type die Werte vor dem = +// filter_type wird von parse_filter_argument() übergeben +void parse_filter_values(const char* values_str, const char* filter_type) { + char values_local[1024]; + // Werte in lokale Variable einlesen, Nullterminator setzen + strncpy(values_local, values_str, sizeof(values_local) - 1); + values_local[sizeof(values_local) - 1] = '\0'; + + // Einlesen der Werte mit strtok. Hier wird das erste Token gesetzt, damit die exit condition der while-Schleife nicht triggert. + // strtok liest einen string in einen Pointer bis zu einem spezifizierten Delimiter ein. Die gelesenen Bytes werden aus dem Input entfernt. + char* token = strtok(values_local, ","); + while (token != NULL) { + // Sollte der Nutzer Leerzeichen verwendet haben, müssen diese übersprungen werden + while (*token == ' ') token++; + // setzen des Modus, Standard inklusiv + filter_mode_t mode = FILTER_INCLUDE; + // ..mit !-Präfix exklusiv. + if (*token == '!') { + mode = FILTER_EXCLUDE; + token++; // den Pointer vom ! weiteriterieren, dieser ist nicht Teil des Filters + } + + if (strlen(token) > 0) { + // wenn das token Werte hat, werden die entsprechenden Funktionen aufgerufen, die die Filter setzen + if (strcmp(filter_type, "status") == 0) { + add_status_filter(token, mode); + } else if (strcmp(filter_type, "ip") == 0) { + add_ip_filter(token, mode); + } else if (strcmp(filter_type, "method") == 0) { + add_method_filter(token, mode); + } else if (strcmp(filter_type, "useragent") == 0) { + add_useragent_filter(token, mode); + } else if (strcmp(filter_type, "url") == 0) { + add_url_filter(token, mode); + } + } + // nächste Iteration - nächstes token einlesen, damit die while-Schleife weiteriteriert + token = strtok(NULL, ","); + } +} + +// Funktionen zum setzen der Filter (existierende Datenstrukturen) +void add_status_filter(const char* value, filter_mode_t mode) { + if (filters.status_count >= MAX_FILTERS) { + printf("WARNUNG: MAX_FILTERS überschritten, ignoriere: %s\n", value); + return; + } + // TODO + int status_code = atoi(value); + if (status_code < 100 || status_code > 599) { + printf("WARNUNG: Invalid status code: %s (must be 100-599)\n", value); + return; + } + + filters.status_filters[filters.status_count].code = status_code; + filters.status_filters[filters.status_count].mode = mode; + filters.status_count++; + + printf("Added status filter: %s%d\n", mode == FILTER_EXCLUDE ? "!" : "", status_code); +} + +void add_ip_filter(const char* value, filter_mode_t mode) { + if (filters.ip_count >= MAX_FILTERS) { + printf("WARNUNG: MAX_FILTERS überschritten, ignoriere: %s\n", value); + return; + } + + if (strlen(value) >= sizeof(filters.ip_filters[0].ip_address)) { + printf("WARNUNG: IP address too long: %s\n", value); + return; + } + + strcpy(filters.ip_filters[filters.ip_count].ip_address, value); + filters.ip_filters[filters.ip_count].mode = mode; + filters.ip_count++; + + printf("Added IP filter: %s%s\n", mode == FILTER_EXCLUDE ? "!" : "", value); +} + +void add_method_filter(const char* value, filter_mode_t mode) { + if (filters.method_count >= MAX_FILTERS) { + printf("WARNUNG: MAX_FILTERS überschritten, ignoriere: %s\n", value); + return; + } + + if (strlen(value) >= sizeof(filters.method_filters[0].pattern)) { + printf("WARNUNG: Method pattern too long: %s\n", value); + return; + } + + strcpy(filters.method_filters[filters.method_count].pattern, value); + filters.method_filters[filters.method_count].mode = mode; + filters.method_count++; + + printf("Added method filter: %s%s\n", mode == FILTER_EXCLUDE ? "!" : "", value); +} + +void add_useragent_filter(const char* value, filter_mode_t mode) { + if (filters.user_agent_count >= MAX_FILTERS) { + printf("WARNUNG: MAX_FILTERS überschritten, ignoriere: %s\n", value); + return; + } + + if (strlen(value) >= sizeof(filters.user_agent_filters[0].pattern)) { + printf("WARNUNG: User agent pattern too long: %s\n", value); + return; + } + + strcpy(filters.user_agent_filters[filters.user_agent_count].pattern, value); + filters.user_agent_filters[filters.user_agent_count].mode = mode; + filters.user_agent_count++; + + printf("Added user agent filter: %s%s\n", mode == FILTER_EXCLUDE ? "!" : "", value); +} + +void add_url_filter(const char* value, filter_mode_t mode) { + if (filters.url_count >= MAX_FILTERS) { + printf("WARNUNG: MAX_FILTERS überschritten, ignoriere: %s\n", value); + return; + } + + if (strlen(value) >= sizeof(filters.url_filters[0].pattern)) { + printf("WARNUNG: URL pattern too long: %s\n", value); + return; + } + + strcpy(filters.url_filters[filters.url_count].pattern, value); + filters.url_filters[filters.url_count].mode = mode; + filters.url_count++; + + printf("Added URL filter: %s%s\n", mode == FILTER_EXCLUDE ? "!" : "", value); +} + +// Main filter parsing function +int parse_filter_argument(const char* arg) { + if (!starts_with(arg, "--")) { + return 0; // Not a filter argument + } + + // Find the '=' character + const char* equals_pos = strchr(arg, '='); + if (equals_pos == NULL) { + printf("WARNUNG: Invalid filter format (missing =): %s\n", arg); + return 0; + } + + // Extract filter type (between -- and =) + int type_len = equals_pos - arg - 2; // -2 for the "--" + char filter_type[50]; + strncpy(filter_type, arg + 2, type_len); + filter_type[type_len] = '\0'; + + // Extract values (after =) + const char* values = equals_pos + 1; + + // Parse based on filter type + if (strcmp(filter_type, "status") == 0) { + parse_filter_values(values, add_status_filter); + } else if (strcmp(filter_type, "ip") == 0) { + parse_filter_values(values, add_ip_filter); + } else if (strcmp(filter_type, "method") == 0) { + parse_filter_values(values, add_method_filter); + } else if (strcmp(filter_type, "useragent") == 0) { + parse_filter_values(values, add_useragent_filter); + } else if (strcmp(filter_type, "url") == 0) { + parse_filter_values(values, add_url_filter); + } else if (strcmp(filter_type, "mode") == 0) { + if (strcmp(values, "and") == 0 || strcmp(values, "AND") == 0) { + filters.combination_mode = 0; + printf("Set filter combination mode: AND\n"); + } else if (strcmp(values, "or") == 0 || strcmp(values, "OR") == 0) { + filters.combination_mode = 1; + printf("Set filter combination mode: OR\n"); + } else { + printf("WARNUNG: Invalid mode value: %s (use 'and' or 'or')\n", values); + } + } else { + printf("WARNUNG: Unknown filter type: %s\n", filter_type); + return 0; + } + + return 1; // Successfully parsed +} + +// Add this to your main() function after allocate_initial_memory() +void parse_command_line_filters(int argc, char* argv[]) { + int has_filter_flag = 0; + + // First, check if -f flag is present + for (int i = 2; i < argc; i++) { + if (strcmp(argv[i], "-f") == 0) { + has_filter_flag = 1; + break; + } + } + + if (!has_filter_flag) { + return; // No filter flag, skip filter parsing + } + + printf("Parsing command line filters...\n"); + + // Parse all filter arguments + for (int i = 2; i < argc; i++) { + if (starts_with(argv[i], "--")) { + parse_filter_argument(argv[i]); + } + } + + // Show summary of parsed filters + int total_filters = filters.status_count + filters.method_count + filters.ip_count + + filters.time_count + filters.user_agent_count + filters.url_count; + + if (total_filters > 0) { + printf("Successfully parsed %d filters from command line.\n", total_filters); + } else { + printf("No valid filters found in command line arguments.\n"); + } +} + void print_help(char* binary) { printf("\nNGINX EXAMINATOR\n"); printf("Verwendung:\n"); @@ -2220,16 +2452,48 @@ void print_help(char* binary) { int main(int argc, char* argv[]) { - if (argc <= 3) { + if (argc < 3) { print_help(argv[0]); return 1; } printf("\nNGINX EXAMINATOR\n"); + char* input_path = argv[1]; + + int flag_interactive = 0; + int flag_export = 0; + int flag_filters = 0; + int flag_help = 0; + + char export_filename[90]; + int flag_has_filename = 0; + allocate_initial_memory(); - if (strcmp(argv[2], "-i") == 0) { + if (argc >= 3){ + for (int i=2; i