Got it working for http

This commit is contained in:
Nate Choe
2022-04-01 19:58:50 -05:00
parent 26c0eaca15
commit 25c0505bf5
9 changed files with 238 additions and 125 deletions

View File

@@ -1,7 +1,7 @@
SRC = $(wildcard src/*.c)
OBJ = $(subst .c,.o,$(subst src,work,$(SRC)))
LIBS = -pthread -pie -lrt -ldl $(shell pkg-config --libs gnutls)
CFLAGS := -O2 -pipe -Wall -Wpedantic -ansi
CFLAGS := -O2 -pipe -Wall -Wpedantic -Wshadow -ansi -ggdb
CFLAGS += -Isrc/ -fpie -D_POSIX_C_SOURCE=200809L $(shell pkg-config --cflags gnutls)
INSTALLDIR := /usr/sbin
HEADERDIR := /usr/include/

View File

@@ -1,6 +1,6 @@
define port 8000
#define transport TLS
define transport TLS
define key domain.key
define cert domain.crt
define timeout 2000

View File

@@ -20,10 +20,13 @@
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <sys/un.h>
#include <pthread.h>
#include <sys/wait.h>
#include <swebs/util.h>
#include <swebs/setup.h>
@@ -31,15 +34,91 @@
#include <swebs/sockets.h>
#include <swebs/sitefile.h>
int main(int argc, char **argv) {
Sitefile *site;
Listener *listener;
int processes;
typedef struct {
pid_t pid;
int fd;
} Runner;
int *pending, pendingid, (*notify)[2];
pthread_t *threads;
static Runner *runners;
static int processes;
static int mainfd;
static int *pending;
static Listener *listener;
static Sitefile *site;
static struct sockaddr_un addr;
/* We want to be able to handle a signal at any time, so some global variables
* are needed. */
static void createProcess(int id) {
pid_t pid;
int connfd;
socklen_t addrlen;
createLog("Creating a new process");
pending[id] = 0;
pid = fork();
switch (pid) {
case -1:
createLog("fork() failed");
exit(EXIT_FAILURE);
case 0:
break;
default:
addrlen = sizeof(addr);
runners[id].pid = pid;
runners[id].fd = accept(mainfd,
(struct sockaddr *) &addr, &addrlen);
return;
}
connfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (connfd < 0)
exit(EXIT_FAILURE);
if (connect(connfd, (struct sockaddr *) &addr, sizeof(addr))) {
createLog("connect() failed, killing child");
exit(EXIT_FAILURE);
}
close(mainfd);
runServer(connfd, site, listener, pending, id);
createLog("child runServer() finished");
exit(EXIT_SUCCESS);
}
static void remakeChild(int signal) {
pid_t pid;
int i;
pid = wait(NULL);
createLog("A child has died, recreating");
for (i = 0; i < processes - 1; i++) {
if (runners[i].pid == pid) {
close(runners[i].fd);
createProcess(i);
return;
}
}
}
static void exitClean(int signal) {
freeListener(listener);
close(mainfd);
remove(addr.sun_path);
exit(EXIT_SUCCESS);
}
static void setsignal(int signal, void (*handler)(int)) {
struct sigaction action;
sigset_t sigset;
sigemptyset(&sigset);
action.sa_handler = handler;
action.sa_mask = sigset;
action.sa_flags = SA_NODEFER;
sigaction(SIGCHLD, &action, NULL);
}
int main(int argc, char **argv) {
int i;
int pendingid;
setup(argc, argv, &site, &listener, &processes);
@@ -48,63 +127,53 @@ int main(int argc, char **argv) {
createLog("smalloc() failed");
exit(EXIT_FAILURE);
}
pending = saddr(pendingid);;
pending = saddr(pendingid);
if (pending == NULL) {
createLog("saddr() failed");
exit(EXIT_FAILURE);
}
memset(pending, 0, sizeof(int) * (processes - 1));
notify = malloc(sizeof(int[2]) * (processes - 1));
if (notify == NULL) {
createLog("malloc() failed");
exit(EXIT_FAILURE);
}
mainfd = socket(AF_UNIX, SOCK_STREAM, 0);
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, tmpnam(NULL), sizeof(addr.sun_path) - 1);
/* I know that tmpname is deprecated, I think this usage is safe
* though. */
addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
bind(mainfd, (struct sockaddr *) &addr, sizeof(addr));
listen(mainfd, processes);
threads = malloc(sizeof(pthread_t) * (processes - 1));
if (threads == NULL) {
createLog("malloc() failed");
exit(EXIT_FAILURE);
}
runners = malloc(sizeof(Runner) * (processes - 1));
for (i = 0; i < processes - 1; i++)
createProcess(i);
for (i = 0; i < processes - 1; i++) {
RunnerArgs *args = malloc(sizeof(RunnerArgs));
if (args == NULL) {
createLog("malloc() failed");
exit(EXIT_FAILURE);
}
if (pipe(notify[i])) {
createLog("pipe() failed");
exit(EXIT_FAILURE);
}
args->site = site;
args->pendingid = pendingid;
args->notify = notify[i][0];
args->id = i;
pthread_create(threads + i, NULL,
(void*(*)(void*)) runServer, args);
}
signal(SIGPIPE, SIG_IGN);
setsignal(SIGPIPE, SIG_IGN);
setsignal(SIGKILL, exitClean);
setsignal(SIGINT, exitClean);
setsignal(SIGCHLD, remakeChild);
createLog("swebs started");
for (;;) {
Stream *stream = acceptStream(listener, O_NONBLOCK);
int lowestThread;
createLog("Accepted stream");
if (stream == NULL) {
createLog("Accepting a stream failed");
int fd;
int lowestProc;
fd = accept(listener->fd, (struct sockaddr *) &listener->addr,
&listener->addrlen);
if (fd < 0) {
if (errno == ENOTSOCK || errno == EOPNOTSUPP ||
errno == EINVAL) {
createLog("You've majorly screwed up");
exit(EXIT_FAILURE);
}
continue;
}
createLog("Accepted stream");
lowestThread = 0;
lowestProc = 0;
for (i = 1; i < processes - 1; i++)
if (pending[i] < pending[lowestThread])
lowestThread = i;
if (write(notify[lowestThread][1], &stream, sizeof(&stream))
< sizeof(&stream))
continue;
if (pending[i] < pending[lowestProc])
lowestProc = i;
sendFd(fd, runners[lowestProc].fd);
}
}

View File

@@ -15,12 +15,14 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <poll.h>
#include <fcntl.h>
#include <unistd.h>
#include <swebs/util.h>
@@ -28,12 +30,8 @@
#include <swebs/sitefile.h>
#include <swebs/connections.h>
void *runServer(RunnerArgs *args) {
Sitefile *site = args->site;
int *pending = saddr(args->pendingid);
int notify = args->notify;
int id = args->id;
void runServer(int connfd, Sitefile *site, Listener *listener,
int *pending, int id) {
int allocConns = 100;
struct pollfd *fds = malloc(sizeof(struct pollfd) * allocConns);
Connection *connections = malloc(sizeof(Connection) * allocConns);
@@ -41,7 +39,7 @@ void *runServer(RunnerArgs *args) {
/* connections are 1 indexed because fds[0] is the notify fd. */
assert(fds != NULL);
assert(connections != NULL);
fds[0].fd = notify;
fds[0].fd = connfd;
fds[0].events = POLLIN;
for (;;) {
@@ -71,6 +69,21 @@ remove:
if (fds[0].revents & POLLIN) {
Stream *newstream;
int newfd;
newfd = recvFd(connfd);
if (newfd < 0) {
createLog("Message received that included an invalid fd");
continue;
}
newstream = createStream(listener, O_NONBLOCK, newfd);
createLog("Obtained file descriptor from child");
if (newstream == NULL) {
createLog("Stream couldn't be created from file descriptor");
close(newfd);
continue;
}
if (connCount >= allocConns) {
struct pollfd *newfds;
Connection *newconns;
@@ -91,17 +104,15 @@ remove:
}
connections = newconns;
}
if (read(notify, &newstream, sizeof(newstream))
< sizeof(newstream))
continue;
fds[connCount].fd = newstream->fd;
fds[connCount].events = POLLIN;
if (newConnection(newstream, connections + connCount))
if (newConnection(newstream, connections + connCount)) {
createLog("Couldn't initialize connection from stream");
continue;
}
fds[connCount].fd = newfd;
fds[connCount].events = POLLIN;
connCount++;
pending[id]++;
}
}
return NULL;
}

View File

@@ -25,6 +25,7 @@
#include <netinet/in.h>
#include <gnutls/gnutls.h>
#include <swebs/util.h>
#include <swebs/sockets.h>
int initTLS() {
@@ -34,12 +35,20 @@ int initTLS() {
Listener *createListener(SocketType type, unsigned short port,
int backlog, ...) {
int shmid;
Listener *ret = malloc(sizeof(Listener));
va_list ap;
if (ret == NULL)
shmid = smalloc(sizeof(Listener));
if (shmid < 0)
return NULL;
ret = saddr(shmid);
if (ret == NULL) {
sdestroy(shmid);
return NULL;
}
ret->type = type;
ret->fd = socket(AF_INET, SOCK_STREAM, 0);
ret->shmid = shmid;
if (ret->fd < 0) {
free(ret);
return NULL;
@@ -89,22 +98,17 @@ Listener *createListener(SocketType type, unsigned short port,
return ret;
error:
close(ret->fd);
free(ret);
sfree(ret);
sdestroy(shmid);
return NULL;
}
Stream *acceptStream(Listener *listener, int flags) {
Stream *createStream(Listener *listener, int flags, int fd) {
Stream *ret = malloc(sizeof(Stream));
if (ret == NULL)
return NULL;
ret->type = listener->type;
ret->fd = accept(listener->fd, (struct sockaddr *) &listener->addr,
&listener->addrlen);
if (ret->fd < 0) {
free(ret);
return NULL;
}
ret->fd = fd;
{
int oldflags = fcntl(ret->fd, F_GETFL);
@@ -140,13 +144,23 @@ error:
return NULL;
}
Stream *acceptStream(Listener *listener, int flags) {
int fd;
fd = accept(listener->fd, (struct sockaddr *) &listener->addr,
&listener->addrlen);
return createStream(listener, flags, fd);
}
void freeListener(Listener *listener) {
int shmid;
if (listener->type == TLS) {
gnutls_certificate_free_credentials(listener->creds);
gnutls_priority_deinit(listener->priority);
}
close(listener->fd);
free(listener);
shmid = listener->shmid;
sfree(listener);
sdestroy(shmid);
}
void freeStream(Stream *stream) {

View File

@@ -19,25 +19,10 @@
#define HAVE_RUNNER
#include <sys/socket.h>
#include <swebs/sockets.h>
#include <swebs/sitefile.h>
#include <swebs/connections.h>
typedef struct {
Sitefile *site;
int pendingid;
/* int *pending */
/*
* 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. notify is an fd
* */
int id;
} RunnerArgs;
/* my least favourite anti pattern */
void *runServer(RunnerArgs *args);
void runServer(int connfd, Sitefile *site, Listener *listener,
int *pending, int id);
#endif

View File

@@ -27,6 +27,7 @@
typedef struct {
SocketType type;
int fd;
int shmid;
struct sockaddr_in addr;
socklen_t addrlen;
gnutls_certificate_credentials_t creds;
@@ -46,6 +47,7 @@ Listener *createListener(SocketType type, uint16_t port, int backlog, ...);
* tcp: (void)
* tls: (char *keyfile, char *certfile, char *ocspfile)
* */
Stream *createStream(Listener *listener, int flags, int fd);
Stream *acceptStream(Listener *listener, int flags);
/* returns 1 on error, accepts fcntl flags */

View File

@@ -32,4 +32,7 @@ int createLog(char *msg);
int istrcmp(char *s1, char *s2);
/* case insensitive strcmp */
RequestType getType(char *str);
void sendFd(int fd, int dest);
int recvFd(int source);
#endif

View File

@@ -22,6 +22,7 @@
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/socket.h>
#include <swebs/util.h>
#include <swebs/types.h>
@@ -39,7 +40,11 @@ int smalloc(size_t size) {
}
void *saddr(int id) {
return shmat(id, NULL, 0);
void *addr;
addr = shmat(id, NULL, 0);
if (addr == (void *) -1)
return NULL;
return addr;
}
void sfree(void *addr) {
@@ -82,35 +87,59 @@ int istrcmp(char *s1, char *s2) {
}
RequestType getType(char *str) {
unsigned long type;
int i;
if (strlen(str) >= 8)
return INVALID;
type = 0;
for (i = 0; str[i]; i++) {
type <<= 8;
type |= str[i];
}
switch (type) {
case 0x474554l:
return GET;
case 0x504f5354l:
return POST;
case 0x505554l:
return PUT;
case 0x48454144l:
return HEAD;
case 0x44454c455445l:
return DELETE;
case 0x5041544348l:
return PATCH;
case 0x4f5054494f4e53l:
return OPTIONS;
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.
* */
if (strcmp(str, "GET") == 0)
return GET;
if (strcmp(str, "POST") == 0)
return POST;
if (strcmp(str, "PUT") == 0)
return PUT;
if (strcmp(str, "HEAD") == 0)
return HEAD;
if (strcmp(str, "DELETE") == 0)
return DELETE;
if (strcmp(str, "PATCH") == 0)
return PATCH;
if (strcmp(str, "OPTIONS") == 0)
return OPTIONS;
return INVALID;
}
void sendFd(int fd, int dest) {
struct msghdr msg;
struct cmsghdr *cmsg;
char iobuf[1];
struct iovec io;
union {
char buf[CMSG_SPACE(sizeof(fd))];
struct cmsghdr align;
} u;
memset(&msg, 0, sizeof(msg));
io.iov_base = iobuf;
io.iov_len = sizeof(iobuf);
msg.msg_iov = &io;
msg.msg_iovlen = 1;
msg.msg_control = u.buf;
msg.msg_controllen = sizeof(u.buf);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
sendmsg(dest, &msg, 0);
}
int recvFd(int source) {
struct msghdr msg;
struct cmsghdr *cmsg;
char cmsgbuf[CMSG_SPACE(sizeof(int))];
unsigned char *data;
int ret;
memset(&msg, 0, sizeof(msg));
msg.msg_control = cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
recvmsg(source, &msg, 0);
cmsg = CMSG_FIRSTHDR(&msg);
data = CMSG_DATA(cmsg);
memcpy(&ret, data, sizeof(ret));
return ret;
}