Implement Server (#1)
- Generic Server struct - TCP and UDP Reviewed-on: #1
This commit is contained in:
		@@ -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: |
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -5,4 +5,7 @@ compile_commands.json
 | 
			
		||||
site
 | 
			
		||||
libflint.so
 | 
			
		||||
test
 | 
			
		||||
tcptest
 | 
			
		||||
testrunner
 | 
			
		||||
.idea
 | 
			
		||||
netmanual
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										32
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								Makefile
									
									
									
									
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										10
									
								
								build.sh
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								build.sh
									
									
									
									
									
								
							@@ -1,10 +0,0 @@
 | 
			
		||||
#!/bin/sh -e
 | 
			
		||||
 | 
			
		||||
# For building outside of CLion
 | 
			
		||||
 | 
			
		||||
mkdir -p build
 | 
			
		||||
cd build
 | 
			
		||||
cmake ..
 | 
			
		||||
make
 | 
			
		||||
cp compile_commands.json ..
 | 
			
		||||
cd
 | 
			
		||||
							
								
								
									
										17
									
								
								clanggen.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										17
									
								
								clanggen.sh
									
									
									
									
									
										Executable file
									
								
							@@ -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
 | 
			
		||||
@@ -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);
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										106
									
								
								docs/network.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								docs/network.md
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
```
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										28
									
								
								include/lfnetwork.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								include/lfnetwork.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
#ifndef LIBFLINT_NET_H
 | 
			
		||||
#define LIBFLINT_NET_H
 | 
			
		||||
 | 
			
		||||
#include <netdb.h>
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
							
								
								
									
										12
									
								
								run_tests.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										12
									
								
								run_tests.sh
									
									
									
									
									
										Executable file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										16
									
								
								src/input.c
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								src/input.c
									
									
									
									
									
								
							@@ -2,6 +2,7 @@
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										150
									
								
								src/network.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								src/network.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,150 @@
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
#include <netdb.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
 | 
			
		||||
#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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								tests/netmanual.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								tests/netmanual.c
									
									
									
									
									
										Normal file
									
								
							@@ -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);
 | 
			
		||||
}
 | 
			
		||||
@@ -2,8 +2,11 @@
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
 | 
			
		||||
#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,6 +478,7 @@ int main() {
 | 
			
		||||
    test_string();
 | 
			
		||||
    test_crypto();
 | 
			
		||||
    test_parsing();
 | 
			
		||||
    test_network();
 | 
			
		||||
 | 
			
		||||
#if defined(__APPLE__) || defined(__MACH__)
 | 
			
		||||
    test_macos();
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user