From e94212b080c99a2644e011128f7348d3196f7c1b Mon Sep 17 00:00:00 2001 From: Jordan Bancino Date: Thu, 11 Aug 2022 19:40:13 -0400 Subject: [PATCH] Bind to socket; HTTP server event thread; signal handling. --- src/HttpServer.c | 134 ++++++++++++++++++++++++++++++++ src/Telodendria.c | 109 +++++++++++++++++++++++++- src/TelodendriaConfig.c | 9 +-- src/include/Http.h | 14 ---- src/include/HttpServer.h | 48 ++++++++++++ src/include/TelodendriaConfig.h | 3 +- 6 files changed, 291 insertions(+), 26 deletions(-) create mode 100644 src/HttpServer.c create mode 100644 src/include/HttpServer.h diff --git a/src/HttpServer.c b/src/HttpServer.c new file mode 100644 index 0000000..70e54f7 --- /dev/null +++ b/src/HttpServer.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +#include +#include +#include + +struct HttpServer +{ + int sd; + unsigned int nThreads; + HttpHandler *requestHandler; + void *handlerArgs; + + pthread_t socketThread; + + volatile unsigned int stop:1; + volatile unsigned int isRunning:1; +}; + +HttpServer * +HttpServerCreate(int socketDesc, unsigned int nThreads, HttpHandler * requestHandler, void *handlerArgs) +{ + HttpServer *server = malloc(sizeof(HttpServer)); + + if (!server) + { + return NULL; + } + + server->sd = socketDesc; + server->nThreads = nThreads; + server->requestHandler = requestHandler; + server->handlerArgs = handlerArgs; + server->stop = 0; + server->isRunning = 0; + + return server; +} + +void +HttpServerFree(HttpServer * server) +{ + free(server); +} + +#include + +static void * +HttpServerEventThread(void *args) +{ + HttpServer *server = (HttpServer *) args; + + server->isRunning = 1; + server->stop = 0; + + while (!server->stop) + { + printf("In server event thread\n"); + fflush(stdout); + sleep(1); + } + + server->isRunning = 0; + + printf("Event thread dying!\n"); + + return NULL; +} + +int +HttpServerStart(HttpServer * server) +{ + if (!server) + { + return 0; + } + + if (server->isRunning) + { + return 1; + } + + if (pthread_create(&server->socketThread, NULL, HttpServerEventThread, server) != 0) + { + return 0; + } + + return 1; +} + +void +HttpServerJoin(HttpServer * server) +{ + if (!server) + { + return; + } + + pthread_join(server->socketThread, NULL); +} + +void +HttpServerStop(HttpServer * server) +{ + if (!server) + { + return; + } + + server->stop = 1; +} diff --git a/src/Telodendria.c b/src/Telodendria.c index f21d511..a90461a 100644 --- a/src/Telodendria.c +++ b/src/Telodendria.c @@ -26,15 +26,35 @@ #include #include #include +#include #include #include -#include +#include +#include +#include +#include #include #include #include +#include + +HttpServer *httpServer = NULL; + +static void +TelodendriaHttpHandler(HttpRequest * req, HttpResponse * res, void *args) +{ + +} + +static void +TelodendriaSignalHandler(int signalNo) +{ + (void) signalNo; + HttpServerStop(httpServer); +} typedef enum ArgFlag { @@ -73,6 +93,31 @@ TelodendriaPrintUsage(LogConfig * lc) Log(lc, LOG_MESSAGE, " -h Print this usage, then exit."); } +static int +TelodendriaBindSocket(unsigned short port) +{ + struct sockaddr_in remote = {0}; + int s = socket(AF_INET, SOCK_STREAM, 0); + + if (s < 0) + { + /* Unable to create the socket */ + return -1; + } + + remote.sin_family = AF_INET; + remote.sin_port = port; + remote.sin_addr.s_addr = htonl(INADDR_ANY); + + if (bind(s, (struct sockaddr *) & remote, sizeof(remote)) < 0) + { + /* Unable to bind the socket */ + return -1; + } + + return s; +} + int main(int argc, char **argv) { @@ -96,6 +141,12 @@ main(int argc, char **argv) struct passwd *userInfo; struct group *groupInfo; + /* Networking */ + int httpSocket = -1; + + /* Signal handling */ + struct sigaction sigAction; + lc = LogConfigCreate(); if (!lc) @@ -239,7 +290,7 @@ main(int argc, char **argv) Log(lc, LOG_DEBUG, "Configuration:"); LogConfigIndent(lc); - Log(lc, LOG_DEBUG, "Listen On: %s:%d", tConfig->listenHost, tConfig->listenPort); + Log(lc, LOG_DEBUG, "Listen On: %d", tConfig->listenPort); Log(lc, LOG_DEBUG, "Server Name: %s", tConfig->serverName); Log(lc, LOG_DEBUG, "Chroot: %s", tConfig->chroot); Log(lc, LOG_DEBUG, "Run As: %s:%s", tConfig->uid, tConfig->gid); @@ -248,8 +299,6 @@ main(int argc, char **argv) Log(lc, LOG_DEBUG, "Flags: %x", tConfig->flags); LogConfigUnindent(lc); - Log(lc, LOG_TASK, "Setting permissions..."); - if (chdir(tConfig->chroot) != 0) { Log(lc, LOG_ERROR, "Unable to change into data directory: %s.", strerror(errno)); @@ -277,6 +326,15 @@ main(int argc, char **argv) Log(lc, LOG_DEBUG, "Found user/group information using getpwnam() and getgrnam()."); } + /* Bind the socket before possibly dropping permissions */ + httpSocket = TelodendriaBindSocket(tConfig->listenPort); + if (httpSocket < 0) + { + Log(lc, LOG_ERROR, "Unable to bind to port %d: %s", strerror(errno)); + exit = EXIT_FAILURE; + goto finish; + } + if (getuid() == 0) { #ifndef __OpenBSD__ @@ -325,7 +383,50 @@ main(int argc, char **argv) tConfig->uid = NULL; tConfig->gid = NULL; + Log(lc, LOG_TASK, "Starting server..."); + + httpServer = HttpServerCreate(httpSocket, tConfig->threads, TelodendriaHttpHandler, NULL); + if (!httpServer) + { + Log(lc, LOG_ERROR, "Unable to create HTTP server."); + exit = EXIT_FAILURE; + goto finish; + } + + if (!HttpServerStart(httpServer)) + { + Log(lc, LOG_ERROR, "Unable to start HTTP server."); + exit = EXIT_FAILURE; + goto finish; + } + + Log(lc, LOG_MESSAGE, "Ready."); + + sigAction.sa_handler = TelodendriaSignalHandler; + sigfillset(&sigAction.sa_mask); + sigAction.sa_flags = SA_RESTART; + + if (sigaction(SIGINT, &sigAction, NULL) < 0) + { + Log(lc, LOG_ERROR, "Unable to install signal handler."); + exit = EXIT_FAILURE; + goto finish; + } + + /* Block this thread until the server is terminated by a signal + * handler */ + HttpServerJoin(httpServer); + finish: + Log(lc, LOG_TASK, "Shutting down..."); + if (httpSocket > 0) + { + close(httpSocket); + } + if (httpServer) + { + HttpServerFree(httpServer); + } Log(lc, LOG_DEBUG, "Exiting with code '%d'.", exit); TelodendriaConfigFree(tConfig); LogConfigFree(lc); diff --git a/src/TelodendriaConfig.c b/src/TelodendriaConfig.c index ecd0f96..abe03b0 100644 --- a/src/TelodendriaConfig.c +++ b/src/TelodendriaConfig.c @@ -95,17 +95,15 @@ TelodendriaConfigParse(HashMap * config, LogConfig * lc) if (!directive) { - Log(lc, LOG_WARNING, "No 'listen' directive specified; using defaults, which may change."); - tConfig->listenHost = UtilStringDuplicate("localhost"); + Log(lc, LOG_WARNING, "No 'listen' directive specified; using default value, which may change."); tConfig->listenPort = 8008; } else { ASSERT_NO_CHILDREN("listen"); - ASSERT_VALUES("listen", 2); - COPY_VALUE(tConfig->listenHost, 0); + ASSERT_VALUES("listen", 1); - tConfig->listenPort = (unsigned short) atoi(ArrayGet(value, 1)); + tConfig->listenPort = (unsigned short) atoi(ArrayGet(value, 0)); if (!tConfig->listenPort) { Log(lc, LOG_ERROR, "Expected numeric value for listen port, got '%s'.", ArrayGet(value, 1)); @@ -316,7 +314,6 @@ TelodendriaConfigFree(TelodendriaConfig * tConfig) return; } - free(tConfig->listenHost); free(tConfig->serverName); free(tConfig->chroot); free(tConfig->uid); diff --git a/src/include/Http.h b/src/include/Http.h index 6b28e7a..1801806 100644 --- a/src/include/Http.h +++ b/src/include/Http.h @@ -99,18 +99,6 @@ typedef enum HttpStatus HTTP_NETWORK_AUTH_REQUIRED = 511 } HttpStatus; -struct HttpRequest -{ - HttpRequestMethod method; - HashMap *headers; -}; - -struct HttpResponse -{ - HttpStatus status; - HashMap *headers; -}; - extern char * HttpGetStatusString(const HttpStatus httpStatus); @@ -120,6 +108,4 @@ extern HttpRequestMethod typedef struct HttpRequest HttpRequest; typedef struct HttpResponse HttpResponse; -typedef void (*HttpHandler) (HttpRequest *, HttpResponse *); - #endif diff --git a/src/include/HttpServer.h b/src/include/HttpServer.h new file mode 100644 index 0000000..a2d61da --- /dev/null +++ b/src/include/HttpServer.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef TELODENDRIA_HTTPSERVER_H +#define TELODENDRIA_HTTPSERVER_H + +#include + +typedef struct HttpServer HttpServer; + +typedef void (HttpHandler) (HttpRequest *, HttpResponse *, void *); + +extern HttpServer * + HttpServerCreate(int, unsigned int, HttpHandler *, void *); + +extern void + HttpServerFree(HttpServer *); + +extern int + HttpServerStart(HttpServer *); + +extern void + HttpServerJoin(HttpServer *); + +extern void + HttpServerStop(HttpServer *); + +#endif diff --git a/src/include/TelodendriaConfig.h b/src/include/TelodendriaConfig.h index 4a4afe2..fd88a9a 100644 --- a/src/include/TelodendriaConfig.h +++ b/src/include/TelodendriaConfig.h @@ -52,14 +52,13 @@ typedef enum TelodendriaConfigFlag */ typedef struct TelodendriaConfig { - char *listenHost; - unsigned short listenPort; char *serverName; char *chroot; char *uid; char *gid; char *dataDir; + unsigned short listenPort; unsigned int flags; unsigned int threads;