commit 48f773b3abbc8524ddb57b4a6a6d37a0749deac6 from: Evan Burkey date: Tue Jul 9 21:03:23 2024 UTC Implement Server (#1) - Generic Server struct - TCP and UDP Reviewed-on: https://git.burkey.co/eburk/libflint/pulls/1 commit - 074798ed626bafae8efcea1413bf3f9571338d70 commit + 48f773b3abbc8524ddb57b4a6a6d37a0749deac6 blob - 5cf184209f166fd81670f72bb612aa7a92629697 blob + 288ac9dbb72a00d89672c8a551b4ce624a7268c2 --- .gitea/workflows/jobs.yaml +++ .gitea/workflows/jobs.yaml @@ -14,7 +14,7 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y libbsd-dev cmake build-essential + sudo apt-get install -y libbsd-dev cmake build-essential netcat - name: Build and test run: | blob - c3157f7e765fc8a62f20693def316be0b5739d91 blob + 221cf5558f79da78e54a00bb1e33cb3b7b840279 --- .gitignore +++ .gitignore @@ -5,4 +5,7 @@ compile_commands.json site libflint.so test +tcptest +testrunner .idea +netmanual blob - a8dc82399ae04425657d6b38ea7d34122bda06b6 blob + fa09448298f620bf1b6c9500a71ad2f59b8ff92e --- CMakeLists.txt +++ CMakeLists.txt @@ -2,6 +2,10 @@ cmake_minimum_required(VERSION 3.17) project(flint C) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +if ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux")) + add_compile_definitions(flint __USE_XOPEN_EXTENDED) +endif() + set(CMAKE_C_STANDARD 99) include_directories(include) @@ -17,16 +21,17 @@ set(SOURCES src/utility.c src/crypto.c src/parsing.c + src/network.c ) if ((${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")) - add_library(flint ${SOURCES} src/macos/macos.c) + add_library(flint ${SOURCES} src/macos.c) else() add_library(flint ${SOURCES}) endif() if ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux")) - target_link_libraries(flint bsd) + target_link_libraries(flint pthread bsd) endif() if(${CMAKE_PROJECT_NAME} STREQUAL flint) @@ -34,8 +39,17 @@ if(${CMAKE_PROJECT_NAME} STREQUAL flint) target_include_directories(tests PRIVATE include) if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - target_link_libraries(tests flint bsd) + target_link_libraries(tests flint pthread bsd) else() - target_link_libraries(tests flint) + target_link_libraries(tests flint pthread) endif() + + add_executable(netmanual tests/netmanual.c) + target_include_directories(netmanual PRIVATE include) + + if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + target_link_libraries(netmanual flint pthread bsd) + else() + target_link_libraries(netmanual flint pthread) + endif() endif() blob - e4a25820995867a5ef353a5e246145fb255afef8 (mode 644) blob + /dev/null --- Makefile +++ /dev/null @@ -1,32 +0,0 @@ -.PHONY : clean - -CFLAGS = -std=c99 -Iinclude -pedantic -Wall -Wextra -LDFLAGS = -fPIC -shared - -TARGET = libflint.so -SRC != ls src/*.c -OBJ = $(SRC:./src/$.c=./obj/%.o) - -PREFIX = $(DESTDIR)/usr/local -LIBDIR = $(PREFIX)/lib - -all: $(TARGET) - -$(TARGET): $(OBJ) - cc $(CFLAGS) $(LDFLAGS) -o $(TARGET) $(OBJ) - -./obj/%.o: ./src/%.c - cc $(CFLAGS) -c $< -o $@ - -install: $(TARGET) - cp $(TARGET) $(LIBDIR) - -uninstall: - rm -f $(LIBDIR)/$(TARGET) - -clean: - rm -f $(TARGET) - rm -f test - -test: - cc $(CFLAGS) -o test tests/tests.c src/*.c blob - /dev/null blob + fbcb53112ec7ad10d51ee0c8136c63e0daa44f90 (mode 755) --- /dev/null +++ clanggen.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env sh + +set -e + +CFLAGS="-I./include -I/usr/local/include" + +rm -f compile_commands.json + +for f in $(find ./src -type f | grep -v macos); do + n=$(echo $f | grep -o '[^/]*$' | sed 's/c$/json/') + cc -MJ $n $CFLAGS -c $f +done + +rm *.o +sed -e '1s/^/[/' -e '$s/,$/]/' *.json > compile_commands.out +rm *.json +mv compile_commands.out compile_commands.json blob - c8603550b60da781c8d7936d293d293916f1ce73 (mode 755) blob + /dev/null --- build.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh -e - -# For building outside of CLion - -mkdir -p build -cd build -cmake .. -make -cp compile_commands.json .. -cd blob - 8537d0e64d60f6afc713db24394f31ec71301cab blob + dae3f84edc29eae7c81087250269466fff5d2326 --- docs/input.md +++ docs/input.md @@ -92,7 +92,8 @@ printf("%s\n", sp[0]); // Prints "Split" ### del_split -Frees all memory used by `split()`. Just like `split`, it does not touch the original string +Frees all memory used by `split()`. Just like `split`, it does not touch the original string. + ```c void del_split(char **sp); @@ -101,3 +102,19 @@ size_t sp_sz = 0; char **sp = split("Delete Me!", &sp_sz, " "); void del_split(sp); ``` + +### capture_system + +Runs a command on the system shell and returns stdout as a string. `buffsize` is the size of +the returned buffer that holds `stdout`. Passing `0` to `buffsize` will use the default buffer size of `1024`. + +User is responsible for freeing the returned string. + +```c +const char *capture_system(const char *cmd, int buffsize); + +/* Usage */ +const char *cap = capture_system("ls $HOME", 0); +printf("%s\n", cap); +free(cap); +``` \ No newline at end of file blob - /dev/null blob + b47a96c84956411541fa5365cf203828337e9174 (mode 644) --- /dev/null +++ docs/network.md @@ -0,0 +1,106 @@ +# network + +This module provides a generic `Server` type that abstracts away the setup and teardown of a socket + +## Enums + +### ServerType + +Types of servers. Currently supports TCP and UDP, will eventually add UNIX sockets. + +```c +typedef enum ServerType { + SERVERTYPE_TCP, + SERVERTYPE_UDP +} ServerType; +``` + +## Structs + +### Server + +Server is a generic abstraction over sockets. The type of the server is defined by `server_type`. + +```c +typedef struct Server { + ServerType server_type; + int fd; + int port; + void (*handler)(struct Server *s); +} Server; +``` + +## Functions + +### new_server + +Create a `Server*`. User is responsible for freeing the memory. + +```c +Server *new_server(ServerType type, const char *port, void(handler)(Server *s)); +``` + +### delete_server + +Frees the memory allocated for `Server*` and sets the pointer to `NULL`. + +```c +void delete_server(Server *s); +``` + +### serve + +Starts up the server. `backlog_size` is the size of the backlog buffer for the underlying socket. Use the macro +`DEFAULT_BACKLOG_SIZE` or pass `0` to use a reasonable default. + +```c +int serve(Server *s, int backlog_size); +``` + +### get_in_addr + +Convenience method to get an IP address from a `struct sockaddr_storage` of either IPV4 or IPV6. + +```c +void *get_in_addr(struct sockaddr *sa); + +/* Usage */ +struct sockaddr_storage client_addr; +socklen_t client_addr_sz = sizeof(client_addr); +char buf[33]; + +if (new_fd = accept(s->fd, (struct sockaddr *)&client_addr, &client_addr_sz) == -1) { + /* error handling */ +} +inet_ntop(client_addr.ss_family, get_in_addr((struct sockaddr *)&client_addr), buf, 32); +printf("Received connection from %s\n", buf); + +``` + +### handler_tcp_echo + +An example handler for a multithreaded tcp echo server. + +```c +void handler_tcp_echo(Server *s); + +/* Usage */ +#include "lfnetwork.h" + +int main(int argc, char **argv) { + Server *server = new_server(SERVERTYPE_TCP, "80", handler_tcp_echo); + serve(server, DEFAULT_BACKLOG); + delete_server(server); +} +``` + +## Macros + +### DEFAULT_BACKLOG_SIZE + +A default size for the socket's backlog buffer. `5` is a standard default size, providing some backlog but not +enough to make huge buffers for each socket + +```c +#define DEFAULT_BACKLOG_SIZE 5 +``` \ No newline at end of file blob - 53ecb0c1769a6ec9fa5362aa7b4717137f9cdd7c blob + 3e5bc239140cb52650fb5c582e14275216e684a3 --- include/lfinput.h +++ include/lfinput.h @@ -17,4 +17,8 @@ void del_split(char **); void del_lines(char **); +#define DEFAULT_CAPTURE_SYSTEM_BUFSIZE 1024 + +const char *capture_system(const char *cmd, int buf_sz); + #endif // LIBFLINT_INPUT_H blob - /dev/null blob + b4869f0bd0d8c622974b9284ac3623d794dedffb (mode 644) --- /dev/null +++ include/lfnetwork.h @@ -0,0 +1,28 @@ +#ifndef LIBFLINT_NET_H +#define LIBFLINT_NET_H + +#include + +typedef enum ServerType { + SERVERTYPE_TCP, + SERVERTYPE_UDP +} ServerType; + +typedef struct Server { + ServerType server_type; + int fd; + int port; + void (*handler)(struct Server *s); +} Server; + +#define DEFAULT_BACKLOG 5 + +Server *new_server(ServerType type, const char *port, void(handler)(Server *s)); +void delete_server(Server *s); +int serve(Server *s, int backlog_size); +void *get_in_addr(struct sockaddr *sa); + +// Example handlers +void handler_tcp_echo(Server *s); + +#endif //LIBFLINT_NET_H blob - /dev/null blob + ba1a8603f7586d7eeb17a88cfc6ff4b521905727 (mode 755) --- /dev/null +++ run_tests.sh @@ -0,0 +1,12 @@ +set -e + +#./testrunner +./tcptest & +tcpout=$(echo "hello" | nc localhost 18632) +echo "tcpout: $tcpout" +if [ "$tcpout" != "TEST SEND" ]; then + echo "Error: \"$tcpout\" != \"TEST SEND\"" + exit 1 +fi + +exit 0 blob - f20b8200a38aa43fd268e959f03dee2969515d13 blob + 23d54c9df9d8498a4e6c0e3b3fc2ff863ecb955e --- src/input.c +++ src/input.c @@ -2,6 +2,7 @@ #include #include #include +#include #ifdef __linux__ @@ -121,3 +122,18 @@ void del_split(char **sp) { void del_lines(char **lines) { del_split(lines); } + +const char *capture_system(const char *cmd, int buf_sz) { + if (buf_sz == 0) { + buf_sz = DEFAULT_CAPTURE_SYSTEM_BUFSIZE; + } + char *buf = malloc(buf_sz); + FILE *tmp = popen(cmd, "r"); + if (tmp == NULL) { + fprintf(stderr, "libflint: failed to open FILE *tmp in capture_system. Errno: %d\n", errno); + free(buf); + return NULL; + } + fgets(buf, buf_sz, tmp); + return buf; +} \ No newline at end of file blob - 3bbcf1c246a5b5475ef1fb51e62831f676c6dcf6 (mode 644) blob + /dev/null --- src/macos/macos.c +++ /dev/null @@ -1,82 +0,0 @@ -#include -#include -#include -#include -#include - -#include "lfmacos.h" - -#define NS_TO_SECONDS 1000000000.0 -#define NEW_PROCESS_SENTINEL (-1.0) - -ProcessData *new_ProcessData() { - ProcessData *pd = malloc(sizeof(ProcessData)); - pd->last_total_consumed = NEW_PROCESS_SENTINEL; - return pd; -} - -int update_process(pid_t pid, ProcessData *proc) { - struct proc_taskinfo taskinfo; - const int r = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &taskinfo, PROC_PIDTASKINFO_SIZE); - if (r != PROC_PIDTASKINFO_SIZE) { - return 1; - } - - mach_timebase_info_data_t info; - mach_timebase_info(&info); - const double ns_per_tick = (double)info.numer / (double)info.denom; - - time(&(proc->timestamp)); - proc->total_kernel_time = (taskinfo.pti_total_system * ns_per_tick) / NS_TO_SECONDS; - proc->total_user_time = (taskinfo.pti_total_user * ns_per_tick) / NS_TO_SECONDS; - proc->virtual_memory = taskinfo.pti_virtual_size; - proc->resident_memory = taskinfo.pti_resident_size; - - if (proc->last_total_consumed != NEW_PROCESS_SENTINEL) { - time_t t = proc->timestamp - proc->last_timestamp; - proc->percent_cpu = 100.0 * (proc->total_user_time + proc->total_kernel_time - proc->last_total_consumed) / t; - } else { - proc->percent_cpu = 0.0; - } - - proc->last_timestamp = proc->timestamp; - proc->last_total_consumed = proc->total_kernel_time + proc->total_user_time; - - return 0; -} - -/* reallocarray is reimplemented here for macOS because Apple doesn't expose - * their implementation. This is taken straight from the OpenBSD source as - * shown in the below copyright notice - */ - -/* $OpenBSD: reallocarray.c,v 1.2 2014/12/08 03:45:00 bcook Exp $ */ -/* - * Copyright (c) 2008 Otto Moerbeek - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/* OPENBSD ORIGINAL: lib/libc/stdlib/reallocarray.c */ - -#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) - -void *reallocarray(void *optr, size_t nmemb, size_t size) -{ - if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && - nmemb > 0 && SIZE_MAX / nmemb < size) { - errno = ENOMEM; - return NULL; - } - return realloc(optr, size * nmemb); -} blob - /dev/null blob + 3bbcf1c246a5b5475ef1fb51e62831f676c6dcf6 (mode 644) --- /dev/null +++ src/macos.c @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include + +#include "lfmacos.h" + +#define NS_TO_SECONDS 1000000000.0 +#define NEW_PROCESS_SENTINEL (-1.0) + +ProcessData *new_ProcessData() { + ProcessData *pd = malloc(sizeof(ProcessData)); + pd->last_total_consumed = NEW_PROCESS_SENTINEL; + return pd; +} + +int update_process(pid_t pid, ProcessData *proc) { + struct proc_taskinfo taskinfo; + const int r = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &taskinfo, PROC_PIDTASKINFO_SIZE); + if (r != PROC_PIDTASKINFO_SIZE) { + return 1; + } + + mach_timebase_info_data_t info; + mach_timebase_info(&info); + const double ns_per_tick = (double)info.numer / (double)info.denom; + + time(&(proc->timestamp)); + proc->total_kernel_time = (taskinfo.pti_total_system * ns_per_tick) / NS_TO_SECONDS; + proc->total_user_time = (taskinfo.pti_total_user * ns_per_tick) / NS_TO_SECONDS; + proc->virtual_memory = taskinfo.pti_virtual_size; + proc->resident_memory = taskinfo.pti_resident_size; + + if (proc->last_total_consumed != NEW_PROCESS_SENTINEL) { + time_t t = proc->timestamp - proc->last_timestamp; + proc->percent_cpu = 100.0 * (proc->total_user_time + proc->total_kernel_time - proc->last_total_consumed) / t; + } else { + proc->percent_cpu = 0.0; + } + + proc->last_timestamp = proc->timestamp; + proc->last_total_consumed = proc->total_kernel_time + proc->total_user_time; + + return 0; +} + +/* reallocarray is reimplemented here for macOS because Apple doesn't expose + * their implementation. This is taken straight from the OpenBSD source as + * shown in the below copyright notice + */ + +/* $OpenBSD: reallocarray.c,v 1.2 2014/12/08 03:45:00 bcook Exp $ */ +/* + * Copyright (c) 2008 Otto Moerbeek + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/stdlib/reallocarray.c */ + +#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) + +void *reallocarray(void *optr, size_t nmemb, size_t size) +{ + if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + nmemb > 0 && SIZE_MAX / nmemb < size) { + errno = ENOMEM; + return NULL; + } + return realloc(optr, size * nmemb); +} blob - /dev/null blob + 29841222c56782549f59bf4c7bff135b55aafe67 (mode 644) --- /dev/null +++ src/network.c @@ -0,0 +1,150 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lfnetwork.h" + +static void sighandler(int s) { + int saved_errno = errno; + while (waitpid(-1, NULL, WNOHANG) > 0); + errno = saved_errno; +} + +void *get_in_addr(struct sockaddr *sa) +{ + if (sa->sa_family == AF_INET) { + return &(((struct sockaddr_in*)sa)->sin_addr); + } + + return &(((struct sockaddr_in6*)sa)->sin6_addr); +} + +Server *new_server(ServerType type, const char *port, void(handler)(Server *s)) { + Server *s = (Server *)malloc(sizeof(Server)); + if (s == NULL) { + return NULL; + } + s->server_type = type; + s->handler = handler; + + struct addrinfo *addr = NULL; + if (getaddrinfo(NULL, port, NULL, &addr) != 0) { + free(s); + return NULL; + } + s->port = (int)strtol(port, NULL, 10); + + int socktype = 0; + switch (type) { + case SERVERTYPE_TCP: + socktype = SOCK_STREAM; + break; + case SERVERTYPE_UDP: + socktype = SOCK_DGRAM; + break; + } + + struct addrinfo *p; + for (p = addr; p != NULL; p = p->ai_next) { + s->fd = socket(AF_INET, socktype, 0); + if (s->fd == -1) { + continue; + } + + if (bind(s->fd, p->ai_addr, p->ai_addrlen) != 0) { + close(s->fd); + continue; + } + + break; + } + + if (p == NULL) { + fprintf(stderr, "Failed to bind\n"); + free(s); + return NULL; + } + + freeaddrinfo(addr); + return s; +} + +void delete_server(Server *s) { + free(s); + s = NULL; +} + +int serve(Server *s, int backlog_size) { + if (backlog_size == 0) { + backlog_size = DEFAULT_BACKLOG; + } + + if (listen(s->fd, backlog_size) != 0) { + return 1; + } + + // Linux doesn't handle SA_RESTART properly, and I don't know about Windows (nor do I care) + // This is just for macOS and BSDs + #if !defined(__linux__) && !defined(_WIN32) + struct sigaction sa; + sa.sa_handler = sighandler; + sa.sa_flags = SA_RESTART; + if (sigaction(SIGCHLD, &sa, NULL) == -1) { + fprintf(stderr, "Failed to set sigaction\n"); + return 1; + } + #endif + + s->handler(s); + + return 0; +} + +static void *tcp_echo_thread(void *vargp) { + while (1) { + char recv_buf[256]; + int fd = *(int *) vargp; + + int r = (int)recv(fd, recv_buf, 256, 0); + if (r < 1) { + if (r == -1) { + fprintf(stderr, "Failed to recv. Errno: %d\n", errno); + } + close(fd); + break; + } + + if (send(fd, recv_buf, strlen(recv_buf), 0) == -1) { + fprintf(stderr, "Failed to send echo. Errno: %d\n", errno); + close(fd); + break; + } + } +} + +void handler_tcp_echo(Server *s) { + while (1) { + struct sockaddr_storage client_addr; + socklen_t client_addr_sz = sizeof(client_addr); + int new_fd = accept(s->fd, (struct sockaddr *)&client_addr, &client_addr_sz); + if (new_fd == -1) { + fprintf(stderr, "failed to accept. Errno: %d\n", errno); + continue; + } + + pthread_t srv_tid; + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&srv_tid, &attr, tcp_echo_thread, &new_fd); + } +} blob - 541550ed203d975a79e17157c67a45991f80cbba blob + 663631f37f739d19158a37db96eaed3a4b6c30db --- tests/tests.c +++ tests/tests.c @@ -2,8 +2,11 @@ #include #include #include +#include +#include #include "lflinkedlist.h" +#include "lfnetwork.h" #include "lfset.h" #include "lfstack.h" #include "lfbinarytree.h" @@ -12,6 +15,7 @@ #include "lfstring.h" #include "lfcrypto.h" #include "lfparsing.h" +#include "lfinput.h" #if defined(__APPLE__) || defined(__MACH__) #include "lfmacos.h" @@ -395,6 +399,60 @@ void test_parsing() { printf("Passes all parsing tests\n"); } +#define NET_MSG "TEST SEND" + +void tcp_test_handler(Server *s) { + struct sockaddr_storage client_addr; + socklen_t client_addr_sz = sizeof(client_addr); + int new_fd = accept(s->fd, (struct sockaddr *)&client_addr, &client_addr_sz); + assert(new_fd != -1); + assert(send(new_fd, NET_MSG, 10, 0) != -1); + close(new_fd); +} + +void *tcp_server_thread(void *vargp) { + Server *server = new_server(SERVERTYPE_TCP, "18632", tcp_test_handler); + serve(server, DEFAULT_BACKLOG); + delete_server(server); +} + +void udp_test_handler(Server *s) { + struct sockaddr_storage client_addr; + socklen_t client_addr_sz = sizeof(client_addr); + char recv_buf[128]; + + int r = (int)recvfrom(s->fd, recv_buf, 128, 0, (struct sockaddr*)&client_addr, &client_addr_sz); + assert(r > 0); + assert(strcmp(recv_buf, NET_MSG) == 0); +} + +void *udp_server_thread(void *vargp) { + Server *server = new_server(SERVERTYPE_UDP, "18633", udp_test_handler); + serve(server, DEFAULT_BACKLOG); + delete_server(server); +} + +void test_network() { + printf("\n--- NETWORK TEST ---\n"); + pthread_t srv_tid; + pthread_create(&srv_tid, NULL, tcp_server_thread, NULL); + + sleep(1); + const char *s = capture_system("echo hello | nc localhost 18632", 0); + assert(strcmp(s, NET_MSG) == 0); + free((char *)s); + + pthread_join(srv_tid, NULL); + printf("Passed TCP test\n"); + + pthread_create(&srv_tid, NULL, udp_server_thread, NULL); + sleep(1); + system("echo hello | nc localhost 18633"); + + pthread_join(srv_tid, NULL); + printf("Passed UDP test\n"); +} + #if defined(__APPLE__) || defined(__MACH__) void test_macos() { printf("\n--- macOS TEST ---\n"); @@ -420,10 +478,11 @@ int main() { test_string(); test_crypto(); test_parsing(); + test_network(); #if defined(__APPLE__) || defined(__MACH__) test_macos(); #endif return 0; -} \ No newline at end of file +} blob - /dev/null blob + c1eb3dfb79f83d7264007e335b15a1530a8956dd (mode 644) --- /dev/null +++ tests/netmanual.c @@ -0,0 +1,7 @@ +#include "lfnetwork.h" + +int main(int argc, char **argv) { + Server *server = new_server(SERVERTYPE_TCP, "18632", handler_tcp_echo); + serve(server, DEFAULT_BACKLOG); + delete_server(server); +}