src/main.c aktualisiert
This commit is contained in:
		
							parent
							
								
									65e82f45b7
								
							
						
					
					
						commit
						3d11d2f0f2
					
				
							
								
								
									
										385
									
								
								src/main.c
									
									
									
									
									
								
							
							
						
						
									
										385
									
								
								src/main.c
									
									
									
									
									
								
							@ -22,7 +22,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 | 
			
		||||
#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 MAX_FILTERS 100
 | 
			
		||||
#define MAX_URL_PATH_LENGTH 512
 | 
			
		||||
#define MAX_REQUEST_LENGTH 1024 // das hohe Limit ist erforderlich, da teilweise ausufernde JSON-Requests in nginx auflaufen können.
 | 
			
		||||
 | 
			
		||||
// definiert Variablen für den Filtermodus. FILTER_INCLUDE=0, FILTER_EXCLUDE=1. Verbessert die Lesbarkeit des Codes.
 | 
			
		||||
typedef enum {
 | 
			
		||||
@ -44,10 +44,12 @@ struct simple_time {
 | 
			
		||||
struct log_entry {
 | 
			
		||||
    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_URL_PATH_LENGTH]; // Pfade können lang werden, insbesodere bei base64-Strings wie oft in modernen Applikationen oder Malware verwendet
 | 
			
		||||
    char url_path[MAX_REQUEST_LENGTH]; // Pfade können lang werden, insbesodere bei base64-Strings wie oft in modernen Applikationen oder Malware verwendet
 | 
			
		||||
    int status_code;
 | 
			
		||||
    int bytes_sent;
 | 
			
		||||
    struct simple_time time;
 | 
			
		||||
    char referrer[128];
 | 
			
		||||
    char user_agent[256];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Struktur für einen Status-Filtereintrag mit Inhalt & Modus
 | 
			
		||||
@ -69,6 +71,12 @@ struct time_filter {
 | 
			
		||||
    filter_mode_t mode;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Filter für User-Agent
 | 
			
		||||
struct user_agent_filter {
 | 
			
		||||
    char pattern[256];  
 | 
			
		||||
    filter_mode_t mode;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Struktur zum erhalten aller Filtereinträge, kann im Dialogbetrieb bearbeitet werden. Mit Zähler.
 | 
			
		||||
struct filter_system {
 | 
			
		||||
    struct status_filter status_filters[MAX_FILTERS];
 | 
			
		||||
@ -80,6 +88,9 @@ struct filter_system {
 | 
			
		||||
    struct time_filter time_filters[MAX_FILTERS];
 | 
			
		||||
    int time_count;
 | 
			
		||||
 | 
			
		||||
    struct user_agent_filter user_agent_filters[MAX_FILTERS];
 | 
			
		||||
    int user_agent_count;
 | 
			
		||||
    
 | 
			
		||||
    int combination_mode; // 0=AND-Filter oder 1=OR-Filter
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -198,7 +209,7 @@ 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, Programm wird beendet...\n", max_entries);
 | 
			
		||||
            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)));
 | 
			
		||||
            cleanup_and_exit();
 | 
			
		||||
        }
 | 
			
		||||
@ -213,7 +224,7 @@ 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, Programm wird beendet...\n", max_entries);
 | 
			
		||||
        printf("ERROR: Konnte %d Einträge nicht allozieren, ..\n", max_entries);
 | 
			
		||||
        printf("ERROR: %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
 | 
			
		||||
    }
 | 
			
		||||
@ -258,8 +269,10 @@ Daher ist das Parsing am einfachsten, wenn ein Pointer-basierter Algorithmus die
 | 
			
		||||
Fehleranfällig, wenn das Logformat nicht dem Standard entspricht - das gilt aber auch für andere Parser.
 | 
			
		||||
*/
 | 
			
		||||
// Standard-nginx-accesslog: 
 | 
			
		||||
// Standard-nginx-accesslog: 
 | 
			
		||||
// 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
 | 
			
		||||
    printf("Parsing line: %s\n", line);
 | 
			
		||||
    char* current_pos = line;
 | 
			
		||||
    // leere Zeichen am Anfang überspringen
 | 
			
		||||
    current_pos = skip_spaces(current_pos);
 | 
			
		||||
@ -280,6 +293,8 @@ int parse_simple_log_line(char* line, int entry_index) { // Nimmt den Pointer au
 | 
			
		||||
    // mehrmaliges Überspringen des Wertes, der nicht von Interesse ist, Überspringen des Leerzeichens
 | 
			
		||||
    while (*current_pos != ' ' && *current_pos != '\0') current_pos++;
 | 
			
		||||
    current_pos = skip_spaces(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);" "-"
 | 
			
		||||
    //                  ^
 | 
			
		||||
    while (*current_pos != ' ' && *current_pos != '\0') current_pos++;
 | 
			
		||||
    current_pos = skip_spaces(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);" "-"
 | 
			
		||||
@ -344,35 +359,85 @@ int parse_simple_log_line(char* line, int entry_index) { // Nimmt den Pointer au
 | 
			
		||||
        // 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]\nProgramm wird beendet.\n");
 | 
			
		||||
        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");
 | 
			
		||||
        cleanup_and_exit();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    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);" "-"
 | 
			
		||||
        //                                                  ^
 | 
			
		||||
        // HTTP-Methode bis zum nächsten Leerzeichen einlesen und speichern
 | 
			
		||||
        copy_until_space(all_entries[entry_index].request_method, current_pos, sizeof(all_entries[entry_index].request_method));
 | 
			
		||||
        while (*current_pos != ' ' && *current_pos != '\0') current_pos++;
 | 
			
		||||
        current_pos = skip_spaces(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);" "-"
 | 
			
		||||
        //                                                      ^
 | 
			
		||||
        // 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) {
 | 
			
		||||
            all_entries[entry_index].url_path[i] = *current_pos;
 | 
			
		||||
            i++;
 | 
			
		||||
            current_pos++;
 | 
			
		||||
        }
 | 
			
		||||
        all_entries[entry_index].url_path[i] = '\0';
 | 
			
		||||
        
 | 
			
		||||
        while (*current_pos != '"' && *current_pos != '\0') current_pos++;
 | 
			
		||||
        if (*current_pos == '"') current_pos++;
 | 
			
		||||
        // First, try to read what should be the HTTP method
 | 
			
		||||
        char temp_method[50];
 | 
			
		||||
        copy_until_space(temp_method, current_pos, sizeof(temp_method));
 | 
			
		||||
        
 | 
			
		||||
        // Check if it looks like a valid HTTP method (starts with letters, reasonable length)
 | 
			
		||||
        int is_valid_method = 1;
 | 
			
		||||
        if (strlen(temp_method) == 0 || strlen(temp_method) > 10) {
 | 
			
		||||
            is_valid_method = 0;
 | 
			
		||||
        } else {
 | 
			
		||||
            // Check if method contains only letters (no binary data)
 | 
			
		||||
            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'))) {
 | 
			
		||||
                    is_valid_method = 0;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if (is_valid_method) {
 | 
			
		||||
            // Normal parsing: 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') current_pos++;
 | 
			
		||||
            current_pos = skip_spaces(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);" "-"
 | 
			
		||||
            //                                                      ^
 | 
			
		||||
            // 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) {
 | 
			
		||||
                all_entries[entry_index].url_path[i] = *current_pos;
 | 
			
		||||
                i++;
 | 
			
		||||
                current_pos++;
 | 
			
		||||
            }
 | 
			
		||||
            all_entries[entry_index].url_path[i] = '\0';
 | 
			
		||||
            
 | 
			
		||||
            while (*current_pos != '"' && *current_pos != '\0') 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 "MALFORMED" repräsentiert
 | 
			
		||||
            strcpy(all_entries[entry_index].request_method, "MALFORMED");
 | 
			
		||||
            
 | 
			
		||||
            // Read entire quoted content into url_path for forensic analysis
 | 
			
		||||
            int i = 0;
 | 
			
		||||
            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++;
 | 
			
		||||
            }
 | 
			
		||||
            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') {
 | 
			
		||||
                current_pos++;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            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);" "-"
 | 
			
		||||
        //                                                                              ^
 | 
			
		||||
    } 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\"\nProgramm wird beendet.\n");
 | 
			
		||||
        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");
 | 
			
		||||
        cleanup_and_exit();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
@ -388,12 +453,44 @@ int parse_simple_log_line(char* line, int entry_index) { // Nimmt den Pointer au
 | 
			
		||||
    
 | 
			
		||||
    current_pos = skip_spaces(current_pos);
 | 
			
		||||
    // genauso mit bytegröße der Anfrage
 | 
			
		||||
    // 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') {
 | 
			
		||||
        all_entries[entry_index].bytes_sent = all_entries[entry_index].bytes_sent * 10 + (*current_pos - '0');
 | 
			
		||||
        current_pos++;
 | 
			
		||||
    }
 | 
			
		||||
    // TODO: Der User Agent wäre noch eine interessante Metrik. Kann relativ einfach implementiert werden.
 | 
			
		||||
    current_pos = skip_spaces(current_pos);
 | 
			
		||||
    
 | 
			
		||||
    // Parsen des Referrer-Feldes innerhalb "", wird übersprungen da nicht gespeichert
 | 
			
		||||
    if (*current_pos == '"') {
 | 
			
		||||
        current_pos++; // öffnendes Anführungszeichen überspringen
 | 
			
		||||
        // Referrer-Inhalt bis zum schließenden Anführungszeichen überspringen
 | 
			
		||||
        while (*current_pos != '"' && *current_pos != '\0') {
 | 
			
		||||
            current_pos++;
 | 
			
		||||
        }
 | 
			
		||||
        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");
 | 
			
		||||
        cleanup_and_exit();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    current_pos = skip_spaces(current_pos);
 | 
			
		||||
    // parsen des user agents innerhalb ""
 | 
			
		||||
    if (*current_pos == '"') {
 | 
			
		||||
        current_pos++;
 | 
			
		||||
        int i = 0;
 | 
			
		||||
        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++;
 | 
			
		||||
        }
 | 
			
		||||
        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");
 | 
			
		||||
        cleanup_and_exit();
 | 
			
		||||
    }
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -484,6 +581,76 @@ void load_log_file(char* path) {
 | 
			
		||||
    printf("Aktueller Speicherverbrauch: %lu Bytes für %d Einträge\n", (unsigned long)(max_entries * sizeof(struct log_entry)), max_entries);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Funktion zum suchen eines Suchbegriffs innerhalb eines Strings (lowercase)
 | 
			
		||||
int search_in_string(const char* raw_string, const 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') {
 | 
			
		||||
            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
 | 
			
		||||
        }
 | 
			
		||||
        i++;
 | 
			
		||||
    }
 | 
			
		||||
    raw_string_lower[i] = '\0';  // Nullterminator anfügen
 | 
			
		||||
    
 | 
			
		||||
    // gleiche Methode mit dem Suchbegriff
 | 
			
		||||
    i = 0;
 | 
			
		||||
    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
 | 
			
		||||
        }
 | 
			
		||||
        i++;
 | 
			
		||||
    }
 | 
			
		||||
    search_string_lower[i] = '\0';  // Nullterminator anfügen
 | 
			
		||||
    
 | 
			
		||||
    // strstr()-Vergleich - gibt NULL zurück wenn nichts gefunden
 | 
			
		||||
    char* result = strstr(raw_string_lower, search_string_lower);
 | 
			
		||||
    
 | 
			
		||||
    // Einfache Rückgabe: 1 wenn gefunden, 0 wenn nicht gefunden
 | 
			
		||||
    if (result != NULL) {
 | 
			
		||||
        return 1;  
 | 
			
		||||
    } else {
 | 
			
		||||
        return 0; 
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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) {
 | 
			
		||||
    if (filters.user_agent_count == 0) return 1;
 | 
			
		||||
    
 | 
			
		||||
    int has_include_filters = 0;
 | 
			
		||||
    int include_match = 0;
 | 
			
		||||
    
 | 
			
		||||
    for (int i = 0; i < filters.user_agent_count; i++) {
 | 
			
		||||
        int pattern_found = search_in_string(user_agent, filters.user_agent_filters[i].pattern);
 | 
			
		||||
        
 | 
			
		||||
        if (filters.user_agent_filters[i].mode == FILTER_INCLUDE) {
 | 
			
		||||
            has_include_filters = 1;
 | 
			
		||||
            if (pattern_found) {
 | 
			
		||||
                include_match = 1;
 | 
			
		||||
            }
 | 
			
		||||
        } else { // ausschließen-Filter
 | 
			
		||||
            if (pattern_found) {
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    if (has_include_filters) {
 | 
			
		||||
        return include_match;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int status_code_matches(int status_code) {
 | 
			
		||||
    if (filters.status_count == 0) return 1;
 | 
			
		||||
    
 | 
			
		||||
@ -573,14 +740,14 @@ int passes_filter(int entry_index) {
 | 
			
		||||
    int status_match = status_code_matches(all_entries[entry_index].status_code);
 | 
			
		||||
    int ip_match = ip_address_matches(all_entries[entry_index].ip_address);
 | 
			
		||||
    int time_match = time_matches(all_entries[entry_index].time);
 | 
			
		||||
    
 | 
			
		||||
    int user_agent_match = user_agent_matches(all_entries[entry_index].user_agent);
 | 
			
		||||
    if (filters.combination_mode == 0) {
 | 
			
		||||
        return status_match && ip_match && time_match;
 | 
			
		||||
        return status_match && ip_match && time_match && user_agent_match;
 | 
			
		||||
    } else {
 | 
			
		||||
        int total_filters = filters.status_count + filters.ip_count + filters.time_count;
 | 
			
		||||
        int total_filters = filters.status_count + filters.ip_count + filters.time_count + filters.user_agent_count;
 | 
			
		||||
        if (total_filters == 0) return 1;
 | 
			
		||||
        
 | 
			
		||||
        return status_match || ip_match || time_match;
 | 
			
		||||
        return status_match || ip_match || time_match || user_agent_match;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -606,12 +773,13 @@ void show_status() {
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    printf("\n🔍 Aktive Filter:\n");
 | 
			
		||||
    int total_filters = filters.status_count + filters.ip_count + filters.time_count;
 | 
			
		||||
    int total_filters = filters.status_count + filters.ip_count + filters.time_count + filters.user_agent_count;  // UPDATE THIS LINE
 | 
			
		||||
    
 | 
			
		||||
    if (total_filters == 0) {
 | 
			
		||||
        printf("   Keine Filter gesetzt\n");
 | 
			
		||||
        printf("   Filter-Modus: %s\n", filters.combination_mode == 0 ? "AND" : "OR");
 | 
			
		||||
    } else {
 | 
			
		||||
        printf("   Filter-Modus: %s\n", filters.combination_mode == 0 ? "AND (alle müssen zutreffen)" : "OR (einer muss zutreffen)");
 | 
			
		||||
        printf("   Filter-Modus: %s\n", filters.combination_mode == 0 ? "AND" : "OR");
 | 
			
		||||
        
 | 
			
		||||
        if (filters.status_count > 0) {
 | 
			
		||||
            printf("   Status-Codes (%d): ", filters.status_count);
 | 
			
		||||
@ -633,6 +801,16 @@ void show_status() {
 | 
			
		||||
            printf("\n");
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if (filters.user_agent_count > 0) {
 | 
			
		||||
            printf("   User-Agent Pattern (%d): ", filters.user_agent_count);
 | 
			
		||||
            for (int i = 0; i < filters.user_agent_count; i++) {
 | 
			
		||||
                char mode_char = (filters.user_agent_filters[i].mode == FILTER_EXCLUDE) ? '!' : ' ';
 | 
			
		||||
                printf("%c\"%s\"", mode_char, filters.user_agent_filters[i].pattern);
 | 
			
		||||
                if (i < filters.user_agent_count - 1) printf(", ");
 | 
			
		||||
            }
 | 
			
		||||
            printf("\n");
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if (filters.time_count > 0) {
 | 
			
		||||
            printf("   Zeiträume (%d):\n", filters.time_count);
 | 
			
		||||
            for (int i = 0; i < filters.time_count; i++) {
 | 
			
		||||
@ -704,11 +882,11 @@ void export_filtered_entries() {
 | 
			
		||||
    
 | 
			
		||||
    FILE* file = fopen(filename, "w");
 | 
			
		||||
    if (file == NULL) {
 | 
			
		||||
        printf("FEHLER: Kann Datei '%s' nicht erstellen!\n", filename);
 | 
			
		||||
        printf("ERROR: Kann Datei '%s' nicht erstellen!\n", filename);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    fprintf(file, "message,datetime,timestamp_desc,timestamp,ip_address,method,url_path,status_code,bytes_sent\n");
 | 
			
		||||
    fprintf(file, "message,datetime,timestamp_desc,timestamp,ip_address,method,url_path,status_code,bytes_sent,user_agent\n");
 | 
			
		||||
    
 | 
			
		||||
    int exported_count = 0;
 | 
			
		||||
    char iso_datetime[32];
 | 
			
		||||
@ -726,7 +904,8 @@ void export_filtered_entries() {
 | 
			
		||||
                     all_entries[i].request_method,
 | 
			
		||||
                     all_entries[i].url_path,
 | 
			
		||||
                     all_entries[i].status_code,
 | 
			
		||||
                     all_entries[i].bytes_sent);
 | 
			
		||||
                     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,
 | 
			
		||||
@ -736,14 +915,15 @@ void export_filtered_entries() {
 | 
			
		||||
                   all_entries[i].request_method,
 | 
			
		||||
                   all_entries[i].url_path,
 | 
			
		||||
                   all_entries[i].status_code,
 | 
			
		||||
                   all_entries[i].bytes_sent);
 | 
			
		||||
                   all_entries[i].bytes_sent,
 | 
			
		||||
                   all_entries[i].user_agent);
 | 
			
		||||
            
 | 
			
		||||
            exported_count++;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    fclose(file);
 | 
			
		||||
    printf("✅ %d Einträge erfolgreich als Timesketch-kompatible CSV nach '%s' exportiert.\n", exported_count, filename);
 | 
			
		||||
    printf("%d Logeinträge erfolgreich als Timesketch-kompatible CSV-Datei nach '%s' exportiert.\n", exported_count, filename);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ip_stat {
 | 
			
		||||
@ -805,12 +985,72 @@ void show_top_10_ips() {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void show_top_user_agents() {
 | 
			
		||||
    struct user_agent_stat {
 | 
			
		||||
        char user_agent[256];
 | 
			
		||||
        int count;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    struct user_agent_stat agent_stats[1000];
 | 
			
		||||
    int unique_agents = 0;
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < total_entries; i++) {
 | 
			
		||||
        if (!passes_filter(i)) continue;
 | 
			
		||||
 | 
			
		||||
        char* current_agent = all_entries[i].user_agent;
 | 
			
		||||
        int found_index = -1;
 | 
			
		||||
 | 
			
		||||
        for (int j = 0; j < unique_agents; j++) {
 | 
			
		||||
            if (strcmp(agent_stats[j].user_agent, current_agent) == 0) {
 | 
			
		||||
                found_index = j;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (found_index >= 0) {
 | 
			
		||||
            agent_stats[found_index].count++;
 | 
			
		||||
        } else {
 | 
			
		||||
            if (unique_agents < 1000) {
 | 
			
		||||
                strcpy(agent_stats[unique_agents].user_agent, current_agent);
 | 
			
		||||
                agent_stats[unique_agents].count = 1;
 | 
			
		||||
                unique_agents++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Sort by count (descending)
 | 
			
		||||
    for (int i = 0; i < unique_agents - 1; i++) {
 | 
			
		||||
        for (int j = 0; j < unique_agents - i - 1; j++) {
 | 
			
		||||
            if (agent_stats[j].count < agent_stats[j + 1].count) {
 | 
			
		||||
                struct user_agent_stat temp = agent_stats[j];
 | 
			
		||||
                agent_stats[j] = agent_stats[j + 1];
 | 
			
		||||
                agent_stats[j + 1] = temp;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    printf("\n=== TOP 10 USER AGENTS ===\n");
 | 
			
		||||
    printf("Rang | User Agent                          | Anzahl Anfragen\n");
 | 
			
		||||
    printf("-----|--------------------------------------|----------------\n");
 | 
			
		||||
 | 
			
		||||
    int show_count = (unique_agents < 10) ? unique_agents : 10;
 | 
			
		||||
    for (int i = 0; i < show_count; i++) {
 | 
			
		||||
        printf("%-4d | %-34s | %d\n", i + 1, agent_stats[i].user_agent, agent_stats[i].count);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (unique_agents == 0) {
 | 
			
		||||
        printf("Keine User Agents in den gefilterten Daten gefunden.\n");
 | 
			
		||||
    } else {
 | 
			
		||||
        printf("\nInsgesamt %d verschiedene User Agents gefunden.\n", unique_agents);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void show_filtered_entries() {
 | 
			
		||||
    int shown_count = 0;
 | 
			
		||||
    
 | 
			
		||||
    printf("\n=== GEFILTERTE LOG-EINTRÄGE ===\n");
 | 
			
		||||
    printf("IP-Adresse       | Methode | URL                    | Status | Bytes | Zeit\n");
 | 
			
		||||
    printf("-----------------|---------|------------------------|--------|-------|------------------\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)) {
 | 
			
		||||
@ -820,6 +1060,7 @@ void show_filtered_entries() {
 | 
			
		||||
                   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,
 | 
			
		||||
@ -874,10 +1115,11 @@ void menu_set_filters() {
 | 
			
		||||
    while (choice != 4) {
 | 
			
		||||
        show_status();
 | 
			
		||||
        printf("\n=== FILTER SETZEN ===\n");
 | 
			
		||||
        printf("1. Status-Code Filter hinzufügen\n");
 | 
			
		||||
        printf("2. IP-Adresse Filter hinzufügen\n");
 | 
			
		||||
        printf("3. Zeitraum Filter hinzufügen\n");
 | 
			
		||||
        printf("4. Zurück zum Filter-Menü\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. Zurück zum Filter-Menü\n");
 | 
			
		||||
        printf("Auswahl: ");
 | 
			
		||||
        
 | 
			
		||||
        choice = read_safe_integer();
 | 
			
		||||
@ -1049,15 +1291,43 @@ void menu_set_filters() {
 | 
			
		||||
            printf("✅ Zeitraum-Filter hinzugefügt. Total: %d\n", filters.time_count);
 | 
			
		||||
 | 
			
		||||
        } else if (choice == 4) {
 | 
			
		||||
            if (filters.user_agent_count >= MAX_FILTERS) {
 | 
			
		||||
                printf("FEHLER: Maximale Anzahl User-Agent Filter erreicht (%d)!\n", MAX_FILTERS);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            printf("User-Agent Suchtext eingeben (z.B. 'chrome', 'bot', 'upti'): ");
 | 
			
		||||
            char pattern[256];
 | 
			
		||||
            if (scanf("%255s", pattern) == 1) {
 | 
			
		||||
                printf("Filter-Typ wählen:\n");
 | 
			
		||||
                printf("1. Einschließen (nur User-Agents mit '%s' anzeigen)\n", pattern);
 | 
			
		||||
                printf("2. Ausschließen (User-Agents mit '%s' NICHT anzeigen)\n", pattern);
 | 
			
		||||
                printf("Auswahl: ");
 | 
			
		||||
                int filter_type = read_safe_integer();
 | 
			
		||||
                
 | 
			
		||||
                if (filter_type == 1 || filter_type == 2) {
 | 
			
		||||
                    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);
 | 
			
		||||
                } else {
 | 
			
		||||
                    printf("FEHLER: Ungültiger Filter-Typ!\n");
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                printf("FEHLER: Ungültiger Suchtext!\n");
 | 
			
		||||
                clear_input_buffer();
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
        } else if (choice == 5) {
 | 
			
		||||
            return;
 | 
			
		||||
        } else if (choice != -1) {
 | 
			
		||||
            printf("Ungültige Auswahl! Bitte wählen Sie 1-4.\n");
 | 
			
		||||
            printf("Ungültige Auswahl! Bitte wählen Sie 1-5.\n");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void menu_delete_filters() {
 | 
			
		||||
    int total_filters = filters.status_count + filters.ip_count + filters.time_count;
 | 
			
		||||
    int total_filters = filters.status_count + filters.ip_count + filters.time_count + filters.user_agent_count;  // UPDATE THIS LINE
 | 
			
		||||
    
 | 
			
		||||
    if (total_filters == 0) {
 | 
			
		||||
        printf("Keine Filter gesetzt zum Löschen.\n");
 | 
			
		||||
@ -1079,6 +1349,11 @@ void menu_delete_filters() {
 | 
			
		||||
        printf("%d. IP-Adresse: %s %s\n", filter_index++, filters.ip_filters[i].ip_address, mode_str);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    for (int i = 0; i < filters.user_agent_count; i++) {
 | 
			
		||||
        char* mode_str = (filters.user_agent_filters[i].mode == FILTER_EXCLUDE) ? "(ausschließen)" : "(einschließen)";
 | 
			
		||||
        printf("%d. User-Agent: \"%s\" %s\n", filter_index++, filters.user_agent_filters[i].pattern, mode_str);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    for (int i = 0; i < filters.time_count; i++) {
 | 
			
		||||
        char* mode_str = (filters.time_filters[i].mode == FILTER_EXCLUDE) ? "(ausschließen)" : "(einschließen)";
 | 
			
		||||
        printf("%d. Zeitraum: %02d.%02d.%d %02d:%02d:%02d - %02d.%02d.%d %02d:%02d:%02d %s\n", 
 | 
			
		||||
@ -1136,6 +1411,18 @@ void menu_delete_filters() {
 | 
			
		||||
        current_index++;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    for (int i = 0; i < filters.user_agent_count; i++) {
 | 
			
		||||
        if (current_index == choice) {
 | 
			
		||||
            for (int j = i; j < filters.user_agent_count - 1; j++) {
 | 
			
		||||
                filters.user_agent_filters[j] = filters.user_agent_filters[j + 1];
 | 
			
		||||
            }
 | 
			
		||||
            filters.user_agent_count--;
 | 
			
		||||
            printf("✅ User-Agent Filter gelöscht.\n");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        current_index++;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    for (int i = 0; i < filters.time_count; i++) {
 | 
			
		||||
        if (current_index == choice) {
 | 
			
		||||
            for (int j = i; j < filters.time_count - 1; j++) {
 | 
			
		||||
@ -1173,6 +1460,7 @@ void menu_reset_filters() {
 | 
			
		||||
    filters.status_count = 0;
 | 
			
		||||
    filters.ip_count = 0;
 | 
			
		||||
    filters.time_count = 0;
 | 
			
		||||
    filters.user_agent_count = 0;
 | 
			
		||||
    filters.combination_mode = 0;
 | 
			
		||||
    
 | 
			
		||||
    printf("✅ Alle Filter zurückgesetzt.\n");
 | 
			
		||||
@ -1215,10 +1503,11 @@ void menu_show_entries() {
 | 
			
		||||
        printf("\n=== EINTRÄGE ANZEIGEN ===\n");
 | 
			
		||||
        int filtered_count = count_filtered_entries();
 | 
			
		||||
        printf("Aktuell %d gefilterte Einträge verfügbar.\n", filtered_count);
 | 
			
		||||
        printf("1. Gefilterte Einträge vollständig anzeigen\n");
 | 
			
		||||
        printf("2. Gefilterte Einträge in Datei exportieren\n");
 | 
			
		||||
        printf("3. Top 10 IP-Adressen anzeigen\n");
 | 
			
		||||
        printf("4. Zurück zum Hauptmenü\n");
 | 
			
		||||
        printf("1. Vollständig anzeigen\n");
 | 
			
		||||
        printf("2. Zu .csv exportieren (Timesketch-kompatibel)\n");
 | 
			
		||||
        printf("3. Top 10 IP-Adressen\n");
 | 
			
		||||
        printf("4. Top 10 User-Agents\n");
 | 
			
		||||
        printf("5. Zurück zum Hauptmenü\n");
 | 
			
		||||
        printf("Auswahl: ");
 | 
			
		||||
        
 | 
			
		||||
        choice = read_safe_integer();
 | 
			
		||||
@ -1239,6 +1528,8 @@ void menu_show_entries() {
 | 
			
		||||
        } else if (choice == 3) {
 | 
			
		||||
            show_top_10_ips();
 | 
			
		||||
        } else if (choice == 4) {
 | 
			
		||||
            show_top_user_agents();
 | 
			
		||||
        } else if (choice == 5) {
 | 
			
		||||
            return;
 | 
			
		||||
        } else if (choice != -1) {
 | 
			
		||||
            printf("Ungültige Auswahl! Bitte wählen Sie 1-4.\n");
 | 
			
		||||
@ -1249,7 +1540,7 @@ void menu_show_entries() {
 | 
			
		||||
void show_main_menu() {
 | 
			
		||||
    printf("\nHauptmenü:\n");
 | 
			
		||||
    printf("1. Filter verwalten\n");
 | 
			
		||||
    printf("2. Gefilterte Einträge anzeigen\n");
 | 
			
		||||
    printf("2. Anzeigemodi\n");
 | 
			
		||||
    printf("3. Statistiken anzeigen\n");
 | 
			
		||||
    printf("4. Beenden\n");
 | 
			
		||||
    printf("Auswahl: ");
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user