dunnel

An experimental DTLS proxy

git clone https://git.8pit.net/dunnel.git

  1#include <err.h>
  2#include <dtls.h>
  3#include <dtls_debug.h>
  4#include <poll.h>
  5#include <stdio.h>
  6#include <stdlib.h>
  7#include <string.h>
  8#include <unistd.h>
  9
 10#include "dat.h"
 11#include "fns.h"
 12
 13/**
 14 * The session used for the DTLS socket.
 15 */
 16session_t dsess;
 17
 18/**
 19 * The address of the client socket from which we last
 20 * received a datagram.
 21 *
 22 * XXX: We don't support multiple clients.
 23 */
 24session_t csess;
 25
 26/**
 27 * Whether dunnel was started in server mode using -s.
 28 */
 29int smode;
 30
 31/**
 32 * The global DTLS context.
 33 */
 34static dtls_context_t *ctx;
 35
 36#define newpollfd(FD) \
 37	(struct pollfd){.fd = FD, .events = POLLIN | POLLERR};
 38
 39static void
 40usage(char *progname)
 41{
 42	fprintf(stderr, "Usage: %s "
 43		"-s -v [LOG LEVEL] -a [ADDR] -p [PORT] "
 44		"-i [ID FILE] -k [KEY FILE] "
 45		"DTLS_HOST DTLS_PORT\n", progname);
 46	exit(EXIT_FAILURE);
 47}
 48
 49static void
 50handle(int fd, struct dctx *dctx)
 51{
 52	ssize_t r;
 53	session_t sess, *sptr;
 54	unsigned char buf[DTLS_MAX_BUF];
 55
 56	memset(&sess, '\0', sizeof(sess));
 57	if (dctx->ufd == fd) {
 58		sptr = (smode) ? &sess : &csess;
 59	} else { /* dtls socket */
 60		sptr = (smode) ? &csess : &sess;
 61	}
 62
 63	sptr->size = sizeof(sptr->addr);
 64	if ((r = recvfrom(fd, buf, DTLS_MAX_BUF, MSG_DONTWAIT,
 65			&sptr->addr.sa, &sptr->size)) == -1) {
 66		warn("recvfrom failed");
 67		return;
 68	}
 69
 70	if (dctx->ufd == fd) {
 71		sptr = (smode) ? &csess : &dsess;
 72		if (dtls_write(ctx, sptr, buf, r) == -1) {
 73			warnx("dtls_write failed");
 74			return;
 75		}
 76	} else {
 77		dtls_handle_message(ctx, sptr, buf, r);
 78	}
 79}
 80
 81static void
 82ploop(struct dctx *dctx)
 83{
 84	size_t i;
 85	short ev;
 86	nfds_t nfds;
 87	int fd, ufd, dfd;
 88	struct pollfd fds[2];
 89
 90	nfds = sizeof(fds) / sizeof(fds[0]);
 91	dfd = dctx->dfd;
 92	ufd = dctx->ufd;
 93
 94	fds[0] = newpollfd(dfd);
 95	fds[1] = newpollfd(ufd);
 96
 97	for (;;) {
 98		if (poll(fds, nfds, -1) == -1)
 99			err(EXIT_FAILURE, "poll failed");
100
101		for (i = 0; i < nfds; i++) {
102			fd = fds[i].fd;
103			ev = fds[i].revents;
104
105			if (ev & POLLIN) {
106				handle(fd, dctx);
107			} else if (ev & POLLERR) {
108				errx(EXIT_FAILURE, "Received POLLERR on %s socket\n",
109					(fd == ufd) ? "UDP" : "DTLS");
110			}
111		}
112	}
113}
114
115int
116main(int argc, char **argv)
117{
118	int opt, ufd;
119	sockop uop, dop;
120	unsigned char *key, *id;
121	char *uaddr, *uport, *daddr, *dport;
122
123	smode = 0;
124	uaddr = uport = NULL;
125	key = id = NULL;
126
127	dtls_init();
128	while ((opt = getopt(argc, argv, "a:i:k:p:sv:")) != -1) {
129		switch (opt) {
130		case 'a':
131			uaddr = optarg;
132			break;
133		case 'i':
134			if (!(id = readfile(optarg)))
135				err(EXIT_FAILURE, "couldn't read identity");
136			break;
137		case 'k':
138			if (!(key = readfile(optarg)))
139				err(EXIT_FAILURE, "couldn't read key");
140			break;
141		case 'p':
142			uport = optarg;
143			break;
144		case 's': /* act as dtls server, default: act as dtls client */
145			smode = 1;
146			break;
147		case 'v':
148			dtls_set_log_level(atoi(optarg));
149			break;
150		default:
151			usage(*argv);
152			break;
153		}
154	}
155
156	if (smode) {
157		uop = SOCK_CONN;
158		dop = SOCK_BIND;
159	} else {
160		uop = SOCK_BIND;
161		dop = SOCK_CONN;
162	}
163
164	if (argc <= 2 || optind + 1 >= argc)
165		usage(*argv);
166	else if (!key || !id)
167		errx(EXIT_FAILURE, "A key and an identity must be provided");
168
169	daddr = argv[optind];
170	dport = argv[optind + 1];
171
172	if ((ufd = usock(uaddr, (!uport) ? dport : uport, uop)) == -1)
173		err(EXIT_FAILURE, "usock failed");
174	if (!(ctx = dsock(daddr, dport, id, key, ufd, dop)))
175		err(EXIT_FAILURE, "dsock failed");
176
177	ploop(dtls_get_app_data(ctx));
178	return EXIT_SUCCESS;
179}