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