chiark / gitweb /
server/admin: Fix client destruction some more.
[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 } gobbler;
85
86 typedef struct dribbler {
87   sel_file f;
88   pkq q;
89   void (*done)(struct dribbler *, int, void *);
90   void *p;
91 } dribbler;
92
93 typedef struct waiter {
94   struct waiter *next;
95   int fd;
96   gobbler *g;
97 } waiter;
98
99 /*----- Static variables --------------------------------------------------*/
100
101 static pkq q_in;
102 static dribbler *dribble_out;
103 static dstr slipbuf = DSTR_INIT;
104 static int slipstate = SYNC1;
105 static sel_file slip_in, listener;
106 static sel_state sel;
107 static unsigned reasons;
108 static waiter *wait_head, **wait_tail = &wait_head;
109 static unsigned char buf[16384];
110 static const char *name;
111
112 /*----- Utilities ---------------------------------------------------------*/
113
114 static 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
131 static void initqueue(pkq *q) { q->head = 0; q->tail = &q->head; }
132
133 static 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
148 static 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
161 static void destroy_pkqnode(pkq_node *pn) { xfree(pn->buf); DESTROY(pn); }
162
163 static 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
177 static 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
195 static void gobbler_close(gobbler *g)
196   { if (g->f.fd != -1) { sel_rmfile(&g->f); g->f.fd = -1; } }
197
198 static void gobbler_destroy(gobbler *g) { gobbler_close(g); DESTROY(g); }
199
200 static 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
222 static 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
242 static void cripple_dribbler(dribbler *d) { close(d->f.fd); d->f.fd = -1; }
243
244 static void destroy_dribbler(dribbler *d)
245   { cripple_dribbler(d); DESTROY(d); }
246
247 static 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
256 static 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
282 static 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
296 static 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
310 static 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
319 static 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
339 static 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
348 static 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
421 static 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
445 static 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
455 static 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);
524               break;
525             case SL_ESCESC:
526               DPUTC(&slipbuf, SL_ESC);
527               break;
528             case SL_END:
529               moan("found escaped end byte (discard packet and resync");
530               DRESET(&slipbuf);
531               slipstate = OK;
532               break;
533             default:
534               moan("unspected escape char 0x%02x", buf[i]);
535               slipstate = BAD;
536               break;
537           }
538           break;
539         case START:
540         case OK:
541         start:
542           switch (buf[i]) {
543             case SL_ESC:
544               slipstate = ESC;
545               break;
546             case SL_END:
547               if (enqueue(&q_in, make_pkqnode(slipbuf.buf, slipbuf.len)))
548                 reasons++;
549               DRESET(&slipbuf);
550               dequeue_to_waiter();
551               slipstate = START;
552               break;
553             default:
554               DPUTC(&slipbuf, buf[i]);
555               slipstate = OK;
556               break;
557           }
558           break;
559       }
560     }
561   }
562 }
563
564 static void slipif(void)
565 {
566   int fd;
567   dstr d = DSTR_INIT;
568   struct sockaddr_un sun;
569   size_t sz;
570
571   /* --- Make the socket --- */
572
573   if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
574     die(EXIT_FAILURE, "socket: %s", strerror(errno));
575   socketaddr(&sun, &sz);
576   if (bind(fd, (struct sockaddr *)&sun, sz))
577     die(EXIT_FAILURE, "bind: %s", strerror(errno));
578   if (listen(fd, 5))
579     die(EXIT_FAILURE, "listen: %s", strerror(errno));
580
581   /* --- Set up listeners for things --- */
582
583   fdflags(fd, O_NONBLOCK, O_NONBLOCK, 0, 0);
584   sel_initfile(&sel, &listener, fd, SEL_READ, do_accept, 0);
585   sel_addfile(&listener);
586
587   fdflags(STDIN_FILENO, O_NONBLOCK, O_NONBLOCK, 0, 0);
588   fdflags(STDOUT_FILENO, O_NONBLOCK, O_NONBLOCK, 0, 0);
589   sel_initfile(&sel, &slip_in, STDIN_FILENO, SEL_READ, do_slip_in, 0);
590   dribble_out = make_dribbler(STDOUT_FILENO, done_slip_dribble, 0);
591   sel_addfile(&slip_in);
592
593   initqueue(&q_in);
594   reasons++;
595
596   /* --- Write the interface name --- */
597
598   dstr_putf(&d, "%s-%s\n", QUIS, name);
599   if (enqueue_dribble(dribble_out, make_pkqnode(d.buf, d.len)))
600     reasons++;
601   dstr_destroy(&d);
602
603   /* --- Main loop --- */
604
605   while (reasons) {
606     if (sel_select(&sel))
607       die(EXIT_FAILURE, "select: %s", strerror(errno));
608   }
609
610   /* --- Done --- */
611
612   unlink(name);
613 }
614
615 /*----- Putting and getting -----------------------------------------------*/
616
617 static int make_sock(int mode)
618 {
619   struct sockaddr_un sun;
620   size_t sz;
621   int fd;
622   char ch;
623
624   if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
625     die(EXIT_FAILURE, "socket: %s", strerror(errno));
626   socketaddr(&sun, &sz);
627   if (connect(fd, (struct sockaddr *)&sun, sz))
628     die(EXIT_FAILURE, "connect: %s", strerror(errno));
629   ch = mode;
630   if (write(fd, &ch, 1) < 0)
631     die(EXIT_FAILURE, "write (mode): %s", strerror(errno));
632   return (fd);
633 }
634
635 static void shovel(int from, int to)
636 {
637   ssize_t n;
638   size_t sz;
639   unsigned char *p;
640
641   for (;;) {
642     n = read(from, buf, sizeof(buf));
643     if (n < 0) {
644       if (errno == EINTR)
645         continue;
646       else
647         die(EXIT_FAILURE, "read (shovel): %s", strerror(errno));
648     } else if (n == 0)
649       break;
650
651     sz = n;
652     p = buf;
653     while (sz) {
654       n = write(to, p, sz);
655       if (n < 0) {
656         if (errno == EINTR)
657           continue;
658         else
659           die(EXIT_FAILURE, "write (shovel): %s", strerror(errno));
660       }
661       p += n;
662       sz -= n;
663     }
664   }
665   close(from);
666   close(to);
667 }
668
669 static void put(void) { shovel(STDIN_FILENO, make_sock('>')); }
670 static void get(void) { shovel(make_sock('<'), STDOUT_FILENO); }
671
672 /*----- Main code ---------------------------------------------------------*/
673
674 static void usage(FILE *fp) { pquis(fp, "Usage: $ [-pg] SOCKET\n"); }
675
676 static void version(void)
677   { pquis(stdout, "$ (" PACKAGE " version " VERSION")\n"); }
678
679 static void help(void)
680 {
681   version();
682   fputc('\n', stdout);
683   usage(stdout);
684   puts("\n\
685 With no options, provides a SLIP interface for TrIPE.\n\
686 \n\
687 Options:\n\
688   -p, --put     Send packet on stdin to TrIPE.\n\
689   -g, --get     Receive packet from TrIPE and write to stdout.");
690 }
691
692 int main(int argc, char *argv[])
693 {
694   int mode = 'd';
695   int i;
696
697   ego(argv[0]);
698   for (;;) {
699     const struct option opt[] = {
700       { "help",                 0,              0,              'h' },
701       { "version",              0,              0,              'v' },
702       { "put",                  0,              0,              'p' },
703       { "get",                  0,              0,              'g' },
704       { 0,                      0,              0,              0 }
705     };
706     i = mdwopt(argc, argv, "hvpg", opt, 0, 0, 0);
707     if (i < 0)
708       break;
709     switch (i) {
710       case 'h': help(); return (0);
711       case 'v': version(); return (0);
712       case 'p': case 'g': mode = i; break;
713       default: usage(stderr); exit(EXIT_FAILURE); break;
714     }
715   }
716   if (argc - optind != 1) { usage(stderr); exit(EXIT_FAILURE); }
717   name = argv[optind];
718   signal(SIGPIPE, SIG_IGN);
719   switch (mode) {
720     case 'd': slipif(); break;
721     case 'p': put(); break;
722     case 'g': get(); break;
723   }
724   return (0);
725 }
726
727 /*----- That's all, folks -------------------------------------------------*/