X-Git-Url: https://git.distorted.org.uk/~mdw/tripe/blobdiff_plain/b5de3f42f0f35a489f6ea1d23dbcf0e15ccebcf0..5ae728a6a2bb6db57a8586e839221cc33cf9c69c:/uslip/uslip.c diff --git a/uslip/uslip.c b/uslip/uslip.c index eb60eb69..629da123 100644 --- a/uslip/uslip.c +++ b/uslip/uslip.c @@ -9,19 +9,18 @@ * * This file is part of Trivial IP Encryption (TrIPE). * - * TrIPE is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * TrIPE is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at your + * option) any later version. * - * TrIPE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * TrIPE is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. * * You should have received a copy of the GNU General Public License - * along with TrIPE; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with TrIPE. If not, see . */ /*----- Header files ------------------------------------------------------*/ @@ -47,13 +46,17 @@ #include #include +#include +#include #include #include #include #include #include #include +#include #include +#include #include "slip.h" @@ -81,6 +84,8 @@ typedef struct client { typedef struct gobbler { sel_file f; + void (*done)(struct gobbler *, int, void *); + void *p; } gobbler; typedef struct dribbler { @@ -130,16 +135,15 @@ static void socketaddr(struct sockaddr_un *sun, size_t *sz) static void initqueue(pkq *q) { q->head = 0; q->tail = &q->head; } -static pkq_node *make_pkqnode(void *p, size_t n) +static pkq_node *make_pkqnode(const void *p, size_t n) { pkq_node *pn; - if (!n) - return (0); + if (!n) return (0); pn = CREATE(pkq_node); pn->next = 0; pn->buf = xmalloc(n); - memcpy(pn->buf, p, n); + if (p) memcpy(pn->buf, p, n); pn->sz = n; pn->n = 0; return (pn); @@ -149,8 +153,7 @@ static int enqueue(pkq *q, pkq_node *pn) { int rc = 0; - if (!pn) - return (0); + if (!pn) return (0); rc = !q->head; pn->next = 0; *q->tail = pn; @@ -188,14 +191,14 @@ static void destroy_pkq(pkq *q) /*----- Gobblers ----------------------------------------------------------* * * A gobbler just eats everything it sees on its input descriptor. - * Eventually, when it sees end-of-file, it closes the input descriptor and - * quits. + * Eventually, when it sees end-of-file, it closes the input descriptor, + * calls a user-supplied calback function, and quits. */ -static void gobbler_close(gobbler *g) - { if (g->f.fd != -1) { sel_rmfile(&g->f); g->f.fd = -1; } } +static void close_gobbler(gobbler *g) + { if (g->f.fd != -1) { sel_rmfile(&g->f); close(g->f.fd); g->f.fd = -1; } } -static void gobbler_destroy(gobbler *g) { gobbler_close(g); DESTROY(g); } +static void destroy_gobbler(gobbler *g) { close_gobbler(g); DESTROY(g); } static void do_gobble_in(int fd, unsigned mode, void *p) { @@ -209,21 +212,27 @@ static void do_gobble_in(int fd, unsigned mode, void *p) break; else { moan("read (gobble): %s", strerror(errno)); - gobbler_close(g); + if (g->done) g->done(g, errno, g->p); + close_gobbler(g); break; } } else if (n == 0) { - gobbler_close(g); + if (g->done) g->done(g, 0, g->p); + close_gobbler(g); break; } } } -static gobbler *make_gobbler(int fd) +static gobbler *make_gobbler(int fd, + void (*done)(gobbler *, int, void *), + void *p) { gobbler *g; g = CREATE(gobbler); + g->done = done; + g->p = p; sel_initfile(&sel, &g->f, fd, SEL_READ, do_gobble_in, g); sel_addfile(&g->f); do_gobble_in(fd, SEL_READ, g); @@ -311,7 +320,7 @@ static void done_client_dribble(dribbler *d, int err, void *p) { if (err) moan("write (client): %s", strerror(err)); - gobbler_destroy(p); + destroy_gobbler(p); destroy_dribbler(d); reasons--; } @@ -336,7 +345,7 @@ static void dequeue_to_waiter(void) } } -static void client_destroy(client *c) +static void destroy_client(client *c) { sel_rmfile(&c->f); close(c->f.fd); @@ -365,7 +374,7 @@ static void do_client_in(int fd, unsigned mode, void *p) break; else { moan("read (client): %s", strerror(errno)); - client_destroy(c); + destroy_client(c); return; } } else if (n == 0) { @@ -374,7 +383,7 @@ static void do_client_in(int fd, unsigned mode, void *p) if (enqueue_dribble(dribble_out, make_pkqnode(c->d.buf, c->d.len))) reasons++; } - client_destroy(c); + destroy_client(c); return; } if (c->mode == '?') { @@ -385,7 +394,7 @@ static void do_client_in(int fd, unsigned mode, void *p) break; case '<': w = CREATE(waiter); - w->g = make_gobbler(fd); + w->g = make_gobbler(fd, 0, 0); w->next = 0; w->fd = fd; *wait_tail = w; @@ -396,7 +405,7 @@ static void do_client_in(int fd, unsigned mode, void *p) return; default: moan("bad client mode `%c'", buf[0]); - client_destroy(c); + destroy_client(c); return; } } @@ -442,7 +451,7 @@ static void do_accept(int fd, unsigned mode, void *hunoz) /*----- Main daemon -------------------------------------------------------*/ -static void done_slip_dribble(dribbler *d, int err, void *p) +static void done_slip_dribble(dribbler *d, int err, void *hunoz) { if (!err) reasons--; @@ -452,6 +461,16 @@ static void done_slip_dribble(dribbler *d, int err, void *p) cripple_dribbler(d); } +static void close_slip(int fd) +{ + switch (slipstate) { + case SYNC1: case SYNC2: case START: case BAD: break; + default: moan("eof found while processing packet (discarding)"); break; + } + close(fd); sel_rmfile(&slip_in); + reasons--; +} + static void do_slip_in(int fd, unsigned mode, void *hunoz) { ssize_t i, n; @@ -468,19 +487,7 @@ static void do_slip_in(int fd, unsigned mode, void *hunoz) for (;;) { n = read(fd, buf, sizeof(buf)); if (n == 0) { - switch (slipstate) { - case SYNC1: - case SYNC2: - case START: - case BAD: - break; - default: - moan("eof found while processing packet (discarding)"); - break; - } - close(fd); - sel_rmfile(&slip_in); - reasons--; + close_slip(fd); return; } else if (n < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) @@ -563,11 +570,15 @@ static void do_slip_in(int fd, unsigned mode, void *hunoz) } } +static void slip_term(int n, void *fdp) + { close_slip(*(int *)fdp); } + static void slipif(void) { int fd; dstr d = DSTR_INIT; struct sockaddr_un sun; + sig term; size_t sz; /* --- Make the socket --- */ @@ -592,6 +603,11 @@ static void slipif(void) dribble_out = make_dribbler(STDOUT_FILENO, done_slip_dribble, 0); sel_addfile(&slip_in); + sig_init(&sel); + sig_add(&term, SIGTERM, slip_term, &fd); + sig_add(&term, SIGHUP, slip_term, &fd); + sig_add(&term, SIGINT, slip_term, &fd); + initqueue(&q_in); reasons++; @@ -605,7 +621,7 @@ static void slipif(void) /* --- Main loop --- */ while (reasons) { - if (sel_select(&sel)) + if (sel_select(&sel) && errno != EINTR) die(EXIT_FAILURE, "select: %s", strerror(errno)); } @@ -671,9 +687,163 @@ static void shovel(int from, int to) static void put(void) { shovel(STDIN_FILENO, make_sock('>')); } static void get(void) { shovel(make_sock('<'), STDOUT_FILENO); } +/*----- Flooding and sinking ----------------------------------------------*/ + +/* --- Connection jobs --- */ + +typedef struct conninfo { + struct conninfo *next; + conn c; +} conninfo; + +#define MAXCONN 32 +static conninfo conns[MAXCONN], *freeconns; + +static void (*connhook)(int, conninfo *); + +static void connrecycle(conninfo *c) { c->next = freeconns; freeconns = c; } + +static void connerr(conninfo *c) +{ + if (errno == EAGAIN) connrecycle(c); + else die(EXIT_FAILURE, "connect: %s", strerror(errno)); +} + +static void connected(int fd, void *p) + { if (fd == -1) connerr(p); else connhook(fd, p); } + +static void conndoconnect(conninfo *c) +{ + int fd; + struct sockaddr_un sun; + size_t sz; + + if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) + die(EXIT_FAILURE, "socket: %s", strerror(errno)); + socketaddr(&sun, &sz); + if (conn_init(&c->c, &sel, fd, (struct sockaddr *)&sun, sz, connected, c)) + connerr(c); +} + +static int timerflag; + +static void conndrip(struct timeval *tv, void *hunoz) +{ + conninfo *c, *cc; + + timerflag = 0; + while (freeconns) { + c = freeconns; freeconns = 0; + while (c) { cc = c->next; conndoconnect(c); c = cc; } + } +} + +static void connloop(void (*connected)(int, conninfo *)) +{ + int i; + conninfo **cc; + sel_timer t; + struct timeval tv; + + connhook = connected; + for (i = 0, cc = &freeconns; i < MAXCONN; i++) { + *cc = &conns[i]; + cc = &conns[i].next; + } + *cc = 0; + + for (;;) { + if (freeconns && !timerflag) { + gettimeofday(&tv, 0); + TV_ADDL(&tv, &tv, 0, 10000); + sel_addtimer(&sel, &t, &tv, conndrip, 0); + timerflag = 1; + } + if (sel_select(&sel)) + die(EXIT_FAILURE, "select: %s", strerror(errno)); + } +} + +/* --- Sinking (glug glug) --- */ + +static void close_sink(gobbler *g, int err, void *p) +{ + static char baton[4] = "/-\\|"; + static int pos = 0; + static int count = 0; + static int state = '?'; + + conninfo *c = p; + + if (!err) { + if (state == '?') + state = isatty(STDOUT_FILENO) ? 'y' : 'n'; + if (state == 'y') { + if (count) count--; + else { + putchar(baton[pos]); + putchar('\b'); + fflush(stdout); + pos++; + if (pos >= sizeof(baton)) pos = 0; + count = 128; + } + } + } + connrecycle(c); +} + +static void sink_connected(int fd, conninfo *c) +{ + char dir = '<'; + + if (write(fd, &dir, 1) != 1) { + moan("write: %s (continuing)", strerror(errno)); + close(fd); + connrecycle(c); + return; + } + make_gobbler(fd, close_sink, c); +} + +static void sink(void) { connloop(sink_connected); } + +/* --- Flooding --- */ + +static void close_flood(dribbler *d, int err, void *p) +{ + conninfo *c = p; + + if (err) moan("write: %s (continuing)\n", strerror(errno)); + destroy_dribbler(d); + connrecycle(c); +} + +static void flood_connected(int fd, conninfo *c) +{ + static uint32 seq; + + dribbler *d; + pkq_node *pn; + int i; + +#define FLOOD_PKSZ 1024 + + pn = make_pkqnode(0, 1 + FLOOD_PKSZ); + pn->buf[0] = '>'; + STORE32(pn->buf + 1, seq); + for (i = 4; i < FLOOD_PKSZ; i++) + pn->buf[i + 1] = i & 0xff; + seq++; + d = make_dribbler(fd, close_flood, c); + enqueue_dribble(d, pn); +} + +static void flood(void) { connloop(flood_connected); } + /*----- Main code ---------------------------------------------------------*/ -static void usage(FILE *fp) { pquis(fp, "Usage: $ [-pg] SOCKET\n"); } +static void usage(FILE *fp) { pquis(fp, "Usage: $ [-fgps] SOCKET\n"); } static void version(void) { pquis(stdout, "$ (" PACKAGE " version " VERSION")\n"); } @@ -687,8 +857,10 @@ static void help(void) With no options, provides a SLIP interface for TrIPE.\n\ \n\ Options:\n\ + -f, --flood Send packets to TrIPE as fast as possible.\n\ + -g, --get Receive packet from TrIPE and write to stdout.\n\ -p, --put Send packet on stdin to TrIPE.\n\ - -g, --get Receive packet from TrIPE and write to stdout."); + -s, --sink Slurp packets out of TrIPE and display progress."); } int main(int argc, char *argv[]) @@ -703,15 +875,17 @@ int main(int argc, char *argv[]) { "version", 0, 0, 'v' }, { "put", 0, 0, 'p' }, { "get", 0, 0, 'g' }, + { "flood", 0, 0, 'f' }, + { "sink", 0, 0, 's' }, { 0, 0, 0, 0 } }; - i = mdwopt(argc, argv, "hvpg", opt, 0, 0, 0); + i = mdwopt(argc, argv, "hvpgfs", opt, 0, 0, 0); if (i < 0) break; switch (i) { case 'h': help(); return (0); case 'v': version(); return (0); - case 'p': case 'g': mode = i; break; + case 'p': case 'g': case 's': case 'f': mode = i; break; default: usage(stderr); exit(EXIT_FAILURE); break; } } @@ -722,6 +896,8 @@ int main(int argc, char *argv[]) case 'd': slipif(); break; case 'p': put(); break; case 'g': get(); break; + case 'f': flood(); break; + case 's': sink(); break; } return (0); }