diff --git a/client/Makefile b/client/Makefile index 66139d6..85c549f 100644 --- a/client/Makefile +++ b/client/Makefile @@ -1,6 +1,6 @@ CC = gcc FLAGS = -Wall -Wextra -Wshadow -SRC = src/main.c src/sock.c src/http.c src/utils.c src/fraction.c src/crc32.c +SRC = src/main.c src/sock.c src/http.c src/utils.c src/fraction.c src/crc32.c src/log.c OUT = client all: diff --git a/client/include/fraction.h b/client/include/fraction.h index ccffe4f..71513c2 100644 --- a/client/include/fraction.h +++ b/client/include/fraction.h @@ -10,6 +10,8 @@ #include +#include "log.h" + #define MAGIC 0xdeadbeef typedef struct { diff --git a/client/include/http.h b/client/include/http.h index 2102a87..c3a0df0 100644 --- a/client/include/http.h +++ b/client/include/http.h @@ -7,6 +7,7 @@ #include "sock.h" #include "utils.h" +#include "log.h" // error codes #define HTTP_SUCCESS 0 #define HTTP_SOCKET_ERR -1 @@ -26,6 +27,7 @@ typedef struct { void http_free(http_res_t *res); int http_get(int sfd, const char *path, http_res_t *res); +/* Keeping in case we end up using it sometime */ int http_post(int sfd,const char* path,const char *content_type, const char* parameters, http_res_t *res); long parse_http_status_code(const char *buf); diff --git a/client/include/log.h b/client/include/log.h new file mode 100644 index 0000000..d8b3e3a --- /dev/null +++ b/client/include/log.h @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2020 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See `log.c` for details. + */ + +#ifndef LOG_H +#define LOG_H + +#include +#include +#include +#include + +#define LOG_VERSION "0.1.0" +#define LOG_USE_COLOR 1 + + +typedef struct { + va_list ap; + const char *fmt; + const char *file; + struct tm *time; + void *udata; + int line; + int level; +} log_Event; + +typedef void (*log_LogFn)(log_Event *ev); +typedef void (*log_LockFn)(bool lock, void *udata); + +enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL }; + +#define log_trace(...) log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__) +#define log_debug(...) log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__) +#define log_info(...) log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__) +#define log_warn(...) log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__) +#define log_error(...) log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__) +#define log_fatal(...) log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__) + +const char *log_level_string(int level); +void log_set_lock(log_LockFn fn, void *udata); +void log_set_level(int level); +void log_set_quiet(bool enable); +int log_add_callback(log_LogFn fn, void *udata, int level); +int log_add_fp(FILE *fp, int level); + +void log_log(int level, const char *file, int line, const char *fmt, ...); +int log_get_level(void); + +#endif diff --git a/client/src/fraction.c b/client/src/fraction.c index b5fe91a..3a17285 100644 --- a/client/src/fraction.c +++ b/client/src/fraction.c @@ -9,13 +9,13 @@ int download_fraction(int sfd, char *url, fraction_t *fraction) { // Parse the URL to get the path path = get_path_from_url(url); if (!path) { - fprintf(stderr, "Invalid URL: %s\n", url); + log_error("Invalid URL: %s\n", url); return 1; } // Perform the HTTP GET request if (http_get(sfd, path, &res) != HTTP_SUCCESS) { - fprintf(stderr, "Failed to download: %s\n", url); + log_error("Failed to download: %s\n", url); return 1; } @@ -45,7 +45,7 @@ int fraction_parse(char *data, size_t size, fraction_t *fraction) { // Ensure the data size is sufficient if (size < HEADER_SIZE) { - fprintf(stderr, "Insufficient size: %lu\n", size); + log_error("Insufficient size: %lu\n", size); return 1; } @@ -59,7 +59,7 @@ int fraction_parse(char *data, size_t size, fraction_t *fraction) { // Check the magic number if (!check_magic(magic)) { - fprintf(stderr, "Wrong magic number: %02x\n", magic); + log_error("Wrong magic number: %02x\n", magic); return 1; } @@ -67,7 +67,7 @@ int fraction_parse(char *data, size_t size, fraction_t *fraction) { data_size = size - HEADER_SIZE; fraction->data = malloc(data_size); if (!fraction->data) { - fprintf(stderr, "Failed to allocate data for fraction\n"); + log_error("Failed to allocate data for fraction\n"); return 1; } // Set the extracted values in the fraction structure @@ -93,15 +93,19 @@ int compare_fractions(const void *a, const void *b) { } void print_fraction(fraction_t fraction) { - printf("Magic: 0x%08x\n", fraction.magic); - printf("Index: %u\n", fraction.index); - printf("IV: "); - for (size_t i = 0; i < sizeof(fraction.iv); i++) { - printf("%02x ", (unsigned char)fraction.iv[i]); - } - printf("\n"); - printf("CRC-32: 0x%08x\n", fraction.crc); - printf("Data size: %lu\n\n", fraction.data_size); + log_debug("Magic: 0x%08x\n", fraction.magic); + log_debug("Index: %u\n", fraction.index); + if (log_get_level() == LOG_DEBUG) { + char iv_str[sizeof(fraction.iv) * 3] = { + 0}; // 2 characters for hex + 1 for space + for (size_t i = 0; i < sizeof(fraction.iv); i++) { + snprintf(iv_str + i * 3, 4, "%02x ", (unsigned char)fraction.iv[i]); + } + log_debug("IV: %s\n", iv_str); + } + + log_debug("CRC-32: 0x%08x\n", fraction.crc); + log_debug("Data size: %lu\n\n", fraction.data_size); } int calc_crc(fraction_t *frac){ @@ -123,9 +127,9 @@ int calc_crc(fraction_t *frac){ uint32_t calculated_crc = crc32(buffer, offset); if (calculated_crc != frac->crc) { - printf("Checksum incorrect\n"); - printf("Checksum generated: %08X\n", calculated_crc); - printf("Checksum from fraction: %08X\n\n", frac->crc); + log_warn("Checksum incorrect\n"); + log_warn("Checksum generated: %08X\n", calculated_crc); + log_warn("Checksum from fraction: %08X\n\n", frac->crc); } return calculated_crc == frac->crc; @@ -135,7 +139,7 @@ int check_fractions(fraction_t *fraction, size_t size){ int res = 0; for(size_t i = 0; i < size; i++){ if (!calc_crc(&fraction[i])) { - fprintf(stderr, "Failed to validate integrity of fraction:\n"); + log_error("Failed to validate integrity of fraction:\n"); print_fraction(fraction[i]); res += 1; } diff --git a/client/src/http.c b/client/src/http.c index 355380a..15c19c8 100644 --- a/client/src/http.c +++ b/client/src/http.c @@ -22,11 +22,9 @@ const char *POST_REQ_TEMPLATE = "POST %s HTTP/1.1\r\nContent-Type: " /* Log a http_res_t */ void print_http_res(const http_res_t *res) { - printf("--[ STATUS CODE: %i ]--\n", res->status_code); - printf("--[ REQUEST ]--\n%s\n--[ REQUEST ]--\n", res->request); - // res->data is not null-terminated so use write here - write(1, res->data, res->size); - puts("--[ END ]--\n"); + log_debug("[STATUS CODE: %i ]", res->status_code); + log_debug("[REQUEST]\n%s", res->request); + log_debug("[END REQUEST]\n"); } /* Parse HTTP status code */ @@ -74,7 +72,7 @@ int parse_http_body(int sfd, char *src, char *dest, long content_length, long to body_start = strstr(src, "\r\n\r\n"); if (body_start == NULL) { - perror("Header delimeter not found\n"); + log_error("Header delimeter not found\n"); return HTTP_INVALID_RESPONSE; } body_start += 4; @@ -88,7 +86,7 @@ int parse_http_body(int sfd, char *src, char *dest, long content_length, long to if (content_length > received_length) { left_length = content_length - received_length; if (recv_response(sfd, dest + received_length, left_length) < 0) { - perror("Failed to receive left over data\n"); + log_error("Failed to receive left over data\n"); return HTTP_SOCKET_ERR; } } @@ -116,12 +114,12 @@ int http_post(int sfd, const char *path, strncpy(res->request, req_buffer, req_buf_len - 1); if (send_request(sfd, req_buffer) < 0) { - perror("Error: failed to send request\n"); + log_error("Error: failed to send request\n"); return HTTP_SOCKET_ERR; } - if (HTTP_VERBOSE) - puts("Sent POST request"); + + log_debug("Sent POST request\n"); /* Receive response from server */ total_bytes = 0; @@ -170,8 +168,8 @@ int http_post(int sfd, const char *path, return HTTP_INVALID_RESPONSE; } - if (HTTP_VERBOSE) - puts("Parsed response"); + + log_debug("Parsed response\n"); if (HTTP_VERBOSE > 1) print_http_res(res); @@ -198,12 +196,12 @@ int http_get(int sfd, const char *path, http_res_t *res) { req_buf_len = strlen(request_buf); if (send_request(sfd, request_buf) < 0) { - perror("Error: failed to send request\n"); + log_error("Error: failed to send request\n"); err = HTTP_SOCKET_ERR; goto error; } - VERBOSE_ONLY(puts("Sent GET request");) + log_debug("Sent GET request\n"); res->request = malloc(req_buf_len + 1); if (res->request == NULL) { @@ -237,7 +235,7 @@ int http_get(int sfd, const char *path, http_res_t *res) { // by this time buf will be null terminated - VERBOSE_ONLY(puts("Received data from server");) + log_debug("Received data from server"); /* Check if response starts with "HTTP" */ if (memcmp(buf, "HTTP", 4)) { @@ -273,9 +271,9 @@ int http_get(int sfd, const char *path, http_res_t *res) { goto error; } - VERBOSE_ONLY(puts("Parsed response");) + log_debug("Parsed response"); - VERY_VERBOSE_ONLY(print_http_res(res);) + print_http_res(res); return HTTP_SUCCESS; diff --git a/client/src/log.c b/client/src/log.c new file mode 100644 index 0000000..5eaaa16 --- /dev/null +++ b/client/src/log.c @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2020 rxi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "../include/log.h" + +#define MAX_CALLBACKS 32 + +typedef struct { + log_LogFn fn; + void *udata; + int level; +} Callback; + +struct { + void *udata; + log_LockFn lock; + int level; + bool quiet; + Callback callbacks[MAX_CALLBACKS]; +} L; + +static const char *level_strings[] = {"TRACE", "DEBUG", "INFO", + "WARN", "ERROR", "FATAL"}; + +#ifdef LOG_USE_COLOR +static const char *level_colors[] = {"\x1b[94m", "\x1b[36m", "\x1b[32m", + "\x1b[33m", "\x1b[31m", "\x1b[35m"}; +#endif + +static void stdout_callback(log_Event *ev) { + char buf[16]; + buf[strftime(buf, sizeof(buf), "%H:%M:%S", ev->time)] = '\0'; +#ifdef LOG_USE_COLOR + fprintf(ev->udata, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", buf, + level_colors[ev->level], level_strings[ev->level], ev->file, + ev->line); +#else + fprintf(ev->udata, "%s %-5s %s:%d: ", buf, level_strings[ev->level], ev->file, + ev->line); +#endif + vfprintf(ev->udata, ev->fmt, ev->ap); + fprintf(ev->udata, "\n"); + fflush(ev->udata); +} + +static void file_callback(log_Event *ev) { + char buf[64]; + buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0'; + fprintf(ev->udata, "%s %-5s %s:%d: ", buf, level_strings[ev->level], ev->file, + ev->line); + vfprintf(ev->udata, ev->fmt, ev->ap); + fprintf(ev->udata, "\n"); + fflush(ev->udata); +} + +static void lock(void) { + if (L.lock) { + L.lock(true, L.udata); + } +} + +static void unlock(void) { + if (L.lock) { + L.lock(false, L.udata); + } +} + +const char *log_level_string(int level) { return level_strings[level]; } + +void log_set_lock(log_LockFn fn, void *udata) { + L.lock = fn; + L.udata = udata; +} + +void log_set_level(int level) { L.level = level; } +int log_get_level(void) { return L.level; } + +void log_set_quiet(bool enable) { L.quiet = enable; } + +int log_add_callback(log_LogFn fn, void *udata, int level) { + for (int i = 0; i < MAX_CALLBACKS; i++) { + if (!L.callbacks[i].fn) { + L.callbacks[i] = (Callback){fn, udata, level}; + return 0; + } + } + return -1; +} + +int log_add_fp(FILE *fp, int level) { + return log_add_callback(file_callback, fp, level); +} + +static void init_event(log_Event *ev, void *udata) { + if (!ev->time) { + time_t t = time(NULL); + ev->time = localtime(&t); + } + ev->udata = udata; +} + +void log_log(int level, const char *file, int line, const char *fmt, ...) { + log_Event ev = { + .fmt = fmt, + .file = file, + .line = line, + .level = level, + }; + + lock(); + + if (!L.quiet && level >= L.level) { + init_event(&ev, stderr); + va_start(ev.ap, fmt); + stdout_callback(&ev); + va_end(ev.ap); + } + + for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) { + Callback *cb = &L.callbacks[i]; + if (level >= cb->level) { + init_event(&ev, cb->udata); + va_start(ev.ap, fmt); + cb->fn(&ev); + va_end(ev.ap); + } + } + + unlock(); +} diff --git a/client/src/main.c b/client/src/main.c index a27a753..e60eb88 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -6,6 +6,7 @@ #include "../include/http.h" #include "../include/sock.h" #include "../include/utils.h" +#include "../include/log.h" #define SERVER_IP "127.0.0.1" #define SERVER_PORT "8000" @@ -28,86 +29,72 @@ static void cleanup_fraction_array(fraction_t *array, int n_elem) { int main(void) { struct addrinfo hints, *ainfo; int sfd = -1; // to be extra professional - http_res_t http_fraction_res, http_post_res; + http_res_t http_fraction_res = {0}; + http_res_t http_post_res = {0}; char **fraction_links = NULL; fraction_t *fractions = NULL; + log_set_level(LOG_DEBUG); setup_hints(&hints); if (h_getaddrinfo(SERVER_IP, SERVER_PORT, &hints, &ainfo) != 0) { - fprintf(stderr, "Failed to resolve server address\n"); + log_error("Failed to resolve server address\n"); return EXIT_FAILURE; } printf("Connecting to: %s:%s\n", SERVER_IP, SERVER_PORT); sfd = create_sock_and_conn(ainfo); if (sfd == -1) { - fprintf(stderr, "Failed to create socket and connect\n"); + log_error("Failed to create socket and connect\n"); return EXIT_FAILURE; } freeaddrinfo(ainfo); if (http_get(sfd, "/", &http_fraction_res) != HTTP_SUCCESS) { - fprintf(stderr, "Failed to retrieve fraction links\n"); + log_error("Failed to retrieve fraction links\n"); goto cleanup; } int num_links = count_lines(http_fraction_res.data) + 1; fraction_links = malloc(num_links * sizeof(char *)); if (!fraction_links) { - fprintf(stderr, "Failed to allocate memory for fraction links\n"); - http_free(&http_fraction_res); + log_error("Failed to allocate memory for fraction links\n"); goto cleanup; } int lines_read = split_fraction_links(http_fraction_res.data, fraction_links, num_links); if (lines_read < 0) { - fprintf(stderr, "Failed to split fraction links\n"); - cleanup_char_array(fraction_links, num_links); - http_free(&http_fraction_res); + log_error("Failed to split fraction links\n"); goto cleanup; } fractions = malloc(lines_read * sizeof(fraction_t)); if (!fractions) { - fprintf(stderr, "Failed to allocate memory for fractions\n"); - cleanup_char_array(fraction_links, num_links); - http_free(&http_fraction_res); - http_free(&http_post_res); + log_error("Failed to allocate memory for fractions\n"); goto cleanup; } for (int i = 0; i < lines_read; i++) { if (download_fraction(sfd, fraction_links[i], &fractions[i]) != 0) { - fprintf(stderr, "Failed to download fraction\n"); + log_error("Failed to download fraction\n"); + goto cleanup; } } - puts("Downloaded fractions"); + log_info("Downloaded fractions"); qsort(fractions, lines_read, sizeof(fraction_t), compare_fractions); - if (check_fractions(fractions, lines_read)) { // if this works, s0s4 and skelly is to blame! - fprintf(stderr, "Fractions check failed\n"); - cleanup_char_array(fraction_links, num_links); - cleanup_fraction_array(fractions, lines_read); - http_free(&http_fraction_res); - http_free(&http_post_res); - goto cleanup; - } - puts("Verified fractions"); - - if (http_post(sfd, "/deadbeef", "plain/text", "{'downloaded':true}", - &http_post_res) != HTTP_SUCCESS) { - fprintf(stderr, "Failed to send POST request\n"); - cleanup_char_array(fraction_links, num_links); - http_free(&http_fraction_res); + if (check_fractions(fractions, lines_read) != 0) { // if this works, s0s4 and skelly is to blame! + log_error("Fractions check failed\n"); goto cleanup; } + log_info("Verified fractions"); + for (int i=0; i None: """Prepare a Fractionator object for reading and generating fractions.""" self.file_path: str = file_path diff --git a/server/utils.py b/server/utils.py index ed0000c..b0f3234 100644 --- a/server/utils.py +++ b/server/utils.py @@ -7,23 +7,29 @@ from cryptography.hazmat.primitives._cipheralgorithm import BlockCipherAlgorithm from cryptography.hazmat.primitives import padding + class AES_WITH_IV_HELPER: LENGTH_IV: int = 16 - def __init__(self, key: bytes, algorithm: Type[BlockCipherAlgorithm], mode: Type[modes.ModeWithInitializationVector]) -> None: + def __init__( + self, + key: bytes, + algorithm: Type[BlockCipherAlgorithm], + mode: Type[modes.ModeWithInitializationVector], + ) -> None: self.key = key - + self.algorithm = algorithm(self.key) self.mode = mode - + self.padder_ctx = padding.PKCS7(self.algorithm.block_size) - + self._iv: Optional[bytes] = None - + def pad(self, data: bytes) -> bytes: padder = self.padder_ctx.padder() return padder.update(data) + padder.finalize() - + def unpad(self, data: bytes) -> bytes: unpadder = self.padder_ctx.unpadder() return unpadder.update(data) + unpadder.finalize() @@ -48,6 +54,7 @@ def decrypt(self, data: bytes, iv: bytes) -> bytes: decryptor = self.get_cipher(iv).decryptor() return self.unpad(decryptor.update(data) + decryptor.finalize()) + def random_string(n: int = 16, sample: str = string.ascii_lowercase + string.digits): """Returns a random string using the characters defined in sample""" return "".join(random.choices(sample, k=n))