tskrtt

Simple libev based gopher server
git clone https://git.inz.fi/tskrtt/
Log | Files | Refs | README

commit 4942d295cc93007df376fb2a325fcf6336702ab5
parent 554a4490777e44371547a2337f2fc23cec5f376a
Author: Santtu Lakkala <inz@inz.fi>
Date:   Sat, 15 May 2021 23:38:28 +0300

Implement dcgi, cgi improvements

Diffstat:
MMakefile | 2+-
Mmain.c | 166++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
2 files changed, 141 insertions(+), 27 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,5 +1,5 @@ LDFLAGS += -lev -ltls -CFLAGS += -W -Wall -std=c99 -DUSE_TLS +CFLAGS += -W -Wall -std=c99 -DUSE_TLS -g -O0 SOURCES := main.c OBJS := $(patsubst %.c,%.o,$(SOURCES)) diff --git a/main.c b/main.c @@ -21,6 +21,7 @@ #include <grp.h> #include <fcntl.h> #include <limits.h> +#include <signal.h> #ifdef USE_TLS #include <tls.h> #endif @@ -49,7 +50,8 @@ enum task { TASK_BINARY, TASK_ERROR, TASK_REDIRECT, - TASK_CGI + TASK_CGI, + TASK_DCGI, }; struct dir_task { @@ -67,9 +69,9 @@ struct txt_task { struct gph_task { char linebuf[512]; - int rfd; size_t used; char *base; + int rfd; }; struct binary_task { @@ -83,6 +85,11 @@ struct cgi_task { int rfd; }; +struct dcgi_task { + struct gph_task gpht; + struct cgi_task ct; +}; + struct listener { ev_io watcher; int fd; @@ -115,6 +122,7 @@ struct client { struct gph_task gpht; struct binary_task bt; struct cgi_task ct; + struct dcgi_task dct; } task_data; #ifdef USE_TLS struct tls *tlsctx; @@ -122,6 +130,8 @@ struct client { #endif }; +static void read_dcgi(EV_P_ ev_io *w, int revents); + static void init_dir(EV_P_ struct client *c, int fd, struct stat *sb, const char *path, const char *fn, const char *qs, const char *ss); static void init_text(EV_P_ struct client *, int fd, struct stat *sb, const char *path, const char *fn, const char *qs, const char *ss); static void init_gph(EV_P_ struct client *c, int fd, struct stat *sb, const char *path, const char *fn, const char *qs, const char *ss); @@ -130,6 +140,7 @@ static void init_binary(EV_P_ struct client *c, int fd, struct stat *sb, const c static void init_error(EV_P_ struct client *c, int fd, struct stat *sb, const char *path, const char *fn, const char *qs, const char *ss); static void init_redirect(EV_P_ struct client *c, int fd, struct stat *sb, const char *path, const char *fn, const char *qs, const char *ss); static void init_cgi(EV_P_ struct client *c, int fd, struct stat *sb, const char *path, const char *fn, const char *qs, const char *ss); +static void init_dcgi(EV_P_ struct client *c, int fd, struct stat *sb, const char *path, const char *fn, const char *qs, const char *ss); static void update_read(EV_P_ struct client *c, int events); static void update_dir(EV_P_ struct client *c, int events); @@ -140,6 +151,7 @@ static void update_binary(EV_P_ struct client *c, int events); static void update_error(EV_P_ struct client *c, int events); static void update_redirect(EV_P_ struct client *c, int events); static void update_cgi(EV_P_ struct client *c, int events); +static void update_dcgi(EV_P_ struct client *c, int events); static void finish_read(EV_P_ struct client *c); static void finish_dir(EV_P_ struct client *c); @@ -150,6 +162,7 @@ static void finish_binary(EV_P_ struct client *c); static void finish_error(EV_P_ struct client *c); static void finish_redirect(EV_P_ struct client *c); static void finish_cgi(EV_P_ struct client *c); +static void finish_dcgi(EV_P_ struct client *c); static const struct { void (*init)(EV_P_ struct client *c, int fd, struct stat *sb, const char *path, const char *fn, const char *qs, const char *ss); @@ -165,6 +178,7 @@ static const struct { { init_error, update_error, finish_error }, { init_redirect, update_redirect, finish_redirect }, { init_cgi, update_cgi, finish_cgi }, + { init_dcgi, update_dcgi, finish_dcgi }, }; struct listener listen_watcher; @@ -237,6 +251,7 @@ static char *dupensurepath(const char *w) { size_t l = strlen(w); char *rv; + if (!l) return strdup(""); if (w[l - 1] == '/') @@ -247,6 +262,18 @@ static char *dupensurepath(const char *w) return rv; } +static char *dupdirname(const char *w) +{ + char *rv; + char *ls = strrchr(w, '/'); + + if (!ls++) + return strdup(""); + rv = memcpy(malloc(ls - w + 1), w, ls - w); + rv[ls - w] = '\0'; + return rv; +} + static bool tryfileat(int *fd, const char *fn) { int f = openat(*fd, fn, O_RDONLY); @@ -259,16 +286,33 @@ static bool tryfileat(int *fd, const char *fn) return true; } +static char *joinstr(const char *a, const char *b, char separator) +{ + char *rv = malloc(strlen(a) + strlen(b) + 2); + sprintf(rv, "%s%c%s", a, separator, b); + return rv; +} + void guess_task(EV_P_ struct client *c, int fd, struct stat *sb, const char *path, const char *fn, const char *qs, const char *ss) { + char *t = NULL; + (void)qs; + if (sb->st_mode & S_IFDIR) { if (tryfileat(&fd, "gophermap")) { c->task = TASK_GOPHERMAP; sb = NULL; } else if (tryfileat(&fd, "index.gph")) { + path = t = joinstr(path, "index.gph", '/'); c->task = TASK_GPH; sb = NULL; + } else if (!faccessat(fd, "index.cgi", X_OK, 0)) { + path = t = joinstr(path, "index.cgi", '/'); + c->task = TASK_CGI; + } else if (!faccessat(fd, "index.dcgi", X_OK, 0)) { + path = t = joinstr(path, "index.dcgi", '/'); + c->task = TASK_DCGI; } else { c->task = TASK_DIR; } @@ -281,7 +325,11 @@ void guess_task(EV_P_ struct client *c, int fd, struct stat *sb, const char *pat } else { c->task = TASK_BINARY; } + tasks[c->task].init(EV_A_ c, fd, sb, path, fn, qs, ss); + + if (t) + free(t); } static void client_close(EV_P_ struct client *c) @@ -315,6 +363,9 @@ static bool client_flush(struct client *c) w = client_write(c, c->buffer, c->buffer_used); + if (w <= 0) + return false; + if ((size_t)w < c->buffer_used) { memmove(c->buffer, c->buffer + w, c->buffer_used - w); c->buffer_used -= w; @@ -460,12 +511,11 @@ static void init_gophermap(EV_P_ struct client *c, int fd, struct stat *sb, cons static void init_gph(EV_P_ struct client *c, int fd, struct stat *sb, const char *path, const char *fn, const char *qs, const char *ss) { (void)sb; - (void)path; (void)fn; (void)qs; (void)ss; c->task_data.gpht.rfd = fd; - c->task_data.gpht.base = dupensurepath(path); + c->task_data.gpht.base = dupdirname(path); c->task_data.gpht.used = 0; } @@ -536,13 +586,6 @@ static void init_redirect(EV_P_ struct client *c, int fd, struct stat *sb, const b, b, b); } -static char *joinstr(const char *a, const char *b, char separator) -{ - char *rv = malloc(strlen(a) + strlen(b) + 2); - sprintf(rv, "%s%c%s", a, separator, b); - return rv; -} - static char *envstr(const char *key, const char *value) { return joinstr(key, value ? value : "", '='); @@ -570,13 +613,17 @@ static void read_cgi(EV_P_ ev_io *w, int revents) ev_io_start(EV_A_ &c->watcher); } -static void reap_cgi(EV_P_ ev_child *c, int revent) + +static void reap_cgi(EV_P_ ev_child *w, int revent) { - (void)c; + struct cgi_task *ct = PTR_FROM_FIELD(struct cgi_task, input_watcher, w); + (void)revent; + + ct->pid = 0; } -static void init_cgi(EV_P_ struct client *c, int fd, struct stat *sb, const char *path, const char *fn, const char *qs, const char *ss) +static void init_cgi_common(EV_P_ struct client *c, struct cgi_task *ct, int fd, struct stat *sb, const char *path, const char *fn, const char *qs, const char *ss, void (*read_cb)(EV_P_ ev_io *w, int revents)) { int pfd[2]; int nfd; @@ -585,7 +632,6 @@ static void init_cgi(EV_P_ struct client *c, int fd, struct stat *sb, const char char abuf[INET6_ADDRSTRLEN]; char *file; - (void)fd; (void)sb; (void)fn; @@ -594,7 +640,7 @@ static void init_cgi(EV_P_ struct client *c, int fd, struct stat *sb, const char return; } - switch ((c->task_data.ct.pid = fork())) { + switch ((ct->pid = fork())) { case 0: break; case -1: @@ -603,12 +649,13 @@ static void init_cgi(EV_P_ struct client *c, int fd, struct stat *sb, const char client_close(EV_A_ c); return; default: + close(fd); close(pfd[1]); - c->task_data.ct.rfd = pfd[0]; + ct->rfd = pfd[0]; - ev_io_init(&c->task_data.ct.input_watcher, read_cgi, pfd[0], EV_READ); - ev_child_init(&c->task_data.ct.child_watcher, reap_cgi, c->task_data.ct.pid, 0); - ev_io_start(EV_A_ &c->task_data.ct.input_watcher); + ev_io_init(&ct->input_watcher, read_cb, pfd[0], EV_READ); + ev_child_init(&ct->child_watcher, reap_cgi, ct->pid, 0); + ev_io_start(EV_A_ &ct->input_watcher); return; } @@ -634,6 +681,8 @@ static void init_cgi(EV_P_ struct client *c, int fd, struct stat *sb, const char } file = joinstr(gopherroot, path, '/'); + fchdir(fd); + close(fd); getnameinfo((struct sockaddr *)&c->addr, c->addrlen, abuf, sizeof(abuf), NULL, 0, NI_NUMERICHOST); env[nenv++] = envstr("GATEWAY_INTERFACE", "CGI/1.1"); @@ -665,6 +714,17 @@ static void init_cgi(EV_P_ struct client *c, int fd, struct stat *sb, const char exit(1); } +static void init_cgi(EV_P_ struct client *c, int fd, struct stat *sb, const char *path, const char *fn, const char *qs, const char *ss) +{ + init_cgi_common(EV_A_ c, &c->task_data.ct, fd, sb, path, fn, qs, ss, read_cgi); +} + +static void init_dcgi(EV_P_ struct client *c, int fd, struct stat *sb, const char *path, const char *fn, const char *qs, const char *ss) +{ + init_cgi_common(EV_A_ c, &c->task_data.dct.ct, fd, sb, path, fn, qs, ss, read_dcgi); + init_gph(EV_A_ c, -1, sb, path, fn, qs, ss); +} + static void update_dir(EV_P_ struct client *c, int revents) { size_t pos = 0; @@ -1083,7 +1143,10 @@ static void update_read(EV_P_ struct client *c, int revents) if (dfd >= 0) { if (strsfx(bn, ".cgi") && !faccessat(dfd, p, X_OK, 0)) { c->task = TASK_CGI; - tasks[c->task].init(EV_A_ c, -1, NULL, p, bn, qs, ss); + tasks[c->task].init(EV_A_ c, dfd, NULL, p, bn, qs, ss); + } else if (strsfx(bn, ".dcgi") && !faccessat(dfd, p, X_OK, 0)) { + c->task = TASK_DCGI; + tasks[c->task].init(EV_A_ c, dfd, NULL, p, bn, qs, ss); } else { int ffd = openat(dfd, rl ? p : ".", O_RDONLY); if (ffd >= 0) { @@ -1128,6 +1191,41 @@ static void update_cgi(EV_P_ struct client *c, int revents) ev_io_start(EV_A_ &c->task_data.ct.input_watcher); } +static void read_dcgi(EV_P_ ev_io *w, int revents) +{ + struct client *c = PTR_FROM_FIELD(struct client, task_data.dct.ct.input_watcher, w); + + (void)revents; + + if (!line_foreach(EV_A_ c->task_data.dct.ct.rfd, c->task_data.dct.gpht.linebuf, sizeof(c->task_data.dct.gpht.linebuf) - 1, &c->task_data.dct.gpht.used, process_gph_line, c)) { + int n = snprintf(c->buffer + c->buffer_used, sizeof(c->buffer) - c->buffer_used, ".\r\n"); + + if ((size_t)n > sizeof(c->buffer) - c->buffer_used) + return; + + c->buffer_used += n; + + close(c->task_data.dct.ct.rfd); + c->task_data.dct.ct.rfd = -1; + } + + ev_io_stop(EV_A_ &c->task_data.dct.ct.input_watcher); + ev_io_start(EV_A_ &c->watcher); +} + +static void update_dcgi(EV_P_ struct client *c, int revents) +{ + (void)revents; + + if (c->task_data.dct.ct.rfd < 0) { + client_close(EV_A_ c); + return; + } + + ev_io_stop(EV_A_ &c->watcher); + ev_io_start(EV_A_ &c->task_data.dct.ct.input_watcher); +} + static void finish_read(EV_P_ struct client *c) { (void)c; @@ -1155,8 +1253,8 @@ static void finish_gophermap(EV_P_ struct client *c) static void finish_gph(EV_P_ struct client *c) { - if (c->task_data.tt.rfd >= 0) - close(c->task_data.tt.rfd); + if (c->task_data.gpht.rfd >= 0) + close(c->task_data.gpht.rfd); free(c->task_data.gpht.base); } @@ -1175,11 +1273,25 @@ static void finish_redirect(EV_P_ struct client *c) (void)c; } +static void finish_cgi_common(EV_P_ struct cgi_task *ct) +{ + if (ct->pid) + kill(ct->pid, SIGINT); + if (ct->rfd >= 0) + close(ct->rfd); + ev_io_stop(EV_A_ &ct->input_watcher); + ev_child_stop(EV_A_ &ct->child_watcher); +} + static void finish_cgi(EV_P_ struct client *c) { - if (c->task_data.ct.rfd >= 0) - close(c->task_data.ct.rfd); - ev_io_stop(EV_A_ &c->task_data.ct.input_watcher); + finish_cgi_common(EV_A_ &c->task_data.ct); +} + +static void finish_dcgi(EV_P_ struct client *c) +{ + finish_gph(EV_A_ c); + finish_cgi_common(EV_A_ &c->task_data.dct.ct); } static void child_timeout(EV_P_ ev_timer *w, int revents) @@ -1246,6 +1358,8 @@ int main (int argc, char *argv[]) int lfd = -1; bool dofork = true; + signal(SIGPIPE, SIG_IGN); + ARGBEGIN { case '4': hints.ai_family = AF_INET;