Compare commits
11 Commits
6b2e112f3b
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ffd4ee86cf | ||
|
|
47f09c3ce2 | ||
|
|
dce50642a0 | ||
|
|
8c6b4b9f76 | ||
|
|
3cb29c6b76 | ||
|
|
df9484283e | ||
|
|
92eda6f326 | ||
|
|
06c6faed73 | ||
|
|
4630000b9f | ||
|
|
52df580ccb | ||
|
|
36d2fefa3b |
28
.github/workflows/docker.yml
vendored
28
.github/workflows/docker.yml
vendored
@@ -1,28 +0,0 @@
|
|||||||
name: Docker Hub
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ master ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Set login
|
|
||||||
uses: azure/docker-login@v1
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
|
||||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
|
||||||
|
|
||||||
- name: Build image
|
|
||||||
working-directory: ${{github.workspace}}
|
|
||||||
run: ./build.sh
|
|
||||||
|
|
||||||
- name: Push image
|
|
||||||
run: docker push natechoe/swebs
|
|
||||||
12
Dockerfile
12
Dockerfile
@@ -1,16 +1,8 @@
|
|||||||
FROM debian:stable-slim AS build
|
FROM debian:stable-slim AS build
|
||||||
RUN apt-get update -y && apt-get upgrade -y && apt-get install -y libgnutls28-dev libgnutls30 gcc make pkg-config
|
RUN apt-get update -y && apt-get upgrade -y && apt-get install -y libgnutls28-dev libgnutls30 gcc make pkg-config
|
||||||
|
|
||||||
COPY . /swebs
|
COPY . /swebs
|
||||||
WORKDIR /swebs
|
WORKDIR /swebs
|
||||||
RUN make
|
RUN make && make install
|
||||||
|
|
||||||
FROM debian:stable-slim AS run
|
|
||||||
RUN apt-get update -y && apt-get upgrade -y && apt-get install -y libgnutls28-dev libgnutls30
|
|
||||||
|
|
||||||
COPY --from=build /swebs/build/swebs /usr/sbin/swebs
|
|
||||||
RUN mkdir /usr/include/swebs
|
|
||||||
COPY --from=build /swebs/src/swebs /usr/include/swebs/
|
|
||||||
|
|
||||||
RUN useradd -M swebs
|
|
||||||
|
|
||||||
ENTRYPOINT [ "swebs", "-s", "/site/sitefile" ]
|
ENTRYPOINT [ "swebs", "-s", "/site/sitefile" ]
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -2,7 +2,7 @@ SRC = $(wildcard src/*.c)
|
|||||||
OBJ = $(subst .c,.o,$(subst src,work,$(SRC)))
|
OBJ = $(subst .c,.o,$(subst src,work,$(SRC)))
|
||||||
LIBS = gnutls
|
LIBS = gnutls
|
||||||
LDFLAGS = -pie -lrt -ldl $(shell pkg-config --libs $(LIBS))
|
LDFLAGS = -pie -lrt -ldl $(shell pkg-config --libs $(LIBS))
|
||||||
CFLAGS := -O2 -pipe -Wall -Wpedantic -Wshadow -ansi -D_XOPEN_SOURCE=500
|
CFLAGS := -O2 -pipe -Wall -Wpedantic -Wshadow -ansi -D_XOPEN_SOURCE=500 -ggdb
|
||||||
CFLAGS += -Isrc/ -fpie -D_POSIX_C_SOURCE=200809L $(shell pkg-config --cflags $(LIBS))
|
CFLAGS += -Isrc/ -fpie -D_POSIX_C_SOURCE=200809L $(shell pkg-config --cflags $(LIBS))
|
||||||
INSTALLDIR := /usr/sbin
|
INSTALLDIR := /usr/sbin
|
||||||
HEADERDIR := /usr/include/
|
HEADERDIR := /usr/include/
|
||||||
|
|||||||
@@ -9,6 +9,15 @@ sitefiles consist of commands, which are of the form
|
|||||||
|
|
||||||
sitefiles also allow comments with #
|
sitefiles also allow comments with #
|
||||||
|
|
||||||
|
Actions and arguments are made of tokens. A token is some text surrounded by
|
||||||
|
whitespace, where backslashes are always escaped literally.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
`token` -> `"token"`
|
||||||
|
`token\ with\ escapes` -> 'token with escapes'
|
||||||
|
`token\\with\\backslashes` -> 'token\with\backslashes'
|
||||||
|
|
||||||
# Part 2: Commands
|
# Part 2: Commands
|
||||||
|
|
||||||
* ```set [variable] [value]``` - sets some local variable for the following
|
* ```set [variable] [value]``` - sets some local variable for the following
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<p1>I have nothing to blog about</p1>
|
<p>I have nothing to blog about</p>
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ key domain.key 8001
|
|||||||
cert domain.crt 8001
|
cert domain.crt 8001
|
||||||
# Port 8001 can have TLS, but it's self signed and very bad.
|
# Port 8001 can have TLS, but it's self signed and very bad.
|
||||||
|
|
||||||
timeout 200000 8000
|
timeout 20000 8000
|
||||||
timeout 200000 8001
|
timeout 20000 8001
|
||||||
# Set these values for the ports
|
# Set these values for the ports
|
||||||
|
|
||||||
set port 8000
|
set port 8000
|
||||||
@@ -22,7 +22,7 @@ set type text/html
|
|||||||
# The following pages are html
|
# The following pages are html
|
||||||
read / site/index.html
|
read / site/index.html
|
||||||
# The path / should be read from site/index.html
|
# The path / should be read from site/index.html
|
||||||
read /hello site/hello.html
|
read /hello site/hello.html
|
||||||
# The path /hello should be read from site/hello.html
|
# The path /hello should be read from site/hello.html
|
||||||
|
|
||||||
throw /blog/forbidden 403
|
throw /blog/forbidden 403
|
||||||
|
|||||||
@@ -49,8 +49,9 @@ static struct sockaddr_un addr;
|
|||||||
/* We want to be able to handle a signal at any time, so some global variables
|
/* We want to be able to handle a signal at any time, so some global variables
|
||||||
* are needed. */
|
* are needed. */
|
||||||
static const int signals[] = {
|
static const int signals[] = {
|
||||||
SIGPIPE, SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE,
|
SIGPIPE, SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS,
|
||||||
SIGKILL, SIGSEGV, SIGTERM, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ,
|
SIGFPE, SIGKILL, SIGSEGV, SIGTERM, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU,
|
||||||
|
SIGXFSZ,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void exitClean(int signal) {
|
static void exitClean(int signal) {
|
||||||
@@ -196,6 +197,7 @@ int main(int argc, char **argv) {
|
|||||||
if (pending[j] < pending[lowestproc])
|
if (pending[j] < pending[lowestproc])
|
||||||
lowestproc = j;
|
lowestproc = j;
|
||||||
sendFd(fd, runners[lowestproc].fd, &i, sizeof i);
|
sendFd(fd, runners[lowestproc].fd, &i, sizeof i);
|
||||||
|
close(fd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -307,9 +307,42 @@ static int wasasked(char *request, char *type) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sendCertainResponse(Connection *conn, Sitefile *site, int index) {
|
||||||
|
int ret;
|
||||||
|
ret = 0;
|
||||||
|
switch (site->content[index].command) {
|
||||||
|
case READ:
|
||||||
|
ret = readResponse(conn, site->content + index);
|
||||||
|
break;
|
||||||
|
case THROW:
|
||||||
|
ret = sendErrorResponse(conn->stream, site->content[index].arg);
|
||||||
|
break;
|
||||||
|
case LINKED:
|
||||||
|
#if DYNAMIC_LINKED_PAGES
|
||||||
|
if (!site->getResponse) {
|
||||||
|
sendErrorResponse(conn->stream, ERROR_500);
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = linkedResponse(conn, site->getResponse,
|
||||||
|
site->content[index].contenttype);
|
||||||
|
#else
|
||||||
|
/* Unreachable state (if a linked response was in the sitefile,
|
||||||
|
* the parse would've thrown an error) */
|
||||||
|
ret = sendErrorResponse(conn->stream, ERROR_500);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sendErrorResponse(conn->stream, ERROR_500);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
resetConnection(conn);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
int sendResponse(Connection *conn, Sitefile *site) {
|
int sendResponse(Connection *conn, Sitefile *site) {
|
||||||
char *host = NULL;
|
char *host = NULL;
|
||||||
char *accept = NULL;
|
char *accept = "*/*";
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < conn->fieldCount; i++) {
|
for (i = 0; i < conn->fieldCount; i++) {
|
||||||
if (strcmp(conn->fields[i].field, "Host") == 0)
|
if (strcmp(conn->fields[i].field, "Host") == 0)
|
||||||
@@ -317,7 +350,7 @@ int sendResponse(Connection *conn, Sitefile *site) {
|
|||||||
else if (strcmp(conn->fields[i].field, "Accept") == 0)
|
else if (strcmp(conn->fields[i].field, "Accept") == 0)
|
||||||
accept = conn->fields[i].value;
|
accept = conn->fields[i].value;
|
||||||
}
|
}
|
||||||
if (host == NULL || accept == NULL) {
|
if (host == NULL) {
|
||||||
sendErrorResponse(conn->stream, ERROR_400);
|
sendErrorResponse(conn->stream, ERROR_400);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -337,41 +370,8 @@ int sendResponse(Connection *conn, Sitefile *site) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
foundport:
|
foundport:
|
||||||
if (fullmatch(&site->content[i].path, conn->path.data) == 0) {
|
if (fullmatch(&site->content[i].path, conn->path.data) == 0)
|
||||||
switch (site->content[i].command) {
|
return sendCertainResponse(conn, site, i);
|
||||||
case READ:
|
|
||||||
if (readResponse(conn,
|
|
||||||
site->content + i))
|
|
||||||
return 1;
|
|
||||||
break;
|
|
||||||
case THROW:
|
|
||||||
if (sendErrorResponse(conn->stream,
|
|
||||||
site->content[i].arg))
|
|
||||||
return 1;
|
|
||||||
break;
|
|
||||||
case LINKED:
|
|
||||||
#if DYNAMIC_LINKED_PAGES
|
|
||||||
if (!site->getResponse)
|
|
||||||
sendErrorResponse(conn->stream,
|
|
||||||
ERROR_500);
|
|
||||||
else if (linkedResponse(conn,
|
|
||||||
site->getResponse,
|
|
||||||
site->content[i].contenttype))
|
|
||||||
return 1;
|
|
||||||
#else
|
|
||||||
/* Unreachable state (filtered by startup) */
|
|
||||||
sendErrorResponse(conn->stream,
|
|
||||||
ERROR_500);
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
sendErrorResponse(conn->stream,
|
|
||||||
ERROR_500);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
resetConnection(conn);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
sendErrorResponse(conn->stream, ERROR_404);
|
sendErrorResponse(conn->stream, ERROR_404);
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
@@ -128,6 +128,8 @@ int sendErrorResponse(Stream *stream, const char *error) {
|
|||||||
int ret;
|
int ret;
|
||||||
int len = snprintf(NULL, 0, template, error);
|
int len = snprintf(NULL, 0, template, error);
|
||||||
char *response = malloc(len + 1);
|
char *response = malloc(len + 1);
|
||||||
|
if (response == NULL)
|
||||||
|
return 1;
|
||||||
sprintf(response, template, error);
|
sprintf(response, template, error);
|
||||||
ret = sendStringResponse(stream, error, response,
|
ret = sendStringResponse(stream, error, response,
|
||||||
"Content-Type: text/html\r\n", NULL);
|
"Content-Type: text/html\r\n", NULL);
|
||||||
@@ -155,6 +157,7 @@ static int sendKnownPipeValist(Stream *stream, const char *status,
|
|||||||
return totalSent != len;
|
return totalSent != len;
|
||||||
if (resilientSend(stream, buffer, inBuffer))
|
if (resilientSend(stream, buffer, inBuffer))
|
||||||
return 1;
|
return 1;
|
||||||
|
totalSent += inBuffer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,6 +188,8 @@ int sendPipe(Stream *stream, const char *status, int fd, ...) {
|
|||||||
size_t responseLen = 0;
|
size_t responseLen = 0;
|
||||||
char *response = malloc(allocResponse);
|
char *response = malloc(allocResponse);
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
if (response == NULL)
|
||||||
|
goto error;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
if (responseLen >= allocResponse) {
|
if (responseLen >= allocResponse) {
|
||||||
|
|||||||
177
src/runner.c
177
src/runner.c
@@ -31,19 +31,39 @@
|
|||||||
#include <swebs/sitefile.h>
|
#include <swebs/sitefile.h>
|
||||||
#include <swebs/connections.h>
|
#include <swebs/connections.h>
|
||||||
|
|
||||||
void runServer(int connfd, Sitefile *site, volatile int *pending, int id) {
|
typedef struct {
|
||||||
int allocConns = 100;
|
|
||||||
struct pollfd *fds;
|
struct pollfd *fds;
|
||||||
Connection *connections;
|
Connection *conns;
|
||||||
int connCount;
|
int len;
|
||||||
|
int alloc;
|
||||||
|
} ConnList;
|
||||||
|
|
||||||
|
static int createConnList(ConnList *list);
|
||||||
|
static int addConnList(ConnList *list, struct pollfd *fd, Connection *conn);
|
||||||
|
static void removeConnList(ConnList *list, int ind);
|
||||||
|
static void pollConnList(ConnList *list);
|
||||||
|
static void freeConnList(ConnList *list);
|
||||||
|
|
||||||
|
void runServer(int connfd, Sitefile *site, volatile int *pending, int id) {
|
||||||
Context **contexts;
|
Context **contexts;
|
||||||
int i;
|
int i;
|
||||||
|
ConnList conns;
|
||||||
|
|
||||||
connCount = 1;
|
if (createConnList(&conns))
|
||||||
fds = xmalloc(allocConns * sizeof *fds);
|
return;
|
||||||
connections = xmalloc(allocConns * sizeof *connections);
|
|
||||||
fds[0].fd = connfd;
|
{
|
||||||
fds[0].events = POLLIN;
|
struct pollfd newfd;
|
||||||
|
Connection newconn;
|
||||||
|
|
||||||
|
newfd.fd = connfd;
|
||||||
|
newfd.events = POLLIN;
|
||||||
|
|
||||||
|
if (addConnList(&conns, &newfd, &newconn)) {
|
||||||
|
freeConnList(&conns);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
/* connections are 1 indexed because fds[0] is the notify fd. I hate
|
/* connections are 1 indexed because fds[0] is the notify fd. I hate
|
||||||
* that poll() forces us to do these hacks. */
|
* that poll() forces us to do these hacks. */
|
||||||
|
|
||||||
@@ -78,94 +98,115 @@ void runServer(int connfd, Sitefile *site, volatile int *pending, int id) {
|
|||||||
createErrorLog("seteuid() failed", errno);
|
createErrorLog("seteuid() failed", errno);
|
||||||
root = getpwnam("root");
|
root = getpwnam("root");
|
||||||
if (root != NULL) {
|
if (root != NULL) {
|
||||||
/* I don't know why this if statement could be false but we have it
|
/* I don't know why this if statement could be false but we have
|
||||||
* just in case. */
|
* it just in case. */
|
||||||
if (geteuid() == root->pw_uid)
|
if (geteuid() == root->pw_uid)
|
||||||
createLog("swebs probably should not be run as root");
|
createLog("swebs probably should not be run as root");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
poll(fds, connCount, -1);
|
pollConnList(&conns);
|
||||||
|
|
||||||
createFormatLog("poll() finished with %d connections", connCount);
|
createFormatLog("poll() finished with %d connections",
|
||||||
|
conns.len);
|
||||||
|
|
||||||
for (i = 1; i < connCount; i++) {
|
for (i = 1; i < conns.len; i++) {
|
||||||
if (fds[i].revents & POLLIN) {
|
if (conns.fds[i].revents & POLLIN) {
|
||||||
createFormatLog("Connection %d has data", i);
|
createFormatLog("Connection %d has data", i);
|
||||||
if (updateConnection(connections + i, site))
|
if (updateConnection(conns.conns + i, site)) {
|
||||||
goto remove;
|
freeConnection(conns.conns + i);
|
||||||
}
|
removeConnList(&conns, i);
|
||||||
continue;
|
--i;
|
||||||
remove:
|
}
|
||||||
{
|
|
||||||
int remove, replace;
|
|
||||||
remove = i;
|
|
||||||
replace = connCount - 1;
|
|
||||||
freeConnection(connections + remove);
|
|
||||||
|
|
||||||
memcpy(fds + remove, fds + replace,
|
|
||||||
sizeof(struct pollfd));
|
|
||||||
memcpy(connections + remove,
|
|
||||||
connections + replace,
|
|
||||||
sizeof(struct pollfd));
|
|
||||||
|
|
||||||
--pending[id];
|
|
||||||
|
|
||||||
--i;
|
|
||||||
--connCount;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fds[0].revents & POLLIN) {
|
if (conns.fds[0].revents & POLLIN) {
|
||||||
Stream *newstream;
|
Stream *newstream;
|
||||||
int newfd;
|
Connection newconn;
|
||||||
int portind;
|
int portind;
|
||||||
|
struct pollfd newfd;
|
||||||
|
|
||||||
createLog("Main fd has data");
|
createLog("Main fd has data");
|
||||||
newfd = recvFd(connfd, &portind, sizeof portind);
|
newfd.fd = recvFd(connfd, &portind, sizeof portind);
|
||||||
if (newfd < 0) {
|
if (newfd.fd < 0) {
|
||||||
createLog("Message received that included an invalid fd, quitting");
|
createLog("Message received that included an invalid fd, quitting");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
newfd.events = POLLIN;
|
||||||
|
|
||||||
newstream = createStream(contexts[portind], O_NONBLOCK, newfd);
|
newstream = createStream(contexts[portind],
|
||||||
|
O_NONBLOCK, newfd.fd);
|
||||||
if (newstream == NULL) {
|
if (newstream == NULL) {
|
||||||
createLog(
|
createLog(
|
||||||
"Stream couldn't be created from file descriptor");
|
"Stream couldn't be created from file descriptor");
|
||||||
shutdown(newfd, SHUT_RDWR);
|
shutdown(newfd.fd, SHUT_RDWR);
|
||||||
close(newfd);
|
close(newfd.fd);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connCount >= allocConns) {
|
if (newConnection(newstream, &newconn, portind)) {
|
||||||
struct pollfd *newfds;
|
|
||||||
Connection *newconns;
|
|
||||||
allocConns *= 2;
|
|
||||||
newfds = realloc(fds,
|
|
||||||
sizeof(struct pollfd) * allocConns);
|
|
||||||
if (newfds == NULL) {
|
|
||||||
allocConns /= 2;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
fds = newfds;
|
|
||||||
|
|
||||||
newconns = realloc(connections,
|
|
||||||
sizeof(Connection) * allocConns);
|
|
||||||
if (newconns == NULL) {
|
|
||||||
allocConns /= 2;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
connections = newconns;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newConnection(newstream, connections + connCount, portind)) {
|
|
||||||
createLog("Couldn't initialize connection from stream");
|
createLog("Couldn't initialize connection from stream");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
fds[connCount].fd = newfd;
|
|
||||||
fds[connCount].events = POLLIN;
|
if (addConnList(&conns, &newfd, &newconn)) {
|
||||||
connCount++;
|
freeConnection(&newconn);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
pending[id]++;
|
pending[id]++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int createConnList(ConnList *list) {
|
||||||
|
list->alloc = 100;
|
||||||
|
list->fds = xmalloc(list->alloc * sizeof *list->fds);
|
||||||
|
list->conns = xmalloc(list->alloc * sizeof *list->conns);
|
||||||
|
list->len = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int addConnList(ConnList *list, struct pollfd *fd, Connection *conn) {
|
||||||
|
if (list->len >= list->alloc) {
|
||||||
|
int newalloc;
|
||||||
|
struct pollfd *newfds;
|
||||||
|
Connection *newconns;
|
||||||
|
newalloc = list->alloc * 2;
|
||||||
|
newfds = realloc(list->fds, newalloc * sizeof *list->fds);
|
||||||
|
if (newfds == NULL)
|
||||||
|
return 1;
|
||||||
|
newconns = realloc(list->conns, newalloc * sizeof *list->conns);
|
||||||
|
if (newconns == NULL)
|
||||||
|
return 1;
|
||||||
|
list->alloc = newalloc;
|
||||||
|
list->fds = newfds;
|
||||||
|
list->conns = newconns;
|
||||||
|
}
|
||||||
|
memcpy(list->fds + list->len, fd, sizeof *fd);
|
||||||
|
memcpy(list->conns + list->len, conn, sizeof *conn);
|
||||||
|
++list->len;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void removeConnList(ConnList *list, int ind) {
|
||||||
|
const int replace = list->len - 1;
|
||||||
|
|
||||||
|
memcpy(list->fds + ind, list->fds + replace, sizeof *list->fds);
|
||||||
|
memcpy(list->conns + ind, list->conns + replace, sizeof *list->conns);
|
||||||
|
|
||||||
|
--list->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pollConnList(ConnList *list) {
|
||||||
|
poll(list->fds, list->len, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void freeConnList(ConnList *list) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < list->len; ++i)
|
||||||
|
freeConnection(list->conns + i);
|
||||||
|
free(list->fds);
|
||||||
|
free(list->conns);
|
||||||
|
}
|
||||||
|
|||||||
652
src/sitefile.c
652
src/sitefile.c
@@ -33,145 +33,119 @@
|
|||||||
* good.
|
* good.
|
||||||
* */
|
* */
|
||||||
|
|
||||||
|
#define CFLAGS (REG_EXTENDED | REG_ICASE)
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SUCCESS,
|
ARG,
|
||||||
LINE_END,
|
LINE_END,
|
||||||
FILE_END,
|
FILE_END,
|
||||||
ERROR
|
TOKEN_ERROR
|
||||||
} ReturnCode;
|
} TokenType;
|
||||||
/* this isn't ideal, but it's necessary to avoid namespace collisions. */
|
|
||||||
|
|
||||||
static void freeTokens(int argc, char **argv) {
|
typedef struct {
|
||||||
|
TokenType type;
|
||||||
|
char *data;
|
||||||
|
} Token;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
NORMAL,
|
||||||
|
PAST_END,
|
||||||
|
COMMAND_ERROR
|
||||||
|
} CommandType;
|
||||||
|
|
||||||
|
static void freecommand(int argc, char **argv) {
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < argc; i++)
|
for (i = 0; i < argc; i++)
|
||||||
free(argv[i]);
|
free(argv[i]);
|
||||||
free(argv);
|
free(argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ReturnCode getToken(FILE *file, char **ret) {
|
static void gettoken(FILE *file, Token *ret) {
|
||||||
typedef enum {
|
int c;
|
||||||
QUOTED,
|
char *data;
|
||||||
NONQUOTED
|
|
||||||
} TokenType;
|
|
||||||
TokenType type;
|
|
||||||
size_t allocatedLen = 50;
|
|
||||||
size_t len;
|
size_t len;
|
||||||
|
size_t alloc;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int c = fgetc(file);
|
c = fgetc(file);
|
||||||
if (c == '\n')
|
switch (c) {
|
||||||
return LINE_END;
|
case '#':
|
||||||
if (c == EOF)
|
while (c != '\n' && c != EOF)
|
||||||
return FILE_END;
|
|
||||||
if (c == '#') {
|
|
||||||
while (c != '\n')
|
|
||||||
c = fgetc(file);
|
c = fgetc(file);
|
||||||
return LINE_END;
|
case '\n':
|
||||||
}
|
ret->type = LINE_END;
|
||||||
if (!isspace(c)) {
|
return;
|
||||||
if (c == '"')
|
case EOF:
|
||||||
type = QUOTED;
|
ret->type = FILE_END;
|
||||||
else {
|
return;
|
||||||
type = NONQUOTED;
|
case ' ': case '\t':
|
||||||
ungetc(c, file);
|
continue;
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
ret->type = ARG;
|
||||||
|
ungetc(c, file);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
*ret = malloc(allocatedLen);
|
alloc = 20;
|
||||||
|
data = xmalloc(alloc);
|
||||||
for (len = 0;; len++) {
|
for (len = 0;; ++len) {
|
||||||
int c;
|
if (len >= alloc) {
|
||||||
if (len >= allocatedLen) {
|
alloc *= 2;
|
||||||
char *newret;
|
data = xrealloc(data, alloc);
|
||||||
allocatedLen *= 2;
|
|
||||||
newret = realloc(*ret, allocatedLen);
|
|
||||||
if (newret == NULL)
|
|
||||||
goto error;
|
|
||||||
*ret = newret;
|
|
||||||
}
|
}
|
||||||
c = fgetc(file);
|
c = fgetc(file);
|
||||||
switch (type) {
|
if (isspace(c) || c == EOF) {
|
||||||
case QUOTED:
|
ungetc(c, file);
|
||||||
if (c == '"')
|
data[len] = '\0';
|
||||||
goto gotToken;
|
ret->type = ARG;
|
||||||
break;
|
ret->data = data;
|
||||||
case NONQUOTED:
|
return;
|
||||||
if (isspace(c)) {
|
|
||||||
ungetc(c, file);
|
|
||||||
goto gotToken;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '\\':
|
case '\\':
|
||||||
c = fgetc(file);
|
c = fgetc(file);
|
||||||
if (c == EOF)
|
if (c == EOF) {
|
||||||
goto error;
|
ret->type = TOKEN_ERROR;
|
||||||
break;
|
return;
|
||||||
case EOF:
|
}
|
||||||
if (type == NONQUOTED)
|
default:
|
||||||
goto gotToken;
|
data[len] = c;
|
||||||
goto error;
|
|
||||||
}
|
}
|
||||||
(*ret)[len] = c;
|
|
||||||
}
|
}
|
||||||
gotToken:
|
|
||||||
(*ret)[len] = '\0';
|
|
||||||
return SUCCESS;
|
|
||||||
error:
|
|
||||||
free(*ret);
|
|
||||||
return ERROR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static ReturnCode getCommand(FILE *file, int *argcret, char ***argvret) {
|
static CommandType getcommand(FILE *file, int *argcret, char ***argvret) {
|
||||||
/* THIS FUNCTION WILL NOT RETURN LINE_END */
|
int argc, argalloc;
|
||||||
int argc;
|
|
||||||
char **argv;
|
char **argv;
|
||||||
int allocatedTokens;
|
argalloc = 5;
|
||||||
if (feof(file))
|
argv = xmalloc(argalloc * sizeof *argv);
|
||||||
return FILE_END;
|
|
||||||
argc = 0;
|
|
||||||
allocatedTokens = 5;
|
|
||||||
argv = malloc(allocatedTokens * sizeof(*argv));
|
|
||||||
for (;;) {
|
|
||||||
ReturnCode code;
|
|
||||||
if (argc >= allocatedTokens) {
|
|
||||||
char **newargv;
|
|
||||||
allocatedTokens *= 2;
|
|
||||||
newargv = realloc(argv,
|
|
||||||
allocatedTokens * sizeof(char *));
|
|
||||||
if (newargv == NULL)
|
|
||||||
goto error;
|
|
||||||
argv = newargv;
|
|
||||||
}
|
|
||||||
code = getToken(file, argv + argc);
|
|
||||||
|
|
||||||
switch (code) {
|
for (argc = 0;; ++argc) {
|
||||||
case ERROR:
|
Token token;
|
||||||
goto error;
|
if (argc >= argalloc) {
|
||||||
case LINE_END:
|
argalloc *= 2;
|
||||||
if (argc == 0)
|
argv = xrealloc(argv, argalloc * sizeof *argv);
|
||||||
continue;
|
}
|
||||||
/* We allow empty lines */
|
gettoken(file, &token);
|
||||||
/* fallthrough */
|
switch (token.type) {
|
||||||
case FILE_END:
|
case FILE_END:
|
||||||
if (argc == 0) {
|
if (argc == 0)
|
||||||
free(argv);
|
return PAST_END;
|
||||||
return FILE_END;
|
goto gotcommand;
|
||||||
}
|
case LINE_END:
|
||||||
*argcret = argc;
|
if (argc == 0)
|
||||||
*argvret = argv;
|
return getcommand(file, argcret, argvret);
|
||||||
return SUCCESS;
|
goto gotcommand;
|
||||||
case SUCCESS:
|
gotcommand:
|
||||||
argc++;
|
*argcret = argc;
|
||||||
break;
|
*argvret = argv;
|
||||||
|
return NORMAL;
|
||||||
|
case ARG:
|
||||||
|
argv[argc] = token.data;
|
||||||
|
break;
|
||||||
|
case TOKEN_ERROR:
|
||||||
|
return COMMAND_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
error:
|
|
||||||
freeTokens(argc, argv);
|
|
||||||
return ERROR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *getport(char *data, unsigned short *ret) {
|
static char *getport(char *data, unsigned short *ret) {
|
||||||
@@ -213,27 +187,223 @@ static int getports(unsigned short **ports, int *portcount, char *data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Sitefile *parseSitefile(char *path) {
|
typedef struct {
|
||||||
FILE *file;
|
RequestType respondto;
|
||||||
RequestType respondto = GET;
|
char *host;
|
||||||
const int cflags = REG_EXTENDED | REG_ICASE;
|
|
||||||
char *host = NULL;
|
|
||||||
int argc;
|
|
||||||
char **argv;
|
|
||||||
Sitefile *ret;
|
|
||||||
unsigned short *ports;
|
unsigned short *ports;
|
||||||
int portcount;
|
int portcount;
|
||||||
char *contenttype;
|
char *contenttype;
|
||||||
|
} LocalVars;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DATA_CHANGE,
|
||||||
|
SITE_SPEC,
|
||||||
|
COMMAND_RET_ERROR
|
||||||
|
} CommandReturn;
|
||||||
|
|
||||||
|
static CommandReturn localvar(LocalVars *vars, Sitefile *sitefile,
|
||||||
|
int argc, char **argv) {
|
||||||
|
if (argc < 3)
|
||||||
|
return COMMAND_RET_ERROR;
|
||||||
|
if (strcmp(argv[1], "respondto") == 0) {
|
||||||
|
if ((vars->respondto = getType(argv[2])) == INVALID)
|
||||||
|
return COMMAND_RET_ERROR;
|
||||||
|
return DATA_CHANGE;
|
||||||
|
}
|
||||||
|
if (strcmp(argv[1], "host") == 0) {
|
||||||
|
free(vars->host);
|
||||||
|
vars->host = xstrdup(argv[2]);
|
||||||
|
return DATA_CHANGE;
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[1], "port") == 0) {
|
||||||
|
free(vars->ports);
|
||||||
|
if (getports(&vars->ports, &vars->portcount, argv[2])) {
|
||||||
|
fprintf(stderr, "Invalid port list %s\n", argv[2]);
|
||||||
|
return COMMAND_RET_ERROR;
|
||||||
|
}
|
||||||
|
return DATA_CHANGE;
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[1], "type") == 0) {
|
||||||
|
free(vars->contenttype);
|
||||||
|
vars->contenttype = strdup(argv[2]);
|
||||||
|
return DATA_CHANGE;
|
||||||
|
}
|
||||||
|
return COMMAND_RET_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CommandReturn globalvar(LocalVars *vars, Sitefile *sitefile,
|
||||||
|
int argc, char **argv) {
|
||||||
|
if (argc < 3)
|
||||||
|
return COMMAND_RET_ERROR;
|
||||||
|
if (strcmp(argv[1], "library") == 0) {
|
||||||
|
#if DYNAMIC_LINKED_PAGES
|
||||||
|
sitefile->getResponse = loadGetResponse(argv[2]);
|
||||||
|
return DATA_CHANGE;
|
||||||
|
#else
|
||||||
|
fputs("This version of swebs has no dynamic page support\n",
|
||||||
|
stderr);
|
||||||
|
return COMMAND_RET_ERROR;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return COMMAND_RET_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CommandReturn declareport(LocalVars *vars, Sitefile *sitefile,
|
||||||
|
int argc, char **argv) {
|
||||||
|
Port newport;
|
||||||
|
int i;
|
||||||
|
if (argc < 3) {
|
||||||
|
fputs("Usage: declare [transport] [port]\n", stderr);
|
||||||
|
return COMMAND_RET_ERROR;
|
||||||
|
}
|
||||||
|
newport.num = atoi(argv[2]);
|
||||||
|
|
||||||
|
for (i = 0; i < sitefile->portcount; ++i) {
|
||||||
|
if (sitefile->ports[i].num == newport.num) {
|
||||||
|
fprintf(stderr, "Port %hu declared multiple times\n",
|
||||||
|
newport.num);
|
||||||
|
return COMMAND_RET_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]);
|
||||||
|
return COMMAND_RET_ERROR;
|
||||||
|
}
|
||||||
|
newport.timeout = 2000;
|
||||||
|
newport.key = newport.cert = NULL;
|
||||||
|
if (sitefile->portcount >= sitefile->portalloc) {
|
||||||
|
sitefile->portalloc *= 2;
|
||||||
|
sitefile->ports = xrealloc(sitefile->ports,
|
||||||
|
sitefile->portalloc * sizeof *sitefile->ports);
|
||||||
|
}
|
||||||
|
memcpy(sitefile->ports + sitefile->portcount, &newport,
|
||||||
|
sizeof newport);
|
||||||
|
++sitefile->portcount;
|
||||||
|
return DATA_CHANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CommandReturn portvar(LocalVars *vars, Sitefile *sitefile,
|
||||||
|
int argc, char **argv) {
|
||||||
|
#define PORT_ATTRIBUTE(name, func) \
|
||||||
|
if (strcmp(argv[0], #name) == 0) { \
|
||||||
|
int i; \
|
||||||
|
unsigned short port; \
|
||||||
|
if (argc < 3) { \
|
||||||
|
fputs("Usage: " #name " [" #name "] [port]\n", \
|
||||||
|
stderr); \
|
||||||
|
return COMMAND_RET_ERROR; \
|
||||||
|
} \
|
||||||
|
port = atoi(argv[2]); \
|
||||||
|
for (i = 0; i < sitefile->portcount; ++i) \
|
||||||
|
if (sitefile->ports[i].num == port) \
|
||||||
|
sitefile->ports[i].name = func(argv[1]); \
|
||||||
|
return DATA_CHANGE; \
|
||||||
|
}
|
||||||
|
PORT_ATTRIBUTE(key, xstrdup)
|
||||||
|
PORT_ATTRIBUTE(cert, xstrdup)
|
||||||
|
PORT_ATTRIBUTE(timeout, atoi)
|
||||||
|
#undef PORT_ATTRIBUTE
|
||||||
|
return COMMAND_RET_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int expandsitefile(Sitefile *sitefile, char *regex) {
|
||||||
|
if (sitefile->size >= sitefile->alloc) {
|
||||||
|
SiteCommand *newcontent;
|
||||||
|
sitefile->alloc *= 2;
|
||||||
|
newcontent = xrealloc(sitefile->content, sitefile->alloc *
|
||||||
|
sizeof *newcontent);
|
||||||
|
sitefile->content = newcontent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return regcomp(&sitefile->content[sitefile->size].path, regex, CFLAGS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *getcodestring(const char *str) {
|
||||||
|
return getCode(atoi(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
static CommandReturn defsitespec(LocalVars *vars, Sitefile *sitefile,
|
||||||
|
int argc, char **argv) {
|
||||||
|
const struct {
|
||||||
|
char *command;
|
||||||
|
char *(*getarg)(const char *);
|
||||||
|
Command type;
|
||||||
|
} sitespecs[] = {
|
||||||
|
{"read", strdup, READ},
|
||||||
|
{"throw", getcodestring, THROW},
|
||||||
|
};
|
||||||
|
int i;
|
||||||
|
if (argc < 3)
|
||||||
|
return COMMAND_RET_ERROR;
|
||||||
|
expandsitefile(sitefile, argv[1]);
|
||||||
|
for (i = 0; i < LEN(sitespecs); ++i) {
|
||||||
|
if (strcmp(argv[0], sitespecs[i].command) == 0) {
|
||||||
|
sitefile->content[sitefile->size].arg =
|
||||||
|
sitespecs[i].getarg(argv[2]);
|
||||||
|
if (sitefile->content[sitefile->size].arg == NULL)
|
||||||
|
return COMMAND_RET_ERROR;
|
||||||
|
sitefile->content[sitefile->size].command =
|
||||||
|
sitespecs[i].type;
|
||||||
|
return SITE_SPEC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return COMMAND_RET_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CommandReturn linkedsitespec(LocalVars *vars, Sitefile *sitefile,
|
||||||
|
int argc, char **argv) {
|
||||||
|
#if DYNAMIC_LINKED_PAGES
|
||||||
|
if (argc < 2)
|
||||||
|
return COMMAND_RET_ERROR;
|
||||||
|
expandsitefile(sitefile, argv[1]);
|
||||||
|
sitefile->content[sitefile->size].command = LINKED;
|
||||||
|
return SITE_SPEC;
|
||||||
|
#else
|
||||||
|
fputs("This version of swebs doesn't have linked page support", stderr);
|
||||||
|
return COMMAND_RET_ERROR;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
Sitefile *parseSitefile(char *path) {
|
||||||
|
FILE *file;
|
||||||
|
int argc;
|
||||||
|
char **argv;
|
||||||
|
Sitefile *ret;
|
||||||
|
const struct {
|
||||||
|
char *name;
|
||||||
|
CommandReturn (*updatesitefile)(LocalVars *vars,
|
||||||
|
Sitefile *sitefile,
|
||||||
|
int argc, char **argv);
|
||||||
|
} commandspec[] = {
|
||||||
|
{"set", localvar},
|
||||||
|
{"define", globalvar},
|
||||||
|
{"read", defsitespec},
|
||||||
|
{"throw", defsitespec},
|
||||||
|
{"linked", linkedsitespec},
|
||||||
|
{"declare", declareport},
|
||||||
|
{"key", portvar},
|
||||||
|
{"cert", portvar},
|
||||||
|
{"timeout", portvar},
|
||||||
|
};
|
||||||
|
LocalVars vars;
|
||||||
|
|
||||||
file = fopen(path, "r");
|
file = fopen(path, "r");
|
||||||
if (file == NULL)
|
if (file == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
vars.respondto = GET;
|
||||||
|
vars.host = xstrdup(".*");
|
||||||
|
vars.ports = xmalloc(sizeof *vars.ports);
|
||||||
|
vars.ports[0] = 80;
|
||||||
|
vars.portcount = 1;
|
||||||
|
vars.contenttype = xstrdup("text/html");
|
||||||
|
|
||||||
ret = xmalloc(sizeof *ret);
|
ret = xmalloc(sizeof *ret);
|
||||||
|
|
||||||
ports = malloc(sizeof *ports);
|
|
||||||
ports[0] = 80;
|
|
||||||
portcount = 1;
|
|
||||||
|
|
||||||
ret->size = 0;
|
ret->size = 0;
|
||||||
ret->alloc = 50;
|
ret->alloc = 50;
|
||||||
ret->content = xmalloc(ret->alloc * sizeof *ret->content);
|
ret->content = xmalloc(ret->alloc * sizeof *ret->content);
|
||||||
@@ -244,206 +414,88 @@ Sitefile *parseSitefile(char *path) {
|
|||||||
ret->getResponse = NULL;
|
ret->getResponse = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
contenttype = xstrdup("text/html");
|
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
ReturnCode status = getCommand(file, &argc, &argv);
|
int i;
|
||||||
switch (status) {
|
CommandType commandtype;
|
||||||
int i;
|
nextcommand:
|
||||||
case FILE_END:
|
commandtype = getcommand(file, &argc, &argv);
|
||||||
free(ports);
|
switch (commandtype) {
|
||||||
for (i = 0; i < ret->portcount; ++i) {
|
case PAST_END:
|
||||||
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:
|
|
||||||
goto nterror;
|
|
||||||
case SUCCESS:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (strcmp(argv[0], "set") == 0) {
|
|
||||||
if (argc < 3)
|
|
||||||
goto error;
|
|
||||||
if (strcmp(argv[1], "respondto") == 0) {
|
|
||||||
respondto = getType(argv[2]);
|
|
||||||
if (respondto == INVALID)
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
else if (strcmp(argv[1], "host") == 0) {
|
|
||||||
free(host);
|
|
||||||
host = xstrdup(argv[2]);
|
|
||||||
}
|
|
||||||
else if (strcmp(argv[1], "port") == 0) {
|
|
||||||
free(ports);
|
|
||||||
if (getports(&ports, &portcount, argv[2])) {
|
|
||||||
fprintf(stderr, "Invalid port list %s\n",
|
|
||||||
argv[2]);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (strcmp(argv[1], "type") == 0) {
|
|
||||||
free(contenttype);
|
|
||||||
contenttype = strdup(argv[2]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
goto error;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (strcmp(argv[0], "define") == 0) {
|
|
||||||
if (argc < 3)
|
|
||||||
goto error;
|
|
||||||
else if (strcmp(argv[1], "library") == 0) {
|
|
||||||
#if DYNAMIC_LINKED_PAGES
|
|
||||||
ret->getResponse = loadGetResponse(argv[2]);
|
|
||||||
#else
|
|
||||||
fputs(
|
|
||||||
"This version of swebs has no dynamic page support\n", stderr);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else
|
|
||||||
goto error;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
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) {
|
for (i = 0; i < ret->portcount; ++i) {
|
||||||
if (ret->ports[i].num == newport.num) {
|
Port *port = ret->ports + i;
|
||||||
|
if (port->type == TLS &&
|
||||||
|
(port->key == NULL ||
|
||||||
|
port->cert == NULL)) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Port %hu declared multiple times\n", newport.num);
|
"Port %hu declared without proper TLS files\n", port->num);
|
||||||
goto error;
|
goto nterror;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
free(vars.ports);
|
||||||
if (strcmp(argv[1], "TCP") == 0)
|
free(vars.contenttype);
|
||||||
newport.type = TCP;
|
free(vars.host);
|
||||||
else if (strcmp(argv[1], "TLS") == 0)
|
fclose(file);
|
||||||
newport.type = TLS;
|
return ret;
|
||||||
else {
|
case COMMAND_ERROR:
|
||||||
fprintf(stderr, "Invalid transport %s\n",
|
goto nterror;
|
||||||
argv[1]);
|
case NORMAL:
|
||||||
goto error;
|
break;
|
||||||
|
}
|
||||||
|
for (i = 0; i < LEN(commandspec); ++i) {
|
||||||
|
if (strcmp(argv[0], commandspec[i].name) == 0) {
|
||||||
|
switch (commandspec[i].updatesitefile(&vars,
|
||||||
|
ret, argc, argv)) {
|
||||||
|
case DATA_CHANGE:
|
||||||
|
goto nextcommand;
|
||||||
|
case SITE_SPEC:
|
||||||
|
goto newsitespec;
|
||||||
|
case COMMAND_RET_ERROR:
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
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;
|
|
||||||
ret->alloc *= 2;
|
|
||||||
newcontent = realloc(ret->content, ret->alloc *
|
|
||||||
sizeof *newcontent);
|
|
||||||
if (newcontent == NULL)
|
|
||||||
goto error;
|
|
||||||
ret->content = newcontent;
|
|
||||||
}
|
}
|
||||||
|
fprintf(stderr, "Unknown sitefile command %s", argv[0]);
|
||||||
|
goto error;
|
||||||
|
newsitespec:
|
||||||
|
freecommand(argc, argv);
|
||||||
|
ret->content[ret->size].respondto = vars.respondto;
|
||||||
|
regcomp(&ret->content[ret->size].host, vars.host, CFLAGS);
|
||||||
|
|
||||||
if (regcomp(&ret->content[ret->size].path, argv[1],
|
ret->content[ret->size].ports = xmalloc(vars.portcount *
|
||||||
cflags))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
if (strcmp(argv[0], "read") == 0) {
|
|
||||||
if (argc < 3)
|
|
||||||
goto error;
|
|
||||||
ret->content[ret->size].arg = xstrdup(argv[2]);
|
|
||||||
if (ret->content[ret->size].arg == NULL)
|
|
||||||
goto error;
|
|
||||||
ret->content[ret->size].command = READ;
|
|
||||||
}
|
|
||||||
else if (strcmp(argv[0], "throw") == 0) {
|
|
||||||
if (argc < 3)
|
|
||||||
goto error;
|
|
||||||
ret->content[ret->size].arg = getCode(atoi(argv[2]));
|
|
||||||
if (ret->content[ret->size].arg == NULL)
|
|
||||||
goto error;
|
|
||||||
ret->content[ret->size].command = THROW;
|
|
||||||
}
|
|
||||||
else if (strcmp(argv[0], "linked") == 0) {
|
|
||||||
#if DYNAMIC_LINKED_PAGES
|
|
||||||
ret->content[ret->size].command = LINKED;
|
|
||||||
#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].ports = xmalloc(portcount *
|
|
||||||
sizeof *ret->content[ret->size].ports);
|
sizeof *ret->content[ret->size].ports);
|
||||||
memcpy(ret->content[ret->size].ports, ports, portcount * sizeof *ports);
|
memcpy(ret->content[ret->size].ports, vars.ports,
|
||||||
ret->content[ret->size].portcount = portcount;
|
vars.portcount * sizeof *vars.ports);
|
||||||
|
ret->content[ret->size].portcount = vars.portcount;
|
||||||
|
|
||||||
ret->content[ret->size].contenttype = xstrdup(contenttype);
|
ret->content[ret->size].contenttype = xstrdup(vars.contenttype);
|
||||||
|
|
||||||
ret->size++;
|
++ret->size;
|
||||||
}
|
}
|
||||||
error:
|
error:
|
||||||
freeTokens(argc, argv);
|
freecommand(argc, argv);
|
||||||
nterror:
|
nterror:
|
||||||
|
free(vars.ports);
|
||||||
|
free(vars.contenttype);
|
||||||
|
free(vars.host);
|
||||||
freeSitefile(ret);
|
freeSitefile(ret);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void freeSitefile(Sitefile *site) {
|
void freeSitefile(Sitefile *site) {
|
||||||
long i;
|
long i;
|
||||||
for (i = 0; i < site->size; i++) {
|
for (i = 0; i < site->size; ++i) {
|
||||||
regfree(&site->content[i].path);
|
regfree(&site->content[i].path);
|
||||||
regfree(&site->content[i].host);
|
regfree(&site->content[i].host);
|
||||||
free(site->content[i].arg);
|
free(site->content[i].arg);
|
||||||
|
free(site->content[i].ports);
|
||||||
|
free(site->content[i].contenttype);
|
||||||
}
|
}
|
||||||
free(site->content);
|
free(site->content);
|
||||||
|
for (i = 0; i < site->portcount; ++i) {
|
||||||
|
free(site->ports[i].key);
|
||||||
|
free(site->ports[i].cert);
|
||||||
|
}
|
||||||
|
free(site->ports);
|
||||||
free(site);
|
free(site);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,18 +144,18 @@ Stream *createStream(Context *context, int flags, int fd) {
|
|||||||
break;
|
break;
|
||||||
case TLS:
|
case TLS:
|
||||||
if (gnutls_init(&ret->session, GNUTLS_SERVER) < 0) {
|
if (gnutls_init(&ret->session, GNUTLS_SERVER) < 0) {
|
||||||
createErrorLog("gnutls_init() failed", errno);
|
createLog("gnutls_init() failed");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (gnutls_priority_set(ret->session,
|
if (gnutls_priority_set(ret->session,
|
||||||
context->priority) < 0) {
|
context->priority) < 0) {
|
||||||
createErrorLog("gnutls_priority_set() failed", errno);
|
createLog("gnutls_priority_set() failed");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (gnutls_credentials_set(ret->session,
|
if (gnutls_credentials_set(ret->session,
|
||||||
GNUTLS_CRD_CERTIFICATE,
|
GNUTLS_CRD_CERTIFICATE,
|
||||||
context->creds) < 0) {
|
context->creds) < 0) {
|
||||||
createErrorLog("gnutls_credentials_set() failed", errno);
|
createLog("gnutls_credentials_set() failed");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
gnutls_certificate_server_set_request(ret->session,
|
gnutls_certificate_server_set_request(ret->session,
|
||||||
@@ -164,7 +164,7 @@ Stream *createStream(Context *context, int flags, int fd) {
|
|||||||
GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
|
GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
|
||||||
gnutls_transport_set_int(ret->session, ret->fd);
|
gnutls_transport_set_int(ret->session, ret->fd);
|
||||||
if (gnutls_handshake(ret->session) < 0) {
|
if (gnutls_handshake(ret->session) < 0) {
|
||||||
createErrorLog("gnutls_handshake() failed", errno);
|
createLog("gnutls_handshake() failed");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -22,6 +22,8 @@
|
|||||||
|
|
||||||
int initLogging(char *path);
|
int initLogging(char *path);
|
||||||
|
|
||||||
|
#define LEN(arr) (sizeof (arr) / sizeof (*arr))
|
||||||
|
|
||||||
int smalloc(size_t size);
|
int smalloc(size_t size);
|
||||||
/* returns an id passed to saddr, or -1 on error */
|
/* returns an id passed to saddr, or -1 on error */
|
||||||
void *saddr(int id);
|
void *saddr(int id);
|
||||||
|
|||||||
Reference in New Issue
Block a user