dee9f94d63e92d64df14c538d810ad34d3d0171b
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
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.
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.
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.
27 /*----- Header files ------------------------------------------------------*/
39 #include <sys/types.h>
43 #include <sys/socket.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
49 #include <mLib/alloc.h>
50 #include <mLib/dstr.h>
51 #include <mLib/fdflags.h>
52 #include <mLib/mdwopt.h>
53 #include <mLib/quis.h>
54 #include <mLib/report.h>
62 /*----- Data structures ---------------------------------------------------*/
64 typedef struct pkq_node
{
65 struct pkq_node
*next
;
71 pkq_node
*head
, **tail
;
74 enum { START
, OK
, ESC
, BAD
, SYNC1
, SYNC2
};
76 typedef struct client
{
82 typedef struct gobbler
{
84 void (*done
)(struct gobbler
*, int, void *);
88 typedef struct dribbler
{
91 void (*done
)(struct dribbler
*, int, void *);
95 typedef struct waiter
{
101 /*----- Static variables --------------------------------------------------*/
104 static dribbler
*dribble_out
;
105 static dstr slipbuf
= DSTR_INIT
;
106 static int slipstate
= SYNC1
;
107 static sel_file slip_in
, listener
;
108 static sel_state sel
;
109 static unsigned reasons
;
110 static waiter
*wait_head
, **wait_tail
= &wait_head
;
111 static unsigned char buf
[16384];
112 static const char *name
;
114 /*----- Utilities ---------------------------------------------------------*/
116 static void socketaddr(struct sockaddr_un
*sun
, size_t *sz
)
118 size_t n
= strlen(name
) + 1;
119 if (n
+ offsetof(struct sockaddr_un
, sun_path
) > sizeof(*sun
))
120 die(EXIT_FAILURE
, "name too long: `%s'", name
);
121 sun
->sun_family
= AF_UNIX
;
122 memcpy(sun
->sun_path
, name
, n
);
124 *sz
= n
+ offsetof(struct sockaddr_un
, sun_path
);
127 /*------ Packet queue -----------------------------------------------------*
129 * A packet queue contains a sequence of octet strings. Packets can be added
130 * to the end and read off the front.
133 static void initqueue(pkq
*q
) { q
->head
= 0; q
->tail
= &q
->head
; }
135 static pkq_node
*make_pkqnode(void *p
, size_t n
)
141 pn
= CREATE(pkq_node
);
143 pn
->buf
= xmalloc(n
);
144 memcpy(pn
->buf
, p
, n
);
150 static int enqueue(pkq
*q
, pkq_node
*pn
)
163 static void destroy_pkqnode(pkq_node
*pn
) { xfree(pn
->buf
); DESTROY(pn
); }
165 static int dequeue(pkq
*q
, int freep
)
167 pkq_node
*pn
= q
->head
;
179 static void destroy_pkq(pkq
*q
)
183 for (pn
= q
->head
; pn
; pn
= pnn
) {
187 q
->head
= 0; q
->tail
= &q
->head
;
190 /*----- Gobblers ----------------------------------------------------------*
192 * A gobbler just eats everything it sees on its input descriptor.
193 * Eventually, when it sees end-of-file, it closes the input descriptor,
194 * calls a user-supplied calback function, and quits.
197 static void gobbler_close(gobbler
*g
)
198 { if (g
->f
.fd
!= -1) { sel_rmfile(&g
->f
); close(g
->f
.fd
); g
->f
.fd
= -1; } }
200 static void gobbler_destroy(gobbler
*g
) { gobbler_close(g
); DESTROY(g
); }
202 static void do_gobble_in(int fd
, unsigned mode
, void *p
)
208 n
= read(fd
, buf
, sizeof(buf
));
210 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
|| errno
== EINTR
)
213 moan("read (gobble): %s", strerror(errno
));
214 if (g
->done
) g
->done(g
, errno
, g
->p
);
219 if (g
->done
) g
->done(g
, 0, g
->p
);
226 static gobbler
*make_gobbler(int fd
,
227 void (*done
)(gobbler
*, int, void *),
235 sel_initfile(&sel
, &g
->f
, fd
, SEL_READ
, do_gobble_in
, g
);
237 do_gobble_in(fd
, SEL_READ
, g
);
241 /*----- Dribbler ----------------------------------------------------------*
243 * A dribbler hands out data from a packet queue to a file descriptor. It
244 * makes no attempt to preserve the record boundaries inherent in the packet
245 * queue structure. If the dribbler reaches the end of its queue, it invokes
246 * a user-supplied `done' function and stops selecting its output descriptor
250 static void cripple_dribbler(dribbler
*d
) { close(d
->f
.fd
); d
->f
.fd
= -1; }
252 static void destroy_dribbler(dribbler
*d
)
253 { cripple_dribbler(d
); DESTROY(d
); }
255 static void dribble_done(dribbler
*d
, int err
)
261 d
->done(d
, err
, d
->p
);
264 static void do_dribble_out(int fd
, unsigned mode
, void *p
)
272 n
= write(fd
, pn
->buf
+ pn
->n
, pn
->sz
- pn
->n
);
274 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
|| errno
== EINTR
)
277 dribble_done(d
, errno
);
282 if (pn
->n
== pn
->sz
&& dequeue(&d
->q
, 1)) {
290 static int enqueue_dribble(dribbler
*d
, pkq_node
*pn
)
296 if (enqueue(&d
->q
, pn
)) {
298 do_dribble_out(d
->f
.fd
, SEL_WRITE
, d
);
304 static dribbler
*make_dribbler(int fd
,
305 void (*done
)(dribbler
*, int, void *),
308 dribbler
*d
= CREATE(dribbler
);
309 sel_initfile(&sel
, &d
->f
, fd
, SEL_WRITE
, do_dribble_out
, d
);
316 /*----- Clients -----------------------------------------------------------*/
318 static void done_client_dribble(dribbler
*d
, int err
, void *p
)
321 moan("write (client): %s", strerror(err
));
327 static void dequeue_to_waiter(void)
333 while (q_in
.head
&& wait_head
) {
337 wait_tail
= &wait_head
;
338 d
= make_dribbler(w
->fd
, done_client_dribble
, w
->g
);
341 if (dequeue(&q_in
, 0))
343 enqueue_dribble(d
, pn
);
347 static void client_destroy(client
*c
)
356 static void do_client_in(int fd
, unsigned mode
, void *p
)
362 /* --- Attention --- *
364 * The queue for outbound packets is SLIP-encoded; we need to encode it
365 * here. The queue for inbound packets is raw.
369 n
= read(fd
, buf
, sizeof(buf
));
372 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
|| errno
== EINTR
)
375 moan("read (client): %s", strerror(errno
));
380 if (c
->mode
== '>') {
381 DPUTC(&c
->d
, SL_END
);
382 if (enqueue_dribble(dribble_out
, make_pkqnode(c
->d
.buf
, c
->d
.len
)))
388 if (c
->mode
== '?') {
396 w
->g
= make_gobbler(fd
, 0, 0);
400 wait_tail
= &w
->next
;
406 moan("bad client mode `%c'", buf
[0]);
411 for (i
= i0
; i
< n
; i
++) {
414 DPUTC(&c
->d
, SL_ESC
);
415 DPUTC(&c
->d
, SL_ESCESC
);
418 DPUTC(&c
->d
, SL_ESC
);
419 DPUTC(&c
->d
, SL_ESCEND
);
422 DPUTC(&c
->d
, buf
[i
]);
429 static void do_accept(int fd
, unsigned mode
, void *hunoz
)
432 struct sockaddr_un sun
;
433 socklen_t n
= sizeof(sun
);
436 if ((nfd
= accept(fd
, (struct sockaddr
*)&sun
, &n
)) < 0) {
437 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
|| errno
== EINTR
)
440 die(EXIT_FAILURE
, "accept: %s", strerror(errno
));
445 fdflags(nfd
, O_NONBLOCK
, O_NONBLOCK
, 0, 0);
446 sel_initfile(&sel
, &c
->f
, nfd
, SEL_READ
, do_client_in
, c
);
451 /*----- Main daemon -------------------------------------------------------*/
453 static void done_slip_dribble(dribbler
*d
, int err
, void *p
)
457 else if (err
!= EPIPE
)
458 die(EXIT_FAILURE
, "write (slip): %s", strerror(errno
));
463 static void do_slip_in(int fd
, unsigned mode
, void *hunoz
)
467 /* --- Attention --- *
469 * The queue for inbound packets contains raw data; we need to decode it
470 * here. The queue for outbound packets is SLIP-encoded.
472 * TrIPE sends two empty packets on start-up, in order to resynchronize the
473 * target. We don't need this and it messes us up.
477 n
= read(fd
, buf
, sizeof(buf
));
486 moan("eof found while processing packet (discarding)");
490 sel_rmfile(&slip_in
);
494 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
|| errno
== EINTR
)
496 die(EXIT_FAILURE
, "read (slip in): %s", strerror(errno
));
498 for (i
= 0; i
< n
; i
++) {
531 DPUTC(&slipbuf
, SL_END
);
535 DPUTC(&slipbuf
, SL_ESC
);
539 moan("found escaped end byte (discard packet and resync");
544 moan("unexpected escape char 0x%02x", buf
[i
]);
557 if (enqueue(&q_in
, make_pkqnode(slipbuf
.buf
, slipbuf
.len
)))
564 DPUTC(&slipbuf
, buf
[i
]);
574 static void slipif(void)
578 struct sockaddr_un sun
;
581 /* --- Make the socket --- */
583 if ((fd
= socket(PF_UNIX
, SOCK_STREAM
, 0)) < 0)
584 die(EXIT_FAILURE
, "socket: %s", strerror(errno
));
585 socketaddr(&sun
, &sz
);
586 if (bind(fd
, (struct sockaddr
*)&sun
, sz
))
587 die(EXIT_FAILURE
, "bind: %s", strerror(errno
));
589 die(EXIT_FAILURE
, "listen: %s", strerror(errno
));
591 /* --- Set up listeners for things --- */
593 fdflags(fd
, O_NONBLOCK
, O_NONBLOCK
, 0, 0);
594 sel_initfile(&sel
, &listener
, fd
, SEL_READ
, do_accept
, 0);
595 sel_addfile(&listener
);
597 fdflags(STDIN_FILENO
, O_NONBLOCK
, O_NONBLOCK
, 0, 0);
598 fdflags(STDOUT_FILENO
, O_NONBLOCK
, O_NONBLOCK
, 0, 0);
599 sel_initfile(&sel
, &slip_in
, STDIN_FILENO
, SEL_READ
, do_slip_in
, 0);
600 dribble_out
= make_dribbler(STDOUT_FILENO
, done_slip_dribble
, 0);
601 sel_addfile(&slip_in
);
606 /* --- Write the interface name --- */
608 dstr_putf(&d
, "%s-%s\n", QUIS
, name
);
609 if (enqueue_dribble(dribble_out
, make_pkqnode(d
.buf
, d
.len
)))
613 /* --- Main loop --- */
616 if (sel_select(&sel
))
617 die(EXIT_FAILURE
, "select: %s", strerror(errno
));
625 /*----- Putting and getting -----------------------------------------------*/
627 static int make_sock(int mode
)
629 struct sockaddr_un sun
;
634 if ((fd
= socket(PF_UNIX
, SOCK_STREAM
, 0)) < 0)
635 die(EXIT_FAILURE
, "socket: %s", strerror(errno
));
636 socketaddr(&sun
, &sz
);
637 if (connect(fd
, (struct sockaddr
*)&sun
, sz
))
638 die(EXIT_FAILURE
, "connect: %s", strerror(errno
));
640 if (write(fd
, &ch
, 1) < 0)
641 die(EXIT_FAILURE
, "write (mode): %s", strerror(errno
));
645 static void shovel(int from
, int to
)
652 n
= read(from
, buf
, sizeof(buf
));
657 die(EXIT_FAILURE
, "read (shovel): %s", strerror(errno
));
664 n
= write(to
, p
, sz
);
669 die(EXIT_FAILURE
, "write (shovel): %s", strerror(errno
));
679 static void put(void) { shovel(STDIN_FILENO
, make_sock('>')); }
680 static void get(void) { shovel(make_sock('<'), STDOUT_FILENO
); }
682 /*----- Main code ---------------------------------------------------------*/
684 static void usage(FILE *fp
) { pquis(fp
, "Usage: $ [-pg] SOCKET\n"); }
686 static void version(void)
687 { pquis(stdout
, "$ (" PACKAGE
" version " VERSION
")\n"); }
689 static void help(void)
695 With no options, provides a SLIP interface for TrIPE.\n\
698 -p, --put Send packet on stdin to TrIPE.\n\
699 -g, --get Receive packet from TrIPE and write to stdout.");
702 int main(int argc
, char *argv
[])
709 const struct option opt
[] = {
710 { "help", 0, 0, 'h' },
711 { "version", 0, 0, 'v' },
712 { "put", 0, 0, 'p' },
713 { "get", 0, 0, 'g' },
716 i
= mdwopt(argc
, argv
, "hvpg", opt
, 0, 0, 0);
720 case 'h': help(); return (0);
721 case 'v': version(); return (0);
722 case 'p': case 'g': mode
= i
; break;
723 default: usage(stderr
); exit(EXIT_FAILURE
); break;
726 if (argc
- optind
!= 1) { usage(stderr
); exit(EXIT_FAILURE
); }
728 signal(SIGPIPE
, SIG_IGN
);
730 case 'd': slipif(); break;
731 case 'p': put(); break;
732 case 'g': get(); break;
737 /*----- That's all, folks -------------------------------------------------*/