chiark / gitweb /
uslip/uslip.c: Shut the server down on `SIGTERM'.
[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>
1e14551a
MW
50#include <mLib/bits.h>
51#include <mLib/conn.h>
49f86fe4
MW
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>
aec8bcc8 58#include <mLib/sig.h>
49f86fe4 59#include <mLib/sub.h>
1e14551a 60#include <mLib/tv.h>
49f86fe4
MW
61
62#include "slip.h"
63
64#undef sun
65
66/*----- Data structures ---------------------------------------------------*/
67
68typedef struct pkq_node {
69 struct pkq_node *next;
70 unsigned char *buf;
71 size_t n, sz;
72} pkq_node;
73
74typedef struct pkq {
75 pkq_node *head, **tail;
76} pkq;
77
78enum { START, OK, ESC, BAD, SYNC1, SYNC2 };
79
80typedef struct client {
81 sel_file f;
82 dstr d;
83 int mode;
84} client;
85
86typedef struct gobbler {
87 sel_file f;
e12ebbe8
MW
88 void (*done)(struct gobbler *, int, void *);
89 void *p;
49f86fe4
MW
90} gobbler;
91
92typedef struct dribbler {
93 sel_file f;
94 pkq q;
95 void (*done)(struct dribbler *, int, void *);
96 void *p;
97} dribbler;
98
99typedef struct waiter {
100 struct waiter *next;
101 int fd;
102 gobbler *g;
103} waiter;
104
105/*----- Static variables --------------------------------------------------*/
106
107static pkq q_in;
108static dribbler *dribble_out;
109static dstr slipbuf = DSTR_INIT;
110static int slipstate = SYNC1;
111static sel_file slip_in, listener;
112static sel_state sel;
113static unsigned reasons;
114static waiter *wait_head, **wait_tail = &wait_head;
115static unsigned char buf[16384];
116static const char *name;
117
118/*----- Utilities ---------------------------------------------------------*/
119
120static 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
137static void initqueue(pkq *q) { q->head = 0; q->tail = &q->head; }
138
2a74827f 139static pkq_node *make_pkqnode(const void *p, size_t n)
49f86fe4
MW
140{
141 pkq_node *pn;
142
1e14551a 143 if (!n) return (0);
49f86fe4
MW
144 pn = CREATE(pkq_node);
145 pn->next = 0;
146 pn->buf = xmalloc(n);
1e14551a 147 if (p) memcpy(pn->buf, p, n);
49f86fe4
MW
148 pn->sz = n;
149 pn->n = 0;
150 return (pn);
151}
152
153static int enqueue(pkq *q, pkq_node *pn)
154{
155 int rc = 0;
156
1e14551a 157 if (!pn) return (0);
49f86fe4
MW
158 rc = !q->head;
159 pn->next = 0;
160 *q->tail = pn;
161 q->tail = &pn->next;
162 return (rc);
163}
164
165static void destroy_pkqnode(pkq_node *pn) { xfree(pn->buf); DESTROY(pn); }
166
167static 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
181static 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.
e12ebbe8
MW
195 * Eventually, when it sees end-of-file, it closes the input descriptor,
196 * calls a user-supplied calback function, and quits.
49f86fe4
MW
197 */
198
75ed082d 199static void close_gobbler(gobbler *g)
073cc23b 200 { if (g->f.fd != -1) { sel_rmfile(&g->f); close(g->f.fd); g->f.fd = -1; } }
49f86fe4 201
75ed082d 202static void destroy_gobbler(gobbler *g) { close_gobbler(g); DESTROY(g); }
49f86fe4
MW
203
204static 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));
e12ebbe8 216 if (g->done) g->done(g, errno, g->p);
75ed082d 217 close_gobbler(g);
49f86fe4
MW
218 break;
219 }
220 } else if (n == 0) {
e12ebbe8 221 if (g->done) g->done(g, 0, g->p);
75ed082d 222 close_gobbler(g);
49f86fe4
MW
223 break;
224 }
225 }
226}
227
e12ebbe8
MW
228static gobbler *make_gobbler(int fd,
229 void (*done)(gobbler *, int, void *),
230 void *p)
49f86fe4
MW
231{
232 gobbler *g;
233
234 g = CREATE(gobbler);
e12ebbe8
MW
235 g->done = done;
236 g->p = p;
49f86fe4
MW
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
252static void cripple_dribbler(dribbler *d) { close(d->f.fd); d->f.fd = -1; }
253
254static void destroy_dribbler(dribbler *d)
255 { cripple_dribbler(d); DESTROY(d); }
256
257static 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
266static 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
292static 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
306static 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
320static void done_client_dribble(dribbler *d, int err, void *p)
321{
322 if (err)
323 moan("write (client): %s", strerror(err));
75ed082d 324 destroy_gobbler(p);
49f86fe4
MW
325 destroy_dribbler(d);
326 reasons--;
327}
328
329static 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
75ed082d 349static void destroy_client(client *c)
49f86fe4
MW
350{
351 sel_rmfile(&c->f);
352 close(c->f.fd);
353 dstr_destroy(&c->d);
354 reasons--;
355 DESTROY(c);
356}
357
358static 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));
75ed082d 378 destroy_client(c);
49f86fe4
MW
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 }
75ed082d 387 destroy_client(c);
49f86fe4
MW
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);
e12ebbe8 398 w->g = make_gobbler(fd, 0, 0);
49f86fe4
MW
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]);
75ed082d 409 destroy_client(c);
49f86fe4
MW
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
431static 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
5393b556 455static void done_slip_dribble(dribbler *d, int err, void *hunoz)
49f86fe4
MW
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
1b7185f5
MW
465static 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
49f86fe4
MW
475static 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) {
1b7185f5 491 close_slip(fd);
49f86fe4
MW
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);
b5de3f42 532 slipstate = OK;
49f86fe4
MW
533 break;
534 case SL_ESCESC:
535 DPUTC(&slipbuf, SL_ESC);
b5de3f42 536 slipstate = OK;
49f86fe4
MW
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:
b5de3f42 544 moan("unexpected escape char 0x%02x", buf[i]);
49f86fe4
MW
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
aec8bcc8
MW
574static void slip_term(int n, void *fdp)
575 { close_slip(*(int *)fdp); }
576
49f86fe4
MW
577static void slipif(void)
578{
579 int fd;
580 dstr d = DSTR_INIT;
581 struct sockaddr_un sun;
aec8bcc8 582 sig term;
49f86fe4
MW
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
aec8bcc8
MW
607 sig_init(&sel);
608 sig_add(&term, SIGTERM, slip_term, &fd);
609 sig_add(&term, SIGINT, slip_term, &fd);
610
49f86fe4
MW
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
635static 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
653static 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
687static void put(void) { shovel(STDIN_FILENO, make_sock('>')); }
688static void get(void) { shovel(make_sock('<'), STDOUT_FILENO); }
689
1e14551a
MW
690/*----- Flooding and sinking ----------------------------------------------*/
691
692/* --- Connection jobs --- */
693
694typedef struct conninfo {
695 struct conninfo *next;
696 conn c;
697} conninfo;
698
699#define MAXCONN 32
700static conninfo conns[MAXCONN], *freeconns;
701
702static void (*connhook)(int, conninfo *);
703
704static void connrecycle(conninfo *c) { c->next = freeconns; freeconns = c; }
705
706static void connerr(conninfo *c)
707{
708 if (errno == EAGAIN) connrecycle(c);
709 else die(EXIT_FAILURE, "connect: %s", strerror(errno));
710}
711
712static void connected(int fd, void *p)
713 { if (fd == -1) connerr(p); else connhook(fd, p); }
714
715static 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
728static int timerflag;
729
5393b556 730static void conndrip(struct timeval *tv, void *hunoz)
1e14551a
MW
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
741static 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
75ed082d 769static void close_sink(gobbler *g, int err, void *p)
1e14551a
MW
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
796static 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 }
75ed082d 806 make_gobbler(fd, close_sink, c);
1e14551a
MW
807}
808
809static void sink(void) { connloop(sink_connected); }
810
811/* --- Flooding --- */
812
75ed082d 813static void close_flood(dribbler *d, int err, void *p)
1e14551a
MW
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
822static 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++;
75ed082d 838 d = make_dribbler(fd, close_flood, c);
1e14551a
MW
839 enqueue_dribble(d, pn);
840}
841
842static void flood(void) { connloop(flood_connected); }
843
49f86fe4
MW
844/*----- Main code ---------------------------------------------------------*/
845
1e14551a 846static void usage(FILE *fp) { pquis(fp, "Usage: $ [-fgps] SOCKET\n"); }
49f86fe4
MW
847
848static void version(void)
849 { pquis(stdout, "$ (" PACKAGE " version " VERSION")\n"); }
850
851static void help(void)
852{
853 version();
854 fputc('\n', stdout);
855 usage(stdout);
856 puts("\n\
857With no options, provides a SLIP interface for TrIPE.\n\
858\n\
859Options:\n\
1e14551a
MW
860 -f, --flood Send packets to TrIPE as fast as possible.\n\
861 -g, --get Receive packet from TrIPE and write to stdout.\n\
49f86fe4 862 -p, --put Send packet on stdin to TrIPE.\n\
1e14551a 863 -s, --sink Slurp packets out of TrIPE and display progress.");
49f86fe4
MW
864}
865
866int 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' },
1e14551a
MW
878 { "flood", 0, 0, 'f' },
879 { "sink", 0, 0, 's' },
49f86fe4
MW
880 { 0, 0, 0, 0 }
881 };
1e14551a 882 i = mdwopt(argc, argv, "hvpgfs", opt, 0, 0, 0);
49f86fe4
MW
883 if (i < 0)
884 break;
885 switch (i) {
886 case 'h': help(); return (0);
887 case 'v': version(); return (0);
1e14551a 888 case 'p': case 'g': case 's': case 'f': mode = i; break;
49f86fe4
MW
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;
1e14551a
MW
899 case 'f': flood(); break;
900 case 's': sink(); break;
49f86fe4
MW
901 }
902 return (0);
903}
904
905/*----- That's all, folks -------------------------------------------------*/