diff --git a/bin/main b/bin/main index 2d1beeb..43dc4e1 100755 Binary files a/bin/main and b/bin/main differ diff --git a/src/main.c b/src/main.c index 6f226c3..e4be34c 100644 --- a/src/main.c +++ b/src/main.c @@ -17,10 +17,11 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND #include #include // library für Interaktion mit Ordnerstrukturen #include // library für is_directory: Unterscheidung zwischen Dateien und Ordnern +#include // um aktuelle Zeit zu generieren #define MAX_LINE_LENGTH_BUF 2048 #define INITIAL_ENTRIES 1000 // globale Variable zur initialen Speicherallokation in allocate_initial_memory(). Wird falls nötig um GROWTH_FACTOR erweitert -#define GROWTH_FACTOR 2 // wird in mem_expand_dynamically() genutzt, um den Speicher zu vergrößern +#define GROWTH_FACTOR 1.1 // wird in mem_expand_dynamically() genutzt, um den Speicher zu vergrößern #define MAX_FILTERS 100 #define MAX_REQUEST_LENGTH 1024 // das hohe Limit ist erforderlich, da teilweise ausufernde JSON-Requests in nginx auflaufen können. #define TOP_X 20 // definiert die Anzahl der Einträge, die in den Top-Listen angezeigt werden sollen @@ -51,6 +52,8 @@ struct log_entry { struct simple_time time; char referrer[128]; char user_agent[256]; + char source_file[256]; + char parsing_timestamp[32]; }; // Struktur für einen Status-Filtereintrag mit Inhalt & Modus @@ -222,7 +225,7 @@ void mem_expand_dynamically() { int old_max = max_entries; max_entries = max_entries * GROWTH_FACTOR; - printf("Dynamische Speichererweiterung von %d auf %d Einträge um Faktor %d\n", old_max, max_entries, GROWTH_FACTOR); + printf("Dynamische Speichererweiterung von %d auf %d Einträge um Faktor %f\n", old_max, max_entries, GROWTH_FACTOR); struct log_entry *new_ptr = realloc(all_entries, max_entries * sizeof(struct log_entry)); @@ -250,6 +253,20 @@ void allocate_initial_memory() { printf("Speicher erfolgreich alloziert für %d Log-Einträge (%lu Bytes)\n", max_entries, (unsigned long)(max_entries * sizeof(struct log_entry))); } +void get_current_timestamp(char* buffer, int buffer_size) { + time_t raw_time; + struct tm *time_info; + + time(&raw_time); + time_info = localtime(&raw_time); + + if (time_info != NULL) { + strftime(buffer, buffer_size, "%Y-%m-%d %H:%M:%S", time_info); + } else { + snprintf(buffer, buffer_size, "UNKNOWN"); + } +} + // Hilfsfunktion zum Prüfen, ob es sich beim Pfad um ein Directory handelt - für rekursives Parsen int is_directory(char* path) { struct stat path_stat; @@ -293,7 +310,7 @@ Fehleranfällig, wenn das Logformat nicht dem Standard entspricht - das gilt abe '"$http_user_agent" "$http_x_forwarded_for"'; */ // 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);" "-" -int parse_simple_log_line(char* line, int entry_index) { // Nimmt den Pointer auf die Zeile und einen Index entgegen - dieser ist anfangs 0 (globale Variable) und wird pro Eintrag inkrementiert +int parse_simple_log_line(char* line, int entry_index, char* source_file) { // Nimmt den Pointer auf die Zeile und einen Index entgegen - dieser ist anfangs 0 (globale Variable) und wird pro Eintrag inkrementiert char* current_pos = line; // leere Zeichen am Anfang überspringen current_pos = skip_spaces(current_pos); @@ -386,7 +403,6 @@ int parse_simple_log_line(char* line, int entry_index) { // Nimmt den Pointer au current_pos = skip_spaces(current_pos); // Weiter mit dem String innerhalb "", aus dem die HTTP-Methode und der URL-Pfad zu entnehmen ist - // Enhanced parsing to handle malformed binary requests gracefully if (*current_pos == '"') { current_pos++; // 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);" "-" @@ -433,8 +449,8 @@ int parse_simple_log_line(char* line, int entry_index) { // Nimmt den Pointer au } } else { // in NGINX treten gelegentlich fehlerhafte Requests auf, die binäre Daten übersenden, sodass normales parsen nicht möglich ist. - // der entsprechende Eintrag wird daher mit dem String "MALFORMED" repräsentiert - strcpy(all_entries[entry_index].request_method, "MALFORMED"); + // der entsprechende Eintrag wird daher mit dem String "ATYPICAL" repräsentiert + strcpy(all_entries[entry_index].request_method, "ATYPICAL"); // Read entire quoted content into url_path for forensic analysis int i = 0; @@ -452,8 +468,6 @@ int parse_simple_log_line(char* line, int entry_index) { // Nimmt den Pointer au } if (*current_pos == '"') current_pos++; - - printf("INFO: Fehlerhaften Logeintrag entdeckt, speichere mit MALFORMED-Eintrag.\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);" "-" // ^ @@ -512,30 +526,29 @@ int parse_simple_log_line(char* line, int entry_index) { // Nimmt den Pointer au 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"); cleanup_and_exit(); } - return 1; + get_current_timestamp(all_entries[entry_index].parsing_timestamp, sizeof(all_entries[entry_index].parsing_timestamp)); + // Dateinamen in das Feld schreiben - strncpy um Buffer overflow zu verhindern + strncpy(all_entries[entry_index].source_file, source_file, sizeof(all_entries[entry_index].source_file) - 1); + // strncpy setzt keinen Nullterminator, dieser muss am Ende eingefügt werden + all_entries[entry_index].source_file[sizeof(all_entries[entry_index].source_file) - 1] = '\0'; } -// TODO void load_regular_file(char* filename) { FILE* file = fopen(filename, "r"); if (file == NULL) { printf("ERROR: Kann Datei '%s' nicht öffnen!\n", filename); return; } - printf("Lade Datei: %s\n", filename); char line[MAX_LINE_LENGTH_BUF]; int loaded_from_this_file = 0; - while (fgets(line, sizeof(line), file) != NULL) { mem_expand_dynamically(); - - if (parse_simple_log_line(line, total_entries)) { + if (parse_simple_log_line(line, total_entries, filename)) { total_entries++; loaded_from_this_file++; } } - fclose(file); printf(" -> %d Einträge aus dieser Datei geladen.\n", loaded_from_this_file); } @@ -960,276 +973,24 @@ int count_filtered_entries() { return count; } -void show_status() { - printf("\n========== SYSTEM STATUS ==========\n"); - - if (total_entries > 0) { - printf("✅ Log-Daten: %d Einträge geladen\n", total_entries); - printf(" Speicherverbrauch: %lu Bytes (%d Einträge Kapazität)\n", (unsigned long)(max_entries * sizeof(struct log_entry)), max_entries); - } else { - printf("❌ Keine Log-Daten geladen\n"); - } - - printf("\n🔍 Aktive Filter-Logik:\n"); - 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(" Keine Filter gesetzt → alle Einträge werden angezeigt\n"); - } else { - printf(" Modus: %s\n", filters.combination_mode == 0 ? "AND" : "OR"); - printf(" Regel: Ausschlussfilter (!) haben Vorrang, dann Einschlussfilter\n"); - printf("\n Filter-Ausdruck:\n"); - - if (filters.status_count > 0) { - printf(" Status: "); - - int excludes = 0, includes = 0; - for (int i = 0; i < filters.status_count; i++) { - if (filters.status_filters[i].mode == FILTER_EXCLUDE) excludes++; - else includes++; - } - - // Ausschlussfilter (immer im ODER-Modus) - if (excludes > 0) { - printf("!("); - int first = 1; - for (int i = 0; i < filters.status_count; i++) { - if (filters.status_filters[i].mode == FILTER_EXCLUDE) { - if (!first) printf(" OR "); - printf("%d", filters.status_filters[i].code); - first = 0; - } - } - printf(")"); - if (includes > 0) printf(" AND "); - } - - // Einschlussfilter (folgen dem gesetzten Modus) - if (includes > 0) { - if (includes > 1) printf("("); - int first = 1; - for (int i = 0; i < filters.status_count; i++) { - if (filters.status_filters[i].mode == FILTER_INCLUDE) { - if (!first) printf(" %s ", filters.combination_mode == 0 ? "AND" : "OR"); - printf("%d", filters.status_filters[i].code); - first = 0; - } - } - if (includes > 1) printf(")"); - } - printf("\n"); - } - - if (filters.method_count > 0) { - printf(" Method: "); - - int excludes = 0, includes = 0; - for (int i = 0; i < filters.method_count; i++) { - if (filters.method_filters[i].mode == FILTER_EXCLUDE) excludes++; - else includes++; - } - - if (excludes > 0) { - printf("!("); - int first = 1; - for (int i = 0; i < filters.method_count; i++) { - if (filters.method_filters[i].mode == FILTER_EXCLUDE) { - if (!first) printf(" OR "); - printf("%s", filters.method_filters[i].pattern); - first = 0; - } - } - printf(")"); - if (includes > 0) printf(" AND "); - } - - if (includes > 0) { - if (includes > 1) printf("("); - int first = 1; - for (int i = 0; i < filters.method_count; i++) { - if (filters.method_filters[i].mode == FILTER_INCLUDE) { - if (!first) printf(" %s ", filters.combination_mode == 0 ? "AND" : "OR"); - printf("%s", filters.method_filters[i].pattern); - first = 0; - } - } - if (includes > 1) printf(")"); - } - printf("\n"); - } - - if (filters.ip_count > 0) { - printf(" IP: "); - - int excludes = 0, includes = 0; - for (int i = 0; i < filters.ip_count; i++) { - if (filters.ip_filters[i].mode == FILTER_EXCLUDE) excludes++; - else includes++; - } - - if (excludes > 0) { - printf("!("); - int first = 1; - for (int i = 0; i < filters.ip_count; i++) { - if (filters.ip_filters[i].mode == FILTER_EXCLUDE) { - if (!first) printf(" OR "); - printf("%s", filters.ip_filters[i].ip_address); - first = 0; - } - } - printf(")"); - if (includes > 0) printf(" AND "); - } - - if (includes > 0) { - if (includes > 1) printf("("); - int first = 1; - for (int i = 0; i < filters.ip_count; i++) { - if (filters.ip_filters[i].mode == FILTER_INCLUDE) { - if (!first) printf(" %s ", filters.combination_mode == 0 ? "AND" : "OR"); - printf("%s", filters.ip_filters[i].ip_address); - first = 0; - } - } - if (includes > 1) printf(")"); - } - printf("\n"); - } - - if (filters.user_agent_count > 0) { - printf(" UserAgent: "); - - int excludes = 0, includes = 0; - for (int i = 0; i < filters.user_agent_count; i++) { - if (filters.user_agent_filters[i].mode == FILTER_EXCLUDE) excludes++; - else includes++; - } - - if (excludes > 0) { - printf("!("); - int first = 1; - for (int i = 0; i < filters.user_agent_count; i++) { - if (filters.user_agent_filters[i].mode == FILTER_EXCLUDE) { - if (!first) printf(" OR "); - printf("\"%s\"", filters.user_agent_filters[i].pattern); - first = 0; - } - } - printf(")"); - if (includes > 0) printf(" AND "); - } - - if (includes > 0) { - if (includes > 1) printf("("); - int first = 1; - for (int i = 0; i < filters.user_agent_count; i++) { - if (filters.user_agent_filters[i].mode == FILTER_INCLUDE) { - if (!first) printf(" %s ", filters.combination_mode == 0 ? "AND" : "OR"); - printf("\"%s\"", filters.user_agent_filters[i].pattern); - first = 0; - } - } - if (includes > 1) printf(")"); - } - printf("\n"); - } - - if (filters.url_count > 0) { - printf(" URL: "); - - int excludes = 0, includes = 0; - for (int i = 0; i < filters.url_count; i++) { - if (filters.url_filters[i].mode == FILTER_EXCLUDE) excludes++; - else includes++; - } - - if (excludes > 0) { - printf("!("); - int first = 1; - for (int i = 0; i < filters.url_count; i++) { - if (filters.url_filters[i].mode == FILTER_EXCLUDE) { - if (!first) printf(" OR "); - printf("\"%s\"", filters.url_filters[i].pattern); - first = 0; - } - } - printf(")"); - if (includes > 0) printf(" AND "); - } - - if (includes > 0) { - if (includes > 1) printf("("); - int first = 1; - for (int i = 0; i < filters.url_count; i++) { - if (filters.url_filters[i].mode == FILTER_INCLUDE) { - if (!first) printf(" %s ", filters.combination_mode == 0 ? "AND" : "OR"); - printf("\"%s\"", filters.url_filters[i].pattern); - first = 0; - } - } - if (includes > 1) printf(")"); - } - printf("\n"); - } - - if (filters.time_count > 0) { - printf(" Time: %d Filter(s) gesetzt\n", filters.time_count); - } - - // Zeigt aktuellen Modus - int active_types = (filters.status_count > 0) + (filters.method_count > 0 ) + (filters.ip_count > 0) + - (filters.user_agent_count > 0) + (filters.time_count > 0) + (filters.url_count > 0); - if (active_types > 1) { - printf("\n %s-Verknüpfung - Ausschlussfilter vorrangig\n", - filters.combination_mode == 0 ? "UND" : "ODER"); - } - } - - if (total_entries > 0) { - int filtered_count = count_filtered_entries(); - printf("\n📊 Ergebnis: %d von %d Einträgen entsprechen den Filtern\n", filtered_count, total_entries); - } - - printf("===================================\n"); -} - -long long time_to_unix_microseconds(struct simple_time time) { - int days_since_1970 = (time.year - 1970) * 365 + (time.year - 1970) / 4; - - int days_in_months[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; - if (time.month >= 1 && time.month <= 12) { - days_since_1970 += days_in_months[time.month - 1]; - } - - if (time.month > 2 && ((time.year % 4 == 0 && time.year % 100 != 0) || time.year % 400 == 0)) { - days_since_1970 += 1; - } - - days_since_1970 += time.day - 1; - - long long seconds = (long long)days_since_1970 * 24 * 60 * 60; - seconds += time.hour * 60 * 60; - seconds += time.minute * 60; - seconds += time.second; - - return seconds * 1000000LL; -} - +// notwendig, um konformes Timestamp-Format aus simple_time struct zu generieren. Unterstützt derzeit nur UTC void format_iso8601_time(struct simple_time time, char* buffer, int buffer_size) { snprintf(buffer, buffer_size, "%04d-%02d-%02dT%02d:%02d:%02d+00:00", time.year, time.month, time.day, time.hour, time.minute, time.second); } +//Export in Timesketch-kompatiblem Format void export_filtered_entries() { - printf("Dateiname für Timesketch Export eingeben (ohne .csv): "); - char filename[95]; - if (scanf("%94s", filename) != 1) { + printf("Dateiname für Timesketch-Export eingeben (ohne .csv): "); + //90 chars + delimiter + char filename[91]; + if (scanf("%90s", filename) != 1) { printf("FEHLER: Ungültiger Dateiname!\n"); clear_input_buffer(); return; } clear_input_buffer(); - + // Dateiendung strcat(filename, ".csv"); FILE* file = fopen(filename, "w"); @@ -1238,7 +999,8 @@ void export_filtered_entries() { return; } - fprintf(file, "message,datetime,timestamp_desc,timestamp,ip_address,method,url_path,status_code,bytes_sent,user_agent\n"); + // CSV-Kopfzeile für Timesketch-Kompatibilität + fprintf(file, "datetime,timestamp_desc,ip_address,method,url_path,status_code,bytes_sent,user_agent,source_file,parsing_timestamp\n"); int exported_count = 0; char iso_datetime[32]; @@ -1248,27 +1010,17 @@ void export_filtered_entries() { if (passes_filter(i)) { format_iso8601_time(all_entries[i].time, iso_datetime, sizeof(iso_datetime)); - long long unix_timestamp = time_to_unix_microseconds(all_entries[i].time); - snprintf(message_text, sizeof(message_text), - "%s %s %s - Status: %d, Bytes: %d", - all_entries[i].ip_address, - all_entries[i].request_method, - all_entries[i].url_path, - all_entries[i].status_code, - all_entries[i].bytes_sent, - all_entries[i].user_agent); - - fprintf(file, "\"%s\",\"%s\",\"HTTP Access Log\",%lld,\"%s\",\"%s\",\"%s\",%d,%d\n", - message_text, - iso_datetime, - unix_timestamp, - all_entries[i].ip_address, - all_entries[i].request_method, - all_entries[i].url_path, - all_entries[i].status_code, - all_entries[i].bytes_sent, - all_entries[i].user_agent); + fprintf(file, "\"%s\",\"HTTP Access Log\",\"%s\",\"%s\",\"%s\",%d,%d,\"%s\",\"%s\",\"%s\"\n", + iso_datetime, + all_entries[i].ip_address, + all_entries[i].request_method, + all_entries[i].url_path, + all_entries[i].status_code, + all_entries[i].bytes_sent, + all_entries[i].user_agent, + all_entries[i].source_file, + all_entries[i].parsing_timestamp); exported_count++; } @@ -1397,30 +1149,52 @@ void show_top_user_agents() { } } -void show_filtered_entries() { +void show_filtered_entries(int num_shown) { int shown_count = 0; - printf("\n=== GEFILTERTE LOG-EINTRÄGE ===\n"); + printf("\nLOGDATEN:\n"); printf("IP-Adresse | Methode | URL | Status | Bytes | User Agent | Zeit\n"); printf("-----------------|---------|------------------------|--------|-------|--------------------------------------|------------------\n"); - for (int i = 0; i < total_entries; i++) { - if (passes_filter(i)) { - printf("%-16s | %-7s | %-22s | %-6d | %-5d | %-36s | %02d.%02d.%d %02d:%02d:%02d\n", - all_entries[i].ip_address, - all_entries[i].request_method, - all_entries[i].url_path, - all_entries[i].status_code, - all_entries[i].bytes_sent, - all_entries[i].user_agent, - all_entries[i].time.day, - all_entries[i].time.month, - all_entries[i].time.year, - all_entries[i].time.hour, - all_entries[i].time.minute, - all_entries[i].time.second); - - shown_count++; + if (num_shown != 0) { + for (int i = 0; i < num_shown; i++) { + if (passes_filter(i)) { + printf("%-16s | %-7s | %-22s | %-6d | %-5d | %-36s | %02d.%02d.%d %02d:%02d:%02d\n", + all_entries[i].ip_address, + all_entries[i].request_method, + all_entries[i].url_path, + all_entries[i].status_code, + all_entries[i].bytes_sent, + all_entries[i].user_agent, + all_entries[i].time.day, + all_entries[i].time.month, + all_entries[i].time.year, + all_entries[i].time.hour, + all_entries[i].time.minute, + all_entries[i].time.second); + + shown_count++; + } + } + } else { + for (int i = 0; i < total_entries; i++) { + if (passes_filter(i)) { + printf("%-16s | %-7s | %-22s | %-6d | %-5d | %-36s | %02d.%02d.%d %02d:%02d:%02d\n", + all_entries[i].ip_address, + all_entries[i].request_method, + all_entries[i].url_path, + all_entries[i].status_code, + all_entries[i].bytes_sent, + all_entries[i].user_agent, + all_entries[i].time.day, + all_entries[i].time.month, + all_entries[i].time.year, + all_entries[i].time.hour, + all_entries[i].time.minute, + all_entries[i].time.second); + + shown_count++; + } } } @@ -1431,6 +1205,241 @@ void show_filtered_entries() { } } +// Statusanzeige +void show_status() { + printf("PREVIEW:\n"); + show_filtered_entries(10); + printf("\nSTATUS\n"); + + if (total_entries > 0) { + 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("\n Aktive Filter:\n"); + 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(" -> keine Filter gesetzt\n"); + } else { + printf(" Modus: %s\n", filters.combination_mode == 0 ? "AND" : "OR"); + printf(" Ausschlussfilter (!) haben Vorrang, dann Einschlussfilter\n"); + printf("\n Gesetzt:\n"); + + if (filters.status_count > 0) { + printf(" -> Status: "); + + int excludes = 0, includes = 0; + for (int i = 0; i < filters.status_count; i++) { + if (filters.status_filters[i].mode == FILTER_EXCLUDE) excludes++; + else includes++; + } + + // Ausschlussfilter (immer im ODER-Modus) + if (excludes > 0) { + printf("!("); + int first = 1; + for (int i = 0; i < filters.status_count; i++) { + if (filters.status_filters[i].mode == FILTER_EXCLUDE) { + if (!first) printf(" OR "); + printf("%d", filters.status_filters[i].code); + first = 0; + } + } + printf(")"); + if (includes > 0) printf(" AND "); + } + + // Einschlussfilter (folgen dem gesetzten Modus) + if (includes > 0) { + if (includes > 1) printf("("); + int first = 1; + for (int i = 0; i < filters.status_count; i++) { + if (filters.status_filters[i].mode == FILTER_INCLUDE) { + if (!first) printf(" %s ", filters.combination_mode == 0 ? "AND" : "OR"); + printf("%d", filters.status_filters[i].code); + first = 0; + } + } + if (includes > 1) printf(")"); + } + printf("\n"); + } + + if (filters.method_count > 0) { + printf("Request-Method: "); + + int excludes = 0, includes = 0; + for (int i = 0; i < filters.method_count; i++) { + if (filters.method_filters[i].mode == FILTER_EXCLUDE) excludes++; + else includes++; + } + + if (excludes > 0) { + printf("!("); + int first = 1; + for (int i = 0; i < filters.method_count; i++) { + if (filters.method_filters[i].mode == FILTER_EXCLUDE) { + if (!first) printf(" OR "); + printf("%s", filters.method_filters[i].pattern); + first = 0; + } + } + printf(")"); + if (includes > 0) printf(" AND "); + } + + if (includes > 0) { + if (includes > 1) printf("("); + int first = 1; + for (int i = 0; i < filters.method_count; i++) { + if (filters.method_filters[i].mode == FILTER_INCLUDE) { + if (!first) printf(" %s ", filters.combination_mode == 0 ? "AND" : "OR"); + printf("%s", filters.method_filters[i].pattern); + first = 0; + } + } + if (includes > 1) printf(")"); + } + printf("\n"); + } + + if (filters.ip_count > 0) { + printf("IP-Adresse: "); + + int excludes = 0, includes = 0; + for (int i = 0; i < filters.ip_count; i++) { + if (filters.ip_filters[i].mode == FILTER_EXCLUDE) excludes++; + else includes++; + } + + if (excludes > 0) { + printf("!("); + int first = 1; + for (int i = 0; i < filters.ip_count; i++) { + if (filters.ip_filters[i].mode == FILTER_EXCLUDE) { + if (!first) printf(" OR "); + printf("%s", filters.ip_filters[i].ip_address); + first = 0; + } + } + printf(")"); + if (includes > 0) printf(" AND "); + } + + if (includes > 0) { + if (includes > 1) printf("("); + int first = 1; + for (int i = 0; i < filters.ip_count; i++) { + if (filters.ip_filters[i].mode == FILTER_INCLUDE) { + if (!first) printf(" %s ", filters.combination_mode == 0 ? "AND" : "OR"); + printf("%s", filters.ip_filters[i].ip_address); + first = 0; + } + } + if (includes > 1) printf(")"); + } + printf("\n"); + } + + if (filters.user_agent_count > 0) { + printf("UserAgent: "); + + int excludes = 0, includes = 0; + for (int i = 0; i < filters.user_agent_count; i++) { + if (filters.user_agent_filters[i].mode == FILTER_EXCLUDE) excludes++; + else includes++; + } + + if (excludes > 0) { + printf("!("); + int first = 1; + for (int i = 0; i < filters.user_agent_count; i++) { + if (filters.user_agent_filters[i].mode == FILTER_EXCLUDE) { + if (!first) printf(" OR "); + printf("\"%s\"", filters.user_agent_filters[i].pattern); + first = 0; + } + } + printf(")"); + if (includes > 0) printf(" AND "); + } + + if (includes > 0) { + if (includes > 1) printf("("); + int first = 1; + for (int i = 0; i < filters.user_agent_count; i++) { + if (filters.user_agent_filters[i].mode == FILTER_INCLUDE) { + if (!first) printf(" %s ", filters.combination_mode == 0 ? "AND" : "OR"); + printf("\"%s\"", filters.user_agent_filters[i].pattern); + first = 0; + } + } + if (includes > 1) printf(")"); + } + printf("\n"); + } + + if (filters.url_count > 0) { + printf("Payload/Pfad: "); + + int excludes = 0, includes = 0; + for (int i = 0; i < filters.url_count; i++) { + if (filters.url_filters[i].mode == FILTER_EXCLUDE) excludes++; + else includes++; + } + + if (excludes > 0) { + printf("!("); + int first = 1; + for (int i = 0; i < filters.url_count; i++) { + if (filters.url_filters[i].mode == FILTER_EXCLUDE) { + if (!first) printf(" OR "); + printf("\"%s\"", filters.url_filters[i].pattern); + first = 0; + } + } + printf(")"); + if (includes > 0) printf(" AND "); + } + + if (includes > 0) { + if (includes > 1) printf("("); + int first = 1; + for (int i = 0; i < filters.url_count; i++) { + if (filters.url_filters[i].mode == FILTER_INCLUDE) { + if (!first) printf(" %s ", filters.combination_mode == 0 ? "AND" : "OR"); + printf("\"%s\"", filters.url_filters[i].pattern); + first = 0; + } + } + if (includes > 1) printf(")"); + } + printf("\n"); + } + + if (filters.time_count > 0) { + printf("Zeitraum: %d Filter gesetzt\n", filters.time_count); + } + + // Zeigt aktuellen Modus + int active_types = (filters.status_count > 0) + (filters.method_count > 0 ) + (filters.ip_count > 0) + + (filters.user_agent_count > 0) + (filters.time_count > 0) + (filters.url_count > 0); + if (active_types > 1) { + printf("\n%s-Verknüpfung (nur Einschlussfilter)\n", + filters.combination_mode == 0 ? "UND" : "ODER"); + } + } + + if (total_entries > 0) { + int filtered_count = count_filtered_entries(); + printf("\n ERGEBNIS: \n %d von %d Einträgen entsprechen den Filtern\n", filtered_count, total_entries); + } + +} + void show_statistics() { int count_200 = 0; int count_404 = 0; @@ -1438,7 +1447,7 @@ void show_statistics() { int total_bytes = 0; int filtered_count = 0; - printf("\n=== STATISTIKEN ===\n"); + printf("\nSTATISTIKEN\n"); for (int i = 0; i < total_entries; i++) { if (passes_filter(i)) { @@ -1451,66 +1460,66 @@ void show_statistics() { } } - printf("Gefilterte Einträge: %d von %d\n", filtered_count, total_entries); - printf("Erfolgreiche Anfragen (200): %d\n", count_200); - printf("Nicht gefunden (404): %d\n", count_404); - printf("Server-Fehler (5xx): %d\n", count_500); - printf("Gesamte übertragene Bytes: %d\n", total_bytes); + printf(" Gefilterte Einträge: %d von %d\n", filtered_count, total_entries); + printf(" Erfolgreiche Anfragen (200): %d\n", count_200); + printf(" Nicht gefunden (404): %d\n", count_404); + printf(" Server-Fehler (5xx): %d\n", count_500); + printf(" Gesamte übertragene Bytes: %d\n", total_bytes); if (filtered_count > 0) { - printf("Durchschnittliche Bytes pro Anfrage: %d\n", total_bytes / filtered_count); + printf(" Durchschnittliche Bytes pro Anfrage: %d\n", total_bytes / filtered_count); } } void print_filter_examples() { printf("\nFILTER-DOKUMENTATION\n"); - printf("\n🔴 EXKLUSIONS-FILTER (immer OR-Logik, unabhängig vom Modus):\n"); + printf("\nEXKLUSIONS-FILTER (immer OR-Logik, unabhängig vom Modus):\n"); printf("Beispiel: !('uptime' OR 'scanner')\n"); - printf("→ Schließt ALLE Einträge aus, die 'uptime' ODER 'scanner' enthalten\n\n"); + printf("> Schließt ALLE Einträge aus, die 'uptime' ODER 'scanner' enthalten\n\n"); - printf("🟢 INKLUSIONS-FILTER im AND-Modus:\n"); + printf("INKLUSIONS-FILTER im AND-Modus:\n"); printf("Beispiel: ('bot' AND 'crawl')\n"); - printf("→ Zeigt nur Einträge mit BEIDEN Begriffen\n\n"); + printf("> Zeigt nur Einträge mit BEIDEN Begriffen\n\n"); - printf("🟡 INKLUSIONS-FILTER im OR-Modus:\n"); + printf("INKLUSIONS-FILTER im OR-Modus:\n"); printf("Beispiel: ('bot' OR 'crawl')\n"); - printf("→ Zeigt Einträge mit 'bot' ODER 'crawl'\n\n"); + printf("> Zeigt Einträge mit 'bot' ODER 'crawl'\n\n"); - printf("⚡ KOMBINATION: Exklusion + Inklusion:\n"); + printf("KOMBINATION: Exklusion + Inklusion:\n"); printf("AND-Modus: !('uptime') AND ('bot' AND 'crawl')\n"); printf("OR-Modus: !('uptime') AND ('bot' OR 'crawl')\n\n"); - printf("🎯 PRAKTISCHE ANWENDUNGSFÄLLE:\n"); + printf("PRAKTISCHE ANWENDUNGSFÄLLE:\n"); printf("Malware-Erkennung:\n"); printf(" '.git' OR '.env' OR '/admin'\n"); - printf(" → Verdächtige Pfad-Zugriffe\n\n"); + printf(" > Verdächtige Pfad-Zugriffe\n\n"); printf("Bot-Traffic bereinigen:\n"); printf(" !('bot') AND Status=200 AND Method='GET'\n"); - printf(" → Nur menschliche, erfolgreiche GET-Anfragen\n\n"); + printf(" > Nur menschliche, erfolgreiche GET-Anfragen\n\n"); printf("Zeitraum-Analyse:\n"); printf(" Time='08:00-18:00' AND Status=500\n"); - printf(" → Server-Fehler nur während Geschäftszeiten\n\n"); + printf(" > Server-Fehler nur während Geschäftszeiten\n\n"); printf("DDoS-Verdacht:\n"); printf(" Status=429 OR Status=503 OR Status=500\n"); - printf(" → Alle Überlastungs- und Fehler-Codes\n"); + printf(" > Alle Überlastungs- und Fehler-Codes\n"); } void menu_set_filters() { int choice = 0; while (choice != 7) { show_status(); - printf("\n=== FILTER SETZEN ===\n"); - printf("1. Status-Code Filter hinzufügen (exakte Suche)\n"); - printf("2. IP-Adresse Filter hinzufügen (exakte Suche)\n"); - printf("3. Zeitraum Filter hinzufügen (interaktiv)\n"); - printf("4. User-Agent-Filter setzen (Freitext)\n"); - printf("5. HTTP-Methode Filter hinzufügen (Freitext)\n"); - printf("6. URL-Pfad Filter hinzufügen (Freitext)\n"); - printf("7. Zurück zum Filter-Menü\n"); + printf("\nFILTER SETZEN\n"); + printf("1. Status-Code (exakte Suche)\n"); + printf("2. IP-Adresse (exakte Suche)\n"); + printf("3. Zeitraum (interaktiv)\n"); + printf("4. User-Agent (Freitext)\n"); + printf("5. HTTP-Methode (Freitext)\n"); + printf("6. URL-Pfad (Freitext)\n"); + printf("7. Zurück\n"); printf("Auswahl: "); choice = read_safe_integer(); @@ -1536,7 +1545,7 @@ void menu_set_filters() { filters.status_filters[filters.status_count].code = status; filters.status_filters[filters.status_count].mode = (filter_type == 2) ? FILTER_EXCLUDE : FILTER_INCLUDE; filters.status_count++; - printf("✅ Status-Code Filter hinzugefügt. Total: %d\n", filters.status_count); + printf(">Status-Code Filter hinzugefügt. Total: %d\n", filters.status_count); } else { printf("FEHLER: Ungültiger Filter-Typ!\n"); } @@ -1561,7 +1570,7 @@ void menu_set_filters() { strcpy(filters.ip_filters[filters.ip_count].ip_address, ip); filters.ip_filters[filters.ip_count].mode = (filter_type == 2) ? FILTER_EXCLUDE : FILTER_INCLUDE; filters.ip_count++; - printf("✅ IP-Filter hinzugefügt. Total: %d\n", filters.ip_count); + printf(">IP-Filter hinzugefügt. Total: %d\n", filters.ip_count); } else { printf("FEHLER: Ungültiger Filter-Typ!\n"); } @@ -1679,7 +1688,7 @@ void menu_set_filters() { filters.time_filters[filters.time_count] = new_time_filter; filters.time_count++; - printf("✅ Zeitraum-Filter hinzugefügt. Total: %d\n", filters.time_count); + printf(">Zeitraum-Filter hinzugefügt. Total: %d\n", filters.time_count); } else if (choice == 4) { if (filters.user_agent_count >= MAX_FILTERS) { @@ -1700,7 +1709,7 @@ void menu_set_filters() { strcpy(filters.user_agent_filters[filters.user_agent_count].pattern, pattern); filters.user_agent_filters[filters.user_agent_count].mode = (filter_type == 2) ? FILTER_EXCLUDE : FILTER_INCLUDE; filters.user_agent_count++; - printf("✅ User-Agent Filter hinzugefügt. Total: %d\n", filters.user_agent_count); + printf(">User-Agent Filter hinzugefügt. Total: %d\n", filters.user_agent_count); } else { printf("FEHLER: Ungültiger Filter-Typ!\n"); } @@ -1715,7 +1724,7 @@ void menu_set_filters() { continue; } - printf("HTTP-Methode eingeben (z.B. 'GET', 'POST', 'PUT', ... Sonderwert 'MALFORMED'): "); + printf("HTTP-Methode eingeben (z.B. 'GET', 'POST', 'PUT', ... Sonderwert 'ATYPICAL'): "); char pattern[10]; if (scanf("%9s", pattern) == 1) { printf("Filter-Typ wählen:\n"); @@ -1728,7 +1737,7 @@ void menu_set_filters() { strcpy(filters.method_filters[filters.method_count].pattern, pattern); filters.method_filters[filters.method_count].mode = (filter_type == 2) ? FILTER_EXCLUDE : FILTER_INCLUDE; filters.method_count++; - printf("✅ Method-Filter hinzugefügt. Total: %d\n", filters.method_count); + printf(">Method-Filter hinzugefügt. Total: %d\n", filters.method_count); } else { printf("FEHLER: Ungültiger Filter-Typ!\n"); } @@ -1756,7 +1765,7 @@ void menu_set_filters() { strcpy(filters.url_filters[filters.url_count].pattern, pattern); filters.url_filters[filters.url_count].mode = (filter_type == 2) ? FILTER_EXCLUDE : FILTER_INCLUDE; filters.url_count++; - printf("✅ URL-Filter hinzugefügt. Total: %d\n", filters.url_count); + printf(">URL-Filter hinzugefügt. Total: %d\n", filters.url_count); } else { printf("FEHLER: Ungültiger Filter-Typ!\n"); } @@ -1781,7 +1790,7 @@ void menu_delete_filters() { return; } - printf("\n=== FILTER LÖSCHEN ===\n"); + printf("\nFILTER LÖSCHEN\n"); printf("Aktuell gesetzte Filter:\n"); int filter_index = 1; @@ -1830,7 +1839,7 @@ void menu_delete_filters() { mode_str); } - printf("Welchen Filter möchten Sie löschen (1-%d) oder 0 für Abbrechen: ", total_filters); + printf("Auswahl: (1-%d) oder 0 für Abbrechen: ", total_filters); int choice = read_safe_integer(); if (choice == 0) { @@ -1850,7 +1859,7 @@ void menu_delete_filters() { filters.status_filters[j] = filters.status_filters[j + 1]; } filters.status_count--; - printf("✅ Status-Code Filter gelöscht.\n"); + printf(">Status-Code Filter gelöscht.\n"); return; } current_index++; @@ -1862,7 +1871,7 @@ void menu_delete_filters() { filters.method_filters[j] = filters.method_filters[j + 1]; } filters.method_count--; - printf("✅ Method-Filter gelöscht.\n"); + printf(">Method-Filter gelöscht.\n"); return; } current_index++; @@ -1874,7 +1883,7 @@ void menu_delete_filters() { filters.ip_filters[j] = filters.ip_filters[j + 1]; } filters.ip_count--; - printf("✅ IP-Filter gelöscht.\n"); + printf(">IP-Filter gelöscht.\n"); return; } current_index++; @@ -1886,7 +1895,7 @@ void menu_delete_filters() { filters.user_agent_filters[j] = filters.user_agent_filters[j + 1]; } filters.user_agent_count--; - printf("✅ User-Agent Filter gelöscht.\n"); + printf(">User-Agent Filter gelöscht.\n"); return; } current_index++; @@ -1898,7 +1907,7 @@ void menu_delete_filters() { filters.url_filters[j] = filters.url_filters[j + 1]; } filters.url_count--; - printf("✅ URL-Filter gelöscht.\n"); + printf(">URL-Filter gelöscht.\n"); return; } current_index++; @@ -1910,7 +1919,7 @@ void menu_delete_filters() { filters.time_filters[j] = filters.time_filters[j + 1]; } filters.time_count--; - printf("✅ Zeitraum-Filter gelöscht.\n"); + printf(">Zeitraum-Filter gelöscht.\n"); return; } current_index++; @@ -1918,7 +1927,7 @@ void menu_delete_filters() { } void menu_filter_mode() { - printf("=== FILTER-MODUS ===\n"); + printf("FILTER-MODUS\n"); printf("Aktueller Modus: %s\n", filters.combination_mode == 0 ? "AND" : "OR"); printf("\nACHTUNG:\n"); printf("Der Modus wirkt sich nur auf inklusive Filter aus.\n"); @@ -1932,10 +1941,10 @@ void menu_filter_mode() { if (choice == 1) { filters.combination_mode = 0; - printf("✅ Filter-Modus auf AND gesetzt.\n"); + printf(">Filter-Modus auf AND gesetzt.\n"); } else if (choice == 2) { filters.combination_mode = 1; - printf("✅ Filter-Modus auf OR gesetzt.\n"); + printf(">Filter-Modus auf OR gesetzt.\n"); } else if (choice == 3) { print_filter_examples(); } else if (choice != -1) { @@ -1952,7 +1961,7 @@ void menu_reset_filters() { filters.url_count = 0; filters.combination_mode = 0; - printf("✅ Alle Filter zurückgesetzt.\n"); + printf(">Alle Filter zurückgesetzt.\n"); } void menu_filter_management() { @@ -2014,7 +2023,7 @@ void menu_show_entries() { continue; } } - show_filtered_entries(); + show_filtered_entries(0); } else if (choice == 2) { export_filtered_entries(); } else if (choice == 3) { @@ -2051,7 +2060,7 @@ int main(int argc, char* argv[]) { return 1; } - printf("=== NGINX LOG PARSER - VEREINFACHTE VERSION ===\n"); + printf("NGINX EXAMINATOR\n"); allocate_initial_memory(); load_log_file(argv[1]); @@ -2080,7 +2089,7 @@ int main(int argc, char* argv[]) { } else if (choice == 3) { show_statistics(); } else if (choice == 4) { - printf("Auf Wiedersehen!\n"); + printf("Programmende\n"); } else { printf("Ungültige Auswahl! Bitte wählen Sie 1-4.\n"); }