1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <stdbool.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"
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);
}
|