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 +}