aboutsummaryrefslogtreecommitdiff
path: root/analyzer/utils
diff options
context:
space:
mode:
authorMaciej Borowiec <81316152+Mosder@users.noreply.github.com>2026-07-23 20:17:15 +0200
committerGitHub <noreply@github.com>2026-07-23 20:17:15 +0200
commit3419cad016b3c34d0630456d4390c70063462d91 (patch)
tree3a1aa4416e45f134d38f83d4ea2285f1972f9391 /analyzer/utils
parent2e3f7fe39791cd671a68330f7ba7a5bb5fc6f9ae (diff)
parent682f9fc8288fdd01de1d88af87495d9e43f56b06 (diff)
LGTM
Diffstat (limited to 'analyzer/utils')
-rw-r--r--analyzer/utils/fs.c13
-rw-r--r--analyzer/utils/fs.h11
-rw-r--r--analyzer/utils/memory.c62
-rw-r--r--analyzer/utils/memory.h46
-rw-r--r--analyzer/utils/roman.c15
-rw-r--r--analyzer/utils/roman.h29
-rw-r--r--analyzer/utils/tcp.c177
-rw-r--r--analyzer/utils/tcp.h69
-rw-r--r--analyzer/utils/tcp_seq_math.c41
-rw-r--r--analyzer/utils/tcp_seq_math.h28
-rw-r--r--analyzer/utils/time.c2
11 files changed, 472 insertions, 21 deletions
diff --git a/analyzer/utils/fs.c b/analyzer/utils/fs.c
index 76fb581..5e3eb1c 100644
--- a/analyzer/utils/fs.c
+++ b/analyzer/utils/fs.c
@@ -1,8 +1,9 @@
-#include "utils.h"
+#include "utils/fs.h"
-#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <sys/stat.h>
-#include <unistd.h>
void get_home_dir(char *home_dir) {
strcpy(home_dir, getenv("HOME"));
@@ -35,9 +36,3 @@ void create_necessary_directories() {
mkdir(capture_dir, 0755);
mkdir(saves_dir, 0755);
}
-
-void write_file(char *path, void *contents, int contents_len, int flags, mode_t permissions) {
- int fd = open(path, flags, permissions);
- write(fd, contents, contents_len);
- close(fd);
-}
diff --git a/analyzer/utils/fs.h b/analyzer/utils/fs.h
index 6335799..8083ddc 100644
--- a/analyzer/utils/fs.h
+++ b/analyzer/utils/fs.h
@@ -1,8 +1,6 @@
// utils/fs.h
// functions for operating on the file system, i.e. creating directories, writing files, etc.
-#include <sys/types.h>
-
// get main directory used by the program
// params:
// - main_dir - buffer to save the directory to
@@ -20,12 +18,3 @@ extern void get_saves_dir(char *saves_dir);
// create directories that will be used by the program
extern void create_necessary_directories();
-
-// writes file to the specified path
-// params:
-// - path - path to write the file to
-// - contents - contents of the file
-// - contents_len - amount of bytes of contents to write
-// - flags - flags to open the file with
-// - permissions - permissions of the written file
-extern void write_file(char *path, void *contents, int contents_len, int flags, mode_t permissions);
diff --git a/analyzer/utils/memory.c b/analyzer/utils/memory.c
new file mode 100644
index 0000000..91c5f18
--- /dev/null
+++ b/analyzer/utils/memory.c
@@ -0,0 +1,62 @@
+#include "memory.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void *safe_malloc(size_t size) {
+ for (size_t i = 0; i < MAX_ALLOC_ATTEMPTS; i++) {
+ void *p = malloc(size);
+ if (p)
+ return p;
+ if (i < MAX_ALLOC_ATTEMPTS - 1) {
+ fprintf(stderr, "Failed malloc. Retrying in 1s...\n");
+ sleep(1);
+ }
+ }
+
+ fprintf(stderr, "FATAL: Failed malloc %d times. Exiting...", MAX_ALLOC_ATTEMPTS);
+ exit(EXIT_FAILURE);
+}
+
+void *safe_realloc(void *ptr, size_t size) {
+ if (!ptr)
+ return safe_malloc(size);
+
+ for (size_t i = 0; i < MAX_ALLOC_ATTEMPTS; i++) {
+ void *p = realloc(ptr, size);
+ if (p)
+ return p;
+ if (i < MAX_ALLOC_ATTEMPTS - 1) {
+ fprintf(stderr, "Failed realloc. Retrying in 1s...\n");
+ sleep(1);
+ }
+ }
+
+ fprintf(stderr, "FATAL: Failed realloc %d times. Exiting...", MAX_ALLOC_ATTEMPTS);
+ exit(EXIT_FAILURE);
+}
+
+// expand an array
+// returns pointer to the expanded array
+void *array_expand(void *arr, size_t el_size, size_t *cap, size_t to_fit) {
+ while (*cap < to_fit)
+ *cap *= 2;
+ return safe_realloc(arr, *cap * el_size);
+}
+
+void *array_append(void *arr, size_t el_size, size_t *len, size_t *cap, void *val) {
+ if (*len >= *cap)
+ arr = array_expand(arr, el_size, cap, *len + 1);
+ memcpy((char *)arr + (*len)++ * el_size, val, el_size);
+ return arr;
+}
+
+void *array_concat(void *arr1, size_t el_size, size_t *len1, size_t *cap, void *arr2, size_t len2) {
+ if (*len1 + len2 > *cap)
+ arr1 = array_expand(arr1, el_size, cap, *len1 + len2);
+ memcpy((char *)arr1 + *len1 * el_size, arr2, len2 * el_size);
+ *len1 += len2;
+ return arr1;
+}
diff --git a/analyzer/utils/memory.h b/analyzer/utils/memory.h
new file mode 100644
index 0000000..0973623
--- /dev/null
+++ b/analyzer/utils/memory.h
@@ -0,0 +1,46 @@
+// utils/memory.h
+// helper functions for dealing with memory management
+
+#include <stddef.h>
+
+#define ARR_LEN(arr) (sizeof(arr) / sizeof(arr[0]))
+
+#define MAX_ALLOC_ATTEMPTS 3
+
+// tries malloc MAX_ALLOC_ATTEMPTS times, if all fail - exits the program
+// params:
+// - size - count of bytes to allocate
+// returns:
+// pointer to the allocated memory
+extern void *safe_malloc(size_t size);
+
+// tries realloc MAX_ALLOC_ATTEMPTS times, if all fail - exits the program
+// params:
+// - ptr - pointer to the memory to reallocate
+// - size - new size of the memory
+// returns:
+// pointer to the reallocated memory
+extern void *safe_realloc(void *ptr, size_t size);
+
+// append to array, expand if necessary
+// params:
+// - arr - array to append to
+// - el_size - size of an array element
+// - len - pointer to the length of the array
+// - cap - pointer to the capacity of the array
+// - val - pointer to the value to append to the array
+// returns:
+// new pointer to the array (different if it was realloced)
+extern void *array_append(void *arr, size_t el_size, size_t *len, size_t *cap, void *val);
+
+// concatenate 2 arrays, expand if necessary
+// params:
+// - arr1 - first array (destination)
+// - el_size - size of an array element
+// - len1 - pointer to the length of arr1
+// - cap - pointer to the capacity of arr1
+// - arr2 - second array
+// - len2 - length of arr2
+// returns:
+// new pointer to the array (different if it was realloced)
+extern void *array_concat(void *arr1, size_t el_size, size_t *len1, size_t *cap, void *arr2, size_t len2);
diff --git a/analyzer/utils/roman.c b/analyzer/utils/roman.c
new file mode 100644
index 0000000..4fbe5c0
--- /dev/null
+++ b/analyzer/utils/roman.c
@@ -0,0 +1,15 @@
+#include "utils/roman.h"
+
+#include <string.h>
+
+#include "utils/memory.h"
+
+void to_roman(char *buffer, int number) {
+ buffer[0] = '\0';
+ for (size_t i = 0; i < ARR_LEN(roman_symbols); i++) {
+ for (size_t j = 0; j < number / roman_symbols[i].num; j++) {
+ strcat(buffer, roman_symbols[i].symbol);
+ }
+ number %= roman_symbols[i].num;
+ }
+}
diff --git a/analyzer/utils/roman.h b/analyzer/utils/roman.h
new file mode 100644
index 0000000..cebd16e
--- /dev/null
+++ b/analyzer/utils/roman.h
@@ -0,0 +1,29 @@
+// utils/roman.h
+// functions for roman numbers
+
+typedef struct {
+ int num;
+ char *symbol;
+} RomanSymbol;
+
+static const RomanSymbol roman_symbols[] = {
+ {1000, "M"},
+ {900, "CM"},
+ {500, "D"},
+ {400, "CD"},
+ {100, "C"},
+ {90, "XC"},
+ {50, "L"},
+ {40, "XL"},
+ {10, "X"},
+ {9, "IX"},
+ {5, "V"},
+ {4, "IV"},
+ {1, "I"},
+};
+
+// convert a arabic number to roman number
+// params:
+// buffer - buffer to save the roman number to
+// number - arabic number to convert
+extern void to_roman(char *buffer, int number);
diff --git a/analyzer/utils/tcp.c b/analyzer/utils/tcp.c
new file mode 100644
index 0000000..6309e64
--- /dev/null
+++ b/analyzer/utils/tcp.c
@@ -0,0 +1,177 @@
+#include "utils/tcp.h"
+
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "utils/memory.h"
+#include "utils/tcp_seq_math.h"
+
+// initalize default TCP segment
+TCPSegment new_segment() {
+ // clang-format off
+ TCPSegment segment = {
+ .payload = NULL,
+ .len = 0,
+ .seq = 0,
+ .port = 0
+ };
+ // clang-format on
+ 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 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;
+ segment.len = packet_data->caplen - total_header_len;
+ segment.payload = safe_malloc(segment.len);
+ memcpy(segment.payload, packet + total_header_len, segment.len);
+ return segment;
+}
+
+TCPStream new_stream() {
+ // clang-format off
+ TCPStream stream = {
+ .data = safe_malloc(INIT_STREAM_CAPACITY),
+ .len = 0,
+ .capacity = INIT_STREAM_CAPACITY,
+ .seq = 0,
+ .port = 0,
+ .pending = NULL
+ };
+ // clang-format on
+ 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);
+}
+
+// add segment directly to stream
+void add_segment(TCPStream *stream, TCPSegment segment, size_t data_start) {
+ stream->data = array_concat(stream->data, 1, &stream->len, &stream->capacity, segment.payload + data_start, segment.len - data_start);
+}
+
+// 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 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;
+ }
+
+ // 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..13cbb15
--- /dev/null
+++ b/analyzer/utils/tcp.h
@@ -0,0 +1,69 @@
+// utils/tcp.h
+// functions for handling TCP streams/segments
+
+#include <pcap.h>
+
+#include "structures/heap.h"
+
+#ifndef TCP_H
+#define TCP_H
+
+typedef unsigned char byte_t;
+
+// struct to persistently store TCP segments
+// payload field is malloced and needs to be freed
+typedef struct {
+ 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;
+
+// get TCP segment from captured packet
+// params:
+// - packet - captured packet
+// - packet_data - metadata about packet
+// returns:
+// 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
+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
+ 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;
+
+#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);
+
+#endif
diff --git a/analyzer/utils/tcp_seq_math.c b/analyzer/utils/tcp_seq_math.c
new file mode 100644
index 0000000..2cc6e3f
--- /dev/null
+++ b/analyzer/utils/tcp_seq_math.c
@@ -0,0 +1,41 @@
+#include "utils/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 <stdint.h>
+
+// 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);
diff --git a/analyzer/utils/time.c b/analyzer/utils/time.c
index 72c0bc7..d95da65 100644
--- a/analyzer/utils/time.c
+++ b/analyzer/utils/time.c
@@ -1,4 +1,4 @@
-#include "time.h"
+#include "utils/time.h"
#include <stdio.h>
#include <time.h>