diff options
-rw-r--r-- | ex.c | 337 | ||||
-rw-r--r-- | gmid.c | 75 | ||||
-rw-r--r-- | gmid.h | 51 | ||||
-rw-r--r-- | log.c | 72 | ||||
-rw-r--r-- | regress/puny-test.c | 2 | ||||
-rw-r--r-- | server.c | 140 | ||||
-rw-r--r-- | utils.c | 48 |
7 files changed, 323 insertions, 402 deletions
@@ -27,199 +27,14 @@ #include <stdarg.h> #include <string.h> -int -send_string(int fd, const char *str) -{ - ssize_t len; - - if (str == NULL) - len = 0; - else - len = strlen(str); - - if (write(fd, &len, sizeof(len)) != sizeof(len)) - return 0; - - if (len != 0) - if (write(fd, str, len) != len) - return 0; - - return 1; -} - -int -recv_string(int fd, char **ret) -{ - ssize_t len; - - if (read(fd, &len, sizeof(len)) != sizeof(len)) - return 0; - - if (len == 0) { - *ret = NULL; - return 1; - } - - if ((*ret = calloc(1, len+1)) == NULL) - return 0; - - if (read(fd, *ret, len) != len) - return 0; - return 1; -} - -int -send_iri(int fd, struct iri *i) -{ - return send_string(fd, i->schema) - && send_string(fd, i->host) - && send_string(fd, i->port) - && send_string(fd, i->path) - && send_string(fd, i->query); -} +static void handle_imsg_cgi_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*); -int -recv_iri(int fd, struct iri *i) -{ - memset(i, 0, sizeof(*i)); - - if (!recv_string(fd, &i->schema) - || !recv_string(fd, &i->host) - || !recv_string(fd, &i->port) - || !recv_string(fd, &i->path) - || !recv_string(fd, &i->query)) - return 0; - - return 1; -} - -void -free_recvd_iri(struct iri *i) -{ - free(i->schema); - free(i->host); - free(i->port); - free(i->path); - free(i->query); -} - -int -send_vhost(int fd, struct vhost *vhost) -{ - ssize_t n; - - if (vhost < hosts || vhost > hosts + HOSTSLEN) - return 0; - - n = vhost - hosts; - return write(fd, &n, sizeof(n)) == sizeof(n); -} - -int -recv_vhost(int fd, struct vhost **vhost) -{ - ssize_t n; - - if (read(fd, &n, sizeof(n)) != sizeof(n)) - return 0; - - if (n < 0 || n > HOSTSLEN) - return 0; - - *vhost = &hosts[n]; - if ((*vhost)->domain == NULL) - return 0; - return 1; -} - -int -send_time(int fd, time_t t) -{ - return write(fd, &t, sizeof(t)) == sizeof(t); -} - -int -recv_time(int fd, time_t *t) -{ - return read(fd, t, sizeof(*t)) == sizeof(*t); -} - -/* send d though fd. see /usr/src/usr.sbin/syslogd/privsep_fdpass.c - * for an example */ -int -send_fd(int fd, int d) -{ - struct msghdr msg; - union { - struct cmsghdr hdr; - unsigned char buf[CMSG_SPACE(sizeof(int))]; - } cmsgbuf; - struct cmsghdr *cmsg; - struct iovec vec; - int result = 1; - ssize_t n; - - memset(&msg, 0, sizeof(msg)); - - if (d >= 0) { - msg.msg_control = &cmsgbuf.buf; - msg.msg_controllen = sizeof(cmsgbuf.buf); - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_len = CMSG_LEN(sizeof(int)); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - *(int*)CMSG_DATA(cmsg) = d; - } else - result = 0; - - vec.iov_base = &result; - vec.iov_len = sizeof(int); - msg.msg_iov = &vec; - msg.msg_iovlen = 1; - - if ((n = sendmsg(fd, &msg, 0)) == -1 || n != sizeof(int)) { - log_err(NULL, "sendmsg: got %zu but wanted %zu: (errno) %s", - n, sizeof(int), strerror(errno)); - return 0; - } - return 1; -} - -/* receive a descriptor via fd */ -int -recv_fd(int fd) -{ - struct msghdr msg; - union { - struct cmsghdr hdr; - char buf[CMSG_SPACE(sizeof(int))]; - } cmsgbuf; - struct cmsghdr *cmsg; - struct iovec vec; - ssize_t n; - int result; - - memset(&msg, 0, sizeof(msg)); - vec.iov_base = &result; - vec.iov_len = sizeof(int); - msg.msg_iov = &vec; - msg.msg_iovlen = 1; - msg.msg_control = &cmsgbuf.buf; - msg.msg_controllen = sizeof(cmsgbuf.buf); - - if ((n = recvmsg(fd, &msg, 0)) != sizeof(int)) { - log_err(NULL, "read %zu bytes bu wanted %zu\n", n, sizeof(int)); - return -1; - } - - if (result) { - cmsg = CMSG_FIRSTHDR(&msg); - if (cmsg == NULL || cmsg->cmsg_type != SCM_RIGHTS) - return -1; - return (*(int *)CMSG_DATA(cmsg)); - } else - return -1; -} +static imsg_handlerfn *handlers[] = { + [IMSG_CGI_REQ] = handle_imsg_cgi_req, + [IMSG_QUIT] = handle_imsg_quit, +}; static inline void safe_setenv(const char *name, const char *val) @@ -299,10 +114,7 @@ setenv_time(const char *var, time_t t) /* fd or -1 on error */ static int -launch_cgi(struct iri *iri, const char *spath, char *relpath, - const char *addr, const char *ruser, const char *cissuer, - const char *chash, time_t notbefore, time_t notafter, - struct vhost *vhost) +launch_cgi(struct iri *iri, struct cgireq *req, struct vhost *vhost) { int p[2]; /* read end, write end */ @@ -322,38 +134,38 @@ launch_cgi(struct iri *iri, const char *spath, char *relpath, if (dup2(p[1], 1) == -1) goto childerr; - ex = xasprintf("%s/%s", vhost->dir, spath); + ex = xasprintf("%s/%s", vhost->dir, req->spath); serialize_iri(iri, iribuf, sizeof(iribuf)); safe_setenv("GATEWAY_INTERFACE", "CGI/1.1"); safe_setenv("GEMINI_DOCUMENT_ROOT", vhost->dir); safe_setenv("GEMINI_SCRIPT_FILENAME", - xasprintf("%s/%s", vhost->dir, spath)); + xasprintf("%s/%s", vhost->dir, req->spath)); safe_setenv("GEMINI_URL", iribuf); strlcpy(path, "/", sizeof(path)); - strlcat(path, spath, sizeof(path)); + strlcat(path, req->spath, sizeof(path)); safe_setenv("GEMINI_URL_PATH", path); - if (relpath != NULL) { + if (*req->relpath != '\0') { strlcpy(path, "/", sizeof(path)); - strlcat(path, relpath, sizeof(path)); + strlcat(path, req->relpath, sizeof(path)); safe_setenv("PATH_INFO", path); strlcpy(path, vhost->dir, sizeof(path)); strlcat(path, "/", sizeof(path)); - strlcat(path, relpath, sizeof(path)); + strlcat(path, req->relpath, sizeof(path)); safe_setenv("PATH_TRANSLATED", path); } safe_setenv("QUERY_STRING", iri->query); - safe_setenv("REMOTE_ADDR", addr); - safe_setenv("REMOTE_HOST", addr); + safe_setenv("REMOTE_ADDR", req->addr); + safe_setenv("REMOTE_HOST", req->addr); safe_setenv("REQUEST_METHOD", ""); strlcpy(path, "/", sizeof(path)); - strlcat(path, spath, sizeof(path)); + strlcat(path, req->spath, sizeof(path)); safe_setenv("SCRIPT_NAME", path); safe_setenv("SERVER_NAME", iri->host); @@ -364,16 +176,16 @@ launch_cgi(struct iri *iri, const char *spath, char *relpath, safe_setenv("SERVER_PROTOCOL", "GEMINI"); safe_setenv("SERVER_SOFTWARE", "gmid/1.5"); - if (ruser != NULL) + if (*req->subject != '\0') safe_setenv("AUTH_TYPE", "Certificate"); else safe_setenv("AUTH_TYPE", ""); - safe_setenv("REMOTE_USER", ruser); - safe_setenv("TLS_CLIENT_ISSUER", cissuer); - safe_setenv("TLS_CLIENT_HASH", chash); - setenv_time("TLS_CLIENT_NOT_AFTER", notafter); - setenv_time("TLS_CLIENT_NOT_BEFORE", notbefore); + safe_setenv("REMOTE_USER", req->subject); + safe_setenv("TLS_CLIENT_ISSUER", req->issuer); + safe_setenv("TLS_CLIENT_HASH", req->hash); + setenv_time("TLS_CLIENT_NOT_AFTER", req->notafter); + setenv_time("TLS_CLIENT_NOT_BEFORE", req->notbefore); strlcpy(path, ex, sizeof(path)); @@ -383,7 +195,7 @@ launch_cgi(struct iri *iri, const char *spath, char *relpath, goto childerr; } - do_exec(ex, spath, iri->query); + do_exec(ex, req->spath, iri->query); goto childerr; } @@ -399,52 +211,67 @@ childerr: } static void -handle_fork_req(int fd, short ev, void *data) +handle_imsg_cgi_req(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen) { - char *spath, *relpath, *addr, *ruser, *cissuer, *chash; - struct vhost *vhost; - struct iri iri; - time_t notbefore, notafter; - int d; - - if (!recv_iri(fd, &iri) - || !recv_string(fd, &spath) - || !recv_string(fd, &relpath) - || !recv_string(fd, &addr) - || !recv_string(fd, &ruser) - || !recv_string(fd, &cissuer) - || !recv_string(fd, &chash) - || !recv_time(fd, ¬before) - || !recv_time(fd, ¬after) - || !recv_vhost(fd, &vhost)) { - if (errno == EINTR) { - event_loopbreak(); - return; - } - fatal("failure in handling fork request: %s", strerror(errno)); + struct cgireq req; + struct iri iri; + int fd; + + if (datalen != sizeof(req)) + abort(); + + memcpy(&req, imsg->data, datalen); + + 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 (req.host_off > HOSTSLEN || hosts[req.host_off].domain == NULL) + abort(); + + fd = launch_cgi(&iri, &req, &hosts[req.host_off]); + imsg_compose(ibuf, IMSG_CGI_RES, imsg->hdr.peerid, 0, fd, NULL, 0); + imsg_flush(ibuf); +} + +static void +handle_imsg_quit(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen) +{ + int i; + + (void)ibuf; + (void)imsg; + (void)datalen; + + 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); } - d = launch_cgi(&iri, spath, relpath, addr, ruser, cissuer, chash, - notbefore, notafter, vhost); - if (!send_fd(fd, d)) - fatal("failure in sending the fd to the server: %s", - strerror(errno)); - close(d); - - free_recvd_iri(&iri); - free(spath); - free(relpath); - free(addr); - free(ruser); - free(cissuer); - free(chash); + 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(void) +executor_main(struct imsgbuf *ibuf) { struct vhost *vhost; - struct event evs[PROC_MAX]; + struct event evs[PROC_MAX], imsgev; int i; #ifdef __OpenBSD__ @@ -462,9 +289,15 @@ executor_main(void) 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], servpipes[i], EV_READ | EV_PERSIST, - handle_fork_req, NULL); + event_set(&evs[i], servibuf[i].fd, EV_READ | EV_PERSIST, + handle_dispatch_imsg, &servibuf[i]); event_add(&evs[i], NULL); } @@ -25,13 +25,11 @@ #include <signal.h> #include <string.h> -volatile sig_atomic_t hupped; - struct vhost hosts[HOSTSLEN]; -int exfd, logfd, sock4, sock6, servpipes[PROC_MAX]; +int sock4, sock6; -struct imsgbuf logpibuf, logcibuf; +struct imsgbuf logibuf, exibuf, servibuf[PROC_MAX]; const char *config_path, *certs_dir, *hostname; @@ -40,14 +38,6 @@ struct conf conf; struct tls_config *tlsconf; struct tls *ctx; -void -sig_handler(int sig) -{ - (void)sig; - - hupped = sig == SIGHUP; -} - /* XXX: create recursively */ void mkdirs(const char *path) @@ -196,13 +186,12 @@ setup_tls(void) } static int -listener_main(void) +listener_main(struct imsgbuf *ibuf) { drop_priv(); - unblock_signals(); load_default_mime(&conf.mime); load_vhosts(); - loop(ctx, sock4, sock6); + loop(ctx, sock4, sock6, ibuf); return 0; } @@ -320,22 +309,21 @@ logger_init(void) case -1: err(1, "fork"); case 0: - logfd = p[1]; + signal(SIGHUP, SIG_IGN); close(p[0]); setproctitle("logger"); - imsg_init(&logcibuf, p[1]); + imsg_init(&logibuf, p[1]); drop_priv(); - _exit(logger_main(p[1], &logcibuf)); + _exit(logger_main(p[1], &logibuf)); default: - logfd = p[0]; close(p[1]); - imsg_init(&logpibuf, p[0]); + imsg_init(&logibuf, p[0]); return; } } static int -serve(int argc, char **argv) +serve(int argc, char **argv, struct imsgbuf *ibuf) { char path[PATH_MAX]; int i, p[2]; @@ -380,19 +368,18 @@ serve(int argc, char **argv) fatal("fork: %s", strerror(errno)); case 0: /* child */ close(p[0]); - exfd = p[1]; + imsg_init(&exibuf, p[1]); setproctitle("server"); - _exit(listener_main()); + _exit(listener_main(&exibuf)); default: - servpipes[i] = p[0]; close(p[1]); + imsg_init(&servibuf[i], p[0]); } } setproctitle("executor"); drop_priv(); - unblock_signals(); - _exit(executor_main()); + _exit(executor_main(ibuf)); } int @@ -480,12 +467,6 @@ main(int argc, char **argv) signal(SIGPIPE, SIG_IGN); signal(SIGCHLD, SIG_IGN); -#ifdef SIGINFO - signal(SIGINFO, sig_handler); -#endif - signal(SIGUSR2, sig_handler); - signal(SIGHUP, sig_handler); - if (!conf.foreground && !configless) { if (daemon(1, 1) == -1) err(1, "daemon"); @@ -501,25 +482,43 @@ main(int argc, char **argv) if (conf.ipv6) sock6 = make_socket(conf.port, AF_INET6); - if (configless) - return serve(argc, argv); + if (configless) { + serve(argc, argv, NULL); + imsg_compose(&logibuf, IMSG_QUIT, 0, 0, -1, NULL, 0); + imsg_flush(&logibuf); + return 0; + } /* wait a sighup and reload the daemon */ for (;;) { - block_signals(); + struct imsgbuf exibuf; + int p[2]; + + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, + PF_UNSPEC, p) == -1) + fatal("socketpair: %s", strerror(errno)); - hupped = 0; switch (fork()) { case -1: fatal("fork: %s", strerror(errno)); case 0: - return serve(argc, argv); + signal(SIGHUP, SIG_IGN); + close(p[0]); + imsg_init(&exibuf, p[1]); + _exit(serve(argc, argv, &exibuf)); } + close(p[1]); + imsg_init(&exibuf, p[0]); + wait_sighup(); - unblock_signals(); 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]); + old_ipv6 = conf.ipv6; old_port = conf.port; @@ -26,6 +26,8 @@ #include <netinet/in.h> #include <dirent.h> +#include <limits.h> +#include <netdb.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> @@ -116,11 +118,8 @@ struct conf { extern const char *config_path; extern struct conf conf; -extern int exfd, logfd; -extern struct imsgbuf logpibuf, logcibuf; - -extern volatile sig_atomic_t hupped; +extern struct imsgbuf logibuf, exibuf, servibuf[PROC_MAX]; extern int servpipes[PROC_MAX]; @@ -142,6 +141,8 @@ struct parser { struct client; +typedef void (imsg_handlerfn)(struct imsgbuf*, struct imsg*, size_t); + typedef void (*statefn)(int, short, void*); /* @@ -169,6 +170,7 @@ typedef void (*statefn)(int, short, void*); * send_file -> close_conn */ struct client { + int id; struct tls *ctx; char req[GEMINI_URL_LEN]; struct iri iri; @@ -181,7 +183,33 @@ struct client { char sbuf[1024]; ssize_t len, off; struct sockaddr_storage addr; - struct vhost *host; /* host she's talking to */ + struct vhost *host; /* host they're talking to */ +}; + +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]; + time_t notbefore; + time_t notafter; + + size_t host_off; }; enum { @@ -191,8 +219,14 @@ enum { FILE_MISSING, }; +enum imsg_type { + IMSG_CGI_REQ, + IMSG_CGI_RES, + IMSG_LOG, + IMSG_QUIT, +}; + /* gmid.c */ -void sig_handler(int); void mkdirs(const char*); char *data_dir(void); void load_local_cert(const char*, const char*); @@ -243,7 +277,7 @@ int vhost_strip(struct vhost*, const char*); X509_STORE *vhost_require_ca(struct vhost*, const char*); int vhost_disable_log(struct vhost*, const char*); void mark_nonblock(int); -void loop(struct tls*, int, int); +void loop(struct tls*, int, int, struct imsgbuf*); /* ex.c */ int send_string(int, const char*); @@ -257,7 +291,7 @@ int send_time(int, time_t); int recv_time(int, time_t*); int send_fd(int, int); int recv_fd(int); -int executor_main(void); +int executor_main(struct imsgbuf*); /* sandbox.c */ void sandbox(void); @@ -286,5 +320,6 @@ char *xstrdup(const char*); void gen_certificate(const char*, const char*, const char*); X509_STORE *load_ca(const char*); int validate_against_ca(X509_STORE*, const uint8_t*, size_t); +void dispatch_imsg(struct imsgbuf*, imsg_handlerfn**, size_t); #endif @@ -28,9 +28,16 @@ #include <string.h> #include <syslog.h> -static struct event inlog; +static struct event imsgev; -static void handle_log(int, short, void*); +static void handle_imsg_quit(struct imsgbuf*, struct imsg*, size_t); +static void handle_imsg_log(struct imsgbuf*, struct imsg*, size_t); +static void handle_dispatch_imsg(int, short, void*); + +static imsg_handlerfn *handlers[] = { + [IMSG_QUIT] = handle_imsg_quit, + [IMSG_LOG] = handle_imsg_log, +}; void fatal(const char *fmt, ...) @@ -71,9 +78,9 @@ should_log(int priority) static inline void send_log(const char *msg, size_t len) { - imsg_compose(&logpibuf, 0, 0, 0, -1, msg, len); + imsg_compose(&logibuf, IMSG_LOG, 0, 0, -1, msg, len); /* XXX: use event_once() */ - imsg_flush(&logpibuf); + imsg_flush(&logibuf); } static inline void @@ -229,39 +236,30 @@ log_request(struct client *c, char *meta, size_t l) static void -handle_log(int fd, short ev, void *d) +handle_imsg_quit(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen) { - struct imsgbuf *ibuf = d; - struct imsg imsg; - ssize_t n, datalen; - char *msg; - - if ((n = imsg_read(ibuf)) == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) - return; - err(1, "imsg_read"); - } - if (n == 0) - errx(1, "connection lost?"); - - for (;;) { - if ((n = imsg_get(ibuf, &imsg)) == -1) - err(1, "read error"); - if (n == 0) - return; - - datalen = imsg.hdr.len - IMSG_HEADER_SIZE; - msg = imsg.data; - msg[datalen-1] = '\0'; - - /* ignore imsg.hdr.type for now */ - if (conf.foreground) - fprintf(stderr, "%s\n", msg); - else - syslog(LOG_DAEMON, "%s", msg); + event_loopbreak(); +} - imsg_free(&imsg); - } +static void +handle_imsg_log(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen) +{ + char *msg; + + msg = imsg->data; + msg[datalen-1] = '\0'; + + if (conf.foreground) + fprintf(stderr, "%s\n", msg); + else + syslog(LOG_DAEMON, "%s", msg); +} + +static void +handle_dispatch_imsg(int fd, short ev, void *d) +{ + struct imsgbuf *ibuf = d; + dispatch_imsg(ibuf, handlers, sizeof(handlers)); } int @@ -269,8 +267,8 @@ logger_main(int fd, struct imsgbuf *ibuf) { event_init(); - event_set(&inlog, fd, EV_READ | EV_PERSIST, &handle_log, ibuf); - event_add(&inlog, NULL); + event_set(&imsgev, fd, EV_READ | EV_PERSIST, &handle_dispatch_imsg, ibuf); + event_add(&imsgev, NULL); #ifdef __OpenBSD__ if (pledge("stdio", NULL) == -1) diff --git a/regress/puny-test.c b/regress/puny-test.c index 1d69261..2397e9a 100644 --- a/regress/puny-test.c +++ b/regress/puny-test.c @@ -21,7 +21,7 @@ /* to make the linker happy */ struct conf conf; -struct imsgbuf logpibuf, logcibuf; +struct imsgbuf logibuf, servibuf[PROC_MAX]; struct suite { const char *src; @@ -18,8 +18,6 @@ #include <sys/stat.h> -#include <netdb.h> - #include <assert.h> #include <errno.h> #include <event.h> @@ -28,12 +26,10 @@ #include <limits.h> #include <string.h> -struct server { - struct client clients[MAX_USERS]; - struct tls *ctx; -}; +static struct client clients[MAX_USERS]; +static struct tls *ctx; -static struct event e4, e6, sighup, siginfo, sigusr2; +static struct event e4, e6, imsgev, siginfo, sigusr2; static int has_ipv6, has_siginfo; int connected_clients; @@ -65,7 +61,15 @@ static void handle_cgi_reply(int, short, void*); static void handle_copy(int, short, void*); static void close_conn(int, short, void*); static void do_accept(int, short, void*); -static void handle_sighup(int, short, void*); +struct client *client_by_id(int); +static void handle_imsg_cgi_res(struct imsgbuf*, struct imsg*, size_t); +static void handle_imsg_quit(struct imsgbuf*, struct imsg*, size_t); +static void handle_siginfo(int, short, void*); + +static imsg_handlerfn *handlers[] = { + [IMSG_QUIT] = handle_imsg_quit, + [IMSG_CGI_RES] = handle_imsg_cgi_res, +}; static inline int matches(const char *pattern, const char *path) @@ -633,6 +637,8 @@ 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; e = getnameinfo((struct sockaddr*)&c->addr, sizeof(c->addr), @@ -640,32 +646,41 @@ start_cgi(const char *spath, const char *relpath, struct client *c) NULL, 0, NI_NUMERICHOST); if (e != 0) - goto err; + fatal("getnameinfo failed"); - if (!send_iri(exfd, &c->iri) - || !send_string(exfd, spath) - || !send_string(exfd, relpath) - || !send_string(exfd, addr) - || !send_string(exfd, tls_peer_cert_subject(c->ctx)) - || !send_string(exfd, tls_peer_cert_issuer(c->ctx)) - || !send_string(exfd, tls_peer_cert_hash(c->ctx)) - || !send_time(exfd, tls_peer_cert_notbefore(c->ctx)) - || !send_time(exfd, tls_peer_cert_notafter(c->ctx)) - || !send_vhost(exfd, c->host)) - goto err; + memset(&req, 0, sizeof(req)); - close(c->pfd); - if ((c->pfd = recv_fd(exfd)) == -1) { - start_reply(c, TEMP_FAILURE, "internal server error"); - return; - } + memcpy(req.buf, c->req, sizeof(req.buf)); - reschedule_read(c->pfd, c, &handle_cgi_reply); - return; + 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; -err: - /* fatal("cannot talk to the executor process: %s", strerror(errno)); */ - fatal("cannot talk to the executor process"); + 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)); + + req.notbefore = tls_peer_cert_notbefore(c->ctx); + req.notafter = tls_peer_cert_notafter(c->ctx); + + req.host_off = c->host - hosts; + + imsg_compose(&exibuf, IMSG_CGI_REQ, c->id, 0, -1, &req, sizeof(req)); + imsg_flush(&exibuf); + + close(c->pfd); } static void @@ -986,7 +1001,6 @@ static void do_accept(int sock, short et, void *d) { struct client *c; - struct server *s = d; struct sockaddr_storage addr; struct sockaddr *saddr; socklen_t len; @@ -994,7 +1008,6 @@ do_accept(int sock, short et, void *d) (void)et; - saddr = (struct sockaddr*)&addr; len = sizeof(addr); if ((fd = accept(sock, saddr, &len)) == -1) { @@ -1006,10 +1019,11 @@ do_accept(int sock, short et, void *d) mark_nonblock(fd); for (i = 0; i < MAX_USERS; ++i) { - c = &s->clients[i]; + c = &clients[i]; if (c->fd == -1) { memset(c, 0, sizeof(*c)); - if (tls_accept_socket(s->ctx, &c->ctx, fd) == -1) + c->id = i; + if (tls_accept_socket(ctx, &c->ctx, fd) == -1) break; /* goodbye fd! */ c->fd = fd; @@ -1026,19 +1040,50 @@ do_accept(int sock, short et, void *d) close(fd); } +struct client * +client_by_id(int id) +{ + if ((size_t)id > sizeof(clients)/sizeof(clients[0])) + fatal("in client_by_id: invalid id %d", id); + return &clients[id]; +} + static void -handle_sighup(int fd, short ev, void *d) +handle_imsg_cgi_res(struct imsgbuf *ibuf, struct imsg *imsg, size_t len) { - (void)fd; - (void)ev; + struct client *c; + + c = client_by_id(imsg->hdr.peerid); + + if ((c->pfd = imsg->fd) == -1) + start_reply(c, TEMP_FAILURE, "internal server error"); + else + reschedule_read(c->pfd, c, &handle_cgi_reply); +} + +static void +handle_imsg_quit(struct imsgbuf *ibuf, struct imsg *imsg, size_t len) +{ + (void)imsg; + (void)len; + + /* don't call event_loopbreak since we want to finish to + * handle the ongoing connections. */ event_del(&e4); if (has_ipv6) event_del(&e6); if (has_siginfo) signal_del(&siginfo); + event_del(&imsgev); signal_del(&sigusr2); - signal_del(&sighup); +} + +static void +handle_dispatch_imsg(int fd, short ev, void *d) +{ + struct imsgbuf *ibuf = d; + dispatch_imsg(ibuf, handlers, sizeof(handlers)); } static void @@ -1052,28 +1097,29 @@ handle_siginfo(int fd, short ev, void *d) } void -loop(struct tls *ctx, int sock4, int sock6) +loop(struct tls *ctx_, int sock4, int sock6, struct imsgbuf *ibuf) { - struct server server; size_t i; + ctx = ctx_; + event_init(); - memset(&server, 0, sizeof(server)); + memset(&clients, 0, sizeof(clients)); for (i = 0; i < MAX_USERS; ++i) - server.clients[i].fd = -1; + clients[i].fd = -1; - event_set(&e4, sock4, EV_READ | EV_PERSIST, &do_accept, &server); + event_set(&e4, sock4, EV_READ | EV_PERSIST, &do_accept, NULL); event_add(&e4, NULL); if (sock6 != -1) { has_ipv6 = 1; - event_set(&e6, sock6, EV_READ | EV_PERSIST, &do_accept, &server); + event_set(&e6, sock6, EV_READ | EV_PERSIST, &do_accept, NULL); event_add(&e6, NULL); } - signal_set(&sighup, SIGHUP, &handle_sighup, NULL); - signal_add(&sighup, NULL); + event_set(&imsgev, ibuf->fd, EV_READ | EV_PERSIST, handle_dispatch_imsg, ibuf); + event_add(&imsgev, NULL); #ifdef SIGINFO has_siginfo = 1; @@ -1083,8 +1129,6 @@ loop(struct tls *ctx, int sock4, int sock6) signal_set(&sigusr2, SIGUSR2, &handle_siginfo, NULL); signal_add(&sigusr2, NULL); - server.ctx = ctx; - sandbox(); event_dispatch(); _exit(0); @@ -24,24 +24,6 @@ #include <openssl/x509_vfy.h> #include <openssl/x509v3.h> -static sigset_t set; - -void -block_signals(void) -{ - sigset_t new; - - sigemptyset(&new); - sigaddset(&new, SIGHUP); - sigprocmask(SIG_BLOCK, &new, &set); -} - -void -unblock_signals(void) -{ - sigprocmask(SIG_SETMASK, &set, NULL); -} - int starts_with(const char *str, const char *prefix) { @@ -248,3 +230,33 @@ end: X509_STORE_CTX_free(ctx); return ret; } + +void +dispatch_imsg(struct imsgbuf *ibuf, imsg_handlerfn **handlers, size_t size) +{ + struct imsg imsg; + size_t datalen, i; + ssize_t n; + + if ((n = imsg_read(ibuf)) == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) + return; + _exit(1); + } + + if (n == 0) + _exit(1); + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + _exit(1); + if (n == 0) + return; + datalen = imsg.hdr.len - IMSG_HEADER_SIZE; + i = imsg.hdr.type; + if (i > (size / sizeof(imsg_handlerfn*)) || handlers[i] == NULL) + abort(); + handlers[i](ibuf, &imsg, datalen); + imsg_free(&imsg); + } +} |