diff --git a/src/main.c b/src/main.c index e52a647..1e432ff 100644 --- a/src/main.c +++ b/src/main.c @@ -30,9 +30,10 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND #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 8192 // das hohe Limit ist erforderlich, da teilweise ausufernde JSON-Requests in nginx auflaufen können. +#define MAX_PREVIEW 10000 // struct für die Darstellung von Timestamps. Die granulare Trennung in verschiedene int-Werte macht die spätere Verarbeitung modular anpassbar/erweiterbar und erleichtert die Verarbeitung. -struct simple_time { +struct simple_time_t { int day; int month; int year; @@ -42,13 +43,13 @@ struct simple_time { }; // Struktur für die Darstellung eines Standard-NGINX-Logeintrags. -struct log_entry { +struct log_entry_t { char ip_address[50]; // ausreichende Längenbegrenzung für IP-Adressen. Könnte theoretisch auch ipv6 (ungetestet) char request_method[10]; // GET, POST, PUT, DELETE, PROPFIND ... char url_path[MAX_REQUEST_LENGTH]; // Pfade können lang werden, insbesodere bei base64-Strings wie oft in Malware verwendet int status_code; int bytes_sent; - struct simple_time time; + struct simple_time_t time; char referrer[128]; char user_agent[256]; char source_file[256]; @@ -56,84 +57,84 @@ struct log_entry { }; // Struktur für einen Status-Filtereintrag mit Inhalt & Modus -struct status_filter { +struct status_filter_t { int code; int filter_exclude_flag; }; -struct method_filter { +struct method_filter_t { char pattern[10]; int filter_exclude_flag; }; // für IP-Adressen -struct ip_filter { +struct ip_filter_t { char ip_address[50]; int filter_exclude_flag; }; // Filter für User-Agent -struct user_agent_filter { +struct user_agent_filter_t { char pattern[256]; int filter_exclude_flag; }; // Filter für URL-Pfad/Request -struct url_filter { +struct url_filter_t { char pattern[MAX_REQUEST_LENGTH]; int filter_exclude_flag; }; // Struktur zum erhalten aller Filtereinträge, kann im Dialogbetrieb bearbeitet werden. Mit Zähler. -struct filter_system { - struct status_filter status_filters[MAX_FILTERS]; +struct filter_system_t { + struct status_filter_t status_filters[MAX_FILTERS]; int status_count; - struct method_filter method_filters[MAX_FILTERS]; + struct method_filter_t method_filters[MAX_FILTERS]; int method_count; - struct ip_filter ip_filters[MAX_FILTERS]; + struct ip_filter_t ip_filters[MAX_FILTERS]; int ip_count; - struct user_agent_filter user_agent_filters[MAX_FILTERS]; + struct user_agent_filter_t user_agent_filters[MAX_FILTERS]; int user_agent_count; - struct url_filter url_filters[MAX_FILTERS]; + struct url_filter_t url_filters[MAX_FILTERS]; int url_count; }; // Initialisierung eines Arrays für die Logeinträge und weiterer Startvariablen -struct log_entry *all_entries = NULL; +struct log_entry_t *all_entries = NULL; int max_entries = 0; int total_entries = 0; -struct filter_system filters = {0}; +struct filter_system_t filters = {0}; // für -v option int flag_verbose = 0; // Hilfsfunktion für die Erkennung von Leerzeichen -int is_space(char c) { +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) { +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)) { +char* skip_spaces(char* str){ + while (is_space(*str)){ str++; } return str; } // Kopiert einen Eingabestring von einem Quellbereich zu einem Zielbereich, bis ein Leerzeichen (oder Nullterminator) erreicht wird oder die max. Zeilenlänge nicht überschritten wird -void copy_until_space(char* destination, char* source, int max_length) { +void copy_until_space(char* destination, char* source, int max_length){ int i = 0; - while (source[i] != ' ' && source[i] != '\0' && i < max_length - 1) { + while (source[i] != ' ' && source[i] != '\0' && i < max_length - 1){ destination[i] = source[i]; i++; } @@ -141,7 +142,7 @@ void copy_until_space(char* destination, char* source, int max_length) { } // NGINX speichert Timestamps mit Monatskürzel. Umwandlung in Zahlen für maschinelle Verarbeitung. -int month_name_to_number(char* month_name) { +int month_name_to_number(char* month_name){ if (strncmp(month_name, "Jan", 3) == 0) return 1; if (strncmp(month_name, "Feb", 3) == 0) return 2; if (strncmp(month_name, "Mar", 3) == 0) return 3; @@ -159,8 +160,8 @@ int month_name_to_number(char* month_name) { // Speicher freigeben und mit 0 überschreiben (Prävention von use-after-free-Schwachstelle) void cleanup_memory(){ - if (all_entries != NULL) { - printf("\nDEBUG: %lu Bytes Speicher werden freigegeben\n", (unsigned long)(max_entries * sizeof(struct log_entry))); + if (all_entries != NULL){ + printf("\nDEBUG: %lu Bytes Speicher werden freigegeben\n", (unsigned long)(max_entries * sizeof(struct log_entry_t))); free(all_entries); all_entries = NULL; } @@ -171,7 +172,7 @@ void cleanup_memory(){ // Standardfunktion zum Leeren des Input Buffers void clear_input_buffer(){ int c; - while ((c = getchar()) != '\n' && c != EOF) { + while ((c = getchar()) != '\n' && c != EOF){ } } @@ -185,65 +186,67 @@ void cleanup_and_exit(){ // Erweiterung des Speichers für dynamische Speicherallokation void mem_expand_dynamically(){ // total_entries werden beim parsen am Anfang gezählt (load_regular_file()), max_entries werden initial festgelegt - if (total_entries >= max_entries) { + if (total_entries >= max_entries){ int old_max = max_entries; max_entries = max_entries * GROWTH_FACTOR; if (flag_verbose) printf("DEBUG: 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)); + struct log_entry_t *new_ptr = realloc(all_entries, max_entries * sizeof(struct log_entry_t)); - if (new_ptr == NULL) { + 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("ERROR: Benötigter Speicher: %lu Bytes\n", (unsigned long)(max_entries * sizeof(struct log_entry_t))); cleanup_and_exit(); } all_entries = new_ptr; - if (flag_verbose) printf("DEBUG: Speicher erfolgreich erweitert auf %lu Bytes\n", (unsigned long)(max_entries * sizeof(struct log_entry))); + if (flag_verbose) printf("DEBUG: Speicher erfolgreich erweitert auf %lu Bytes\n", (unsigned long)(max_entries * sizeof(struct log_entry_t))); } } void allocate_initial_memory(){ max_entries = INITIAL_ENTRIES; // Startwert 1000, globale Variable - all_entries = malloc(max_entries * sizeof(struct log_entry)); + all_entries = malloc(max_entries * sizeof(struct log_entry_t)); - if (all_entries == NULL) { + 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("ERROR: %lu Bytes\n", (unsigned long)(max_entries * sizeof(struct log_entry_t))); exit(1); // cleanup_and_exit() nicht nötig, da der Speicherbereich nicht beschrieben wurde - use-after-free unproblematisch } - if (flag_verbose) printf("DEBUG: Speicher erfolgreich alloziert für %d Log-Einträge (%lu Bytes)\n", max_entries, (unsigned long)(max_entries * sizeof(struct log_entry))); + if (flag_verbose) printf("DEBUG: Speicher erfolgreich alloziert für %d Log-Einträge (%lu Bytes)\n", max_entries, (unsigned long)(max_entries * sizeof(struct log_entry_t))); } -void get_current_timestamp(char* buffer, int buffer_size) { +// aktuelle Timestamp erfassen +void get_current_timestamp(char* buffer){ time_t raw_time; struct tm *time_info; - + int timestamp_buffer_size =32; time(&raw_time); time_info = localtime(&raw_time); - if (time_info != NULL) { - strftime(buffer, buffer_size, "%Y-%m-%d %H:%M:%S", time_info); + if (time_info != NULL){ + strftime(buffer, timestamp_buffer_size, "%Y-%m-%d %H:%M:%S", time_info); } else { - snprintf(buffer, buffer_size, "UNKNOWN"); + sprintf(buffer, "UNKNOWN"); } } // Hilfsfunktion zum Prüfen, ob es sich beim Pfad um ein Directory handelt - für rekursives Parsen -int is_directory(char* path) { +// https://stackoverflow.com/questions/4553012/checking-if-a-file-is-a-directory-or-just-a-file +int is_directory(char* path){ struct stat path_stat; - if (stat(path, &path_stat) != 0) { + if (stat(path, &path_stat) != 0){ return 0; } return S_ISDIR(path_stat.st_mode); } // Hilfsfunktion zum prüfen, ob es sich um eine plausible nginx-Logdatei handelt (Metrik: Dateiname - BESSER: Regex oder Magic Bytes?) -int is_log_file(char* filename) { +int is_log_file(char* filename){ // versteckte Dateien sowie . oder .. überspringen - if (filename[0] == '.') { + if (filename[0] == '.'){ return 0; } @@ -258,8 +261,8 @@ int is_log_file(char* filename) { }; // Vergleich der vorhandenen Dateinamen mit den Suchmustern - for (int i = 0; log_patterns[i] != NULL; i++) { - if (strstr(filename, log_patterns[i]) != NULL) { + for (int i = 0; log_patterns[i] != NULL; i++){ + if (strstr(filename, log_patterns[i]) != NULL){ if ((strstr(filename, "error")!= NULL)||(strstr(filename, ".gz")!= NULL)) continue; return 1; } @@ -269,15 +272,15 @@ int is_log_file(char* filename) { } // Funktion zum suchen eines Suchbegriffs innerhalb eines Strings (lowercase) -int search_in_string(char* raw_string, char* search_string) { +int search_in_string(char* raw_string, char* search_string){ char raw_string_lower[512]; // Puffer zum Speichern des zu durchsuchenden Strings char search_string_lower[256]; // Puffer zum Speichern des Suchbegriffs // Konvertierung des Datensatzes zu Kleinbuchstaben int i = 0; // für jeden Buchstaben innerhalb des Datensatzes Verschiebung innerhalb des ASCII-Alphabets um 32 - while (raw_string[i] && i < 511) { - if (raw_string[i] >= 'A' && raw_string[i] <= 'Z') { + while (raw_string[i] && i < 511){ + if (raw_string[i] >= 'A' && raw_string[i] <= 'Z'){ raw_string_lower[i] = raw_string[i] + 32; // Verschiebung im ASCII-Alphabet } else { raw_string_lower[i] = raw_string[i]; // alles was kein Buchstabe ist, wird beibehalten @@ -288,8 +291,8 @@ int search_in_string(char* raw_string, char* search_string) { // gleiche Methode mit dem Suchbegriff i = 0; - while (search_string[i] && i < 255) { - if (search_string[i] >= 'A' && search_string[i] <= 'Z') { + while (search_string[i] && i < 255){ + if (search_string[i] >= 'A' && search_string[i] <= 'Z'){ search_string_lower[i] = search_string[i] + 32; // Verschiebung im ASCII-Alphabet } else { search_string_lower[i] = search_string[i]; // nicht-Buchstaben beibehalten @@ -302,7 +305,7 @@ int search_in_string(char* raw_string, char* search_string) { char* result = strstr(raw_string_lower, search_string_lower); // Einfache Rückgabe: 1 wenn gefunden, 0 wenn nicht gefunden - if (result != NULL) { + if (result != NULL){ return 1; } else { return 0; @@ -321,7 +324,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, char* source_file) { // 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); @@ -349,13 +352,13 @@ 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);" "-" // ^ // Timestamp-Parsing - if (*current_pos == '[') { + 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);" "-" // ^ // es folgt nach und nach das Einlesen von Datum und Uhrzeit, und wiederholtes Verschieben des Pointers all_entries[entry_index].time.day = 0; - while (*current_pos >= '0' && *current_pos <= '9') {all_entries[entry_index].time.day = all_entries[entry_index].time.day * 10 + (*current_pos - '0'); + while (*current_pos >= '0' && *current_pos <= '9'){all_entries[entry_index].time.day = all_entries[entry_index].time.day * 10 + (*current_pos - '0'); current_pos++; } @@ -363,7 +366,7 @@ int parse_simple_log_line(char* line, int entry_index, char* source_file) { // N char month_str[4] = {0}; int month_pos = 0; - while (*current_pos != '/' && *current_pos != '\0' && month_pos < 3) {month_str[month_pos] = *current_pos; + while (*current_pos != '/' && *current_pos != '\0' && month_pos < 3){month_str[month_pos] = *current_pos; month_pos++; current_pos++; } @@ -372,7 +375,7 @@ int parse_simple_log_line(char* line, int entry_index, char* source_file) { // N if (*current_pos == '/') current_pos++; all_entries[entry_index].time.year = 0; - while (*current_pos >= '0' && *current_pos <= '9') { + while (*current_pos >= '0' && *current_pos <= '9'){ all_entries[entry_index].time.year = all_entries[entry_index].time.year * 10 + (*current_pos - '0'); current_pos++; } @@ -380,7 +383,7 @@ int parse_simple_log_line(char* line, int entry_index, char* source_file) { // N if (*current_pos == ':') current_pos++; all_entries[entry_index].time.hour = 0; - while (*current_pos >= '0' && *current_pos <= '9') { + while (*current_pos >= '0' && *current_pos <= '9'){ all_entries[entry_index].time.hour = all_entries[entry_index].time.hour * 10 + (*current_pos - '0'); current_pos++; } @@ -388,7 +391,7 @@ int parse_simple_log_line(char* line, int entry_index, char* source_file) { // N if (*current_pos == ':') current_pos++; all_entries[entry_index].time.minute = 0; - while (*current_pos >= '0' && *current_pos <= '9') { + while (*current_pos >= '0' && *current_pos <= '9'){ all_entries[entry_index].time.minute = all_entries[entry_index].time.minute * 10 + (*current_pos - '0'); current_pos++; } @@ -396,7 +399,7 @@ int parse_simple_log_line(char* line, int entry_index, char* source_file) { // N if (*current_pos == ':') current_pos++; all_entries[entry_index].time.second = 0; - while (*current_pos >= '0' && *current_pos <= '9') { + while (*current_pos >= '0' && *current_pos <= '9'){ all_entries[entry_index].time.second = all_entries[entry_index].time.second * 10 + (*current_pos - '0'); current_pos++; } @@ -412,7 +415,7 @@ int parse_simple_log_line(char* line, int entry_index, char* source_file) { // N current_pos = skip_spaces(current_pos); // Weiter mit dem String innerhalb "", aus dem die HTTP-Methode und der URL-Pfad zu entnehmen ist - if (*current_pos == '"') { + 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);" "-" // ^ @@ -423,23 +426,23 @@ int parse_simple_log_line(char* line, int entry_index, char* source_file) { // N // Längenprüfung des Methodenstrings int is_valid_method = 1; - if (strlen(temp_method) == 0 || strlen(temp_method) > 10) { + if (strlen(temp_method) == 0 || strlen(temp_method) > 10){ is_valid_method = 0; } else { // prüfen, ob die Methode nur ASCII-Zeichen enthält - for (int i = 0; temp_method[i] != '\0'; i++) { + for (int i = 0; temp_method[i] != '\0'; i++){ if (!((temp_method[i] >= 'A' && temp_method[i] <= 'Z') || - (temp_method[i] >= 'a' && temp_method[i] <= 'z'))) { + (temp_method[i] >= 'a' && temp_method[i] <= 'z'))){ is_valid_method = 0; break; } } } - if (is_valid_method) { + if (is_valid_method){ // Normal parsen: HTTP-Methode bis zum nächsten Leerzeichen einlesen und speichern - strcpy(all_entries[entry_index].request_method, temp_method); - while (*current_pos != ' ' && *current_pos != '\0') { + strncpy(all_entries[entry_index].request_method, temp_method,sizeof(all_entries[entry_index].request_method)); + while (*current_pos != ' ' && *current_pos != '\0'){ current_pos++; } current_pos = skip_spaces(current_pos); @@ -447,7 +450,7 @@ int parse_simple_log_line(char* line, int entry_index, char* source_file) { // N // ^ // Einlesen des URL-Path bis zum abschließenden " int i = 0; - while (*current_pos != ' ' && *current_pos != '"' && *current_pos != '\0' && i < sizeof(all_entries[entry_index].url_path) - 1) { + while (*current_pos != ' ' && *current_pos != '"' && *current_pos != '\0' && i < sizeof(all_entries[entry_index].url_path) - 1){ all_entries[entry_index].url_path[i] = *current_pos; i++; current_pos++; @@ -455,25 +458,27 @@ int parse_simple_log_line(char* line, int entry_index, char* source_file) { // N all_entries[entry_index].url_path[i] = '\0'; while (*current_pos != '"' && *current_pos != '\0') current_pos++; - if (*current_pos == '"') { + if (*current_pos == '"'){ current_pos++; } } 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 "ATYPICAL" repräsentiert + // strcpy reicht hier aus, da der Wert "ATYPICAL" hard-gecoded und somit deterministisch ist" strcpy(all_entries[entry_index].request_method, "ATYPICAL"); - // Read entire quoted content into url_path for forensic analysis + // Kompletten Inhalt zwischen "" einlesen int i = 0; - while (*current_pos != '"' && *current_pos != '\0' && i < sizeof(all_entries[entry_index].url_path) - 1) { + while (*current_pos != '"' && *current_pos != '\0' && i < sizeof(all_entries[entry_index].url_path) - 1){ all_entries[entry_index].url_path[i] = *current_pos; i++; current_pos++; } + // Nullterminator anfügen all_entries[entry_index].url_path[i] = '\0'; // zum Ende des request-strings vorarbeiten, wenn der String zu lang war. - while (*current_pos != '"' && *current_pos != '\0') { + while (*current_pos != '"' && *current_pos != '\0'){ current_pos++; } @@ -491,7 +496,7 @@ int parse_simple_log_line(char* line, int entry_index, char* source_file) { // N // ^ // Parsen ded HTTP-Status Codes, der eine Zahl sein muss all_entries[entry_index].status_code = 0; - while (*current_pos >= '0' && *current_pos <= '9') { + while (*current_pos >= '0' && *current_pos <= '9'){ all_entries[entry_index].status_code = all_entries[entry_index].status_code * 10 + (*current_pos - '0'); current_pos++; } @@ -501,31 +506,35 @@ 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);" "-" // ^ all_entries[entry_index].bytes_sent = 0; - while (*current_pos >= '0' && *current_pos <= '9') { + while (*current_pos >= '0' && *current_pos <= '9'){ all_entries[entry_index].bytes_sent = all_entries[entry_index].bytes_sent * 10 + (*current_pos - '0'); current_pos++; } current_pos = skip_spaces(current_pos); - // Parsen des Referrer-Feldes innerhalb "", wird übersprungen da nicht gespeichert - if (*current_pos == '"') { + // Parsen des Referrer-Feldes innerhalb "" + if (*current_pos == '"'){ current_pos++; // öffnendes Anführungszeichen überspringen - // Referrer-Inhalt bis zum schließenden Anführungszeichen überspringen - while (*current_pos != '"' && *current_pos != '\0') { + // Referrer-Inhalt zwischen "" einlesen + int i = 0; + while (*current_pos != '"' && *current_pos != '\0' && i < sizeof(all_entries[entry_index].referrer) - 1){ + all_entries[entry_index].referrer[i] = *current_pos; + i++; current_pos++; } + all_entries[entry_index].referrer[i] = '\0'; + 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.\nLogeintrag: %s\n", line); cleanup_and_exit(); } - current_pos = skip_spaces(current_pos); // parsen des user agents innerhalb "" - if (*current_pos == '"') { + if (*current_pos == '"'){ current_pos++; int i = 0; - while (*current_pos != '"' && *current_pos != '\0' && i < sizeof(all_entries[entry_index].user_agent) - 1) { + while (*current_pos != '"' && *current_pos != '\0' && i < sizeof(all_entries[entry_index].user_agent) - 1){ all_entries[entry_index].user_agent[i] = *current_pos; i++; current_pos++; @@ -536,7 +545,7 @@ int parse_simple_log_line(char* line, int entry_index, char* source_file) { // N 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);\"\nLogeintrag: %s\n", line); cleanup_and_exit(); } - get_current_timestamp(all_entries[entry_index].parsing_timestamp, sizeof(all_entries[entry_index].parsing_timestamp)); + get_current_timestamp(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 @@ -545,18 +554,18 @@ int parse_simple_log_line(char* line, int entry_index, char* source_file) { // N return 1; } -void load_regular_file(char* filename) { +void load_regular_file(char* filename){ FILE* file = fopen(filename, "r"); - if (file == NULL) { + if (file == NULL){ printf("ERROR: Kann Datei '%s' nicht öffnen!\n", filename); return; } printf("INFO: Lade Datei: %s\n", filename); char line[MAX_REQUEST_LENGTH]; int loaded_from_this_file = 0; - while (fgets(line, sizeof(line), file) != NULL) { + while (fgets(line, sizeof(line), file) != NULL){ mem_expand_dynamically(); - if (parse_simple_log_line(line, total_entries, filename)) { + if (parse_simple_log_line(line, total_entries, filename)){ total_entries++; loaded_from_this_file++; } @@ -566,9 +575,9 @@ void load_regular_file(char* filename) { } // https://stackoverflow.com/questions/15417648/uncompressing-a-gz-string-in-c -void load_gz_file(char* filename) { +void load_gz_file(char* filename){ FILE *packedFileHandle = fopen(filename, "rb"); - if (packedFileHandle == NULL) { + if (packedFileHandle == NULL){ printf("ERROR: Kann Datei '%s' nicht öffnen!\n", filename); return; } @@ -577,7 +586,7 @@ void load_gz_file(char* filename) { // Temporäre Datei erstellen FILE *tempFile = tmpfile(); - if (tempFile == NULL) { + if (tempFile == NULL){ printf("ERROR: Kann temporäre Datei nicht erstellen!\n"); fclose(packedFileHandle); return; @@ -592,9 +601,9 @@ void load_gz_file(char* filename) { char line[MAX_REQUEST_LENGTH]; int loaded_from_this_file = 0; - while (fgets(line, sizeof(line), tempFile) != NULL) { + while (fgets(line, sizeof(line), tempFile) != NULL){ mem_expand_dynamically(); - if (parse_simple_log_line(line, total_entries, filename)) { + if (parse_simple_log_line(line, total_entries, filename)){ total_entries++; loaded_from_this_file++; } @@ -605,25 +614,25 @@ void load_gz_file(char* filename) { printf(" -> %d Einträge aus dieser Datei geladen.\n", loaded_from_this_file); } -void load_log_file(char* path) { +void load_log_file(char* path){ total_entries = 0; char full_path[512]; - if (is_directory(path)) { + if (is_directory(path)){ if (flag_verbose) printf("DEBUG: Verzeichnis erkannt: %s\n", path); if (flag_verbose) printf("DEBUG: Suche nach .log Dateien...\n"); DIR* dir = opendir(path); - if (dir == NULL) { + if (dir == NULL){ printf("ERROR: Kann Verzeichnis '%s' nicht öffnen!\n", path); return; } struct dirent* entry; int files_found = 0; - while ((entry = readdir(dir)) != NULL) { + while ((entry = readdir(dir)) != NULL){ char* filename = (*entry).d_name; - if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0) { + if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0){ printf("WARNING: Überspringe Datei %s, unerwartete Dateisignatur\n", filename); continue; } @@ -636,19 +645,19 @@ void load_log_file(char* path) { // Flag, um den Debug Print und den Export zu verbessern, ansonsten können // auftreten int needs_slash = (path_len > 0 && path[path_len - 1] != '/'); - if (is_log_file(filename)) { - (needs_slash) ? snprintf(full_path, sizeof(full_path), "%s/%s", path, filename) : snprintf(full_path, sizeof(full_path), "%s%s", path, filename); + if (is_log_file(filename)){ + (needs_slash) ? sprintf(full_path, "%s/%s", path, filename) : sprintf(full_path, "%s%s", path, filename); load_regular_file(full_path); files_found++; - } else if (strstr(filename, ".gz") != NULL) { - (needs_slash) ? snprintf(full_path, sizeof(full_path), "%s/%s", path, filename) : snprintf(full_path, sizeof(full_path), "%s%s", path, filename); + } else if (strstr(filename, ".gz") != NULL){ + (needs_slash) ? sprintf(full_path, "%s/%s", path, filename) : sprintf(full_path, "%s%s", path, filename); load_gz_file(full_path); files_found++; } } closedir(dir); - if (files_found == 0) { + if (files_found == 0){ printf("WARNING: Keine .log Dateien im Verzeichnis gefunden.\n"); printf(" Tipp: Für .gz Dateien verwenden Sie 'gunzip *.gz' zum Dekomprimieren\n"); } else { @@ -656,7 +665,7 @@ void load_log_file(char* path) { } } else { if (flag_verbose) printf("DEBUG: Einzelne Datei erkannt: %s\n", path); - if (strstr(path, ".gz") != NULL) { + if (strstr(path, ".gz") != NULL){ load_gz_file(path); } else { load_regular_file(path); @@ -665,27 +674,27 @@ void load_log_file(char* path) { printf("INFO: Erfolgreich %d Einträge insgesamt geladen.\n", total_entries); if (flag_verbose) printf("DEBUG: Aktueller Speicherverbrauch: %lu Bytes für %d Einträge\n", - (unsigned long)(max_entries * sizeof(struct log_entry)), max_entries); + (unsigned long)(max_entries * sizeof(struct log_entry_t)), max_entries); } // Filterfunktion für den User-Agent. Nimmt den Datensatz entgegen und prüft gegen die gesetzten Filter, gibt dann 0 oder 1 zurück -int user_agent_matches(char* user_agent) { +int user_agent_matches(char* user_agent){ // kein Filter gesetzt - positiver Rückgabewert if (filters.user_agent_count == 0) return 1; // Ausschluss-Filter, jeden iterativ prüfen - for (int i = 0; i < filters.user_agent_count; i++) { - if (filters.user_agent_filters[i].filter_exclude_flag == 1) { - if (search_in_string(user_agent, filters.user_agent_filters[i].pattern)) { + for (int i = 0; i < filters.user_agent_count; i++){ + if (filters.user_agent_filters[i].filter_exclude_flag == 1){ + if (search_in_string(user_agent, filters.user_agent_filters[i].pattern)){ return 0; // Ausschlussfilter aktiv, User Agent gefunden - nicht anzeigen } } } // Einschlussfilter int has_include_filters = 0; - for (int i = 0; i < filters.user_agent_count; i++) { - if (filters.user_agent_filters[i].filter_exclude_flag == 0) { + for (int i = 0; i < filters.user_agent_count; i++){ + if (filters.user_agent_filters[i].filter_exclude_flag == 0){ has_include_filters = 1; - if (search_in_string(user_agent, filters.user_agent_filters[i].pattern)) { + if (search_in_string(user_agent, filters.user_agent_filters[i].pattern)){ return 1; // Ausschlussfilter inaktiv, Eintrag gefunden - anzeigen } } @@ -695,23 +704,23 @@ int user_agent_matches(char* user_agent) { } // Filterfunktion für URL-Pfad. Nimmt den Datensatz entgegen und prüft gegen die gesetzten Filter, gibt dann 0 oder 1 zurück -int url_matches(char* url_path) { +int url_matches(char* url_path){ // kein Filter gesetzt - positiver Rückgabewert if (filters.url_count == 0) return 1; // Ausschluss-Filter, jeden iterativ prüfen - for (int i = 0; i < filters.url_count; i++) { - if (filters.url_filters[i].filter_exclude_flag == 1) { - if (search_in_string(url_path, filters.url_filters[i].pattern)) { + for (int i = 0; i < filters.url_count; i++){ + if (filters.url_filters[i].filter_exclude_flag == 1){ + if (search_in_string(url_path, filters.url_filters[i].pattern)){ return 0; // Ausschlussfilter aktiv, URL-Pfad gefunden - nicht anzeigen } } } // Einschlussfilter int has_include_filters = 0; - for (int i = 0; i < filters.url_count; i++) { - if (filters.url_filters[i].filter_exclude_flag == 0) { + for (int i = 0; i < filters.url_count; i++){ + if (filters.url_filters[i].filter_exclude_flag == 0){ has_include_filters = 1; - if (search_in_string(url_path, filters.url_filters[i].pattern)) { + if (search_in_string(url_path, filters.url_filters[i].pattern)){ return 1; // Ausschlussfilter inaktiv, Eintrag gefunden - anzeigen } } @@ -721,23 +730,23 @@ int url_matches(char* url_path) { } // Filterfunktion für Zugriffsmethode. Nimmt den Datensatz entgegen und prüft gegen die gesetzten Filter, gibt dann 0 oder 1 zurück -int method_matches(char* request_method) { +int method_matches(char* request_method){ // kein Filter gesetzt - positiver Rückgabewert if (filters.method_count == 0) return 1; // Ausschluss-Filter, jeden iterativ prüfen - for (int i = 0; i < filters.method_count; i++) { - if (filters.method_filters[i].filter_exclude_flag == 1) { - if (search_in_string(request_method, filters.method_filters[i].pattern)) { + for (int i = 0; i < filters.method_count; i++){ + if (filters.method_filters[i].filter_exclude_flag == 1){ + if (search_in_string(request_method, filters.method_filters[i].pattern)){ return 0; // Ausschlussfilter aktiv, HTTP-Methode gefunden - nicht anzeigen } } } // Einschlussfilter int has_include_filters = 0; - for (int i = 0; i < filters.method_count; i++) { - if (filters.method_filters[i].filter_exclude_flag == 0) { + for (int i = 0; i < filters.method_count; i++){ + if (filters.method_filters[i].filter_exclude_flag == 0){ has_include_filters = 1; - if (search_in_string(request_method, filters.method_filters[i].pattern)) { + if (search_in_string(request_method, filters.method_filters[i].pattern)){ return 1; // Ausschlussfilter inaktiv, Eintrag gefunden - anzeigen } } @@ -747,23 +756,23 @@ int method_matches(char* request_method) { } // Filterfunktion für Status-Code. Nimmt den Datensatz entgegen und prüft gegen die gesetzten Filter, gibt dann 0 oder 1 zurück -int status_code_matches(int status_code) { +int status_code_matches(int status_code){ // kein Filter gesetzt - positiver Rückgabewert if (filters.status_count == 0) return 1; // Ausschluss-Filter, jeden iterativ prüfen - for (int i = 0; i < filters.status_count; i++) { - if (filters.status_filters[i].filter_exclude_flag == 1) { - if (status_code == filters.status_filters[i].code) { + for (int i = 0; i < filters.status_count; i++){ + if (filters.status_filters[i].filter_exclude_flag == 1){ + if (status_code == filters.status_filters[i].code){ return 0; // Ausschlussfilter aktiv, Status-Code gefunden - nicht anzeigen } } } // Einschlussfilter int has_include_filters = 0; - for (int i = 0; i < filters.status_count; i++) { - if (filters.status_filters[i].filter_exclude_flag == 0) { + for (int i = 0; i < filters.status_count; i++){ + if (filters.status_filters[i].filter_exclude_flag == 0){ has_include_filters = 1; - if (status_code == filters.status_filters[i].code) { + if (status_code == filters.status_filters[i].code){ return 1; // Ausschlussfilter inaktiv, Eintrag gefunden - anzeigen } } @@ -773,23 +782,23 @@ int status_code_matches(int status_code) { } // Filterfunktion für IP-Adresse. Nimmt den Datensatz entgegen und prüft gegen die gesetzten Filter, gibt dann 0 oder 1 zurück -int ip_address_matches(char* ip_address) { +int ip_address_matches(char* ip_address){ // kein Filter gesetzt - positiver Rückgabewert if (filters.ip_count == 0) return 1; // Ausschluss-Filter, jeden iterativ prüfen - for (int i = 0; i < filters.ip_count; i++) { - if (filters.ip_filters[i].filter_exclude_flag == 1) { - if (search_in_string(ip_address, filters.ip_filters[i].ip_address)) { + for (int i = 0; i < filters.ip_count; i++){ + if (filters.ip_filters[i].filter_exclude_flag == 1){ + if (search_in_string(ip_address, filters.ip_filters[i].ip_address)){ return 0; // Ausschlussfilter aktiv, IP-Adresse gefunden - nicht anzeigen } } } // Einschlussfilter int has_include_filters = 0; - for (int i = 0; i < filters.ip_count; i++) { - if (filters.ip_filters[i].filter_exclude_flag == 0) { + for (int i = 0; i < filters.ip_count; i++){ + if (filters.ip_filters[i].filter_exclude_flag == 0){ has_include_filters = 1; - if (search_in_string(ip_address, filters.ip_filters[i].ip_address)) { + if (search_in_string(ip_address, filters.ip_filters[i].ip_address)){ return 1; // Ausschlussfilter inaktiv, Eintrag gefunden - anzeigen } } @@ -799,7 +808,7 @@ int ip_address_matches(char* ip_address) { } // Prüfen aller Filter, alle müssen zutreffen -int passes_filter(int entry_index) { +int passes_filter(int entry_index){ // AND-Verknüpfung int status_match = status_code_matches(all_entries[entry_index].status_code); int method_match = method_matches(all_entries[entry_index].request_method); @@ -813,8 +822,8 @@ int passes_filter(int entry_index) { // Einfacher Zähler für alle Einträge, die die Filterfunktionen bestehen int count_filtered_entries(){ int count = 0; - for (int i = 0; i < total_entries; i++) { - if (passes_filter(i)) { + for (int i = 0; i < total_entries; i++){ + if (passes_filter(i)){ count++; } } @@ -822,20 +831,45 @@ int count_filtered_entries(){ } // 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); +void format_datetime(struct simple_time_t time, char* buffer){ + sprintf(buffer, "%04d-%02d-%02dT%02d:%02d:%02d+00:00", time.year, time.month, time.day, time.hour, time.minute, time.second); } -void export_filtered_entries(char *filepath) { +void show_preview(){ + char datetime[32]; + int lines_shown = 0; + int count_filtered =0; + printf("\n"); + printf("| TIMESTAMP | IP-Adresse | HTTP-METHODE | PFAD/PAYLOAD | USERAGENT | STATUSCODE | BYTES | REFERRER |\n"); + printf("|---------------------|-----------------|--------------|--------------------------------|----------------------|------------|----------|----------------------|\n"); + + for (int i = 0; i < MAX_PREVIEW; i++){ + if (passes_filter(i)){ + format_datetime(all_entries[i].time, datetime); + printf("| %-19.19s | %-15.15s | %-12.12s | %-30.30s | %-20.20s | %-10d | %-8d | %-20.20s |\n", datetime, all_entries[i].ip_address, all_entries[i].request_method, all_entries[i].url_path, all_entries[i].user_agent, all_entries[i].status_code, all_entries[i].bytes_sent, all_entries[i].referrer); + lines_shown++; + } + } + for (int i = 0; i < total_entries; i++){ + if (passes_filter(i)){ + count_filtered++; + } + } + printf("\n%d Zeilen von insgesamt %d Einträgen(gefiltert) angezeigt.\nInsgesamt %d Einträge im Datensatz.\n", lines_shown, total_entries, count_filtered); +} + + +void export_filtered_entries(char *filepath){ // 90 chars +delimiter char filename[91]; - if (filepath == NULL) { + if (filepath == NULL){ printf("Dateiname für Timesketch-Export eingeben (ohne .csv): "); - if (scanf("%90s", filename) != 1) { + if (scanf("%90s", filename) != 1){ printf("ERROR: Ungültiger Dateiname!\n"); return; } } else { + // buffer overflow verhindern strncpy(filename, filepath, sizeof(filename) - 1); filename[sizeof(filename) - 1] = '\0'; } @@ -843,7 +877,7 @@ void export_filtered_entries(char *filepath) { strcat(filename, ".csv"); printf("\nINFO: Schreibe Datei %s...\n", filename); FILE* file = fopen(filename, "w"); - if (file == NULL) { + if (file == NULL){ printf("ERROR: Kann Datei '%s' nicht erstellen!\n", filename); return; } @@ -851,12 +885,12 @@ void export_filtered_entries(char *filepath) { // CSV-Kopfzeile für Timesketch-Kompatibilität fprintf(file, "datetime,message,timestamp_desc,ip_address,method,url_path,status_code,bytes_sent,user_agent,parsing_timestamp\n"); - char iso_datetime[32]; + char datetime[32]; - for (int i = 0; i < total_entries; i++) { - if (passes_filter(i)) { - format_iso8601_time(all_entries[i].time, iso_datetime, sizeof(iso_datetime)); - fprintf(file, "%s,%s,\"NGINX Log\",%s,%s,%s,%d,%d,%s,%s\n", iso_datetime, all_entries[i].source_file, 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].parsing_timestamp); + for (int i = 0; i < total_entries; i++){ + if (passes_filter(i)){ + format_datetime(all_entries[i].time, datetime); + fprintf(file, "%s,%s,\"NGINX Log\",%s,%s,%s,%d,%d,%s,%s,%s\n", datetime, all_entries[i].source_file, 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].referrer,all_entries[i].parsing_timestamp); } } @@ -865,8 +899,8 @@ void export_filtered_entries(char *filepath) { } // Funktionen zum setzen der Filter (existierende Datenstrukturen) -void apply_status_filter(char* value, int filter_exclude_flag) { - if (filters.status_count >= MAX_FILTERS) { +void apply_status_filter(char* value, int filter_exclude_flag){ + if (filters.status_count >= MAX_FILTERS){ printf("WARNING: MAX_FILTERS überschritten, ignoriere: %s\n", value); return; } @@ -876,7 +910,7 @@ void apply_status_filter(char* value, int filter_exclude_flag) { if (*endptr != '\0'){ printf("ERROR: Ungültiger Wert im Statuscode-Filter: %s", value); } - if (status_code < 100 || status_code > 599) { + if (status_code < 100 || status_code > 599){ printf("WARNING: Invalid status code: %s (must be 100-599)\n", value); return; } @@ -889,20 +923,20 @@ void apply_status_filter(char* value, int filter_exclude_flag) { if (flag_verbose) printf("DEBUG: Filter hinzugefügt: %s%d\n", filter_exclude_flag == 1 ? "!" : "", status_code); } -void apply_ip_filter(char* value, int filter_exclude_flag) { - if (filters.ip_count >= MAX_FILTERS) { +void apply_ip_filter(char* value, int filter_exclude_flag){ + if (filters.ip_count >= MAX_FILTERS){ printf("WARNING: MAX_FILTERS überschritten, ignoriere: %s\n", value); return; } // einfache Plausibilitätsprüfung hinsichtlich der Länge - if (strlen(value) >= sizeof(filters.ip_filters[0].ip_address)) { + if (strlen(value) >= sizeof(filters.ip_filters[0].ip_address)){ printf("WARNING: IP-Adresse zu lang: %s\n", value); return; } // setzen des Filters - strcpy(filters.ip_filters[filters.ip_count].ip_address, value); + strncpy(filters.ip_filters[filters.ip_count].ip_address, value,sizeof(filters.ip_filters[filters.ip_count].ip_address)); filters.ip_filters[filters.ip_count].filter_exclude_flag = filter_exclude_flag; filters.ip_count++; @@ -910,18 +944,18 @@ void apply_ip_filter(char* value, int filter_exclude_flag) { } // gleiche Mechanik wie bei IP-Adresse -void apply_method_filter(char* value, int filter_exclude_flag) { - if (filters.method_count >= MAX_FILTERS) { +void apply_method_filter(char* value, int filter_exclude_flag){ + if (filters.method_count >= MAX_FILTERS){ printf("WARNING: MAX_FILTERS überschritten, ignoriere: %s\n", value); return; } - if (strlen(value) >= sizeof(filters.method_filters[0].pattern)) { + if (strlen(value) >= sizeof(filters.method_filters[0].pattern)){ printf("WARNING: Methoden-Filterwert zu lang: %s\n", value); return; } - strcpy(filters.method_filters[filters.method_count].pattern, value); + strncpy(filters.method_filters[filters.method_count].pattern,value, sizeof(filters.method_filters[filters.method_count].pattern)); filters.method_filters[filters.method_count].filter_exclude_flag = filter_exclude_flag; filters.method_count++; @@ -929,18 +963,18 @@ void apply_method_filter(char* value, int filter_exclude_flag) { } // gleiche Mechanik wie bei IP-Adresse -void apply_useragent_filter(char* value, int filter_exclude_flag) { - if (filters.user_agent_count >= MAX_FILTERS) { +void apply_useragent_filter(char* value, int filter_exclude_flag){ + if (filters.user_agent_count >= MAX_FILTERS){ printf("WARNING: MAX_FILTERS überschritten, ignoriere: %s\n", value); return; } - if (strlen(value) >= sizeof(filters.user_agent_filters[0].pattern)) { + if (strlen(value) >= sizeof(filters.user_agent_filters[0].pattern)){ printf("WARNING: User agent Filterwert zu lang: %s\n", value); return; } - strcpy(filters.user_agent_filters[filters.user_agent_count].pattern, value); + strncpy(filters.user_agent_filters[filters.user_agent_count].pattern, value,sizeof(filters.user_agent_filters[filters.user_agent_count].pattern)); filters.user_agent_filters[filters.user_agent_count].filter_exclude_flag = filter_exclude_flag; filters.user_agent_count++; @@ -948,18 +982,18 @@ void apply_useragent_filter(char* value, int filter_exclude_flag) { } // gleiche Mechanik wie bei IP-Adresse -void apply_url_filter(char* value, int filter_exclude_flag) { - if (filters.url_count >= MAX_FILTERS) { +void apply_url_filter(char* value, int filter_exclude_flag){ + if (filters.url_count >= MAX_FILTERS){ printf("WARNING: MAX_FILTERS überschritten, ignoriere: %s\n", value); return; } - if (strlen(value) >= sizeof(filters.url_filters[0].pattern)) { + if (strlen(value) >= sizeof(filters.url_filters[0].pattern)){ printf("WARNING: URL/Payload Filterwert zu lang: %s\n", value); return; } - strcpy(filters.url_filters[filters.url_count].pattern, value); + strncpy(filters.url_filters[filters.url_count].pattern, value,sizeof(filters.url_filters[filters.url_count].pattern)); filters.url_filters[filters.url_count].filter_exclude_flag = filter_exclude_flag; filters.url_count++; @@ -969,7 +1003,7 @@ void apply_url_filter(char* value, int filter_exclude_flag) { // 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(char* values_str, char* filter_type) { +void parse_filter_values(char* values_str, char* filter_type){ char values_local[1024]; // Werte in lokale Variable einlesen, Nullterminator setzen strncpy(values_local, values_str, sizeof(values_local) - 1); @@ -978,28 +1012,28 @@ void parse_filter_values(char* values_str, char* filter_type) { // 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) { + while (token != NULL){ // Sollte der Nutzer Leerzeichen verwendet haben, müssen diese übersprungen werden while (*token == ' ') token++; // setzen des Modus, Standard inklusiv int filter_exclude_flag = 0; // ..mit !-Präfix exklusiv. - if (*token == '!') { + if (*token == '!'){ filter_exclude_flag = 1; token++; // den Pointer vom ! weiteriterieren, dieser ist nicht Teil des Filters } - if (strlen(token) > 0) { + if (strlen(token) > 0){ // wenn das token Werte hat, werden die entsprechenden Funktionen aufgerufen, die die Filter setzen - if (strcmp(filter_type, "status") == 0) { + if (strcmp(filter_type, "status") == 0){ apply_status_filter(token, filter_exclude_flag); - } else if (strcmp(filter_type, "ip") == 0) { + } else if (strcmp(filter_type, "ip") == 0){ apply_ip_filter(token, filter_exclude_flag); - } else if (strcmp(filter_type, "method") == 0) { + } else if (strcmp(filter_type, "method") == 0){ apply_method_filter(token, filter_exclude_flag); - } else if (strcmp(filter_type, "useragent") == 0) { + } else if (strcmp(filter_type, "useragent") == 0){ apply_useragent_filter(token, filter_exclude_flag); - } else if (strcmp(filter_type, "url") == 0) { + } else if (strcmp(filter_type, "url") == 0){ apply_url_filter(token, filter_exclude_flag); } } @@ -1009,14 +1043,14 @@ void parse_filter_values(char* values_str, char* filter_type) { } // Filter-Argument Parser -int parse_filter_argument(char* arg) { - if (!starts_with(arg, "--")) { +int parse_filter_argument(char* arg){ + if (!starts_with(arg, "--")){ return 0; } // = finden, strchr gibt den Pointer auf das = zurück char* equals_pos = strchr(arg, '='); - if (equals_pos == NULL) { + if (equals_pos == NULL){ printf("WARNING: Ungültiges Filter-Format, kein '=' gefunden: %s\n", arg); return 0; } @@ -1032,15 +1066,15 @@ int parse_filter_argument(char* arg) { // Filter-Werte (nach den =) char* values = equals_pos + 1; - if (strstr(filter_type, "status") != NULL) { + if (strstr(filter_type, "status") != NULL){ parse_filter_values(values, "status"); - } else if (strstr(filter_type, "ip") != NULL) { + } else if (strstr(filter_type, "ip") != NULL){ parse_filter_values(values, "ip"); - } else if (strstr(filter_type, "method") != NULL) { + } else if (strstr(filter_type, "method") != NULL){ parse_filter_values(values, "method"); - } else if (strstr(filter_type, "useragent") != NULL) { + } else if (strstr(filter_type, "useragent") != NULL){ parse_filter_values(values, "useragent"); - } else if (strstr(filter_type, "url") != NULL) { + } else if (strstr(filter_type, "url") != NULL){ parse_filter_values(values, "url"); } else { printf("WARNING: Unbekannter Filtertyp: %s\n", filter_type); @@ -1050,10 +1084,11 @@ int parse_filter_argument(char* arg) { return 1; } -void print_help(char* binary) { +void print_help(char* binary){ printf("NGINX-Auditor (Beleg)\n"); printf("Nutzung: %s [Flags]\n\n", binary); printf("Flags:\n"); + printf(" -i [Logfile|Verzeichnis] Optional Eingabe definieren - Datei oder Verzeichnis; Standard /var/log/nginx\n"); printf(" -e [Dateiname ohne Endung] Export zu Timestamp-kompatiblem CSV\n"); printf(" -f --[Filterobjekt] Filtern mit den folgenden Optionen:\n"); printf(" --status=[HTTP-Statuscode],[Weiterer],[...] HTTP Status Codes, z.B. 200, 404,301,...\n"); @@ -1067,83 +1102,66 @@ void print_help(char* binary) { printf("Datei-Parsing: Unterstützt NGINX Accesslogs und Redirect-Accesslogs, auch .gz komprimierte rotierte Logs.\n"); } -int main(int argc, char* argv[]) { - if (argc < 2) { - print_help(argv[0]); - return 1; - } - - printf("\nNGINX EXAMINATOR\n"); - - // Dateipfad: wenn nicht angegeben, wird /var/log/nginx (Standardpfad) untersucht - char* input_path; - int arg_offset = 1; // Offset für die args - - // Prüfen des ersten Zeichens des ersten Arguments - Pfad oder Flag? - if (argv[1][0] == '-') { - // Ist ein Flag - Standardpfad, Offset bleibt bei 1 - input_path = "/var/log/nginx/"; - arg_offset = 1; - } else { - input_path = argv[1]; - arg_offset = 2; // Offset inkrementieren, Dateipfad schiebt die args nach hinten - } +int main(int argc, char* argv[]){ + char* input_path = "/var/log/nginx"; // Standardpfad + char export_filename[50]; int flag_export = 0; - int flag_help = 0; - // int flag_verbose = 0; - global definiert - - char export_filename[90]; - int flag_has_filename = 0; - + int flag_filter =0; + int flag_has_export_filename = 0; + int flag_showpreview =0; allocate_initial_memory(); - - if (argc >= 2){ - // hier wird das offset angewendet - for (int i=arg_offset; i