From d62be280a151883866554e9ea00a5efed2fa593a Mon Sep 17 00:00:00 2001 From: Mosder Date: Wed, 17 Jun 2026 22:55:55 +0200 Subject: feat: added simple reading of packets from file and printing the payloads. TODO: utilize TCP sequence number to make sure the stream is read in order and parse the packets without reassemblying the entire stream (which might be enormous), but keeping in mind that entire fight result might be accross multiple segments --- analyzer/analyzer.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 analyzer/analyzer.c (limited to 'analyzer/analyzer.c') diff --git a/analyzer/analyzer.c b/analyzer/analyzer.c new file mode 100644 index 0000000..7790f41 --- /dev/null +++ b/analyzer/analyzer.c @@ -0,0 +1,36 @@ +#include "analyzer.h" + +#include + +#include "utils/pcap.h" + +void analyze_file(char *path) { + // open the file + char errbuf[PCAP_ERRBUF_SIZE]; + pcap_t *handle = pcap_open_offline(path, errbuf); + + // handle errors + if (handle == NULL) { + printf("Couldn't open the specified file:\n%s\n", errbuf); + return; + } + + struct pcap_pkthdr *packet_data; + const unsigned char *packet; + const unsigned char *payload; + + // read packets from the file + while (pcap_next_ex(handle, &packet_data, &packet) == 1) { + int payload_len; + payload = get_payload_from_packet(packet, packet_data, &payload_len); + if (payload_len > 0) { + printf("\n----------------\n"); + printf("Payload length: %d\n", payload_len); + printf("Payload:\n"); + fflush(stdout); + write(STDOUT_FILENO, payload, payload_len); + } + } + + pcap_close(handle); +} -- cgit v1.2.3 From f57066460f7c914b07a612d037eb5e57f617d5b2 Mon Sep 17 00:00:00 2001 From: Mosder Date: Mon, 6 Jul 2026 10:06:10 +0200 Subject: feat: added basic structures for fight results parsing and base functionallity of finding only the necessary data. Still need to do tcp stream reassembly and the actual parsing of the data. --- Makefile | 6 ++- analyzer/analyzer.c | 16 +++---- analyzer/parser/fight_results.c | 18 +++++++ analyzer/parser/fight_results.h | 102 ++++++++++++++++++++++++++++++++++++++++ analyzer/parser/parser.c | 15 ++++++ analyzer/parser/parser.h | 21 +++++++++ analyzer/utils/pcap.c | 5 +- analyzer/utils/pcap.h | 3 +- 8 files changed, 173 insertions(+), 13 deletions(-) create mode 100644 analyzer/parser/fight_results.c create mode 100644 analyzer/parser/fight_results.h create mode 100644 analyzer/parser/parser.c create mode 100644 analyzer/parser/parser.h (limited to 'analyzer/analyzer.c') diff --git a/Makefile b/Makefile index d0a760e..f298b4d 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ global: compile local: compile mv branal ${HOME}/.local/bin -compile: utils capture analyzer +compile: utils capture analyzer parser gcc *.o main.c -lpcap -o branal rm *.o @@ -19,4 +19,8 @@ capture: analyzer: gcc -c analyzer/analyzer.c +parser: + gcc -c analyzer/parser/parser.c + gcc -c analyzer/parser/fight_results.c + .PHONY: analyzer diff --git a/analyzer/analyzer.c b/analyzer/analyzer.c index 7790f41..acbd5de 100644 --- a/analyzer/analyzer.c +++ b/analyzer/analyzer.c @@ -1,7 +1,6 @@ #include "analyzer.h" -#include - +#include "parser/parser.h" #include "utils/pcap.h" void analyze_file(char *path) { @@ -22,14 +21,11 @@ void analyze_file(char *path) { // read packets from the file while (pcap_next_ex(handle, &packet_data, &packet) == 1) { int payload_len; - payload = get_payload_from_packet(packet, packet_data, &payload_len); - if (payload_len > 0) { - printf("\n----------------\n"); - printf("Payload length: %d\n", payload_len); - printf("Payload:\n"); - fflush(stdout); - write(STDOUT_FILENO, payload, payload_len); - } + uint32_t tcp_seq; + payload = get_payload_from_packet(packet, packet_data, &payload_len, &tcp_seq); + // TODO: Stream reassembly using TCP sequence number + int parsed_len; + parse_stream((char *)payload, payload_len, &parsed_len); } pcap_close(handle); diff --git a/analyzer/parser/fight_results.c b/analyzer/parser/fight_results.c new file mode 100644 index 0000000..c6ad7cc --- /dev/null +++ b/analyzer/parser/fight_results.c @@ -0,0 +1,18 @@ +#include "fight_results.h" + +#include +#include + +char *find_fight_results(char *stream, int stream_len, int *length) { + char *start = memmem(stream, stream_len, FIGHT_RESULTS_START, strlen(FIGHT_RESULTS_START)); + char *end = memmem(stream, stream_len, FIGHT_RESULTS_END, strlen(FIGHT_RESULTS_END)); + *length = start != NULL && end != NULL ? end - start + strlen(FIGHT_RESULTS_END) : -1; + return start; +} + +FightResults parse_fight_results(char *data, int length) { + // TODO: actually parse that data + printf("len=%d\n%.*s\n----------------\n", length, length, data); + FightResults tmp; + return tmp; +} diff --git a/analyzer/parser/fight_results.h b/analyzer/parser/fight_results.h new file mode 100644 index 0000000..270aba8 --- /dev/null +++ b/analyzer/parser/fight_results.h @@ -0,0 +1,102 @@ +// parser/fight_results.h +// utilities for parsing the fight results data + +// Payload structure: +// FIGHT_RESULTS_START EntityResults (SEPARATOR_ENTITY EntityResults)* FIGHT_RESULTS_END +// +// EntityResults structure: +// field (SEPARATOR_RESULT field)* +// fields are defined in order in the EntityResults struct + +#define FIGHT_RESULTS_START "3;19;" +#define FIGHT_RESULTS_END "[--];" +#define SEPARATOR_ENTITY "[--]" +#define SEPARATOR_RESULT "&" + +typedef struct { + int team; // friend or enemy + char *name; + int exp; + float field4; + int gold; + float field6; + char *field7; + char *gear; // rares, sets, syngs, orbs + int field9; + char *items; // regular items + int field11; + int field12; + float field13; + float field14; // some multiplier most likely exp/gold/psycho/something else idk + float field15; + int level; + double field17; + float field18; + int field19; + int field20; + float field21; + float field22; + int field23; + int field24; + int psycho; + char *drifs; + int splinters; + int field28; + char *field29; + int field30; + int field31; // possibly information who is the player - 1 for current player, 0 for others (need to double check) + int field32; + int field33; + char *field34; + char *saturation; +} EntityResults; + +// values of field "team" +#define TEAM_FRIEND 1 +#define TEAM_ENEMY 2 + +// fight results data for friendly +typedef struct { + char *name; + int exp; + int gold; + char *gear; + char *items; + int level; + int psycho; + char *drifs; + int splinters; + char *saturation_type; + int saturation; +} FriendlyResults; + +// fight results data for enemy +typedef struct { + char *name; + int level; +} EnemyResults; + +// entire fight resutls +typedef struct { + int num_friendly; + FriendlyResults *friendly_results; + int num_enemy; + EnemyResults *enemy_results; +} FightResults; + +// find the first fight results data in stream +// params: +// - stream - stream to find the fight results in +// - stream_len - length of the stream +// - length - length of the fight results data or -1 if there's no end yet +// returns: +// pointer to the beggining of fight results data or NULL if there's none +extern char *find_fight_results(char *stream, int stream_len, int *length); + +// parse given fight results +// params: +// - data - fight results data to parse +// - length - length of fight results data +// returns: +// FightResults struct containing parsed data +extern FightResults parse_fight_results(char *data, int length); diff --git a/analyzer/parser/parser.c b/analyzer/parser/parser.c new file mode 100644 index 0000000..b551d8e --- /dev/null +++ b/analyzer/parser/parser.c @@ -0,0 +1,15 @@ +#include "parser.h" + +#include + +ParsedData *parse_stream(char *stream, int stream_len, int *parsed_len) { + // TODO: Need to do parsing of the entire thing (loop) and return all of the parsed data + if (stream_len > 0) { + int length; + char *start = find_fight_results(stream, stream_len, &length); + if (start != NULL) { + parse_fight_results(start, length); + } + } + return NULL; +} diff --git a/analyzer/parser/parser.h b/analyzer/parser/parser.h new file mode 100644 index 0000000..18b4b83 --- /dev/null +++ b/analyzer/parser/parser.h @@ -0,0 +1,21 @@ +// parser/parser.h +// main parser file + +#include "fight_results.h" + +// "generic" type for parsed data +typedef struct { + enum { FIGHT_RESULTS } data_type; + union { + FightResults fight_results; + } data; +} ParsedData; + +// parse a stream of TCP data +// params: +// - stream - the stream to parse +// - stream_len - length of the stream to parse +// - parsed_len - pointer to inform how much of the stream was parsed (not all if e.g. start of data is there, but the end isn't yet) +// returns: +// pointer to the array of parsed data +extern ParsedData *parse_stream(char *stream, int stream_len, int *parsed_len); diff --git a/analyzer/utils/pcap.c b/analyzer/utils/pcap.c index cdefba7..5e9b611 100644 --- a/analyzer/utils/pcap.c +++ b/analyzer/utils/pcap.c @@ -4,7 +4,7 @@ #include #include -const unsigned char *get_payload_from_packet(const unsigned char *packet, const struct pcap_pkthdr *packet_data, int *payload_len) { +const unsigned char *get_payload_from_packet(const unsigned char *packet, const struct pcap_pkthdr *packet_data, int *payload_len, uint32_t *tcp_seq) { // get Ethernet header const struct ether_header *eth_header = (const struct ether_header *)packet; int eth_header_len = sizeof(struct ether_header); @@ -29,6 +29,9 @@ const unsigned char *get_payload_from_packet(const unsigned char *packet, const const struct tcphdr *tcp_header = (const struct tcphdr *)(packet + eth_header_len + ip_header_len); int tcp_header_len = tcp_header->th_off * 4; + // get TCP sequence number + *tcp_seq = ntohl(tcp_header->seq); + // get payload int total_header_len = eth_header_len + ip_header_len + tcp_header_len; *payload_len = packet_data->caplen - total_header_len; diff --git a/analyzer/utils/pcap.h b/analyzer/utils/pcap.h index d31b961..16da7cd 100644 --- a/analyzer/utils/pcap.h +++ b/analyzer/utils/pcap.h @@ -8,6 +8,7 @@ // - packet - captured packet // - packet_data - metadata about packet // - payload_len - pointer to save the payload length to or -1 if not a correct packet +// - tcp_seq - pointer to save the TCP sequence number // returns: // pointer to the payload or NULL if not a correct packet -extern const unsigned char *get_payload_from_packet(const unsigned char *packet, const struct pcap_pkthdr *packet_data, int *payload_len); +extern const unsigned char *get_payload_from_packet(const unsigned char *packet, const struct pcap_pkthdr *packet_data, int *payload_len, uint32_t *tcp_seq); -- cgit v1.2.3 From 54fdf207352a0ee44a8264972118f6cc9d30cad1 Mon Sep 17 00:00:00 2001 From: Mosder Date: Fri, 10 Jul 2026 10:17:47 +0200 Subject: feat: defined unsigned char as byte_t --- analyzer/analyzer.c | 4 ++-- analyzer/utils/pcap.c | 11 ++++------- analyzer/utils/pcap.h | 15 ++++++++------- 3 files changed, 14 insertions(+), 16 deletions(-) (limited to 'analyzer/analyzer.c') diff --git a/analyzer/analyzer.c b/analyzer/analyzer.c index acbd5de..7db377e 100644 --- a/analyzer/analyzer.c +++ b/analyzer/analyzer.c @@ -15,8 +15,8 @@ void analyze_file(char *path) { } struct pcap_pkthdr *packet_data; - const unsigned char *packet; - const unsigned char *payload; + const byte_t *packet; + const byte_t *payload; // read packets from the file while (pcap_next_ex(handle, &packet_data, &packet) == 1) { diff --git a/analyzer/utils/pcap.c b/analyzer/utils/pcap.c index d9687b3..d38cbff 100644 --- a/analyzer/utils/pcap.c +++ b/analyzer/utils/pcap.c @@ -4,7 +4,7 @@ #include #include -const unsigned char *get_payload_from_packet(const unsigned char *packet, const struct pcap_pkthdr *packet_data, int *payload_len, uint32_t *tcp_seq) { +const byte_t *get_payload_from_packet(const byte_t *packet, const struct pcap_pkthdr *packet_data, int *payload_len, uint32_t *tcp_seq) { // get Ethernet header const struct ether_header *eth_header = (const struct ether_header *)packet; int eth_header_len = sizeof(struct ether_header); @@ -38,20 +38,17 @@ const unsigned char *get_payload_from_packet(const unsigned char *packet, const return packet + total_header_len; } -PendingPayload new_pending(const unsigned char *payload, int payload_len) { - // TODO: impement +PendingPayload new_pending(const byte_t *payload, int payload_len) { PendingPayload tmp; return tmp; } -unsigned char *free_parsed_data(unsigned char *stream, uint32_t stream_seq, int stream_len, int parsed_len) { +byte_t *free_parsed_data(byte_t *stream, uint32_t stream_seq, int stream_len, int parsed_len) { // TODO: impement return NULL; } -int add_segment_to_stream( - unsigned char *stream, uint32_t stream_seq, int stream_len, const unsigned char *payload, uint32_t payload_seq, int payload_len, HashMap *pending -) { +int add_segment_to_stream(byte_t *stream, uint32_t stream_seq, int stream_len, const byte_t *payload, uint32_t payload_seq, int payload_len, HashMap *pending) { // TODO: impement return 0; } diff --git a/analyzer/utils/pcap.h b/analyzer/utils/pcap.h index a423083..10f01f1 100644 --- a/analyzer/utils/pcap.h +++ b/analyzer/utils/pcap.h @@ -5,6 +5,8 @@ #include "hashmap.h" +typedef unsigned char byte_t; + // get payload from a TCP/IP packet // params: // - packet - captured packet @@ -13,11 +15,11 @@ // - tcp_seq - pointer to save the TCP sequence number // returns: // pointer to the payload or NULL if not a correct packet -extern const unsigned char *get_payload_from_packet(const unsigned char *packet, const struct pcap_pkthdr *packet_data, int *payload_len, uint32_t *tcp_seq); +extern const byte_t *get_payload_from_packet(const byte_t *packet, const struct pcap_pkthdr *packet_data, int *payload_len, uint32_t *tcp_seq); // struct to persistently store pending payloads typedef struct { - unsigned char *payload; + byte_t *payload; int payload_len; } PendingPayload; @@ -27,7 +29,7 @@ typedef struct { // - payload_len - length of the payload to store // returns: // PendingPayload struct with stored payload -extern PendingPayload new_pending(const unsigned char *payload, int payload_len); +extern PendingPayload new_pending(const byte_t *payload, int payload_len); // remove parsed part of the stream from memory and return only unparsed part // params: @@ -37,7 +39,7 @@ extern PendingPayload new_pending(const unsigned char *payload, int payload_len) // - parsed_len - count of n first bytes of the stream that were parsed // returns: // pointer to the new, reduced stream -extern unsigned char *free_parsed_data(unsigned char *stream, uint32_t stream_seq, int stream_len, int parsed_len); +extern byte_t *free_parsed_data(byte_t *stream, uint32_t stream_seq, int stream_len, int parsed_len); // add TCP segment to stream // params: @@ -50,6 +52,5 @@ extern unsigned char *free_parsed_data(unsigned char *stream, uint32_t stream_se // - pending - hashmap (seq -> PendingPayload) of pending payloads to be added to stream // returns: // 1 if the payload was added to the stream, 0 if it was discarded or saved to pending -extern int add_segment_to_stream( - unsigned char *stream, uint32_t stream_seq, int stream_len, const unsigned char *payload, uint32_t payload_seq, int payload_len, HashMap *pending -); +extern int +add_segment_to_stream(byte_t *stream, uint32_t stream_seq, int stream_len, const byte_t *payload, uint32_t payload_seq, int payload_len, HashMap *pending); -- cgit v1.2.3 From 0e9be0d902b44894ed747cabb82ea2120f2e4b45 Mon Sep 17 00:00:00 2001 From: Mosder Date: Fri, 10 Jul 2026 22:50:12 +0200 Subject: feat: added functionallity for TCP stream reassembly --- Makefile | 9 +- analyzer/analyzer.c | 45 ++++++---- analyzer/parser/fight_results.c | 10 +-- analyzer/parser/fight_results.h | 9 +- analyzer/parser/parser.c | 19 +++-- analyzer/parser/parser.h | 6 +- analyzer/utils/pcap.c | 54 ------------ analyzer/utils/pcap.h | 56 ------------- analyzer/utils/structures/heap.h | 3 + analyzer/utils/tcp.c | 172 +++++++++++++++++++++++++++++++++++++++ analyzer/utils/tcp.h | 68 ++++++++++++++++ analyzer/utils/tcp_seq_math.c | 41 ++++++++++ analyzer/utils/tcp_seq_math.h | 28 +++++++ 13 files changed, 375 insertions(+), 145 deletions(-) delete mode 100644 analyzer/utils/pcap.c delete mode 100644 analyzer/utils/pcap.h create mode 100644 analyzer/utils/tcp.c create mode 100644 analyzer/utils/tcp.h create mode 100644 analyzer/utils/tcp_seq_math.c create mode 100644 analyzer/utils/tcp_seq_math.h (limited to 'analyzer/analyzer.c') diff --git a/Makefile b/Makefile index f298b4d..e5a9c54 100644 --- a/Makefile +++ b/Makefile @@ -8,10 +8,15 @@ compile: utils capture analyzer parser gcc *.o main.c -lpcap -o branal rm *.o -utils: +utils: structures gcc -c analyzer/utils/fs.c gcc -c analyzer/utils/time.c - gcc -c analyzer/utils/pcap.c + gcc -c analyzer/utils/tcp.c + gcc -c analyzer/utils/tcp_seq_math.c + +structures: + gcc -c analyzer/utils/structures/hashmap.c + gcc -c analyzer/utils/structures/heap.c capture: gcc -c analyzer/capture.c diff --git a/analyzer/analyzer.c b/analyzer/analyzer.c index 7db377e..2e5caf8 100644 --- a/analyzer/analyzer.c +++ b/analyzer/analyzer.c @@ -1,7 +1,32 @@ #include "analyzer.h" #include "parser/parser.h" -#include "utils/pcap.h" +#include "utils/tcp.h" + +// loop through packets received from pcap_handle and analyze the TCP stream +void analyzer_loop(TCPStream *stream, pcap_t *pcap_handle) { + // read packets from handle + struct pcap_pkthdr *packet_data; + const byte_t *packet; + while (pcap_next_ex(pcap_handle, &packet_data, &packet) == 1) { + // get new segment + TCPSegment segment = get_segment_from_packet(packet, packet_data); + + // handle the new segment + if (handle_segment(stream, segment)) { + // if it was added to the stream - parse it + size_t parsed_len; + size_t n_parsed_data; + ParsedData *parsed_data = parse_stream(stream, &parsed_len, &n_parsed_data); + + // if some bytes were parsed - remove them from the stream + if (parsed_len > 0) + remove_parsed_data(stream, parsed_len); + + // TODO: handle parsed data + } + } +} void analyze_file(char *path) { // open the file @@ -14,19 +39,9 @@ void analyze_file(char *path) { return; } - struct pcap_pkthdr *packet_data; - const byte_t *packet; - const byte_t *payload; - - // read packets from the file - while (pcap_next_ex(handle, &packet_data, &packet) == 1) { - int payload_len; - uint32_t tcp_seq; - payload = get_payload_from_packet(packet, packet_data, &payload_len, &tcp_seq); - // TODO: Stream reassembly using TCP sequence number - int parsed_len; - parse_stream((char *)payload, payload_len, &parsed_len); - } - + // analyze the file + TCPStream stream = new_stream(); + analyzer_loop(&stream, handle); + destroy_stream(stream); pcap_close(handle); } diff --git a/analyzer/parser/fight_results.c b/analyzer/parser/fight_results.c index c6ad7cc..1db79ea 100644 --- a/analyzer/parser/fight_results.c +++ b/analyzer/parser/fight_results.c @@ -3,16 +3,16 @@ #include #include -char *find_fight_results(char *stream, int stream_len, int *length) { - char *start = memmem(stream, stream_len, FIGHT_RESULTS_START, strlen(FIGHT_RESULTS_START)); - char *end = memmem(stream, stream_len, FIGHT_RESULTS_END, strlen(FIGHT_RESULTS_END)); +byte_t *find_fight_results(TCPStream *stream, int *length) { + byte_t *start = memmem(stream->data, stream->len, FIGHT_RESULTS_START, strlen(FIGHT_RESULTS_START)); + byte_t *end = memmem(stream->data, stream->len, FIGHT_RESULTS_END, strlen(FIGHT_RESULTS_END)); *length = start != NULL && end != NULL ? end - start + strlen(FIGHT_RESULTS_END) : -1; return start; } -FightResults parse_fight_results(char *data, int length) { +FightResults parse_fight_results(byte_t *data, size_t length) { // TODO: actually parse that data - printf("len=%d\n%.*s\n----------------\n", length, length, data); + printf("len=%zu\n%.*s\n----------------\n", length, (int)length, data); FightResults tmp; return tmp; } diff --git a/analyzer/parser/fight_results.h b/analyzer/parser/fight_results.h index 270aba8..b275f2b 100644 --- a/analyzer/parser/fight_results.h +++ b/analyzer/parser/fight_results.h @@ -1,6 +1,8 @@ // parser/fight_results.h // utilities for parsing the fight results data +#include "../utils/tcp.h" + // Payload structure: // FIGHT_RESULTS_START EntityResults (SEPARATOR_ENTITY EntityResults)* FIGHT_RESULTS_END // @@ -87,11 +89,10 @@ typedef struct { // find the first fight results data in stream // params: // - stream - stream to find the fight results in -// - stream_len - length of the stream // - length - length of the fight results data or -1 if there's no end yet // returns: -// pointer to the beggining of fight results data or NULL if there's none -extern char *find_fight_results(char *stream, int stream_len, int *length); +// pointer to the beginning of fight results data or NULL if there's none +extern byte_t *find_fight_results(TCPStream *stream, int *length); // parse given fight results // params: @@ -99,4 +100,4 @@ extern char *find_fight_results(char *stream, int stream_len, int *length); // - length - length of fight results data // returns: // FightResults struct containing parsed data -extern FightResults parse_fight_results(char *data, int length); +extern FightResults parse_fight_results(byte_t *data, size_t length); diff --git a/analyzer/parser/parser.c b/analyzer/parser/parser.c index b551d8e..7fb155f 100644 --- a/analyzer/parser/parser.c +++ b/analyzer/parser/parser.c @@ -2,14 +2,19 @@ #include -ParsedData *parse_stream(char *stream, int stream_len, int *parsed_len) { +ParsedData *parse_stream(TCPStream *stream, size_t *parsed_len, size_t *n_parsed_data) { // TODO: Need to do parsing of the entire thing (loop) and return all of the parsed data - if (stream_len > 0) { - int length; - char *start = find_fight_results(stream, stream_len, &length); - if (start != NULL) { - parse_fight_results(start, length); - } + int length; + byte_t *start = find_fight_results(stream, &length); + if (start == NULL) { + *parsed_len = stream->len; + } + else if (length == -1) { + *parsed_len = 0; + } + else { + parse_fight_results(start, length); + *parsed_len = start - stream->data + length; } return NULL; } diff --git a/analyzer/parser/parser.h b/analyzer/parser/parser.h index 18b4b83..088bb5d 100644 --- a/analyzer/parser/parser.h +++ b/analyzer/parser/parser.h @@ -3,6 +3,8 @@ #include "fight_results.h" +#include "../utils/tcp.h" + // "generic" type for parsed data typedef struct { enum { FIGHT_RESULTS } data_type; @@ -14,8 +16,8 @@ typedef struct { // parse a stream of TCP data // params: // - stream - the stream to parse -// - stream_len - length of the stream to parse // - parsed_len - pointer to inform how much of the stream was parsed (not all if e.g. start of data is there, but the end isn't yet) +// - n_parsed_data - pointer to the count of parsed data that was retrieved from the stream // returns: // pointer to the array of parsed data -extern ParsedData *parse_stream(char *stream, int stream_len, int *parsed_len); +extern ParsedData *parse_stream(TCPStream *stream, size_t *parsed_len, size_t *n_parsed_data); diff --git a/analyzer/utils/pcap.c b/analyzer/utils/pcap.c deleted file mode 100644 index d38cbff..0000000 --- a/analyzer/utils/pcap.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "pcap.h" - -#include -#include -#include - -const byte_t *get_payload_from_packet(const byte_t *packet, const struct pcap_pkthdr *packet_data, int *payload_len, uint32_t *tcp_seq) { - // get Ethernet header - const struct ether_header *eth_header = (const struct ether_header *)packet; - int eth_header_len = sizeof(struct ether_header); - - // check if it's IP - if (ntohs(eth_header->ether_type) != ETHERTYPE_IP) { - *payload_len = -1; - return NULL; - } - - // get IP header - const struct ip *ip_header = (const struct ip *)(packet + eth_header_len); - int ip_header_len = ip_header->ip_hl * 4; - - // check if it's TCP - if (ip_header->ip_p != IPPROTO_TCP) { - *payload_len = -1; - return NULL; - } - - // get TCP header - const struct tcphdr *tcp_header = (const struct tcphdr *)(packet + eth_header_len + ip_header_len); - int tcp_header_len = tcp_header->th_off * 4; - - // get TCP sequence number - *tcp_seq = ntohl(tcp_header->seq); - - // get payload - int total_header_len = eth_header_len + ip_header_len + tcp_header_len; - *payload_len = packet_data->caplen - total_header_len; - return packet + total_header_len; -} - -PendingPayload new_pending(const byte_t *payload, int payload_len) { - PendingPayload tmp; - return tmp; -} - -byte_t *free_parsed_data(byte_t *stream, uint32_t stream_seq, int stream_len, int parsed_len) { - // TODO: impement - return NULL; -} - -int add_segment_to_stream(byte_t *stream, uint32_t stream_seq, int stream_len, const byte_t *payload, uint32_t payload_seq, int payload_len, HashMap *pending) { - // TODO: impement - return 0; -} diff --git a/analyzer/utils/pcap.h b/analyzer/utils/pcap.h deleted file mode 100644 index 10f01f1..0000000 --- a/analyzer/utils/pcap.h +++ /dev/null @@ -1,56 +0,0 @@ -// utils/pcap.h -// helper functions for usage of libpcap and for handling TCP segments in general - -#include - -#include "hashmap.h" - -typedef unsigned char byte_t; - -// get payload from a TCP/IP packet -// params: -// - packet - captured packet -// - packet_data - metadata about packet -// - payload_len - pointer to save the payload length to or -1 if not a correct packet -// - tcp_seq - pointer to save the TCP sequence number -// returns: -// pointer to the payload or NULL if not a correct packet -extern const byte_t *get_payload_from_packet(const byte_t *packet, const struct pcap_pkthdr *packet_data, int *payload_len, uint32_t *tcp_seq); - -// struct to persistently store pending payloads -typedef struct { - byte_t *payload; - int payload_len; -} PendingPayload; - -// store persistently and return a PendingPayload, the payload is malloced and needs to be freed -// params: -// - payload - payload to store -// - payload_len - length of the payload to store -// returns: -// PendingPayload struct with stored payload -extern PendingPayload new_pending(const byte_t *payload, int payload_len); - -// remove parsed part of the stream from memory and return only unparsed part -// params: -// - stream - stream to free a part of -// - stream_seq - pointer to the current TCP sequence number at the beginning of the stream, updated to the new one in the function -// - stream_len - pointer to the current stream length, updated to the new length in the function -// - parsed_len - count of n first bytes of the stream that were parsed -// returns: -// pointer to the new, reduced stream -extern byte_t *free_parsed_data(byte_t *stream, uint32_t stream_seq, int stream_len, int parsed_len); - -// add TCP segment to stream -// params: -// - stream - current stream -// - stream_seq - TCP sequence number at the beginning of the stream -// - stream_len - current length of the stream -// - payload - payload to add to stream -// - payload_seq - payload TCP sequence number -// - payload_len - length of the payload -// - pending - hashmap (seq -> PendingPayload) of pending payloads to be added to stream -// returns: -// 1 if the payload was added to the stream, 0 if it was discarded or saved to pending -extern int -add_segment_to_stream(byte_t *stream, uint32_t stream_seq, int stream_len, const byte_t *payload, uint32_t payload_seq, int payload_len, HashMap *pending); diff --git a/analyzer/utils/structures/heap.h b/analyzer/utils/structures/heap.h index e960fb7..eb3430b 100644 --- a/analyzer/utils/structures/heap.h +++ b/analyzer/utils/structures/heap.h @@ -6,6 +6,8 @@ #define INIT_HEAP_DEPTH 7 #define INIT_HEAP_CAPACITY (1 << INIT_HEAP_DEPTH) - 1 +#ifndef HEAP_DEFINED +#define HEAP_DEFINED typedef struct { void **data; size_t n_elements; @@ -14,6 +16,7 @@ typedef struct { int (*compare)(void *element1, void *element2); void (*element_cleanup)(void *element); } Heap; +#endif // create a new heap // params: diff --git a/analyzer/utils/tcp.c b/analyzer/utils/tcp.c new file mode 100644 index 0000000..56df842 --- /dev/null +++ b/analyzer/utils/tcp.c @@ -0,0 +1,172 @@ +#include "tcp.h" + +#include +#include +#include +#include +#include + +#include "tcp_seq_math.h" + +// initalize default TCP segment +TCPSegment new_segment() { + TCPSegment segment; + segment.payload = NULL; + segment.len = -1; + segment.seq = 0; + return segment; +} + +// segments compare function to use with heap +int segments_compare(void *segment1, void *segment2) { + TCPSegment *seg1 = segment1; + TCPSegment *seg2 = segment2; + return seq_before(seg1->seq, seg2->seq); +} + +// segment cleanup function to use with heap +void segment_cleanup(void *segment) { + TCPSegment *seg = segment; + free(seg->payload); +} + +TCPSegment get_segment_from_packet(const byte_t *packet, const struct pcap_pkthdr *packet_data) { + TCPSegment segment = new_segment(); + + // get Ethernet header + const struct ether_header *eth_header = (const struct ether_header *)packet; + int eth_header_len = sizeof(struct ether_header); + + // check if it's IP + if (ntohs(eth_header->ether_type) != ETHERTYPE_IP) + return segment; + + // get IP header + const struct ip *ip_header = (const struct ip *)(packet + eth_header_len); + int ip_header_len = ip_header->ip_hl * 4; + + // check if it's TCP + if (ip_header->ip_p != IPPROTO_TCP) + return segment; + + // get TCP header + const struct tcphdr *tcp_header = (const struct tcphdr *)(packet + eth_header_len + ip_header_len); + int tcp_header_len = tcp_header->th_off * 4; + + // get TCP sequence number + segment.seq = ntohl(tcp_header->seq); + + // get payload + int total_header_len = eth_header_len + ip_header_len + tcp_header_len; + segment.len = packet_data->caplen - total_header_len; + segment.payload = malloc(segment.len); + memcpy(segment.payload, packet + total_header_len, segment.len); + return segment; +} + +TCPStream new_stream() { + TCPStream stream; + stream.data = NULL; // NULL to differentiate between first and other segments + stream.len = 0; + stream.capacity = INIT_STREAM_CAPACITY; + stream.seq = 0; + stream.pending = heap_new(sizeof(TCPSegment), segments_compare, segment_cleanup); + return stream; +} + +void destroy_stream(TCPStream stream) { + free(stream.data); + heap_free(stream.pending); +} + +void remove_parsed_data(TCPStream *stream, size_t parsed_len) { + memmove(stream->data, stream->data + parsed_len, stream->len - parsed_len); + stream->len -= parsed_len; + stream->seq += parsed_len; +} + +// get the index of the first relevant byte in segment (its tcp sequence number is the one stream is expecting) +// return the index of the first relevant byte in payload or -1 if segment isn't directly connected to stream +int get_first_relevant_byte_index(TCPStream stream, TCPSegment segment) { + // expected tcp sequence numbers for stream and segment + uint32_t stream_expected = stream.seq + stream.len; + uint32_t segment_expected = segment.seq + segment.len; + if (seq_in_range_ie(stream_expected, segment.seq, segment_expected)) + return stream_expected - segment.seq; + return -1; +} + +// check if the segment is in acceptable range to the stream +int in_acceptable_range(TCPStream stream, TCPSegment segment) { + // expected tcp sequence number for stream and max acceptable sequence number for segment + uint32_t stream_expected = stream.seq + stream.len; + uint32_t max_acceptable_seq = stream_expected + MAX_SEQ_OFFSET; + return seq_in_range_ei(segment.seq, stream_expected, max_acceptable_seq); +} + +// expand stream capacity exponentially to fit new_len +void expand_stream_capacity(TCPStream *stream, size_t new_len) { + while (stream->capacity < new_len) + stream->capacity <<= 1; + stream->data = realloc(stream->data, stream->capacity); +} + +// add segment directly to stream +void add_segment(TCPStream *stream, TCPSegment segment, size_t data_start) { + size_t new_stream_len = stream->len + segment.len - data_start; + if (new_stream_len > stream->capacity) + expand_stream_capacity(stream, new_stream_len); + memcpy(stream->data + stream->len, segment.payload + data_start, segment.len - data_start); + stream->len = new_stream_len; +} + +// add pending segments to the stream +void add_pending_segments(TCPStream *stream) { + int top_changed = 1; + TCPSegment *top; + while (top_changed && (top = heap_top_get(stream->pending))) { + top_changed = 0; + + // add pending segment it it became directly connected + int data_start = get_first_relevant_byte_index(*stream, *top); + if (data_start != -1) { + add_segment(stream, *top, data_start); + top_changed = 1; + } + + // remove top if it was just added or no longer is in acceptable range (e.g. was contained within another one pending) + if (top_changed || !in_acceptable_range(*stream, *top)) { + heap_top_remove(stream->pending); + } + } +} + +int handle_segment(TCPStream *stream, TCPSegment segment) { + // add the segment if it's the first one + if (stream->data == NULL) { + stream->data = malloc(stream->capacity); + stream->seq = segment.seq; + add_segment(stream, segment, 0); + free(segment.payload); + return 1; + } + + // add segment directly to the stream if it's directly connected + int data_start = get_first_relevant_byte_index(*stream, segment); + if (data_start != -1) { + add_segment(stream, segment, data_start); + free(segment.payload); + add_pending_segments(stream); + return 1; + } + + // add to pending if segment is in acceptable range: + if (in_acceptable_range(*stream, segment)) { + heap_insert(stream->pending, &segment); + return 0; + } + + // if neither, discard entirely + free(segment.payload); + return 0; +} diff --git a/analyzer/utils/tcp.h b/analyzer/utils/tcp.h new file mode 100644 index 0000000..37ab4bc --- /dev/null +++ b/analyzer/utils/tcp.h @@ -0,0 +1,68 @@ +// utils/tcp.h +// functions for handling TCP streams/segments + +#include + +#include "structures/heap.h" + +typedef unsigned char byte_t; + +// struct to persistently store TCP segments +// payload field is malloced and needs to be freed +#ifndef TCP_SEGMENT_DEFINED +#define TCP_SEGMENT_DEFINED +typedef struct { + byte_t *payload; + size_t len; + uint32_t seq; +} TCPSegment; +#endif + +// get TCP segment from captured packet +// params: +// - packet - captured packet +// - packet_data - metadata about packet +// returns: +// TCPSegment from packet, payload is NULL and len is -1 if the received packet was incorrect +extern TCPSegment get_segment_from_packet(const byte_t *packet, const struct pcap_pkthdr *packet_data); + +// struct that stores all the information about stream +#ifndef TCP_STREAM_DEFINED +#define TCP_STREAM_DEFINED +typedef struct { + byte_t *data; // bytes in the stream + size_t len; // length of the stream + size_t capacity; // capacity of the data buffer + uint32_t seq; // TCP sequence number of the first byte in the stream + Heap *pending; // min heap of segments yet to be added to stream (sorted by TCP sequence number) +} TCPStream; +#endif + +#define INIT_STREAM_CAPACITY 256 + +// initialize new stream +// returns: +// newly created TCPStream structure with initial values +extern TCPStream new_stream(); + +// cleanup memory allocations within stream +// params: +// stream - stream to destroy +extern void destroy_stream(TCPStream stream); + +// remove parsed data from the stream +// params: +// - stream - pointer to the stream to remove a part of +// - parsed_len - count of n first bytes of the stream that were parsed +extern void remove_parsed_data(TCPStream *stream, size_t parsed_len); + +// max difference between expected TCP seq and received TCP seq to still accept it into pending +#define MAX_SEQ_OFFSET 100000 + +// add TCP segment to stream, pending or completely discard it +// params: +// - stream - pointer to the current stream +// - segment - segment to handle +// returns: +// 1 if the segment was added to the stream, 0 if it was discarded or saved to pending +extern int handle_segment(TCPStream *stream, TCPSegment segment); diff --git a/analyzer/utils/tcp_seq_math.c b/analyzer/utils/tcp_seq_math.c new file mode 100644 index 0000000..272cb5a --- /dev/null +++ b/analyzer/utils/tcp_seq_math.c @@ -0,0 +1,41 @@ +#include "tcp_seq_math.h" + +int seq_after(uint32_t a, uint32_t b) { + return (int32_t)(a - b) > 0; +} + +int seq_before(uint32_t a, uint32_t b) { + return (int32_t)(a - b) < 0; +} + +int seq_after_eq(uint32_t a, uint32_t b) { + return (int32_t)(a - b) >= 0; +} + +int seq_before_eq(uint32_t a, uint32_t b) { + return (int32_t)(a - b) <= 0; +} + +int seq_in_range_ii(uint32_t value, uint32_t range_start, uint32_t range_end) { + int after_eq = seq_after_eq(value, range_start); + int before_eq = seq_before_eq(value, range_end); + return after_eq && before_eq; +} + +int seq_in_range_ee(uint32_t value, uint32_t range_start, uint32_t range_end) { + int after = seq_after(value, range_start); + int before = seq_before(value, range_end); + return after && before; +} + +int seq_in_range_ie(uint32_t value, uint32_t range_start, uint32_t range_end) { + int after_eq = seq_after_eq(value, range_start); + int before = seq_before(value, range_end); + return after_eq && before; +} + +int seq_in_range_ei(uint32_t value, uint32_t range_start, uint32_t range_end) { + int after = seq_after(value, range_start); + int before_eq = seq_before_eq(value, range_end); + return after && before_eq; +} diff --git a/analyzer/utils/tcp_seq_math.h b/analyzer/utils/tcp_seq_math.h new file mode 100644 index 0000000..c4da5f9 --- /dev/null +++ b/analyzer/utils/tcp_seq_math.h @@ -0,0 +1,28 @@ +// utils/tcp_seq_math.h +// helper functions for math regarding tcp sequence numbers which correctly handle overflow + +#include + +// check if a is after b +extern int seq_after(uint32_t a, uint32_t b); + +// check if a is before b +extern int seq_before(uint32_t a, uint32_t b); + +// check if a is after or equal to b +extern int seq_after_eq(uint32_t a, uint32_t b); + +// check if a is before or equal to b +extern int seq_before_eq(uint32_t a, uint32_t b); + +// check if value is in [range_start; range_end] +extern int seq_in_range_ii(uint32_t value, uint32_t range_start, uint32_t range_end); + +// check if value is in (range_start; range_end) +extern int seq_in_range_ee(uint32_t value, uint32_t range_start, uint32_t range_end); + +// check if value is in [range_start; range_end) +extern int seq_in_range_ie(uint32_t value, uint32_t range_start, uint32_t range_end); + +// check if value is in (range_start; range_end] +extern int seq_in_range_ei(uint32_t value, uint32_t range_start, uint32_t range_end); -- cgit v1.2.3 From 32bb25cc2666809759371436a6450574b00c0e6d Mon Sep 17 00:00:00 2001 From: Mosder Date: Mon, 13 Jul 2026 15:32:27 +0200 Subject: feat: added generic functionallity for getting the parsed data from stream --- analyzer/analyzer.c | 24 +++++++++-- analyzer/parser/fight_results.c | 33 +++++++++++--- analyzer/parser/fight_results.h | 40 +++++++++-------- analyzer/parser/find_parse_functions.h | 10 +++++ analyzer/parser/parser.c | 79 ++++++++++++++++++++++++++++------ analyzer/parser/parser.h | 18 ++++---- analyzer/parser/parser_defs.h | 46 ++++++++++++++++++++ 7 files changed, 200 insertions(+), 50 deletions(-) create mode 100644 analyzer/parser/find_parse_functions.h create mode 100644 analyzer/parser/parser_defs.h (limited to 'analyzer/analyzer.c') diff --git a/analyzer/analyzer.c b/analyzer/analyzer.c index 2e5caf8..5997c6d 100644 --- a/analyzer/analyzer.c +++ b/analyzer/analyzer.c @@ -1,10 +1,12 @@ #include "analyzer.h" +#include + #include "parser/parser.h" #include "utils/tcp.h" // loop through packets received from pcap_handle and analyze the TCP stream -void analyzer_loop(TCPStream *stream, pcap_t *pcap_handle) { +void analyzer_loop(TCPStream *stream, pcap_t *pcap_handle, void (*parsed_data_handler)(ParsedData *parsed_data, size_t n_parsed_data)) { // read packets from handle struct pcap_pkthdr *packet_data; const byte_t *packet; @@ -23,7 +25,23 @@ void analyzer_loop(TCPStream *stream, pcap_t *pcap_handle) { if (parsed_len > 0) remove_parsed_data(stream, parsed_len); - // TODO: handle parsed data + // if there's parsed data - call the parsed data handler + if (n_parsed_data > 0) + parsed_data_handler(parsed_data, n_parsed_data); + + // free the entire parsed data memory + free_parsed_data(parsed_data, n_parsed_data); + } + } +} + +// file analyzer parsed data handler +void file_parsed_data_handler(ParsedData *parsed_data, size_t n_parsed_data) { + for (size_t i = 0; i < n_parsed_data; i++) { + switch (parsed_data[i].data_type) { + case TYPE_FIGHT_RESULTS: + printf("Fight results\n"); + break; } } } @@ -41,7 +59,7 @@ void analyze_file(char *path) { // analyze the file TCPStream stream = new_stream(); - analyzer_loop(&stream, handle); + analyzer_loop(&stream, handle, file_parsed_data_handler); destroy_stream(stream); pcap_close(handle); } diff --git a/analyzer/parser/fight_results.c b/analyzer/parser/fight_results.c index 1db79ea..8275af2 100644 --- a/analyzer/parser/fight_results.c +++ b/analyzer/parser/fight_results.c @@ -1,18 +1,39 @@ #include "fight_results.h" #include +#include #include -byte_t *find_fight_results(TCPStream *stream, int *length) { - byte_t *start = memmem(stream->data, stream->len, FIGHT_RESULTS_START, strlen(FIGHT_RESULTS_START)); - byte_t *end = memmem(stream->data, stream->len, FIGHT_RESULTS_END, strlen(FIGHT_RESULTS_END)); +// function to cleanup fight results to be used in ParsedData struct +void cleanup_fight_results(void *data) { + FightResults *fight_results = data; + for (size_t i = 0; i < fight_results->num_friendly; i++) { + free(fight_results->friendly_results[i].name); + free(fight_results->friendly_results[i].gear); + free(fight_results->friendly_results[i].items); + free(fight_results->friendly_results[i].drifs); + } + for (size_t i = 0; i < fight_results->num_enemy; i++) { + free(fight_results->enemy_results[i].name); + } +} + +byte_t *find_fight_results(TCPStream *stream, int offset, int *length) { + byte_t *start = memmem(stream->data + offset, stream->len, FIGHT_RESULTS_START, strlen(FIGHT_RESULTS_START)); + byte_t *end = memmem(stream->data + offset, stream->len, FIGHT_RESULTS_END, strlen(FIGHT_RESULTS_END)); *length = start != NULL && end != NULL ? end - start + strlen(FIGHT_RESULTS_END) : -1; return start; } -FightResults parse_fight_results(byte_t *data, size_t length) { +ParsedData parse_fight_results(byte_t *data, size_t length) { + ParsedData parsed_data; + parsed_data.data_type = TYPE_FIGHT_RESULTS; + // TODO: actually parse that data printf("len=%zu\n%.*s\n----------------\n", length, (int)length, data); - FightResults tmp; - return tmp; + + parsed_data.data = NULL; + parsed_data.data_cleanup = NULL; + // parsed_data.data_cleanup = cleanup_fight_results; + return parsed_data; } diff --git a/analyzer/parser/fight_results.h b/analyzer/parser/fight_results.h index b275f2b..d8064ca 100644 --- a/analyzer/parser/fight_results.h +++ b/analyzer/parser/fight_results.h @@ -2,6 +2,7 @@ // utilities for parsing the fight results data #include "../utils/tcp.h" +#include "parser_defs.h" // Payload structure: // FIGHT_RESULTS_START EntityResults (SEPARATOR_ENTITY EntityResults)* FIGHT_RESULTS_END @@ -15,6 +16,8 @@ #define SEPARATOR_ENTITY "[--]" #define SEPARATOR_RESULT "&" +#ifndef ENTITY_RESULTS_DEFINED +#define ENTITY_RESULTS_DEFINED typedef struct { int team; // friend or enemy char *name; @@ -52,12 +55,15 @@ typedef struct { char *field34; char *saturation; } EntityResults; +#endif // values of field "team" #define TEAM_FRIEND 1 #define TEAM_ENEMY 2 // fight results data for friendly +#ifndef FRIENDLY_RESULTS_DEFINED +#define FRIENDLY_RESULTS_DEFINED typedef struct { char *name; int exp; @@ -71,33 +77,29 @@ typedef struct { char *saturation_type; int saturation; } FriendlyResults; +#endif // fight results data for enemy +#ifndef ENEMY_RESULTS_DEFINED +#define ENEMY_RESULTS_DEFINED typedef struct { char *name; int level; } EnemyResults; +#endif + +#define MAX_ENTITIES_PER_SIDE 8 // entire fight resutls +#ifndef FIGHT_RESULTS_DEFINED +#define FIGHT_RESULTS_DEFINED typedef struct { - int num_friendly; - FriendlyResults *friendly_results; - int num_enemy; - EnemyResults *enemy_results; + size_t num_friendly; + FriendlyResults friendly_results[MAX_ENTITIES_PER_SIDE]; + size_t num_enemy; + EnemyResults enemy_results[MAX_ENTITIES_PER_SIDE]; } FightResults; +#endif -// find the first fight results data in stream -// params: -// - stream - stream to find the fight results in -// - length - length of the fight results data or -1 if there's no end yet -// returns: -// pointer to the beginning of fight results data or NULL if there's none -extern byte_t *find_fight_results(TCPStream *stream, int *length); - -// parse given fight results -// params: -// - data - fight results data to parse -// - length - length of fight results data -// returns: -// FightResults struct containing parsed data -extern FightResults parse_fight_results(byte_t *data, size_t length); +extern find_function_t find_fight_results; +extern parse_function_t parse_fight_results; diff --git a/analyzer/parser/find_parse_functions.h b/analyzer/parser/find_parse_functions.h new file mode 100644 index 0000000..4e53274 --- /dev/null +++ b/analyzer/parser/find_parse_functions.h @@ -0,0 +1,10 @@ +// parser/find_parse_functions.h +// array of find and parse functions for every result type + +#include "fight_results.h" + +#define RESULT_TYPE_COUNT 1 + +FindParseFunctions all_find_parse_functions[RESULT_TYPE_COUNT] = { + {.find_fn = find_fight_results, .parse_fn = parse_fight_results} +}; diff --git a/analyzer/parser/parser.c b/analyzer/parser/parser.c index 7fb155f..246635c 100644 --- a/analyzer/parser/parser.c +++ b/analyzer/parser/parser.c @@ -2,19 +2,72 @@ #include -ParsedData *parse_stream(TCPStream *stream, size_t *parsed_len, size_t *n_parsed_data) { - // TODO: Need to do parsing of the entire thing (loop) and return all of the parsed data - int length; - byte_t *start = find_fight_results(stream, &length); - if (start == NULL) { - *parsed_len = stream->len; - } - else if (length == -1) { - *parsed_len = 0; +#include "find_parse_functions.h" + +// expand parsed data exponentially to fit el_count elements +ParsedData *expand_parsed_data(ParsedData *parsed_data, size_t *capacity, size_t el_count) { + while (*capacity < el_count) + *capacity <<= 1; + return realloc(parsed_data, *capacity * sizeof(ParsedData)); +} + +// find earliest data to parse in stream +byte_t *find_earliest_data(TCPStream *stream, int stream_offset, int *earliest_length, parse_function_t **earliest_parse_fun) { + byte_t *earliest_data = NULL; + for (size_t i = 0; i < RESULT_TYPE_COUNT; i++) { + int length; + byte_t *data = all_find_parse_functions[i].find_fn(stream, stream_offset, &length); + // if it's the first found or earlier than earliest - save it + if (data && (!earliest_data || data < earliest_data)) { + earliest_data = data; + *earliest_length = length; + *earliest_parse_fun = all_find_parse_functions[i].parse_fn; + } } - else { - parse_fight_results(start, length); - *parsed_len = start - stream->data + length; + return earliest_data; +} + +ParsedData *parse_stream(TCPStream *stream, size_t *parsed_len, size_t *n_parsed_data) { + size_t parsed_data_capacity = INIT_PARSED_DATA_CAPACITY; + ParsedData *parsed_data = malloc(parsed_data_capacity * sizeof(ParsedData)); + *n_parsed_data = 0; + + int stream_offset = 0; + byte_t *earliest_data; + do { + // find earliest data to parse + int earliest_length; + parse_function_t *earliest_parse_fun; + earliest_data = find_earliest_data(stream, stream_offset, &earliest_length, &earliest_parse_fun); + + // if start of data to parse was found, but not the end, then finish here + // set parsed_len, so that the beginning of that data is at the beginning of the stream + if (earliest_data && earliest_length == -1) { + *parsed_len = earliest_data - stream->data; + return parsed_data; + } + + // if data to parse was found, parse it and update the offset + if (earliest_data) { + // if no capacity in parsed data array - expand it + if (*n_parsed_data >= parsed_data_capacity) + parsed_data = expand_parsed_data(parsed_data, &parsed_data_capacity, *n_parsed_data + 1); + + parsed_data[(*n_parsed_data)++] = earliest_parse_fun(earliest_data, earliest_length); + stream_offset = earliest_data - stream->data + earliest_length; + } + } while (earliest_data); + + // if all was parsed - set parsed_len to stream_len and return the data + *parsed_len = stream->len; + return parsed_data; +} + +void free_parsed_data(ParsedData *parsed_data, size_t n_parsed_data) { + for (size_t i = 0; i < n_parsed_data; i++) { + if (parsed_data[i].data_cleanup) + parsed_data[i].data_cleanup(parsed_data[i].data); + free(parsed_data[i].data); } - return NULL; + free(parsed_data); } diff --git a/analyzer/parser/parser.h b/analyzer/parser/parser.h index 088bb5d..e2d5c5d 100644 --- a/analyzer/parser/parser.h +++ b/analyzer/parser/parser.h @@ -1,17 +1,11 @@ // parser/parser.h // main parser file -#include "fight_results.h" - #include "../utils/tcp.h" +#include "fight_results.h" +#include "parser_defs.h" -// "generic" type for parsed data -typedef struct { - enum { FIGHT_RESULTS } data_type; - union { - FightResults fight_results; - } data; -} ParsedData; +#define INIT_PARSED_DATA_CAPACITY 4 // parse a stream of TCP data // params: @@ -21,3 +15,9 @@ typedef struct { // returns: // pointer to the array of parsed data extern ParsedData *parse_stream(TCPStream *stream, size_t *parsed_len, size_t *n_parsed_data); + +// free the entire parsed data array from parse_stream function +// params: +// - parsed_data - parsed data to free +// - n_parsed_data - count of parsed data +extern void free_parsed_data(ParsedData *parsed_data, size_t n_parsed_data); diff --git a/analyzer/parser/parser_defs.h b/analyzer/parser/parser_defs.h new file mode 100644 index 0000000..f99c2f3 --- /dev/null +++ b/analyzer/parser/parser_defs.h @@ -0,0 +1,46 @@ +// parser/parser_defs.h +// useful definitions for parser files + +#include "../utils/tcp.h" + +#ifndef RESULTS_ENUM_DEFINED +#define RESULTS_ENUM_DEFINED +typedef enum { TYPE_FIGHT_RESULTS } ResultsEnum; +#endif + +#ifndef PARSED_DATA_DEFINED +#define PARSED_DATA_DEFINED +// "generic" type for parsed data +typedef struct { + ResultsEnum data_type; // type of data represented by an enum + void *data; // pointer to the parsed data + void (*data_cleanup)(void *data); // function to cleanup internal allocations inside data + // if no internal allocations - this should be NULL +} ParsedData; +#endif + +// find the first results data of specific type in stream +// params: +// - stream - stream to find the data in +// - offset - offset to start finding data from +// - length - length of the data or -1 if there's no end yet +// returns: +// pointer to the beginning of data or NULL if there's none +typedef byte_t *find_function_t(TCPStream *stream, int offset, int *length); + +// parse given data of specifc type +// params: +// - data - data to parse +// - length - length of data +// returns: +// ParsedData struct including the parsed data +typedef ParsedData parse_function_t(byte_t *data, size_t length); + +// struct for holding the find and parse function for every result type +#ifndef FIND_PARSE_FUNCTIONS_DEFINED +#define FIND_PARSE_FUNCTIONS_DEFINED +typedef struct { + find_function_t *find_fn; + parse_function_t *parse_fn; +} FindParseFunctions; +#endif -- cgit v1.2.3 From 2a45b1096325ac6743787b6f60fef4800223b2a6 Mon Sep 17 00:00:00 2001 From: Mosder Date: Mon, 13 Jul 2026 19:39:06 +0200 Subject: feat: added basic parsing of fight results data --- analyzer/analyzer.c | 32 +++++++++- analyzer/parser/fight_results.c | 130 ++++++++++++++++++++++++++++++++++++++-- analyzer/parser/fight_results.h | 76 +++++++++++++++++++++++ analyzer/parser/parser_defs.h | 5 ++ 4 files changed, 237 insertions(+), 6 deletions(-) (limited to 'analyzer/analyzer.c') diff --git a/analyzer/analyzer.c b/analyzer/analyzer.c index 5997c6d..03ecd2a 100644 --- a/analyzer/analyzer.c +++ b/analyzer/analyzer.c @@ -1,6 +1,7 @@ #include "analyzer.h" #include +#include #include "parser/parser.h" #include "utils/tcp.h" @@ -35,12 +36,41 @@ void analyzer_loop(TCPStream *stream, pcap_t *pcap_handle, void (*parsed_data_ha } } +// prints fight resutls +void print_fight_results(FightResults results) { + printf("Enemies:\n\t"); + for (size_t i = 0; i < results.num_enemy; i++) { + EnemyResults enemy = results.enemy_results[i]; + printf("%s (%d)%s", enemy.name, enemy.level, i < results.num_enemy - 1 ? ", " : "\n"); + } + printf("Player rewards:\n"); + for (size_t i = 0; i < results.num_friendly; i++) { + FriendlyResults friendly = results.friendly_results[i]; + printf("\t%s (%d):\n", friendly.name, friendly.level); + printf("\t\texp: %d\n", friendly.exp); + printf("\t\tgold: %d\n", friendly.gold); + if (friendly.psycho > 0) + printf("\t\tpsycho: %d\n", friendly.psycho); + if (strlen(friendly.items) > 0) + printf("\t\titems: %s\n", friendly.items); + if (strlen(friendly.gear) > 0) + printf("\t\tgear: %s\n", friendly.gear); + if (strlen(friendly.drifs) > 0) + printf("\t\tdrifs: %s\n", friendly.drifs); + if (friendly.splinters > 0) + printf("\t\tsplinters: %d\n", friendly.splinters); + if (friendly.saturation > 0) + printf("\t\tsaturation: %d (%s)\n", friendly.saturation, friendly.saturation_type); + } + printf("\n"); +} + // file analyzer parsed data handler void file_parsed_data_handler(ParsedData *parsed_data, size_t n_parsed_data) { for (size_t i = 0; i < n_parsed_data; i++) { switch (parsed_data[i].data_type) { case TYPE_FIGHT_RESULTS: - printf("Fight results\n"); + print_fight_results(*(FightResults *)parsed_data[i].data); break; } } diff --git a/analyzer/parser/fight_results.c b/analyzer/parser/fight_results.c index 8275af2..4fbb0f8 100644 --- a/analyzer/parser/fight_results.c +++ b/analyzer/parser/fight_results.c @@ -25,15 +25,135 @@ byte_t *find_fight_results(TCPStream *stream, int offset, int *length) { return start; } +void destroy_entity_results(EntityResults results) { + free(results.name); + free(results.field7); + free(results.gear); + free(results.items); + free(results.drifs); + free(results.field29); + free(results.field34); + free(results.saturation); +} + +// get entity results from the string +EntityResults get_entity_results(byte_t *data, size_t length) { + EntityResults results; + TypeEnum field_types[ENTITY_FIELD_COUNT] = ENTITY_FIELD_TYPES; + void *field_pointers[ENTITY_FIELD_COUNT] = ENTITY_FIELD_ADDRESSES(results); + + for (size_t i = 0; i < ENTITY_FIELD_COUNT; i++) { + // get string buffer of given result + byte_t *result_separator = memmem(data, length, SEPARATOR_RESULT, strlen(SEPARATOR_RESULT)); + size_t len = result_separator ? result_separator - data : length; + char *str = malloc(len + 1); + memcpy(str, data, len); + str[len] = '\0'; + + // parse depending on variable type + switch (field_types[i]) { + case INT: + sscanf(str, "%d", (int *)field_pointers[i]); + free(str); + break; + case FLOAT: + sscanf(str, "%f", (float *)field_pointers[i]); + free(str); + break; + case DOUBLE: + sscanf(str, "%lf", (double *)field_pointers[i]); + free(str); + break; + case STRING: + *(char **)field_pointers[i] = str; + break; + } + + // update data pointer and length + if (result_separator) { + length -= result_separator - data + strlen(SEPARATOR_RESULT); + data = result_separator + strlen(SEPARATOR_RESULT); + } + } + + return results; +} + +// parse EntityResults to FriendlyResults +FriendlyResults parse_to_friendly(EntityResults entity_results) { + FriendlyResults results; + + results.name = strdup(entity_results.name); + results.exp = entity_results.exp; + results.gold = entity_results.gold; + // TODO: parse that to more readable format + results.gear = strdup(entity_results.gear); + // TODO: parse that to more readable format, determine types from ids + results.items = strdup(entity_results.items); + results.level = entity_results.level; + results.psycho = entity_results.psycho; + // TODO: parse that to more readable format + results.drifs = strdup(entity_results.drifs); + results.splinters = entity_results.splinters; + + results.saturation = 0; + if (strlen(entity_results.saturation) > 0) { + int saturation_id; + sscanf(entity_results.saturation, "%d,%d", &saturation_id, &results.saturation); + // TODO: determine type + results.saturation_type = "typ"; + } + + return results; +} + +// parse EntityResults to EnemyResults +EnemyResults parse_to_enemy(EntityResults entity_results) { + EnemyResults results; + + results.name = strdup(entity_results.name); + results.level = entity_results.level; + + return results; +} + ParsedData parse_fight_results(byte_t *data, size_t length) { ParsedData parsed_data; parsed_data.data_type = TYPE_FIGHT_RESULTS; - // TODO: actually parse that data - printf("len=%zu\n%.*s\n----------------\n", length, (int)length, data); + // ignore start and end indicators + data += strlen(FIGHT_RESULTS_START); + length -= strlen(FIGHT_RESULTS_START) + strlen(FIGHT_RESULTS_END); + + FightResults *results = malloc(sizeof(FightResults)); + results->num_friendly = 0; + results->num_enemy = 0; + + byte_t *entity_separator; + do { + // get generic entity results + entity_separator = memmem(data, length, SEPARATOR_ENTITY, strlen(SEPARATOR_ENTITY)); + EntityResults entity_results = get_entity_results(data, entity_separator ? entity_separator - data : length); + + // depending on team - parse it into friendly or enemy results + switch (entity_results.team) { + case TEAM_FRIEND: + results->friendly_results[(results->num_friendly)++] = parse_to_friendly(entity_results); + break; + case TEAM_ENEMY: + results->enemy_results[results->num_enemy++] = parse_to_enemy(entity_results); + break; + } + destroy_entity_results(entity_results); + + // if there are still entities - modify data pointer and length + if (entity_separator) { + length -= entity_separator - data + strlen(SEPARATOR_ENTITY); + data = entity_separator + strlen(SEPARATOR_ENTITY); + } + } while (entity_separator); - parsed_data.data = NULL; - parsed_data.data_cleanup = NULL; - // parsed_data.data_cleanup = cleanup_fight_results; + parsed_data.data = results; + parsed_data.data_cleanup = cleanup_fight_results; return parsed_data; } diff --git a/analyzer/parser/fight_results.h b/analyzer/parser/fight_results.h index d8064ca..2712638 100644 --- a/analyzer/parser/fight_results.h +++ b/analyzer/parser/fight_results.h @@ -16,6 +16,82 @@ #define SEPARATOR_ENTITY "[--]" #define SEPARATOR_RESULT "&" +#define ENTITY_FIELD_COUNT 35 +#define ENTITY_FIELD_TYPES { \ + /* team */ INT, \ + /* name */ STRING, \ + /* exp */ INT, \ + /* field4 */ FLOAT, \ + /* gold */ INT, \ + /* field6 */ FLOAT, \ + /* field7 */ STRING, \ + /* gear */ STRING, \ + /* field9 */ INT, \ + /* items */ STRING, \ + /* field11 */ INT, \ + /* field12 */ INT, \ + /* field13 */ FLOAT, \ + /* field14 */ FLOAT, \ + /* field15 */ FLOAT, \ + /* level */ INT, \ + /* field17 */ DOUBLE, \ + /* field18 */ FLOAT, \ + /* field19 */ INT, \ + /* field20 */ INT, \ + /* field21 */ FLOAT, \ + /* field22 */ FLOAT, \ + /* field23 */ INT, \ + /* field24 */ INT, \ + /* psycho */ INT, \ + /* drifs */ STRING, \ + /* splinters */ INT, \ + /* field28 */ INT, \ + /* field29 */ STRING, \ + /* field30 */ INT, \ + /* field31 */ INT, \ + /* field32 */ INT, \ + /* field33 */ INT, \ + /* field34 */ STRING, \ + /* saturation */ STRING \ +} +#define ENTITY_FIELD_ADDRESSES(entity_results) {\ + &entity_results.team, \ + &entity_results.name, \ + &entity_results.exp, \ + &entity_results.field4, \ + &entity_results.gold, \ + &entity_results.field6, \ + &entity_results.field7, \ + &entity_results.gear, \ + &entity_results.field9, \ + &entity_results.items, \ + &entity_results.field11, \ + &entity_results.field12, \ + &entity_results.field13, \ + &entity_results.field14, \ + &entity_results.field15, \ + &entity_results.level, \ + &entity_results.field17, \ + &entity_results.field18, \ + &entity_results.field19, \ + &entity_results.field20, \ + &entity_results.field21, \ + &entity_results.field22, \ + &entity_results.field23, \ + &entity_results.field24, \ + &entity_results.psycho, \ + &entity_results.drifs, \ + &entity_results.splinters, \ + &entity_results.field28, \ + &entity_results.field29, \ + &entity_results.field30, \ + &entity_results.field31, \ + &entity_results.field32, \ + &entity_results.field33, \ + &entity_results.field34, \ + &entity_results.saturation \ +} + #ifndef ENTITY_RESULTS_DEFINED #define ENTITY_RESULTS_DEFINED typedef struct { diff --git a/analyzer/parser/parser_defs.h b/analyzer/parser/parser_defs.h index f99c2f3..b76b7e6 100644 --- a/analyzer/parser/parser_defs.h +++ b/analyzer/parser/parser_defs.h @@ -3,6 +3,11 @@ #include "../utils/tcp.h" +#ifndef TYPE_ENUM_DEFINED +#define TYPE_ENUM_DEFINED +typedef enum { INT, FLOAT, DOUBLE, STRING } TypeEnum; +#endif + #ifndef RESULTS_ENUM_DEFINED #define RESULTS_ENUM_DEFINED typedef enum { TYPE_FIGHT_RESULTS } ResultsEnum; -- cgit v1.2.3 From c7277e7654c0d8d0e7a4665373c0762c6e8a038d Mon Sep 17 00:00:00 2001 From: Mosder Date: Tue, 14 Jul 2026 13:40:17 +0200 Subject: fix: fixed not handling of different connections and bug regarding TCP SYN packet --- analyzer/analyzer.c | 4 ++-- analyzer/utils/tcp.c | 23 ++++++++++++++++------- analyzer/utils/tcp.h | 10 ++++++---- 3 files changed, 24 insertions(+), 13 deletions(-) (limited to 'analyzer/analyzer.c') diff --git a/analyzer/analyzer.c b/analyzer/analyzer.c index 03ecd2a..7def1e6 100644 --- a/analyzer/analyzer.c +++ b/analyzer/analyzer.c @@ -15,8 +15,8 @@ void analyzer_loop(TCPStream *stream, pcap_t *pcap_handle, void (*parsed_data_ha // get new segment TCPSegment segment = get_segment_from_packet(packet, packet_data); - // handle the new segment - if (handle_segment(stream, segment)) { + // handle the new segment if it's correct one and has data in it (ignore SYN) + if (segment.len > 0 && handle_segment(stream, segment)) { // if it was added to the stream - parse it size_t parsed_len; size_t n_parsed_data; diff --git a/analyzer/utils/tcp.c b/analyzer/utils/tcp.c index ab5303e..b925ecf 100644 --- a/analyzer/utils/tcp.c +++ b/analyzer/utils/tcp.c @@ -12,8 +12,9 @@ TCPSegment new_segment() { TCPSegment segment; segment.payload = NULL; - segment.len = -1; + segment.len = 0; segment.seq = 0; + segment.port = 0; return segment; } @@ -53,8 +54,9 @@ TCPSegment get_segment_from_packet(const byte_t *packet, const struct pcap_pkthd const struct tcphdr *tcp_header = (const struct tcphdr *)(packet + eth_header_len + ip_header_len); int tcp_header_len = tcp_header->th_off * 4; - // get TCP sequence number + // get TCP sequence number and destination port segment.seq = ntohl(tcp_header->seq); + segment.port = ntohs(tcp_header->th_dport); // get payload int total_header_len = eth_header_len + ip_header_len + tcp_header_len; @@ -66,11 +68,12 @@ TCPSegment get_segment_from_packet(const byte_t *packet, const struct pcap_pkthd TCPStream new_stream() { TCPStream stream; - stream.data = NULL; // NULL to differentiate between first and other segments + stream.data = malloc(INIT_STREAM_CAPACITY); stream.len = 0; stream.capacity = INIT_STREAM_CAPACITY; stream.seq = 0; - stream.pending = heap_new(sizeof(TCPSegment), segments_compare, segment_cleanup); + stream.port = 0; + stream.pending = NULL; return stream; } @@ -142,10 +145,16 @@ void add_pending_segments(TCPStream *stream) { } int handle_segment(TCPStream *stream, TCPSegment segment) { - // add the segment if it's the first one - if (stream->data == NULL) { - stream->data = malloc(stream->capacity); + // add the segment if port is different (new connection) + if (stream->port != segment.port) { + // create new heap for new pending segments + heap_free(stream->pending); + stream->pending = heap_new(sizeof(TCPSegment), segments_compare, segment_cleanup); + + stream->len = 0; stream->seq = segment.seq; + stream->port = segment.port; + add_segment(stream, segment, 0); free(segment.payload); return 1; diff --git a/analyzer/utils/tcp.h b/analyzer/utils/tcp.h index 37ab4bc..9c6a949 100644 --- a/analyzer/utils/tcp.h +++ b/analyzer/utils/tcp.h @@ -12,9 +12,10 @@ typedef unsigned char byte_t; #ifndef TCP_SEGMENT_DEFINED #define TCP_SEGMENT_DEFINED typedef struct { - byte_t *payload; - size_t len; - uint32_t seq; + byte_t *payload; // payload in the segment + size_t len; // length of the payload + uint32_t seq; // TCP sequence number of the segment + uint16_t port; // destination (client) port of connection the segment was received from } TCPSegment; #endif @@ -23,7 +24,7 @@ typedef struct { // - packet - captured packet // - packet_data - metadata about packet // returns: -// TCPSegment from packet, payload is NULL and len is -1 if the received packet was incorrect +// TCPSegment from packet, payload is NULL if the received packet was incorrect extern TCPSegment get_segment_from_packet(const byte_t *packet, const struct pcap_pkthdr *packet_data); // struct that stores all the information about stream @@ -34,6 +35,7 @@ typedef struct { size_t len; // length of the stream size_t capacity; // capacity of the data buffer uint32_t seq; // TCP sequence number of the first byte in the stream + uint16_t port; // destination (client) port of current analyzed connection Heap *pending; // min heap of segments yet to be added to stream (sorted by TCP sequence number) } TCPStream; #endif -- cgit v1.2.3 From 640532f1d0b42323333cabc029af91da6d982d42 Mon Sep 17 00:00:00 2001 From: Mosder Date: Thu, 16 Jul 2026 16:09:12 +0200 Subject: feat: added "prettier" printing of fight results. still need to better parse items, drifs and gear --- Makefile | 5 +- analyzer/analyzer.c | 33 +--------- analyzer/parser/fight_results.c | 4 +- analyzer/render.c | 142 ++++++++++++++++++++++++++++++++++++++++ analyzer/render.h | 60 +++++++++++++++++ 5 files changed, 211 insertions(+), 33 deletions(-) create mode 100644 analyzer/render.c create mode 100644 analyzer/render.h (limited to 'analyzer/analyzer.c') diff --git a/Makefile b/Makefile index cb40a5a..dcdabe2 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ global: compile local: compile mv branal ${HOME}/.local/bin -compile: csv structures utils parser capture analyzer +compile: csv structures utils parser capture analyzer render $(CC) $(CFLAGS) -lpcap *.o main.c -o branal rm *.o @@ -36,5 +36,8 @@ capture: analyzer: $(CC) $(CFLAGS) -c analyzer/analyzer.c +render: + $(CC) $(CFLAGS) -c analyzer/render.c + .PHONY: analyzer .PHONY: csv diff --git a/analyzer/analyzer.c b/analyzer/analyzer.c index 7def1e6..9ca2d6a 100644 --- a/analyzer/analyzer.c +++ b/analyzer/analyzer.c @@ -4,6 +4,7 @@ #include #include "parser/parser.h" +#include "render.h" #include "utils/tcp.h" // loop through packets received from pcap_handle and analyze the TCP stream @@ -36,41 +37,13 @@ void analyzer_loop(TCPStream *stream, pcap_t *pcap_handle, void (*parsed_data_ha } } -// prints fight resutls -void print_fight_results(FightResults results) { - printf("Enemies:\n\t"); - for (size_t i = 0; i < results.num_enemy; i++) { - EnemyResults enemy = results.enemy_results[i]; - printf("%s (%d)%s", enemy.name, enemy.level, i < results.num_enemy - 1 ? ", " : "\n"); - } - printf("Player rewards:\n"); - for (size_t i = 0; i < results.num_friendly; i++) { - FriendlyResults friendly = results.friendly_results[i]; - printf("\t%s (%d):\n", friendly.name, friendly.level); - printf("\t\texp: %d\n", friendly.exp); - printf("\t\tgold: %d\n", friendly.gold); - if (friendly.psycho > 0) - printf("\t\tpsycho: %d\n", friendly.psycho); - if (strlen(friendly.items) > 0) - printf("\t\titems: %s\n", friendly.items); - if (strlen(friendly.gear) > 0) - printf("\t\tgear: %s\n", friendly.gear); - if (strlen(friendly.drifs) > 0) - printf("\t\tdrifs: %s\n", friendly.drifs); - if (friendly.splinters > 0) - printf("\t\tsplinters: %d\n", friendly.splinters); - if (friendly.saturation > 0) - printf("\t\tsaturation: %d (%s)\n", friendly.saturation, friendly.saturation_type); - } - printf("\n"); -} - // file analyzer parsed data handler void file_parsed_data_handler(ParsedData *parsed_data, size_t n_parsed_data) { for (size_t i = 0; i < n_parsed_data; i++) { switch (parsed_data[i].data_type) { case TYPE_FIGHT_RESULTS: - print_fight_results(*(FightResults *)parsed_data[i].data); + printf("\nFIGHT:\n"); + render_fight_results(*(FightResults *)parsed_data[i].data); break; } } diff --git a/analyzer/parser/fight_results.c b/analyzer/parser/fight_results.c index b3227e2..603d66d 100644 --- a/analyzer/parser/fight_results.c +++ b/analyzer/parser/fight_results.c @@ -83,11 +83,11 @@ EntityResults get_entity_results(byte_t *data, size_t length) { } char *get_item_name(int item_id) { - return item_id < id_to_item_name_len ? id_to_item_name[item_id] : "item_id out of bounds"; + return item_id < id_to_item_name_len ? id_to_item_name[item_id] : "null"; } char *get_saturation_type(int saturation_id) { - return saturation_id < id_to_saturation_type_len ? id_to_saturation_type[saturation_id] : "saturation_id out of bounds"; + return saturation_id < id_to_saturation_type_len ? id_to_saturation_type[saturation_id] : "null"; } // parse EntityResults to FriendlyResults diff --git a/analyzer/render.c b/analyzer/render.c new file mode 100644 index 0000000..0c3df56 --- /dev/null +++ b/analyzer/render.c @@ -0,0 +1,142 @@ +#include "render.h" + +#include +#include + +// strlen counting utf-8 characters as one +size_t utf8_strlen(char *s) { + size_t len = 0; + while (*s) { + // if the byte is not a continuation byte (10xxxxxx), it's a new character + if ((*s & 0xC0) != 0x80) + len++; + s++; + } + return len; +} + +void print_row_separator() { + for (size_t i = 0; i < TABLE_LINE_WIDTH; i++) + printf("%c", TABLE_ROW_SEPARATOR_CHAR); + printf("\n"); +} + +// print string with given color +void print_color(char *str, int color) { + printf("\033[%dm%s\033[0m", color, str); +} + +void print_center(char *text, int color, size_t width, char padding_char) { + size_t post_char_count = (width - strlen(text)) / 2; + size_t pre_char_count = width - post_char_count - strlen(text); + + for (size_t i = 0; i < pre_char_count; i++) + printf("%c", padding_char); + + print_color(text, color); + + for (size_t i = 0; i < post_char_count; i++) + printf("%c", padding_char); +} + +void print_enemy_list(EnemyResults *results, size_t n_enemies) { + printf(TABLE_LINE_START); + size_t line_length = TABLE_LINE_START_WIDTH; + + for (size_t i = 0; i < n_enemies; i++) { + EnemyResults enemy = results[i]; + char enemy_buffer[TABLE_LINE_WIDTH + 1]; + sprintf(enemy_buffer, "%s (%d)%s", enemy.name, enemy.level, i < n_enemies - 1 ? ", " : ""); + + // break line if length would be too big + if (line_length + utf8_strlen(enemy_buffer) > TABLE_LINE_WIDTH - TABLE_LINE_END_WIDTH) { + printf("%*s\n", (int)(TABLE_LINE_WIDTH - line_length), TABLE_LINE_END); + printf(TABLE_LINE_START); + line_length = TABLE_LINE_START_WIDTH; + } + + printf("%s", enemy_buffer); + line_length += utf8_strlen(enemy_buffer); + } + + printf("%*s\n", (int)(TABLE_LINE_WIDTH - line_length), TABLE_LINE_END); +} + +void print_column_names() { + char *names[TABLE_COLUMN_COUNT] = TABLE_COLUMN_NAMES; + int colors[TABLE_COLUMN_COUNT] = TABLE_COLUMN_COLORS; + size_t widths[TABLE_COLUMN_COUNT] = TABLE_COLUMN_WIDTHS; + + printf(TABLE_LINE_START); + for (size_t i = 0; i < TABLE_COLUMN_COUNT; i++) { + print_center(names[i], colors[i], widths[i], ' '); + printf(i < TABLE_COLUMN_COUNT - 1 ? TABLE_LINE_SEPARATOR : TABLE_LINE_END); + } + printf("\n"); +} + +void print_player_rewards(FriendlyResults results) { + char buffers[TABLE_COLUMN_COUNT][TABLE_LINE_WIDTH]; + size_t widths[TABLE_COLUMN_COUNT] = TABLE_COLUMN_WIDTHS; + int colors[TABLE_COLUMN_COUNT] = TABLE_COLUMN_COLORS; + + sprintf(buffers[0], "%s (%d)", results.name, results.level); + sprintf(buffers[1], "%d", results.exp); + sprintf(buffers[2], "%d", results.gold); + + if (results.psycho > 0) + sprintf(buffers[3], "%d", results.psycho); + else + sprintf(buffers[3], "-"); + + if (results.saturation > 0) + sprintf(buffers[4], "%d (%s)", results.saturation, results.saturation_type); + else + sprintf(buffers[4], "-"); + + if (results.splinters > 0) + sprintf(buffers[5], "%d", results.splinters); + else + sprintf(buffers[5], "-"); + + printf(TABLE_LINE_START); + for (size_t i = 0; i < TABLE_COLUMN_COUNT; i++) { + char to_color[widths[i] + 1]; + sprintf(to_color, "%*s", (int)widths[i], buffers[i]); + print_color(to_color, colors[i]); + printf(i < TABLE_COLUMN_COUNT - 1 ? TABLE_LINE_SEPARATOR : TABLE_LINE_END); + } + printf("\n"); + + // TODO: change this when I parse the items etc. to a readable format + // needs to utilize utf8_strlen, because right now utf8 characters mess the table up + char big_buf[1024]; + sprintf(big_buf, "%s - %s - %s", results.items, results.gear, results.drifs); + if (strlen(big_buf) > 6) { + printf(TABLE_LINE_START); + print_center("ITEMS", COLOR_WHITE, TABLE_ITEMS_WIDTH, ' '); + printf(TABLE_LINE_END "\n"); + + for (size_t i = 0; i < strlen(big_buf); i += TABLE_ITEMS_WIDTH) { + printf(TABLE_LINE_START "%-*.*s" TABLE_LINE_END "\n", (int)TABLE_ITEMS_WIDTH, (int)TABLE_ITEMS_WIDTH, big_buf + i); + } + } +} + +void render_fight_results(FightResults results) { + print_center("ENEMIES", COLOR_RED, TABLE_LINE_WIDTH, TABLE_ROW_SEPARATOR_CHAR); + printf("\n"); + + print_enemy_list(results.enemy_results, results.num_enemy); + + print_center("PLAYER REWARDS", COLOR_GREEN, TABLE_LINE_WIDTH, TABLE_ROW_SEPARATOR_CHAR); + printf("\n"); + + print_column_names(); + print_row_separator(); + + for (size_t i = 0; i < results.num_friendly; i++) { + print_player_rewards(results.friendly_results[i]); + print_row_separator(); + } +} diff --git a/analyzer/render.h b/analyzer/render.h new file mode 100644 index 0000000..d54015a --- /dev/null +++ b/analyzer/render.h @@ -0,0 +1,60 @@ +// render.h +// functions for "rendering" data onto the terminal + +#include "parser/fight_results.h" + +// ANSI escape sequence colors +#define COLOR_RED 91 +#define COLOR_GREEN 92 +#define COLOR_WHITE 97 +#define COLOR_YELLOW 93 +#define COLOR_AQUA 96 +#define COLOR_BLUE 94 + +#define TABLE_ROW_SEPARATOR_CHAR '-' + +#define TABLE_LINE_START "| " +#define TABLE_LINE_END " |" +#define TABLE_LINE_SEPARATOR " | " +#define TABLE_LINE_START_WIDTH (sizeof(TABLE_LINE_START) - 1) +#define TABLE_LINE_END_WIDTH (sizeof(TABLE_LINE_END) - 1) +#define TABLE_LINE_SEPARATOR_WIDTH (sizeof(TABLE_LINE_SEPARATOR) - 1) + +#define TABLE_COLUMN_COUNT 6 + +#define TABLE_NAME_LVL_WIDTH 17 +#define TABLE_EXP_WIDTH 7 +#define TABLE_GOLD_WIDTH 7 +#define TABLE_PSYCHO_WIDTH 6 +#define TABLE_SATURATION_WIDTH 13 +#define TABLE_SPLINTERS_WIDTH 9 + +#define TABLE_COLUMN_NAMES {"Nick (LVL)", "EXP", "Gold", "Psycho", "Saturation", "Splinters"} +#define TABLE_COLUMN_COLORS {COLOR_WHITE, COLOR_WHITE, COLOR_YELLOW, COLOR_AQUA, COLOR_GREEN, COLOR_BLUE} +#define TABLE_COLUMN_WIDTHS { \ + TABLE_NAME_LVL_WIDTH, \ + TABLE_EXP_WIDTH, \ + TABLE_GOLD_WIDTH, \ + TABLE_PSYCHO_WIDTH, \ + TABLE_SATURATION_WIDTH, \ + TABLE_SPLINTERS_WIDTH \ +} + +#define TABLE_LINE_WIDTH ( \ + TABLE_LINE_START_WIDTH + \ + TABLE_NAME_LVL_WIDTH + \ + TABLE_EXP_WIDTH + \ + TABLE_GOLD_WIDTH + \ + TABLE_PSYCHO_WIDTH + \ + TABLE_SATURATION_WIDTH + \ + TABLE_SPLINTERS_WIDTH + \ + (TABLE_COLUMN_COUNT - 1) * TABLE_LINE_SEPARATOR_WIDTH + \ + TABLE_LINE_END_WIDTH \ +) + +#define TABLE_ITEMS_WIDTH (TABLE_LINE_WIDTH - TABLE_LINE_START_WIDTH - TABLE_LINE_END_WIDTH) + +// render fight results onto the terminal +// params: +// - results - fight results to render +extern void render_fight_results(FightResults results); -- cgit v1.2.3 From 165efd111591dbe44ebf057d6680a219b5657df5 Mon Sep 17 00:00:00 2001 From: Mosder Date: Thu, 16 Jul 2026 17:44:57 +0200 Subject: feat: added state to the analyzer loop and data handling function --- analyzer/analyzer.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'analyzer/analyzer.c') diff --git a/analyzer/analyzer.c b/analyzer/analyzer.c index 9ca2d6a..bd24613 100644 --- a/analyzer/analyzer.c +++ b/analyzer/analyzer.c @@ -8,7 +8,9 @@ #include "utils/tcp.h" // loop through packets received from pcap_handle and analyze the TCP stream -void analyzer_loop(TCPStream *stream, pcap_t *pcap_handle, void (*parsed_data_handler)(ParsedData *parsed_data, size_t n_parsed_data)) { +void analyzer_loop( + TCPStream *stream, pcap_t *pcap_handle, void *state, void (*parsed_data_handler)(void *state, ParsedData *parsed_data, size_t n_parsed_data) +) { // read packets from handle struct pcap_pkthdr *packet_data; const byte_t *packet; @@ -29,7 +31,7 @@ void analyzer_loop(TCPStream *stream, pcap_t *pcap_handle, void (*parsed_data_ha // if there's parsed data - call the parsed data handler if (n_parsed_data > 0) - parsed_data_handler(parsed_data, n_parsed_data); + parsed_data_handler(state, parsed_data, n_parsed_data); // free the entire parsed data memory free_parsed_data(parsed_data, n_parsed_data); @@ -38,11 +40,12 @@ void analyzer_loop(TCPStream *stream, pcap_t *pcap_handle, void (*parsed_data_ha } // file analyzer parsed data handler -void file_parsed_data_handler(ParsedData *parsed_data, size_t n_parsed_data) { +void file_parsed_data_handler(void *state, ParsedData *parsed_data, size_t n_parsed_data) { + int *fights_count = state; for (size_t i = 0; i < n_parsed_data; i++) { switch (parsed_data[i].data_type) { case TYPE_FIGHT_RESULTS: - printf("\nFIGHT:\n"); + printf("\nFIGHT %d:\n", ++*fights_count); render_fight_results(*(FightResults *)parsed_data[i].data); break; } @@ -62,7 +65,8 @@ void analyze_file(char *path) { // analyze the file TCPStream stream = new_stream(); - analyzer_loop(&stream, handle, file_parsed_data_handler); + int state = 0; + analyzer_loop(&stream, handle, &state, file_parsed_data_handler); destroy_stream(stream); pcap_close(handle); } -- cgit v1.2.3 From 4add8c9c34c48f01de58b3b41e79a768063f7acf Mon Sep 17 00:00:00 2001 From: Mosder Date: Thu, 23 Jul 2026 20:04:32 +0200 Subject: feat: redirected runtime errors to stderr --- analyzer/analyzer.c | 2 +- analyzer/utils/memory.c | 8 ++++---- csv_table_gen.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'analyzer/analyzer.c') diff --git a/analyzer/analyzer.c b/analyzer/analyzer.c index bd24613..aa4a645 100644 --- a/analyzer/analyzer.c +++ b/analyzer/analyzer.c @@ -59,7 +59,7 @@ void analyze_file(char *path) { // handle errors if (handle == NULL) { - printf("Couldn't open the specified file:\n%s\n", errbuf); + fprintf(stderr, "Couldn't open the specified file:\n%s\n", errbuf); return; } diff --git a/analyzer/utils/memory.c b/analyzer/utils/memory.c index 0a43bd3..91c5f18 100644 --- a/analyzer/utils/memory.c +++ b/analyzer/utils/memory.c @@ -11,12 +11,12 @@ void *safe_malloc(size_t size) { if (p) return p; if (i < MAX_ALLOC_ATTEMPTS - 1) { - printf("Failed malloc. Retrying in 1s...\n"); + fprintf(stderr, "Failed malloc. Retrying in 1s...\n"); sleep(1); } } - printf("ERROR: Failed malloc %d times. Exiting...", MAX_ALLOC_ATTEMPTS); + fprintf(stderr, "FATAL: Failed malloc %d times. Exiting...", MAX_ALLOC_ATTEMPTS); exit(EXIT_FAILURE); } @@ -29,12 +29,12 @@ void *safe_realloc(void *ptr, size_t size) { if (p) return p; if (i < MAX_ALLOC_ATTEMPTS - 1) { - printf("Failed realloc. Retrying in 1s...\n"); + fprintf(stderr, "Failed realloc. Retrying in 1s...\n"); sleep(1); } } - printf("ERROR: Failed realloc %d times. Exiting...", MAX_ALLOC_ATTEMPTS); + fprintf(stderr, "FATAL: Failed realloc %d times. Exiting...", MAX_ALLOC_ATTEMPTS); exit(EXIT_FAILURE); } diff --git a/csv_table_gen.c b/csv_table_gen.c index 0bfd51a..ae80b78 100644 --- a/csv_table_gen.c +++ b/csv_table_gen.c @@ -80,7 +80,7 @@ void create_c(char *path, char *h_path, char *from, char *to, char from_to_array fprintf(fp, "const char *get_%s_from_%s(int %s) {\n", to, from, from); fprintf(fp, " if (%s >= 0 && %s < %s)\n", from, from, arr_len_name); fprintf(fp, " return %s[%s];\n", arr_name, from); - fprintf(fp, " printf(\"%s = %%d is outside of table range [0; %%d]\\n\", %s, %s - 1);\n", from, from, arr_len_name); + fprintf(fp, " fprintf(stderr, \"%s = %%d is outside of table range [0; %%d]\\n\", %s, %s - 1);\n", from, from, arr_len_name); fprintf(fp, " return \"null\";\n"); fprintf(fp, "}"); -- cgit v1.2.3