diff options
Diffstat (limited to 'dispatch.c')
-rw-r--r-- | dispatch.c | 207 |
1 files changed, 84 insertions, 123 deletions
diff --git a/dispatch.c b/dispatch.c index c558729..6353686 100644 --- a/dispatch.c +++ b/dispatch.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 C. McEnroe <june@causal.agency> +/* Copyright (C) 2019 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -12,8 +12,20 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this Program, or any covered work, by linking or + * combining it with OpenSSL (or a modified version of that library), + * containing parts covered by the terms of the OpenSSL License and the + * original SSLeay license, the licensors of this Program grant you + * additional permission to convey the resulting work. Corresponding + * Source for a non-source form of such a combination shall include the + * source code for the parts of OpenSSL used as well as that of the + * covered work. */ +#include <assert.h> #include <err.h> #include <fcntl.h> #include <netdb.h> @@ -29,55 +41,6 @@ #include <sysexits.h> #include <unistd.h> -#ifdef __FreeBSD__ -#include <sys/capsicum.h> -#endif - -#include "compat.h" - -static struct { - struct pollfd *ptr; - size_t len, cap; -} event; - -static void eventAdd(int fd) { - if (event.len == event.cap) { - event.cap = (event.cap ? event.cap * 2 : 8); - event.ptr = realloc(event.ptr, sizeof(*event.ptr) * event.cap); - if (!event.ptr) err(EX_OSERR, "malloc"); - } - event.ptr[event.len++] = (struct pollfd) { - .fd = fd, - .events = POLLIN, - }; -} - -static void eventRemove(size_t i) { - close(event.ptr[i].fd); - event.ptr[i] = event.ptr[--event.len]; -} - -static ssize_t sendfd(int sock, int fd) { - char buf[CMSG_SPACE(sizeof(int))]; - - char x = 0; - struct iovec iov = { .iov_base = &x, .iov_len = 1 }; - struct msghdr msg = { - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = buf, - .msg_controllen = sizeof(buf), - }; - - struct cmsghdr *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) = fd; - - return sendmsg(sock, &msg, 0); -} - static struct { uint8_t buf[4096]; uint8_t *ptr; @@ -112,7 +75,9 @@ static char *serverName(void) { skip(uint8()); skip(uint16()); skip(uint8()); - peek.len = uint16(); + uint16_t len = uint16(); + if (len > peek.len) return NULL; + peek.len = len; while (peek.len) { // Extension uint16_t type = uint16(); @@ -145,14 +110,36 @@ static void alert(int sock) { if (len < 0) warn("send"); } +static ssize_t sendfd(int sock, int fd) { + char buf[CMSG_SPACE(sizeof(int))]; + + char x = 0; + struct iovec iov = { .iov_base = &x, .iov_len = 1 }; + struct msghdr msg = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = buf, + .msg_controllen = sizeof(buf), + }; + + struct cmsghdr *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) = fd; + + return sendmsg(sock, &msg, 0); +} + int main(int argc, char *argv[]) { + int error; + const char *host = "localhost"; const char *port = "6697"; const char *path = NULL; int timeout = 1000; - int opt; - while (0 < (opt = getopt(argc, argv, "H:P:t:"))) { + for (int opt; 0 < (opt = getopt(argc, argv, "H:P:t:"));) { switch (opt) { break; case 'H': host = optarg; break; case 'P': port = optarg; @@ -170,12 +157,21 @@ int main(int argc, char *argv[]) { errx(EX_USAGE, "directory required"); } - int dir = open(path, O_DIRECTORY); - if (dir < 0) err(EX_NOINPUT, "%s", path); +#ifdef __OpenBSD__ + error = unveil(path, "rw"); + if (error) err(EX_OSERR, "unveil"); - int error = fchdir(dir); + error = pledge("stdio rpath inet unix dns sendfd", NULL); + if (error) err(EX_OSERR, "pledge"); +#endif + + error = chdir(path); if (error) err(EX_NOINPUT, "%s", path); + enum { Cap = 1024 }; + struct pollfd fds[Cap]; + size_t nfds = 0; + struct addrinfo *head; struct addrinfo hints = { .ai_family = AF_UNSPEC, @@ -186,7 +182,7 @@ int main(int argc, char *argv[]) { if (error) errx(EX_NOHOST, "%s:%s: %s", host, port, gai_strerror(error)); size_t binds = 0; - for (struct addrinfo *ai = head; ai; ai = ai->ai_next) { + for (struct addrinfo *ai = head; ai && binds < Cap - 1; ai = ai->ai_next) { int sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sock < 0) err(EX_OSERR, "socket"); @@ -203,122 +199,87 @@ int main(int argc, char *argv[]) { continue; } - eventAdd(sock); + fds[nfds++] = (struct pollfd) { .fd = sock, .events = POLLIN }; binds++; } if (!binds) errx(EX_UNAVAILABLE, "could not bind any sockets"); freeaddrinfo(head); -#ifdef __FreeBSD__ - error = cap_enter(); - if (error) err(EX_OSERR, "cap_enter"); - - cap_rights_t dirRights, sockRights, unixRights, bindRights; - cap_rights_init(&dirRights, CAP_CONNECTAT); - cap_rights_init(&sockRights, CAP_EVENT, CAP_RECV, CAP_SEND, CAP_SETSOCKOPT); - cap_rights_init(&unixRights, CAP_CONNECT, CAP_SEND); - cap_rights_init(&bindRights, CAP_LISTEN, CAP_ACCEPT); - cap_rights_merge(&bindRights, &sockRights); - - error = cap_rights_limit(dir, &dirRights); - if (error) err(EX_OSERR, "cap_rights_limit"); for (size_t i = 0; i < binds; ++i) { - error = cap_rights_limit(event.ptr[i].fd, &bindRights); - if (error) err(EX_OSERR, "cap_rights_limit"); - } -#endif - - for (size_t i = 0; i < binds; ++i) { - error = listen(event.ptr[i].fd, 1); + error = listen(fds[i].fd, -1); if (error) err(EX_IOERR, "listen"); } signal(SIGPIPE, SIG_IGN); for (;;) { - int nfds = poll( - event.ptr, event.len, (event.len > binds ? timeout : -1) - ); - if (nfds < 0) err(EX_IOERR, "poll"); + for (size_t i = 0; i < binds; ++i) { + fds[i].events = (nfds < Cap ? POLLIN : 0); + } + + int ready = poll(fds, nfds, (nfds > binds ? timeout : -1)); + if (ready < 0) err(EX_IOERR, "poll"); - if (!nfds) { - for (size_t i = event.len - 1; i >= binds; --i) { - eventRemove(i); + if (!ready) { + for (size_t i = binds; i < nfds; ++i) { + close(fds[i].fd); } + nfds = binds; continue; } - for (size_t i = event.len - 1; i < event.len; --i) { - if (!event.ptr[i].revents) continue; + for (size_t i = nfds - 1; i < nfds; --i) { + if (!fds[i].revents) continue; if (i < binds) { - int sock = accept(event.ptr[i].fd, NULL, NULL); + int sock = accept(fds[i].fd, NULL, NULL); if (sock < 0) { warn("accept"); continue; } - - int yes = 1; - error = setsockopt( - sock, SOL_SOCKET, SO_NOSIGPIPE, &yes, sizeof(yes) - ); - if (error) err(EX_OSERR, "setsockopt"); - - eventAdd(sock); + assert(nfds < Cap); + fds[nfds++] = (struct pollfd) { .fd = sock, .events = POLLIN }; continue; } - if (event.ptr[i].revents & (POLLHUP | POLLERR)) { - eventRemove(i); - continue; - } + if (fds[i].revents & (POLLHUP | POLLERR)) goto remove; ssize_t len = recv( - event.ptr[i].fd, peek.buf, sizeof(peek.buf) - 1, MSG_PEEK + fds[i].fd, peek.buf, sizeof(peek.buf) - 1, MSG_PEEK ); if (len < 0) { warn("recv"); - eventRemove(i); - continue; + goto remove; } peek.len = len; char *name = serverName(); - if (!name || name[0] == '.' || name[0] == '/') { - alert(event.ptr[i].fd); - eventRemove(i); - continue; + if (!name || name[0] == '.' || strchr(name, '/')) { + alert(fds[i].fd); + goto remove; } struct sockaddr_un addr = { .sun_family = AF_UNIX }; - strlcpy(addr.sun_path, name, sizeof(addr.sun_path)); + snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", name); int sock = socket(PF_UNIX, SOCK_STREAM, 0); if (sock < 0) err(EX_OSERR, "socket"); -#ifdef __FreeBSD__ - error = cap_rights_limit(sock, &unixRights); - if (error) err(EX_OSERR, "cap_rights_limit"); - - error = connectat( - dir, sock, (struct sockaddr *)&addr, SUN_LEN(&addr) - ); -#else error = connect(sock, (struct sockaddr *)&addr, SUN_LEN(&addr)); -#endif - if (error) { warn("%s", name); - alert(event.ptr[i].fd); + alert(fds[i].fd); } else { - len = sendfd(sock, event.ptr[i].fd); + len = sendfd(sock, fds[i].fd); if (len < 0) { warn("%s", name); - alert(event.ptr[i].fd); + alert(fds[i].fd); } } - close(sock); - eventRemove(i); + +remove: + close(fds[i].fd); + fds[i] = fds[--nfds]; } } } |