diff --git a/Doxyfile b/Doxyfile index 064688f..988cb19 100644 --- a/Doxyfile +++ b/Doxyfile @@ -42,7 +42,7 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "My Project" +PROJECT_NAME = "Pretty Good Server" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version @@ -157,7 +157,7 @@ ABBREVIATE_BRIEF = "The $name class" \ # description. # The default value is: NO. -ALWAYS_DETAILED_SEC = NO +ALWAYS_DETAILED_SEC = YES # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those @@ -165,7 +165,7 @@ ALWAYS_DETAILED_SEC = NO # operators of the base classes will not be shown. # The default value is: NO. -INLINE_INHERITED_MEMB = NO +INLINE_INHERITED_MEMB = YES # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the @@ -342,7 +342,7 @@ OPTIMIZE_OUTPUT_SLICE = NO # # Note see also the list of default file extension mappings. -EXTENSION_MAPPING = +EXTENSION_MAPPING = *.py *.h *.c # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable @@ -504,7 +504,7 @@ NUM_PROC_THREADS = 1 # Possible values are: YES, NO, DATETIME and DATE. # The default value is: NO. -TIMESTAMP = NO +TIMESTAMP = YES #--------------------------------------------------------------------------- # Build related configuration options @@ -518,7 +518,7 @@ TIMESTAMP = NO # normally produced when WARNINGS is set to YES. # The default value is: NO. -EXTRACT_ALL = NO +EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. @@ -1038,7 +1038,7 @@ FILE_PATTERNS = *.c \ # be searched for input files as well. # The default value is: NO. -RECURSIVE = NO +RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a @@ -1158,7 +1158,7 @@ FILTER_SOURCE_PATTERNS = # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. -USE_MDFILE_AS_MAINPAGE = +USE_MDFILE_AS_MAINPAGE = YES # The Fortran standard specifies that for fixed formatted Fortran code all # characters from position 72 are to be considered as comment. A common @@ -2412,7 +2412,7 @@ SEARCH_INCLUDES = YES # RECURSIVE has no effect here. # This tag requires that the tag SEARCH_INCLUDES is set to YES. -INCLUDE_PATH = +INCLUDE_PATH = . # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the @@ -2420,7 +2420,7 @@ INCLUDE_PATH = # used. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -INCLUDE_FILE_PATTERNS = +INCLUDE_FILE_PATTERNS = *.c *.h *.py # The PREDEFINED tag can be used to specify one or more macro names that are # defined before the preprocessor is started (similar to the -D option of e.g. diff --git a/pgs.c b/pgs.c index 5942b75..47be02b 100644 --- a/pgs.c +++ b/pgs.c @@ -1,5 +1,15 @@ -#define PY_SSIZE_T_CLEAN 1 +// Written by retoor@molodetz.nl + +// This source code sets up a simple TCP server that listens for connections and +// handles cleanup on exit. The server is intended to interact with an upstream +// server defined by its host and port. + +// Imports: Custom includes 'py.h' and 'sock.h' for additional functionality. + +// MIT License + #include "py.h" +#include "sock.h" #include #include #include @@ -12,141 +22,18 @@ #include #include #include +#define PY_SSIZE_T_CLEAN 1 #define LISTEN_PORT 2222 #define UPSTREAM_HOST "127.0.0.1" #define UPSTREAM_PORT 9999 -#define MAX_EVENTS 8096 -#define BUFFER_SIZE 1024 -void set_nonblocking(int fd) { - int flags = fcntl(fd, F_GETFL, 0); - if (flags == -1) { - perror("fcntl get"); - exit(EXIT_FAILURE); - } - if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { - perror("fcntl set"); - exit(EXIT_FAILURE); - } -} - -int prepare_upstream() { - int sockfd = socket(AF_INET, SOCK_STREAM, 0); - - return sockfd; -} - -int connect_upstream(const char *host, int port) { - int sockfd = socket(AF_INET, SOCK_STREAM, 0); - if (sockfd == -1) { - perror("socket"); - return -1; - } - - set_nonblocking(sockfd); - - struct sockaddr_in server_addr; - memset(&server_addr, 0, sizeof(server_addr)); - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(port); - if (inet_pton(AF_INET, host, &server_addr.sin_addr) <= 0) { - perror("inet_pton"); - close(sockfd); - return -1; - } - - if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == - -1) { - if (errno != EINPROGRESS) { - perror("connect"); - close(sockfd); - return -1; - } - } - - return sockfd; -} - -int create_listening_socket(int port) { - int listen_fd = socket(AF_INET, SOCK_STREAM, 0); - if (listen_fd == -1) { - perror("socket"); - return -1; - } - - int opt = 1; - if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == - -1) { - perror("setsockopt"); - close(listen_fd); - return -1; - } - - struct sockaddr_in server_addr; - memset(&server_addr, 0, sizeof(server_addr)); - server_addr.sin_family = AF_INET; - server_addr.sin_addr.s_addr = INADDR_ANY; - server_addr.sin_port = htons(port); - - if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == - -1) { - perror("bind"); - close(listen_fd); - return -1; - } - - if (listen(listen_fd, SOMAXCONN) == -1) { - perror("listen"); - close(listen_fd); - return -1; - } - - set_nonblocking(listen_fd); - return listen_fd; -} - -typedef struct { - int client_fd; - int upstream_fd; -} connection_t; - -void close_connection(int epoll_fd, connection_t *conn) { - if (conn->client_fd != -1) { - epoll_ctl(epoll_fd, EPOLL_CTL_DEL, conn->client_fd, NULL); - close(conn->client_fd); - } - if (conn->upstream_fd != -1) { - epoll_ctl(epoll_fd, EPOLL_CTL_DEL, conn->upstream_fd, NULL); - close(conn->upstream_fd); - } -} - -int forward_data(int from_fd, int to_fd) { - static char buffer[BUFFER_SIZE]; - // Feels great to do somehow. Better safe than sorry. - memset(buffer, 0, BUFFER_SIZE); - ssize_t bytes_read = recv(from_fd, buffer, sizeof(buffer), 0); - if (bytes_read > 0) { - ssize_t bytes_written = send(to_fd, buffer, bytes_read, 0); - if (bytes_written == -1) { - perror("write"); - } - } else if (bytes_read == 0) { - printf("Connection closed by remote (fd=%d)\n", from_fd); - } else { - perror("read"); - } - return (int)bytes_read; -} - -int listen_fd = 0; -int epoll_fd = 0; void cleanup() { close(epoll_fd); close(listen_fd); py_destruct(); printf("Graceful exit.\n"); } + void handle_sigint(int sig) { printf("\nCtrl+C pressed.\n"); exit(0); @@ -157,118 +44,9 @@ int main() { perror("Failed to register signal handler"); return EXIT_FAILURE; } - - listen_fd = create_listening_socket(LISTEN_PORT); - if (listen_fd == -1) { - fprintf(stderr, "Failed to create listening socket\n"); - return EXIT_FAILURE; - } atexit(cleanup); - epoll_fd = epoll_create1(0); - if (epoll_fd == -1) { - perror("epoll_create1"); - close(listen_fd); - return EXIT_FAILURE; - } - struct epoll_event event; - event.events = EPOLLIN; - event.data.fd = listen_fd; + serve(LISTEN_PORT); - if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &event) == -1) { - perror("epoll_ctl"); - close(listen_fd); - close(epoll_fd); - return EXIT_FAILURE; - } - - struct epoll_event events[MAX_EVENTS]; - memset(events, 0, sizeof(events)); - - printf("Pretty Good Server listening on port %d\n", LISTEN_PORT); - connection_t connections[MAX_EVENTS][sizeof(connection_t)] = {0}; - while (1) { - int num_events = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); - if (num_events == -1) { - perror("epoll_wait"); - break; - } - - for (int i = 0; i < num_events; i++) { - if (events[i].data.fd == listen_fd) { - struct sockaddr_in client_addr; - socklen_t client_len = sizeof(client_addr); - int client_fd = - accept(listen_fd, (struct sockaddr *)&client_addr, &client_len); - if (client_fd == -1) { - perror("accept"); - continue; - } - set_nonblocking(client_fd); - - struct epoll_event client_event; - client_event.events = EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLHUP; - client_event.data.ptr = connections[client_fd]; - client_event.data.fd = client_fd; - connections[client_fd]->upstream_fd = -1; - connections[client_fd]->client_fd = client_fd; - - // connections[client_fd]->upstream_fd = upstream_fd; - // connections[conn->client_fd] = conn; - // connections[upstream_fd]->client_fd = client_fd; - // connections[upstream_fd]->upstream_fd = upstream_fd; - - epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &client_event); - - } else { - connection_t *conn = connections[events[i].data.fd]; - if (events[i].events & (EPOLLHUP | EPOLLERR)) { - printf("Connection closed: client_fd=%d, upstream_fd=%d\n", - conn->client_fd, conn->upstream_fd); - close_connection(epoll_fd, conn); - } else if (events[i].events & EPOLLIN) { - if (conn->upstream_fd == -1) { - conn->upstream_fd = prepare_upstream(); - int upstream_fd = py_route(conn->client_fd, conn->upstream_fd); - - if (upstream_fd == -1) { - close_connection(epoll_fd, conn); - continue; - } - set_nonblocking(upstream_fd); - struct epoll_event upstream_event; - upstream_event.events = EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLHUP; - upstream_event.data.ptr = connections[upstream_fd]; - upstream_event.data.fd = upstream_fd; - - connections[conn->client_fd]->upstream_fd = upstream_fd; - connections[upstream_fd]->client_fd = conn->client_fd; - connections[upstream_fd]->upstream_fd = upstream_fd; - - epoll_ctl(epoll_fd, EPOLL_CTL_ADD, upstream_fd, &upstream_event); - - printf("Connected: client_fd=%d, upstream_fd=%d\n", conn->client_fd, - conn->upstream_fd); - continue; - } - - if (events[i].data.fd == conn->client_fd) { - - if (forward_data(conn->client_fd, conn->upstream_fd) < 1) { - close_connection(epoll_fd, conn); - } - } else if (events[i].data.fd == conn->upstream_fd) { - - if (forward_data(conn->upstream_fd, conn->client_fd) < 1) { - close_connection(epoll_fd, conn); - } - } - } - } - } - } - - close(listen_fd); - close(epoll_fd); return EXIT_SUCCESS; }