Blob


1 /*
2 * Copyright (c) 2022 Omar Polo <op@omarpolo.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
17 #include "compat.h"
19 #include <sys/types.h>
20 #include <sys/socket.h>
22 #include <netdb.h>
24 #include <errno.h>
25 #include <signal.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <syslog.h>
30 #include <tls.h>
31 #include <unistd.h>
33 #include "log.h"
35 #define MIN(a, b) ((a) < (b) ? (a) : (b))
37 int debug;
38 int verbose;
39 const char *tohost;
40 const char *fromhost;
42 uint8_t *cert;
43 size_t certlen;
44 uint8_t *key;
45 size_t keylen;
47 #define MAXSOCK 32
48 struct event sockev[MAXSOCK];
49 int socks[MAXSOCK];
50 int nsock;
52 char kamihost[64];
53 char kamiport[8];
55 struct conn {
56 struct tls *ctx;
57 struct bufferevent *server;
58 int kfd;
59 struct bufferevent *client;
60 int lfd;
61 };
63 #ifndef __OpenBSD__
64 # define pledge(a, b) (0)
65 #endif
67 static const char *
68 copysec(const char *s, char *d, size_t len)
69 {
70 const char *c;
72 if ((c = strchr(s, ':')) == NULL)
73 return NULL;
74 if ((size_t)(c-s) >= len-1)
75 return NULL;
76 memset(d, 0, len);
77 memcpy(d, s, c - s);
78 return c;
79 }
81 static void
82 parse_tohost(void)
83 {
84 const char *c;
86 if ((c = strchr(tohost, ':')) == NULL) {
87 strlcpy(kamihost, tohost, sizeof(kamihost));
88 strlcpy(kamiport, "1337", sizeof(kamiport));
89 return;
90 }
92 if ((c = copysec(tohost, kamihost, sizeof(kamihost))) == NULL)
93 fatalx("hostname too long: %s", tohost);
95 strlcpy(kamiport, c+1, sizeof(kamiport));
96 }
98 static void
99 tls_readcb(int fd, short event, void *d)
101 struct bufferevent *bufev = d;
102 struct conn *conn = bufev->cbarg;
103 char buf[IBUF_READ_SIZE];
104 int what = EVBUFFER_READ;
105 int howmuch = IBUF_READ_SIZE;
106 ssize_t ret;
107 size_t len;
109 if (event == EV_TIMEOUT) {
110 what |= EVBUFFER_TIMEOUT;
111 goto err;
114 if (bufev->wm_read.high != 0)
115 howmuch = MIN(sizeof(buf), bufev->wm_read.high);
117 switch (ret = tls_read(conn->ctx, buf, howmuch)) {
118 case TLS_WANT_POLLIN:
119 case TLS_WANT_POLLOUT:
120 goto retry;
121 case -1:
122 what |= EVBUFFER_ERROR;
123 goto err;
125 len = ret;
127 if (len == 0) {
128 what |= EVBUFFER_EOF;
129 goto err;
132 if (evbuffer_add(bufev->input, buf, len) == -1) {
133 what |= EVBUFFER_ERROR;
134 goto err;
137 event_add(&bufev->ev_read, NULL);
139 len = EVBUFFER_LENGTH(bufev->input);
140 if (bufev->wm_read.low != 0 && len < bufev->wm_read.low)
141 return;
142 if (bufev->wm_read.high != 0 && len > bufev->wm_read.high) {
143 /*
144 * here we could implement some read pressure
145 * mechanism.
146 */
149 if (bufev->readcb != NULL)
150 (*bufev->readcb)(bufev, bufev->cbarg);
152 return;
154 retry:
155 event_add(&bufev->ev_read, NULL);
156 return;
158 err:
159 (*bufev->errorcb)(bufev, what, bufev->cbarg);
162 static void
163 tls_writecb(int fd, short event, void *d)
165 struct bufferevent *bufev = d;
166 struct conn *conn = bufev->cbarg;
167 ssize_t ret;
168 size_t len;
169 short what = EVBUFFER_WRITE;
171 if (event == EV_TIMEOUT) {
172 what |= EVBUFFER_TIMEOUT;
173 goto err;
176 if (EVBUFFER_LENGTH(bufev->output) != 0) {
177 ret = tls_write(conn->ctx,
178 EVBUFFER_DATA(bufev->output),
179 EVBUFFER_LENGTH(bufev->output));
180 switch (ret) {
181 case TLS_WANT_POLLIN:
182 case TLS_WANT_POLLOUT:
183 goto retry;
184 case -1:
185 what |= EVBUFFER_ERROR;
186 goto err;
188 len = ret;
189 evbuffer_drain(bufev->output, len);
192 if (EVBUFFER_LENGTH(bufev->output) != 0)
193 event_add(&bufev->ev_write, NULL);
195 if (bufev->writecb != NULL &&
196 EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low)
197 (*bufev->writecb)(bufev, bufev->cbarg);
198 return;
200 retry:
201 event_add(&bufev->ev_write, NULL);
202 return;
204 err:
205 (*bufev->errorcb)(bufev, what, bufev->cbarg);
208 static void
209 setup(void)
211 struct addrinfo hints, *res, *res0;
212 int v, r, saved_errno;
213 char host[64];
214 const char *c, *h, *port, *cause;
216 if ((c = strchr(fromhost, ':')) == NULL) {
217 h = NULL;
218 port = fromhost;
219 } else {
220 if ((c = copysec(fromhost, host, sizeof(host))) == NULL)
221 fatalx("hostname too long: %s", fromhost);
222 h = host;
223 port = c+1;
226 memset(&hints, 0, sizeof(hints));
227 hints.ai_family = AF_UNSPEC;
228 hints.ai_socktype = SOCK_STREAM;
229 hints.ai_flags = AI_PASSIVE;
231 r = getaddrinfo(h, port, &hints, &res0);
232 if (r != 0)
233 fatalx("getaddrinfo(%s): %s", fromhost,
234 gai_strerror(r));
236 for (res = res0; res && nsock < MAXSOCK; res = res->ai_next) {
237 socks[nsock] = socket(res->ai_family, res->ai_socktype,
238 res->ai_protocol);
239 if (socks[nsock] == -1) {
240 cause = "socket";
241 continue;
244 if (bind(socks[nsock], res->ai_addr, res->ai_addrlen) == -1) {
245 cause = "bind";
246 saved_errno = errno;
247 close(socks[nsock]);
248 errno = saved_errno;
249 continue;
252 v = 1;
253 if (setsockopt(socks[nsock], SOL_SOCKET, SO_REUSEADDR, &v,
254 sizeof(v)) == -1)
255 err(1, "setsockopt(SO_REUSEADDR)");
257 v = 1;
258 if (setsockopt(socks[nsock], SOL_SOCKET, SO_REUSEPORT, &v,
259 sizeof(v)) == -1)
260 err(1, "setsockopt(SO_REUSEPORT)");
262 listen(socks[nsock], 5);
263 nsock++;
266 if (nsock == 0)
267 fatal("%s", cause);
269 freeaddrinfo(res0);
272 static int
273 servconnect(void)
275 struct addrinfo hints, *res, *res0;
276 int r, saved_errno, sock;
277 const char *cause;
279 memset(&hints, 0, sizeof(hints));
280 hints.ai_family = AF_UNSPEC;
281 hints.ai_socktype = SOCK_STREAM;
283 r = getaddrinfo(kamihost, kamiport, &hints, &res0);
284 if (r != 0) {
285 log_warnx("getaddrinfo(%s, %s): %s", kamihost, kamiport,
286 gai_strerror(r));
287 return -1;
290 for (res = res0; res != NULL; res = res->ai_next) {
291 sock = socket(res->ai_family, res->ai_socktype,
292 res->ai_protocol);
293 if (sock == -1) {
294 cause = "socket";
295 continue;
298 if (connect(sock, res->ai_addr, res->ai_addrlen) == -1) {
299 cause = "connect";
300 saved_errno = errno;
301 close(sock);
302 errno = saved_errno;
303 sock = -1;
304 continue;
307 /* found one */
308 break;
311 if (sock == -1)
312 log_warn("%s", cause);
314 freeaddrinfo(res0);
315 return sock;
318 static void
319 copy_to_server(struct bufferevent *bev, void *d)
321 struct conn *c = d;
323 bufferevent_write_buffer(c->server, EVBUFFER_INPUT(bev));
326 static void
327 copy_to_client(struct bufferevent *bev, void *d)
329 struct conn *c = d;
331 bufferevent_write_buffer(c->client, EVBUFFER_INPUT(bev));
334 static void
335 nopcb(struct bufferevent *bev, void *d)
337 return;
340 static void
341 errcb(struct bufferevent *bev, short ev, void *d)
343 struct conn *c = d;
345 log_debug("closing connection (event=%x / side=%s)", ev,
346 bev == c->server ? "server" : "client");
348 bufferevent_free(c->server);
349 bufferevent_free(c->client);
351 tls_close(c->ctx);
352 tls_free(c->ctx);
354 close(c->lfd);
355 close(c->kfd);
357 free(c);
360 static void
361 doaccept(int fd, short ev, void *data)
363 struct tls_config *conf;
364 struct conn *c;
365 int r;
367 if ((c = calloc(1, sizeof(*c))) == NULL)
368 fatal("calloc");
370 if ((c->lfd = accept(fd, NULL, 0)) == -1) {
371 log_warn("accept");
372 free(c);
373 return;
376 if ((c->kfd = servconnect()) == -1) {
377 close(c->lfd);
378 free(c);
379 return;
382 if ((c->ctx = tls_client()) == NULL)
383 fatal("tls_client");
385 if ((conf = tls_config_new()) == NULL)
386 fatal("tls_config_new");
388 if (tls_config_set_cert_mem(conf, cert, certlen) == -1 ||
389 tls_config_set_key_mem(conf, key, keylen) == -1)
390 fatalx("tls_config_set_{cert,key}: %s", tls_config_error(conf));
391 tls_config_insecure_noverifycert(conf);
393 if (tls_configure(c->ctx, conf) == -1)
394 fatalx("tls_configure");
396 tls_config_free(conf);
398 if (tls_connect_socket(c->ctx, c->kfd, kamihost) == -1)
399 fatal("tls_connect_socket");
401 again: switch (r = tls_handshake(c->ctx)) {
402 case -1:
403 log_warnx("tls_handshake: %s", tls_error(c->ctx));
404 tls_close(c->ctx);
405 tls_free(c->ctx);
406 close(c->lfd);
407 close(c->kfd);
408 free(c);
409 return;
410 case TLS_WANT_POLLIN:
411 case TLS_WANT_POLLOUT:
412 goto again;
415 c->server = bufferevent_new(c->kfd, copy_to_client, nopcb, errcb, c);
416 if (c->server == NULL)
417 fatal("bufferevent_new");
419 event_set(&c->server->ev_read, c->kfd, EV_READ, tls_readcb,
420 c->server);
421 event_set(&c->server->ev_write, c->kfd, EV_WRITE, tls_writecb,
422 c->server);
424 #if HAVE_EVENT2
425 evbuffer_unfreeze(c->server->input, 0);
426 evbuffer_unfreeze(c->server->output, 1);
427 #endif
429 c->client = bufferevent_new(c->lfd, copy_to_server, nopcb, errcb, c);
430 if (c->client == NULL)
431 fatal("bufferevent_new");
433 bufferevent_enable(c->server, EV_READ|EV_WRITE);
434 bufferevent_enable(c->client, EV_READ|EV_WRITE);
437 __dead static void
438 usage(void)
440 fprintf(stderr,
441 "usage: %s [-dv] -c host[:port] -l [host:]port -C cert [-K key]\n",
442 getprogname());
443 exit(1);
446 int
447 main(int argc, char **argv)
449 int ch, i;
450 const char *certf = NULL, *keyf = NULL;
452 log_init(1, LOG_DAEMON);
453 log_setverbose(1);
455 while ((ch = getopt(argc, argv, "C:c:dK:l:v")) != -1) {
456 switch (ch) {
457 case 'C':
458 certf = optarg;
459 break;
460 case 'c':
461 tohost = optarg;
462 break;
463 case 'd':
464 debug = 1;
465 break;
466 case 'K':
467 keyf = optarg;
468 break;
469 case 'l':
470 fromhost = optarg;
471 break;
472 case 'v':
473 verbose = 1;
474 break;
475 default:
476 usage();
479 argc -= optind;
480 argv += optind;
482 if (argc != 0)
483 usage();
484 if (certf == NULL || tohost == NULL || fromhost == NULL)
485 usage();
486 if (keyf == NULL)
487 keyf = certf;
489 parse_tohost();
491 if ((cert = tls_load_file(certf, &certlen, NULL)) == NULL)
492 fatal("can't load %s", certf);
493 if ((key = tls_load_file(keyf, &keylen, NULL)) == NULL)
494 fatal("can't load %s", keyf);
496 log_init(debug, LOG_DAEMON);
497 log_setverbose(verbose);
499 if (!debug)
500 daemon(1, 0);
502 signal(SIGPIPE, SIG_IGN);
504 event_init();
506 setup();
507 for (i = 0; i < nsock; ++i) {
508 event_set(&sockev[i], socks[i], EV_READ|EV_PERSIST,
509 doaccept, NULL);
510 event_add(&sockev[i], NULL);
513 if (pledge("stdio dns inet", NULL) == -1)
514 err(1, "pledge");
516 log_info("starting");
517 event_dispatch();
519 return 0;