summary refs log tree commit diff
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2019-11-02 04:06:41 -0400
committerJune McEnroe <june@causal.agency>2019-11-02 04:06:41 -0400
commitb6ad43c7e76da131f284a6af5a210a77ba08c610 (patch)
tree709248de1f719ec1cdb3b3123290cff6c4004770
parentDon't try to sendfd if connect failed (diff)
downloadpounce-b6ad43c7e76da131f284a6af5a210a77ba08c610.tar.gz
pounce-b6ad43c7e76da131f284a6af5a210a77ba08c610.zip
Implement UNIX-domain binding
-rw-r--r--bounce.c9
-rw-r--r--bounce.h1
-rw-r--r--listen.c58
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 <err.h>
+#include <errno.h>
 #include <netdb.h>
 #include <netinet/in.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
+#include <sys/un.h>
 #include <sysexits.h>
 #include <tls.h>
 #include <unistd.h>
@@ -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");