diff options
| author | Maciej Borowiec <81316152+Mosder@users.noreply.github.com> | 2026-07-23 20:17:15 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-07-23 20:17:15 +0200 |
| commit | 3419cad016b3c34d0630456d4390c70063462d91 (patch) | |
| tree | 3a1aa4416e45f134d38f83d4ea2285f1972f9391 /analyzer/utils | |
| parent | 2e3f7fe39791cd671a68330f7ba7a5bb5fc6f9ae (diff) | |
| parent | 682f9fc8288fdd01de1d88af87495d9e43f56b06 (diff) | |
LGTM
Diffstat (limited to 'analyzer/utils')
| -rw-r--r-- | analyzer/utils/fs.c | 13 | ||||
| -rw-r--r-- | analyzer/utils/fs.h | 11 | ||||
| -rw-r--r-- | analyzer/utils/memory.c | 62 | ||||
| -rw-r--r-- | analyzer/utils/memory.h | 46 | ||||
| -rw-r--r-- | analyzer/utils/roman.c | 15 | ||||
| -rw-r--r-- | analyzer/utils/roman.h | 29 | ||||
| -rw-r--r-- | analyzer/utils/tcp.c | 177 | ||||
| -rw-r--r-- | analyzer/utils/tcp.h | 69 | ||||
| -rw-r--r-- | analyzer/utils/tcp_seq_math.c | 41 | ||||
| -rw-r--r-- | analyzer/utils/tcp_seq_math.h | 28 | ||||
| -rw-r--r-- | analyzer/utils/time.c | 2 |
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> |
