dee9f94d63e92d64df14c538d810ad34d3d0171b
[tripe] / uslip / uslip.c
1 /* -*-c-*-
2 *
3 * A simple SLIP implementation drivable from the command-line
4 *
5 * (c) 2008 Straylight/Edgeware
6 */
7
8 /*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of Trivial IP Encryption (TrIPE).
11 *
12 * TrIPE is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * TrIPE is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with TrIPE; if not, write to the Free Software Foundation,
24 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 */
26
27 /*----- Header files ------------------------------------------------------*/
28
29 #include "config.h"
30
31 #include <assert.h>
32 #include <ctype.h>
33 #include <errno.h>
34 #include <signal.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38
39 #include <sys/types.h>
40 #include <sys/time.h>
41 #include <unistd.h>
42
43 #include <sys/socket.h>
44 #include <sys/un.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47 #include <netdb.h>
48
49 #include <mLib/alloc.h>
50 #include <mLib/dstr.h>
51 #include <mLib/fdflags.h>
52 #include <mLib/mdwopt.h>
53 #include <mLib/quis.h>
54 #include <mLib/report.h>
55 #include <mLib/sel.h>
56 #include <mLib/sub.h>
57
58 #include "slip.h"
59
60 #undef sun
61
62 /*----- Data structures ---------------------------------------------------*/
63
64 typedef struct pkq_node {
65 struct pkq_node *next;
66 unsigned char *buf;
67 size_t n, sz;
68 } pkq_node;
69
70 typedef struct pkq {
71 pkq_node *head, **tail;
72 } pkq;
73
74 enum { START, OK, ESC, BAD, SYNC1, SYNC2 };
75
76 typedef struct client {
77 sel_file f;
78 dstr d;
79 int mode;
80 } client;
81
82 typedef struct gobbler {
83 sel_file f;
84 void (*done)(struct gobbler *, int, void *);
85 void *p;
86 } gobbler;
87
88 typedef struct dribbler {
89 sel_file f;
90 pkq q;
91 void (*done)(struct dribbler *, int, void *);
92 void *p;
93 } dribbler;
94
95 typedef struct waiter {
96 struct waiter *next;
97 int fd;
98 gobbler *g;
99 } waiter;
100
101 /*----- Static variables --------------------------------------------------*/
102
103 static pkq q_in;
104 static dribbler *dribble_out;
105 static dstr slipbuf = DSTR_INIT;
106 static int slipstate = SYNC1;
107 static sel_file slip_in, listener;
108 static sel_state sel;
109 static unsigned reasons;
110 static waiter *wait_head, **wait_tail = &wait_head;
111 static unsigned char buf[16384];
112 static const char *name;
113
114 /*----- Utilities ---------------------------------------------------------*/
115
116 static void socketaddr(struct sockaddr_un *sun, size_t *sz)
117 {
118 size_t n = strlen(name) + 1;
119 if (n + offsetof(struct sockaddr_un, sun_path) > sizeof(*sun))
120 die(EXIT_FAILURE, "name too long: `%s'", name);
121 sun->sun_family = AF_UNIX;
122 memcpy(sun->sun_path, name, n);
123 if (sz)
124 *sz = n + offsetof(struct sockaddr_un, sun_path);
125 }
126
127 /*------ Packet queue -----------------------------------------------------*
128 *
129 * A packet queue contains a sequence of octet strings. Packets can be added
130 * to the end and read off the front.
131 */
132
133 static void initqueue(pkq *q) { q->head = 0; q->tail = &q->head; }
134
135 static pkq_node *make_pkqnode(void *p, size_t n)
136 {
137 pkq_node *pn;
138
139 if (!n)
140 return (0);
141 pn = CREATE(pkq_node);
142 pn->next = 0;
143 pn->buf = xmalloc(n);
144 memcpy(pn->buf, p, n);
145 pn->sz = n;
146 pn->n = 0;
147 return (pn);
148 }
149
150 static int enqueue(pkq *q, pkq_node *pn)
151 {
152 int rc = 0;
153
154 if (!pn)
155 return (0);
156 rc = !q->head;
157 pn->next = 0;
158 *q->tail = pn;
159 q->tail = &pn->next;
160 return (rc);
161 }
162
163 static void destroy_pkqnode(pkq_node *pn) { xfree(pn->buf); DESTROY(pn); }
164
165 static int dequeue(pkq *q, int freep)
166 {
167 pkq_node *pn = q->head;
168 assert(pn);
169 q->head = pn->next;
170 if (freep)
171 destroy_pkqnode(pn);
172 if (!q->head) {
173 q->tail = &q->head;
174 return (1);
175 }
176 return (0);
177 }
178
179 static void destroy_pkq(pkq *q)
180 {
181 pkq_node *pn, *pnn;
182
183 for (pn = q->head; pn; pn = pnn) {
184 pnn = pn->next;
185 destroy_pkqnode(pn);
186 }
187 q->head = 0; q->tail = &q->head;
188 }
189
190 /*----- Gobblers ----------------------------------------------------------*
191 *
192 * A gobbler just eats everything it sees on its input descriptor.
193 * Eventually, when it sees end-of-file, it closes the input descriptor,
194 * calls a user-supplied calback function, and quits.
195 */
196
197 static void gobbler_close(gobbler *g)
198 { if (g->f.fd != -1) { sel_rmfile(&g->f); close(g->f.fd); g->f.fd = -1; } }
199
200 static void gobbler_destroy(gobbler *g) { gobbler_close(g); DESTROY(g); }
201
202 static void do_gobble_in(int fd, unsigned mode, void *p)
203 {
204 gobbler *g = p;
205 ssize_t n;
206
207 for (;;) {
208 n = read(fd, buf, sizeof(buf));
209 if (n < 0) {
210 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
211 break;
212 else {
213 moan("read (gobble): %s", strerror(errno));
214 if (g->done) g->done(g, errno, g->p);
215 gobbler_close(g);
216 break;
217 }
218 } else if (n == 0) {
219 if (g->done) g->done(g, 0, g->p);
220 gobbler_close(g);
221 break;
222 }
223 }
224 }
225
226 static gobbler *make_gobbler(int fd,
227 void (*done)(gobbler *, int, void *),
228 void *p)
229 {
230 gobbler *g;
231
232 g = CREATE(gobbler);
233 g->done = done;
234 g->p = p;
235 sel_initfile(&sel, &g->f, fd, SEL_READ, do_gobble_in, g);
236 sel_addfile(&g->f);
237 do_gobble_in(fd, SEL_READ, g);
238 return (g);
239 }
240
241 /*----- Dribbler ----------------------------------------------------------*
242 *
243 * A dribbler hands out data from a packet queue to a file descriptor. It
244 * makes no attempt to preserve the record boundaries inherent in the packet
245 * queue structure. If the dribbler reaches the end of its queue, it invokes
246 * a user-supplied `done' function and stops selecting its output descriptor
247 * for writing.
248 */
249
250 static void cripple_dribbler(dribbler *d) { close(d->f.fd); d->f.fd = -1; }
251
252 static void destroy_dribbler(dribbler *d)
253 { cripple_dribbler(d); DESTROY(d); }
254
255 static void dribble_done(dribbler *d, int err)
256 {
257 if (d->q.head) {
258 sel_rmfile(&d->f);
259 destroy_pkq(&d->q);
260 }
261 d->done(d, err, d->p);
262 }
263
264 static void do_dribble_out(int fd, unsigned mode, void *p)
265 {
266 dribbler *d = p;
267 ssize_t n;
268 pkq_node *pn;
269
270 for (;;) {
271 pn = d->q.head;
272 n = write(fd, pn->buf + pn->n, pn->sz - pn->n);
273 if (n < 0) {
274 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
275 break;
276 else {
277 dribble_done(d, errno);
278 break;
279 }
280 }
281 pn->n += n;
282 if (pn->n == pn->sz && dequeue(&d->q, 1)) {
283 sel_rmfile(&d->f);
284 dribble_done(d, 0);
285 break;
286 }
287 }
288 }
289
290 static int enqueue_dribble(dribbler *d, pkq_node *pn)
291 {
292 if (d->f.fd == -1) {
293 destroy_pkqnode(pn);
294 return (0);
295 }
296 if (enqueue(&d->q, pn)) {
297 sel_addfile(&d->f);
298 do_dribble_out(d->f.fd, SEL_WRITE, d);
299 return (1);
300 }
301 return (0);
302 }
303
304 static dribbler *make_dribbler(int fd,
305 void (*done)(dribbler *, int, void *),
306 void *p)
307 {
308 dribbler *d = CREATE(dribbler);
309 sel_initfile(&sel, &d->f, fd, SEL_WRITE, do_dribble_out, d);
310 initqueue(&d->q);
311 d->done = done;
312 d->p = p;
313 return (d);
314 }
315
316 /*----- Clients -----------------------------------------------------------*/
317
318 static void done_client_dribble(dribbler *d, int err, void *p)
319 {
320 if (err)
321 moan("write (client): %s", strerror(err));
322 gobbler_destroy(p);
323 destroy_dribbler(d);
324 reasons--;
325 }
326
327 static void dequeue_to_waiter(void)
328 {
329 waiter *w;
330 dribbler *d;
331 pkq_node *pn;
332
333 while (q_in.head && wait_head) {
334 w = wait_head;
335 wait_head = w->next;
336 if (!wait_head)
337 wait_tail = &wait_head;
338 d = make_dribbler(w->fd, done_client_dribble, w->g);
339 DESTROY(w);
340 pn = q_in.head;
341 if (dequeue(&q_in, 0))
342 reasons--;
343 enqueue_dribble(d, pn);
344 }
345 }
346
347 static void client_destroy(client *c)
348 {
349 sel_rmfile(&c->f);
350 close(c->f.fd);
351 dstr_destroy(&c->d);
352 reasons--;
353 DESTROY(c);
354 }
355
356 static void do_client_in(int fd, unsigned mode, void *p)
357 {
358 client *c = p;
359 ssize_t n, i, i0;
360 waiter *w;
361
362 /* --- Attention --- *
363 *
364 * The queue for outbound packets is SLIP-encoded; we need to encode it
365 * here. The queue for inbound packets is raw.
366 */
367
368 for (;;) {
369 n = read(fd, buf, sizeof(buf));
370 i0 = 0;
371 if (n < 0) {
372 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
373 break;
374 else {
375 moan("read (client): %s", strerror(errno));
376 client_destroy(c);
377 return;
378 }
379 } else if (n == 0) {
380 if (c->mode == '>') {
381 DPUTC(&c->d, SL_END);
382 if (enqueue_dribble(dribble_out, make_pkqnode(c->d.buf, c->d.len)))
383 reasons++;
384 }
385 client_destroy(c);
386 return;
387 }
388 if (c->mode == '?') {
389 switch (buf[0]) {
390 case '>':
391 i0 = 1;
392 c->mode = '>';
393 break;
394 case '<':
395 w = CREATE(waiter);
396 w->g = make_gobbler(fd, 0, 0);
397 w->next = 0;
398 w->fd = fd;
399 *wait_tail = w;
400 wait_tail = &w->next;
401 sel_rmfile(&c->f);
402 DESTROY(c);
403 dequeue_to_waiter();
404 return;
405 default:
406 moan("bad client mode `%c'", buf[0]);
407 client_destroy(c);
408 return;
409 }
410 }
411 for (i = i0; i < n; i++) {
412 switch (buf[i]) {
413 case SL_ESC:
414 DPUTC(&c->d, SL_ESC);
415 DPUTC(&c->d, SL_ESCESC);
416 break;
417 case SL_END:
418 DPUTC(&c->d, SL_ESC);
419 DPUTC(&c->d, SL_ESCEND);
420 break;
421 default:
422 DPUTC(&c->d, buf[i]);
423 break;
424 }
425 }
426 }
427 }
428
429 static void do_accept(int fd, unsigned mode, void *hunoz)
430 {
431 client *c;
432 struct sockaddr_un sun;
433 socklen_t n = sizeof(sun);
434 int nfd;
435
436 if ((nfd = accept(fd, (struct sockaddr *)&sun, &n)) < 0) {
437 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
438 return;
439 else
440 die(EXIT_FAILURE, "accept: %s", strerror(errno));
441 }
442 c = CREATE(client);
443 c->mode = '?';
444 dstr_create(&c->d);
445 fdflags(nfd, O_NONBLOCK, O_NONBLOCK, 0, 0);
446 sel_initfile(&sel, &c->f, nfd, SEL_READ, do_client_in, c);
447 sel_addfile(&c->f);
448 reasons++;
449 }
450
451 /*----- Main daemon -------------------------------------------------------*/
452
453 static void done_slip_dribble(dribbler *d, int err, void *p)
454 {
455 if (!err)
456 reasons--;
457 else if (err != EPIPE)
458 die(EXIT_FAILURE, "write (slip): %s", strerror(errno));
459 else
460 cripple_dribbler(d);
461 }
462
463 static void do_slip_in(int fd, unsigned mode, void *hunoz)
464 {
465 ssize_t i, n;
466
467 /* --- Attention --- *
468 *
469 * The queue for inbound packets contains raw data; we need to decode it
470 * here. The queue for outbound packets is SLIP-encoded.
471 *
472 * TrIPE sends two empty packets on start-up, in order to resynchronize the
473 * target. We don't need this and it messes us up.
474 */
475
476 for (;;) {
477 n = read(fd, buf, sizeof(buf));
478 if (n == 0) {
479 switch (slipstate) {
480 case SYNC1:
481 case SYNC2:
482 case START:
483 case BAD:
484 break;
485 default:
486 moan("eof found while processing packet (discarding)");
487 break;
488 }
489 close(fd);
490 sel_rmfile(&slip_in);
491 reasons--;
492 return;
493 } else if (n < 0) {
494 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
495 break;
496 die(EXIT_FAILURE, "read (slip in): %s", strerror(errno));
497 }
498 for (i = 0; i < n; i++) {
499 switch (slipstate) {
500 case SYNC1:
501 switch (buf[i]) {
502 case SL_END:
503 slipstate = SYNC2;
504 break;
505 default:
506 goto start;
507 }
508 break;
509 case SYNC2:
510 switch (buf[i]) {
511 case SL_END:
512 slipstate = START;
513 break;
514 default:
515 goto start;
516 }
517 break;
518 case BAD:
519 switch (buf[i]) {
520 case SL_END:
521 DRESET(&slipbuf);
522 slipstate = OK;
523 break;
524 default:
525 break;
526 }
527 break;
528 case ESC:
529 switch (buf[i]) {
530 case SL_ESCEND:
531 DPUTC(&slipbuf, SL_END);
532 slipstate = OK;
533 break;
534 case SL_ESCESC:
535 DPUTC(&slipbuf, SL_ESC);
536 slipstate = OK;
537 break;
538 case SL_END:
539 moan("found escaped end byte (discard packet and resync");
540 DRESET(&slipbuf);
541 slipstate = OK;
542 break;
543 default:
544 moan("unexpected escape char 0x%02x", buf[i]);
545 slipstate = BAD;
546 break;
547 }
548 break;
549 case START:
550 case OK:
551 start:
552 switch (buf[i]) {
553 case SL_ESC:
554 slipstate = ESC;
555 break;
556 case SL_END:
557 if (enqueue(&q_in, make_pkqnode(slipbuf.buf, slipbuf.len)))
558 reasons++;
559 DRESET(&slipbuf);
560 dequeue_to_waiter();
561 slipstate = START;
562 break;
563 default:
564 DPUTC(&slipbuf, buf[i]);
565 slipstate = OK;
566 break;
567 }
568 break;
569 }
570 }
571 }
572 }
573
574 static void slipif(void)
575 {
576 int fd;
577 dstr d = DSTR_INIT;
578 struct sockaddr_un sun;
579 size_t sz;
580
581 /* --- Make the socket --- */
582
583 if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
584 die(EXIT_FAILURE, "socket: %s", strerror(errno));
585 socketaddr(&sun, &sz);
586 if (bind(fd, (struct sockaddr *)&sun, sz))
587 die(EXIT_FAILURE, "bind: %s", strerror(errno));
588 if (listen(fd, 5))
589 die(EXIT_FAILURE, "listen: %s", strerror(errno));
590
591 /* --- Set up listeners for things --- */
592
593 fdflags(fd, O_NONBLOCK, O_NONBLOCK, 0, 0);
594 sel_initfile(&sel, &listener, fd, SEL_READ, do_accept, 0);
595 sel_addfile(&listener);
596
597 fdflags(STDIN_FILENO, O_NONBLOCK, O_NONBLOCK, 0, 0);
598 fdflags(STDOUT_FILENO, O_NONBLOCK, O_NONBLOCK, 0, 0);
599 sel_initfile(&sel, &slip_in, STDIN_FILENO, SEL_READ, do_slip_in, 0);
600 dribble_out = make_dribbler(STDOUT_FILENO, done_slip_dribble, 0);
601 sel_addfile(&slip_in);
602
603 initqueue(&q_in);
604 reasons++;
605
606 /* --- Write the interface name --- */
607
608 dstr_putf(&d, "%s-%s\n", QUIS, name);
609 if (enqueue_dribble(dribble_out, make_pkqnode(d.buf, d.len)))
610 reasons++;
611 dstr_destroy(&d);
612
613 /* --- Main loop --- */
614
615 while (reasons) {
616 if (sel_select(&sel))
617 die(EXIT_FAILURE, "select: %s", strerror(errno));
618 }
619
620 /* --- Done --- */
621
622 unlink(name);
623 }
624
625 /*----- Putting and getting -----------------------------------------------*/
626
627 static int make_sock(int mode)
628 {
629 struct sockaddr_un sun;
630 size_t sz;
631 int fd;
632 char ch;
633
634 if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
635 die(EXIT_FAILURE, "socket: %s", strerror(errno));
636 socketaddr(&sun, &sz);
637 if (connect(fd, (struct sockaddr *)&sun, sz))
638 die(EXIT_FAILURE, "connect: %s", strerror(errno));
639 ch = mode;
640 if (write(fd, &ch, 1) < 0)
641 die(EXIT_FAILURE, "write (mode): %s", strerror(errno));
642 return (fd);
643 }
644
645 static void shovel(int from, int to)
646 {
647 ssize_t n;
648 size_t sz;
649 unsigned char *p;
650
651 for (;;) {
652 n = read(from, buf, sizeof(buf));
653 if (n < 0) {
654 if (errno == EINTR)
655 continue;
656 else
657 die(EXIT_FAILURE, "read (shovel): %s", strerror(errno));
658 } else if (n == 0)
659 break;
660
661 sz = n;
662 p = buf;
663 while (sz) {
664 n = write(to, p, sz);
665 if (n < 0) {
666 if (errno == EINTR)
667 continue;
668 else
669 die(EXIT_FAILURE, "write (shovel): %s", strerror(errno));
670 }
671 p += n;
672 sz -= n;
673 }
674 }
675 close(from);
676 close(to);
677 }
678
679 static void put(void) { shovel(STDIN_FILENO, make_sock('>')); }
680 static void get(void) { shovel(make_sock('<'), STDOUT_FILENO); }
681
682 /*----- Main code ---------------------------------------------------------*/
683
684 static void usage(FILE *fp) { pquis(fp, "Usage: $ [-pg] SOCKET\n"); }
685
686 static void version(void)
687 { pquis(stdout, "$ (" PACKAGE " version " VERSION")\n"); }
688
689 static void help(void)
690 {
691 version();
692 fputc('\n', stdout);
693 usage(stdout);
694 puts("\n\
695 With no options, provides a SLIP interface for TrIPE.\n\
696 \n\
697 Options:\n\
698 -p, --put Send packet on stdin to TrIPE.\n\
699 -g, --get Receive packet from TrIPE and write to stdout.");
700 }
701
702 int main(int argc, char *argv[])
703 {
704 int mode = 'd';
705 int i;
706
707 ego(argv[0]);
708 for (;;) {
709 const struct option opt[] = {
710 { "help", 0, 0, 'h' },
711 { "version", 0, 0, 'v' },
712 { "put", 0, 0, 'p' },
713 { "get", 0, 0, 'g' },
714 { 0, 0, 0, 0 }
715 };
716 i = mdwopt(argc, argv, "hvpg", opt, 0, 0, 0);
717 if (i < 0)
718 break;
719 switch (i) {
720 case 'h': help(); return (0);
721 case 'v': version(); return (0);
722 case 'p': case 'g': mode = i; break;
723 default: usage(stderr); exit(EXIT_FAILURE); break;
724 }
725 }
726 if (argc - optind != 1) { usage(stderr); exit(EXIT_FAILURE); }
727 name = argv[optind];
728 signal(SIGPIPE, SIG_IGN);
729 switch (mode) {
730 case 'd': slipif(); break;
731 case 'p': put(); break;
732 case 'g': get(); break;
733 }
734 return (0);
735 }
736
737 /*----- That's all, folks -------------------------------------------------*/