*
* 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 <https://www.gnu.org/licenses/>.
*/
/*----- Header files ------------------------------------------------------*/
#include <catacomb/mprand.h>
#include <catacomb/dh.h>
+#include <catacomb/chacha20.h>
#include <catacomb/noise.h>
#include <catacomb/rand.h>
-#include <catacomb/rc4.h>
#include "util.h"
typedef struct peer {
sel_file sf;
- dh_pub kpub;
const char *name;
struct filter *f;
} peer;
static unsigned npeer = 0;
static key_file keys;
static grand *rng;
+static const char *delim = ":";
#define PASS(f, buf, sz) ((f) ? (f)->func((f), (buf), (sz)) : (void)0)
#define RND(i) (rng->ops->range(rng, (i)))
}
}
-static void addpeer(unsigned ac, char **av)
+static void addpeer_common(const char *cmd, int af, unsigned ac, char **av)
{
- key_packstruct kps[DH_PUBFETCHSZ];
- key_packdef *kp;
- struct hostent *h;
- struct sockaddr_in sin;
- int len = PKBUFSZ;
+ struct addrinfo aihint = { 0 }, *ai0, *ai1;
+ int len = PKBUFSZ, yes = 1;
+ const char *outf, *serv;
+ FILE *fp;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ } addr;
+ socklen_t salen;
+ int err;
peer *p;
- int fd;
- int e;
+ int fd, port;
- if (ac != 4)
- die(1, "syntax: peer:NAME:PORT:ADDR:PORT");
- if (npeer >= 2)
- die(1, "enough peers already");
+ if (ac != 4) die(1, "syntax: %s:NAME:PORT:ADDR:PORT", cmd);
+ if (!key_bytag(&keys, av[0])) die(1, "no key named `%s'", av[0]);
p = &peers[npeer++];
p->name = xstrdup(av[0]);
- kp = key_fetchinit(dh_pubfetch, kps, &p->kpub);
- e = key_fetchbyname(kp, &keys, av[0]);
- key_fetchdone(kp);
- if (e)
- die(1, "key_fetch `%s': %s", av[0], key_strerror(e));
- if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
+ aihint.ai_family = af;
+ aihint.ai_socktype = SOCK_DGRAM;
+ aihint.ai_flags = AI_ADDRCONFIG;
+ if ((err = getaddrinfo(av[2], av[3], &aihint, &ai1)) != 0)
+ die(1, "getaddrinfo(`%s', `%s'): %s", av[2], av[3], gai_strerror(err));
+ if (*av[1] == '?') { serv = "0"; outf = av[1] + 1; }
+ else { serv = av[1]; outf = 0; }
+ aihint.ai_family = ai1->ai_family;
+ aihint.ai_flags = AI_ADDRCONFIG | AI_PASSIVE;
+ if ((err = getaddrinfo(0, serv, &aihint, &ai0)) != 0)
+ die(1, "getaddrinfo(passive, `%s'): %s", av[1], gai_strerror(err));
+ if ((fd = socket(ai1->ai_family, SOCK_DGRAM, ai1->ai_protocol)) < 0)
die(1, "socket: %s", strerror(errno));
- fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
- memset(&sin, 0, sizeof(sin));
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = INADDR_ANY;
- sin.sin_port = htons(atoi(av[1]));
- if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)))
+ if (ai1->ai_family == AF_INET6) {
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)))
+ die(1, "setsockopt: %s", strerror(errno));
+ }
+ if (bind(fd, ai0->ai_addr, ai0->ai_addrlen))
die(1, "bind: %s", strerror(errno));
- memset(&sin, 0, sizeof(sin));
- sin.sin_family = AF_INET;
- if ((h = gethostbyname(av[2])) == 0)
- die(1, "gethostbyname `%s'", av[2]);
if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, sizeof(len)) ||
setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &len, sizeof(len)))
die(1, "setsockopt: %s", strerror(errno));
- memcpy(&sin.sin_addr, h->h_addr, sizeof(sin.sin_addr));
- sin.sin_port = htons(atoi(av[3]));
- if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)))
+ if (connect(fd, ai1->ai_addr, ai1->ai_addrlen))
die(1, "connect: %s", strerror(errno));
+ if (outf) {
+ salen = sizeof(addr);
+ if (getsockname(fd, &addr.sa, &salen))
+ die(1, "getsockname: %s", strerror(errno));
+ switch (addr.sa.sa_family) {
+ case AF_INET: port = ntohs(addr.sin.sin_port); break;
+ case AF_INET6: port = ntohs(addr.sin6.sin6_port); break;
+ default: assert(0);
+ }
+ fp = fopen(outf, "w");
+ if (!fp) die(1, "fopen(%s): %s", outf, strerror(errno));
+ fprintf(fp, "%d\n", port);
+ fclose(fp);
+ }
sel_initfile(&sel, &p->sf, fd, SEL_READ, dopacket, p);
sel_addfile(&p->sf);
+ freeaddrinfo(ai0); freeaddrinfo(ai1);
}
+static void addpeer(unsigned ac, char **av)
+ { addpeer_common("peer", AF_UNSPEC, ac, av); }
+static void addpeer4(unsigned ac, char **av)
+ { addpeer_common("peer4", AF_INET, ac, av); }
+static void addpeer6(unsigned ac, char **av)
+ { addpeer_common("peer6", AF_INET6, ac, av); }
+
/*----- Fork filter -------------------------------------------------------*/
typedef struct forknode {
static void addfork(filter *f, unsigned ac, char **av)
{
forkfilt *ff;
- if (ac != 1)
- die(1, "syntax: filt:fork:NAME");
+ if (ac != 1) die(1, "syntax: filt:fork:NAME");
ff = CREATE(forkfilt);
ff->name = xstrdup(av[0]);
ff->fn = 0;
forknode *fn, **ffn;
peer *p;
- if (ac < 1)
- die(1, "syntax: next:NAME:...");
+ if (ac < 1) die(1, "syntax: next:NAME:...");
for (i = 0; i < 2; i++) {
p = &peers[i];
for (f = p->f; f; f = f->next) {
- if (f->func != dofork)
- continue;
+ if (f->func != dofork) continue;
ff = f->state;
- for (j = 0; j < ac; j++) {
- if (strcmp(av[j], ff->name) == 0)
- goto match;
- }
+ for (j = 0; j < ac; j++)
+ if (strcmp(av[j], ff->name) == 0) goto match;
continue;
match:
fn = CREATE(forknode);
- for (ffn = &ff->fn; *ffn; ffn = &(*ffn)->next)
- ;
+ for (ffn = &ff->fn; *ffn; ffn = &(*ffn)->next);
fn->f = f->next;
f->next = 0;
fn->next = 0;
static void addcorrupt(filter *f, unsigned ac, char **av)
{
corrupt *c;
- if (ac > 1)
- die(1, "syntax: filt:corrupt[:P-CORRUPT]");
+ if (ac > 1) die(1, "syntax: filt:corrupt[:P-CORRUPT]");
c = CREATE(corrupt);
- if (ac > 0)
- c->p_corrupt = atoi(av[0]);
- else
- c->p_corrupt = 5;
+ if (ac > 0) c->p_corrupt = atoi(av[0]);
+ else c->p_corrupt = 5;
f->state = c;
f->func = docorrupt;
}
{
drop *d = f->state;
- if (!RND(d->p_drop))
- puts("drop packet");
- else
- PASS(f->next, buf, sz);
+ if (!RND(d->p_drop)) puts("drop packet");
+ else PASS(f->next, buf, sz);
}
static void adddrop(filter *f, unsigned ac, char **av)
{
drop *d;
- if (ac > 1)
- die(1, "syntax: filt:drop[:P-DROP]");
+ if (ac > 1) die(1, "syntax: filt:drop[:P-DROP]");
d = CREATE(drop);
- if (ac > 0)
- d->p_drop = atoi(av[0]);
- else
- d->p_drop = 5;
+ if (ac > 0) d->p_drop = atoi(av[0]);
+ else d->p_drop = 5;
f->state = d;
f->func = dodrop;
}
{
delay *d = dn->d;
delaynode *ddn;
+ unsigned i;
+
fputs(" send...\n", stdout);
assert(dn->buf);
PASS(d->f->next, dn->buf, dn->sz);
ddn->flag = 0;
printf(" move id %u from slot %u to slot %u", ddn->seq, ddn->i, dn->i);
}
- { unsigned i; for (i = 0; i < d->n; i++) assert(d->q[i].buf); }
+ for (i = 0; i < d->n; i++) assert(d->q[i].buf);
fputs(" remove", stdout);
}
}
delay *d;
unsigned i;
- if (ac < 1 || ac > 3)
- die(1, "syntax: filt:delay:QLEN[:MILLIS:P-REPLAY]");
+ if (ac < 1 || ac > 3) die(1, "syntax: filt:delay:QLEN[:MILLIS:P-REPLAY]");
d = CREATE(delay);
d->max = atoi(av[0]);
- if (ac > 1)
- d->t = strtoul(av[1], 0, 10);
- else
- d->t = 100;
+ if (ac > 1) d->t = strtoul(av[1], 0, 10);
+ else d->t = 100;
d->t *= 1000;
- if (ac > 2)
- d->p_replay = atoi(av[2]);
- else
- d->p_replay = 20;
+ if (ac > 2) d->p_replay = atoi(av[2]);
+ else d->p_replay = 20;
d->n = 0;
d->q = xmalloc(d->max * sizeof(delaynode));
d->f = f;
static void addsend(filter *f, unsigned ac, char **av)
{
- if (ac)
- die(1, "syntax: filt:send");
+ if (ac) die(1, "syntax: filt:send");
f->func = dosend;
}
{
filter **ff, *f = CREATE(filter);
const struct filtab *ft;
- if (ac < 1)
- die(1, "syntax: {l,r,}filt:NAME:...");
+ if (ac < 1) die(1, "syntax: {l,r,}filt:NAME:...");
f->next = 0;
f->p_from = from;
f->p_to = to;
f->state = 0;
- for (ff = &from->f; *ff; ff = &(*ff)->next)
- ;
+ for (ff = &from->f; *ff; ff = &(*ff)->next);
*ff = f;
- for (ft = filtab; ft->name; ft++) {
- if (strcmp(av[0], ft->name) == 0) {
- ft->func(f, ac - 1, av + 1);
- return;
- }
- }
+ for (ft = filtab; ft->name; ft++)
+ if (strcmp(av[0], ft->name) == 0)
+ { ft->func(f, ac - 1, av + 1); return; }
die(1, "unknown filter `%s'", av[0]);
}
sz /= 2;
rng->ops->fill(rng, buf, sz);
- if (f->type < 0x100)
- buf[0] = f->type;
+ if (f->type < 0x100) buf[0] = f->type;
puts("flood packet");
PASS(f->p->f, buf, sz);
setflood(f);
static void doflood(peer *p, unsigned ac, char **av)
{
flood *f;
- if (ac > 3)
- die(1, "syntax: flood[:TYPE:MILLIS:SIZE]");
+ if (ac > 3) die(1, "syntax: flood[:TYPE:MILLIS:SIZE]");
f = CREATE(flood);
f->p = p;
- if (ac > 0)
- f->type = strtoul(av[0], 0, 16);
- else
- f->type = 0x100;
- if (ac > 1)
- f->t = atoi(av[1]);
- else
- f->t = 10;
- if (ac > 2)
- f->sz = atoi(av[2]);
- else
- f->sz = 128;
+ if (ac > 0) f->type = strtoul(av[0], 0, 16);
+ else f->type = 0x100;
+ if (ac > 1) f->t = atoi(av[1]);
+ else f->t = 10;
+ if (ac > 2) f->sz = atoi(av[2]);
+ else f->sz = 128;
f->t *= 1000;
setflood(f);
}
{
FILE *fp;
dstr d = DSTR_INIT;
- if (!ac)
- die(1, "syntax: include:FILE:...");
+ if (!ac) die(1, "syntax: include:FILE:...");
while (*av) {
if ((fp = fopen(*av, "r")) == 0)
die(1, "fopen `%s': %s", *av, strerror(errno));
- while (dstr_putline(&d, fp) != EOF) {
- parse(d.buf);
- DRESET(&d);
- }
+ while (dstr_putline(&d, fp) != EOF) { parse(d.buf); DRESET(&d); }
fclose(fp);
av++;
}
void (*func)(unsigned /*ac*/, char **/*av*/);
} cmdtab[] = {
{ "peer", addpeer },
+ { "peer4", addpeer4 },
+ { "peer6", addpeer6 },
{ "include", include },
{ "filt", addfilter },
{ "lfilt", addlfilter },
unsigned c = 0;
const struct cmdtab *ct;
- p = strtok(p, ":");
- if (!p || *p == '#')
- return;
+ p = strtok(p, delim);
+ if (!p || *p == '#') return;
do {
v[c++] = p;
- p = strtok(0, ":");
+ p = strtok(0, delim);
} while (p && c < AVMAX - 1);
v[c] = 0;
for (ct = cmdtab; ct->name; ct++) {
{ pquis(fp, "$, TrIPE version " VERSION "\n"); }
static void usage(FILE *fp)
- { pquis(fp, "Usage: $ [-k KEYRING] DIRECTIVE...\n"); }
+ { pquis(fp, "Usage: $ [-d CHAR] [-k KEYRING] DIRECTIVE...\n"); }
static void help(FILE *fp)
{
-v, --version Show the version number.\n\
-u, --usage Show terse usage summary.\n\
\n\
+-d, --delimiter=CHAR Use CHAR rather than `:' as delimiter.\n\
-k, --keyring=FILE Fetch keys from FILE.\n\
\n\
Directives:\n\
- peer:NAME:LOCAL-PORT:REMOTE-ADDR:REMOTE-PORT\n\
+ peer{,4,6}:NAME:LOCAL-PORT:REMOTE-ADDR:REMOTE-PORT\n\
include:FILE\n\
{,l,r}filt:FILTER:ARGS:...\n\
next:TAG\n\
const char *kfname = "keyring.pub";
int i;
unsigned f = 0;
- char buf[16];
+ char buf[32];
+ static octet zero[CHACHA_NONCESZ];
#define f_bogus 1u
{ "help", 0, 0, 'h' },
{ "version", 0, 0, 'v' },
{ "usage", 0, 0, 'u' },
+ { "delimiter", OPTF_ARGREQ, 0, 'd' },
{ "keyring", OPTF_ARGREQ, 0, 'k' },
{ 0, 0, 0, 0 }
};
- if ((i = mdwopt(argc, argv, "hvuk:", opt, 0, 0, 0)) < 0)
- break;
+ if ((i = mdwopt(argc, argv, "hvud:k:", opt, 0, 0, 0)) < 0) break;
switch (i) {
- case 'h':
- help(stdout);
- exit(0);
- case 'v':
- version(stdout);
- exit(0);
- case 'u':
- usage(stdout);
- exit(0);
- case 'k':
- kfname = optarg;
- break;
- default:
- f |= f_bogus;
+ case 'h': help(stdout); exit(0);
+ case 'v': version(stdout); exit(0);
+ case 'u': usage(stdout); exit(0);
+ case 'd':
+ if (!optarg[0] || optarg[1])
+ die(1, "delimiter must be a single character");
+ delim = optarg;
break;
+ case 'k': kfname = optarg; break;
+ default: f |= f_bogus; break;
}
}
- if (f & f_bogus) {
- usage(stderr);
- exit(1);
- }
+ if (f & f_bogus) { usage(stderr); exit(1); }
+
rand_noisesrc(RAND_GLOBAL, &noise_source);
- rand_seed(RAND_GLOBAL, 160);
+ rand_seed(RAND_GLOBAL, 256);
rand_get(RAND_GLOBAL, buf, sizeof(buf));
- rng = rc4_rand(buf, sizeof(buf));
+ rng = chacha20_rand(buf, sizeof(buf), zero);
sel_init(&sel);
if (key_open(&keys, kfname, KOPEN_READ, key_moan, 0))
die(1, "couldn't open `%s': %s", kfname, strerror(errno));
- for (i = optind; i < argc; i++)
- parse(argv[i]);
- if (npeer != 2)
- die(1, "need two peers");
- for (;;)
- sel_select(&sel);
+ for (i = optind; i < argc; i++) parse(argv[i]);
+ if (npeer != 2) die(1, "need two peers");
+ for (;;) {
+ if (sel_select(&sel) && errno != EINTR)
+ die(1, "select failed: %s", strerror(errno));
+ }
#undef f_bogus
}