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
 | 
					      - name: Install dependencies
 | 
				
			||||||
        run: |
 | 
					        run: |
 | 
				
			||||||
          sudo apt-get update
 | 
					          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
 | 
					      - name: Build and test
 | 
				
			||||||
        run: |
 | 
					        run: |
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -5,4 +5,7 @@ compile_commands.json
 | 
				
			|||||||
site
 | 
					site
 | 
				
			||||||
libflint.so
 | 
					libflint.so
 | 
				
			||||||
test
 | 
					test
 | 
				
			||||||
 | 
					tcptest
 | 
				
			||||||
 | 
					testrunner
 | 
				
			||||||
.idea
 | 
					.idea
 | 
				
			||||||
 | 
					netmanual
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,10 @@ cmake_minimum_required(VERSION 3.17)
 | 
				
			|||||||
project(flint C)
 | 
					project(flint C)
 | 
				
			||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
 | 
					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)
 | 
					set(CMAKE_C_STANDARD 99)
 | 
				
			||||||
include_directories(include)
 | 
					include_directories(include)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -17,16 +21,17 @@ set(SOURCES
 | 
				
			|||||||
        src/utility.c
 | 
					        src/utility.c
 | 
				
			||||||
        src/crypto.c
 | 
					        src/crypto.c
 | 
				
			||||||
        src/parsing.c
 | 
					        src/parsing.c
 | 
				
			||||||
 | 
					        src/network.c
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if ((${CMAKE_SYSTEM_NAME} STREQUAL "Darwin"))
 | 
					if ((${CMAKE_SYSTEM_NAME} STREQUAL "Darwin"))
 | 
				
			||||||
    add_library(flint ${SOURCES} src/macos/macos.c)
 | 
					    add_library(flint ${SOURCES} src/macos.c)
 | 
				
			||||||
else()
 | 
					else()
 | 
				
			||||||
    add_library(flint ${SOURCES})
 | 
					    add_library(flint ${SOURCES})
 | 
				
			||||||
endif()
 | 
					endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux"))
 | 
					if ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux"))
 | 
				
			||||||
    target_link_libraries(flint bsd)
 | 
					    target_link_libraries(flint pthread bsd)
 | 
				
			||||||
endif()
 | 
					endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if(${CMAKE_PROJECT_NAME} STREQUAL flint)
 | 
					if(${CMAKE_PROJECT_NAME} STREQUAL flint)
 | 
				
			||||||
@@ -34,8 +39,17 @@ if(${CMAKE_PROJECT_NAME} STREQUAL flint)
 | 
				
			|||||||
    target_include_directories(tests PRIVATE include)
 | 
					    target_include_directories(tests PRIVATE include)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
 | 
					    if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
 | 
				
			||||||
        target_link_libraries(tests flint bsd)
 | 
					        target_link_libraries(tests flint pthread bsd)
 | 
				
			||||||
    else()
 | 
					    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()
 | 
				
			||||||
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
 | 
					### 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
 | 
					```c
 | 
				
			||||||
void del_split(char **sp);
 | 
					void del_split(char **sp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -101,3 +102,19 @@ size_t sp_sz = 0;
 | 
				
			|||||||
char **sp = split("Delete Me!", &sp_sz, " ");
 | 
					char **sp = split("Delete Me!", &sp_sz, " ");
 | 
				
			||||||
void del_split(sp);
 | 
					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 **);
 | 
					void del_lines(char **);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define DEFAULT_CAPTURE_SYSTEM_BUFSIZE 1024
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char *capture_system(const char *cmd, int buf_sz);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif // LIBFLINT_INPUT_H
 | 
					#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 <stdlib.h>
 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
#include <limits.h>
 | 
					#include <limits.h>
 | 
				
			||||||
 | 
					#include <errno.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef __linux__
 | 
					#ifdef __linux__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -121,3 +122,18 @@ void del_split(char **sp) {
 | 
				
			|||||||
void del_lines(char **lines) {
 | 
					void del_lines(char **lines) {
 | 
				
			||||||
    del_split(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 <stdlib.h>
 | 
				
			||||||
#include <assert.h>
 | 
					#include <assert.h>
 | 
				
			||||||
#include <unistd.h>
 | 
					#include <unistd.h>
 | 
				
			||||||
 | 
					#include <arpa/inet.h>
 | 
				
			||||||
 | 
					#include <pthread.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "lflinkedlist.h"
 | 
					#include "lflinkedlist.h"
 | 
				
			||||||
 | 
					#include "lfnetwork.h"
 | 
				
			||||||
#include "lfset.h"
 | 
					#include "lfset.h"
 | 
				
			||||||
#include "lfstack.h"
 | 
					#include "lfstack.h"
 | 
				
			||||||
#include "lfbinarytree.h"
 | 
					#include "lfbinarytree.h"
 | 
				
			||||||
@@ -12,6 +15,7 @@
 | 
				
			|||||||
#include "lfstring.h"
 | 
					#include "lfstring.h"
 | 
				
			||||||
#include "lfcrypto.h"
 | 
					#include "lfcrypto.h"
 | 
				
			||||||
#include "lfparsing.h"
 | 
					#include "lfparsing.h"
 | 
				
			||||||
 | 
					#include "lfinput.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if defined(__APPLE__) || defined(__MACH__)
 | 
					#if defined(__APPLE__) || defined(__MACH__)
 | 
				
			||||||
#include "lfmacos.h"
 | 
					#include "lfmacos.h"
 | 
				
			||||||
@@ -395,6 +399,60 @@ void test_parsing() {
 | 
				
			|||||||
    printf("Passes all parsing tests\n");
 | 
					    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__)
 | 
					#if defined(__APPLE__) || defined(__MACH__)
 | 
				
			||||||
void test_macos() {
 | 
					void test_macos() {
 | 
				
			||||||
    printf("\n--- macOS TEST ---\n");
 | 
					    printf("\n--- macOS TEST ---\n");
 | 
				
			||||||
@@ -420,10 +478,11 @@ int main() {
 | 
				
			|||||||
    test_string();
 | 
					    test_string();
 | 
				
			||||||
    test_crypto();
 | 
					    test_crypto();
 | 
				
			||||||
    test_parsing();
 | 
					    test_parsing();
 | 
				
			||||||
 | 
					    test_network();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if defined(__APPLE__) || defined(__MACH__)
 | 
					#if defined(__APPLE__) || defined(__MACH__)
 | 
				
			||||||
    test_macos();
 | 
					    test_macos();
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user