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