Multiple port support

This commit is contained in:
Nate Choe
2022-06-21 23:34:13 -05:00
parent dc044b22e3
commit 47cf044858
19 changed files with 333 additions and 136 deletions

View File

@@ -27,7 +27,7 @@
#include <swebs/responses.h>
#include <swebs/connections.h>
int newConnection(Stream *stream, Connection *ret) {
int newConnection(Stream *stream, Connection *ret, int portind) {
struct timespec currentTime;
ret->stream = stream;
@@ -68,6 +68,8 @@ int newConnection(Stream *stream, Connection *ret) {
return 1;
}
memcpy(&ret->lastdata, &currentTime, sizeof(struct timespec));
ret->portind = portind;
return 0;
}
@@ -360,10 +362,11 @@ int updateConnection(Connection *conn, Sitefile *site) {
ssize_t received;
unsigned long i;
struct timespec currentTime;
const Port *port = site->ports + conn->portind;
if (clock_gettime(CLOCK_MONOTONIC, &currentTime) < 0)
return 1;
if (site->timeout > 0 &&
diff(&conn->lastdata, &currentTime) > site->timeout)
if (port->timeout > 0 &&
diff(&conn->lastdata, &currentTime) > port->timeout)
return 1;
received = recvStream(conn->stream, buff, sizeof(buff));
if (received < 0)

View File

@@ -20,6 +20,7 @@
#include <stdarg.h>
#include <string.h>
#include <poll.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
@@ -41,11 +42,11 @@ typedef struct {
static Runner *runners;
static int processes;
static int mainfd;
static int *pending;
static Listener *listener;
static Sitefile *site;
static int mainfd; /* fd of the UNIX socket */
static struct sockaddr_un addr;
static ConnInfo *conninfo;
/* We want to be able to handle a signal at any time, so some global variables
* are needed. */
static const int signals[] = {
@@ -54,7 +55,6 @@ static const int signals[] = {
};
static void exitClean(int signal) {
freeListener(listener);
close(mainfd);
remove(addr.sun_path);
exit(EXIT_SUCCESS);
@@ -109,14 +109,16 @@ static void createProcess(int id) {
unsetsignal(SIGCHLD);
connfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (connfd < 0)
if (connfd < 0) {
createErrorLog("socket() failed, killing child", errno);
exit(EXIT_FAILURE);
}
if (connect(connfd, (struct sockaddr *) &addr, sizeof(addr))) {
createErrorLog("connect() failed, killing child", errno);
exit(EXIT_FAILURE);
}
close(mainfd);
runServer(connfd, site, listener, pending, id);
runServer(connfd, site, pending, id, conninfo);
createLog("child runServer() finished");
exit(EXIT_SUCCESS);
}
@@ -138,8 +140,24 @@ static void remakeChild(int signal) {
int main(int argc, char **argv) {
int i;
int pendingid;
int backlog;
int conninfoid;
Listener **listeners;
struct pollfd *pollfds;
setup(argc, argv, &site, &listener, &processes);
setup(argc, argv, &site, &processes, &backlog);
listeners = xmalloc(site->portcount * sizeof *listeners);
pollfds = xmalloc(site->portcount * sizeof *pollfds);
for (i = 0; i < site->portcount; ++i) {
listeners[i] = createListener(site->ports[i].num, backlog);
if (listeners[i] == NULL) {
fprintf(stderr, "Failed to listen on port %hu\n",
site->ports[i].num);
}
pollfds[i].fd = listenerfd(listeners[i]);
pollfds[i].events = POLLIN;
}
pendingid = smalloc(sizeof(int) * (processes - 1));
if (pendingid < 0) {
@@ -151,6 +169,18 @@ int main(int argc, char **argv) {
createErrorLog("saddr() failed", errno);
exit(EXIT_FAILURE);
}
conninfoid = smalloc(sizeof *conninfo);
if (conninfoid < 0) {
createErrorLog("smalloc() failed", errno);
exit(EXIT_FAILURE);
}
conninfo = saddr(conninfoid);
if (conninfo == NULL) {
createErrorLog("saddr() failed", errno);
exit(EXIT_FAILURE);
}
memset(pending, 0, sizeof(int) * (processes - 1));
mainfd = socket(AF_UNIX, SOCK_STREAM, 0);
@@ -175,25 +205,29 @@ int main(int argc, char **argv) {
createLog("swebs started");
for (;;) {
int fd;
int lowestProc;
fd = acceptConnection(listener);
if (fd < 0) {
if (errno == ENOTSOCK || errno == EOPNOTSUPP ||
errno == EINVAL) {
createErrorLog("You've majorly screwed up. Good luck",
errno);
exit(EXIT_FAILURE);
}
continue;
if (poll(pollfds, site->portcount, -1) < 0) {
if (errno == EINTR)
continue;
createErrorLog("You've majorly screwed up. Good luck",
errno);
exit(EXIT_FAILURE);
}
createLog("Accepted stream");
lowestProc = 0;
for (i = 1; i < processes - 1; i++)
if (pending[i] < pending[lowestProc])
lowestProc = i;
sendFd(fd, runners[lowestProc].fd);
for (i = 0; i < site->portcount; ++i) {
if (pollfds[i].revents & POLLIN) {
int j, lowestproc, fd;
fd = acceptConnection(listeners[i]);
while (conninfo->valid) ;
lowestproc = 0;
for (j = 0; j < processes - 1; j++)
if (pending[j] < pending[lowestproc])
lowestproc = j;
conninfo->portind = i;
conninfo->valid = 1;
sendFd(fd, runners[lowestproc].fd);
}
}
}
}

View File

@@ -31,33 +31,42 @@
#include <swebs/sitefile.h>
#include <swebs/connections.h>
void runServer(int connfd, Sitefile *site, Listener *listener,
int *pending, int id) {
void runServer(int connfd, Sitefile *site, int *pending, int id,
ConnInfo *conninfo) {
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. */
Context *context;
assert(fds != NULL);
assert(connections != NULL);
struct pollfd *fds;
Connection *connections;
int connCount;
Context **contexts;
int i;
connCount = 1;
fds = xmalloc(allocConns * sizeof *fds);
connections = xmalloc(allocConns * sizeof *connections);
fds[0].fd = connfd;
fds[0].events = POLLIN;
/* connections are 1 indexed because fds[0] is the notify fd. I hate
* that poll() forces us to do these hacks. */
switch (site->type) {
case TCP:
context = createContext(TCP);
break;
case TLS:
context = createContext(TLS, site->key, site->cert);
break;
default:
createLog("Socket type is somehow invalid");
return;
}
if (context == NULL) {
createErrorLog("Failed to create context", errno);
exit(EXIT_FAILURE);
contexts = xmalloc(site->portcount * sizeof *contexts);
for (i = 0; i < site->portcount; ++i) {
Port *port = site->ports + i;
switch (port->type) {
case TCP:
contexts[i] = createContext(TCP);
break;
case TLS:
contexts[i] = createContext(TLS, port->key, port->cert);
break;
default:
createLog("Socket type is somehow invalid");
return;
}
if (contexts[i] == NULL) {
createErrorLog("Failed to create context", errno);
exit(EXIT_FAILURE);
}
}
{
@@ -78,7 +87,6 @@ void runServer(int connfd, Sitefile *site, Listener *listener,
}
for (;;) {
int i;
poll(fds, connCount, -1);
{
char log[200];
@@ -105,17 +113,22 @@ remove:
if (fds[0].revents & POLLIN) {
Stream *newstream;
int newfd;
int portind;
newfd = recvFd(connfd);
if (newfd < 0) {
createLog("Message received that included an invalid fd");
continue;
}
while (conninfo->valid == 0) ;
portind = conninfo->portind;
conninfo->valid = 0;
createLog("Obtained file descriptor from child");
newstream = createStream(context, O_NONBLOCK, newfd);
newstream = createStream(contexts[portind], O_NONBLOCK, newfd);
if (newstream == NULL) {
createLog("Stream couldn't be created from file descriptor");
createLog(
"Stream couldn't be created from file descriptor");
close(newfd);
continue;
}
@@ -141,7 +154,7 @@ remove:
connections = newconns;
}
if (newConnection(newstream, connections + connCount)) {
if (newConnection(newstream, connections + connCount, portind)) {
createLog("Couldn't initialize connection from stream");
continue;
}

View File

@@ -58,15 +58,15 @@ static void printLongMessage(char *first, ...) {
va_end(ap);
}
void setup(int argc, char **argv,
Sitefile **site, Listener **listener, int *processes) {
void setup(int argc, char **argv, Sitefile **site, int *processes,
int *backlog) {
char *logout = "/var/log/swebs.log";
char *sitefile = NULL;
int backlog = 100;
char shouldDaemonize = 0;
char *pidfile = "/run/swebs.pid";
*processes = sysconf(_SC_NPROCESSORS_ONLN) + 1;
*backlog = 100;
for (;;) {
int c = getopt(argc, argv, "o:j:s:b:c:Bp:hl");
@@ -83,7 +83,7 @@ void setup(int argc, char **argv,
sitefile = optarg;
break;
case 'b':
backlog = atoi(optarg);
*backlog = atoi(optarg);
break;
case 'B':
shouldDaemonize = 1;
@@ -139,12 +139,6 @@ NULL
exit(EXIT_FAILURE);
}
*listener = createListener((*site)->port, backlog);
if (*listener == NULL) {
fprintf(stderr, "Failed to create socket\n");
exit(EXIT_FAILURE);
}
if (shouldDaemonize)
daemonize(pidfile);

View File

@@ -175,40 +175,46 @@ error:
}
Sitefile *parseSitefile(char *path) {
FILE *file = fopen(path, "r");
FILE *file;
RequestType respondto = GET;
const int cflags = REG_EXTENDED | REG_ICASE;
char *host = NULL;
int argc;
char **argv;
int allocatedLength = 50;
char gotPort = 0;
Sitefile *ret;
unsigned short currport;
currport = 80;
file = fopen(path, "r");
if (file == NULL)
return NULL;
ret = malloc(sizeof(Sitefile));
if (ret == NULL)
return NULL;
ret->type = TCP;
ret->key = NULL;
ret->cert = NULL;
ret->timeout = 2000;
ret = xmalloc(sizeof *ret);
ret->size = 0;
ret->content = malloc(allocatedLength * sizeof(SiteCommand));
ret->alloc = 50;
ret->content = xmalloc(ret->alloc * sizeof *ret->content);
ret->portcount = 0;
ret->portalloc = 5;
ret->ports = xmalloc(ret->portalloc * sizeof *ret->ports);
#if DYNAMIC_LINKED_PAGES
ret->getResponse = NULL;
#endif
if (ret->content == NULL) {
free(ret);
return NULL;
}
for (;;) {
ReturnCode status = getCommand(file, &argc, &argv);
switch (status) {
int i;
case FILE_END:
if (!gotPort)
goto nterror;
for (i = 0; i < ret->portcount; ++i) {
Port *port = ret->ports + i;
if (port->type == TLS &&
(port->key == NULL ||
port->cert == NULL)) {
fprintf(stderr,
"Port %hu declared as TLS without proper TLS files\n", port->num);
goto nterror;
}
}
fclose(file);
return ret;
case ERROR: case LINE_END:
@@ -225,7 +231,9 @@ Sitefile *parseSitefile(char *path) {
goto error;
}
else if (strcmp(argv[1], "host") == 0)
host = strdup(argv[2]);
host = xstrdup(argv[2]);
else if (strcmp(argv[1], "port") == 0)
currport = atoi(argv[2]);
else
goto error;
continue;
@@ -233,31 +241,12 @@ Sitefile *parseSitefile(char *path) {
else if (strcmp(argv[0], "define") == 0) {
if (argc < 3)
goto error;
if (strcmp(argv[1], "transport") == 0) {
if (strcmp(argv[2], "TCP") == 0)
ret->type = TCP;
else if (strcmp(argv[2], "TLS") == 0)
ret->type = TLS;
else
goto error;
}
else if (strcmp(argv[1], "port") == 0) {
ret->port = atoi(argv[2]);
gotPort = 1;
}
else if (strcmp(argv[1], "key") == 0)
ret->key = strdup(argv[2]);
else if (strcmp(argv[1], "cert") == 0)
ret->cert = strdup(argv[2]);
else if (strcmp(argv[1], "timeout") == 0)
ret->timeout = atoi(argv[2]);
else if (strcmp(argv[1], "library") == 0) {
#if DYNAMIC_LINKED_PAGES
ret->getResponse = loadGetResponse(argv[2]);
#else
fprintf(stderr,
"This version of swebs has no dynamic page support\n"
);
fputs(
"This version of swebs has no dynamic page support\n", stderr);
exit(EXIT_FAILURE);
#endif
}
@@ -265,11 +254,69 @@ Sitefile *parseSitefile(char *path) {
goto error;
continue;
}
if (ret->size >= allocatedLength) {
else if (strcmp(argv[0], "declare") == 0) {
Port newport;
int i;
if (argc < 3) {
fputs(
"Usage: declare [transport] [port]\n", stderr);
goto error;
}
newport.num = atoi(argv[2]);
for (i = 0; i < ret->portcount; ++i) {
if (ret->ports[i].num == newport.num) {
fprintf(stderr,
"Port %hu declared multiple times\n", newport.num);
goto error;
}
}
if (strcmp(argv[1], "TCP") == 0)
newport.type = TCP;
else if (strcmp(argv[1], "TLS") == 0)
newport.type = TLS;
else {
fprintf(stderr, "Invalid transport %s\n",
argv[1]);
goto error;
}
newport.timeout = 2000;
newport.key = newport.cert = NULL;
if (ret->portcount >= ret->portalloc) {
ret->portalloc *= 2;
ret->ports = xrealloc(ret->ports,
ret->portalloc * sizeof *ret->ports);
}
memcpy(ret->ports + ret->portcount, &newport,
sizeof newport);
++ret->portcount;
continue;
}
#define PORT_ATTRIBUTE(name, func) \
else if (strcmp(argv[0], #name) == 0) { \
int i; \
unsigned short port; \
if (argc < 3) { \
fputs("Usage: " #name " [" #name "] [port]\n", \
stderr); \
goto error; \
} \
port = atoi(argv[2]); \
for (i = 0; i < ret->portcount; ++i) \
if (ret->ports[i].num == port) \
ret->ports[i].name = func(argv[1]); \
continue; \
}
PORT_ATTRIBUTE(key, xstrdup)
PORT_ATTRIBUTE(cert, xstrdup)
PORT_ATTRIBUTE(timeout, atoi)
#undef PORT_ATTRIBUTE
if (ret->size >= ret->alloc) {
SiteCommand *newcontent;
allocatedLength *= 2;
newcontent = realloc(ret->content,
allocatedLength * sizeof(SiteCommand));
ret->alloc *= 2;
newcontent = realloc(ret->content, ret->alloc *
sizeof *newcontent);
if (newcontent == NULL)
goto error;
ret->content = newcontent;
@@ -282,7 +329,7 @@ Sitefile *parseSitefile(char *path) {
if (strcmp(argv[0], "read") == 0) {
if (argc < 3)
goto error;
ret->content[ret->size].arg = strdup(argv[2]);
ret->content[ret->size].arg = xstrdup(argv[2]);
if (ret->content[ret->size].arg == NULL)
goto error;
ret->content[ret->size].command = READ;
@@ -295,16 +342,26 @@ Sitefile *parseSitefile(char *path) {
goto error;
ret->content[ret->size].command = THROW;
}
else if (strcmp(argv[0], "linked") == 0)
else if (strcmp(argv[0], "linked") == 0) {
#if DYNAMIC_LINKED_PAGES
ret->content[ret->size].command = LINKED;
else
#else
fputs(
"This version of swebs doesn't have linked page support", stderr);
goto error;
#endif
}
else {
fprintf(stderr, "Unknown sitefile command %s", argv[0]);
goto error;
}
freeTokens(argc, argv);
ret->content[ret->size].respondto = respondto;
if (host == NULL)
regcomp(&ret->content[ret->size].host, ".*", cflags);
else
regcomp(&ret->content[ret->size].host, host, cflags);
ret->content[ret->size].port = currport;
ret->size++;
}
error:

View File

@@ -66,6 +66,10 @@ error:
return NULL;
}
int listenerfd(Listener *listener) {
return listener->fd;
}
Context *createContext(SocketType type, ...) {
Context *ret;
va_list ap;

View File

@@ -57,6 +57,8 @@ typedef struct Connection {
/* persistent */
size_t currLineAlloc;
size_t currLineLen;
int portind;
} Connection;
/*
* The 2 types of fields:
@@ -65,7 +67,7 @@ typedef struct Connection {
* request, path, body
* */
int newConnection(Stream *stream, Connection *ret);
int newConnection(Stream *stream, Connection *ret, int portind);
/* returns non-zero on error. */
void resetConnection(Connection *conn);
void freeConnection(Connection *conn);

View File

@@ -23,6 +23,14 @@
#include <swebs/sitefile.h>
#include <swebs/connections.h>
void runServer(int connfd, Sitefile *site, Listener *listener,
int *pending, int id);
typedef struct {
int valid;
int portind;
} ConnInfo;
void runServer(int connfd, Sitefile *site, int *pending, int id,
ConnInfo *info);
/* pending and info are shared memory. pending[id] is the amount of connections
* that are being processed by that process, and info contains info about the
* connection being sent through. */
#endif

View File

@@ -21,8 +21,8 @@
#include <swebs/sockets.h>
#include <swebs/sitefile.h>
void setup(int argc, char **argv,
Sitefile **site, Listener **listener, int *processes);
void setup(int argc, char **argv, Sitefile **site, int *processes,
int *backlog);
/* Setup parses args, utilizes them, and returns only what is needed in the
* main loop. */

View File

@@ -28,22 +28,33 @@ typedef enum {
LINKED
} Command;
typedef struct {
SocketType type;
unsigned short num;
int timeout;
char *key;
char *cert;
/* key and cert are possible unused */
} Port;
typedef struct {
RequestType respondto;
regex_t host;
Command command;
regex_t path;
char *arg;
unsigned short port;
} SiteCommand;
typedef struct {
int size;
size_t size;
size_t alloc;
SiteCommand *content;
SocketType type;
char *key;
char *cert;
int timeout;
unsigned short port;
size_t portcount;
size_t portalloc;
Port *ports;
#if DYNAMIC_LINKED_PAGES
int (*getResponse)(Request *, Response *);
#endif

View File

@@ -45,6 +45,7 @@ typedef struct {
int initTLS();
Listener *createListener(uint16_t port, int backlog);
int listenerfd(Listener *listener);
Context *createContext(SocketType type, ...);
/*
* extra arguments depend on type (similar to fcntl):

View File

@@ -28,6 +28,12 @@ void *saddr(int id);
void sfree(void *addr);
void sdestroy(int id);
void *xmalloc(size_t size);
void *xrealloc(void *ptr, size_t size);
char *xstrdup(char *str);
/* These functions should only be used during setup (reading sitefiles and such)
* and not real runtime. */
int createLog(char *msg);
int createErrorLog(char *msg, int err);
int istrcmp(char *s1, char *s2);

View File

@@ -57,6 +57,36 @@ void sdestroy(int id) {
shmctl(id, IPC_RMID, 0);
}
void *xmalloc(size_t size) {
void *ret;
ret = malloc(size);
if (ret == NULL) {
fputs("xmalloc() failed\n", stderr);
exit(EXIT_FAILURE);
}
return ret;
}
void *xrealloc(void *ptr, size_t size) {
void *ret;
ret = realloc(ptr, size);
if (ret == NULL) {
fputs("xrealloc() failed\n", stderr);
exit(EXIT_FAILURE);
}
return ret;
}
char *xstrdup(char *str) {
char *ret;
ret = strdup(str);
if (ret == NULL) {
fputs("xstrdup() failed\n", stderr);
exit(EXIT_FAILURE);
}
return ret;
}
int createLog(char *msg) {
time_t currenttime;
struct tm *timeinfo;