Greetings neighbor, have you heard the good news? DNS now comes with integrity
and confidentiality. I refer, of course, to DNS over HTTPS.

The patch below adds support to rebound. It seems to work with cloudflare's
1.1.1.1 service. It's quite preliminary, but it keeps the modifications to the
existing code minimal. Just a few insertions.

For now, it's enabled by adding "https 1.1.1.1 cloudflare-dns.com" to the
config file. https [ip] [hostname for cert checking].

The main caveat is that the https code all runs on a blocking socket. The tls
reads and writes can be added into the main event loop, and there should be
more than one socket. Or HTTP/2. But that's not necessary to get a feel for
how it works just yet.


Index: Makefile
===================================================================
RCS file: /cvs/src/usr.sbin/rebound/Makefile,v
retrieving revision 1.1
diff -u -p -r1.1 Makefile
--- Makefile    15 Oct 2015 19:43:30 -0000      1.1
+++ Makefile    28 Nov 2018 21:01:16 -0000
@@ -1,7 +1,9 @@
 # $OpenBSD: Makefile,v 1.1 2015/10/15 19:43:30 tedu Exp $
 
 PROG=  rebound
-SRCS=  rebound.c randomid.c
+SRCS=  rebound.c randomid.c https.c
+LDADD= -ltls
+
 CFLAGS+=-Wall
 
 MAN=   rebound.8
Index: https.c
===================================================================
RCS file: https.c
diff -N https.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ https.c     29 Nov 2018 04:56:10 -0000
@@ -0,0 +1,175 @@
+/* $OpenBSD: rebound.c,v 1.102 2018/11/20 03:42:56 tedu Exp $ */
+/*
+ * Copyright (c) 2018 Ted Unangst <t...@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * this file attempts to implement a client for RFC 8484:
+ * DNS Queries over HTTPS (DoH)
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <syslog.h>
+
+#include <tls.h>
+
+void logmsg(int prio, const char *msg, ...);
+void __dead logerr(const char *msg, ...);
+
+static struct tls_config *config;
+static struct tls *ctx;
+
+static int connected;
+static const char *servername;
+
+int
+https_init(void)
+{
+       int rv;
+
+       config = tls_config_new();
+       if (!config) {
+               logmsg(LOG_NOTICE, "failed to create tls config");
+               return -1;
+       }
+       tls_config_set_ca_file(config, tls_default_ca_cert_file());
+       ctx = tls_client();
+       if (!ctx) {
+               logmsg(LOG_NOTICE, "failed to create tls client");
+               goto fail;
+       }
+       rv = tls_configure(ctx, config);
+       if (rv != 0) {
+               logmsg(LOG_NOTICE, "failed to config tls client");
+               goto fail;
+       }
+       return 0;
+
+fail:
+       tls_free(ctx);
+       ctx = NULL;
+       tls_config_free(config);
+       config = NULL;
+       return -1;
+}
+
+int
+https_connect(const char *ip, const char *name)
+{
+       const char *port = "443";
+       int rv;
+
+       if (!ctx)
+               return -1;
+
+       if (connected)
+               tls_close(ctx);
+       connected = 0;
+       tls_reset(ctx);
+       rv = tls_configure(ctx, config);
+       if (rv != 0) {
+               logmsg(LOG_NOTICE, "failed to reconfig tls client");
+               return -1;
+       }
+       rv = tls_connect_servername(ctx, ip, port, name);
+       if (rv != 0) {
+               logmsg(LOG_NOTICE, "failed to connect with tls");
+               goto fail;
+       }
+       servername = name;
+       connected = 1;
+       return 0;
+
+fail:
+       return -1;
+}
+
+static const char headerfmt[] =
+       "POST /dns-query HTTP/1.1\r\n"
+       "Host: %s\r\n"
+       "User-Agent: rebound 6.4\r\n"
+       "Accept: application/dns-message\r\n"
+       "Content-Type: application/dns-message\r\n"
+       "Content-Length: %zu\r\n"
+       "\r\n";
+
+int
+https_query(uint8_t *query, size_t qlen, uint8_t *resp, size_t *resplen)
+{
+       char header[1024];
+       unsigned char buf[65536 + 1024];
+       unsigned char *two, *clptr, *endptr, *dataptr;
+       ssize_t amt, headerlen, have, needlen, contlen;
+       const char *errstr;
+
+       if (!connected)
+               return -1;
+
+       headerlen = snprintf(header, sizeof(header), headerfmt, servername, 
qlen);
+       tls_write(ctx, header, headerlen);
+       tls_write(ctx, query, qlen);
+       amt = tls_read(ctx, buf, sizeof(buf) - 1);
+       /* what in the world is going on here? */
+       if (amt < 10)
+               return -1;
+       buf[amt] = 0;
+       two = buf;
+       while (*two && two < buf + 10 && *two != '2')
+               two++;
+       if (*two != '2')
+               return -1;
+       if (memcmp(two, "200", 3) != 0)
+               return -1;
+       clptr = strcasestr(buf, "content-length:");
+       if (!clptr)
+               return -1;
+       while (*clptr && !isdigit(*clptr))
+               clptr++;
+       if (!isdigit(*clptr))
+               return -1;
+       endptr = clptr;
+       while (*endptr && *endptr != '\r')
+               endptr++;
+       if (*endptr != '\r')
+               return -1;
+       *endptr = 0;
+       contlen = strtonum(clptr, 0, 65536, &errstr);
+       if (errstr)
+               return -1;
+       dataptr = memmem(endptr + 1, amt - (endptr + 1 - buf), "\r\n\r\n", 4);
+       if (!dataptr)
+               return -1;
+       dataptr += 4;
+       have = amt - (dataptr - buf);
+       if (have > contlen)
+               have = contlen;
+       memcpy(resp, dataptr, have);
+       needlen = contlen;
+       needlen -= have;
+       while (needlen > 0) {
+               amt = tls_read(ctx, buf, needlen);
+               if (amt <= 0)
+                       break;
+               memcpy(resp + have, buf, amt);
+               have += amt;
+               needlen -= amt;
+       }
+       *resplen = have;
+
+       return 0;
+}
Index: rebound.c
===================================================================
RCS file: /cvs/src/usr.sbin/rebound/rebound.c,v
retrieving revision 1.102
diff -u -p -r1.102 rebound.c
--- rebound.c   20 Nov 2018 03:42:56 -0000      1.102
+++ rebound.c   29 Nov 2018 04:35:55 -0000
@@ -51,6 +51,14 @@
 
 uint16_t randomid(void);
 
+int https_init(void);
+int https_connect(const char *ip, const char *name);
+int https_query(uint8_t *query, size_t qlen, uint8_t *resp, size_t *resplen);
+
+static int https;
+static char https_ip[256];
+static char https_name[256];
+
 union sockun {
        struct sockaddr a;
        struct sockaddr_storage s;
@@ -122,7 +130,9 @@ static int connmax;
 static uint64_t conntotal;
 static int stopaccepting;
 
-static void
+static void sendreply(struct request *req, uint8_t *buf, size_t r);
+
+void
 logmsg(int prio, const char *msg, ...)
 {
        va_list ap;
@@ -140,7 +150,7 @@ logmsg(int prio, const char *msg, ...)
        }
 }
 
-static void __dead
+void __dead
 logerr(const char *msg, ...)
 {
        va_list ap;
@@ -418,6 +428,26 @@ newrequest(int ud, struct sockaddr *remo
                req->cacheent = hit;
        }
 
+       if (https) {
+               int rv;
+               char resp[65536];
+               size_t resplen;
+
+               rv = https_query(buf, r, resp, &resplen);
+               if (rv != 0) {
+                       rv = https_connect(https_ip, https_name);
+                       if (rv == 0)
+                               rv = https_query(buf, r, buf, &resplen);
+               }
+               if (rv != 0) {
+                       logmsg(LOG_NOTICE, "failed to make https query");
+                       goto fail;
+               }
+               sendreply(req, resp, resplen);
+               freerequest(req);
+               return NULL;
+       }
+
        req->s = socket(remoteaddr->sa_family, SOCK_DGRAM, 0);
        if (req->s == -1)
                goto fail;
@@ -485,17 +515,13 @@ minttl(struct dnspacket *resp, u_int rle
 }
 
 static void
-sendreply(struct request *req)
+sendreply(struct request *req, uint8_t *buf, size_t r)
 {
-       uint8_t buf[65536];
        struct dnspacket *resp;
        struct dnscache *ent;
-       size_t r;
        uint32_t ttl;
 
        resp = (struct dnspacket *)buf;
-
-       r = recv(req->s, buf, sizeof(buf), 0);
        if (r == 0 || r == -1 || r < sizeof(struct dnspacket))
                return;
        if (resp->id != req->reqid)
@@ -540,6 +566,16 @@ sendreply(struct request *req)
        }
 }
 
+static void
+handlereply(struct request *req)
+{
+       uint8_t buf[65536];
+       size_t r;
+
+       r = recv(req->s, buf, sizeof(buf), 0);
+       sendreply(req, buf, r);
+}
+
 static struct request *
 tcpphasetwo(struct request *req)
 {
@@ -743,6 +779,7 @@ readconfig(int conffd, union sockun *rem
 {
        const char ns[] = "nameserver";
        const char rc[] = "record";
+       const char doh[] = "https";
        char buf[1024];
        char *p;
        struct sockaddr_in *sin = &remoteaddr->i;
@@ -791,6 +828,13 @@ readconfig(int conffd, union sockun *rem
                                preloadA(name, value);
                                preloadPTR(value, name);
                        }
+               } else if (strncmp(buf, doh, strlen(doh)) == 0) {
+                       p = buf + strlen(doh) + 1;
+                       if (sscanf(p, "%255s %255s", https_ip, https_name) != 2)
+                               logerr("do not like https line");
+                       https = 1;
+                       if (rv == -1)
+                               rv = 0;
                }
        }
        fclose(conf);
@@ -934,7 +978,7 @@ workerloop(int conffd, int ud, int ld, i
                                } else {
                                        req = ke->udata;
                                        if (req->tcp == 0)
-                                               sendreply(req);
+                                               handlereply(req);
                                        freerequest(req);
                                }
                                break;
@@ -1148,6 +1192,8 @@ main(int argc, char **argv)
 
        signal(SIGPIPE, SIG_IGN);
        signal(SIGUSR1, SIG_IGN);
+
+       https_init();
 
        while ((ch = getopt(argc, argv, "c:dl:W")) != -1) {
                switch (ch) {

Reply via email to