2530f15118a99336a07825b7daba21afcff66b7e
3 * An evil proxy for TrIPE
5 * (c) 2001 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 ------------------------------------------------------*/
37 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
47 #include <mLib/alloc.h>
48 #include <mLib/dstr.h>
49 #include <mLib/fdflags.h>
50 #include <mLib/mdwopt.h>
51 #include <mLib/quis.h>
52 #include <mLib/report.h>
57 #include <catacomb/buf.h>
59 #include <catacomb/key.h>
61 #include <catacomb/mp.h>
62 #include <catacomb/mprand.h>
63 #include <catacomb/dh.h>
65 #include <catacomb/chacha20.h>
66 #include <catacomb/noise.h>
67 #include <catacomb/rand.h>
71 /*----- Data structures ---------------------------------------------------*/
79 typedef struct filter
{
82 void (*func
)(struct filter */
*f*/
, const octet */
*buf*/
, size_t /*sz*/);
86 typedef struct qnode
{
91 /*----- Static variables --------------------------------------------------*/
97 static unsigned npeer
= 0;
100 static const char *delim
= ":";
102 #define PASS(f, buf, sz) ((f) ? (f)->func((f), (buf), (sz)) : (void)0)
103 #define RND(i) (rng->ops->range(rng, (i)))
105 /*----- Peer management ---------------------------------------------------*/
107 static void dopacket(int fd
, unsigned mode
, void *vv
)
111 int r
= read(fd
, buf
, sizeof(buf
));
113 printf("recv from `%s'\n", p
->name
);
118 static void addpeer(unsigned ac
, char **av
)
121 struct sockaddr_in sin
;
126 if (ac
!= 4) die(1, "syntax: peer:NAME:PORT:ADDR:PORT");
127 if (npeer
>= 2) die(1, "enough peers already");
128 if (!key_bytag(&keys
, av
[0])) die(1, "no key named `%s'", av
[0]);
130 p
->name
= xstrdup(av
[0]);
131 if ((fd
= socket(PF_INET
, SOCK_DGRAM
, 0)) < 0)
132 die(1, "socket: %s", strerror(errno
));
133 fdflags(fd
, O_NONBLOCK
, O_NONBLOCK
, FD_CLOEXEC
, FD_CLOEXEC
);
134 memset(&sin
, 0, sizeof(sin
));
135 sin
.sin_family
= AF_INET
;
136 sin
.sin_addr
.s_addr
= INADDR_ANY
;
137 sin
.sin_port
= htons(atoi(av
[1]));
138 if (bind(fd
, (struct sockaddr
*)&sin
, sizeof(sin
)))
139 die(1, "bind: %s", strerror(errno
));
140 memset(&sin
, 0, sizeof(sin
));
141 sin
.sin_family
= AF_INET
;
142 if ((h
= gethostbyname(av
[2])) == 0)
143 die(1, "gethostbyname `%s'", av
[2]);
144 if (setsockopt(fd
, SOL_SOCKET
, SO_RCVBUF
, &len
, sizeof(len
)) ||
145 setsockopt(fd
, SOL_SOCKET
, SO_SNDBUF
, &len
, sizeof(len
)))
146 die(1, "setsockopt: %s", strerror(errno
));
147 memcpy(&sin
.sin_addr
, h
->h_addr
, sizeof(sin
.sin_addr
));
148 sin
.sin_port
= htons(atoi(av
[3]));
149 if (connect(fd
, (struct sockaddr
*)&sin
, sizeof(sin
)))
150 die(1, "connect: %s", strerror(errno
));
151 sel_initfile(&sel
, &p
->sf
, fd
, SEL_READ
, dopacket
, p
);
155 /*----- Fork filter -------------------------------------------------------*/
157 typedef struct forknode
{
158 struct forknode
*next
;
162 typedef struct forkfilt
{
167 static void dofork(filter
*f
, const octet
*buf
, size_t sz
)
169 forkfilt
*ff
= f
->state
;
174 for (fn
= ff
->fn
; fn
; fn
= fn
->next
) {
175 printf("fork branch %u of fork `%s'\n", i
++, ff
->name
);
176 PASS(fn
->f
, buf
, sz
);
178 printf("fork branch %u of fork `%s'\n", i
++, ff
->name
);
179 PASS(f
->next
, buf
, sz
);
182 static void addfork(filter
*f
, unsigned ac
, char **av
)
185 if (ac
!= 1) die(1, "syntax: filt:fork:NAME");
186 ff
= CREATE(forkfilt
);
187 ff
->name
= xstrdup(av
[0]);
193 static void nextfork(unsigned ac
, char **av
)
201 if (ac
< 1) die(1, "syntax: next:NAME:...");
202 for (i
= 0; i
< 2; i
++) {
204 for (f
= p
->f
; f
; f
= f
->next
) {
205 if (f
->func
!= dofork
) continue;
207 for (j
= 0; j
< ac
; j
++)
208 if (strcmp(av
[j
], ff
->name
) == 0) goto match
;
211 fn
= CREATE(forknode
);
212 for (ffn
= &ff
->fn
; *ffn
; ffn
= &(*ffn
)->next
);
221 /*----- Corrupt filter ----------------------------------------------------*/
223 typedef struct corrupt
{
227 static void docorrupt(filter
*f
, const octet
*buf
, size_t sz
)
229 corrupt
*c
= f
->state
;
233 while (!RND(c
->p_corrupt
)) {
234 puts("corrupt packet");
235 b
[RND(sz
)] ^= RND(256);
237 PASS(f
->next
, b
, sz
);
240 static void addcorrupt(filter
*f
, unsigned ac
, char **av
)
243 if (ac
> 1) die(1, "syntax: filt:corrupt[:P-CORRUPT]");
245 if (ac
> 0) c
->p_corrupt
= atoi(av
[0]);
246 else c
->p_corrupt
= 5;
251 /*----- Drop filter -------------------------------------------------------*/
253 typedef struct drop
{
257 static void dodrop(filter
*f
, const octet
*buf
, size_t sz
)
261 if (!RND(d
->p_drop
)) puts("drop packet");
262 else PASS(f
->next
, buf
, sz
);
265 static void adddrop(filter
*f
, unsigned ac
, char **av
)
268 if (ac
> 1) die(1, "syntax: filt:drop[:P-DROP]");
270 if (ac
> 0) d
->p_drop
= atoi(av
[0]);
276 /*----- Delay filter ------------------------------------------------------*/
278 typedef struct delaynode
{
288 typedef struct delay
{
296 static void dtimer(struct timeval
*tv
, void *vv
);
298 static void dinsert(delaynode
*dn
)
302 unsigned long tdelta
= RND(dn
->d
->t
);
303 gettimeofday(&tv
, 0);
304 TV_ADDL(&tv
, &tv
, 0, tdelta
);
306 sel_addtimer(&sel
, &dn
->tm
, &tv
, dtimer
, dn
);
308 for (ta
= tb
= sel
.timers
; ta
; ta
= ta
->next
) {
309 ta
= ta
->next
; if (!ta
) break; assert(ta
!= tb
);
310 ta
= ta
->next
; if (!ta
) break; assert(ta
!= tb
);
313 printf(" delay %lu usecs", tdelta
);
316 static void dsend(delaynode
*dn
, unsigned force
)
322 fputs(" send...\n", stdout
);
324 PASS(d
->f
->next
, dn
->buf
, dn
->sz
);
325 fputs("delay ...", stdout
);
334 sel_rmtimer(&ddn
->tm
);
335 sel_addtimer(&sel
, &dn
->tm
, &ddn
->tm
.tv
, dtimer
, dn
);
342 printf(" move id %u from slot %u to slot %u", ddn
->seq
, ddn
->i
, dn
->i
);
344 for (i
= 0; i
< d
->n
; i
++) assert(d
->q
[i
].buf
);
345 fputs(" remove", stdout
);
349 static void dtimer(struct timeval
*tv
, void *vv
)
352 printf("delay timer peer `%s' id %u slot %u",
353 dn
->d
->f
->p_from
->name
, dn
->seq
, dn
->i
);
355 dsend(dn
, RND(dn
->d
->p_replay
));
359 static void dodelay(filter
*f
, const octet
*buf
, size_t sz
)
363 static unsigned seq
= 0;
365 fputs("delay", stdout
);
366 if (d
->n
== d
->max
) {
367 dn
= &d
->q
[RND(d
->n
)];
368 printf(" force uid %u", dn
->seq
);
369 sel_rmtimer(&dn
->tm
);
376 printf(" new id %u in slot %u", dn
->seq
, dn
->i
);
377 dn
->buf
= xmalloc(sz
);
379 memcpy(dn
->buf
, buf
, sz
);
384 static void adddelay(filter
*f
, unsigned ac
, char **av
)
389 if (ac
< 1 || ac
> 3) die(1, "syntax: filt:delay:QLEN[:MILLIS:P-REPLAY]");
391 d
->max
= atoi(av
[0]);
392 if (ac
> 1) d
->t
= strtoul(av
[1], 0, 10);
395 if (ac
> 2) d
->p_replay
= atoi(av
[2]);
396 else d
->p_replay
= 20;
398 d
->q
= xmalloc(d
->max
* sizeof(delaynode
));
402 for (i
= 0; i
< d
->max
; i
++) {
410 /*----- Filters -----------------------------------------------------------*/
412 static void dosend(filter
*f
, const octet
*buf
, size_t sz
)
414 printf("send to `%s'\n", f
->p_to
->name
);
415 DISCARD(write(f
->p_to
->sf
.fd
, buf
, sz
));
418 static void addsend(filter
*f
, unsigned ac
, char **av
)
420 if (ac
) die(1, "syntax: filt:send");
424 const struct filtab
{
426 void (*func
)(filter */
*f*/
, unsigned /*ac*/, char **/
*av*/
);
430 { "delay", adddelay
},
432 { "corrupt", addcorrupt
},
436 static void dofilter(peer
*from
, peer
*to
, unsigned ac
, char **av
)
438 filter
**ff
, *f
= CREATE(filter
);
439 const struct filtab
*ft
;
440 if (ac
< 1) die(1, "syntax: {l,r,}filt:NAME:...");
445 for (ff
= &from
->f
; *ff
; ff
= &(*ff
)->next
);
447 for (ft
= filtab
; ft
->name
; ft
++)
448 if (strcmp(av
[0], ft
->name
) == 0)
449 { ft
->func(f
, ac
- 1, av
+ 1); return; }
450 die(1, "unknown filter `%s'", av
[0]);
453 /*----- Flooding ----------------------------------------------------------*/
455 typedef struct flood
{
463 static void setflood(flood
*f
);
465 static void floodtimer(struct timeval
*tv
, void *vv
)
477 rng
->ops
->fill(rng
, buf
, sz
);
478 if (f
->type
< 0x100) buf
[0] = f
->type
;
479 puts("flood packet");
480 PASS(f
->p
->f
, buf
, sz
);
484 static void setflood(flood
*f
)
487 gettimeofday(&tv
, 0);
488 TV_ADDL(&tv
, &tv
, 0, RND(f
->t
));
489 sel_addtimer(&sel
, &f
->tm
, &tv
, floodtimer
, f
);
492 static void doflood(peer
*p
, unsigned ac
, char **av
)
495 if (ac
> 3) die(1, "syntax: flood[:TYPE:MILLIS:SIZE]");
498 if (ac
> 0) f
->type
= strtoul(av
[0], 0, 16);
499 else f
->type
= 0x100;
500 if (ac
> 1) f
->t
= atoi(av
[1]);
502 if (ac
> 2) f
->sz
= atoi(av
[2]);
508 /*----- Configuration commands --------------------------------------------*/
510 static void parse(char *p
);
512 static void addflood(unsigned ac
, char **av
) {
513 doflood(&peers
[0], ac
, av
);
514 doflood(&peers
[1], ac
, av
);
516 static void addlflood(unsigned ac
, char **av
) {
517 doflood(&peers
[0], ac
, av
);
519 static void addrflood(unsigned ac
, char **av
) {
520 doflood(&peers
[1], ac
, av
);
523 static void addfilter(unsigned ac
, char **av
) {
524 dofilter(&peers
[0], &peers
[1], ac
, av
);
525 dofilter(&peers
[1], &peers
[0], ac
, av
);
527 static void addlfilter(unsigned ac
, char **av
) {
528 dofilter(&peers
[0], &peers
[1], ac
, av
);
530 static void addrfilter(unsigned ac
, char **av
) {
531 dofilter(&peers
[1], &peers
[0], ac
, av
);
534 static void include(unsigned ac
, char **av
)
538 if (!ac
) die(1, "syntax: include:FILE:...");
540 if ((fp
= fopen(*av
, "r")) == 0)
541 die(1, "fopen `%s': %s", *av
, strerror(errno
));
542 while (dstr_putline(&d
, fp
) != EOF
) { parse(d
.buf
); DRESET(&d
); }
548 const struct cmdtab
{
550 void (*func
)(unsigned /*ac*/, char **/
*av*/
);
553 { "include", include
},
554 { "filt", addfilter
},
555 { "lfilt", addlfilter
},
556 { "rfilt", addrfilter
},
557 { "next", nextfork
},
558 { "flood", addflood
},
559 { "lflood", addlflood
},
560 { "rflood", addrflood
},
566 static void parse(char *p
)
570 const struct cmdtab
*ct
;
572 p
= strtok(p
, delim
);
573 if (!p
|| *p
== '#') return;
576 p
= strtok(0, delim
);
577 } while (p
&& c
< AVMAX
- 1);
579 for (ct
= cmdtab
; ct
->name
; ct
++) {
580 if (strcmp(ct
->name
, v
[0]) == 0) {
581 ct
->func(c
- 1, v
+ 1);
585 die(1, "unknown command `%s'", v
[0]);
588 /*----- Main driver -------------------------------------------------------*/
590 static void version(FILE *fp
)
591 { pquis(fp
, "$, TrIPE version " VERSION
"\n"); }
593 static void usage(FILE *fp
)
594 { pquis(fp
, "Usage: $ [-d CHAR] [-k KEYRING] DIRECTIVE...\n"); }
596 static void help(FILE *fp
)
604 -h, --help Show this help text.\n\
605 -v, --version Show the version number.\n\
606 -u, --usage Show terse usage summary.\n\
608 -d, --delimiter=CHAR Use CHAR rather than `:' as delimiter.\n\
609 -k, --keyring=FILE Fetch keys from FILE.\n\
612 peer:NAME:LOCAL-PORT:REMOTE-ADDR:REMOTE-PORT\n\
614 {,l,r}filt:FILTER:ARGS:...\n\
616 {,l,r}flood:TYPE:MILLIS:SIZE\n\
621 delay:QLEN[:MILLIS:P-REPLAY]\n\
623 corrupt[:P-CORRUPT]\n",
627 int main(int argc
, char *argv
[])
629 const char *kfname
= "keyring.pub";
633 static octet zero
[CHACHA_NONCESZ
];
639 static const struct option opt
[] = {
640 { "help", 0, 0, 'h' },
641 { "version", 0, 0, 'v' },
642 { "usage", 0, 0, 'u' },
643 { "delimiter", OPTF_ARGREQ
, 0, 'd' },
644 { "keyring", OPTF_ARGREQ
, 0, 'k' },
647 if ((i
= mdwopt(argc
, argv
, "hvud:k:", opt
, 0, 0, 0)) < 0) break;
649 case 'h': help(stdout
); exit(0);
650 case 'v': version(stdout
); exit(0);
651 case 'u': usage(stdout
); exit(0);
653 if (!optarg
[0] || optarg
[1])
654 die(1, "delimiter must be a single character");
657 case 'k': kfname
= optarg
; break;
658 default: f
|= f_bogus
; break;
661 if (f
& f_bogus
) { usage(stderr
); exit(1); }
663 rand_noisesrc(RAND_GLOBAL
, &noise_source
);
664 rand_seed(RAND_GLOBAL
, 256);
665 rand_get(RAND_GLOBAL
, buf
, sizeof(buf
));
666 rng
= chacha20_rand(buf
, sizeof(buf
), zero
);
668 if (key_open(&keys
, kfname
, KOPEN_READ
, key_moan
, 0))
669 die(1, "couldn't open `%s': %s", kfname
, strerror(errno
));
670 for (i
= optind
; i
< argc
; i
++) parse(argv
[i
]);
671 if (npeer
!= 2) die(1, "need two peers");
673 if (sel_select(&sel
) && errno
!= EINTR
)
674 die(1, "select failed: %s", strerror(errno
));
680 /*----- That's all, folks -------------------------------------------------*/