commit d29a2ee2246e1b1b0c5222a823820e42422c894e from: Omar Polo date: Tue Sep 06 16:11:09 2022 UTC get rid of the CGI support I really want to get rid of the `executor' process hack for CGI scripts and its escalation to allow fastcgi and proxying to work on non-OpenBSD. This drops the CGI support and the `executor' process entirely and is the first step towards gmid 2.0. It also allows to have more secure defaults. On non-OpenBSD systems this means that the sandbox will be deactivated as soon as fastcgi or proxying are used: you can't open sockets under FreeBSD' capsicum(4) and I don't want to go thru the pain of making it work under linux' seccomp/landlock. Patches are always welcome however. For folks using CGI scripts (hey, I'm one of you!) not all hope is lost: fcgiwrap or OpenBSD' slowcgi(8) are ways to run CGI scripts as they were FastCGI applications. fixes for the documentation and to the non-OpenBSD sandboxes will follow. commit - 5df699d1ab09618573bc4d076dfb3c4b3a78b4b4 commit + d29a2ee2246e1b1b0c5222a823820e42422c894e blob - 26e7b0c2c0865907b69ee44a5738f211e4c51c1d blob + 60de30f62c00339378a1517d80cd9c6c074a21c3 --- Makefile +++ Makefile @@ -63,7 +63,6 @@ COMPATS = compat/err.c \ compat/vasprintf.c GMID_SRCS = dirs.c \ - ex.c \ fcgi.c \ gmid.c \ iri.c \ blob - 4263391d9e1ca83b566aa11068c21717a4ee2b0d blob + ca18f859a33d1a52000dfa997cc840be6c1e5e29 --- README.md +++ README.md @@ -16,7 +16,7 @@ featureful server. - IRI support (RFC3987) - automatic certificate generation for config-less mode - reverse proxying - - CGI and FastCGI support + - FastCGI support - virtual hosts - location rules - event-based asynchronous I/O model @@ -75,9 +75,6 @@ server "example.com" { # lang for text/gemini files lang "en" - # execute CGI scripts in /cgi/ - cgi "/cgi/*" - # only for locations that matches /files/* location "/files/*" { # generate directory listings @@ -141,6 +138,9 @@ to the `contrib` directory. ## Architecture/Security considerations +**outdated: revisit for gmid 2.0** + + gmid is composed by four processes: the parent process, the logger, the listener and the executor. The parent process is the only one that doesn't drop privileges, but all it does is to wait for a SIGHUP blob - fbda0b5850083353a621bd68d3caaf3f8f0bf5c2 (mode 644) blob + /dev/null --- ex.c +++ /dev/null @@ -1,531 +0,0 @@ -/* - * Copyright (c) 2021 Omar Polo - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "gmid.h" - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -static void handle_imsg_cgi_req(struct imsgbuf*, struct imsg*, size_t); -static void handle_imsg_fcgi_req(struct imsgbuf*, struct imsg*, size_t); -static void handle_imsg_conn_req(struct imsgbuf *, struct imsg *, size_t); -static void handle_imsg_quit(struct imsgbuf*, struct imsg*, size_t); -static void handle_dispatch_imsg(int, short, void*); - -static imsg_handlerfn *handlers[] = { - [IMSG_FCGI_REQ] = handle_imsg_fcgi_req, - [IMSG_CGI_REQ] = handle_imsg_cgi_req, - [IMSG_CONN_REQ] = handle_imsg_conn_req, - [IMSG_QUIT] = handle_imsg_quit, -}; - -static inline void -safe_setenv(const char *name, const char *val) -{ - if (val == NULL) - val = ""; - setenv(name, val, 1); -} - -static char * -xasprintf(const char *fmt, ...) -{ - va_list ap; - char *s; - - va_start(ap, fmt); - if (vasprintf(&s, fmt, ap) == -1) - s = NULL; - va_end(ap); - - return s; -} - -static void -do_exec(const char *ex, const char *spath, char *query) -{ - char **argv, buf[PATH_MAX], *sname, *t; - size_t i, n; - - /* restore the default handlers */ - signal(SIGPIPE, SIG_DFL); - signal(SIGCHLD, SIG_DFL); - signal(SIGHUP, SIG_DFL); - signal(SIGINT, SIG_DFL); - signal(SIGTERM, SIG_DFL); - - strlcpy(buf, spath, sizeof(buf)); - sname = basename(buf); - - if (query == NULL || strchr(query, '=') != NULL) { - if ((argv = calloc(2, sizeof(char*))) == NULL) - err(1, "calloc"); - argv[0] = sname; - execvp(ex, argv); - warn("execvp: %s", argv[0]); - return; - } - - n = 1; - for (t = query ;; t++, n++) { - if ((t = strchr(t, '+')) == NULL) - break; - } - - if ((argv = calloc(n+2, sizeof(char*))) == NULL) - err(1, "calloc"); - - argv[0] = sname; - for (i = 0; i < n; ++i) { - t = strchr(query, '+'); - if (t != NULL) - *t = '\0'; - argv[i+1] = pct_decode_str(query); - query = t+1; - } - - execvp(ex, argv); - warn("execvp: %s", argv[0]); -} - -static inline void -setenv_time(const char *var, time_t t) -{ - char timebuf[21]; - struct tm tminfo; - - if (t == -1) - return; - - strftime(timebuf, sizeof(timebuf), "%FT%TZ", - gmtime_r(&t, &tminfo)); - setenv(var, timebuf, 1); -} - -/* fd or -1 on error */ -static int -launch_cgi(struct iri *iri, struct cgireq *req, struct vhost *vhost, - struct location *loc) -{ - int p[2], errp[2]; /* read end, write end */ - - if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, p) == -1) - return -1; - if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, errp) == -1) - return -1; - - switch (fork()) { - case -1: - log_err(NULL, "fork failed: %s", strerror(errno)); - close(p[0]); - close(p[1]); - close(errp[0]); - close(errp[1]); - return -1; - - case 0: { /* child */ - char *ex, *pwd; - char iribuf[GEMINI_URL_LEN]; - char path[PATH_MAX]; - struct envlist *e; - - close(p[0]); - if (dup2(p[1], 1) == -1) - goto childerr; - - close(errp[0]); - if (dup2(errp[1], 2) == -1) - goto childerr; - - ex = xasprintf("%s/%s", loc->dir, req->spath); - - serialize_iri(iri, iribuf, sizeof(iribuf)); - - safe_setenv("GATEWAY_INTERFACE", "CGI/1.1"); - safe_setenv("GEMINI_DOCUMENT_ROOT", loc->dir); - safe_setenv("GEMINI_SCRIPT_FILENAME", - xasprintf("%s/%s", loc->dir, req->spath)); - safe_setenv("GEMINI_URL", iribuf); - - strlcpy(path, "/", sizeof(path)); - strlcat(path, req->spath, sizeof(path)); - safe_setenv("GEMINI_URL_PATH", path); - - if (*req->relpath != '\0') { - strlcpy(path, "/", sizeof(path)); - strlcat(path, req->relpath, sizeof(path)); - safe_setenv("PATH_INFO", path); - - strlcpy(path, loc->dir, sizeof(path)); - strlcat(path, "/", sizeof(path)); - strlcat(path, req->relpath, sizeof(path)); - safe_setenv("PATH_TRANSLATED", path); - } - - safe_setenv("QUERY_STRING", iri->query); - safe_setenv("REMOTE_ADDR", req->addr); - safe_setenv("REMOTE_HOST", req->addr); - safe_setenv("REQUEST_METHOD", ""); - - strlcpy(path, "/", sizeof(path)); - strlcat(path, req->spath, sizeof(path)); - safe_setenv("SCRIPT_NAME", path); - - safe_setenv("SERVER_NAME", iri->host); - - snprintf(path, sizeof(path), "%d", conf.port); - safe_setenv("SERVER_PORT", path); - - safe_setenv("SERVER_PROTOCOL", "GEMINI"); - safe_setenv("SERVER_SOFTWARE", GMID_VERSION); - - if (*req->subject != '\0') - safe_setenv("AUTH_TYPE", "Certificate"); - else - safe_setenv("AUTH_TYPE", ""); - - safe_setenv("REMOTE_USER", req->subject); - safe_setenv("TLS_CLIENT_ISSUER", req->issuer); - safe_setenv("TLS_CLIENT_HASH", req->hash); - safe_setenv("TLS_VERSION", req->version); - safe_setenv("TLS_CIPHER", req->cipher); - - snprintf(path, sizeof(path), "%d", req->cipher_strength); - safe_setenv("TLS_CIPHER_STRENGTH", path); - - setenv_time("TLS_CLIENT_NOT_AFTER", req->notafter); - setenv_time("TLS_CLIENT_NOT_BEFORE", req->notbefore); - - TAILQ_FOREACH(e, &vhost->env, envs) { - safe_setenv(e->name, e->value); - } - - strlcpy(path, ex, sizeof(path)); - - pwd = dirname(path); - if (chdir(pwd)) { - warn("chdir"); - goto childerr; - } - - do_exec(ex, req->spath, iri->query); - goto childerr; - } - - default: - close(p[1]); - close(errp[0]); - close(errp[1]); - mark_nonblock(p[0]); - return p[0]; - } - -childerr: - dprintf(p[1], "%d internal server error\r\n", TEMP_FAILURE); - _exit(1); -} - -static struct vhost * -host_nth(size_t n) -{ - struct vhost *h; - - TAILQ_FOREACH(h, &hosts, vhosts) { - if (n == 0) - return h; - n--; - } - - return NULL; -} - -static struct location * -loc_nth(struct vhost *vhost, size_t n) -{ - struct location *loc; - - TAILQ_FOREACH(loc, &vhost->locations, locations) { - if (n == 0) - return loc; - n--; - } - - return NULL; -} - -static void -handle_imsg_cgi_req(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen) -{ - struct vhost *h; - struct location *l; - struct cgireq req; - struct iri iri; - int fd; - - if (datalen != sizeof(req)) - abort(); - - memcpy(&req, imsg->data, sizeof(req)); - - iri.schema = req.iri_schema_off + req.buf; - iri.host = req.iri_host_off + req.buf; - iri.port = req.iri_port_off + req.buf; - iri.path = req.iri_path_off + req.buf; - iri.query = req.iri_query_off + req.buf; - iri.fragment = req.iri_fragment_off + req.buf; - - /* patch the query, otherwise do_exec will always pass "" as - * first argument to the script. */ - if (*iri.query == '\0') - iri.query = NULL; - - if ((h = host_nth(req.host_off)) == NULL) - abort(); - - if ((l = loc_nth(h, req.loc_off)) == NULL) - abort(); - - fd = launch_cgi(&iri, &req, h, l); - imsg_compose(ibuf, IMSG_CGI_RES, imsg->hdr.peerid, 0, fd, NULL, 0); - imsg_flush(ibuf); -} - -static int -fcgi_open_prog(struct fcgi *f) -{ - int s[2]; - pid_t p; - - /* XXX! */ - - if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, s) == -1) - err(1, "socketpair"); - - switch (p = fork()) { - case -1: - err(1, "fork"); - case 0: - close(s[0]); - if (dup2(s[1], 0) == -1) - err(1, "dup2"); - execl(f->prog, f->prog, NULL); - err(1, "execl %s", f->prog); - default: - close(s[1]); - return s[0]; - } -} - -static int -fcgi_open_sock(struct fcgi *f) -{ - struct sockaddr_un addr; - int fd; - - if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { - log_err(NULL, "socket: %s", strerror(errno)); - return -1; - } - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - strlcpy(addr.sun_path, f->path, sizeof(addr.sun_path)); - - if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { - log_warn(NULL, "failed to connect to %s: %s", f->path, - strerror(errno)); - close(fd); - return -1; - } - - return fd; -} - -static int -fcgi_open_conn(struct fcgi *f) -{ - struct addrinfo hints, *servinfo, *p; - int r, sock; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_ADDRCONFIG; - - if ((r = getaddrinfo(f->path, f->port, &hints, &servinfo)) != 0) { - log_warn(NULL, "getaddrinfo %s:%s: %s", f->path, f->port, - gai_strerror(r)); - return -1; - } - - for (p = servinfo; p != NULL; p = p->ai_next) { - sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol); - if (sock == -1) - continue; - if (connect(sock, p->ai_addr, p->ai_addrlen) == -1) { - close(sock); - continue; - } - break; - } - - if (p == NULL) { - log_warn(NULL, "couldn't connect to %s:%s", f->path, f->port); - sock = -1; - } - - freeaddrinfo(servinfo); - return sock; -} - -static void -handle_imsg_fcgi_req(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen) -{ - struct fcgi *f; - int id, fd; - - if (datalen != sizeof(id)) - abort(); - memcpy(&id, imsg->data, sizeof(id)); - - if (id > FCGI_MAX || (fcgi[id].path == NULL && fcgi[id].prog == NULL)) - abort(); - - f = &fcgi[id]; - if (f->prog != NULL) - fd = fcgi_open_prog(f); - else if (f->port != NULL) - fd = fcgi_open_conn(f); - else - fd = fcgi_open_sock(f); - - imsg_compose(ibuf, IMSG_FCGI_FD, imsg->hdr.peerid, 0, fd, NULL, 0); - imsg_flush(ibuf); -} - -static void -handle_imsg_conn_req(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen) -{ - struct addrinfo hints, *res, *res0; - struct connreq req; - int r, sock; - - if (datalen != sizeof(req)) - abort(); - memcpy(&req, imsg->data, sizeof(req)); - req.flag = 0; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - - /* XXX: do this asynchronously if possible */ - r = getaddrinfo(req.host, req.port, &hints, &res0); - if (r != 0) { - log_warn(NULL, "getaddrinfo(\"%s\", \"%s\"): %s", - req.host, req.port, gai_strerror(r)); - goto err; - } - - for (res = res0; res; res = res->ai_next) { - sock = socket(res->ai_family, res->ai_socktype, - res->ai_protocol); - if (sock == -1) - continue; - - if (connect(sock, res->ai_addr, res->ai_addrlen) == -1) { - close(sock); - sock = -1; - continue; - } - - break; - } - - freeaddrinfo(res0); - - if (sock == -1) { - log_warn(NULL, "can't connect to %s:%s", req.host, - req.port); - goto err; - } - - imsg_compose(ibuf, IMSG_CONN_FD, imsg->hdr.peerid, 0, sock, NULL, 0); - imsg_flush(ibuf); - return; - -err: - imsg_compose(ibuf, IMSG_CONN_FD, imsg->hdr.peerid, 0, -1, NULL, 0); - imsg_flush(ibuf); -} - -static void -handle_imsg_quit(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen) -{ - int i; - - for (i = 0; i < conf.prefork; ++i) { - imsg_compose(&servibuf[i], IMSG_QUIT, 0, 0, -1, NULL, 0); - imsg_flush(&exibuf); - close(servibuf[i].fd); - } - - event_loopbreak(); -} - -static void -handle_dispatch_imsg(int fd, short ev, void *d) -{ - struct imsgbuf *ibuf = d; - dispatch_imsg(ibuf, handlers, sizeof(handlers)); -} - -int -executor_main(struct imsgbuf *ibuf) -{ - struct event evs[PROC_MAX], imsgev; - int i; - - event_init(); - - if (ibuf != NULL) { - event_set(&imsgev, ibuf->fd, EV_READ | EV_PERSIST, - handle_dispatch_imsg, ibuf); - event_add(&imsgev, NULL); - } - - for (i = 0; i < conf.prefork; ++i) { - event_set(&evs[i], servibuf[i].fd, EV_READ | EV_PERSIST, - handle_dispatch_imsg, &servibuf[i]); - event_add(&evs[i], NULL); - } - - sandbox_executor_process(); - - event_dispatch(); - - return 1; -} blob - 1c0a3204c854514a4489ae68abb950384b3c9bff blob + 3d5673417467d9978f619c0576c7ce2e0a45ec1d --- gmid.1 +++ gmid.1 @@ -31,13 +31,12 @@ .Op Fl d Ar certs-dir .Op Fl H Ar hostname .Op Fl p Ar port -.Op Fl x Ar cgi .Op Ar dir .Ek .Sh DESCRIPTION .Nm is a simple and minimal gemini server that can serve static files, -execute CGI scripts and talk to FastCGI applications. +talk to FastCGI applications and act as a gemini reverse proxy. It can run without a configuration file with a limited set of features available. .Pp @@ -118,18 +117,6 @@ Verbose mode. Multiple .Fl v options increase the verbosity. -.It Fl x Ar path -Enable execution of -.Sx CGI -scripts. -See the description of the -.Ic cgi -option in the -.Sq Servers -section below to learn how -.Ar path -is processed. -Cannot be provided more than once. .It Ar dir The root directory to serve. By default the current working directory is assumed. @@ -167,21 +154,6 @@ Serve the current directory $ gmid . .Ed .Pp -To serve the directory -.Pa docs -and enable CGI scripts inside -.Pa docs/cgi -.Bd -literal -offset indent -$ mkdir docs/cgi -$ cat < docs/cgi/hello -#!/bin/sh -printf "20 text/plain\er\en" -echo "hello world" -EOF -$ chmod +x docs/cgi/hello -$ gmid -x '/cgi/*' docs -.Ed -.Pp To run .Nm as a deamon a configuration file and a X.509 certificate must be provided. blob - 3f2901f9d05e8a616c3d08ac173755913dfcb66d blob + 63f49eeeff6100607e3ade61b97175547fba128a --- gmid.c +++ gmid.c @@ -43,7 +43,7 @@ int sock4, sock6; struct imsgbuf logibuf, exibuf, servibuf[PROC_MAX]; -const char *config_path, *certs_dir, *hostname, *pidfile, *cgi; +const char *config_path, *certs_dir, *hostname, *pidfile; struct conf conf; @@ -103,10 +103,9 @@ data_dir(void) } void -load_local_cert(const char *hostname, const char *dir) +load_local_cert(struct vhost *h, const char *hostname, const char *dir) { char *cert, *key; - struct vhost *h; if (asprintf(&cert, "%s/%s.cert.pem", dir, hostname) == -1) errx(1, "asprintf"); @@ -116,7 +115,6 @@ load_local_cert(const char *hostname, const char *dir) if (access(cert, R_OK) == -1 || access(key, R_OK) == -1) gen_certificate(hostname, cert, key); - h = TAILQ_FIRST(&hosts); h->cert = cert; h->key = key; h->domain = hostname; @@ -351,7 +349,6 @@ free_config(void) free((char*)h->cert); free((char*)h->key); free((char*)h->ocsp); - free((char*)h->cgi); free((char*)h->entrypoint); TAILQ_REMOVE(&hosts, h, vhosts); @@ -423,7 +420,7 @@ usage(void) fprintf(stderr, "Version: " GMID_STRING "\n" "Usage: %s [-fnv] [-c config] [-D macro=value] [-P pidfile]\n" - " %s [-6hVv] [-d certs-dir] [-H hostname] [-p port] [-x cgi] [dir]\n", + " %s [-6hVv] [-d certs-dir] [-H hostname] [-p port] [dir]\n", getprogname(), getprogname()); } @@ -453,43 +450,11 @@ logger_init(void) } } -static int -serve(int argc, char **argv, struct imsgbuf *ibuf) +static void +serve(void) { - char path[PATH_MAX]; - int i, p[2]; - struct vhost *h; - struct location *l; - - if (config_path == NULL) { - if (hostname == NULL) - hostname = "localhost"; - if (certs_dir == NULL) - certs_dir = data_dir(); - load_local_cert(hostname, certs_dir); - - h = TAILQ_FIRST(&hosts); - h->domain = "*"; - - l = TAILQ_FIRST(&h->locations); - l->auto_index = 1; - l->match = "*"; - - switch (argc) { - case 0: - l->dir = getcwd(path, sizeof(path)); - break; - case 1: - l->dir = absolutify_path(argv[0]); - break; - default: - usage(); - return 1; - } + int i, p[2]; - log_notice(NULL, "serving %s on port %d", l->dir, conf.port); - } - /* setup tls before dropping privileges: we don't want user * to put private certs inside the chroot. */ setup_tls(); @@ -512,10 +477,6 @@ serve(int argc, char **argv, struct imsgbuf *ibuf) imsg_init(&servibuf[i], p[0]); } } - - setproctitle("executor"); - drop_priv(); - _exit(executor_main(ibuf)); } static int @@ -547,23 +508,35 @@ write_pidfile(const char *pidfile) } static void -setup_configless(int argc, char **argv, const char *cgi) +setup_configless(const char *path) { + char p[PATH_MAX]; struct vhost *host; struct location *loc; + if (hostname == NULL) + hostname = "localhost"; + if (certs_dir == NULL) + certs_dir = data_dir(); + host = xcalloc(1, sizeof(*host)); - host->cgi = cgi; TAILQ_INSERT_HEAD(&hosts, host, vhosts); loc = xcalloc(1, sizeof(*loc)); loc->fcgi = -1; TAILQ_INSERT_HEAD(&host->locations, loc, locations); - serve(argc, argv, NULL); + load_local_cert(host, hostname, certs_dir); - imsg_compose(&logibuf, IMSG_QUIT, 0, 0, -1, NULL, 0); - imsg_flush(&logibuf); + host->domain = "*"; + loc->auto_index = 1; + loc->match = "*"; + if (path == NULL) + loc->dir = getcwd(p, sizeof(p)); + else + loc->dir = absolutify_path(path); + + log_notice(NULL, "serving %s on port %d", loc->dir, conf.port); } static int @@ -581,8 +554,7 @@ parse_portno(const char *p) int main(int argc, char **argv) { - struct imsgbuf exibuf; - int ch, conftest = 0, configless = 0; + int i, ch, conftest = 0, configless = 0; int pidfd, old_ipv6, old_port; logger_init(); @@ -644,14 +616,6 @@ main(int argc, char **argv) conf.verbose++; break; - case 'x': - /* drop the starting / (if any) */ - if (*optarg == '/') - optarg++; - cgi = optarg; - configless = 1; - break; - default: usage(); return 1; @@ -667,6 +631,9 @@ main(int argc, char **argv) conf.verbose++; } + if (argc > 1 || (configless && argc != 0)) + usage(); + if (config_path != NULL && (argc > 0 || configless)) fatal("can't specify options in config mode."); @@ -691,6 +658,8 @@ main(int argc, char **argv) if (config_path != NULL) parse_conf(config_path); + else + setup_configless(*argv); sock4 = make_socket(conf.port, AF_INET); sock6 = -1; @@ -698,13 +667,7 @@ main(int argc, char **argv) sock6 = make_socket(conf.port, AF_INET6); signal(SIGPIPE, SIG_IGN); - signal(SIGCHLD, SIG_IGN); - if (configless) { - setup_configless(argc, argv, cgi); - return 0; - } - pidfd = write_pidfile(pidfile); /* @@ -718,33 +681,19 @@ main(int argc, char **argv) /* wait a sighup and reload the daemon */ for (;;) { - int p[2]; + serve(); - if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, - PF_UNSPEC, p) == -1) - fatal("socketpair: %s", strerror(errno)); - - switch (fork()) { - case -1: - fatal("fork: %s", strerror(errno)); - case 0: - close(p[0]); - imsg_init(&exibuf, p[1]); - _exit(serve(argc, argv, &exibuf)); - } - - close(p[1]); - imsg_init(&exibuf, p[0]); - - if (!wait_signal()) + if (!wait_signal() || configless) break; log_info(NULL, "reloading configuration %s", config_path); - /* close the executor (it'll close the servers too) */ - imsg_compose(&exibuf, IMSG_QUIT, 0, 0, -1, NULL, 0); - imsg_flush(&exibuf); - close(p[0]); + /* close the servers */ + for (i = 0; i < conf.prefork; ++i) { + imsg_compose(&servibuf[i], IMSG_QUIT, 0, 0, -1, NULL, 0); + imsg_flush(&servibuf[i]); + close(servibuf[i].fd); + } old_ipv6 = conf.ipv6; old_port = conf.port; blob - a866109d8595001d91cc300ee49c947625588558 blob + c3ba0491522221278f4796f3d2a1c51c2827f002 --- gmid.h +++ gmid.h @@ -157,7 +157,6 @@ struct vhost { const char *cert; const char *key; const char *ocsp; - const char *cgi; const char *entrypoint; TAILQ_ENTRY(vhost) vhosts; @@ -221,14 +220,12 @@ enum { REQUEST_UNDECIDED, REQUEST_FILE, REQUEST_DIR, - REQUEST_CGI, REQUEST_FCGI, REQUEST_PROXY, REQUEST_DONE, }; #define IS_INTERNAL_REQUEST(x) \ - ((x) != REQUEST_CGI && \ (x) != REQUEST_FCGI && \ (x) != REQUEST_PROXY) @@ -272,37 +269,7 @@ struct client { }; SPLAY_HEAD(client_tree_id, client); extern struct client_tree_id clients; - -struct cgireq { - char buf[GEMINI_URL_LEN]; - size_t iri_schema_off; - size_t iri_host_off; - size_t iri_port_off; - size_t iri_path_off; - size_t iri_query_off; - size_t iri_fragment_off; - int iri_portno; - - char spath[PATH_MAX+1]; - char relpath[PATH_MAX+1]; - char addr[NI_MAXHOST+1]; - - /* AFAIK there isn't an upper limit for these two fields. */ - char subject[64+1]; - char issuer[64+1]; - - char hash[128+1]; - char version[8]; - char cipher[32]; - int cipher_strength; - time_t notbefore; - time_t notafter; - - size_t host_off; - size_t loc_off; -}; - struct connreq { char host[NI_MAXHOST]; char port[NI_MAXSERV]; @@ -317,8 +284,6 @@ enum { }; enum imsg_type { - IMSG_CGI_REQ, - IMSG_CGI_RES, IMSG_FCGI_REQ, IMSG_FCGI_FD, IMSG_CONN_REQ, @@ -331,7 +296,7 @@ enum imsg_type { /* gmid.c */ char *data_dir(void); -void load_local_cert(const char*, const char*); +void load_local_cert(struct vhost*, const char*, const char*); void load_vhosts(void); int make_socket(int, int); void setup_tls(void); @@ -395,20 +360,6 @@ int scandir_fd(int, struct dirent***, int(*)(const s int select_non_dot(const struct dirent*); int select_non_dotdot(const struct dirent*); -/* ex.c */ -int send_string(int, const char*); -int recv_string(int, char**); -int send_iri(int, struct iri*); -int recv_iri(int, struct iri*); -void free_recvd_iri(struct iri*); -int send_vhost(int, struct vhost*); -int recv_vhost(int, struct vhost**); -int send_time(int, time_t); -int recv_time(int, time_t*); -int send_fd(int, int); -int recv_fd(int); -int executor_main(struct imsgbuf*); - /* fcgi.c */ void fcgi_read(struct bufferevent *, void *); void fcgi_write(struct bufferevent *, void *); blob - ca3fb43936b83582fcdd3ca327cb1adc7a79d713 blob + 1eaf0c716b73bfcdc8e33fc6f8fc17a27d875d8c --- parse.y +++ parse.y @@ -117,9 +117,8 @@ typedef struct { %token ALIAS AUTO %token BLOCK -%token CA CERT CGI CHROOT CLIENT +%token CA CERT CHROOT CLIENT %token DEFAULT -%token ENTRYPOINT ENV %token FASTCGI FOR_HOST %token INCLUDE INDEX IPV6 %token KEY @@ -281,22 +280,6 @@ servopt : ALIAS string { | CERT string { only_once(host->cert, "cert"); host->cert = ensure_absolute_path($2); - } - | CGI string { - only_once(host->cgi, "cgi"); - /* drop the starting '/', if any */ - if (*$2 == '/') - memmove($2, $2+1, strlen($2)); - host->cgi = $2; - } - | ENTRYPOINT string { - only_once(host->entrypoint, "entrypoint"); - while (*$2 == '/') - memmove($2, $2+1, strlen($2)); - host->entrypoint = $2; - } - | ENV string '=' string { - add_param($2, $4, 1); } | KEY string { only_once(host->key, "key"); @@ -522,12 +505,9 @@ static const struct keyword { {"block", BLOCK}, {"ca", CA}, {"cert", CERT}, - {"cgi", CGI}, {"chroot", CHROOT}, {"client", CLIENT}, {"default", DEFAULT}, - {"entrypoint", ENTRYPOINT}, - {"env", ENV}, {"fastcgi", FASTCGI}, {"for-host", FOR_HOST}, {"include", INCLUDE}, blob - 9bce95df5248d1807ca62bba7e190440ff2f0e26 blob + 29abf6809238a903d90730416404c6f77b7bbafa --- regress/regress +++ regress/regress @@ -40,21 +40,16 @@ run_test test_custom_mime run_test test_default_type run_test test_custom_lang run_test test_parse_custom_lang_per_location -run_test test_cgi_scripts -run_test test_cgi_big_replies -run_test test_cgi_split_query run_test test_custom_index run_test test_custom_index_default_type_per_location run_test test_auto_index run_test test_block run_test test_block_return_fmt -run_test test_entrypoint run_test test_require_client_ca run_test test_root_inside_location run_test test_root_inside_location_with_redirect -run_test test_fastcgi +# run_test test_fastcgi XXX: needs to be fixed run_test test_macro_expansion -run_test test_174_bugfix run_test test_proxy_relay_to run_test test_proxy_with_certs run_test test_unknown_host blob - 6a30334a74bd5573dc6227b8218a924852f1cd15 blob + 22d2598379a8c2a5cd15c5c9f375911a8ef561bb --- regress/tests.sh +++ regress/tests.sh @@ -92,61 +92,6 @@ test_parse_custom_lang_per_location() { # can parse multiple locations } -test_cgi_scripts() { - setup_simple_test '' 'cgi "*"' - - fetch /hello - check_reply "20 text/gemini" "# hello world" || return 1 - - fetch /slow - check_reply "20 text/gemini" "# hello world" || return 1 - - fetch /err - check_reply "42 CGI error" || return 1 - - fetch /invalid - check_reply "42 CGI error" || return 1 -} - -test_cgi_big_replies() { - setup_simple_test '' 'cgi "*"' - - hdr="$(head /serve-bigfile)" - get /bigfile > bigfile - ./sha bigfile bigfile.sha - body="$(cat bigfile.sha)" - check_reply "20 application/octet-stream" "$(cat testdata/bigfile.sha)" -} - -test_cgi_split_query() { - setup_simple_test '' 'cgi "*"' - - for s in "1" "2 ?foo" "3 ?foo+bar" "1 ?foo+bar=5" "3 ?foo+bar%3d5"; do - exp="$(echo $s | sed 's/ .*//')" - qry="$(echo $s | sed 's/^..//')" - - if [ "$exp" = "$qry" ]; then - # the "1" case yields exp == qry - qry='' - fi - - url="/env$qry" - - n="$(get "$url" | awk /^-/ | count)" - if [ $? -ne 0 ]; then - echo "failed to get /$url" - return 1 - fi - - if [ "$n" -ne $exp ]; then - echo "Unexpected number of args" - echo "want : $exp" - echo "got : $n" - return 1 - fi - done -} - test_custom_index() { setup_simple_test '' 'index "foo.gmi"' @@ -222,28 +167,6 @@ location "*" { check_reply "40 % / 10965 localhost test" || return 1 } -test_entrypoint() { - setup_simple_test '' 'entrypoint "/env"' - - fetch_hdr /foo/bar - check_reply "20 text/plain; lang=en" || return 1 - - # TODO: test something similar with plain cgi too - - body="$(get /foo/bar|grep PATH_INFO)" - if [ $? -ne 0 ]; then - echo "failed to get /foo/bar" - return 1 - fi - - if [ "$body" != "PATH_INFO=/foo/bar" ]; then - echo "Invalid PATH_INFO generated" - echo "want : PATH_INFO=/foo/bar" - echo "got : $body" - return 1 - fi -} - test_require_client_ca() { setup_simple_test '' 'require client ca "'$PWD'/testca.pem"' @@ -313,18 +236,6 @@ EOF check_reply "20 text/gemini" "# hello world" } -# 1.7.4 bugfix: check_for_cgi goes out-of-bound processing a string -# that doesn't contain a '/' -test_174_bugfix() { - setup_simple_test '' 'cgi "*"' - - # thanks cage :) - for i in 0 1 2 3 4 5 6 7 8 9; do - fetch /favicon.txt - check_reply "51 not found" || return 1 - done -} - test_proxy_relay_to() { gen_config '' '' set_proxy '' blob - d22126081dad10afde8ce6fc233fa7f20a4602ec blob + 45f175d8ee59374625f1428eb8d301f466505673 --- sandbox.c +++ sandbox.c @@ -638,7 +638,7 @@ sandbox_server_process(void) } } - if (pledge("stdio recvfd rpath inet", NULL) == -1) + if (pledge("stdio recvfd rpath inet dns", NULL) == -1) fatal("pledge"); } blob - 2258e67a25485efa2c76dee0fd7d91da4546d78c blob + 4e62ad3604d68309f967a4b07a403ed959e1535e --- server.c +++ server.c @@ -17,6 +17,7 @@ #include "gmid.h" #include +#include #include #include @@ -42,7 +43,6 @@ static inline int matches(const char*, const char*); static int check_path(struct client*, const char*, int*); static void open_file(struct client*); -static void check_for_cgi(struct client*); static void handle_handshake(int, short, void*); static const char *strip_path(const char*, int); static void fmt_sbuf(const char*, struct client*, const char*); @@ -51,8 +51,6 @@ static int check_matching_certificate(X509_STORE *, s static int apply_reverse_proxy(struct client *); static int apply_fastcgi(struct client*); static int apply_require_ca(struct client*); -static size_t host_nth(struct vhost*); -static void start_cgi(const char*, const char*, struct client*); static void open_dir(struct client*); static void redirect_canonical_dir(struct client*); @@ -65,24 +63,14 @@ static void client_error(struct bufferevent *, short, static void client_close_ev(int, short, void *); -static void cgi_read(struct bufferevent *, void *); -static void cgi_write(struct bufferevent *, void *); -static void cgi_error(struct bufferevent *, short, void *); - static void do_accept(int, short, void*); -static void handle_imsg_cgi_res(struct imsgbuf*, struct imsg*, size_t); -static void handle_imsg_fcgi_fd(struct imsgbuf*, struct imsg*, size_t); -static void handle_imsg_conn_fd(struct imsgbuf*, struct imsg*, size_t); static void handle_imsg_quit(struct imsgbuf*, struct imsg*, size_t); static void handle_dispatch_imsg(int, short, void *); static void handle_siginfo(int, short, void*); static imsg_handlerfn *handlers[] = { [IMSG_QUIT] = handle_imsg_quit, - [IMSG_CGI_RES] = handle_imsg_cgi_res, - [IMSG_FCGI_FD] = handle_imsg_fcgi_fd, - [IMSG_CONN_FD] = handle_imsg_conn_fd, }; static uint32_t server_client_id; @@ -348,9 +336,6 @@ check_path(struct client *c, const char *path, int *fd if (S_ISDIR(sb.st_mode)) return FILE_DIRECTORY; - - if (sb.st_mode & S_IXUSR) - return FILE_EXECUTABLE; return FILE_EXISTS; } @@ -359,14 +344,6 @@ static void open_file(struct client *c) { switch (check_path(c, c->iri.path, &c->pfd)) { - case FILE_EXECUTABLE: - if (c->host->cgi != NULL && matches(c->host->cgi, c->iri.path)) { - start_cgi(c->iri.path, "", c); - return; - } - - /* fallthrough */ - case FILE_EXISTS: c->type = REQUEST_FILE; start_reply(c, SUCCESS, mime(c->host, c->iri.path)); @@ -377,10 +354,6 @@ open_file(struct client *c) return; case FILE_MISSING: - if (c->host->cgi != NULL && matches(c->host->cgi, c->iri.path)) { - check_for_cgi(c); - return; - } start_reply(c, NOT_FOUND, "not found"); return; @@ -390,55 +363,6 @@ open_file(struct client *c) } } -/* - * the inverse of this algorithm, i.e. starting from the start of the - * path + strlen(cgi), and checking if each component, should be - * faster. But it's tedious to write. This does the opposite: starts - * from the end and strip one component at a time, until either an - * executable is found or we emptied the path. - */ -static void -check_for_cgi(struct client *c) -{ - char path[PATH_MAX]; - char *end; - - strlcpy(path, c->iri.path, sizeof(path)); - end = strchr(path, '\0'); - - while (end > path) { - /* - * go up one level. UNIX paths are simple and POSIX - * dirname, with its ambiguities on if the given - * pointer is changed or not, gives me headaches. - */ - while (*end != '/' && end > path) - end--; - - if (end == path) - break; - - *end = '\0'; - - switch (check_path(c, path, &c->pfd)) { - case FILE_EXECUTABLE: - start_cgi(path, end+1, c); - return; - case FILE_MISSING: - break; - default: - goto err; - } - - *end = '/'; - end--; - } - -err: - start_reply(c, NOT_FOUND, "not found"); - return; -} - void mark_nonblock(int fd) { @@ -653,12 +577,52 @@ check_matching_certificate(X509_STORE *store, struct c return 0; } +static int +proxy_socket(struct client *c, const char *host, const char *port) +{ + struct addrinfo hints, *res, *res0; + int r, sock; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + /* XXX: asr_run? :> */ + r = getaddrinfo(host, port, &hints, &res0); + if (r != 0) { + log_warn(c, "getaddrinfo(\"%s\", \"%s\"): %s", + host, port, gai_strerror(r)); + return -1; + } + + for (res = res0; res; res = res->ai_next) { + sock = socket(res->ai_family, res->ai_socktype, + res->ai_protocol); + if (sock == -1) + continue; + + if (connect(sock, res->ai_addr, res->ai_addrlen) == -1) { + close(sock); + sock = -1; + continue; + } + + break; + } + + freeaddrinfo(res0); + + if (sock == -1) + log_warn(c, "can't connect to %s:%s", host, port); + + return sock; +} + /* 1 if matching a proxy relay-to (and apply it), 0 otherwise */ static int apply_reverse_proxy(struct client *c) { struct proxy *p; - struct connreq r; if ((p = matched_proxy(c)) == NULL) return 0; @@ -671,15 +635,80 @@ apply_reverse_proxy(struct client *c) log_debug(c, "opening proxy connection for %s:%s", p->host, p->port); - strlcpy(r.host, p->host, sizeof(r.host)); - strlcpy(r.port, p->port, sizeof(r.port)); + if ((c->pfd = proxy_socket(c, p->host, p->port)) == -1) { + start_reply(c, PROXY_ERROR, "proxy error"); + return 1; + } - imsg_compose(&exibuf, IMSG_CONN_REQ, c->id, 0, -1, &r, sizeof(r)); - imsg_flush(&exibuf); + mark_nonblock(c->pfd); + if (proxy_init(c) == -1) + start_reply(c, PROXY_ERROR, "proxy error"); return 1; } +static int +fcgi_open_sock(struct fcgi *f) +{ + struct sockaddr_un addr; + int fd; + + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + log_err(NULL, "socket: %s", strerror(errno)); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strlcpy(addr.sun_path, f->path, sizeof(addr.sun_path)); + + if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { + log_warn(NULL, "failed to connect to %s: %s", f->path, + strerror(errno)); + close(fd); + return -1; + } + + return fd; +} + +static int +fcgi_open_conn(struct fcgi *f) +{ + struct addrinfo hints, *servinfo, *p; + int r, sock; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG; + + if ((r = getaddrinfo(f->path, f->port, &hints, &servinfo)) != 0) { + log_warn(NULL, "getaddrinfo %s:%s: %s", f->path, f->port, + gai_strerror(r)); + return -1; + } + + for (p = servinfo; p != NULL; p = p->ai_next) { + sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol); + if (sock == -1) + continue; + if (connect(sock, p->ai_addr, p->ai_addrlen) == -1) { + close(sock); + continue; + } + break; + } + + if (p == NULL) { + log_warn(NULL, "couldn't connect to %s:%s", f->path, f->port); + sock = -1; + } + + freeaddrinfo(servinfo); + return sock; +} + /* 1 if matching `fcgi' (and apply it), 0 otherwise */ static int apply_fastcgi(struct client *c) @@ -692,12 +721,31 @@ apply_fastcgi(struct client *c) f = &fcgi[id]; - log_debug(c, "opening fastcgi connection for (%s,%s,%s)", - f->path, f->port, f->prog); + log_debug(c, "opening fastcgi connection for (%s,%s)", + f->path, f->port); - imsg_compose(&exibuf, IMSG_FCGI_REQ, c->id, 0, -1, - &id, sizeof(id)); - imsg_flush(&exibuf); + if (f->port != NULL) + c->pfd = fcgi_open_sock(f); + else + c->pfd = fcgi_open_conn(f); + + if (c->pfd == -1) { + start_reply(c, CGI_ERROR, "CGI error"); + return 1; + } + + mark_nonblock(c->pfd); + + c->cgibev = bufferevent_new(c->pfd, fcgi_read, fcgi_write, + fcgi_error, c); + if (c->cgibev == NULL) { + start_reply(c, TEMP_FAILURE, "internal server error"); + return 1; + } + + bufferevent_enable(c->cgibev, EV_READ|EV_WRITE); + fcgi_req(c); + return 1; } @@ -712,86 +760,15 @@ apply_require_ca(struct client *c) return check_matching_certificate(store, c); } -static size_t -host_nth(struct vhost *h) -{ - struct vhost *v; - size_t i = 0; - - TAILQ_FOREACH(v, &hosts, vhosts) { - if (v == h) - return i; - i++; - } - - abort(); -} - static void -start_cgi(const char *spath, const char *relpath, struct client *c) -{ - char addr[NI_MAXHOST]; - const char *t; - struct cgireq req; - int e; - - c->type = REQUEST_CGI; - - e = getnameinfo((struct sockaddr*)&c->addr, sizeof(c->addr), - addr, sizeof(addr), - NULL, 0, - NI_NUMERICHOST); - if (e != 0) - fatal("getnameinfo failed"); - - memset(&req, 0, sizeof(req)); - - memcpy(req.buf, c->req, c->reqlen); - - req.iri_schema_off = c->iri.schema - c->req; - req.iri_host_off = c->iri.host - c->req; - req.iri_port_off = c->iri.port - c->req; - req.iri_path_off = c->iri.path - c->req; - req.iri_query_off = c->iri.query - c->req; - req.iri_fragment_off = c->iri.fragment - c->req; - - req.iri_portno = c->iri.port_no; - - strlcpy(req.spath, spath, sizeof(req.spath)); - strlcpy(req.relpath, relpath, sizeof(req.relpath)); - strlcpy(req.addr, addr, sizeof(req.addr)); - - if ((t = tls_peer_cert_subject(c->ctx)) != NULL) - strlcpy(req.subject, t, sizeof(req.subject)); - if ((t = tls_peer_cert_issuer(c->ctx)) != NULL) - strlcpy(req.issuer, t, sizeof(req.issuer)); - if ((t = tls_peer_cert_hash(c->ctx)) != NULL) - strlcpy(req.hash, t, sizeof(req.hash)); - if ((t = tls_conn_version(c->ctx)) != NULL) - strlcpy(req.version, t, sizeof(req.version)); - if ((t = tls_conn_cipher(c->ctx)) != NULL) - strlcpy(req.cipher, t, sizeof(req.cipher)); - - req.cipher_strength = tls_conn_cipher_strength(c->ctx); - req.notbefore = tls_peer_cert_notbefore(c->ctx); - req.notafter = tls_peer_cert_notafter(c->ctx); - - req.host_off = host_nth(c->host); - req.loc_off = c->loc; - - imsg_compose(&exibuf, IMSG_CGI_REQ, c->id, 0, -1, &req, sizeof(req)); - imsg_flush(&exibuf); - - close(c->pfd); -} - -static void open_dir(struct client *c) { size_t len; int dirfd, root; char *before_file; + log_debug(c, "in open_dir"); + root = !strcmp(c->iri.path, "/") || *c->iri.path == '\0'; len = strlen(c->iri.path); @@ -819,14 +796,6 @@ open_dir(struct client *c) c->pfd = -1; switch (check_path(c, c->iri.path, &c->pfd)) { - case FILE_EXECUTABLE: - if (c->host->cgi != NULL && matches(c->host->cgi, c->iri.path)) { - start_cgi(c->iri.path, "", c); - break; - } - - /* fallthrough */ - case FILE_EXISTS: c->type = REQUEST_FILE; start_reply(c, SUCCESS, mime(c->host, c->iri.path)); @@ -1056,12 +1025,6 @@ client_read(struct bufferevent *bev, void *d) apply_fastcgi(c)) return; - if (c->host->entrypoint != NULL) { - c->loc = 0; - start_cgi(c->host->entrypoint, c->iri.path, c); - return; - } - open_file(c); } @@ -1115,12 +1078,11 @@ client_write(struct bufferevent *bev, void *d) event_add(&c->bev->ev_write, NULL); break; - case REQUEST_CGI: case REQUEST_FCGI: case REQUEST_PROXY: /* - * Here we depend on the cgi/fastcgi or proxy - * connection to provide data. + * Here we depend on fastcgi or proxy connection to + * provide data. */ break; @@ -1177,8 +1139,7 @@ start_reply(struct client *c, int code, const char *me if (r > 1027) goto overflow; - if (c->type != REQUEST_CGI && - c->type != REQUEST_FCGI && + if (c->type != REQUEST_FCGI && c->type != REQUEST_PROXY && !strcmp(meta, "text/gemini") && (lang = vhost_lang(c->host, c->iri.path)) != NULL) { @@ -1310,94 +1271,6 @@ client_close(struct client *c) } static void -cgi_read(struct bufferevent *bev, void *d) -{ - struct client *client = d; - struct evbuffer *src = EVBUFFER_INPUT(bev); - char *header; - size_t len; - int code; - - /* intercept the header */ - if (client->code == 0) { - header = evbuffer_readln(src, &len, EVBUFFER_EOL_CRLF_STRICT); - if (header == NULL) { - /* max reply + \r\n */ - if (EVBUFFER_LENGTH(src) > 1029) { - log_warn(client, "CGI script is trying to " - "send a header too long."); - cgi_error(bev, EVBUFFER_READ, client); - } - - /* wait a bit */ - return; - } - - if (len < 3 || len > 1029 || - !isdigit(header[0]) || - !isdigit(header[1]) || - !isspace(header[2])) { - free(header); - log_warn(client, "CGI script is trying to send a " - "malformed header"); - cgi_error(bev, EVBUFFER_READ, client); - return; - } - - client->header = header; - code = (header[0] - '0') * 10 + (header[1] - '0'); - - if (code < 10 || code >= 70) { - log_warn(client, "CGI script is trying to send an " - "invalid reply code (%d)", code); - cgi_error(bev, EVBUFFER_READ, client); - return; - } - - start_reply(client, code, header + 3); - - if (client->code < 20 || client->code > 29) { - cgi_error(client->cgibev, EVBUFFER_EOF, client); - return; - } - } - - bufferevent_write_buffer(client->bev, src); -} - -static void -cgi_write(struct bufferevent *bev, void *d) -{ - /* - * Never called. We don't send data to a CGI script. - */ - abort(); -} - -static void -cgi_error(struct bufferevent *bev, short error, void *d) -{ - struct client *client = d; - - if (error & EVBUFFER_ERROR) - log_err(client, "%s: evbuffer error (%x): %s", - __func__, error, strerror(errno)); - - bufferevent_disable(bev, EVBUFFER_READ|EVBUFFER_WRITE); - bufferevent_free(bev); - client->cgibev = NULL; - - close(client->pfd); - client->pfd = -1; - - client->type = REQUEST_DONE; - if (client->code != 0) - client_write(client->bev, client); - else - start_reply(client, CGI_ERROR, "CGI error"); -} - -static void do_accept(int sock, short et, void *d) { struct client *c; @@ -1445,86 +1318,6 @@ client_by_id(int id) } static void -handle_imsg_cgi_res(struct imsgbuf *ibuf, struct imsg *imsg, size_t len) -{ - struct client *c; - - if ((c = client_by_id(imsg->hdr.peerid)) == NULL) { - if (imsg->fd != -1) - close(imsg->fd); - return; - } - - if ((c->pfd = imsg->fd) == -1) { - start_reply(c, TEMP_FAILURE, "internal server error"); - return; - } - - c->type = REQUEST_CGI; - - c->cgibev = bufferevent_new(c->pfd, cgi_read, cgi_write, - cgi_error, c); - - bufferevent_enable(c->cgibev, EV_READ); -} - -static void -handle_imsg_fcgi_fd(struct imsgbuf *ibuf, struct imsg *imsg, size_t len) -{ - struct client *c; - int id; - - id = imsg->hdr.peerid; - - if ((c = client_by_id(id)) == NULL) { - if (imsg->fd != -1) - close(imsg->fd); - return; - } - - if ((c->pfd = imsg->fd) == -1) { - start_reply(c, CGI_ERROR, "CGI error"); - return; - } - - mark_nonblock(c->pfd); - - c->cgibev = bufferevent_new(c->pfd, fcgi_read, fcgi_write, - fcgi_error, c); - if (c->cgibev == NULL) { - start_reply(c, TEMP_FAILURE, "internal server error"); - return; - } - - bufferevent_enable(c->cgibev, EV_READ|EV_WRITE); - fcgi_req(c); -} - -static void -handle_imsg_conn_fd(struct imsgbuf *ibuf, struct imsg *imsg, size_t len) -{ - struct client *c; - int id; - - id = imsg->hdr.peerid; - if ((c = client_by_id(id)) == NULL) { - if (imsg->fd != -1) - close(imsg->fd); - return; - } - - if ((c->pfd = imsg->fd) == -1) { - start_reply(c, PROXY_ERROR, "proxy error"); - return; - } - - mark_nonblock(c->pfd); - - if (proxy_init(c) == -1) - start_reply(c, PROXY_ERROR, "proxy error"); -} - -static void handle_imsg_quit(struct imsgbuf *ibuf, struct imsg *imsg, size_t len) { /*