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