From: Mark Wooding Date: Mon, 8 Dec 2008 12:11:20 +0000 (+0000) Subject: uslip: New program providing a fake SLIP interface. X-Git-Tag: 1.0.0pre8~56 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/tripe/commitdiff_plain/49f86fe47a4da6219516d0fc28feaa0054abebe8 uslip: New program providing a fake SLIP interface. This is useful for doing testing on a single machine. We'll work out how to test the system-specific tunnel drivers later, but that really will need multiple machines. --- diff --git a/Makefile.am b/Makefile.am index 21412150..504ca56b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -32,6 +32,7 @@ SUBDIRS = ## Core components. SUBDIRS += common +SUBDIRS += uslip SUBDIRS += client SUBDIRS += server SUBDIRS += proxy @@ -109,6 +110,9 @@ EXTRA_DIST += debian/tripe.install EXTRA_DIST += debian/tripe.postinst EXTRA_DIST += debian/tripe.logrotate +## uslip +EXTRA_DIST += debian/tripe-uslip.install + ## keys EXTRA_DIST += debian/tripe-keys.install diff --git a/common/Makefile.am b/common/Makefile.am index e329db15..27ba6941 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -34,6 +34,7 @@ libtripe_a_SOURCES = ## Protocol definitions. libtripe_a_SOURCES += protocol.h +libtripe_a_SOURCES += slip.h ## Miscellaneous utilties. libtripe_a_SOURCES += util.c util.h diff --git a/configure.ac b/configure.ac index 58ee877f..0eeb1ec5 100644 --- a/configure.ac +++ b/configure.ac @@ -290,6 +290,7 @@ AC_CONFIG_TESTDIR([t]) AC_CONFIG_FILES( [Makefile] [common/Makefile] + [uslip/Makefile] [client/Makefile] [server/Makefile] [proxy/Makefile] diff --git a/debian/.gitignore b/debian/.gitignore index 97c07248..87f0ea24 100644 --- a/debian/.gitignore +++ b/debian/.gitignore @@ -17,3 +17,4 @@ tripe-wireshark tripemon tripe-keys tripe-ethereal +tripe-uslip diff --git a/debian/control b/debian/control index 568c9f53..d59c2629 100644 --- a/debian/control +++ b/debian/control @@ -29,6 +29,17 @@ Description: Forward UDP packets over a stream packets on standard input and output; it also natively understands TCP sockets. Anything else can probably be fudged up with a port forwarder. +Package: tripe-uslip +Architecture: any +Depends: ${shlibs:Depends} +Recommends: tripe +Description: Trivial IP Encryption: a simple virtual private network + TrIPE is a simple VPN protocol. It uses cryptography to ensure secrecy + and authenticity of packets it sends and receives. + . + The tripe-uslip tool provides a fake SLIP tunnel which can be driven from + scripts and is useful for testing. `If in doubt, say N here.' + Package: tripe-wireshark Architecture: any Depends: wireshark-common (= ${tripe:Wireshark-Version}) diff --git a/debian/tripe-uslip.install b/debian/tripe-uslip.install new file mode 100644 index 00000000..9115b0ff --- /dev/null +++ b/debian/tripe-uslip.install @@ -0,0 +1,2 @@ +debian/tmp/usr/bin/tripe-uslip +debian/tmp/usr/share/man/man1/tripe-uslip.1 diff --git a/uslip/Makefile.am b/uslip/Makefile.am new file mode 100644 index 00000000..182f8391 --- /dev/null +++ b/uslip/Makefile.am @@ -0,0 +1,43 @@ +### -*-makefile-*- +### +### Build script for uslip +### +### (c) 2008 Straylight/Edgeware +### + +###----- Licensing notice --------------------------------------------------- +### +### 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 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. + +include $(top_srcdir)/vars.am + +bin_PROGRAMS = +man_MANS = + +###-------------------------------------------------------------------------- +### Fake slip interface. + +## The program. +bin_PROGRAMS += tripe-uslip +tripe_uslip_SOURCES = uslip.c + +## Docuemntation. +man_MANS += tripe-uslip.1 +CLEANFILES += tripe-uslip.1 +EXTRA_DIST += tripe-uslip.1.in + +###----- That's all, folks -------------------------------------------------- diff --git a/uslip/tripe-uslip.1.in b/uslip/tripe-uslip.1.in new file mode 100644 index 00000000..16b0a7e5 --- /dev/null +++ b/uslip/tripe-uslip.1.in @@ -0,0 +1,212 @@ +.\" -*-nroff-*- +.\" +.\" Documentation for uslip +.\" +.\" (c) 2008 Straylight/Edgeware. +.\" + +.\"----- Licensing notice --------------------------------------------------- +.\" +.\" 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 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. +. +.\"-------------------------------------------------------------------------- +.so ../defs.man.in \" @@@PRE@@@ +. +.\"-------------------------------------------------------------------------- +.TH tripe-uslip 1 "7 April 2008" "Straylight/Edgeware" "TrIPE: Trivial IP Encryption" +. +.\"-------------------------------------------------------------------------- +.SH "NAME" +. +tripe-uslip \- fake SLIP interface for testing tripe +. +.\"-------------------------------------------------------------------------- +.SH "SYNOPSIS" +. +.B tripe-uslip +.RB [ \-gp ] +.I socket +. +.\"-------------------------------------------------------------------------- +.SH "DESCRIPTION" +. +The +.B tripe-uslip +provides a mechanism for pushing packets of data into a +.BR tripe (8) +server, and extracting them. This is useful for testing the server; it +isn't useful in a production environment. +.SS "Overview and theory of operation" +Testing the +.BR tripe (8) +server is difficult: configuring network interfaces and creating tunnels +requires root privileges (undesirable for a program under development!) +and testing that it successfully transports network packets needs two +separate instances running on separate machines. (If both ends of a +tunnel are on the same host then the packets won't actually go over the +tunnel: the kernel will just loop them back internally.) +.PP +The +.B tripe-uslip +program implements the interface required of a dynamic allocation script +(see the +.BR tripe (8) +manual for details). However, it doesn't actually make a network +interface. +.PP +You use it by setting +.IP +.BI TRIPE_SLIPIF= dir /tripe-uslip +.PP +in the environment passed to the +.BR tripe (8) +server (and, typically, passing it the +.B \-tslip +command-line option). When you add a new peer with the +.B ADD +.I peer +.IR address ... +administration command, the server runs +.IB dir /tripe-uslip +.IR peer , +which in turn creates a Unix-domain socket called +.I peer +in the server's current directory. If you run +.IP +.B tripe-uslip +.B \-p +.I peer +.BI < file +.PP +in this directory, then the contents of +.I file +are sent to +.B tripe +as if they were a network packet to be encrypted and transmitted over +its tunnel. (Any method of providing the data on standard input is +acceptable: it doesn't have to be a regular file. In particular, pipes +are fine. Note also that +.B tripe +doesn't actually care that the data it receives is actually network +packets: it can be absolutely anything you like.) +.PP +If you run +.IP +.B tripe-uslip +.B \-g +.I peer +.BI > file +.PP +then the contents of the next network packet the server decrypts will be +written to the +.IR file . +(Again, you can use pipes or whatever.) +.PP +The +.B tripe-uslip +program is fully nonblocking. This means that you won't deadlock the +server by attaching duff scripts to it via +.BR tripe-uslip . +.SS "Technical details" +Without options, +.B tripe-uslip +performs the following actions. +.hP \*o +It creates a Unix-domain socket with name +.I socket +(which will typically be the name of the peer that +.BR tripe (8) +created this interface for). +.hP \*o +It writes the string +.BI tripe-uslip- socket +to its standard output, followed by a newline. +.hP \*o +It reads and discards up to two bytes with value 192 (SLIP +.BR END ) +on stdin. +.hP \*o +It enters its main loop, during which it accepts and processes client +connections, and reads and writes SLIP-encoded packets on standard input +and output. Unless a fatal error occurs, the main loop continues until +it (a) has no connected clients, (b) has no packets queued for output to +clients or to standard output, and (c) has seen end-of-file on its +standard input. +.PP +The main loop works as follows. When a SLIP-encoded packet arrives on +standard input, it is decoded and placed on a queue waiting to be read +from a client. If a client connects and writes a packet, the packet is +SLIP-encoded and written to standard output. +.PP +Clients connecting to the +Unix-domain socket send an initial character +.RB ` < ' +to read a packet or +.RB ` > ' +to write. Packets, as far as clients are concerned, consist of +uninterpreted strings of octets and continue until end-of-file. It is +not possible to read or write more than one packet in a single connection. +.PP +The command-line options allow +.B tripe-uslip +to be used from scripts to inject or collect packets. They are as follows. +.TP +.B \-g, \-\-get +Connect to +.IR socket , +read a packet from the socket and write it to standard output. +.TP +.B \-p, \-\-put +Connect to +.IR socket , +read a packet from standard input and write it to the socket. +. +.\"-------------------------------------------------------------------------- +.SH "BUGS" +. +The +.B tripe-uslip +program is intended as a tool for testing the +.BR tripe (8) +server. It is not expected to be useful in production environments. In +particular, it intentionally imposes no limits on queue lengths or +packet sizes, and its internals and interface (one packet per client +connection) are not well-suited for high performance. +.PP +If +.B tripe-uslip +turns out to be useful in other contexts then it might be improved. +Patches are, of course, welcome. +.PP +The initial ignoring of +.B END +bytes is unpleasant but necessary to cope with the +.BR tripe (8) +server, which sends this sequence in order to ensure that it's properly +synchronized with the SLIP interface. +. +.\"-------------------------------------------------------------------------- +.SH "SEE ALSO" +. +.BR tripe (8). +. +.\"-------------------------------------------------------------------------- +.SH "AUTHOR" +. +Mark Wooding, +. +.\"----- That's all, folks -------------------------------------------------- diff --git a/uslip/uslip.c b/uslip/uslip.c new file mode 100644 index 00000000..a552ba9e --- /dev/null +++ b/uslip/uslip.c @@ -0,0 +1,727 @@ +/* -*-c-*- + * + * A simple SLIP implementation drivable from the command-line + * + * (c) 2008 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * 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 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. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "slip.h" + +#undef sun + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct pkq_node { + struct pkq_node *next; + unsigned char *buf; + size_t n, sz; +} pkq_node; + +typedef struct pkq { + pkq_node *head, **tail; +} pkq; + +enum { START, OK, ESC, BAD, SYNC1, SYNC2 }; + +typedef struct client { + sel_file f; + dstr d; + int mode; +} client; + +typedef struct gobbler { + sel_file f; +} gobbler; + +typedef struct dribbler { + sel_file f; + pkq q; + void (*done)(struct dribbler *, int, void *); + void *p; +} dribbler; + +typedef struct waiter { + struct waiter *next; + int fd; + gobbler *g; +} waiter; + +/*----- Static variables --------------------------------------------------*/ + +static pkq q_in; +static dribbler *dribble_out; +static dstr slipbuf = DSTR_INIT; +static int slipstate = SYNC1; +static sel_file slip_in, listener; +static sel_state sel; +static unsigned reasons; +static waiter *wait_head, **wait_tail = &wait_head; +static unsigned char buf[16384]; +static const char *name; + +/*----- Utilities ---------------------------------------------------------*/ + +static void socketaddr(struct sockaddr_un *sun, size_t *sz) +{ + size_t n = strlen(name) + 1; + if (n + offsetof(struct sockaddr_un, sun_path) > sizeof(*sun)) + die(EXIT_FAILURE, "name too long: `%s'", name); + sun->sun_family = AF_UNIX; + memcpy(sun->sun_path, name, n); + if (sz) + *sz = n + offsetof(struct sockaddr_un, sun_path); +} + +/*------ Packet queue -----------------------------------------------------* + * + * A packet queue contains a sequence of octet strings. Packets can be added + * to the end and read off the front. + */ + +static void initqueue(pkq *q) { q->head = 0; q->tail = &q->head; } + +static pkq_node *make_pkqnode(void *p, size_t n) +{ + pkq_node *pn; + + if (!n) + return (0); + pn = CREATE(pkq_node); + pn->next = 0; + pn->buf = xmalloc(n); + memcpy(pn->buf, p, n); + pn->sz = n; + pn->n = 0; + return (pn); +} + +static int enqueue(pkq *q, pkq_node *pn) +{ + int rc = 0; + + if (!pn) + return (0); + rc = !q->head; + pn->next = 0; + *q->tail = pn; + q->tail = &pn->next; + return (rc); +} + +static void destroy_pkqnode(pkq_node *pn) { xfree(pn->buf); DESTROY(pn); } + +static int dequeue(pkq *q, int freep) +{ + pkq_node *pn = q->head; + assert(pn); + q->head = pn->next; + if (freep) + destroy_pkqnode(pn); + if (!q->head) { + q->tail = &q->head; + return (1); + } + return (0); +} + +static void destroy_pkq(pkq *q) +{ + pkq_node *pn, *pnn; + + for (pn = q->head; pn; pn = pnn) { + pnn = pn->next; + destroy_pkqnode(pn); + } + q->head = 0; q->tail = &q->head; +} + +/*----- 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. + */ + +static void gobbler_close(gobbler *g) + { if (g->f.fd != -1) { sel_rmfile(&g->f); g->f.fd = -1; } } + +static void gobbler_destroy(gobbler *g) { gobbler_close(g); DESTROY(g); } + +static void do_gobble_in(int fd, unsigned mode, void *p) +{ + gobbler *g = p; + ssize_t n; + + for (;;) { + n = read(fd, buf, sizeof(buf)); + if (n < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + break; + else { + moan("read (gobble): %s", strerror(errno)); + gobbler_close(g); + break; + } + } else if (n == 0) { + gobbler_close(g); + break; + } + } +} + +static gobbler *make_gobbler(int fd) +{ + gobbler *g; + + g = CREATE(gobbler); + sel_initfile(&sel, &g->f, fd, SEL_READ, do_gobble_in, g); + sel_addfile(&g->f); + do_gobble_in(fd, SEL_READ, g); + return (g); +} + +/*----- Dribbler ----------------------------------------------------------* + * + * A dribbler hands out data from a packet queue to a file descriptor. It + * makes no attempt to preserve the record boundaries inherent in the packet + * queue structure. If the dribbler reaches the end of its queue, it invokes + * a user-supplied `done' function and stops selecting its output descriptor + * for writing. + */ + +static void cripple_dribbler(dribbler *d) { close(d->f.fd); d->f.fd = -1; } + +static void destroy_dribbler(dribbler *d) + { cripple_dribbler(d); DESTROY(d); } + +static void dribble_done(dribbler *d, int err) +{ + if (d->q.head) { + sel_rmfile(&d->f); + destroy_pkq(&d->q); + } + d->done(d, err, d->p); +} + +static void do_dribble_out(int fd, unsigned mode, void *p) +{ + dribbler *d = p; + ssize_t n; + pkq_node *pn; + + for (;;) { + pn = d->q.head; + n = write(fd, pn->buf + pn->n, pn->sz - pn->n); + if (n < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + break; + else { + dribble_done(d, errno); + break; + } + } + pn->n += n; + if (pn->n == pn->sz && dequeue(&d->q, 1)) { + sel_rmfile(&d->f); + dribble_done(d, 0); + break; + } + } +} + +static int enqueue_dribble(dribbler *d, pkq_node *pn) +{ + if (d->f.fd == -1) { + destroy_pkqnode(pn); + return (0); + } + if (enqueue(&d->q, pn)) { + sel_addfile(&d->f); + do_dribble_out(d->f.fd, SEL_WRITE, d); + return (1); + } + return (0); +} + +static dribbler *make_dribbler(int fd, + void (*done)(dribbler *, int, void *), + void *p) +{ + dribbler *d = CREATE(dribbler); + sel_initfile(&sel, &d->f, fd, SEL_WRITE, do_dribble_out, d); + initqueue(&d->q); + d->done = done; + d->p = p; + return (d); +} + +/*----- Clients -----------------------------------------------------------*/ + +static void done_client_dribble(dribbler *d, int err, void *p) +{ + if (err) + moan("write (client): %s", strerror(err)); + gobbler_destroy(p); + destroy_dribbler(d); + reasons--; +} + +static void dequeue_to_waiter(void) +{ + waiter *w; + dribbler *d; + pkq_node *pn; + + while (q_in.head && wait_head) { + w = wait_head; + wait_head = w->next; + if (!wait_head) + wait_tail = &wait_head; + d = make_dribbler(w->fd, done_client_dribble, w->g); + DESTROY(w); + pn = q_in.head; + if (dequeue(&q_in, 0)) + reasons--; + enqueue_dribble(d, pn); + } +} + +static void client_destroy(client *c) +{ + sel_rmfile(&c->f); + close(c->f.fd); + dstr_destroy(&c->d); + reasons--; + DESTROY(c); +} + +static void do_client_in(int fd, unsigned mode, void *p) +{ + client *c = p; + ssize_t n, i, i0; + waiter *w; + + /* --- Attention --- * + * + * The queue for outbound packets is SLIP-encoded; we need to encode it + * here. The queue for inbound packets is raw. + */ + + for (;;) { + n = read(fd, buf, sizeof(buf)); + i0 = 0; + if (n < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + break; + else { + moan("read (client): %s", strerror(errno)); + client_destroy(c); + return; + } + } else if (n == 0) { + if (c->mode == '>') { + DPUTC(&c->d, SL_END); + if (enqueue_dribble(dribble_out, make_pkqnode(c->d.buf, c->d.len))) + reasons++; + } + client_destroy(c); + return; + } + if (c->mode == '?') { + switch (buf[0]) { + case '>': + i0 = 1; + c->mode = '>'; + break; + case '<': + w = CREATE(waiter); + w->g = make_gobbler(fd); + w->next = 0; + w->fd = fd; + *wait_tail = w; + wait_tail = &w->next; + sel_rmfile(&c->f); + DESTROY(c); + dequeue_to_waiter(); + return; + default: + moan("bad client mode `%c'", buf[0]); + client_destroy(c); + return; + } + } + for (i = i0; i < n; i++) { + switch (buf[i]) { + case SL_ESC: + DPUTC(&c->d, SL_ESC); + DPUTC(&c->d, SL_ESCESC); + break; + case SL_END: + DPUTC(&c->d, SL_ESC); + DPUTC(&c->d, SL_ESCEND); + break; + default: + DPUTC(&c->d, buf[i]); + break; + } + } + } +} + +static void do_accept(int fd, unsigned mode, void *hunoz) +{ + client *c; + struct sockaddr_un sun; + socklen_t n = sizeof(sun); + int nfd; + + if ((nfd = accept(fd, (struct sockaddr *)&sun, &n)) < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + return; + else + die(EXIT_FAILURE, "accept: %s", strerror(errno)); + } + c = CREATE(client); + c->mode = '?'; + dstr_create(&c->d); + fdflags(nfd, O_NONBLOCK, O_NONBLOCK, 0, 0); + sel_initfile(&sel, &c->f, nfd, SEL_READ, do_client_in, c); + sel_addfile(&c->f); + reasons++; +} + +/*----- Main daemon -------------------------------------------------------*/ + +static void done_slip_dribble(dribbler *d, int err, void *p) +{ + if (!err) + reasons--; + else if (err != EPIPE) + die(EXIT_FAILURE, "write (slip): %s", strerror(errno)); + else + cripple_dribbler(d); +} + +static void do_slip_in(int fd, unsigned mode, void *hunoz) +{ + ssize_t i, n; + + /* --- Attention --- * + * + * The queue for inbound packets contains raw data; we need to decode it + * here. The queue for outbound packets is SLIP-encoded. + * + * TrIPE sends two empty packets on start-up, in order to resynchronize the + * target. We don't need this and it messes us up. + */ + + 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--; + return; + } else if (n < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + break; + die(EXIT_FAILURE, "read (slip in): %s", strerror(errno)); + } + for (i = 0; i < n; i++) { + switch (slipstate) { + case SYNC1: + switch (buf[i]) { + case SL_END: + slipstate = SYNC2; + break; + default: + goto start; + } + break; + case SYNC2: + switch (buf[i]) { + case SL_END: + slipstate = START; + break; + default: + goto start; + } + break; + case BAD: + switch (buf[i]) { + case SL_END: + DRESET(&slipbuf); + slipstate = OK; + break; + default: + break; + } + break; + case ESC: + switch (buf[i]) { + case SL_ESCEND: + DPUTC(&slipbuf, SL_END); + break; + case SL_ESCESC: + DPUTC(&slipbuf, SL_ESC); + break; + case SL_END: + moan("found escaped end byte (discard packet and resync"); + DRESET(&slipbuf); + slipstate = OK; + break; + default: + moan("unspected escape char 0x%02x", buf[i]); + slipstate = BAD; + break; + } + break; + case START: + case OK: + start: + switch (buf[i]) { + case SL_ESC: + slipstate = ESC; + break; + case SL_END: + if (enqueue(&q_in, make_pkqnode(slipbuf.buf, slipbuf.len))) + reasons++; + DRESET(&slipbuf); + dequeue_to_waiter(); + slipstate = START; + break; + default: + DPUTC(&slipbuf, buf[i]); + slipstate = OK; + break; + } + break; + } + } + } +} + +static void slipif(void) +{ + int fd; + dstr d = DSTR_INIT; + struct sockaddr_un sun; + size_t sz; + + /* --- Make the socket --- */ + + if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) + die(EXIT_FAILURE, "socket: %s", strerror(errno)); + socketaddr(&sun, &sz); + if (bind(fd, (struct sockaddr *)&sun, sz)) + die(EXIT_FAILURE, "bind: %s", strerror(errno)); + if (listen(fd, 5)) + die(EXIT_FAILURE, "listen: %s", strerror(errno)); + + /* --- Set up listeners for things --- */ + + fdflags(fd, O_NONBLOCK, O_NONBLOCK, 0, 0); + sel_initfile(&sel, &listener, fd, SEL_READ, do_accept, 0); + sel_addfile(&listener); + + fdflags(STDIN_FILENO, O_NONBLOCK, O_NONBLOCK, 0, 0); + fdflags(STDOUT_FILENO, O_NONBLOCK, O_NONBLOCK, 0, 0); + sel_initfile(&sel, &slip_in, STDIN_FILENO, SEL_READ, do_slip_in, 0); + dribble_out = make_dribbler(STDOUT_FILENO, done_slip_dribble, 0); + sel_addfile(&slip_in); + + initqueue(&q_in); + reasons++; + + /* --- Write the interface name --- */ + + dstr_putf(&d, "%s-%s\n", QUIS, name); + if (enqueue_dribble(dribble_out, make_pkqnode(d.buf, d.len))) + reasons++; + dstr_destroy(&d); + + /* --- Main loop --- */ + + while (reasons) { + if (sel_select(&sel)) + die(EXIT_FAILURE, "select: %s", strerror(errno)); + } + + /* --- Done --- */ + + unlink(name); +} + +/*----- Putting and getting -----------------------------------------------*/ + +static int make_sock(int mode) +{ + struct sockaddr_un sun; + size_t sz; + int fd; + char ch; + + if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) + die(EXIT_FAILURE, "socket: %s", strerror(errno)); + socketaddr(&sun, &sz); + if (connect(fd, (struct sockaddr *)&sun, sz)) + die(EXIT_FAILURE, "connect: %s", strerror(errno)); + ch = mode; + if (write(fd, &ch, 1) < 0) + die(EXIT_FAILURE, "write (mode): %s", strerror(errno)); + return (fd); +} + +static void shovel(int from, int to) +{ + ssize_t n; + size_t sz; + unsigned char *p; + + for (;;) { + n = read(from, buf, sizeof(buf)); + if (n < 0) { + if (errno == EINTR) + continue; + else + die(EXIT_FAILURE, "read (shovel): %s", strerror(errno)); + } else if (n == 0) + break; + + sz = n; + p = buf; + while (sz) { + n = write(to, p, sz); + if (n < 0) { + if (errno == EINTR) + continue; + else + die(EXIT_FAILURE, "write (shovel): %s", strerror(errno)); + } + p += n; + sz -= n; + } + } + close(from); + close(to); +} + +static void put(void) { shovel(STDIN_FILENO, make_sock('>')); } +static void get(void) { shovel(make_sock('<'), STDOUT_FILENO); } + +/*----- Main code ---------------------------------------------------------*/ + +static void usage(FILE *fp) { pquis(fp, "Usage: $ [-pg] SOCKET\n"); } + +static void version(void) + { pquis(stdout, "$ (" PACKAGE " version " VERSION")\n"); } + +static void help(void) +{ + version(); + fputc('\n', stdout); + usage(stdout); + puts("\n\ +With no options, provides a SLIP interface for TrIPE.\n\ +\n\ +Options:\n\ + -p, --put Send packet on stdin to TrIPE.\n\ + -g, --get Receive packet from TrIPE and write to stdout."); +} + +int main(int argc, char *argv[]) +{ + int mode = 'd'; + int i; + + ego(argv[0]); + for (;;) { + const struct option opt[] = { + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "put", 0, 0, 'p' }, + { "get", 0, 0, 'g' }, + { 0, 0, 0, 0 } + }; + i = mdwopt(argc, argv, "hvpg", 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; + default: usage(stderr); exit(EXIT_FAILURE); break; + } + } + if (argc - optind != 1) { usage(stderr); exit(EXIT_FAILURE); } + name = argv[optind]; + signal(SIGPIPE, SIG_IGN); + switch (mode) { + case 'd': slipif(); break; + case 'p': put(); break; + case 'g': get(); break; + } + return (0); +} + +/*----- That's all, folks -------------------------------------------------*/