From 0f75aeea7a25af659549937ac2105535418a17f5 Mon Sep 17 00:00:00 2001 From: Lorenz Stechauner Date: Sat, 10 Dec 2022 01:33:16 +0100 Subject: [PATCH] Add tests for some utils --- .gitignore | 2 + Makefile | 10 ++++- src/client.c | 3 +- src/lib/utils.c | 96 +++++++++++++++++++++++++--------------------- src/lib/utils.h | 7 +--- test/mock_socket.c | 28 ++++++++++++++ test/mock_socket.h | 20 ++++++++++ test/mock_ssl.c | 33 ++++++++++++++++ test/test_sock.c | 15 ++++++++ test/test_utils.c | 86 +++++++++++++++++++++++++++++++++++++++++ 10 files changed, 248 insertions(+), 52 deletions(-) create mode 100644 test/mock_socket.c create mode 100644 test/mock_socket.h create mode 100644 test/mock_ssl.c create mode 100644 test/test_sock.c create mode 100644 test/test_utils.c diff --git a/.gitignore b/.gitignore index d14ab51..4539b93 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ !src/** !doc !doc/** +!test +!test/** !Makefile !.gitignore !README.md diff --git a/Makefile b/Makefile index 4722e12..f4b225d 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ LIBS=-lssl -lcrypto -lmagic -lz -lmaxminddb -lbrotlienc DEBIAN_OPTS=-D CACHE_MAGIC_FILE="\"/usr/share/file/magic.mgc\"" -D PHP_FPM_SOCKET="\"/var/run/php/php7.4-fpm.sock\"" -.PHONY: all prod debug default permit clean +.PHONY: all prod debug default permit clean test all: prod default: bin bin/lib bin/libsesimos.so bin/sesimos prod: CFLAGS += -O3 @@ -14,6 +14,10 @@ debug: default debian: CFLAGS += $(DEBIAN_OPTS) debian: prod +test: CFLAGS += -include test/mock_*.h +test: bin bin/test + bin/test + bin: mkdir -p bin @@ -22,6 +26,10 @@ bin/lib: mkdir -p bin/lib +bin/test: test/mock_*.c test/test_*.c src/lib/utils.c src/lib/sock.c + $(CC) -o $@ $(CFLAGS) $^ -lcriterion + + bin/%.o: src/%.c $(CC) -c -o $@ $(CFLAGS) $< diff --git a/src/client.c b/src/client.c index aebc0a8..145bd24 100644 --- a/src/client.c +++ b/src/client.c @@ -179,8 +179,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int int change_proto = strncmp(uri.uri, "/.well-known/", 13) != 0 && !client->enc; if (strcmp(uri.uri, buf0) != 0 || change_proto) { res.status = http_get_status(308); - size = sizeof(buf0); - url_encode(uri.uri, buf0, &size); + size = url_encode(uri.uri, strlen(uri.uri), buf0, sizeof(buf0)); if (change_proto) { p_len = snprintf(buf1, sizeof(buf1), "https://%s%s", host, buf0); if (p_len < 0 || p_len >= sizeof(buf1)) { diff --git a/src/lib/utils.c b/src/lib/utils.c index f4d0937..9f89a49 100644 --- a/src/lib/utils.c +++ b/src/lib/utils.c @@ -15,69 +15,77 @@ char *log_prefix; +static const char base64_encode_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const int base64_mod_table[3] = {0, 2, 1}; + + char *format_duration(unsigned long micros, char *buf) { if (micros < 10000) { sprintf(buf, "%.1f ms", (double) micros / 1000); - } else if (micros < 1000000) { - sprintf(buf, "%li ms", micros / 1000); - } else if (micros < 60000000) { + } else if (micros < 1000000 - 1000) { + sprintf(buf, "%.0f ms", (double) micros / 1000); + } else if (micros < 60000000 - 1000000) { sprintf(buf, "%.1f s", (double) micros / 1000000); } else if (micros < 6000000000) { sprintf(buf, "%.1f min", (double) micros / 1000000 / 60); } else { - sprintf(buf, "%li min", micros / 1000000 / 60); + sprintf(buf, "%.0f min", (double) micros / 1000000 / 60); } return buf; } -int url_encode_component(const char *str, char *enc, long *size) { - char *ptr = enc; - char ch; - memset(enc, 0, *size); - for (int i = 0; i < strlen(str); i++, ptr++) { - if ((ptr - enc) >= *size) { - return -1; +int url_encode_component(const void *in, size_t size_in, char *out, size_t size_out) { + int size = 0; + + // Encode control characters + for (int i = 0; i < size_in; i++) { + unsigned char ch = ((unsigned char *) in)[i]; + if (ch == ' ') { + ch = '+'; + } else if ( + ch <= 0x20 || ch >= 0x7F || + !((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || + ch == '-' || ch == '_' || ch == '.' || ch == '!' || ch == '~' || ch == '*' || ch == '\'' || + ch == '(' || ch == ')') + ) { + size += 3; + if (size < size_out) sprintf(out + size - 3, "%%%02X", ch); + ch = 0; } - ch = str[i]; - if (ch == ':' || ch == '/' || ch == '?' || ch == '#' || ch == '[' || ch == ']' || ch == '@' || ch == '!' || - ch == '$' || ch == '&' || ch == '\'' || ch == '(' || ch == ')' || ch == '*' || ch == '+' || ch == ',' || - ch == ';' || ch == '=' || ch < ' ' || ch > '~') { - if ((ptr - enc + 2) >= *size) { - return -1; - } - sprintf(ptr, "%%%02X", ch); - ptr += 2; - } else if (ch == ' ') { - ptr[0] = '+'; - } else { - ptr[0] = ch; + + if (ch != 0) { + size++; + if (size < size_out) out[size - 1] = (char) ch; } } - *size = ptr - enc; - return 0; + + // Set terminating null byte + if (size_out > 0) out[size < size_out ? size : size_out - 1] = 0; + + // Return theoretical size + return size; } -int url_encode(const char *str, char *enc, long *size) { - char *ptr = enc; - unsigned char ch; - memset(enc, 0, *size); - for (int i = 0; i < strlen(str); i++, ptr++) { - if ((ptr - enc) >= *size) { - return -1; - } - ch = str[i]; - if (ch > 0x7F || ch == ' ') { - if ((ptr - enc + 2) >= *size) { - return -1; - } - sprintf(ptr, "%%%02X", ch); - ptr += 2; +int url_encode(const void *in, size_t size_in, char *out, size_t size_out) { + int size = 0; + + // Encode control characters + for (int i = 0; i < size_in; i++) { + unsigned char ch = ((unsigned char *) in)[i]; + if (ch <= 0x20 || ch >= 0x7F) { + size += 3; + if (size < size_out) sprintf(out + size - 3, "%%%02X", ch); } else { - ptr[0] = (char) ch; + size++; + if (size < size_out) out[size - 1] = (char) ch; } } - *size = ptr - enc; - return 0; + + // Set terminating null byte + if (size_out > 0) out[size < size_out ? size : size_out - 1] = 0; + + // Return theoretical size + return size; } int url_decode(const char *str, char *dec, long *size) { diff --git a/src/lib/utils.h b/src/lib/utils.h index 4547279..ea2bb03 100644 --- a/src/lib/utils.h +++ b/src/lib/utils.h @@ -21,9 +21,6 @@ extern char *log_prefix; -static const char base64_encode_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -static const int base64_mod_table[3] = {0, 2, 1}; - #define out_1(fmt) fprintf(stdout, "%s" fmt "\n", log_prefix) #define out_2(fmt, args...) fprintf(stdout, "%s" fmt "\n", log_prefix, args) @@ -37,9 +34,9 @@ static const int base64_mod_table[3] = {0, 2, 1}; char *format_duration(unsigned long micros, char *buf); -int url_encode_component(const char *str, char *enc, long *size); +int url_encode_component(const void *in, size_t size_in, char *out, size_t size_out); -int url_encode(const char *str, char *enc, long *size); +int url_encode(const void *in, size_t size_in, char *out, size_t size_out); int url_decode(const char *str, char *dec, long *size); diff --git a/test/mock_socket.c b/test/mock_socket.c new file mode 100644 index 0000000..bdeb817 --- /dev/null +++ b/test/mock_socket.c @@ -0,0 +1,28 @@ + +#include +#include + +#include "mock_socket.h" + +int mock_socket_send_mode; + +static int sockets[256] = {0}; +static int n_sockets = 0; + +int mock_socket(int domain, int type, int protocol) { + printf("SOCKET\n"); + return (n_sockets++) + 100; +} + +ssize_t mock_send(int fd, const void *buf, size_t n, int flags) { + printf("SEND\n"); + if (mock_socket_send_mode == MOCK_SOCKET_MODE_EINTR) { + errno = EINTR; + return rand() % ((ssize_t) n) - 1; + } else if (mock_socket_send_mode == MOCK_SOCKET_MODE_CLOSED) { + errno = 0; // TODO + return -1; + } + + return (ssize_t) n; +} diff --git a/test/mock_socket.h b/test/mock_socket.h new file mode 100644 index 0000000..fc991fb --- /dev/null +++ b/test/mock_socket.h @@ -0,0 +1,20 @@ + +#ifndef SESIMOS_MOCK_SOCKET_H +#define SESIMOS_MOCK_SOCKET_H + +#include + +#define MOCK_SOCKET_MODE_SUCCESS 0 +#define MOCK_SOCKET_MODE_EINTR 1 +#define MOCK_SOCKET_MODE_CLOSED 2 + +#define socket(args...) mock_socket(args) +#define send(args...) mock_send(args) + +extern int mock_socket_send_mode; + +int mock_socket(int domain, int type, int protocol); + +ssize_t mock_send(int fd, const void *buf, size_t n, int flags); + +#endif //SESIMOS_MOCK_SOCKET_H diff --git a/test/mock_ssl.c b/test/mock_ssl.c new file mode 100644 index 0000000..55bdcd9 --- /dev/null +++ b/test/mock_ssl.c @@ -0,0 +1,33 @@ + +#include + + +int SSL_write(SSL *ssl, const void *buf, int num) { + return num; +} + +int SSL_read(SSL *ssl, void *buf, int num) { + return num; +} + +int SSL_peek(SSL *ssl, void *buf, int num) { + return num; +} + +int SSL_get_error(const SSL *s, int ret_code) { + return 0; +} + +const char *ERR_reason_error_string(unsigned long e) { + return ""; +} + +int SSL_shutdown(SSL *s) { + return 0; +} + +void SSL_free(SSL *ssl) {} + +unsigned long ERR_get_error(void) { + return 0; +} diff --git a/test/test_sock.c b/test/test_sock.c new file mode 100644 index 0000000..eff73c4 --- /dev/null +++ b/test/test_sock.c @@ -0,0 +1,15 @@ + +#include +#include "mock_socket.h" +#include "../src/lib/sock.h" + + +Test(sock, sock_send_1) { + int fd = socket(AF_INET6, SOCK_STREAM, 0); + sock s; + s.enc = 0; + s.socket = fd; + + long ret = sock_send(&s, "Hello", 5, 0); + cr_assert_eq(ret, 5); +} diff --git a/test/test_utils.c b/test/test_utils.c new file mode 100644 index 0000000..912a017 --- /dev/null +++ b/test/test_utils.c @@ -0,0 +1,86 @@ + +#include +#include + +#include "../src/lib/utils.h" + +struct url_encode_t { + long in_size; + char in[256]; + long exp_size; + char exp[256]; +}; + +struct format_duration_t { + unsigned long micros; + char exp[16]; +}; + +ParameterizedTestParameters(utils, url_encode) { + static struct url_encode_t params[] = { + {0, "", 0, ""}, + {9, "Test Text", 11, "Test%20Text"}, + {21, "Text\0with\0null\0bytes\0", 29, "Text%00with%00null%00bytes%00"}, + {59, "Text&with+some/strange_symbols-or#something?I%don't|know...", 59, "Text&with+some/strange_symbols-or#something?I%don't|know..."}, + {33, "Data\x12With\x13Some" "\xFF" "Control" "\xFE" "Characters", 41, "Data%12With%13Some%FFControl%FECharacters"} + }; + return cr_make_param_array(struct url_encode_t, params, sizeof(params) / sizeof(struct url_encode_t)); +} + +ParameterizedTest(struct url_encode_t *param, utils, url_encode) { + char out[256]; + cr_assert_eq(url_encode(param->in, param->in_size, out, sizeof(out)), param->exp_size); + cr_assert_arr_eq(out, param->exp, param->exp_size + 1); +} + +Test(utils, url_encode_bytes) { + char out[4]; + char exp[4]; + + for (int i = 0; i < 256; i++) { + unsigned char ch = i; + if (ch <= 0x20 || ch >= 0x7F) { + cr_assert_eq(url_encode(&ch, 1, out, sizeof(out)), 3); + sprintf(exp, "%%%02X", ch); + cr_assert_str_eq(out, exp); + } else { + cr_assert_eq(url_encode(&ch, 1, out, sizeof(out)), 1); + sprintf(exp, "%c", ch); + cr_assert_str_eq(out, exp); + } + } +} + +Test(utils, url_encode_invalid) { + cr_assert_eq(url_encode("Hello", 5, NULL, 0), 5); +} + +ParameterizedTestParameters(utils, format_duration) { + static struct format_duration_t params[] = { + {0, "0.0 ms"}, + {1, "0.0 ms"}, + {90, "0.1 ms"}, + {100, "0.1 ms"}, + {110, "0.1 ms"}, + {900, "0.9 ms"}, + {1000, "1.0 ms"}, + {9000, "9.0 ms"}, + {9899, "9.9 ms"}, + {9999, "10.0 ms"}, + {10000, "10 ms"}, + {11999, "12 ms"}, + {999999, "1.0 s"}, + {1000000, "1.0 s"}, + {3000000, "3.0 s"}, + {1000000 * 60, "1.0 min"}, + {1000000 * 60 * 30L - 30000000, "29.5 min"}, + {1000000 * 60 * 60L, "60.0 min"}, + {1000000 * 60 * 120L, "120 min"}, + }; + return cr_make_param_array(struct format_duration_t, params, sizeof(params) / sizeof(struct format_duration_t)); +} + +ParameterizedTest(struct format_duration_t *param, utils, format_duration) { + char buf[16]; + cr_assert_str_eq(format_duration(param->micros, buf), param->exp); +}