Bind to socket; HTTP server event thread; signal handling.

This commit is contained in:
Jordan Bancino 2022-08-11 19:40:13 -04:00
parent 46cd0edaf8
commit e94212b080
6 changed files with 291 additions and 26 deletions

134
src/HttpServer.c Normal file
View file

@ -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 <HttpServer.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
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 <stdio.h>
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;
}

View file

@ -26,15 +26,35 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <signal.h>
#include <grp.h> #include <grp.h>
#include <pwd.h> #include <pwd.h>
#include <TelodendriaConfig.h> #include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <TelodendriaConfig.h>
#include <Log.h> #include <Log.h>
#include <HashMap.h> #include <HashMap.h>
#include <Config.h> #include <Config.h>
#include <HttpServer.h>
HttpServer *httpServer = NULL;
static void
TelodendriaHttpHandler(HttpRequest * req, HttpResponse * res, void *args)
{
}
static void
TelodendriaSignalHandler(int signalNo)
{
(void) signalNo;
HttpServerStop(httpServer);
}
typedef enum ArgFlag typedef enum ArgFlag
{ {
@ -73,6 +93,31 @@ TelodendriaPrintUsage(LogConfig * lc)
Log(lc, LOG_MESSAGE, " -h Print this usage, then exit."); 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 int
main(int argc, char **argv) main(int argc, char **argv)
{ {
@ -96,6 +141,12 @@ main(int argc, char **argv)
struct passwd *userInfo; struct passwd *userInfo;
struct group *groupInfo; struct group *groupInfo;
/* Networking */
int httpSocket = -1;
/* Signal handling */
struct sigaction sigAction;
lc = LogConfigCreate(); lc = LogConfigCreate();
if (!lc) if (!lc)
@ -239,7 +290,7 @@ main(int argc, char **argv)
Log(lc, LOG_DEBUG, "Configuration:"); Log(lc, LOG_DEBUG, "Configuration:");
LogConfigIndent(lc); 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, "Server Name: %s", tConfig->serverName);
Log(lc, LOG_DEBUG, "Chroot: %s", tConfig->chroot); Log(lc, LOG_DEBUG, "Chroot: %s", tConfig->chroot);
Log(lc, LOG_DEBUG, "Run As: %s:%s", tConfig->uid, tConfig->gid); 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); Log(lc, LOG_DEBUG, "Flags: %x", tConfig->flags);
LogConfigUnindent(lc); LogConfigUnindent(lc);
Log(lc, LOG_TASK, "Setting permissions...");
if (chdir(tConfig->chroot) != 0) if (chdir(tConfig->chroot) != 0)
{ {
Log(lc, LOG_ERROR, "Unable to change into data directory: %s.", strerror(errno)); 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()."); 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) if (getuid() == 0)
{ {
#ifndef __OpenBSD__ #ifndef __OpenBSD__
@ -325,7 +383,50 @@ main(int argc, char **argv)
tConfig->uid = NULL; tConfig->uid = NULL;
tConfig->gid = 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: 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); Log(lc, LOG_DEBUG, "Exiting with code '%d'.", exit);
TelodendriaConfigFree(tConfig); TelodendriaConfigFree(tConfig);
LogConfigFree(lc); LogConfigFree(lc);

View file

@ -95,17 +95,15 @@ TelodendriaConfigParse(HashMap * config, LogConfig * lc)
if (!directive) if (!directive)
{ {
Log(lc, LOG_WARNING, "No 'listen' directive specified; using defaults, which may change."); Log(lc, LOG_WARNING, "No 'listen' directive specified; using default value, which may change.");
tConfig->listenHost = UtilStringDuplicate("localhost");
tConfig->listenPort = 8008; tConfig->listenPort = 8008;
} }
else else
{ {
ASSERT_NO_CHILDREN("listen"); ASSERT_NO_CHILDREN("listen");
ASSERT_VALUES("listen", 2); ASSERT_VALUES("listen", 1);
COPY_VALUE(tConfig->listenHost, 0);
tConfig->listenPort = (unsigned short) atoi(ArrayGet(value, 1)); tConfig->listenPort = (unsigned short) atoi(ArrayGet(value, 0));
if (!tConfig->listenPort) if (!tConfig->listenPort)
{ {
Log(lc, LOG_ERROR, "Expected numeric value for listen port, got '%s'.", ArrayGet(value, 1)); Log(lc, LOG_ERROR, "Expected numeric value for listen port, got '%s'.", ArrayGet(value, 1));
@ -316,7 +314,6 @@ TelodendriaConfigFree(TelodendriaConfig * tConfig)
return; return;
} }
free(tConfig->listenHost);
free(tConfig->serverName); free(tConfig->serverName);
free(tConfig->chroot); free(tConfig->chroot);
free(tConfig->uid); free(tConfig->uid);

View file

@ -99,18 +99,6 @@ typedef enum HttpStatus
HTTP_NETWORK_AUTH_REQUIRED = 511 HTTP_NETWORK_AUTH_REQUIRED = 511
} HttpStatus; } HttpStatus;
struct HttpRequest
{
HttpRequestMethod method;
HashMap *headers;
};
struct HttpResponse
{
HttpStatus status;
HashMap *headers;
};
extern char * extern char *
HttpGetStatusString(const HttpStatus httpStatus); HttpGetStatusString(const HttpStatus httpStatus);
@ -120,6 +108,4 @@ extern HttpRequestMethod
typedef struct HttpRequest HttpRequest; typedef struct HttpRequest HttpRequest;
typedef struct HttpResponse HttpResponse; typedef struct HttpResponse HttpResponse;
typedef void (*HttpHandler) (HttpRequest *, HttpResponse *);
#endif #endif

48
src/include/HttpServer.h Normal file
View file

@ -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 <Http.h>
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

View file

@ -52,14 +52,13 @@ typedef enum TelodendriaConfigFlag
*/ */
typedef struct TelodendriaConfig typedef struct TelodendriaConfig
{ {
char *listenHost;
unsigned short listenPort;
char *serverName; char *serverName;
char *chroot; char *chroot;
char *uid; char *uid;
char *gid; char *gid;
char *dataDir; char *dataDir;
unsigned short listenPort;
unsigned int flags; unsigned int flags;
unsigned int threads; unsigned int threads;