This commit is contained in:
overcuriousity 2025-09-02 13:03:35 +02:00
parent c0984b242e
commit 3e990ff05e
2 changed files with 372 additions and 363 deletions

BIN
bin/main

Binary file not shown.

View File

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