From 1424925cbf4d5cd40d90caf1e4955386c742ebe5 Mon Sep 17 00:00:00 2001 From: Evan Burkey Date: Sun, 7 Jul 2024 19:50:28 -0700 Subject: [PATCH 1/8] implement basic network server --- .gitignore | 2 + CMakeLists.txt | 41 -------------- Makefile | 19 +++++-- build.sh | 10 ---- clanggen.sh | 17 ++++++ include/lfnetwork.h | 28 ++++++++++ run_tests.sh | 12 ++++ src/network.c | 130 ++++++++++++++++++++++++++++++++++++++++++++ tests/tcptest.c | 34 ++++++++++++ tests/tests.c | 3 +- 10 files changed, 238 insertions(+), 58 deletions(-) delete mode 100644 CMakeLists.txt delete mode 100755 build.sh create mode 100755 clanggen.sh create mode 100644 include/lfnetwork.h create mode 100755 run_tests.sh create mode 100644 src/network.c create mode 100644 tests/tcptest.c diff --git a/.gitignore b/.gitignore index c3157f7..04ab71d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,6 @@ compile_commands.json site libflint.so test +tcptest +testrunner .idea diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index a8dc823..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -cmake_minimum_required(VERSION 3.17) -project(flint C) -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - -set(CMAKE_C_STANDARD 99) -include_directories(include) - -set(SOURCES - src/linkedlist.c - src/set.c - src/stack.c - src/binarytree.c - src/input.c - src/math.c - src/string.c - src/vector.c - src/utility.c - src/crypto.c - src/parsing.c -) - -if ((${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")) - add_library(flint ${SOURCES} src/macos/macos.c) -else() - add_library(flint ${SOURCES}) -endif() - -if ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux")) - target_link_libraries(flint bsd) -endif() - -if(${CMAKE_PROJECT_NAME} STREQUAL flint) - add_executable(tests tests/tests.c) - target_include_directories(tests PRIVATE include) - - if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - target_link_libraries(tests flint bsd) - else() - target_link_libraries(tests flint) - endif() -endif() diff --git a/Makefile b/Makefile index e4a2582..f6b6d93 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ -.PHONY : clean +.PHONY : clean tests all -CFLAGS = -std=c99 -Iinclude -pedantic -Wall -Wextra +CFLAGS = -std=c99 -Iinclude -pedantic +WARNINGS= -Wall -Wextra LDFLAGS = -fPIC -shared TARGET = libflint.so @@ -11,9 +12,10 @@ PREFIX = $(DESTDIR)/usr/local LIBDIR = $(PREFIX)/lib all: $(TARGET) + ./clanggen.sh $(TARGET): $(OBJ) - cc $(CFLAGS) $(LDFLAGS) -o $(TARGET) $(OBJ) + cc $(CFLAGS) $(WARNINGS) $(LDFLAGS) -o $(TARGET) $(OBJ) ./obj/%.o: ./src/%.c cc $(CFLAGS) -c $< -o $@ @@ -26,7 +28,12 @@ uninstall: clean: rm -f $(TARGET) - rm -f test + rm -f testrunner + rm -f tcptest + rm -f compile_commands.json + +tests: + cc $(CFLAGS) -o testrunner tests/tests.c src/*.c + cc $(CFLAGS) -o tcptest tests/tcptest.c src/*.c + ./run_tests.sh -test: - cc $(CFLAGS) -o test tests/tests.c src/*.c diff --git a/build.sh b/build.sh deleted file mode 100755 index c860355..0000000 --- a/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 diff --git a/clanggen.sh b/clanggen.sh new file mode 100755 index 0000000..fbcb531 --- /dev/null +++ b/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 diff --git a/include/lfnetwork.h b/include/lfnetwork.h new file mode 100644 index 0000000..0c2abd2 --- /dev/null +++ b/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 10 + +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 diff --git a/run_tests.sh b/run_tests.sh new file mode 100755 index 0000000..ba1a860 --- /dev/null +++ b/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 diff --git a/src/network.c b/src/network.c new file mode 100644 index 0000000..40c71e2 --- /dev/null +++ b/src/network.c @@ -0,0 +1,130 @@ +#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 (listen(s->fd, backlog_size) != 0) { + return 1; + } + + 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; + } + + printf("Server is waiting for connections on port %d...\n", s->port); + s->handler(s); + + return 0; +} + +void handler_tcp_echo(Server *s) { + struct sockaddr_storage client_addr; + while (1) { + 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) { + printf("failed to accept. Errno: %d\n", errno); + continue; + } + + char buf[33]; + inet_ntop(client_addr.ss_family, get_in_addr((struct sockaddr *)&client_addr), buf, 32); + printf("Received connection from %s\n", buf); + + // Start child process + if (!fork()) { + close(s->fd); // Child doesn't need the initial socket + if (send(new_fd, "Hello, world!", 13, 0) == -1) { + fprintf(stderr, "Failed to send hello world\n"); + } + close(new_fd); + _exit(0); + } + // End child process + + close(new_fd); // new_fd is not used by the parent + } +} diff --git a/tests/tcptest.c b/tests/tcptest.c new file mode 100644 index 0000000..4d3c7ee --- /dev/null +++ b/tests/tcptest.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "lfnetwork.h" + +void 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); + if (new_fd == -1) { + printf("failed to accept. Errno: %d\n", errno); + return; + } + + char buf[33]; + inet_ntop(client_addr.ss_family, get_in_addr((struct sockaddr *)&client_addr), buf, 32); + printf("Received connection from %s\n", buf); + + if (send(new_fd, "TEST SEND", 10, 0) == -1) { + printf("Failed to send hello world. Errno: %d\n", errno); + } + close(new_fd); +} + +int main(int argc, char **argv) { + Server *server = new_server(SERVERTYPE_TCP, "18632", test_handler); + serve(server, DEFAULT_BACKLOG); + delete_server(server); +} diff --git a/tests/tests.c b/tests/tests.c index 541550e..334263f 100644 --- a/tests/tests.c +++ b/tests/tests.c @@ -4,6 +4,7 @@ #include #include "lflinkedlist.h" +#include "lfnetwork.h" #include "lfset.h" #include "lfstack.h" #include "lfbinarytree.h" @@ -426,4 +427,4 @@ int main() { #endif return 0; -} \ No newline at end of file +} -- 2.30.2 From c52712471fb92472b6450d832de81a0f99dc0dba Mon Sep 17 00:00:00 2001 From: Evan Burkey Date: Sun, 7 Jul 2024 20:05:32 -0700 Subject: [PATCH 2/8] manual testing --- .gitea/workflows/jobs.yaml | 8 ++------ .gitignore | 1 + Makefile | 5 ++++- src/network.c | 28 +++++++++++++++++++++------- tests/netmanual.c | 15 +++++++++++++++ 5 files changed, 43 insertions(+), 14 deletions(-) create mode 100644 tests/netmanual.c diff --git a/.gitea/workflows/jobs.yaml b/.gitea/workflows/jobs.yaml index 5cf1842..b01099a 100644 --- a/.gitea/workflows/jobs.yaml +++ b/.gitea/workflows/jobs.yaml @@ -14,15 +14,11 @@ 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 build-essential - name: Build and test run: | - mkdir build - cd build - cmake .. - make - ./tests + make tests docs: runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 04ab71d..221cf55 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ test tcptest testrunner .idea +netmanual diff --git a/Makefile b/Makefile index f6b6d93..4c62e57 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY : clean tests all +.PHONY : clean tests all manual CFLAGS = -std=c99 -Iinclude -pedantic WARNINGS= -Wall -Wextra @@ -30,6 +30,7 @@ clean: rm -f $(TARGET) rm -f testrunner rm -f tcptest + rm -f netmanual rm -f compile_commands.json tests: @@ -37,3 +38,5 @@ tests: cc $(CFLAGS) -o tcptest tests/tcptest.c src/*.c ./run_tests.sh +manual: + cc $(CFLAGS) -o netmanual tests/netmanual.c src/*.c diff --git a/src/network.c b/src/network.c index 40c71e2..637ed17 100644 --- a/src/network.c +++ b/src/network.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -115,13 +116,26 @@ void handler_tcp_echo(Server *s) { printf("Received connection from %s\n", buf); // Start child process - if (!fork()) { - close(s->fd); // Child doesn't need the initial socket - if (send(new_fd, "Hello, world!", 13, 0) == -1) { - fprintf(stderr, "Failed to send hello world\n"); - } - close(new_fd); - _exit(0); + if (!fork()) { + close(s->fd); // Child doesn't need the initial socket + + char recv_buf[256]; + int r = recv(new_fd, recv_buf, 256, 0); + if (r == -1) { + fprintf(stderr, "Failed to recv. Errno: %d\n", errno); + goto CHILD_END; + } else if (r == 0) { + fprintf(stderr, "Client closed connection\n"); + goto CHILD_END; + } + + if (send(new_fd, recv_buf, strlen(recv_buf), 0) == -1) { + fprintf(stderr, "Failed to send echo\n"); + } + +CHILD_END: + close(new_fd); + _exit(0); } // End child process diff --git a/tests/netmanual.c b/tests/netmanual.c new file mode 100644 index 0000000..60ede69 --- /dev/null +++ b/tests/netmanual.c @@ -0,0 +1,15 @@ +#include +#include +#include +#include +#include +#include +#include + +#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); +} -- 2.30.2 From 9720e0ce989f7e4c484bf3aaf296291a4fa29755 Mon Sep 17 00:00:00 2001 From: Evan Burkey Date: Sun, 7 Jul 2024 20:10:03 -0700 Subject: [PATCH 3/8] fix linux --- src/network.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/network.c b/src/network.c index 637ed17..46c8654 100644 --- a/src/network.c +++ b/src/network.c @@ -1,3 +1,8 @@ +// Needed for sigaction on Linux +#ifdef __linux__ +#define _POSIX_C_SOURCE 1 +#endif // __linux__ + #include #include #include -- 2.30.2 From 8d67851767ca2e193f405e3bd10e22ff04f1442d Mon Sep 17 00:00:00 2001 From: Evan Burkey Date: Mon, 8 Jul 2024 08:53:21 -0700 Subject: [PATCH 4/8] midway --- Makefile | 7 +++++++ src/network.c | 5 ----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 4c62e57..5aa38ee 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,7 @@ CFLAGS = -std=c99 -Iinclude -pedantic WARNINGS= -Wall -Wextra LDFLAGS = -fPIC -shared +MACROS= -D_POSIX_C_SOURCE=1 TARGET = libflint.so SRC != ls src/*.c @@ -11,10 +12,15 @@ OBJ = $(SRC:./src/$.c=./obj/%.o) PREFIX = $(DESTDIR)/usr/local LIBDIR = $(PREFIX)/lib +OS_NAME != uname -s | tr A-Z a-z + all: $(TARGET) ./clanggen.sh $(TARGET): $(OBJ) + ifeq ($(OS_NAME),"openbsd") + @echo "Building for openbsd" +endif cc $(CFLAGS) $(WARNINGS) $(LDFLAGS) -o $(TARGET) $(OBJ) ./obj/%.o: ./src/%.c @@ -40,3 +46,4 @@ tests: manual: cc $(CFLAGS) -o netmanual tests/netmanual.c src/*.c + diff --git a/src/network.c b/src/network.c index 46c8654..637ed17 100644 --- a/src/network.c +++ b/src/network.c @@ -1,8 +1,3 @@ -// Needed for sigaction on Linux -#ifdef __linux__ -#define _POSIX_C_SOURCE 1 -#endif // __linux__ - #include #include #include -- 2.30.2 From 7a8207253926cdeb71afe9af538d546fd8ba7c48 Mon Sep 17 00:00:00 2001 From: Evan Burkey Date: Tue, 9 Jul 2024 09:38:31 -0700 Subject: [PATCH 5/8] finish network --- CMakeLists.txt | 51 ++++++++++++++++++++++++++++++++++++ docs/input.md | 19 +++++++++++++- include/lfinput.h | 4 +++ src/input.c | 16 ++++++++++++ src/network.c | 66 +++++++++++++++++++++++------------------------ tests/netmanual.c | 8 ------ tests/tcptest.c | 34 ------------------------ tests/tests.c | 59 ++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 180 insertions(+), 77 deletions(-) create mode 100644 CMakeLists.txt delete mode 100644 tests/tcptest.c diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..fdfc1e6 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 3.17) +project(flint C) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +set(CMAKE_C_STANDARD 99) +include_directories(include) + +set(SOURCES + src/linkedlist.c + src/set.c + src/stack.c + src/binarytree.c + src/input.c + src/math.c + src/string.c + src/vector.c + 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) +else() + add_library(flint ${SOURCES}) +endif() + +if ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux")) + target_link_libraries(flint pthread bsd) +endif() + +if(${CMAKE_PROJECT_NAME} STREQUAL flint) + add_executable(tests tests/tests.c) + target_include_directories(tests PRIVATE include) + + if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + target_link_libraries(tests flint pthread bsd) + else() + 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() diff --git a/docs/input.md b/docs/input.md index 8537d0e..dae3f84 100644 --- a/docs/input.md +++ b/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 diff --git a/include/lfinput.h b/include/lfinput.h index 53ecb0c..3e5bc23 100644 --- a/include/lfinput.h +++ b/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 diff --git a/src/input.c b/src/input.c index f20b820..23d54c9 100644 --- a/src/input.c +++ b/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 diff --git a/src/network.c b/src/network.c index 637ed17..711f24b 100644 --- a/src/network.c +++ b/src/network.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "lfnetwork.h" @@ -95,50 +96,47 @@ int serve(Server *s, int backlog_size) { return 1; } - printf("Server is waiting for connections on port %d...\n", s->port); s->handler(s); return 0; } -void handler_tcp_echo(Server *s) { - struct sockaddr_storage client_addr; +static void *tcp_echo_thread(void *vargp) { while (1) { - 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) { - printf("failed to accept. Errno: %d\n", errno); - continue; - } + char recv_buf[256]; + int fd = *(int *) vargp; - char buf[33]; - inet_ntop(client_addr.ss_family, get_in_addr((struct sockaddr *)&client_addr), buf, 32); - printf("Received connection from %s\n", buf); - - // Start child process - if (!fork()) { - close(s->fd); // Child doesn't need the initial socket - - char recv_buf[256]; - int r = recv(new_fd, recv_buf, 256, 0); + int r = (int)recv(fd, recv_buf, 256, 0); + if (r < 1) { if (r == -1) { fprintf(stderr, "Failed to recv. Errno: %d\n", errno); - goto CHILD_END; - } else if (r == 0) { - fprintf(stderr, "Client closed connection\n"); - goto CHILD_END; } + close(fd); + break; + } - if (send(new_fd, recv_buf, strlen(recv_buf), 0) == -1) { - fprintf(stderr, "Failed to send echo\n"); - } - -CHILD_END: - close(new_fd); - _exit(0); - } - // End child process - - close(new_fd); // new_fd is not used by the parent + 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); } } diff --git a/tests/netmanual.c b/tests/netmanual.c index 60ede69..c1eb3df 100644 --- a/tests/netmanual.c +++ b/tests/netmanual.c @@ -1,11 +1,3 @@ -#include -#include -#include -#include -#include -#include -#include - #include "lfnetwork.h" int main(int argc, char **argv) { diff --git a/tests/tcptest.c b/tests/tcptest.c deleted file mode 100644 index 4d3c7ee..0000000 --- a/tests/tcptest.c +++ /dev/null @@ -1,34 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "lfnetwork.h" - -void 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); - if (new_fd == -1) { - printf("failed to accept. Errno: %d\n", errno); - return; - } - - char buf[33]; - inet_ntop(client_addr.ss_family, get_in_addr((struct sockaddr *)&client_addr), buf, 32); - printf("Received connection from %s\n", buf); - - if (send(new_fd, "TEST SEND", 10, 0) == -1) { - printf("Failed to send hello world. Errno: %d\n", errno); - } - close(new_fd); -} - -int main(int argc, char **argv) { - Server *server = new_server(SERVERTYPE_TCP, "18632", test_handler); - serve(server, DEFAULT_BACKLOG); - delete_server(server); -} diff --git a/tests/tests.c b/tests/tests.c index 334263f..1537423 100644 --- a/tests/tests.c +++ b/tests/tests.c @@ -2,6 +2,8 @@ #include #include #include +#include +#include #include "lflinkedlist.h" #include "lfnetwork.h" @@ -16,6 +18,7 @@ #if defined(__APPLE__) || defined(__MACH__) #include "lfmacos.h" +#include "lfinput.h" #endif /* defined(__APPLE__) || defined(__MACH__) */ void print_ll(List *list) { @@ -396,6 +399,61 @@ 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"); + assert(strcmp(s, NET_MSG) == 0); + + pthread_join(srv_tid, NULL); + printf("Passed UDP test\n"); +} + #if defined(__APPLE__) || defined(__MACH__) void test_macos() { printf("\n--- macOS TEST ---\n"); @@ -421,6 +479,7 @@ int main() { test_string(); test_crypto(); test_parsing(); + test_network(); #if defined(__APPLE__) || defined(__MACH__) test_macos(); -- 2.30.2 From adc2c121d7b4488c5678968510bdf8cf236774b5 Mon Sep 17 00:00:00 2001 From: Evan Burkey Date: Tue, 9 Jul 2024 12:53:03 -0700 Subject: [PATCH 6/8] docs, backlog adjustment --- docs/network.md | 106 ++++++++++++++++++++++++++++++++++++++++++++ include/lfnetwork.h | 2 +- src/network.c | 4 ++ 3 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 docs/network.md diff --git a/docs/network.md b/docs/network.md new file mode 100644 index 0000000..b47a96c --- /dev/null +++ b/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 diff --git a/include/lfnetwork.h b/include/lfnetwork.h index 0c2abd2..b4869f0 100644 --- a/include/lfnetwork.h +++ b/include/lfnetwork.h @@ -15,7 +15,7 @@ typedef struct Server { void (*handler)(struct Server *s); } Server; -#define DEFAULT_BACKLOG 10 +#define DEFAULT_BACKLOG 5 Server *new_server(ServerType type, const char *port, void(handler)(Server *s)); void delete_server(Server *s); diff --git a/src/network.c b/src/network.c index 711f24b..fa62c84 100644 --- a/src/network.c +++ b/src/network.c @@ -84,6 +84,10 @@ void delete_server(Server *s) { } int serve(Server *s, int backlog_size) { + if (backlog_size == 0) { + backlog_size = DEFAULT_BACKLOG; + } + if (listen(s->fd, backlog_size) != 0) { return 1; } -- 2.30.2 From 0ac251279ac50fbabb92c23f523b3a9401e6730b Mon Sep 17 00:00:00 2001 From: Evan Burkey Date: Tue, 9 Jul 2024 12:57:33 -0700 Subject: [PATCH 7/8] add POSIX --- CMakeLists.txt | 6 ++++- Makefile | 49 ----------------------------------------- src/{macos => }/macos.c | 0 src/network.c | 4 ++++ 4 files changed, 9 insertions(+), 50 deletions(-) delete mode 100644 Makefile rename src/{macos => }/macos.c (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index fdfc1e6..fa09448 100644 --- a/CMakeLists.txt +++ b/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) @@ -21,7 +25,7 @@ set(SOURCES ) 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() diff --git a/Makefile b/Makefile deleted file mode 100644 index 5aa38ee..0000000 --- a/Makefile +++ /dev/null @@ -1,49 +0,0 @@ -.PHONY : clean tests all manual - -CFLAGS = -std=c99 -Iinclude -pedantic -WARNINGS= -Wall -Wextra -LDFLAGS = -fPIC -shared -MACROS= -D_POSIX_C_SOURCE=1 - -TARGET = libflint.so -SRC != ls src/*.c -OBJ = $(SRC:./src/$.c=./obj/%.o) - -PREFIX = $(DESTDIR)/usr/local -LIBDIR = $(PREFIX)/lib - -OS_NAME != uname -s | tr A-Z a-z - -all: $(TARGET) - ./clanggen.sh - -$(TARGET): $(OBJ) - ifeq ($(OS_NAME),"openbsd") - @echo "Building for openbsd" -endif - cc $(CFLAGS) $(WARNINGS) $(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 testrunner - rm -f tcptest - rm -f netmanual - rm -f compile_commands.json - -tests: - cc $(CFLAGS) -o testrunner tests/tests.c src/*.c - cc $(CFLAGS) -o tcptest tests/tcptest.c src/*.c - ./run_tests.sh - -manual: - cc $(CFLAGS) -o netmanual tests/netmanual.c src/*.c - diff --git a/src/macos/macos.c b/src/macos.c similarity index 100% rename from src/macos/macos.c rename to src/macos.c diff --git a/src/network.c b/src/network.c index fa62c84..2984122 100644 --- a/src/network.c +++ b/src/network.c @@ -92,6 +92,9 @@ int serve(Server *s, int backlog_size) { 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; @@ -99,6 +102,7 @@ int serve(Server *s, int backlog_size) { fprintf(stderr, "Failed to set sigaction\n"); return 1; } + #endif s->handler(s); -- 2.30.2 From f063bccec70ffe83d1421f852c184e6965cb0b84 Mon Sep 17 00:00:00 2001 From: Evan Burkey Date: Tue, 9 Jul 2024 13:54:03 -0700 Subject: [PATCH 8/8] fix automation --- .gitea/workflows/jobs.yaml | 8 ++++++-- tests/tests.c | 3 +-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.gitea/workflows/jobs.yaml b/.gitea/workflows/jobs.yaml index b01099a..288ac9d 100644 --- a/.gitea/workflows/jobs.yaml +++ b/.gitea/workflows/jobs.yaml @@ -14,11 +14,15 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y libbsd-dev build-essential + sudo apt-get install -y libbsd-dev cmake build-essential netcat - name: Build and test run: | - make tests + mkdir build + cd build + cmake .. + make + ./tests docs: runs-on: ubuntu-latest diff --git a/tests/tests.c b/tests/tests.c index 1537423..663631f 100644 --- a/tests/tests.c +++ b/tests/tests.c @@ -15,10 +15,10 @@ #include "lfstring.h" #include "lfcrypto.h" #include "lfparsing.h" +#include "lfinput.h" #if defined(__APPLE__) || defined(__MACH__) #include "lfmacos.h" -#include "lfinput.h" #endif /* defined(__APPLE__) || defined(__MACH__) */ void print_ll(List *list) { @@ -448,7 +448,6 @@ void test_network() { pthread_create(&srv_tid, NULL, udp_server_thread, NULL); sleep(1); system("echo hello | nc localhost 18633"); - assert(strcmp(s, NET_MSG) == 0); pthread_join(srv_tid, NULL); printf("Passed UDP test\n"); -- 2.30.2