teed.c - teed - A multiplex relay tee(1) daemon.
HTML git clone git://bitreich.org/teed git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/teed
DIR Log
DIR Files
DIR Refs
DIR Tags
DIR README
DIR LICENSE
---
teed.c (5223B)
---
1 /*
2 * See LICENSE file for license information.
3 *
4 * Copy me if you can.
5 * by 20h
6 */
7
8 #include <unistd.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/socket.h>
13 #include <sys/un.h>
14 #include <signal.h>
15 #include <errno.h>
16 #include <sys/stat.h>
17 #include <sys/select.h>
18
19 volatile sig_atomic_t isrunning = 1;
20
21 void *
22 memdup(void *p, int l)
23 {
24 char *ret;
25
26 ret = calloc(1, l);
27 if (ret == NULL) {
28 perror("calloc");
29 exit(1);
30 }
31 memcpy(ret, p, l);
32
33 return (void *)ret;
34 }
35
36 typedef struct llist llist;
37 struct llist {
38 llist *prev;
39 llist *next;
40 int dlen;
41 void *data;
42 };
43
44 #define forllist(list, elem) for (elem = list;\
45 elem; elem = elem->next)
46
47 void
48 closeallfds(llist *fds)
49 {
50 llist *e;
51
52 forllist(fds, e)
53 close(*(int *)e->data);
54 }
55
56 void
57 llist_free1(llist *l)
58 {
59 if (l == NULL)
60 return;
61 free(l->data);
62 l->data = NULL;
63 l->next = NULL;
64 l->prev = NULL;
65 free(l);
66 }
67
68 void
69 llist_free(llist *l)
70 {
71 llist *tl;
72
73 while (l != NULL) {
74 tl = l->next;
75 llist_free1(l);
76 l = tl;
77 }
78 }
79
80 llist *
81 llist_new(llist *prev, llist *next, void *data, int len)
82 {
83 llist *ret;
84
85 ret = calloc(1, sizeof(llist));
86 if (ret == NULL) {
87 perror("calloc");
88 exit(1);
89 }
90 ret->prev = prev;
91 ret->next = next;
92 if (data != NULL) {
93 ret->data = memdup(data, len);
94 ret->dlen = len;
95 } else {
96 ret->data = NULL;
97 ret->dlen = 0;
98 }
99
100 return ret;
101 }
102
103 int
104 llist_len(llist *l)
105 {
106 int llistl = 0;
107 for (;l != NULL; l = l->next)
108 llistl++;
109 return llistl;
110 }
111
112 llist *
113 llist_put(llist *l, void *data, int len)
114 {
115 llist *ol;
116
117 if (l == NULL)
118 return llist_new(NULL, NULL, data, len);
119
120 ol = l;
121 for (; l->next; l = l->next);
122 l->next = llist_new(l, NULL, data, len);
123
124 return ol;
125 }
126
127 llist *
128 llist_del(llist **l, llist *e)
129 {
130 llist *enext, *eprev;
131
132 enext = e->next;
133 eprev = e->prev;
134 if (enext != NULL)
135 enext->prev = eprev;
136 if (eprev != NULL)
137 eprev->next = enext;
138 llist_free1(e);
139
140 if (*l == e)
141 *l = enext;
142
143 if (eprev != NULL)
144 return eprev;
145 if (enext != NULL)
146 return enext;
147 return NULL;
148 }
149
150 void
151 sighandler(int signo)
152 {
153 switch (signo) {
154 case SIGINT:
155 case SIGTERM:
156 isrunning = 0;
157 break;
158 }
159 }
160
161 void
162 initsignals(void)
163 {
164 signal(SIGPIPE, SIG_IGN);
165 signal(SIGINT, sighandler);
166 signal(SIGTERM, sighandler);
167 }
168
169 int
170 main(int argc, char *argv[])
171 {
172 int on, i, maxsfd, afd, rval, recvl,
173 sendl, sent, lfd;
174 struct sockaddr_un saddrs[2], clt;
175 char *bindpaths[2] = {"in", "out"},
176 recvbuf[4*1024],
177 *sendbufi;
178 llist *wfds, *rfds, *lfds, *e, *qe;
179 socklen_t saddrlen, cltlen;
180 fd_set fdset;
181 struct timespec timeout;
182
183 initsignals();
184
185 rval = 1;
186 rfds = NULL;
187 wfds = NULL;
188 lfds = NULL;
189
190 on = 1;
191 for (i = 0; i < 2; i++) {
192 lfd = socket(AF_UNIX, SOCK_STREAM, 0);
193 if (lfd < 0) {
194 perror("socket");
195 goto stop_serving;
196 }
197 if (setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR,
198 &on, sizeof(on)) < 0) {
199 perror("setsockopt");
200 goto stop_serving;
201 }
202
203 if (access(bindpaths[i], F_OK) == 0) {
204 if (remove(bindpaths[i]) < 0) {
205 perror("remove");
206 goto stop_serving;
207 }
208 }
209
210 saddrs[i].sun_family = AF_UNIX;
211 strncpy(saddrs[i].sun_path, bindpaths[i],
212 sizeof(bindpaths[i])-1);
213 saddrlen = sizeof(saddrs[i]);
214
215 if (bind(lfd, (struct sockaddr *)&saddrs[i], saddrlen) < 0) {
216 perror("bind");
217 goto stop_serving;
218 }
219
220 if (chmod(bindpaths[i], 0775) < 0) {
221 perror("chmod");
222 goto stop_serving;
223 }
224
225 if (listen(lfd, 255) < 0) {
226 perror("listen");
227 goto stop_serving;
228 }
229
230 lfds = llist_put(lfds, &lfd, sizeof(lfd));
231 }
232
233 timeout.tv_sec = 1;
234 timeout.tv_nsec = 0;
235 for (;isrunning;) {
236 maxsfd = 0;
237 FD_ZERO(&fdset);
238 forllist(lfds, e) {
239 FD_SET(*(int *)e->data, &fdset);
240 if (*(int *)e->data > maxsfd)
241 maxsfd = *(int *)e->data;
242 }
243 forllist(rfds, e) {
244 FD_SET(*(int *)e->data, &fdset);
245 if (*(int *)e->data > maxsfd)
246 maxsfd = *(int *)e->data;
247 }
248
249 if (pselect(maxsfd+1, &fdset, NULL, NULL, &timeout, NULL) < 0) {
250 if (errno == EINTR)
251 continue;
252 perror("pselect");
253 goto stop_serving;
254 }
255
256 i = -1;
257 forllist(lfds, e) {
258 i++;
259 if (FD_ISSET(*(int *)e->data, &fdset)) {
260 cltlen = sizeof(clt);
261 afd = accept(*(int *)e->data,
262 (struct sockaddr *)&clt,
263 &cltlen);
264 if (afd < 0 && errno != ECONNABORTED
265 && errno != EINTR) {
266 perror("accept");
267 goto stop_serving;
268 }
269
270 if (i == 0) {
271 rfds = llist_put(rfds, &afd,
272 sizeof(afd));
273 } else {
274 wfds = llist_put(wfds, &afd,
275 sizeof(afd));
276 }
277 }
278 }
279
280 forllist(rfds, e) {
281 if (FD_ISSET(*(int *)e->data, &fdset)) {
282 recvl = read(*(int *)e->data, recvbuf,
283 sizeof(recvbuf));
284 if (recvl <= 0) {
285 close(*(int *)e->data);
286 e = llist_del(&rfds, e);
287 if (e == NULL)
288 break;
289 }
290
291 forllist(wfds, qe) {
292 sendbufi = recvbuf;
293 sendl = recvl;
294 while (sendl > 0) {
295 if ((sent = write(*(int *)qe->data, sendbufi, sendl)) < 0) {
296 close(*(int *)qe->data);
297 qe = llist_del(&wfds, qe);
298 break;
299 }
300 sendl -= sent;
301 sendbufi += sent;
302 }
303 if (qe == NULL)
304 break;
305 }
306 }
307 }
308
309 }
310
311 rval = 0;
312 stop_serving:
313 closeallfds(lfds);
314 llist_free(lfds);
315 closeallfds(rfds);
316 llist_free(rfds);
317 closeallfds(wfds);
318 llist_free(wfds);
319
320 return rval;
321 }
322