chiark / gitweb /
uslip/uslip.c: Shut the server down on `SIGTERM'.
[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/bits.h>
51 #include <mLib/conn.h>
52 #include <mLib/dstr.h>
53 #include <mLib/fdflags.h>
54 #include <mLib/mdwopt.h>
55 #include <mLib/quis.h>
56 #include <mLib/report.h>
57 #include <mLib/sel.h>
58 #include <mLib/sig.h>
59 #include <mLib/sub.h>
60 #include <mLib/tv.h>
61
62 #include "slip.h"
63
64 #undef sun
65
66 /*----- Data structures ---------------------------------------------------*/
67
68 typedef struct pkq_node {
69   struct pkq_node *next;
70   unsigned char *buf;
71   size_t n, sz;
72 } pkq_node;
73
74 typedef struct pkq {
75   pkq_node *head, **tail;
76 } pkq;
77
78 enum { START, OK, ESC, BAD, SYNC1, SYNC2 };
79
80 typedef struct client {
81   sel_file f;
82   dstr d;
83   int mode;
84 } client;
85
86 typedef struct gobbler {
87   sel_file f;
88   void (*done)(struct gobbler *, int, void *);
89   void *p;
90 } gobbler;
91
92 typedef struct dribbler {
93   sel_file f;
94   pkq q;
95   void (*done)(struct dribbler *, int, void *);
96   void *p;
97 } dribbler;
98
99 typedef struct waiter {
100   struct waiter *next;
101   int fd;
102   gobbler *g;
103 } waiter;
104
105 /*----- Static variables --------------------------------------------------*/
106
107 static pkq q_in;
108 static dribbler *dribble_out;
109 static dstr slipbuf = DSTR_INIT;
110 static int slipstate = SYNC1;
111 static sel_file slip_in, listener;
112 static sel_state sel;
113 static unsigned reasons;
114 static waiter *wait_head, **wait_tail = &wait_head;
115 static unsigned char buf[16384];
116 static const char *name;
117
118 /*----- Utilities ---------------------------------------------------------*/
119
120 static void socketaddr(struct sockaddr_un *sun, size_t *sz)
121 {
122   size_t n = strlen(name) + 1;
123   if (n + offsetof(struct sockaddr_un, sun_path) > sizeof(*sun))
124     die(EXIT_FAILURE, "name too long: `%s'", name);
125   sun->sun_family = AF_UNIX;
126   memcpy(sun->sun_path, name, n);
127   if (sz)
128     *sz = n + offsetof(struct sockaddr_un, sun_path);
129 }
130
131 /*------ Packet queue -----------------------------------------------------*
132  *
133  * A packet queue contains a sequence of octet strings.  Packets can be added
134  * to the end and read off the front.
135  */
136
137 static void initqueue(pkq *q) { q->head = 0; q->tail = &q->head; }
138
139 static pkq_node *make_pkqnode(const void *p, size_t n)
140 {
141   pkq_node *pn;
142
143   if (!n) return (0);
144   pn = CREATE(pkq_node);
145   pn->next = 0;
146   pn->buf = xmalloc(n);
147   if (p) memcpy(pn->buf, p, n);
148   pn->sz = n;
149   pn->n = 0;
150   return (pn);
151 }
152
153 static int enqueue(pkq *q, pkq_node *pn)
154 {
155   int rc = 0;
156
157   if (!pn) return (0);
158   rc = !q->head;
159   pn->next = 0;
160   *q->tail = pn;
161   q->tail = &pn->next;
162   return (rc);
163 }
164
165 static void destroy_pkqnode(pkq_node *pn) { xfree(pn->buf); DESTROY(pn); }
166
167 static int dequeue(pkq *q, int freep)
168 {
169   pkq_node *pn = q->head;
170   assert(pn);
171   q->head = pn->next;
172   if (freep)
173     destroy_pkqnode(pn);
174   if (!q->head) {
175     q->tail = &q->head;
176     return (1);
177   }
178   return (0);
179 }
180
181 static void destroy_pkq(pkq *q)
182 {
183   pkq_node *pn, *pnn;
184
185   for (pn = q->head; pn; pn = pnn) {
186     pnn = pn->next;
187     destroy_pkqnode(pn);
188   }
189   q->head = 0; q->tail = &q->head;
190 }
191
192 /*----- Gobblers ----------------------------------------------------------*
193  *
194  * A gobbler just eats everything it sees on its input descriptor.
195  * Eventually, when it sees end-of-file, it closes the input descriptor,
196  * calls a user-supplied calback function, and quits.
197  */
198
199 static void close_gobbler(gobbler *g)
200   { if (g->f.fd != -1) { sel_rmfile(&g->f); close(g->f.fd); g->f.fd = -1; } }
201
202 static void destroy_gobbler(gobbler *g) { close_gobbler(g); DESTROY(g); }
203
204 static void do_gobble_in(int fd, unsigned mode, void *p)
205 {
206   gobbler *g = p;
207   ssize_t n;
208
209   for (;;) {
210     n = read(fd, buf, sizeof(buf));
211     if (n < 0) {
212       if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
213         break;
214       else {
215         moan("read (gobble): %s", strerror(errno));
216         if (g->done) g->done(g, errno, g->p);
217         close_gobbler(g);
218         break;
219       }
220     } else if (n == 0) {
221       if (g->done) g->done(g, 0, g->p);
222       close_gobbler(g);
223       break;
224     }
225   }
226 }
227
228 static gobbler *make_gobbler(int fd,
229                              void (*done)(gobbler *, int, void *),
230                              void *p)
231 {
232   gobbler *g;
233
234   g = CREATE(gobbler);
235   g->done = done;
236   g->p = p;
237   sel_initfile(&sel, &g->f, fd, SEL_READ, do_gobble_in, g);
238   sel_addfile(&g->f);
239   do_gobble_in(fd, SEL_READ, g);
240   return (g);
241 }
242
243 /*----- Dribbler ----------------------------------------------------------*
244  *
245  * A dribbler hands out data from a packet queue to a file descriptor.  It
246  * makes no attempt to preserve the record boundaries inherent in the packet
247  * queue structure.  If the dribbler reaches the end of its queue, it invokes
248  * a user-supplied `done' function and stops selecting its output descriptor
249  * for writing.
250  */
251
252 static void cripple_dribbler(dribbler *d) { close(d->f.fd); d->f.fd = -1; }
253
254 static void destroy_dribbler(dribbler *d)
255   { cripple_dribbler(d); DESTROY(d); }
256
257 static void dribble_done(dribbler *d, int err)
258 {
259   if (d->q.head) {
260     sel_rmfile(&d->f);
261     destroy_pkq(&d->q);
262   }
263   d->done(d, err, d->p);
264 }
265
266 static void do_dribble_out(int fd, unsigned mode, void *p)
267 {
268   dribbler *d = p;
269   ssize_t n;
270   pkq_node *pn;
271
272   for (;;) {
273     pn = d->q.head;
274     n = write(fd, pn->buf + pn->n, pn->sz - pn->n);
275     if (n < 0) {
276       if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
277         break;
278       else {
279         dribble_done(d, errno);
280         break;
281       }
282     }
283     pn->n += n;
284     if (pn->n == pn->sz && dequeue(&d->q, 1)) {
285       sel_rmfile(&d->f);
286       dribble_done(d, 0);
287       break;
288     }
289   }
290 }
291
292 static int enqueue_dribble(dribbler *d, pkq_node *pn)
293 {
294   if (d->f.fd == -1) {
295     destroy_pkqnode(pn);
296     return (0);
297   }
298   if (enqueue(&d->q, pn)) {
299     sel_addfile(&d->f);
300     do_dribble_out(d->f.fd, SEL_WRITE, d);
301     return (1);
302   }
303   return (0);
304 }
305
306 static dribbler *make_dribbler(int fd,
307                                void (*done)(dribbler *, int, void *),
308                                void *p)
309 {
310   dribbler *d = CREATE(dribbler);
311   sel_initfile(&sel, &d->f, fd, SEL_WRITE, do_dribble_out, d);
312   initqueue(&d->q);
313   d->done = done;
314   d->p = p;
315   return (d);
316 }
317
318 /*----- Clients -----------------------------------------------------------*/
319
320 static void done_client_dribble(dribbler *d, int err, void *p)
321 {
322   if (err)
323     moan("write (client): %s", strerror(err));
324   destroy_gobbler(p);
325   destroy_dribbler(d);
326   reasons--;
327 }
328
329 static void dequeue_to_waiter(void)
330 {
331   waiter *w;
332   dribbler *d;
333   pkq_node *pn;
334
335   while (q_in.head && wait_head) {
336     w = wait_head;
337     wait_head = w->next;
338     if (!wait_head)
339       wait_tail = &wait_head;
340     d = make_dribbler(w->fd, done_client_dribble, w->g);
341     DESTROY(w);
342     pn = q_in.head;
343     if (dequeue(&q_in, 0))
344       reasons--;
345     enqueue_dribble(d, pn);
346   }
347 }
348
349 static void destroy_client(client *c)
350 {
351   sel_rmfile(&c->f);
352   close(c->f.fd);
353   dstr_destroy(&c->d);
354   reasons--;
355   DESTROY(c);
356 }
357
358 static void do_client_in(int fd, unsigned mode, void *p)
359 {
360   client *c = p;
361   ssize_t n, i, i0;
362   waiter *w;
363
364   /* --- Attention --- *
365    *
366    * The queue for outbound packets is SLIP-encoded; we need to encode it
367    * here. The queue for inbound packets is raw.
368    */
369
370   for (;;) {
371     n = read(fd, buf, sizeof(buf));
372     i0 = 0;
373     if (n < 0) {
374       if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
375         break;
376       else {
377         moan("read (client): %s", strerror(errno));
378         destroy_client(c);
379         return;
380       }
381     } else if (n == 0) {
382       if (c->mode == '>') {
383         DPUTC(&c->d, SL_END);
384         if (enqueue_dribble(dribble_out, make_pkqnode(c->d.buf, c->d.len)))
385           reasons++;
386       }
387       destroy_client(c);
388       return;
389     }
390     if (c->mode == '?') {
391       switch (buf[0]) {
392         case '>':
393           i0 = 1;
394           c->mode = '>';
395           break;
396         case '<':
397           w = CREATE(waiter);
398           w->g = make_gobbler(fd, 0, 0);
399           w->next = 0;
400           w->fd = fd;
401           *wait_tail = w;
402           wait_tail = &w->next;
403           sel_rmfile(&c->f);
404           DESTROY(c);
405           dequeue_to_waiter();
406           return;
407         default:
408           moan("bad client mode `%c'", buf[0]);
409           destroy_client(c);
410           return;
411       }
412     }
413     for (i = i0; i < n; i++) {
414       switch (buf[i]) {
415         case SL_ESC:
416           DPUTC(&c->d, SL_ESC);
417           DPUTC(&c->d, SL_ESCESC);
418           break;
419         case SL_END:
420           DPUTC(&c->d, SL_ESC);
421           DPUTC(&c->d, SL_ESCEND);
422           break;
423         default:
424           DPUTC(&c->d, buf[i]);
425           break;
426       }
427     }
428   }
429 }
430
431 static void do_accept(int fd, unsigned mode, void *hunoz)
432 {
433   client *c;
434   struct sockaddr_un sun;
435   socklen_t n = sizeof(sun);
436   int nfd;
437
438   if ((nfd = accept(fd, (struct sockaddr *)&sun, &n)) < 0) {
439     if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
440       return;
441     else
442       die(EXIT_FAILURE, "accept: %s", strerror(errno));
443   }
444   c = CREATE(client);
445   c->mode = '?';
446   dstr_create(&c->d);
447   fdflags(nfd, O_NONBLOCK, O_NONBLOCK, 0, 0);
448   sel_initfile(&sel, &c->f, nfd, SEL_READ, do_client_in, c);
449   sel_addfile(&c->f);
450   reasons++;
451 }
452
453 /*----- Main daemon -------------------------------------------------------*/
454
455 static void done_slip_dribble(dribbler *d, int err, void *hunoz)
456 {
457   if (!err)
458     reasons--;
459   else if (err != EPIPE)
460     die(EXIT_FAILURE, "write (slip): %s", strerror(errno));
461   else
462     cripple_dribbler(d);
463 }
464
465 static void close_slip(int fd)
466 {
467   switch (slipstate) {
468     case SYNC1: case SYNC2: case START: case BAD: break;
469     default: moan("eof found while processing packet (discarding)"); break;
470   }
471   close(fd); sel_rmfile(&slip_in);
472   reasons--;
473 }
474
475 static void do_slip_in(int fd, unsigned mode, void *hunoz)
476 {
477   ssize_t i, n;
478
479   /* --- Attention --- *
480    *
481    * The queue for inbound packets contains raw data; we need to decode it
482    * here.  The queue for outbound packets is SLIP-encoded.
483    *
484    * TrIPE sends two empty packets on start-up, in order to resynchronize the
485    * target.  We don't need this and it messes us up.
486    */
487
488   for (;;) {
489     n = read(fd, buf, sizeof(buf));
490     if (n == 0) {
491       close_slip(fd);
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 slip_term(int n, void *fdp)
575   { close_slip(*(int *)fdp); }
576
577 static void slipif(void)
578 {
579   int fd;
580   dstr d = DSTR_INIT;
581   struct sockaddr_un sun;
582   sig term;
583   size_t sz;
584
585   /* --- Make the socket --- */
586
587   if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
588     die(EXIT_FAILURE, "socket: %s", strerror(errno));
589   socketaddr(&sun, &sz);
590   if (bind(fd, (struct sockaddr *)&sun, sz))
591     die(EXIT_FAILURE, "bind: %s", strerror(errno));
592   if (listen(fd, 5))
593     die(EXIT_FAILURE, "listen: %s", strerror(errno));
594
595   /* --- Set up listeners for things --- */
596
597   fdflags(fd, O_NONBLOCK, O_NONBLOCK, 0, 0);
598   sel_initfile(&sel, &listener, fd, SEL_READ, do_accept, 0);
599   sel_addfile(&listener);
600
601   fdflags(STDIN_FILENO, O_NONBLOCK, O_NONBLOCK, 0, 0);
602   fdflags(STDOUT_FILENO, O_NONBLOCK, O_NONBLOCK, 0, 0);
603   sel_initfile(&sel, &slip_in, STDIN_FILENO, SEL_READ, do_slip_in, 0);
604   dribble_out = make_dribbler(STDOUT_FILENO, done_slip_dribble, 0);
605   sel_addfile(&slip_in);
606
607   sig_init(&sel);
608   sig_add(&term, SIGTERM, slip_term, &fd);
609   sig_add(&term, SIGINT, slip_term, &fd);
610
611   initqueue(&q_in);
612   reasons++;
613
614   /* --- Write the interface name --- */
615
616   dstr_putf(&d, "%s-%s\n", QUIS, name);
617   if (enqueue_dribble(dribble_out, make_pkqnode(d.buf, d.len)))
618     reasons++;
619   dstr_destroy(&d);
620
621   /* --- Main loop --- */
622
623   while (reasons) {
624     if (sel_select(&sel))
625       die(EXIT_FAILURE, "select: %s", strerror(errno));
626   }
627
628   /* --- Done --- */
629
630   unlink(name);
631 }
632
633 /*----- Putting and getting -----------------------------------------------*/
634
635 static int make_sock(int mode)
636 {
637   struct sockaddr_un sun;
638   size_t sz;
639   int fd;
640   char ch;
641
642   if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
643     die(EXIT_FAILURE, "socket: %s", strerror(errno));
644   socketaddr(&sun, &sz);
645   if (connect(fd, (struct sockaddr *)&sun, sz))
646     die(EXIT_FAILURE, "connect: %s", strerror(errno));
647   ch = mode;
648   if (write(fd, &ch, 1) < 0)
649     die(EXIT_FAILURE, "write (mode): %s", strerror(errno));
650   return (fd);
651 }
652
653 static void shovel(int from, int to)
654 {
655   ssize_t n;
656   size_t sz;
657   unsigned char *p;
658
659   for (;;) {
660     n = read(from, buf, sizeof(buf));
661     if (n < 0) {
662       if (errno == EINTR)
663         continue;
664       else
665         die(EXIT_FAILURE, "read (shovel): %s", strerror(errno));
666     } else if (n == 0)
667       break;
668
669     sz = n;
670     p = buf;
671     while (sz) {
672       n = write(to, p, sz);
673       if (n < 0) {
674         if (errno == EINTR)
675           continue;
676         else
677           die(EXIT_FAILURE, "write (shovel): %s", strerror(errno));
678       }
679       p += n;
680       sz -= n;
681     }
682   }
683   close(from);
684   close(to);
685 }
686
687 static void put(void) { shovel(STDIN_FILENO, make_sock('>')); }
688 static void get(void) { shovel(make_sock('<'), STDOUT_FILENO); }
689
690 /*----- Flooding and sinking ----------------------------------------------*/
691
692 /* --- Connection jobs --- */
693
694 typedef struct conninfo {
695   struct conninfo *next;
696   conn c;
697 } conninfo;
698
699 #define MAXCONN 32
700 static conninfo conns[MAXCONN], *freeconns;
701
702 static void (*connhook)(int, conninfo *);
703
704 static void connrecycle(conninfo *c) { c->next = freeconns; freeconns = c; }
705
706 static void connerr(conninfo *c)
707 {
708   if (errno == EAGAIN) connrecycle(c);
709   else die(EXIT_FAILURE, "connect: %s", strerror(errno));
710 }
711
712 static void connected(int fd, void *p)
713   { if (fd == -1) connerr(p); else connhook(fd, p); }
714
715 static void conndoconnect(conninfo *c)
716 {
717   int fd;
718   struct sockaddr_un sun;
719   size_t sz;
720
721   if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
722     die(EXIT_FAILURE, "socket: %s", strerror(errno));
723   socketaddr(&sun, &sz);
724   if (conn_init(&c->c, &sel, fd, (struct sockaddr *)&sun, sz, connected, c))
725     connerr(c);
726 }
727
728 static int timerflag;
729
730 static void conndrip(struct timeval *tv, void *hunoz)
731 {
732   conninfo *c, *cc;
733
734   timerflag = 0;
735   while (freeconns) {
736     c = freeconns; freeconns = 0;
737     while (c) { cc = c->next; conndoconnect(c); c = cc; }
738   }
739 }
740
741 static void connloop(void (*connected)(int, conninfo *))
742 {
743   int i;
744   conninfo **cc;
745   sel_timer t;
746   struct timeval tv;
747
748   connhook = connected;
749   for (i = 0, cc = &freeconns; i < MAXCONN; i++) {
750     *cc = &conns[i];
751     cc = &conns[i].next;
752   }
753   *cc = 0;
754
755   for (;;) {
756     if (freeconns && !timerflag) {
757       gettimeofday(&tv, 0);
758       TV_ADDL(&tv, &tv, 0, 10000);
759       sel_addtimer(&sel, &t, &tv, conndrip, 0);
760       timerflag = 1;
761     }
762     if (sel_select(&sel))
763       die(EXIT_FAILURE, "select: %s", strerror(errno));
764   }
765 }
766
767 /* --- Sinking (glug glug) --- */
768
769 static void close_sink(gobbler *g, int err, void *p)
770 {
771   static char baton[4] = "/-\\|";
772   static int pos = 0;
773   static int count = 0;
774   static int state = '?';
775
776   conninfo *c = p;
777
778   if (!err) {
779     if (state == '?')
780       state = isatty(STDOUT_FILENO) ? 'y' : 'n';
781     if (state == 'y') {
782       if (count) count--;
783       else {
784         putchar(baton[pos]);
785         putchar('\b');
786         fflush(stdout);
787         pos++;
788         if (pos >= sizeof(baton)) pos = 0;
789         count = 128;
790       }
791     }
792   }
793   connrecycle(c);
794 }
795
796 static void sink_connected(int fd, conninfo *c)
797 {
798   char dir = '<';
799
800   if (write(fd, &dir, 1) != 1) {
801     moan("write: %s (continuing)", strerror(errno));
802     close(fd);
803     connrecycle(c);
804     return;
805   }
806   make_gobbler(fd, close_sink, c);
807 }
808
809 static void sink(void) { connloop(sink_connected); }
810
811 /* --- Flooding --- */
812
813 static void close_flood(dribbler *d, int err, void *p)
814 {
815   conninfo *c = p;
816
817   if (err) moan("write: %s (continuing)\n", strerror(errno));
818   destroy_dribbler(d);
819   connrecycle(c);
820 }
821
822 static void flood_connected(int fd, conninfo *c)
823 {
824   static uint32 seq;
825
826   dribbler *d;
827   pkq_node *pn;
828   int i;
829
830 #define FLOOD_PKSZ 1024
831
832   pn = make_pkqnode(0, 1 + FLOOD_PKSZ);
833   pn->buf[0] = '>';
834   STORE32(pn->buf + 1, seq);
835   for (i = 4; i < FLOOD_PKSZ; i++)
836     pn->buf[i + 1] = i & 0xff;
837   seq++;
838   d = make_dribbler(fd, close_flood, c);
839   enqueue_dribble(d, pn);
840 }
841
842 static void flood(void) { connloop(flood_connected); }
843
844 /*----- Main code ---------------------------------------------------------*/
845
846 static void usage(FILE *fp) { pquis(fp, "Usage: $ [-fgps] SOCKET\n"); }
847
848 static void version(void)
849   { pquis(stdout, "$ (" PACKAGE " version " VERSION")\n"); }
850
851 static void help(void)
852 {
853   version();
854   fputc('\n', stdout);
855   usage(stdout);
856   puts("\n\
857 With no options, provides a SLIP interface for TrIPE.\n\
858 \n\
859 Options:\n\
860   -f, --flood   Send packets to TrIPE as fast as possible.\n\
861   -g, --get     Receive packet from TrIPE and write to stdout.\n\
862   -p, --put     Send packet on stdin to TrIPE.\n\
863   -s, --sink    Slurp packets out of TrIPE and display progress.");
864 }
865
866 int main(int argc, char *argv[])
867 {
868   int mode = 'd';
869   int i;
870
871   ego(argv[0]);
872   for (;;) {
873     const struct option opt[] = {
874       { "help",                 0,              0,              'h' },
875       { "version",              0,              0,              'v' },
876       { "put",                  0,              0,              'p' },
877       { "get",                  0,              0,              'g' },
878       { "flood",                0,              0,              'f' },
879       { "sink",                 0,              0,              's' },
880       { 0,                      0,              0,              0 }
881     };
882     i = mdwopt(argc, argv, "hvpgfs", opt, 0, 0, 0);
883     if (i < 0)
884       break;
885     switch (i) {
886       case 'h': help(); return (0);
887       case 'v': version(); return (0);
888       case 'p': case 'g': case 's': case 'f': mode = i; break;
889       default: usage(stderr); exit(EXIT_FAILURE); break;
890     }
891   }
892   if (argc - optind != 1) { usage(stderr); exit(EXIT_FAILURE); }
893   name = argv[optind];
894   signal(SIGPIPE, SIG_IGN);
895   switch (mode) {
896     case 'd': slipif(); break;
897     case 'p': put(); break;
898     case 'g': get(); break;
899     case 'f': flood(); break;
900     case 's': sink(); break;
901   }
902   return (0);
903 }
904
905 /*----- That's all, folks -------------------------------------------------*/