refactoring: imsg everywhere

use imsg to handle ALL kinds of IPC in gmid.  This simplifies and shorten the
code, and  makes everything more uniform too.
This commit is contained in:
Omar Polo 2021-03-19 19:21:29 +00:00
parent 1fbac5ba7c
commit bc99d868bc
7 changed files with 320 additions and 399 deletions

333
ex.c
View File

@ -27,199 +27,14 @@
#include <stdarg.h>
#include <string.h>
int
send_string(int fd, const char *str)
{
ssize_t len;
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*);
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);
}
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;
struct cgireq req;
struct iri iri;
int fd;
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, &notbefore)
|| !recv_time(fd, &notafter)
|| !recv_vhost(fd, &vhost)) {
if (errno == EINTR) {
event_loopbreak();
return;
}
fatal("failure in handling fork request: %s", strerror(errno));
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);
event_loopbreak();
}
free_recvd_iri(&iri);
free(spath);
free(relpath);
free(addr);
free(ruser);
free(cissuer);
free(chash);
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);
}

75
gmid.c
View File

@ -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;

51
gmid.h
View File

@ -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

66
log.c
View File

@ -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;
event_loopbreak();
}
if ((n = imsg_read(ibuf)) == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK)
return;
err(1, "imsg_read");
}
if (n == 0)
errx(1, "connection lost?");
static void
handle_imsg_log(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen)
{
char *msg;
for (;;) {
if ((n = imsg_get(ibuf, &imsg)) == -1)
err(1, "read error");
if (n == 0)
return;
msg = imsg->data;
msg[datalen-1] = '\0';
datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
msg = imsg.data;
msg[datalen-1] = '\0';
if (conf.foreground)
fprintf(stderr, "%s\n", msg);
else
syslog(LOG_DAEMON, "%s", msg);
}
/* ignore imsg.hdr.type for now */
if (conf.foreground)
fprintf(stderr, "%s\n", msg);
else
syslog(LOG_DAEMON, "%s", msg);
imsg_free(&imsg);
}
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)

View File

@ -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;

144
server.c
View File

@ -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));
memcpy(req.buf, c->req, sizeof(req.buf));
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));
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);
if ((c->pfd = recv_fd(exfd)) == -1) {
start_reply(c, TEMP_FAILURE, "internal server error");
return;
}
reschedule_read(c->pfd, c, &handle_cgi_reply);
return;
err:
/* fatal("cannot talk to the executor process: %s", strerror(errno)); */
fatal("cannot talk to the executor process");
}
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);
}
static void
handle_sighup(int fd, short ev, void *d)
struct client *
client_by_id(int id)
{
(void)fd;
(void)ev;
if ((size_t)id > sizeof(clients)/sizeof(clients[0]))
fatal("in client_by_id: invalid id %d", id);
return &clients[id];
}
static void
handle_imsg_cgi_res(struct imsgbuf *ibuf, struct imsg *imsg, size_t len)
{
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);

48
utils.c
View File

@ -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);
}
}