summaryrefslogtreecommitdiff
path: root/tunnel.c
diff options
context:
space:
mode:
Diffstat (limited to 'tunnel.c')
-rw-r--r--tunnel.c116
1 files changed, 116 insertions, 0 deletions
diff --git a/tunnel.c b/tunnel.c
new file mode 100644
index 0000000..56bdcdd
--- /dev/null
+++ b/tunnel.c
@@ -0,0 +1,116 @@
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+
+#include <stddef.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <err.h>
+
+#include "tunnel.h"
+#include "macro.h"
+
+int tunnel_socket(struct sockaddr **sa, socklen_t *salen)
+{
+ int sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+ if (sock < 0) {
+ warn("socket");
+ return -1;
+ }
+
+ struct sockaddr_un sun = {
+ .sun_family = AF_UNIX
+ };
+
+ const char *path = TUNNEL_SOCK_PATH;
+
+ int n = snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", path);
+ socklen_t len = offsetof(struct sockaddr_un, sun_path) + n + 1;
+
+ if (len > sizeof(sun)) {
+ warnx("too large unix path");
+ close(sock);
+ return -1;
+ }
+
+ *sa = _copy(&sun, len);
+ *salen = len;
+
+ return sock;
+}
+
+int tunnel_listen(int sock, struct sockaddr *sa, socklen_t salen)
+{
+ if (bind(sock, sa, salen) < 0) {
+ warn("bind");
+ return -1;
+ }
+
+ if (listen(sock, 0) < 0) {
+ warn("listen");
+ return -1;
+ }
+
+ return 0;
+}
+
+int tunnel_server(void)
+{
+ struct sockaddr *sa = NULL;
+ socklen_t salen = 0;
+
+ int sock = tunnel_socket(&sa, &salen);
+ if (sock < 0)
+ return -1;
+
+ defer {
+ close(sock);
+ free(sa);
+ }
+
+ if (tunnel_listen(sock, sa, salen) < 0)
+ return -1;
+
+ if (tunnel_daemon(sock) < 0)
+ return -1;
+
+ return 0;
+}
+
+int tunnel_connect(int sock, struct sockaddr *sa, socklen_t salen)
+{
+ if (connect(sock, sa, salen) < 0) {
+ if (errno == ENOENT) {
+ if (tunnel_server() < 0)
+ return -1;
+ }
+
+ if (errno != ENOENT || connect(sock, sa, salen) < 0) {
+ warn("connect");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int tunnel_client(void)
+{
+ struct sockaddr *sa = NULL;
+ socklen_t salen = 0;
+
+ int sock = tunnel_socket(&sa, &salen);
+ if (sock < 0)
+ return -1;
+
+ defer { free(sa); }
+
+ if (tunnel_connect(sock, sa, salen) < 0) {
+ close(sock);
+ return -1;
+ }
+
+ return sock;
+}