From b6ad43c7e76da131f284a6af5a210a77ba08c610 Mon Sep 17 00:00:00 2001 From: "C. McEnroe" Date: Sat, 2 Nov 2019 04:06:41 -0400 Subject: Implement UNIX-domain binding --- bounce.c | 9 +++++++-- bounce.h | 1 + listen.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/bounce.c b/bounce.c index 06340fd..a3a9693 100644 --- a/bounce.c +++ b/bounce.c @@ -172,6 +172,7 @@ static void signalHandler(int signal) { int main(int argc, char *argv[]) { const char *bindHost = "localhost"; const char *bindPort = "6697"; + const char *bindUnix = NULL; char certPath[PATH_MAX] = ""; char privPath[PATH_MAX] = ""; const char *save = NULL; @@ -189,7 +190,7 @@ int main(int argc, char *argv[]) { const char *away = "pounced :3"; const char *quit = "connection reset by purr"; - const char *Opts = "!A:C:H:K:NP:Q:W:a:f:h:j:n:p:r:s:u:vw:"; + const char *Opts = "!A:C:H:K:NP:Q:U:W:a:f:h:j:n:p:r:s:u:vw:"; const struct option LongOpts[] = { { "insecure", no_argument, NULL, '!' }, { "away", required_argument, NULL, 'A' }, @@ -199,6 +200,7 @@ int main(int argc, char *argv[]) { { "names", no_argument, NULL, 'N' }, { "bind-port", required_argument, NULL, 'P' }, { "quit", required_argument, NULL, 'Q' }, + { "bind-unix", required_argument, NULL, 'U' }, { "client-pass", required_argument, NULL, 'W' }, { "sasl", required_argument, NULL, 'a' }, { "save", required_argument, NULL, 'f' }, @@ -225,6 +227,7 @@ int main(int argc, char *argv[]) { break; case 'N': stateJoinNames = true; break; case 'P': bindPort = optarg; break; case 'Q': quit = optarg; + break; case 'U': bindUnix = optarg; break; case 'W': clientPass = optarg; break; case 'a': auth = optarg; break; case 'f': save = optarg; @@ -271,7 +274,9 @@ int main(int argc, char *argv[]) { fclose(priv); int bind[8]; - size_t binds = listenBind(bind, 8, bindHost, bindPort); + size_t binds = bindUnix + ? listenUnix(bind, ARRAY_LEN(bind), bindUnix) + : listenBind(bind, ARRAY_LEN(bind), bindHost, bindPort); int server = serverConnect(insecure, host, port); #ifdef __FreeBSD__ diff --git a/bounce.h b/bounce.h index 60a6795..d07c2cb 100644 --- a/bounce.h +++ b/bounce.h @@ -76,6 +76,7 @@ void ringLoad(FILE *file); void listenConfig(FILE *cert, FILE *priv); size_t listenBind(int fds[], size_t cap, const char *host, const char *port); +size_t listenUnix(int fds[], size_t cap, const char *path); struct tls *listenAccept(int *fd, int bind); int serverConnect(bool insecure, const char *host, const char *port); diff --git a/listen.c b/listen.c index f943ff2..635e7c6 100644 --- a/listen.c +++ b/listen.c @@ -15,12 +15,15 @@ */ #include +#include #include #include +#include #include #include #include #include +#include #include #include #include @@ -105,10 +108,65 @@ size_t listenBind(int fds[], size_t cap, const char *host, const char *port) { return len; } +static bool unix; + +size_t listenUnix(int fds[], size_t cap, const char *path) { + if (!cap) return 0; + + int sock = socket(PF_UNIX, SOCK_STREAM, 0); + if (sock < 0) err(EX_OSERR, "socket"); + + struct sockaddr_un addr = { .sun_family = AF_UNIX }; + if (strlen(path) > sizeof(addr.sun_path)) { + errx(EX_CONFIG, "path too long: %s", path); + } + strncpy(addr.sun_path, path, sizeof(addr.sun_path)); + + // FIXME: unlinkat atexit. + int error = unlink(path); + if (error && errno != ENOENT) err(EX_UNAVAILABLE, "%s", path); + + error = bind(sock, (struct sockaddr *)&addr, SUN_LEN(&addr)); + if (error) err(EX_UNAVAILABLE, "%s", path); + + unix = true; + fds[0] = sock; + return 1; +} + +static int recvfd(int sock) { + size_t len = CMSG_SPACE(sizeof(int)); + char buf[len]; + + char x; + struct iovec iov = { .iov_base = &x, .iov_len = 1 }; + struct msghdr msg = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = buf, + .msg_controllen = len, + }; + if (0 > recvmsg(sock, &msg, 0)) return -1; + + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + if (!cmsg || cmsg->cmsg_type != SCM_RIGHTS) { + errno = ENOMSG; + return -1; + } + return *(int *)CMSG_DATA(cmsg); +} + struct tls *listenAccept(int *fd, int bind) { *fd = accept(bind, NULL, NULL); if (*fd < 0) err(EX_IOERR, "accept"); + if (unix) { + int sent = recvfd(*fd); + if (sent < 0) err(EX_IOERR, "recvfd"); + close(*fd); + *fd = sent; + } + int yes = 1; int error = setsockopt(*fd, SOL_SOCKET, SO_NOSIGPIPE, &yes, sizeof(yes)); if (error) err(EX_OSERR, "setsockopt"); -- cgit 1.4.1