diff --git a/Makefile b/Makefile index b1e1d63..99f0ab9 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ SRC = $(wildcard src/*.c) OBJ = $(subst .c,.o,$(subst src,work,$(SRC))) LIBS = -pthread -pie -lrt $(shell pkg-config --libs gnutls) -CFLAGS := -O2 -pipe -Wall -Wpedantic -Werror +CFLAGS := -O2 -pipe -Wall -Wpedantic -Werror -ansi -D_POSIX_C_SOURCE=200809L CFLAGS += -Isrc/include -fpie $(shell pkg-config --cflags gnutls) INSTALLDIR := /usr/sbin OUT = swebs diff --git a/example/run.sh b/example/run.sh index 5b7879f..84a4ab1 100755 --- a/example/run.sh +++ b/example/run.sh @@ -1,3 +1,3 @@ #!/bin/sh -../build/swebs -B -P swebs.pid -s sitefile -o logs -p 8000 -b 100 +../build/swebs -P swebs.pid -s sitefile -o logs -p 8000 -b 100 -j2 diff --git a/src/connections.c b/src/connections.c index 87681a4..c9d83a1 100644 --- a/src/connections.c +++ b/src/connections.c @@ -33,6 +33,8 @@ #include int newConnection(Stream *stream, Connection *ret) { + struct timespec currentTime; + ret->stream = stream; ret->progress = RECEIVE_REQUEST; @@ -51,10 +53,11 @@ int newConnection(Stream *stream, Connection *ret) { ret->path = NULL; ret->body = NULL; - //pointers to things that are allocated within functions should be - //initialized to NULL so that free() doens't fail. + /* + * pointers to things that are allocated within functions should be + * initialized to NULL so that free() doens't fail. + * */ - struct timespec currentTime; if (clock_gettime(CLOCK_MONOTONIC, ¤tTime) < 0) { free(ret->currLine); return 1; @@ -81,8 +84,15 @@ void freeConnection(Connection *conn) { } static int processRequest(Connection *conn) { - char *line = conn->currLine; - for (int i = 0;; i++) { + char *line; + long i; + + line = conn->currLine; + /* + * line is not necessarily always conn->currentLine. It's the beginning + * of the currently parsing thing. + * */ + for (i = 0;; i++) { if (line[i] == ' ') { line[i] = '\0'; conn->type = getType(line); @@ -95,7 +105,7 @@ static int processRequest(Connection *conn) { return 1; } } - for (int i = 0;; i++) { + for (i = 0;; i++) { if (line[i] == ' ') { line[i] = '\0'; conn->path = malloc(i + 1); @@ -118,10 +128,14 @@ static int processRequest(Connection *conn) { } static int processField(Connection *conn) { + long i; + char *line, *split, *field, *value; + size_t linelen; + Field *newfields; if (conn->currLineLen == 0) { conn->progress = RECEIVE_BODY; conn->bodylen = 0; - for (size_t i = 0; i < conn->fieldCount; i++) { + for (i = 0; i < conn->fieldCount; i++) { if (strcmp(conn->fields[i].field, "Content-Length") == 0) conn->bodylen = atol(conn->fields[i].value); @@ -135,26 +149,26 @@ static int processField(Connection *conn) { if (conn->fieldCount >= conn->allocatedFields) { conn->allocatedFields *= 2; - Field *newfields = realloc(conn->fields, conn->allocatedFields * + newfields = realloc(conn->fields, conn->allocatedFields * sizeof(char *[2])); if (newfields == NULL) return 1; conn->fields = newfields; } - char *line = conn->currLine; - char *split = strstr(line, ": "); - size_t linelen = conn->currLineLen; + line = conn->currLine; + split = strstr(line, ": "); + linelen = conn->currLineLen; if (split == NULL) return 1; - char *field = malloc(split - line + 1); + field = malloc(split - line + 1); memcpy(field, line, split - line); field[split - line] = '\0'; linelen -= split - line + 2; line += split - line + 2; - char *value = malloc(linelen + 1); + value = malloc(linelen + 1); memcpy(value, line, linelen + 1); conn->fields[conn->fieldCount].field = field; @@ -168,8 +182,9 @@ static int processField(Connection *conn) { static int processChar(Connection *conn, char c, Sitefile *site) { if (conn->progress < RECEIVE_BODY) { if (conn->currLineLen >= conn->currLineAlloc) { + char *newline; conn->currLineAlloc *= 2; - char *newline = realloc(conn->currLine, + newline = realloc(conn->currLine, conn->currLineAlloc); assert(newline != NULL); conn->currLine = newline; @@ -203,26 +218,29 @@ static int processChar(Connection *conn, char c, Sitefile *site) { } static long diff(struct timespec *t1, struct timespec *t2) { +/* returns the difference in times in milliseconds */ return (t2->tv_sec - t1->tv_sec) * 1000 + (t2->tv_nsec - t1->tv_nsec) / 1000000; } int updateConnection(Connection *conn, Sitefile *site) { - char buff[300]; for (;;) { + char buff[300]; + ssize_t received; + unsigned long i; struct timespec currentTime; if (clock_gettime(CLOCK_MONOTONIC, ¤tTime) < 0) return 1; if (site->timeout > 0 && diff(&conn->lastdata, ¤tTime) > site->timeout) return 1; - ssize_t received = recvStream(conn->stream, buff, sizeof(buff)); + received = recvStream(conn->stream, buff, sizeof(buff)); if (received < 0) return errno != EAGAIN; if (received == 0) return 0; memcpy(&conn->lastdata, ¤tTime, sizeof(struct timespec)); - for (unsigned long i = 0; i < received; i++) { + for (i = 0; i < received; i++) { if (processChar(conn, buff[i], site)) return 1; } diff --git a/src/include/connections.h b/src/include/connections.h index d35da53..a552fe1 100644 --- a/src/include/connections.h +++ b/src/include/connections.h @@ -15,8 +15,9 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#ifndef _HAVE_CONNECTIONS -#define _HAVE_CONNECTIONS +#ifndef HAVE_CONNECTIONS +#define HAVE_CONNECTIONS + #include #include #include @@ -24,7 +25,7 @@ typedef enum { RECEIVE_REQUEST, RECEIVE_HEADER, - RECEIVE_BODY, + RECEIVE_BODY } ConnectionSteps; typedef struct { @@ -37,38 +38,42 @@ typedef struct Connection { ConnectionSteps progress; struct timespec lastdata; - //the last time that data was received from this connection. + /* the last time that data was received from this connection. */ RequestType type; char *path; - //ephemeral + /* ephemeral */ Field *fields; - //pointer to array of 2 pointers, persistent + /* pointer to array of 2 pointers, persistent */ size_t fieldCount; size_t allocatedFields; char *body; - //ephemeral + /* ephemeral */ size_t bodylen; size_t receivedBody; char *currLine; - //persistent + /* persistent */ size_t currLineAlloc; size_t currLineLen; } Connection; -//The 2 types of fields: -//Persistent fields: Things which aren't freed after a reset, currLine, fields -//Ephemeral fields: Things which are freed and reallocated after each new -//request, path, body +/* + * The 2 types of fields: + * Persistent fields: Things which aren't freed after a reset, currLine, fields + * Ephemeral fields: Things which are freed and reallocated after each new + * request, path, body + * */ int newConnection(Stream *stream, Connection *ret); -//returns non-zero on error. creates a new connection bound to fd +/* returns non-zero on error. */ void resetConnection(Connection *conn); void freeConnection(Connection *conn); int updateConnection(Connection *conn, Sitefile *site); -//returns non-zero on error. -//Generating a new connection and repeatedly calling updateConnection will -//handle everything. +/* + * returns non-zero on error. + * Generating a new connection and repeatedly calling updateConnection will + * handle everything. + * */ #endif diff --git a/src/include/responses.h b/src/include/responses.h index 63c4b8c..b6ce69f 100644 --- a/src/include/responses.h +++ b/src/include/responses.h @@ -15,11 +15,11 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#ifndef _HAVE_RESPONSES -#define _HAVE_RESPONSES +#ifndef HAVE_RESPONSES +#define HAVE_RESPONSES #include #include int sendResponse(Connection *conn, Sitefile *site); -//returns 1 on error, sets conn->progress to SEND_RESPONSE and sets conn->fd +/* returns 1 on error, sets conn->progress to SEND_RESPONSE */ #endif diff --git a/src/include/responseutil.h b/src/include/responseutil.h index 5f2d8dc..3edfab4 100644 --- a/src/include/responseutil.h +++ b/src/include/responseutil.h @@ -15,8 +15,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#ifndef _HAVE_RESPONSE_UTIL -#define _HAVE_RESPONSE_UTIL +#ifndef HAVE_RESPONSE_UTIL +#define HAVE_RESPONSE_UTIL #include #define CODE_200 "200 OK" @@ -26,10 +26,12 @@ #define ERROR_500 "500 Internal Server Error" char *getCode(int code); -int sendStringResponse(Connection *conn, const char *status, char *str); -int sendBinaryResponse(Connection *conn, const char *status, +int sendStringResponse(Stream *stream, const char *status, char *str); +int sendBinaryResponse(Stream *stream, const char *status, void *data, size_t len); -int sendErrorResponse(Connection *conn, const char *error); -//sendErrorResponse(conn, ERROR_404); -int sendHeader(Connection *conn, const char *status, size_t len); +int sendErrorResponse(Stream *stream, const char *error); +/* sendErrorResponse(conn, ERROR_404); */ +int sendHeader(Stream *stream, const char *status, size_t len); +int sendSeekableFile(Stream *stream, const char *status, int fd); +int sendPipe(Stream *stream, const char *status, int fd); #endif diff --git a/src/include/runner.h b/src/include/runner.h index 9fa20e9..bd57a50 100644 --- a/src/include/runner.h +++ b/src/include/runner.h @@ -15,8 +15,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#ifndef _HAVE_RUNNER -#define _HAVE_RUNNER +#ifndef HAVE_RUNNER +#define HAVE_RUNNER #include #include @@ -25,16 +25,18 @@ typedef struct { Sitefile *site; int *pending; - //pending[thread id] = the number of connections being handled by that - // thread + /* + * pending[thread id] = the number of connections being handled by that + * thread + * */ int notify; /* * When this runner should accept a connection, notify will contain an * int ready to be read. - */ + * */ int id; } RunnerArgs; -//my least favourite anti pattern +/* my least favourite anti pattern */ void *runServer(RunnerArgs *args); #endif diff --git a/src/include/sitefile.h b/src/include/sitefile.h index 5f5eb0a..814afac 100644 --- a/src/include/sitefile.h +++ b/src/include/sitefile.h @@ -15,8 +15,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#ifndef _HAVE_SITEFILE -#define _HAVE_SITEFILE +#ifndef HAVE_SITEFILE +#define HAVE_SITEFILE #include #include @@ -24,7 +24,7 @@ typedef enum { READ, EXEC, - THROW, + THROW } Command; typedef struct { diff --git a/src/include/sockets.h b/src/include/sockets.h index 0fbc34b..15b1337 100644 --- a/src/include/sockets.h +++ b/src/include/sockets.h @@ -15,8 +15,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#ifndef _HAVE_SOCKETS -#define _HAVE_SOCKETS +#ifndef HAVE_SOCKETS +#define HAVE_SOCKETS #include #include @@ -41,16 +41,18 @@ typedef struct { int initTLS(); Listener *createListener(SocketType type, uint16_t port, int backlog, ...); -//extra arguments depend on type (similar to fcntl): -//tcp: (void) -//tls: (char *keyfile, char *certfile, char *ocspfile) +/* + * extra arguments depend on type (similar to fcntl): + * tcp: (void) + * tls: (char *keyfile, char *certfile, char *ocspfile) + * */ Stream *acceptStream(Listener *listener, int flags); -//returns 1 on error, accepts fcntl flags +/* returns 1 on error, accepts fcntl flags */ void freeListener(Listener *listener); void freeStream(Stream *stream); ssize_t sendStream(Stream *stream, const void *data, size_t len); ssize_t recvStream(Stream *stream, void *data, size_t len); -//return value is the same as the read and write syscalls. +/* return value is the same as the read and write syscalls. */ #endif diff --git a/src/include/util.h b/src/include/util.h index 9fba6bc..342a78b 100644 --- a/src/include/util.h +++ b/src/include/util.h @@ -15,11 +15,11 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#ifndef _HAVE_UTIL -#define _HAVE_UTIL +#ifndef HAVE_UTIL +#define HAVE_UTIL typedef enum { TCP, - TLS, + TLS } SocketType; typedef enum { @@ -30,14 +30,16 @@ typedef enum { DELETE, PATCH, OPTIONS, - INVALID, - //this indicates an invalid type of request, there is no request called - //INVALID in HTTP/1.1. + INVALID + /* + * this indicates an invalid type of request, there is no request called + * INVALID in HTTP/1.1. + * */ } RequestType; int initLogging(char *path); int createLog(char *msg); int istrcmp(char *s1, char *s2); -//case insensitive strcmp +/* case insensitive strcmp */ RequestType getType(char *str); #endif diff --git a/src/main.c b/src/main.c index 78f065a..b87eb26 100644 --- a/src/main.c +++ b/src/main.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include @@ -36,14 +36,55 @@ #include #include +static void daemonize(char *pidfile) { + pid_t pid; + FILE *pidout; + pid = fork(); + switch (pid) { + case -1: + exit(EXIT_FAILURE); + case 0: + break; + default: + pidout = fopen(pidfile, "w"); + fprintf(pidout, "%d\n", getpid()); + fclose(pidout); + exit(EXIT_SUCCESS); + } + + if (setsid() < 0) + exit(EXIT_FAILURE); +} + +static void printLongMessage(char *first, ...) { + va_list ap; + va_start(ap, first); + puts(first); + for (;;) { + char *nextarg = va_arg(ap, char *); + if (nextarg == NULL) + break; + puts(nextarg); + } + va_end(ap); +} + int main(int argc, char **argv) { char *logout = "/var/log/swebs.log"; char *sitefile = NULL; int processes = sysconf(_SC_NPROCESSORS_ONLN) + 1; uint16_t port = 443; int backlog = 100; - bool shouldDaemonize = false; + char shouldDaemonize = 0; char *pidfile = "/run/swebs.pid"; + + Sitefile *site; + Listener *listener; + + int *pending, (*notify)[2]; + pthread_t *threads; + + int i; for (;;) { int c = getopt(argc, argv, "o:j:s:p:b:c:BP:hl"); if (c == -1) @@ -65,39 +106,41 @@ int main(int argc, char **argv) { backlog = atoi(optarg); break; case 'B': - shouldDaemonize = true; + shouldDaemonize = 1; break; case 'P': pidfile = optarg; break; case 'l': - printf( -"swebs Copyright (C) 2022 Nate Choe\n" -"This is free software, and you are welcome to redistribute under certain\n" -"conditions, but comes with ABSOLUTELY NO WARRANTY. For more details see the\n" -"GNU General Public License Version 3\n" -"\n" -"This program dynamically links with:\n" -" gnutls (gnutls.org)\n" -"\n" -"For any complaints, email me at natechoe9@gmail.com\n" -"I'm a programmer not a lawyer, so there's a good chance I accidentally\n" -"violated the LGPL.\n" + printLongMessage( +"swebs Copyright (C) 2022 Nate Choe", +"This is free software, and you are welcome to redistribute under certain", +"conditions, but comes with ABSOLUTELY NO WARRANTY. For more details see the", +"GNU General Public License Version 3\n", + +"This program dynamically links with:", +" gnutls (gnutls.org)\n", + +"For any complaints, email me at natechoe9@gmail.com", +"I'm a programmer not a lawyer, so there's a good chance I accidentally", +"violated the LGPL.", +NULL ); exit(EXIT_SUCCESS); case 'h': - printf( -"Usage: swebs [options]\n" -" -o [out] Set the log file (default: /var/log/swebs.log)\n" -" -j [cores] Use that many cores (default: $(nproc)+1)\n" -" -s [site file] Use that site file (required)\n" -" -p [port] Set the port (default: 443)\n" -" -b [backlog] Set the socket backlog (default: 100)\n" -" -B Run swebs in the background and daemonize\n" -" -P [pidfile] Specify PID file if daemonizing\n" -" (defualt: /run/swebs.pid)\n" -" -l Show some legal details\n" -" -h Show this help message\n" + printLongMessage( +"Usage: swebs [options]", +" -o [out] Set the log file (default: /var/log/swebs.log)", +" -j [cores] Use that many cores (default: $(nproc)+1)", +" -s [site file] Use that site file (required)", +" -p [port] Set the port (default: 443)", +" -b [backlog] Set the socket backlog (default: 100)", +" -B Run swebs in the background and daemonize", +" -P [pidfile] Specify PID file if daemonizing", +" (defualt: /run/swebs.pid)", +" -l Show some legal details", +" -h Show this help message", +NULL ); exit(EXIT_SUCCESS); case '?': @@ -111,13 +154,12 @@ int main(int argc, char **argv) { fprintf(stderr, "No sitefile configured\n"); exit(EXIT_FAILURE); } - Sitefile *site = parseSitefile(sitefile); + site = parseSitefile(sitefile); if (site == NULL) { fprintf(stderr, "Invalid sitefile %s\n", sitefile); exit(EXIT_FAILURE); } - Listener *listener; switch (site->type) { case TCP: default: listener = createListener(TCP, port, backlog); @@ -133,13 +175,8 @@ int main(int argc, char **argv) { exit(EXIT_FAILURE); } - if (shouldDaemonize) { - if (daemon(1, 0) < 0) - exit(EXIT_FAILURE); - FILE *pid = fopen(pidfile, "w"); - fprintf(pid, "%d\n", getpid()); - fclose(pid); - } + if (shouldDaemonize) + daemonize(pidfile); if (initLogging(logout)) { fprintf(stderr, "Couldn't open logs file %s\n", logout); @@ -147,13 +184,14 @@ int main(int argc, char **argv) { } { - struct passwd *swebs = getpwnam("swebs"); + struct passwd *swebs, *root; + swebs = getpwnam("swebs"); if (swebs == NULL) createLog("Couldn't find swebs user"); else if (seteuid(swebs->pw_uid)) createLog("seteuid() failed"); - struct passwd *root = getpwnam("root"); + root = getpwnam("root"); if (root == NULL) { createLog("Couldn't find root user, quitting"); exit(EXIT_FAILURE); @@ -164,18 +202,18 @@ int main(int argc, char **argv) { } } - int *pending = calloc(processes - 1, sizeof(int)); - int (*notify)[2] = malloc(sizeof(int[2]) * (processes - 1)); - pthread_t *threads = malloc(sizeof(pthread_t) * (processes - 1)); + pending = calloc(processes - 1, sizeof(int)); + notify = malloc(sizeof(int[2]) * (processes - 1)); + threads = malloc(sizeof(pthread_t) * (processes - 1)); if (threads == NULL) exit(EXIT_FAILURE); - for (int i = 0; i < processes - 1; i++) { - if (pipe(notify[i])) - exit(EXIT_FAILURE); + for (i = 0; i < processes - 1; i++) { RunnerArgs *args = malloc(sizeof(RunnerArgs)); if (args == NULL) exit(EXIT_FAILURE); + if (pipe(notify[i])) + exit(EXIT_FAILURE); args->site = site; args->pending = pending; args->notify = notify[i][0]; @@ -188,21 +226,19 @@ int main(int argc, char **argv) { for (;;) { Stream *stream = acceptStream(listener, O_NONBLOCK); + int lowestThread; if (stream == NULL) { createLog("Accepting a stream failed"); continue; } - int lowestThread = 0; - int lowestCount = pending[0]; - for (int i = 1; i < processes - 1; i++) { - if (pending[i] < lowestCount) { + lowestThread = 0; + for (i = 1; i < processes - 1; i++) + if (pending[i] < pending[lowestThread]) lowestThread = i; - lowestCount = pending[i]; - } - } if (write(notify[lowestThread][1], &stream, sizeof(&stream)) < sizeof(&stream)) exit(EXIT_FAILURE); } + } diff --git a/src/responses.c b/src/responses.c index 1897238..377444c 100644 --- a/src/responses.c +++ b/src/responses.c @@ -30,71 +30,59 @@ #include #include -static int resilientSend(Stream *stream, const void *buff, size_t len) { -//Will either write len bytes, or return 1 for error. - const char *data = (const char *) buff; - //using character pointers to do pointer arithmetic - while (len > 0) { - ssize_t sent = sendStream(stream, data, len); - if (sent < 0) - return 1; - len -= sent; - data += sent; - } - return 0; -} - static int readResponse(Connection *conn, char *path) { - int fd = 1; + int fd = -1; struct stat statbuf; if (stat(path, &statbuf)) { - sendErrorResponse(conn, ERROR_404); + sendErrorResponse(conn->stream, ERROR_404); return 1; } if (S_ISDIR(statbuf.st_mode)) { long reqPathLen = strlen(conn->path); long pathLen = strlen(path); char *assembledPath = malloc(reqPathLen + pathLen + 1); + char requestPath[PATH_MAX], responsePath[PATH_MAX]; + size_t responsePathLen; + struct stat requestBuff; if (assembledPath == NULL) goto error; memcpy(assembledPath, path, pathLen); memcpy(assembledPath + pathLen, conn->path, reqPathLen + 1); - char requestPath[PATH_MAX]; if (realpath(assembledPath, requestPath) == NULL) { if (errno == ENOENT) { free(assembledPath); - sendErrorResponse(conn, ERROR_404); + sendErrorResponse(conn->stream, ERROR_404); return 1; } free(assembledPath); goto error; } - char responsePath[PATH_MAX]; if (realpath(path, responsePath) == NULL) { free(assembledPath); goto error; } - size_t responsePathLen = strlen(responsePath); + responsePathLen = strlen(responsePath); if (memcmp(requestPath, responsePath, responsePathLen)) { free(assembledPath); goto forbidden; } - //in theory an attacker could just request - // /blog/../../../../site/privatekey.pem - //so we make sure that the filepath is actually within the path - //specified by the page. + /* + * in theory an attacker could just request + * /blog/../../../../site/privatekey.pem + * so we make sure that the filepath is actually within the path + * specified by the page. + * */ - struct stat requestbuf; - if (stat(requestPath, &requestbuf)) { + if (stat(requestPath, &requestBuff)) { free(assembledPath); - sendErrorResponse(conn, ERROR_404); + sendErrorResponse(conn->stream, ERROR_404); return 1; } - if (S_ISDIR(requestbuf.st_mode)) { + if (S_ISDIR(requestBuff.st_mode)) { free(assembledPath); - sendErrorResponse(conn, ERROR_400); + sendErrorResponse(conn->stream, ERROR_400); return 1; } @@ -105,53 +93,38 @@ static int readResponse(Connection *conn, char *path) { fd = open(path, O_RDONLY); if (fd < 0) goto forbidden; - off_t len = lseek(fd, 0, SEEK_END); - if (len < 0) - goto error; - if (lseek(fd, 0, SEEK_SET) < 0) { - sendErrorResponse(conn, ERROR_500); - close(fd); - return 1; - } - sendHeader(conn, CODE_200, len); - - while (len > 0) { - char buff[1024]; - size_t readLen = read(fd, buff, sizeof(buff)); - if (resilientSend(conn->stream, buff, readLen)) - goto error; - len -= readLen; - } - - return 0; + return sendSeekableFile(conn->stream, CODE_200, fd); error: - sendErrorResponse(conn, ERROR_500); + sendErrorResponse(conn->stream, ERROR_500); return 1; forbidden: - sendErrorResponse(conn, ERROR_403); + sendErrorResponse(conn->stream, ERROR_403); return 1; } static int execResponse(Connection *conn, char *path) { int output[2]; + int status; + pid_t pid; if (pipe(output)) goto error; - pid_t pid = fork(); + pid = fork(); if (pid < 0) goto error; if (pid == 0) { + char **args; + int i; close(1); close(output[0]); dup2(output[1], 1); - char **args = - malloc((conn->fieldCount*2 + 2) * sizeof(char *)); + args = malloc((conn->fieldCount*2 + 2) * sizeof(char *)); if (args == NULL) { close(output[1]); exit(EXIT_FAILURE); } args[0] = conn->path; - for (int i = 0; i < conn->fieldCount; i++) { + for (i = 0; i < conn->fieldCount; i++) { args[i * 2 + 1] = conn->fields[i].field; args[i * 2 + 2] = conn->fields[i].value; } @@ -165,46 +138,17 @@ static int execResponse(Connection *conn, char *path) { free(args); exit(EXIT_SUCCESS); } - size_t allocResponse = 1024; - size_t responseLen = 0; - char *response = malloc(allocResponse); + close(output[1]); - for (;;) { - if (responseLen == allocResponse) { - allocResponse *= 2; - char *newresponse = realloc(response, allocResponse); - if (newresponse == NULL) - goto looperror; - response = newresponse; - } - ssize_t len = read(output[0], - response + responseLen, - allocResponse - responseLen); - if (len < 0) - goto looperror; - if (len == 0) - break; - responseLen += len; - continue; -looperror: - close(output[0]); - int status; - waitpid(pid, &status, 0); - free(response); - goto error; + + status = sendPipe(conn->stream, CODE_200, output[0]); + { + int exitcode; + waitpid(pid, &exitcode, 0); } - close(output[0]); - int status; - waitpid(pid, &status, 0); - sendHeader(conn, CODE_200, responseLen); - if (resilientSend(conn->stream, response, responseLen)) { - free(response); - return 1; - } - free(response); - return 0; + return status; error: - sendErrorResponse(conn, ERROR_500); + sendErrorResponse(conn->stream, ERROR_500); return 1; } @@ -217,17 +161,18 @@ static int fullmatch(regex_t *regex, char *str) { int sendResponse(Connection *conn, Sitefile *site) { char *host = NULL; - for (int i = 0; i < conn->fieldCount; i++) { + int i; + for (i = 0; i < conn->fieldCount; i++) { if (strcmp(conn->fields[i].field, "Host") == 0) { host = conn->fields[i].value; break; } } if (host == NULL) { - sendErrorResponse(conn, ERROR_400); + sendErrorResponse(conn->stream, ERROR_400); return 1; } - for (int i = 0; i < site->size; i++) { + for (i = 0; i < site->size; i++) { if (site->content[i].respondto != conn->type) continue; if (fullmatch(&site->content[i].host, host)) @@ -243,17 +188,18 @@ int sendResponse(Connection *conn, Sitefile *site) { site->content[i].arg); break; case THROW: - sendErrorResponse(conn, + sendErrorResponse(conn->stream, site->content[i].arg); break; default: - sendErrorResponse(conn, ERROR_500); + sendErrorResponse(conn->stream, + ERROR_500); return 1; } resetConnection(conn); return 0; } } - sendErrorResponse(conn, ERROR_404); + sendErrorResponse(conn->stream, ERROR_404); return 1; } diff --git a/src/responseutil.c b/src/responseutil.c index 5733eb1..0fb1f81 100644 --- a/src/responseutil.c +++ b/src/responseutil.c @@ -22,17 +22,35 @@ #include #include +#include #include #include #define CONST_FIELDS "Server: swebs/0.1\r\n" -static int sendConnection(Connection *conn, char *format, ...) { +static int resilientSend(Stream *stream, void *data, size_t len) { + char *buffer = (char *) data; + size_t left = len; + while (left) { + ssize_t sent = sendStream(stream, buffer, left); + if (sent < 0) + return 1; + if (sent == 0) + break; + buffer += sent; + left -= sent; + } + return left != 0; +} + +static int sendStreamValist(Stream *stream, char *format, ...) { va_list ap; + int len; + char *data; va_start(ap, format); - int len = vsnprintf(NULL, 0, format, ap); - char *data = malloc(len + 1); + len = vsnprintf(NULL, 0, format, ap); + data = malloc(len + 1); if (data == NULL) return 1; @@ -40,7 +58,7 @@ static int sendConnection(Connection *conn, char *format, ...) { va_start(ap, format); vsprintf(data, format, ap); - if (sendStream(conn->stream, data, len) < len) { + if (resilientSend(stream, data, len) < len) { free(data); return 1; } @@ -65,8 +83,8 @@ char *getCode(int code) { } } -int sendStringResponse(Connection *conn, const char *status, char *str) { - return sendConnection(conn, +int sendStringResponse(Stream *stream, const char *status, char *str) { + return sendStreamValist(stream, "HTTP/1.1 %s\r\n" CONST_FIELDS "Content-Length: %lu\r\n" @@ -76,38 +94,89 @@ int sendStringResponse(Connection *conn, const char *status, char *str) { ); } -int sendErrorResponse(Connection *conn, const char *error) { +int sendErrorResponse(Stream *stream, const char *error) { const char *template = "" "

" "%s" "

"; + int ret; int len = snprintf(NULL, 0, template, error); char *response = malloc(len + 1); sprintf(response, template, error); - int ret = sendStringResponse(conn, error, response); + ret = sendStringResponse(stream, error, response); free(response); return ret; } -int sendBinaryResponse(Connection *conn, const char *status, +int sendBinaryResponse(Stream *stream, const char *status, void *data, size_t len) { - if (sendConnection(conn, - "HTTP/1.1 %s\r\n" - CONST_FIELDS - "Content-Length: %lu\r\n" - "\r\n" - , status, len) - ) + if (sendHeader(stream, status, len)) return 1; - return sendStream(conn->stream, data, len) < len; + return resilientSend(stream, data, len); } -int sendHeader(Connection *conn, const char *status, size_t len) { - return (sendConnection(conn, +int sendHeader(Stream *stream, const char *status, size_t len) { + return (sendStreamValist(stream, "HTTP/1.1 %s\r\n" CONST_FIELDS "Content-Length: %lu\r\n" "\r\n" , status, len)); } + +int sendSeekableFile(Stream *stream, const char *status, int fd) { + off_t len; + size_t totalSent = 0; + len = lseek(fd, 0, SEEK_END); + lseek(fd, 0, SEEK_SET); + sendHeader(stream, status, len); + for (;;) { + char buffer[1024]; + ssize_t inBuffer = read(fd, buffer, sizeof(buffer)); + if (inBuffer < 0) + return 1; + if (inBuffer == 0) + return totalSent != len; + if (resilientSend(stream, buffer, inBuffer)) + return 1;; + } +} + +int sendPipe(Stream *stream, const char *status, int fd) { + size_t allocResponse = 1024; + size_t responseLen = 0; + char *response = malloc(allocResponse); + for (;;) { + ssize_t len; + if (responseLen >= allocResponse) { + char *newresponse; + allocResponse *= 2; + newresponse = realloc(response, allocResponse); + if (newresponse == NULL) + goto error; + response = newresponse; + } + len = read(fd, + response + responseLen, + allocResponse - responseLen); + if (len < 0) + goto error; + else if (len == 0) + break; + responseLen += len; + } + close(fd); + sendHeader(stream, CODE_200, responseLen); + if (resilientSend(stream, response, responseLen)) { + free(response); + return 1; + } + free(response); + return 0; +error: + close(fd); + free(response); + sendErrorResponse(stream, ERROR_500); + return 1; +} diff --git a/src/runner.c b/src/runner.c index 43ea5fd..a4370f1 100644 --- a/src/runner.c +++ b/src/runner.c @@ -15,7 +15,6 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#define _GNU_SOURCE #include #include #include @@ -39,19 +38,18 @@ void *runServer(RunnerArgs *args) { int allocConns = 100; struct pollfd *fds = malloc(sizeof(struct pollfd) * allocConns); Connection *connections = malloc(sizeof(Connection) * allocConns); + int connCount = 1; + /* connections are 1 indexed because fds[0] is the notify fd. */ assert(fds != NULL); assert(connections != NULL); fds[0].fd = notify; fds[0].events = POLLIN; - int connCount = 1; - //connections are 1 indexed, this is because fds[0] is the notify fd. for (;;) { - poll(fds, connCount, -1); + int i; + poll(fds, connCount, site->timeout); - for (int i = 1; i < connCount; i++) { - if (fds[i].revents & POLLRDHUP) - goto remove; + for (i = 1; i < connCount; i++) { if (updateConnection(connections + i, site)) goto remove; continue; @@ -65,27 +63,30 @@ remove: } if (fds[0].revents & POLLIN) { + Stream *newstream; if (connCount >= allocConns) { + struct pollfd *newfds; + Connection *newconns; allocConns *= 2; - struct pollfd *newfds = realloc(fds, + newfds = realloc(fds, sizeof(struct pollfd) * allocConns); if (newfds == NULL) exit(EXIT_FAILURE); fds = newfds; - Connection *newconns = realloc(connections, + newconns = realloc(connections, sizeof(Connection) * allocConns); if (newconns == NULL) exit(EXIT_FAILURE); connections = newconns; } - Stream *stream; - if (read(notify, &stream, sizeof(stream)) < sizeof(stream)) + if (read(notify, &newstream, sizeof(newstream)) + < sizeof(newstream)) exit(EXIT_FAILURE); - fds[connCount].fd = stream->fd; - fds[connCount].events = POLLIN | POLLRDHUP; + fds[connCount].fd = newstream->fd; + fds[connCount].events = POLLIN; - if (newConnection(stream, connections + connCount)) + if (newConnection(newstream, connections + connCount)) exit(EXIT_FAILURE); connCount++; pending[id]++; diff --git a/src/sitefile.c b/src/sitefile.c index 218dfd8..2f81d33 100644 --- a/src/sitefile.c +++ b/src/sitefile.c @@ -28,12 +28,13 @@ typedef enum { SUCCESS, LINE_END, FILE_END, - ERROR, + ERROR } ReturnCode; -//this isn't ideal, but it's necessary to avoid namespace collisions. +/* this isn't ideal, but it's necessary to avoid namespace collisions. */ static void freeTokens(int argc, char **argv) { - for (int i = 0; i < argc; i++) + int i; + for (i = 0; i < argc; i++) free(argv[i]); free(argv); } @@ -41,9 +42,11 @@ static void freeTokens(int argc, char **argv) { static ReturnCode getToken(FILE *file, char **ret) { typedef enum { QUOTED, - NONQUOTED, + NONQUOTED } TokenType; TokenType type; + size_t allocatedLen = 50; + size_t len; for (;;) { int c = fgetc(file); @@ -67,19 +70,19 @@ static ReturnCode getToken(FILE *file, char **ret) { } } - long allocatedLen = 30; - long len; *ret = malloc(allocatedLen); for (len = 0;; len++) { + int c; if (len >= allocatedLen) { + char *newret; allocatedLen *= 2; - char *newret = realloc(*ret, allocatedLen); + newret = realloc(*ret, allocatedLen); if (newret == NULL) goto error; *ret = newret; } - int c = fgetc(file); + c = fgetc(file); switch (type) { case QUOTED: if (c == '"') @@ -114,22 +117,27 @@ error: } static ReturnCode getCommand(FILE *file, int *argcret, char ***argvret) { -//THIS FUNCTION WILL NOT RETURN LINE_END +/* THIS FUNCTION WILL NOT RETURN LINE_END */ + int argc; + char **argv; + int allocatedTokens; if (feof(file)) return FILE_END; - int argc = 0; - int allocatedTokens = 5; - char **argv = malloc(allocatedTokens * sizeof(char *)); + argc = 0; + allocatedTokens = 5; + argv = malloc(allocatedTokens * sizeof(*argv)); for (;;) { + ReturnCode code; if (argc >= allocatedTokens) { + char **newargv; allocatedTokens *= 2; - char **newargv = realloc(argv, + newargv = realloc(argv, allocatedTokens * sizeof(char *)); if (newargv == NULL) goto error; argv = newargv; } - ReturnCode code = getToken(file, argv + argc); + code = getToken(file, argv + argc); switch (code) { case ERROR: @@ -137,8 +145,8 @@ static ReturnCode getCommand(FILE *file, int *argcret, char ***argvret) { case LINE_END: if (argc == 0) continue; - //We allow empty lines - //fallthrough + /* We allow empty lines */ + /* fallthrough */ case FILE_END: if (argc == 0) { free(argv); @@ -159,28 +167,29 @@ error: Sitefile *parseSitefile(char *path) { FILE *file = fopen(path, "r"); + RequestType respondto = GET; + const int cflags = REG_EXTENDED | REG_ICASE; + char *host = NULL; + int argc; + char **argv; + int allocatedLength = 50; + Sitefile *ret; + if (file == NULL) return NULL; - Sitefile *ret = malloc(sizeof(Sitefile)); + ret = malloc(sizeof(Sitefile)); if (ret == NULL) return NULL; ret->type = TCP; ret->key = NULL; ret->cert = NULL; ret->timeout = 0; - int allocatedLength = 50; ret->size = 0; ret->content = malloc(allocatedLength * sizeof(SiteCommand)); if (ret->content == NULL) { free(ret); return NULL; } - RequestType respondto = GET; - const int cflags = REG_EXTENDED | REG_ICASE; - char *host = NULL; - int lasthostset = 0; - int argc; - char **argv; for (;;) { ReturnCode status = getCommand(file, &argc, &argv); switch (status) { @@ -200,12 +209,8 @@ Sitefile *parseSitefile(char *path) { if (respondto == INVALID) goto error; } - else if (strcmp(argv[1], "host") == 0) { - if (ret->size == lasthostset) - free(host); + else if (strcmp(argv[1], "host") == 0) host = strdup(argv[2]); - lasthostset = ret->size; - } else goto error; continue; @@ -232,8 +237,9 @@ Sitefile *parseSitefile(char *path) { continue; } if (ret->size >= allocatedLength) { + SiteCommand *newcontent; allocatedLength *= 2; - SiteCommand *newcontent = realloc(ret->content, + newcontent = realloc(ret->content, allocatedLength * sizeof(SiteCommand)); if (newcontent == NULL) goto error; @@ -282,7 +288,8 @@ nterror: } void freeSitefile(Sitefile *site) { - for (long i = 0; i < site->size; i++) { + long i; + for (i = 0; i < site->size; i++) { regfree(&site->content[i].path); regfree(&site->content[i].host); free(site->content[i].arg); diff --git a/src/sockets.c b/src/sockets.c index 24ae6fd..7a53b83 100644 --- a/src/sockets.c +++ b/src/sockets.c @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -35,6 +36,7 @@ int initTLS() { Listener *createListener(SocketType type, uint16_t port, int backlog, ...) { Listener *ret = malloc(sizeof(Listener)); + va_list ap; if (ret == NULL) return NULL; ret->type = type; @@ -43,11 +45,13 @@ Listener *createListener(SocketType type, uint16_t port, int backlog, ...) { free(ret); return NULL; } - int opt = 1; - if (setsockopt(ret->fd, SOL_SOCKET, - SO_REUSEPORT, - &opt, sizeof(opt)) < 0) { - goto error; + { + int opt = 1; + if (setsockopt(ret->fd, SOL_SOCKET, SO_REUSEADDR, + &opt, sizeof(opt))) { + free(ret); + return NULL; + } } ret->addr.sin_family = AF_INET; ret->addr.sin_addr.s_addr = INADDR_ANY; @@ -58,7 +62,6 @@ Listener *createListener(SocketType type, uint16_t port, int backlog, ...) { if (listen(ret->fd, backlog) < 0) goto error; - va_list ap; va_start(ap, backlog); switch (type) { case TCP: default: @@ -104,8 +107,10 @@ Stream *acceptStream(Listener *listener, int flags) { return NULL; } - int oldflags = fcntl(ret->fd, F_GETFL); - fcntl(ret->fd, F_SETFL, oldflags | flags); + { + int oldflags = fcntl(ret->fd, F_GETFL); + fcntl(ret->fd, F_SETFL, oldflags | flags); + } switch (listener->type) { case TCP: default: diff --git a/src/util.c b/src/util.c index 85faa57..03d94e8 100644 --- a/src/util.c +++ b/src/util.c @@ -32,8 +32,9 @@ int initLogging(char *path) { int createLog(char *msg) { time_t currenttime; + struct tm *timeinfo; time(¤ttime); - struct tm *timeinfo = gmtime(¤ttime); + timeinfo = gmtime(¤ttime); if (timeinfo == NULL) return 1; fprintf(logs, "[%d-%02d-%02dT%02d:%02d:%02dZ] %s\n", @@ -49,7 +50,8 @@ int createLog(char *msg) { } int istrcmp(char *s1, char *s2) { - for (int i = 0;; i++) { + int i; + for (i = 0;; i++) { char c1 = tolower(s1[i]); char c2 = tolower(s2[i]); if (c1 != c2) @@ -60,10 +62,12 @@ int istrcmp(char *s1, char *s2) { } RequestType getType(char *str) { + uint64_t type; + int i; if (strlen(str) >= 8) return INVALID; - uint64_t type = 0; - for (int i = 0; str[i]; i++) { + type = 0; + for (i = 0; str[i]; i++) { type <<= 8; type |= str[i]; } @@ -85,6 +89,8 @@ RequestType getType(char *str) { default: return INVALID; } - //This would actually be far nicer in HolyC of all languages. I feel - //like the context immediately following each magic number is enough. + /* + * This would actually be far nicer in HolyC of all languages. I feel + * like the context immediately following each magic number is enough. + * */ }