3 * A simple SLIP implementation drivable from the command-line
5 * (c) 2008 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of Trivial IP Encryption (TrIPE).
12 * TrIPE is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU General Public License as published by the Free
14 * Software Foundation; either version 3 of the License, or (at your
15 * option) any later version.
17 * TrIPE is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 * You should have received a copy of the GNU General Public License
23 * along with TrIPE. If not, see <https://www.gnu.org/licenses/>.
26 /*----- Header files ------------------------------------------------------*/
38 #include <sys/types.h>
42 #include <sys/socket.h>
44 #include <netinet/in.h>
45 #include <arpa/inet.h>
48 #include <mLib/alloc.h>
49 #include <mLib/bits.h>
50 #include <mLib/conn.h>
51 #include <mLib/dstr.h>
52 #include <mLib/fdflags.h>
53 #include <mLib/mdwopt.h>
54 #include <mLib/quis.h>
55 #include <mLib/report.h>
65 /*----- Data structures ---------------------------------------------------*/
67 typedef struct pkq_node {
68 struct pkq_node *next;
74 pkq_node *head, **tail;
77 enum { START, OK, ESC, BAD, SYNC1, SYNC2 };
79 typedef struct client {
85 typedef struct gobbler {
87 void (*done)(struct gobbler *, int, void *);
91 typedef struct dribbler {
94 void (*done)(struct dribbler *, int, void *);
98 typedef struct waiter {
104 /*----- Static variables --------------------------------------------------*/
107 static dribbler *dribble_out;
108 static dstr slipbuf = DSTR_INIT;
109 static int slipstate = SYNC1;
110 static sel_file slip_in, listener;
111 static sel_state sel;
112 static unsigned reasons;
113 static waiter *wait_head, **wait_tail = &wait_head;
114 static unsigned char buf[16384];
115 static const char *name;
117 /*----- Utilities ---------------------------------------------------------*/
119 static void socketaddr(struct sockaddr_un *sun, size_t *sz)
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);
127 *sz = n + offsetof(struct sockaddr_un, sun_path);
130 /*------ Packet queue -----------------------------------------------------*
132 * A packet queue contains a sequence of octet strings. Packets can be added
133 * to the end and read off the front.
136 static void initqueue(pkq *q) { q->head = 0; q->tail = &q->head; }
138 static pkq_node *make_pkqnode(const void *p, size_t n)
143 pn = CREATE(pkq_node);
145 pn->buf = xmalloc(n);
146 if (p) memcpy(pn->buf, p, n);
152 static int enqueue(pkq *q, pkq_node *pn)
164 static void destroy_pkqnode(pkq_node *pn) { xfree(pn->buf); DESTROY(pn); }
166 static int dequeue(pkq *q, int freep)
168 pkq_node *pn = q->head;
180 static void destroy_pkq(pkq *q)
184 for (pn = q->head; pn; pn = pnn) {
188 q->head = 0; q->tail = &q->head;
191 /*----- Gobblers ----------------------------------------------------------*
193 * A gobbler just eats everything it sees on its input descriptor.
194 * Eventually, when it sees end-of-file, it closes the input descriptor,
195 * calls a user-supplied calback function, and quits.
198 static void close_gobbler(gobbler *g)
199 { if (g->f.fd != -1) { sel_rmfile(&g->f); close(g->f.fd); g->f.fd = -1; } }
201 static void destroy_gobbler(gobbler *g) { close_gobbler(g); DESTROY(g); }
203 static void do_gobble_in(int fd, unsigned mode, void *p)
209 n = read(fd, buf, sizeof(buf));
211 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
214 moan("read (gobble): %s", strerror(errno));
215 if (g->done) g->done(g, errno, g->p);
220 if (g->done) g->done(g, 0, g->p);
227 static gobbler *make_gobbler(int fd,
228 void (*done)(gobbler *, int, void *),
236 sel_initfile(&sel, &g->f, fd, SEL_READ, do_gobble_in, g);
238 do_gobble_in(fd, SEL_READ, g);
242 /*----- Dribbler ----------------------------------------------------------*
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
251 static void cripple_dribbler(dribbler *d) { close(d->f.fd); d->f.fd = -1; }
253 static void destroy_dribbler(dribbler *d)
254 { cripple_dribbler(d); DESTROY(d); }
256 static void dribble_done(dribbler *d, int err)
262 d->done(d, err, d->p);
265 static void do_dribble_out(int fd, unsigned mode, void *p)
273 n = write(fd, pn->buf + pn->n, pn->sz - pn->n);
275 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
278 dribble_done(d, errno);
283 if (pn->n == pn->sz && dequeue(&d->q, 1)) {
291 static int enqueue_dribble(dribbler *d, pkq_node *pn)
297 if (enqueue(&d->q, pn)) {
299 do_dribble_out(d->f.fd, SEL_WRITE, d);
305 static dribbler *make_dribbler(int fd,
306 void (*done)(dribbler *, int, void *),
309 dribbler *d = CREATE(dribbler);
310 sel_initfile(&sel, &d->f, fd, SEL_WRITE, do_dribble_out, d);
317 /*----- Clients -----------------------------------------------------------*/
319 static void done_client_dribble(dribbler *d, int err, void *p)
322 moan("write (client): %s", strerror(err));
328 static void dequeue_to_waiter(void)
334 while (q_in.head && wait_head) {
338 wait_tail = &wait_head;
339 d = make_dribbler(w->fd, done_client_dribble, w->g);
342 if (dequeue(&q_in, 0))
344 enqueue_dribble(d, pn);
348 static void destroy_client(client *c)
357 static void do_client_in(int fd, unsigned mode, void *p)
363 /* --- Attention --- *
365 * The queue for outbound packets is SLIP-encoded; we need to encode it
366 * here. The queue for inbound packets is raw.
370 n = read(fd, buf, sizeof(buf));
373 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
376 moan("read (client): %s", strerror(errno));
381 if (c->mode == '>') {
382 DPUTC(&c->d, SL_END);
383 if (enqueue_dribble(dribble_out, make_pkqnode(c->d.buf, c->d.len)))
389 if (c->mode == '?') {
397 w->g = make_gobbler(fd, 0, 0);
401 wait_tail = &w->next;
407 moan("bad client mode `%c'", buf[0]);
412 for (i = i0; i < n; i++) {
415 DPUTC(&c->d, SL_ESC);
416 DPUTC(&c->d, SL_ESCESC);
419 DPUTC(&c->d, SL_ESC);
420 DPUTC(&c->d, SL_ESCEND);
423 DPUTC(&c->d, buf[i]);
430 static void do_accept(int fd, unsigned mode, void *hunoz)
433 struct sockaddr_un sun;
434 socklen_t n = sizeof(sun);
437 if ((nfd = accept(fd, (struct sockaddr *)&sun, &n)) < 0) {
438 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
441 die(EXIT_FAILURE, "accept: %s", strerror(errno));
446 fdflags(nfd, O_NONBLOCK, O_NONBLOCK, 0, 0);
447 sel_initfile(&sel, &c->f, nfd, SEL_READ, do_client_in, c);
452 /*----- Main daemon -------------------------------------------------------*/
454 static void done_slip_dribble(dribbler *d, int err, void *hunoz)
458 else if (err != EPIPE)
459 die(EXIT_FAILURE, "write (slip): %s", strerror(errno));
464 static void close_slip(int fd)
467 case SYNC1: case SYNC2: case START: case BAD: break;
468 default: moan("eof found while processing packet (discarding)"); break;
470 close(fd); sel_rmfile(&slip_in);
474 static void do_slip_in(int fd, unsigned mode, void *hunoz)
478 /* --- Attention --- *
480 * The queue for inbound packets contains raw data; we need to decode it
481 * here. The queue for outbound packets is SLIP-encoded.
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.
488 n = read(fd, buf, sizeof(buf));
493 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
495 die(EXIT_FAILURE, "read (slip in): %s", strerror(errno));
497 for (i = 0; i < n; i++) {
530 DPUTC(&slipbuf, SL_END);
534 DPUTC(&slipbuf, SL_ESC);
538 moan("found escaped end byte (discard packet and resync");
543 moan("unexpected escape char 0x%02x", buf[i]);
556 if (enqueue(&q_in, make_pkqnode(slipbuf.buf, slipbuf.len)))
563 DPUTC(&slipbuf, buf[i]);
573 static void slip_term(int n, void *fdp)
574 { close_slip(*(int *)fdp); }
576 static void slipif(void)
580 struct sockaddr_un sun;
584 /* --- Make the socket --- */
586 if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
587 die(EXIT_FAILURE, "socket: %s", strerror(errno));
588 socketaddr(&sun, &sz);
589 if (bind(fd, (struct sockaddr *)&sun, sz))
590 die(EXIT_FAILURE, "bind: %s", strerror(errno));
592 die(EXIT_FAILURE, "listen: %s", strerror(errno));
594 /* --- Set up listeners for things --- */
596 fdflags(fd, O_NONBLOCK, O_NONBLOCK, 0, 0);
597 sel_initfile(&sel, &listener, fd, SEL_READ, do_accept, 0);
598 sel_addfile(&listener);
600 fdflags(STDIN_FILENO, O_NONBLOCK, O_NONBLOCK, 0, 0);
601 fdflags(STDOUT_FILENO, O_NONBLOCK, O_NONBLOCK, 0, 0);
602 sel_initfile(&sel, &slip_in, STDIN_FILENO, SEL_READ, do_slip_in, 0);
603 dribble_out = make_dribbler(STDOUT_FILENO, done_slip_dribble, 0);
604 sel_addfile(&slip_in);
607 sig_add(&term, SIGTERM, slip_term, &fd);
608 sig_add(&term, SIGHUP, slip_term, &fd);
609 sig_add(&term, SIGINT, slip_term, &fd);
614 /* --- Write the interface name --- */
616 dstr_putf(&d, "%s-%s\n", QUIS, name);
617 if (enqueue_dribble(dribble_out, make_pkqnode(d.buf, d.len)))
621 /* --- Main loop --- */
624 if (sel_select(&sel) && errno != EINTR)
625 die(EXIT_FAILURE, "select: %s", strerror(errno));
633 /*----- Putting and getting -----------------------------------------------*/
635 static int make_sock(int mode)
637 struct sockaddr_un sun;
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));
648 if (write(fd, &ch, 1) < 0)
649 die(EXIT_FAILURE, "write (mode): %s", strerror(errno));
653 static void shovel(int from, int to)
660 n = read(from, buf, sizeof(buf));
665 die(EXIT_FAILURE, "read (shovel): %s", strerror(errno));
672 n = write(to, p, sz);
677 die(EXIT_FAILURE, "write (shovel): %s", strerror(errno));
687 static void put(void) { shovel(STDIN_FILENO, make_sock('>')); }
688 static void get(void) { shovel(make_sock('<'), STDOUT_FILENO); }
690 /*----- Flooding and sinking ----------------------------------------------*/
692 /* --- Connection jobs --- */
694 typedef struct conninfo {
695 struct conninfo *next;
700 static conninfo conns[MAXCONN], *freeconns;
702 static void (*connhook)(int, conninfo *);
704 static void connrecycle(conninfo *c) { c->next = freeconns; freeconns = c; }
706 static void connerr(conninfo *c)
708 if (errno == EAGAIN) connrecycle(c);
709 else die(EXIT_FAILURE, "connect: %s", strerror(errno));
712 static void connected(int fd, void *p)
713 { if (fd == -1) connerr(p); else connhook(fd, p); }
715 static void conndoconnect(conninfo *c)
718 struct sockaddr_un sun;
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))
728 static int timerflag;
730 static void conndrip(struct timeval *tv, void *hunoz)
736 c = freeconns; freeconns = 0;
737 while (c) { cc = c->next; conndoconnect(c); c = cc; }
741 static void connloop(void (*connected)(int, conninfo *))
748 connhook = connected;
749 for (i = 0, cc = &freeconns; i < MAXCONN; i++) {
756 if (freeconns && !timerflag) {
757 gettimeofday(&tv, 0);
758 TV_ADDL(&tv, &tv, 0, 10000);
759 sel_addtimer(&sel, &t, &tv, conndrip, 0);
762 if (sel_select(&sel))
763 die(EXIT_FAILURE, "select: %s", strerror(errno));
767 /* --- Sinking (glug glug) --- */
769 static void close_sink(gobbler *g, int err, void *p)
771 static char baton[4] = "/-\\|";
773 static int count = 0;
774 static int state = '?';
780 state = isatty(STDOUT_FILENO) ? 'y' : 'n';
788 if (pos >= sizeof(baton)) pos = 0;
796 static void sink_connected(int fd, conninfo *c)
800 if (write(fd, &dir, 1) != 1) {
801 moan("write: %s (continuing)", strerror(errno));
806 make_gobbler(fd, close_sink, c);
809 static void sink(void) { connloop(sink_connected); }
811 /* --- Flooding --- */
813 static void close_flood(dribbler *d, int err, void *p)
817 if (err) moan("write: %s (continuing)\n", strerror(errno));
822 static void flood_connected(int fd, conninfo *c)
830 #define FLOOD_PKSZ 1024
832 pn = make_pkqnode(0, 1 + FLOOD_PKSZ);
834 STORE32(pn->buf + 1, seq);
835 for (i = 4; i < FLOOD_PKSZ; i++)
836 pn->buf[i + 1] = i & 0xff;
838 d = make_dribbler(fd, close_flood, c);
839 enqueue_dribble(d, pn);
842 static void flood(void) { connloop(flood_connected); }
844 /*----- Main code ---------------------------------------------------------*/
846 static void usage(FILE *fp) { pquis(fp, "Usage: $ [-fgps] SOCKET\n"); }
848 static void version(void)
849 { pquis(stdout, "$ (" PACKAGE " version " VERSION")\n"); }
851 static void help(void)
857 With no options, provides a SLIP interface for TrIPE.\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.");
866 int main(int argc, char *argv[])
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' },
882 i = mdwopt(argc, argv, "hvpgfs", opt, 0, 0, 0);
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;
892 if (argc - optind != 1) { usage(stderr); exit(EXIT_FAILURE); }
894 signal(SIGPIPE, SIG_IGN);
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;
905 /*----- That's all, folks -------------------------------------------------*/