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 -------------------------------------------------*/