#include #include #include #include #include #include #include #include #include #include #include "tunnel.h" #include "macro.h" struct socket { int fd; struct sockaddr *sa; socklen_t salen; }; static bool socket_connect(struct socket *sock) { if (connect(sock->fd, sock->sa, sock->salen) < 0) return false; return true; } static void socket_free(struct socket *sock) { if (sock) { if (sock->fd >= 0) close(sock->fd); free(sock->sa); free(sock); } } static int socket_detach_fd(struct socket *sock) { int fd = sock->fd; sock->fd = -1; return fd; } struct socket *tunnel_socket(void) { int fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); if (fd < 0) { warn("socket"); return NULL; } 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(fd); return NULL; } struct socket *sock = _new(struct socket, .fd = fd, .salen = len, .sa = _copy(&sun, len), ); return sock; } bool tunnel_listen(struct socket *sock) { if (bind(sock->fd, sock->sa, sock->salen) < 0) { warn("bind"); return false; } if (listen(sock->fd, 0) < 0) { warn("listen"); return false; } return true; } bool tunnel_server(void) { struct socket *sock = tunnel_socket(); if (!sock) return false; defer { socket_free(sock); } if (! tunnel_listen(sock)) return false; if (! tunnel_daemon(sock->fd)) return false; return true; } bool tunnel_connect(struct socket *sock) { if (! socket_connect(sock)) { if (errno == ENOENT) { if (! tunnel_server()) return false; } if (errno != ENOENT || ! socket_connect(sock)) { warn("connect"); return false; } } return true; } int tunnel_client(void) { struct socket *sock = tunnel_socket(); if (! sock) return -1; defer { socket_free(sock); } if (! tunnel_connect(sock)) return -1; return socket_detach_fd(sock); }