This commit is contained in:
overcuriousity 2025-09-03 23:30:55 +02:00
parent 4db2cb753a
commit 654641c87a

View File

@ -624,7 +624,7 @@ void load_log_file(char* path) {
} }
// Funktion zum suchen eines Suchbegriffs innerhalb eines Strings (lowercase) // Funktion zum suchen eines Suchbegriffs innerhalb eines Strings (lowercase)
int search_in_string(const char* raw_string, const char* search_string) { int search_in_string(char* raw_string, char* search_string) {
char raw_string_lower[512]; // Puffer zum Speichern des zu durchsuchenden Strings char raw_string_lower[512]; // Puffer zum Speichern des zu durchsuchenden Strings
char search_string_lower[256]; // Puffer zum Speichern des Suchbegriffs char search_string_lower[256]; // Puffer zum Speichern des Suchbegriffs
@ -1199,6 +1199,78 @@ void show_filtered_entries(int num_shown) {
} }
} }
void print_filter_args() {
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) {
return;
}
printf("-f ");
if (filters.status_count > 0) {
printf("--status=");
for (int i = 0; i < filters.status_count; i++) {
if (i > 0) printf(",");
if (filters.status_filters[i].mode == FILTER_EXCLUDE) printf("!");
printf("%d", filters.status_filters[i].code);
}
printf(" ");
}
if (filters.method_count > 0) {
printf("--method=");
for (int i = 0; i < filters.method_count; i++) {
if (i > 0) printf(",");
if (filters.method_filters[i].mode == FILTER_EXCLUDE) printf("!");
printf("%s", filters.method_filters[i].pattern);
}
printf(" ");
}
if (filters.ip_count > 0) {
printf("--ip=");
for (int i = 0; i < filters.ip_count; i++) {
if (i > 0) printf(",");
if (filters.ip_filters[i].mode == FILTER_EXCLUDE) printf("!");
printf("%s", filters.ip_filters[i].ip_address);
}
printf(" ");
}
if (filters.user_agent_count > 0) {
printf("--useragent=");
for (int i = 0; i < filters.user_agent_count; i++) {
if (i > 0) printf(",");
if (filters.user_agent_filters[i].mode == FILTER_EXCLUDE) printf("!");
printf("%s", filters.user_agent_filters[i].pattern);
}
printf(" ");
}
if (filters.url_count > 0) {
printf("--url=");
for (int i = 0; i < filters.url_count; i++) {
if (i > 0) printf(",");
if (filters.url_filters[i].mode == FILTER_EXCLUDE) printf("!");
printf("%s", filters.url_filters[i].pattern);
}
printf(" ");
}
if (filters.combination_mode == 1) {
printf("--mode=or ");
}else {
printf("--mode=and ");
}
printf("\n");
// TODO
if (filters.time_count > 0) {
printf("HINWEIS: %d Zeitraum-Filter sind aktiv, aber nur im interaktiven Modus verfügbar.\n", filters.time_count);
}
}
void show_status() { void show_status() {
printf("\nPREVIEW:\n"); printf("\nPREVIEW:\n");
@ -1218,209 +1290,14 @@ void show_status() {
if (total_filters == 0) { if (total_filters == 0) {
printf(" -> keine Filter gesetzt\n"); printf(" -> keine Filter gesetzt\n");
} else { } else {
printf(" Modus: %s\n", filters.combination_mode == 0 ? "AND" : "OR"); printf("\n ");
print_filter_args();
printf("\n <Verwendbar als Startargument>\n");
printf(" Ausschlussfilter (!) haben Vorrang, dann Einschlussfilter\n"); printf(" Ausschlussfilter (!) haben Vorrang, dann Einschlussfilter\n");
printf("\n Gesetzt:\n");
if (filters.status_count > 0) { 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);
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++;
}
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 ");
}
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);
}
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) { if (active_types > 1) {
printf("\n%s-Verknüpfung (nur Einschlussfilter)\n", printf(" %s-Verknüpfung (nur Einschlussfilter, Ausschlussfilter sind stets ODER-verknüpft)\n", filters.combination_mode == 0 ? "UND" : "ODER");
filters.combination_mode == 0 ? "UND" : "ODER");
} }
} }
@ -1480,7 +1357,7 @@ int handle_menu_shortcuts(int choice) {
} }
// überall wo integer aus String-Input gelesen werden müssen. basiert auf strtol, was gegen Buffer Overflow sicher sein soll // überall wo integer aus String-Input gelesen werden müssen. basiert auf strtol, was gegen Buffer Overflow sicher sein soll
int safe_read_integer(const char* prompt, int min_val, int max_val) { int safe_read_integer(char* prompt, int min_val, int max_val) {
char input[50]; char input[50];
int value; int value;
char *endptr; char *endptr;
@ -1516,7 +1393,7 @@ int safe_read_integer(const char* prompt, int min_val, int max_val) {
} }
} }
int safe_read_string(const char* prompt, char* buffer, int buffer_size) { int safe_read_string(char* prompt, char* buffer, int buffer_size) {
while (1) { while (1) {
printf("%s", prompt); printf("%s", prompt);
if (scanf("%s", buffer) != 1) { if (scanf("%s", buffer) != 1) {
@ -1539,9 +1416,11 @@ int safe_read_string(const char* prompt, char* buffer, int buffer_size) {
} }
} }
// Menü mit standardisierter Navigation
int read_menu_input() { int read_menu_input() {
char input[10]; char input[10];
if (scanf("%9s", input) != 1) { // scanf braucht eine Begrenzung, %s würde alle Zeichen in stdin lesen - buffer overflow
if (scanf("%5s", input) != 1) {
clear_input_buffer(); clear_input_buffer();
return -1; return -1;
} }
@ -1552,113 +1431,20 @@ int read_menu_input() {
if (strcmp(input, "q") == 0 || strcmp(input, "Q") == 0) return -4; if (strcmp(input, "q") == 0 || strcmp(input, "Q") == 0) return -4;
char *endptr; char *endptr;
//string to long
long number = strtol(input, &endptr, 10); long number = strtol(input, &endptr, 10);
// Prüfen, ob Eingabe erfolgreich zu Zahl umgewandelt werden konnte - endptr speichert das Zeichen, das nicht mehr gepasst hat - muss also Nullterminator sein
if (*endptr != '\0' || number < 0 || number > 999) { if (*endptr != '\0' || number < 0 || number > 999) {
return -1; return -1;
} }
return (int)number; return (int)number;
} }
void show_stats() {
int filtered_count = count_filtered_entries();
int count_200 = 0, count_300 = 0, count_400 = 0, count_500 = 0;
int count_get = 0, count_post = 0, count_atypical = 0, count_other = 0;
long total_bytes = 0;
int unique_ips = 0;
char ip_list[1000][50];
int ip_count = 0;
struct simple_time earliest_time = {31, 12, 9999, 23, 59, 59};
struct simple_time latest_time = {1, 1, 1970, 0, 0, 0};
for (int i = 0; i < total_entries; i++) {
if (passes_filter(i)) {
if (all_entries[i].status_code >= 200 && all_entries[i].status_code < 300) count_200++;
else if (all_entries[i].status_code >= 300 && all_entries[i].status_code < 400) count_300++;
else if (all_entries[i].status_code >= 400 && all_entries[i].status_code < 500) count_400++;
else if (all_entries[i].status_code >= 500) count_500++;
if (strcmp(all_entries[i].request_method, "GET") == 0) count_get++;
else if (strcmp(all_entries[i].request_method, "POST") == 0) count_post++;
else if (strcmp(all_entries[i].request_method, "ATYPICAL") == 0) count_atypical++;
else count_other++;
total_bytes += all_entries[i].bytes_sent;
int ip_exists = 0;
for (int j = 0; j < ip_count; j++) {
if (strcmp(ip_list[j], all_entries[i].ip_address) == 0) {
ip_exists = 1;
break;
}
}
if (!ip_exists && ip_count < 1000) {
strcpy(ip_list[ip_count], all_entries[i].ip_address);
ip_count++;
}
if (compare_times(all_entries[i].time, earliest_time) < 0) {
earliest_time = all_entries[i].time;
}
if (compare_times(all_entries[i].time, latest_time) > 0) {
latest_time = all_entries[i].time;
}
}
}
unique_ips = ip_count;
printf("\nDATENANALYSE\n");
printf(" Gesamte Einträge: %d\n Gefilterte Einträge: %d (%.1f%%)\n Eindeutige IPs: %d\n",
total_entries, filtered_count,
total_entries > 0 ? (filtered_count * 100.0 / total_entries) : 0.0, unique_ips);
if (filtered_count > 0) {
printf(" Ø Anfragen pro IP: %.1f\n", (double)filtered_count / unique_ips);
printf(" Zeitspanne: %02d.%02d.%d %02d:%02d:%02d - %02d.%02d.%d %02d:%02d:%02d\n",
earliest_time.day, earliest_time.month, earliest_time.year,
earliest_time.hour, earliest_time.minute, earliest_time.second,
latest_time.day, latest_time.month, latest_time.year,
latest_time.hour, latest_time.minute, latest_time.second);
int total_minutes = (latest_time.year - earliest_time.year) * 525600 +
(latest_time.month - earliest_time.month) * 43800 +
(latest_time.day - earliest_time.day) * 1440 +
(latest_time.hour - earliest_time.hour) * 60 +
(latest_time.minute - earliest_time.minute);
if (total_minutes > 0) {
printf(" Ø Anfragen/Minute: %.2f", (double)filtered_count / total_minutes);
if (total_minutes >= 60) {
printf("\n Ø Anfragen/Stunde: %.2f", (double)filtered_count * 60 / total_minutes);
}
printf("\n");
}
printf(" Status-Codes:\n 2xx:%d (%.1f%%)\n 3xx:%d (%.1f%%)\n 4xx:%d (%.1f%%)\n 5xx:%d (%.1f%%)\n",
count_200, count_200 * 100.0 / filtered_count,
count_300, count_300 * 100.0 / filtered_count,
count_400, count_400 * 100.0 / filtered_count,
count_500, count_500 * 100.0 / filtered_count);
printf(" HTTP-Methoden:\n GET:%d (%.1f%%)\n POST:%d (%.1f%%)\n Andere:%d (%.1f%%)\n",
count_get, count_get * 100.0 / filtered_count,
count_post, count_post * 100.0 / filtered_count,
count_other, count_other * 100.0 / filtered_count);
printf(" Datenumfang:\n %ld Bytes total\n Ø %.1f Bytes/Anfrage\n Fehlerrate: %.1f%%\n",
total_bytes, (double)total_bytes / filtered_count,
((count_400 + count_500) * 100.0) / filtered_count);
}
}
void show_main_menu() { void show_main_menu() {
printf("\nHAUPTMENÜ\n"); printf("\nHAUPTMENÜ\n");
printf("1. Filter verwalten\n"); printf("1. Filter verwalten\n");
printf("2. Daten anzeigen und exportieren\n"); printf("2. Daten anzeigen und exportieren\n");
printf("3. Statistiken anzeigen\n"); printf("3. Export (CSV, Timesketch-kompatibel)\n");
printf("4. Programm beenden\n"); printf("4. Programm beenden\n");
printf("Navigation: [b]Zurück [m]Hauptmenü [q]Beenden\n"); printf("Navigation: [b]Zurück [m]Hauptmenü [q]Beenden\n");
printf("Auswahl: "); printf("Auswahl: ");
@ -1666,6 +1452,7 @@ void show_main_menu() {
int menu_set_filters() { int menu_set_filters() {
int choice = 0; int choice = 0;
// Standardnavigation aus read_menu_input
while (choice != -2 && choice != -3) { while (choice != -2 && choice != -3) {
show_status(); show_status();
@ -1735,7 +1522,7 @@ int menu_set_filters() {
struct time_filter new_time_filter = {0}; struct time_filter new_time_filter = {0};
printf("STARTZEIT:\n"); printf("STARTZEIT:\n");
int start_year = safe_read_integer("Jahr (z.B. 2023): ", 1970, 2100); int start_year = safe_read_integer("Jahr (z.B. 2025): ", 1970, 2100);
if (start_year < 0) continue; if (start_year < 0) continue;
int start_month = safe_read_integer("Monat (1-12): ", 1, 12); int start_month = safe_read_integer("Monat (1-12): ", 1, 12);
@ -2089,6 +1876,7 @@ void menu_reset_filters() {
void menu_filter_management() { void menu_filter_management() {
int choice = 0; int choice = 0;
// Standardnavigation aus read_menu_input
while (choice != -2 && choice != -3) { while (choice != -2 && choice != -3) {
show_status(); show_status();
@ -2128,6 +1916,7 @@ void menu_filter_management() {
void menu_show_entries() { void menu_show_entries() {
int choice = 0; int choice = 0;
int supress_preview = 0; int supress_preview = 0;
// Standardnavigation aus read_menu_input()
while (choice != -2 && choice != -3) { while (choice != -2 && choice != -3) {
if (supress_preview == 0) { if (supress_preview == 0) {
show_status(); show_status();
@ -2180,10 +1969,112 @@ void menu_show_entries() {
} }
} }
// Funktionen zum setzen der Filter (existierende Datenstrukturen)
void add_status_filter(char* value, filter_mode_t mode) {
if (filters.status_count >= MAX_FILTERS) {
printf("WARNING: MAX_FILTERS überschritten, ignoriere: %s\n", value);
return;
}
// Kovertierung des Statuscodes zu long mit Error handling
char* endptr;
int status_code = strtol(value, &endptr, 10);
if (*endptr != '\n' ){
printf("ERROR: Ungültiger Wert im Statuscode-Filter: %s", value);
}
if (status_code < 100 || status_code > 599) {
printf("WARNING: Invalid status code: %s (must be 100-599)\n", value);
return;
}
// setzen des Filters
filters.status_filters[filters.status_count].code = status_code;
filters.status_filters[filters.status_count].mode = mode;
filters.status_count++;
printf("DEBUG: Filter hinzugefügt: %s%d\n", mode == FILTER_EXCLUDE ? "!" : "", status_code);
}
void add_ip_filter(char* value, filter_mode_t mode) {
if (filters.ip_count >= MAX_FILTERS) {
printf("WARNING: MAX_FILTERS überschritten, ignoriere: %s\n", value);
return;
}
// einfache Plausibilitätsprüfung hinsichtlich der Länge
if (strlen(value) >= sizeof(filters.ip_filters[0].ip_address)) {
printf("WARNING: IP-Adresse zu lang: %s\n", value);
return;
}
// setzen des Filters
strcpy(filters.ip_filters[filters.ip_count].ip_address, value);
filters.ip_filters[filters.ip_count].mode = mode;
filters.ip_count++;
printf("DEBUG: IP-Adressfilter hinzugefügt: %s%s\n", mode == FILTER_EXCLUDE ? "!" : "", value);
}
// gleiche Mechanik wie bei IP-Adresse
void add_method_filter(char* value, filter_mode_t mode) {
if (filters.method_count >= MAX_FILTERS) {
printf("WARNING: MAX_FILTERS überschritten, ignoriere: %s\n", value);
return;
}
if (strlen(value) >= sizeof(filters.method_filters[0].pattern)) {
printf("WARNING: Methoden-Filterwert zu lang: %s\n", value);
return;
}
strcpy(filters.method_filters[filters.method_count].pattern, value);
filters.method_filters[filters.method_count].mode = mode;
filters.method_count++;
printf("DEBUG: Methoden-Filter hinzugefügt: %s%s\n", mode == FILTER_EXCLUDE ? "!" : "", value);
}
// gleiche Mechanik wie bei IP-Adresse
void add_useragent_filter(char* value, filter_mode_t mode) {
if (filters.user_agent_count >= MAX_FILTERS) {
printf("WARNING: MAX_FILTERS überschritten, ignoriere: %s\n", value);
return;
}
if (strlen(value) >= sizeof(filters.user_agent_filters[0].pattern)) {
printf("WARNING: User agent Filterwert zu lang: %s\n", value);
return;
}
strcpy(filters.user_agent_filters[filters.user_agent_count].pattern, value);
filters.user_agent_filters[filters.user_agent_count].mode = mode;
filters.user_agent_count++;
printf("DEBUG: User Agent Filter hinzugefügt: %s%s\n", mode == FILTER_EXCLUDE ? "!" : "", value);
}
// gleiche Mechanik wie bei IP-Adresse
void add_url_filter(char* value, filter_mode_t mode) {
if (filters.url_count >= MAX_FILTERS) {
printf("WARNING: MAX_FILTERS überschritten, ignoriere: %s\n", value);
return;
}
if (strlen(value) >= sizeof(filters.url_filters[0].pattern)) {
printf("WARNING: URL/Payload Filterwert zu lang: %s\n", value);
return;
}
strcpy(filters.url_filters[filters.url_count].pattern, value);
filters.url_filters[filters.url_count].mode = mode;
filters.url_count++;
printf("DEBUG: URL/Payload-Filter hinzugefügt: %s%s\n", mode == FILTER_EXCLUDE ? "!" : "", value);
}
// Funktion zum Parsen der Filter-Werte, die mit --<Filtertyp>=<Werte,Komma-getrennt> übergeben werden. // Funktion zum Parsen der Filter-Werte, die mit --<Filtertyp>=<Werte,Komma-getrennt> übergeben werden.
// values_str sind die Werte hinter dem =, filter_type die Werte vor dem = // values_str sind die Werte hinter dem =, filter_type die Werte vor dem =
// filter_type wird von parse_filter_argument() übergeben // filter_type wird von parse_filter_argument() übergeben
void parse_filter_values(const char* values_str, const char* filter_type) { void parse_filter_values(char* values_str, char* filter_type) {
char values_local[1024]; char values_local[1024];
// Werte in lokale Variable einlesen, Nullterminator setzen // Werte in lokale Variable einlesen, Nullterminator setzen
strncpy(values_local, values_str, sizeof(values_local) - 1); strncpy(values_local, values_str, sizeof(values_local) - 1);
@ -2222,192 +2113,56 @@ void parse_filter_values(const char* values_str, const char* filter_type) {
} }
} }
// Funktionen zum setzen der Filter (existierende Datenstrukturen)
void add_status_filter(const char* value, filter_mode_t mode) {
if (filters.status_count >= MAX_FILTERS) {
printf("WARNING: MAX_FILTERS überschritten, ignoriere: %s\n", value);
return;
}
// Kovertierung des Statuscodes zu long mit Error handling
int status_code = strtol(value, &endptr, 10);
if (endptr != '\n' ){
printf("ERROR: Ungültiger Wert im Statuscode-Filter: %s", value);
}
if (status_code < 100 || status_code > 599) {
printf("WARNING: Invalid status code: %s (must be 100-599)\n", value);
return;
}
// setzen des Filters
filters.status_filters[filters.status_count].code = status_code;
filters.status_filters[filters.status_count].mode = mode;
filters.status_count++;
printf("DEBUG: Filter hinzugefügt: %s%d\n", mode == FILTER_EXCLUDE ? "!" : "", status_code);
}
void add_ip_filter(const char* value, filter_mode_t mode) {
if (filters.ip_count >= MAX_FILTERS) {
printf("WARNING: MAX_FILTERS überschritten, ignoriere: %s\n", value);
return;
}
// einfache Plausibilitätsprüfung hinsichtlich der Länge
if (strlen(value) >= sizeof(filters.ip_filters[0].ip_address)) {
printf("WARNING: IP-Adresse zu lang: %s\n", value);
return;
}
// setzen des Filters
strcpy(filters.ip_filters[filters.ip_count].ip_address, value);
filters.ip_filters[filters.ip_count].mode = mode;
filters.ip_count++;
printf("DEBUG: IP-Adressfilter hinzugefügt: %s%s\n", mode == FILTER_EXCLUDE ? "!" : "", value);
}
// gleiche Mechanik wie bei IP-Adresse
void add_method_filter(const char* value, filter_mode_t mode) {
if (filters.method_count >= MAX_FILTERS) {
printf("WARNING: MAX_FILTERS überschritten, ignoriere: %s\n", value);
return;
}
if (strlen(value) >= sizeof(filters.method_filters[0].pattern)) {
printf("WARNING: Methoden-Filterwert zu lang: %s\n", value);
return;
}
strcpy(filters.method_filters[filters.method_count].pattern, value);
filters.method_filters[filters.method_count].mode = mode;
filters.method_count++;
printf("DEBUG: Methoden-Filter hinzugefügt: %s%s\n", mode == FILTER_EXCLUDE ? "!" : "", value);
}
// gleiche Mechanik wie bei IP-Adresse
void add_useragent_filter(const char* value, filter_mode_t mode) {
if (filters.user_agent_count >= MAX_FILTERS) {
printf("WARNING: MAX_FILTERS überschritten, ignoriere: %s\n", value);
return;
}
if (strlen(value) >= sizeof(filters.user_agent_filters[0].pattern)) {
printf("WARNING: User agent Filterwert zu lang: %s\n", value);
return;
}
strcpy(filters.user_agent_filters[filters.user_agent_count].pattern, value);
filters.user_agent_filters[filters.user_agent_count].mode = mode;
filters.user_agent_count++;
printf("DEBUG: User Agent Filter hinzugefügt: %s%s\n", mode == FILTER_EXCLUDE ? "!" : "", value);
}
// gleiche Mechanik wie bei IP-Adresse
void add_url_filter(const char* value, filter_mode_t mode) {
if (filters.url_count >= MAX_FILTERS) {
printf("WARNING: MAX_FILTERS überschritten, ignoriere: %s\n", value);
return;
}
if (strlen(value) >= sizeof(filters.url_filters[0].pattern)) {
printf("WARNING: URL/Payload Filterwert zu lang: %s\n", value);
return;
}
strcpy(filters.url_filters[filters.url_count].pattern, value);
filters.url_filters[filters.url_count].mode = mode;
filters.url_count++;
printf("DEBUG: URL/Payload-Filter hinzugefügt: %s%s\n", mode == FILTER_EXCLUDE ? "!" : "", value);
}
// Filter-Argument Parser // Filter-Argument Parser
int parse_filter_argument(const char* arg) { int parse_filter_argument(char* arg) {
if (!starts_with(arg, "--")) { if (!starts_with(arg, "--")) {
return 0; return 0;
} }
// = finden // = finden, strchr gibt den Pointer auf das = zurück
const char* equals_pos = strchr(arg, '='); char* equals_pos = strchr(arg, '=');
if (equals_pos == NULL) { if (equals_pos == NULL) {
printf("WARNING: Invalid filter format (missing =): %s\n", arg); printf("WARNING: Ungültiges Filter-Format, kein '=' gefunden: %s\n", arg);
return 0; return 0;
} }
// filter-Typ parsen //int filterstr_start = arg -2;
int type_len = equals_pos - arg - 2; // Position anpassen, // filter-Typ parsen: 1. Länge des Strings errechnen (Position des '=' abzüglich Startposition des Strings, abzüglich 2 (für die '--' am Anfang))
int filterstr_length = equals_pos - arg -2;
// 2.
char filter_type[50]; char filter_type[50];
strncpy(filter_type, arg + 2, type_len); strncpy(filter_type, arg +2, filterstr_length);
filter_type[type_len] = '\0'; filter_type[filterstr_length] = '\0';
// Extract values (after =) // Filter-Werte (nach den =)
const char* values = equals_pos + 1; char* values = equals_pos + 1;
// Parse based on filter type if (strstr(filter_type, "status") != NULL) {
if (strcmp(filter_type, "status") == 0) { parse_filter_values(values, "status");
parse_filter_values(values, add_status_filter); } else if (strstr(filter_type, "ip") != NULL) {
} else if (strcmp(filter_type, "ip") == 0) { parse_filter_values(values, "ip");
parse_filter_values(values, add_ip_filter); } else if (strstr(filter_type, "method") != NULL) {
} else if (strcmp(filter_type, "method") == 0) { parse_filter_values(values, "method");
parse_filter_values(values, add_method_filter); } else if (strstr(filter_type, "useragent") != NULL) {
} else if (strcmp(filter_type, "useragent") == 0) { parse_filter_values(values, "useragent");
parse_filter_values(values, add_useragent_filter); } else if (strstr(filter_type, "url") != NULL) {
} else if (strcmp(filter_type, "url") == 0) { parse_filter_values(values, "url");
parse_filter_values(values, add_url_filter); } else if (strstr(filter_type, "mode") != NULL) {
} else if (strcmp(filter_type, "mode") == 0) { if (strstr(values, "and") != NULL || strstr(values, "AND") != NULL) {
if (strcmp(values, "and") == 0 || strcmp(values, "AND") == 0) {
filters.combination_mode = 0; filters.combination_mode = 0;
printf("Set filter combination mode: AND\n"); printf("DEBUG: AND-Modus gesetzt\n");
} else if (strcmp(values, "or") == 0 || strcmp(values, "OR") == 0) { } else if (strstr(values, "or") != NULL || strstr(values, "OR") != NULL) {
filters.combination_mode = 1; filters.combination_mode = 1;
printf("Set filter combination mode: OR\n"); printf("DEBUG: OR-Modus gesetzt\n");
} else { } else {
printf("WARNING: Invalid mode value: %s (use 'and' or 'or')\n", values); printf("WARNING: ungültiger Modus-Wert: %s ('and' oder 'oder' möglich)\n", values);
} }
} else { } else {
printf("WARNING: Unknown filter type: %s\n", filter_type); printf("WARNING: Unbekannter Filtertyp: %s\n", filter_type);
return 0; return 0;
} }
return 1; // Successfully parsed return 1;
}
// Add this to your main() function after allocate_initial_memory()
void parse_command_line_filters(int argc, char* argv[]) {
int has_filter_flag = 0;
// First, check if -f flag is present
for (int i = 2; i < argc; i++) {
if (strcmp(argv[i], "-f") == 0) {
has_filter_flag = 1;
break;
}
}
if (!has_filter_flag) {
return; // No filter flag, skip filter parsing
}
printf("Parsing command line filters...\n");
// Parse all filter arguments
for (int i = 2; i < argc; i++) {
if (starts_with(argv[i], "--")) {
parse_filter_argument(argv[i]);
}
}
// Show summary of parsed filters
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("Successfully parsed %d filters from command line.\n", total_filters);
} else {
printf("No valid filters found in command line arguments.\n");
}
} }
void print_help(char* binary) { void print_help(char* binary) {
@ -2415,6 +2170,7 @@ void print_help(char* binary) {
printf("Verwendung:\n"); printf("Verwendung:\n");
printf(" %s <LOGDATEI|VERZEICHNIS> -i Interaktiver Modus (Filter/Analyse/Export)\n", binary); printf(" %s <LOGDATEI|VERZEICHNIS> -i Interaktiver Modus (Filter/Analyse/Export)\n", binary);
printf(" %s <LOGDATEI|VERZEICHNIS> -e <DATEINAME(optional)> Export generieren\n", binary); printf(" %s <LOGDATEI|VERZEICHNIS> -e <DATEINAME(optional)> Export generieren\n", binary);
printf(" %s <LOGDATEI|VERZEICHNIS> -f [FILTER...] Mit Kommandozeilen-Filtern\n", binary);
printf(" %s -h Diese Hilfe anzeigen\n", binary); printf(" %s -h Diese Hilfe anzeigen\n", binary);
printf("\nArgumente:\n"); printf("\nArgumente:\n");
@ -2425,8 +2181,29 @@ void print_help(char* binary) {
printf(" -i Startet interaktiven Modus mit Filtern (Status, Methode, IP, Zeitraum,\n"); printf(" -i Startet interaktiven Modus mit Filtern (Status, Methode, IP, Zeitraum,\n");
printf(" User-Agent, URL-Teilstring), Vorschau, Statistiken und CSV-Export (Timesketch).\n"); printf(" User-Agent, URL-Teilstring), Vorschau, Statistiken und CSV-Export (Timesketch).\n");
printf(" -e Generiert Timesketch-kompatible CSV-Datei, nimmt optional Dateinamen entgegen.\n"); printf(" -e Generiert Timesketch-kompatible CSV-Datei, nimmt optional Dateinamen entgegen.\n");
printf(" -f Aktiviert Kommandozeilen-Filter. Muss von Filter-Argumenten gefolgt werden.\n");
printf(" -h Zeigt diese Hilfe an.\n"); printf(" -h Zeigt diese Hilfe an.\n");
printf("\nKOMMNDOZEILEN-FILTER (nur mit -f Flag):\n");
printf(" --status=CODE[,CODE...] HTTP-Status-Codes (z.B. 200,404,500)\n");
printf(" --ip=ADRESSE[,ADRESSE...] IP-Adressen (exakte Übereinstimmung)\n");
printf(" --method=METHODE[,METHODE...] HTTP-Methoden (z.B. GET,POST,ATYPICAL)\n");
printf(" --useragent=TEXT[,TEXT...] User-Agent Teilstrings (z.B. bot,crawler)\n");
printf(" --url=PFAD[,PFAD...] URL-Pfad Teilstrings (z.B. .git,.php,wp-)\n");
printf(" --mode=MODE Filtermodus: 'and' oder 'or' (Standard: and)\n");
printf("\nFILTER-SYNTAX:\n");
printf(" Einschluss: Wert (nur Einträge MIT diesem Wert)\n");
printf(" Ausschluss: !Wert (alle Einträge OHNE diesen Wert)\n");
printf(" Mehrere: Wert1,Wert2 (kommagetrennt, keine Leerzeichen)\n");
printf(" Gemischt: Wert1,!Wert2 (Wert1 einschließen, Wert2 ausschließen)\n");
printf("\nFILTER-LOGIK:\n");
printf(" - Ausschluss-Filter (!) haben IMMER Vorrang vor Einschluss-Filtern\n");
printf(" - AND-Modus: ALLE Einschluss-Filter pro Kategorie müssen zutreffen\n");
printf(" - OR-Modus: MINDESTENS EIN Einschluss-Filter pro Kategorie muss zutreffen\n");
printf(" - Ausschluss-Filter arbeiten kategorie-intern immer im OR-Modus\n");
printf("\nUnterstützte Eingaben:\n"); printf("\nUnterstützte Eingaben:\n");
printf(" - Normale NGINX-Access-Logs: *.log\n"); printf(" - Normale NGINX-Access-Logs: *.log\n");
printf(" - Rotierte Logs: *.log.1, *.log.2, ... (rein textbasiert)\n"); printf(" - Rotierte Logs: *.log.1, *.log.2, ... (rein textbasiert)\n");
@ -2443,12 +2220,6 @@ void print_help(char* binary) {
printf("\nBeispielzeile (eine Zeile pro Request):\n"); printf("\nBeispielzeile (eine Zeile pro Request):\n");
printf(" 0.0.0.0 - - [31/Aug/2025:00:11:42 +0000] \"GET /.git/config HTTP/1.1\" 400 255 \"-\" \"Mozilla/5.0\" \"-\"\n"); printf(" 0.0.0.0 - - [31/Aug/2025:00:11:42 +0000] \"GET /.git/config HTTP/1.1\" 400 255 \"-\" \"Mozilla/5.0\" \"-\"\n");
printf("\nBeispiele:\n");
printf(" - Einzeldatei analysieren:\n");
printf(" %s /var/log/nginx/access.log -i\n", binary);
printf(" - Verzeichnis mit mehreren .log / .log.N Dateien:\n");
printf(" %s /var/log/nginx/ -i\n", binary);
printf("\nCSV-Export (Timesketch-kompatibel):\n"); printf("\nCSV-Export (Timesketch-kompatibel):\n");
printf(" Spalten: datetime, timestamp_desc, ip_address, method, url_path, status_code,\n"); printf(" Spalten: datetime, timestamp_desc, ip_address, method, url_path, status_code,\n");
printf(" bytes_sent, user_agent, source_file, parsing_timestamp\n"); printf(" bytes_sent, user_agent, source_file, parsing_timestamp\n");
@ -2457,9 +2228,9 @@ void print_help(char* binary) {
printf(" - Parser erwartet das obige Standardformat. Abweichungen können zum Abbruch führen.\n"); printf(" - Parser erwartet das obige Standardformat. Abweichungen können zum Abbruch führen.\n");
printf(" - Zeitzone aus dem Log wird gelesen, aber intern als einfache Felder verarbeitet.\n"); printf(" - Zeitzone aus dem Log wird gelesen, aber intern als einfache Felder verarbeitet.\n");
printf(" - ATYPICAL-Methode: für fehlerhafte/binäre Requests innerhalb der \"request\"-Spalte.\n"); printf(" - ATYPICAL-Methode: für fehlerhafte/binäre Requests innerhalb der \"request\"-Spalte.\n");
printf(" - Zeitraum-Filter sind nur im interaktiven Modus verfügbar (nicht über -f).\n");
} }
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
if (argc < 3) { if (argc < 3) {
print_help(argv[0]); print_help(argv[0]);
@ -2472,7 +2243,6 @@ int main(int argc, char* argv[]) {
int flag_interactive = 0; int flag_interactive = 0;
int flag_export = 0; int flag_export = 0;
int flag_filters = 0;
int flag_help = 0; int flag_help = 0;
char export_filename[90]; char export_filename[90];
@ -2491,8 +2261,15 @@ int main(int argc, char* argv[]) {
flag_has_filename = 1; flag_has_filename = 1;
i++; // Schleife weiter iterieren i++; // Schleife weiter iterieren
} }
} else if (strcmp(argv[i], "--filters")==0) { } else if (strcmp(argv[i], "-f")==0) {
flag_filters = 1; // parsen der nachfolgenden Argumente --ip= usw.
for (int j = i + 1; j < argc; j++) {
if (starts_with(argv[j], "--")) {
parse_filter_argument(argv[j]);
}else{
break;
}
}
} else if (strcmp(argv[i], "-h")==0) { } else if (strcmp(argv[i], "-h")==0) {
flag_help = 1; flag_help = 1;
@ -2506,18 +2283,14 @@ int main(int argc, char* argv[]) {
load_log_file(argv[1]); load_log_file(argv[1]);
if (total_entries == 0) { if (total_entries == 0) {
printf("Keine gültigen Log-Einträge gefunden. Überprüfen Sie den Pfad und die Dateiformate.\n"); printf("ERROR: Keine gültigen Log-Einträge gefunden. Überprüfen Sie den Pfad und die Dateiformate.\n");
cleanup_memory(); cleanup_memory();
return 1; return 1;
} }
int choice = 0; int choice = 0;
int stats_show = 0; // Standardnavigation aus read_menu_input
while (choice != 4 && choice != -4) { while (choice != 4 && choice != -4) {
show_status(); show_status();
if (stats_show == 1) {
show_stats();
stats_show = 0;
}
show_main_menu(); show_main_menu();
choice = read_menu_input(); choice = read_menu_input();
@ -2533,7 +2306,7 @@ int main(int argc, char* argv[]) {
} else if (choice == 2) { } else if (choice == 2) {
menu_show_entries(); menu_show_entries();
} else if (choice == 3) { } else if (choice == 3) {
stats_show = 1; export_filtered_entries(0);
} else if (choice == 4) { } else if (choice == 4) {
printf("Programmende\n"); printf("Programmende\n");
break; break;