]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- #1217. DNSCrypt support, with --enable-dnscrypt, libsodium and then
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Mon, 20 Mar 2017 14:55:31 +0000 (14:55 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Mon, 20 Mar 2017 14:55:31 +0000 (14:55 +0000)
  enabled in the config file from Manu Bretelle.

git-svn-id: file:///svn/unbound/trunk@4065 be551aaa-1e26-0410-a405-d3ace91eadb9

25 files changed:
.gitignore
Makefile.in
config.h.in
configure.ac
daemon/daemon.c
daemon/daemon.h
daemon/worker.c
dnscrypt/cert.h [new file with mode: 0644]
dnscrypt/dnscrypt.c [new file with mode: 0644]
dnscrypt/dnscrypt.h [new file with mode: 0644]
dnscrypt/dnscrypt.m4 [new file with mode: 0644]
dnscrypt/dnscrypt_config.h.in [new file with mode: 0644]
doc/Changelog
doc/example.conf.in
doc/unbound.conf.5.in
services/listen_dnsport.c
services/listen_dnsport.h
sldns/sbuffer.h
testcode/do-tests.sh
util/config_file.c
util/config_file.h
util/configlexer.lex
util/configparser.y
util/netevent.c
util/netevent.h

index 7fed8d74d3860854ffd4344a194b3b465ec996ef..baf06faadb6eea610f7ee1d147b68ef5a967219c 100644 (file)
@@ -7,6 +7,7 @@
 /config.log
 /config.status
 /dnstap/dnstap_config.h
+/dnscrypt/dnscrypt_config.h
 /doc/example.conf
 /doc/libunbound.3
 /doc/unbound-anchor.8
index a4bb909fb7a1f2ecdc9f0a819ce6437ad0215fd6..036e5b7308505392dccdd00fc13d5a075aa0c76a 100644 (file)
@@ -23,6 +23,8 @@ CHECKLOCK_SRC=testcode/checklocks.c
 CHECKLOCK_OBJ=@CHECKLOCK_OBJ@
 DNSTAP_SRC=@DNSTAP_SRC@
 DNSTAP_OBJ=@DNSTAP_OBJ@
+DNSCRYPT_SRC=@DNSCRYPT_SRC@
+DNSCRYPT_OBJ=@DNSCRYPT_OBJ@
 WITH_PYTHONMODULE=@WITH_PYTHONMODULE@
 WITH_PYUNBOUND=@WITH_PYUNBOUND@
 PY_MAJOR_VERSION=@PY_MAJOR_VERSION@
@@ -115,7 +117,7 @@ validator/val_kcache.c validator/val_kentry.c validator/val_neg.c \
 validator/val_nsec3.c validator/val_nsec.c validator/val_secalgo.c \
 validator/val_sigcrypt.c validator/val_utils.c dns64/dns64.c \
 cachedb/cachedb.c respip/respip.c $(CHECKLOCK_SRC) \
-$(DNSTAP_SRC)
+$(DNSTAP_SRC) $(DNSCRYPT_SRC)
 COMMON_OBJ_WITHOUT_NETCALL=dns.lo infra.lo rrset.lo dname.lo msgencode.lo \
 as112.lo msgparse.lo msgreply.lo packed_rrset.lo iterator.lo iter_delegpt.lo \
 iter_donotq.lo iter_fwd.lo iter_hints.lo iter_priv.lo iter_resptype.lo \
@@ -126,7 +128,7 @@ random.lo rbtree.lo regional.lo rtt.lo dnstree.lo lookup3.lo lruhash.lo \
 slabhash.lo timehist.lo tube.lo winsock_event.lo autotrust.lo val_anchor.lo \
 validator.lo val_kcache.lo val_kentry.lo val_neg.lo val_nsec3.lo val_nsec.lo \
 val_secalgo.lo val_sigcrypt.lo val_utils.lo dns64.lo cachedb.lo \
-$(PYTHONMOD_OBJ) $(CHECKLOCK_OBJ) $(DNSTAP_OBJ)
+$(PYTHONMOD_OBJ) $(CHECKLOCK_OBJ) $(DNSTAP_OBJ) $(DNSCRYPT_OBJ)
 COMMON_OBJ_WITHOUT_NETCALL+=respip.lo
 COMMON_OBJ_WITHOUT_UB_EVENT=$(COMMON_OBJ_WITHOUT_NETCALL) netevent.lo listen_dnsport.lo \
 outside_network.lo
@@ -382,6 +384,13 @@ dnstap/dnstap.pb-c.c dnstap/dnstap.pb-c.h: $(srcdir)/dnstap/dnstap.proto
 
 dnstap.pb-c.lo dnstap.pb-c.o: dnstap/dnstap.pb-c.c dnstap/dnstap.pb-c.h
 
+# dnscrypt
+dnscrypt.lo dnscrypt.o: $(srcdir)/dnscrypt/dnscrypt.c config.h \
+       dnscrypt/dnscrypt_config.h \
+       $(srcdir)/dnscrypt/dnscrypt.h $(srcdir)/dnscrypt/cert.h \
+       $(srcdir)/util/config_file.h $(srcdir)/util/log.h \
+       $(srcdir)/util/netevent.h
+
 # Python Module
 pythonmod.lo pythonmod.o: $(srcdir)/pythonmod/pythonmod.c config.h \
        pythonmod/interface.h \
@@ -587,6 +596,7 @@ depend:
                        -e 's?$$(srcdir)/util/configparser.c?util/configparser.c?g' \
                        -e 's?$$(srcdir)/util/configparser.h?util/configparser.h?g' \
                        -e 's?$$(srcdir)/dnstap/dnstap_config.h??g' \
+                       -e 's?$$(srcdir)/dnscrypt/dnscrypt_config.h??g' \
                        -e 's?$$(srcdir)/pythonmod/pythonmod.h?$$(PYTHONMOD_HEADER)?g' \
                        -e 's!\(.*\)\.o[ :]*!\1.lo \1.o: !g' \
                        > $(DEPEND_TMP)
index ada2b69a2c3dcdbe6f425f9b97eec77974f92afa..3c973e949530a220e38ec5fab64e2f03d80b0ef5 100644 (file)
 /* Define to 1 to use cachedb support */
 #undef USE_CACHEDB
 
+/* Define to 1 to enable dnscrypt support */
+#undef USE_DNSCRYPT
+
 /* Define to 1 to enable dnstap support */
 #undef USE_DNSTAP
 
index 7f7ab6fa5f6b5f51154a9ca3e902e462a768a3d8..c50a070de15fa38de72defd6819dc38e4799a6c1 100644 (file)
@@ -6,6 +6,7 @@ sinclude(ax_pthread.m4)
 sinclude(acx_python.m4)
 sinclude(ac_pkg_swig.m4)
 sinclude(dnstap/dnstap.m4)
+sinclude(dnscrypt/dnscrypt.m4)
 
 # must be numbers. ac_defun because of later processing
 m4_define([VERSION_MAJOR],[1])
@@ -1314,6 +1315,19 @@ dt_DNSTAP([$UNBOUND_RUN_DIR/dnstap.sock],
     ]
 )
 
+# check for dnscrypt if requested
+dnsc_DNSCRYPT([
+        AC_DEFINE([USE_DNSCRYPT], [1], [Define to 1 to enable dnscrypt support])
+        AC_SUBST([ENABLE_DNSCRYPT], [1])
+
+        AC_SUBST([DNSCRYPT_SRC], ["dnscrypt/dnscrypt.c"])
+        AC_SUBST([DNSCRYPT_OBJ], ["dnscrypt.lo"])
+    ],
+    [
+        AC_SUBST([ENABLE_DNSCRYPT], [0])
+    ]
+)
+
 # check for cachedb if requested
 AC_ARG_ENABLE(cachedb, AC_HELP_STRING([--enable-cachedb], [enable cachedb module that can use external cache storage]))
 case "$enable_cachedb" in
@@ -1617,6 +1631,6 @@ dnl if this is a distro tarball, that was already done by makedist.sh
 AC_SUBST(version, [VERSION_MAJOR.VERSION_MINOR.VERSION_MICRO])
 AC_SUBST(date, [`date +'%b %e, %Y'`])
 
-AC_CONFIG_FILES([Makefile doc/example.conf doc/libunbound.3 doc/unbound.8 doc/unbound-anchor.8 doc/unbound-checkconf.8 doc/unbound.conf.5 doc/unbound-control.8 doc/unbound-host.1 smallapp/unbound-control-setup.sh dnstap/dnstap_config.h contrib/libunbound.pc contrib/unbound.socket contrib/unbound.service])
+AC_CONFIG_FILES([Makefile doc/example.conf doc/libunbound.3 doc/unbound.8 doc/unbound-anchor.8 doc/unbound-checkconf.8 doc/unbound.conf.5 doc/unbound-control.8 doc/unbound-host.1 smallapp/unbound-control-setup.sh dnstap/dnstap_config.h dnscrypt/dnscrypt_config.h contrib/libunbound.pc contrib/unbound.socket contrib/unbound.service])
 AC_CONFIG_HEADER([config.h])
 AC_OUTPUT
index af65b47ac1c2eb29854e40d247be7c2ec4e32497..1d06392e7b85b9a66d7eade88b0758bb7fe0f4d5 100644 (file)
@@ -574,6 +574,17 @@ daemon_fork(struct daemon* daemon)
 
        if(!acl_list_apply_cfg(daemon->acl, daemon->cfg, daemon->views))
                fatal_exit("Could not setup access control list");
+       if(daemon->cfg->dnscrypt) {
+#ifdef USE_DNSCRYPT
+               daemon->dnscenv = dnsc_create();
+               if (!daemon->dnscenv)
+                       fatal_exit("dnsc_create failed");
+               dnsc_apply_cfg(daemon->dnscenv, daemon->cfg);
+#else
+               fatal_exit("dnscrypt enabled in config but unbound was not built with "
+                                  "dnscypt support");
+#endif
+       }
        /* create global local_zones */
        if(!(daemon->local_zones = local_zones_create()))
                fatal_exit("Could not create local zones: out of memory");
index 2e70e4e5202186f561c920931f0d9ca61dcd2c2c..031e05da34336ee02c4d763c80d6047e3921ebad 100644 (file)
@@ -64,6 +64,11 @@ struct shm_main_info;
 struct dt_env;
 #endif
 
+#include "dnscrypt/dnscrypt_config.h"
+#ifdef USE_DNSCRYPT
+struct dnsc_env;
+#endif
+
 /**
  * Structure holding worker list.
  * Holds globally visible information.
@@ -125,6 +130,10 @@ struct daemon {
        struct respip_set* respip_set;
        /** some response-ip tags or actions are configured if true */
        int use_response_ip;
+#ifdef USE_DNSCRYPT
+       /** the dnscrypt environment */
+       struct dnsc_env* dnscenv;
+#endif
 };
 
 /**
index 422400de3da39fec33841123d5b828a0bfc99447..580846d54998f18c81f939d4490beea5c1fc9d94 100644 (file)
@@ -75,6 +75,7 @@
 #include "sldns/sbuffer.h"
 #include "sldns/wire2str.h"
 #include "util/shm_side/shm_main.h"
+#include "dnscrypt/dnscrypt.h"
 
 #ifdef HAVE_SYS_TYPES_H
 #  include <sys/types.h>
@@ -973,6 +974,40 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
                verbose(VERB_ALGO, "handle request called with err=%d", error);
                return 0;
        }
+#ifdef USE_DNSCRYPT
+    repinfo->max_udp_size = worker->daemon->cfg->max_udp_size;
+    if(!dnsc_handle_curved_request(worker->daemon->dnscenv, repinfo)) {
+        return 0;
+    }
+    if(c->dnscrypt && !repinfo->is_dnscrypted) {
+        char buf[LDNS_MAX_DOMAINLEN+1];
+        // Check if this is unencrypted and asking for certs
+        if(worker_check_request(c->buffer, worker) != 0) {
+            verbose(VERB_ALGO, "dnscrypt: worker check request: bad query.");
+            log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
+            comm_point_drop_reply(repinfo);
+            return 0;
+        }
+        if(!query_info_parse(&qinfo, c->buffer)) {
+            verbose(VERB_ALGO, "dnscrypt: worker parse request: formerror.");
+            log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
+            comm_point_drop_reply(repinfo);
+            return 0;
+        }
+        dname_str(qinfo.qname, buf);
+        if(!(qinfo.qtype == LDNS_RR_TYPE_TXT &&
+             strcasecmp(buf, worker->daemon->dnscenv->provider_name) == 0)) {
+            verbose(VERB_ALGO,
+                    "dnscrypt: not TXT %s. Receive: %s %s",
+                    worker->daemon->dnscenv->provider_name,
+                    sldns_rr_descript(qinfo.qtype)->_name,
+                    buf);
+            comm_point_drop_reply(repinfo);
+            return 0;
+        }
+        sldns_buffer_rewind(c->buffer);
+    }
+#endif
 #ifdef USE_DNSTAP
        if(worker->dtenv.log_client_query_messages)
                dt_msg_send_client_query(&worker->dtenv, &repinfo->addr, c->type,
@@ -1340,6 +1375,11 @@ send_reply_rc:
                log_reply_info(0, &qinfo, &repinfo->addr, repinfo->addrlen,
                        tv, 1, c->buffer);
        }
+#ifdef USE_DNSCRYPT
+    if(!dnsc_handle_uncurved_request(repinfo)) {
+        return 0;
+    }
+#endif
        return rc;
 }
 
diff --git a/dnscrypt/cert.h b/dnscrypt/cert.h
new file mode 100644 (file)
index 0000000..50c9f81
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef UNBOUND_DNSCRYPT_CERT_H
+#define UNBOUND_DNSCRYPT_CERT_H
+
+#include <sodium.h>
+#define CERT_MAGIC_CERT "DNSC"
+#define CERT_MAJOR_VERSION 1
+#define CERT_MINOR_VERSION 0
+#define CERT_OLD_MAGIC_HEADER "7PYqwfzt"
+
+#define CERT_FILE_EXPIRE_DAYS 365
+
+struct SignedCert {
+    uint8_t magic_cert[4];
+    uint8_t version_major[2];
+    uint8_t version_minor[2];
+
+    // Signed Content
+    uint8_t server_publickey[crypto_box_PUBLICKEYBYTES];
+    uint8_t magic_query[8];
+    uint8_t serial[4];
+    uint8_t ts_begin[4];
+    uint8_t ts_end[4];
+    uint8_t end[64];
+};
+
+
+#endif
diff --git a/dnscrypt/dnscrypt.c b/dnscrypt/dnscrypt.c
new file mode 100644 (file)
index 0000000..3478f82
--- /dev/null
@@ -0,0 +1,522 @@
+
+#include "config.h"
+#include <stdlib.h>
+#include <fcntl.h>
+#include <event2/util.h>
+#include "sldns/sbuffer.h"
+#include "util/config_file.h"
+#include "util/net_help.h"
+#include "util/netevent.h"
+#include "util/log.h"
+
+#include "dnscrypt/cert.h"
+#include "dnscrypt/dnscrypt.h"
+
+#include <ctype.h>
+
+#define DNSCRYPT_QUERY_BOX_OFFSET \
+    (DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_PUBLICKEYBYTES + crypto_box_HALF_NONCEBYTES)
+
+//  8 bytes: magic header (CERT_MAGIC_HEADER)
+// 12 bytes: the client's nonce
+// 12 bytes: server nonce extension
+// 16 bytes: Poly1305 MAC (crypto_box_ZEROBYTES - crypto_box_BOXZEROBYTES)
+
+#define DNSCRYPT_REPLY_BOX_OFFSET \
+    (DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_HALF_NONCEBYTES + crypto_box_HALF_NONCEBYTES)
+
+/**
+ * Decrypt a query using the keypair that was found using dnsc_find_keypair.
+ * The client nonce will be extracted from the encrypted query and stored in
+ * client_nonce, a shared secret will be computed and stored in nmkey and the
+ * buffer will be decrypted inplace.
+ * \param[in] keypair the keypair that matches this encrypted query.
+ * \param[in] client_nonce where the client nonce will be stored.
+ * \param[in] nmkey where the shared secret key will be written.
+ * \param[in] buffer the encrypted buffer.
+ * \return 0 on success.
+ */
+static int
+dnscrypt_server_uncurve(const KeyPair *keypair,
+                        uint8_t client_nonce[crypto_box_HALF_NONCEBYTES],
+                        uint8_t nmkey[crypto_box_BEFORENMBYTES],
+                        struct sldns_buffer* buffer)
+{
+    size_t len = sldns_buffer_limit(buffer);
+    uint8_t *const buf = sldns_buffer_begin(buffer);
+    uint8_t nonce[crypto_box_NONCEBYTES];
+    struct dnscrypt_query_header *query_header;
+
+    if (len <= DNSCRYPT_QUERY_HEADER_SIZE) {
+        return -1;
+    }
+
+    query_header = (struct dnscrypt_query_header *)buf;
+    memcpy(nmkey, query_header->publickey, crypto_box_PUBLICKEYBYTES);
+    if (crypto_box_beforenm(nmkey, nmkey, keypair->crypt_secretkey) != 0) {
+        return -1;
+    }
+
+    memcpy(nonce, query_header->nonce, crypto_box_HALF_NONCEBYTES);
+    memset(nonce + crypto_box_HALF_NONCEBYTES, 0, crypto_box_HALF_NONCEBYTES);
+
+    sldns_buffer_set_at(buffer,
+                        DNSCRYPT_QUERY_BOX_OFFSET - crypto_box_BOXZEROBYTES,
+                        0, crypto_box_BOXZEROBYTES);
+
+    if (crypto_box_open_afternm
+        (buf + DNSCRYPT_QUERY_BOX_OFFSET - crypto_box_BOXZEROBYTES,
+         buf + DNSCRYPT_QUERY_BOX_OFFSET - crypto_box_BOXZEROBYTES,
+         len - DNSCRYPT_QUERY_BOX_OFFSET + crypto_box_BOXZEROBYTES, nonce,
+         nmkey) != 0) {
+        return -1;
+    }
+
+    while (*sldns_buffer_at(buffer, --len) == 0);
+
+    if (*sldns_buffer_at(buffer, len) != 0x80) {
+        return -1;
+    }
+
+    memcpy(client_nonce, nonce, crypto_box_HALF_NONCEBYTES);
+    memmove(sldns_buffer_begin(buffer),
+            sldns_buffer_at(buffer, DNSCRYPT_QUERY_HEADER_SIZE),
+            len - DNSCRYPT_QUERY_HEADER_SIZE);
+
+    sldns_buffer_set_position(buffer, 0);
+    sldns_buffer_set_limit(buffer, len - DNSCRYPT_QUERY_HEADER_SIZE);
+
+    return 0;
+}
+
+
+/**
+ * Add random padding to a buffer, according to a client nonce.
+ * The length has to depend on the query in order to avoid reply attacks.
+ *
+ * @param buf a buffer
+ * @param len the initial size of the buffer
+ * @param max_len the maximum size
+ * @param nonce a nonce, made of the client nonce repeated twice
+ * @param secretkey
+ * @return the new size, after padding
+ */
+size_t
+dnscrypt_pad(uint8_t *buf, const size_t len, const size_t max_len,
+             const uint8_t *nonce, const uint8_t *secretkey)
+{
+    uint8_t *buf_padding_area = buf + len;
+    size_t padded_len;
+    uint32_t rnd;
+
+    // no padding
+    if (max_len < len + DNSCRYPT_MIN_PAD_LEN)
+        return len;
+
+    assert(nonce[crypto_box_HALF_NONCEBYTES] == nonce[0]);
+
+    crypto_stream((unsigned char *)&rnd, (unsigned long long)sizeof(rnd), nonce,
+                  secretkey);
+    padded_len =
+        len + DNSCRYPT_MIN_PAD_LEN + rnd % (max_len - len -
+                                            DNSCRYPT_MIN_PAD_LEN + 1);
+    padded_len += DNSCRYPT_BLOCK_SIZE - padded_len % DNSCRYPT_BLOCK_SIZE;
+    if (padded_len > max_len)
+        padded_len = max_len;
+
+    memset(buf_padding_area, 0, padded_len - len);
+    *buf_padding_area = 0x80;
+
+    return padded_len;
+}
+
+uint64_t
+dnscrypt_hrtime(void)
+{
+    struct timeval tv;
+    uint64_t ts = (uint64_t)0U;
+    int ret;
+
+    ret = evutil_gettimeofday(&tv, NULL);
+    assert(ret == 0);
+    if (ret == 0) {
+        ts = (uint64_t)tv.tv_sec * 1000000U + (uint64_t)tv.tv_usec;
+    }
+    return ts;
+}
+
+/**
+ * Add the server nonce part to once.
+ * The nonce is made half of client nonce and the seconf half of the server
+ * nonce, both of them of size crypto_box_HALF_NONCEBYTES.
+ * \param[in] nonce: a uint8_t* of size crypto_box_NONCEBYTES
+ */
+static void
+add_server_nonce(uint8_t *nonce)
+{
+    uint64_t ts;
+    uint64_t tsn;
+    uint32_t suffix;
+    ts = dnscrypt_hrtime();
+    // TODO? dnscrypt-wrapper does some logic with context->nonce_ts_last
+    // unclear if we really need it, so skipping it for now.
+    tsn = (ts << 10) | (randombytes_random() & 0x3ff);
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+    tsn =
+        (((uint64_t)htonl((uint32_t)tsn)) << 32) | htonl((uint32_t)(tsn >> 32));
+#endif
+    memcpy(nonce + crypto_box_HALF_NONCEBYTES, &tsn, 8);
+    suffix = randombytes_random();
+    memcpy(nonce + crypto_box_HALF_NONCEBYTES + 8, &suffix, 4);
+}
+
+/**
+ * Encrypt a reply using the keypair that was used with the query.
+ * The client nonce will be extracted from the encrypted query and stored in
+ * The buffer will be encrypted inplace.
+ * \param[in] keypair the keypair that matches this encrypted query.
+ * \param[in] client_nonce client nonce used during the query
+ * \param[in] nmkey shared secret key used during the query.
+ * \param[in] buffer the buffer where to encrypt the reply.
+ * \param[in] udp if whether or not it is a UDP query.
+ * \param[in] max_udp_size configured max udp size.
+ * \return 0 on success.
+ */
+static int
+dnscrypt_server_curve(const KeyPair *keypair,
+                      uint8_t client_nonce[crypto_box_HALF_NONCEBYTES],
+                      uint8_t nmkey[crypto_box_BEFORENMBYTES],
+                      struct sldns_buffer* buffer,
+                      uint8_t udp,
+                      size_t max_udp_size)
+{
+    size_t dns_reply_len = sldns_buffer_limit(buffer);
+    size_t max_len = dns_reply_len + DNSCRYPT_MAX_PADDING + DNSCRYPT_REPLY_HEADER_SIZE;
+    size_t max_reply_size = max_udp_size - 20U - 8U;
+    uint8_t nonce[crypto_box_NONCEBYTES];
+    uint8_t *boxed;
+    uint8_t *const buf = sldns_buffer_begin(buffer);
+    size_t len = sldns_buffer_limit(buffer);
+
+    if(udp){
+        if (max_len > max_reply_size)
+            max_len = max_reply_size;
+    }
+
+
+    memcpy(nonce, client_nonce, crypto_box_HALF_NONCEBYTES);
+    memcpy(nonce + crypto_box_HALF_NONCEBYTES, client_nonce,
+           crypto_box_HALF_NONCEBYTES);
+
+    boxed = buf + DNSCRYPT_REPLY_BOX_OFFSET;
+    memmove(boxed + crypto_box_MACBYTES, buf, len);
+    len = dnscrypt_pad(boxed + crypto_box_MACBYTES, len,
+                       max_len - DNSCRYPT_REPLY_HEADER_SIZE, nonce,
+                       keypair->crypt_secretkey);
+    sldns_buffer_set_at(buffer,
+                        DNSCRYPT_REPLY_BOX_OFFSET - crypto_box_BOXZEROBYTES,
+                        0, crypto_box_ZEROBYTES);
+
+    // add server nonce extension
+    add_server_nonce(nonce);
+
+    if (crypto_box_afternm
+        (boxed - crypto_box_BOXZEROBYTES, boxed - crypto_box_BOXZEROBYTES,
+         len + crypto_box_ZEROBYTES, nonce, nmkey) != 0) {
+        return -1;
+    }
+
+    sldns_buffer_write_at(buffer, 0, DNSCRYPT_MAGIC_RESPONSE, DNSCRYPT_MAGIC_HEADER_LEN);
+    sldns_buffer_write_at(buffer, DNSCRYPT_MAGIC_HEADER_LEN, nonce, crypto_box_NONCEBYTES);
+    sldns_buffer_set_limit(buffer, len + DNSCRYPT_REPLY_HEADER_SIZE);
+    return 0;
+}
+
+/**
+ * Read the content of fname into buf.
+ * \param[in] fname name of the file to read.
+ * \param[in] buf the buffer in which to read the content of the file.
+ * \param[in] count number of bytes to read.
+ * \return 0 on success.
+ */
+static int
+dnsc_read_from_file(char *fname, char *buf, size_t count)
+{
+       int fd;
+       fd = open(fname, O_RDONLY);
+       if (fd == -1) {
+               return -1;
+       }
+       if (read(fd, buf, count) != (ssize_t)count) {
+               close(fd);
+               return -2;
+       }
+       close(fd);
+       return 0;
+}
+
+/**
+ * Parse certificates files provided by the configuration and load them into
+ * dnsc_env.
+ * \param[in] env the dnsc_env structure to load the certs into.
+ * \param[in] cfg the configuration.
+ * \return the number of certificates loaded.
+ */
+static int
+dnsc_parse_certs(struct dnsc_env *env, struct config_file *cfg)
+{
+       struct config_strlist *head;
+       size_t signed_cert_id;
+
+       env->signed_certs_count = 0U;
+       for (head = cfg->dnscrypt_provider_cert; head; head = head->next) {
+               env->signed_certs_count++;
+       }
+       env->signed_certs = sodium_allocarray(env->signed_certs_count,
+                                                                                 sizeof *env->signed_certs);
+
+       signed_cert_id = 0U;
+       for(head = cfg->dnscrypt_provider_cert; head; head = head->next, signed_cert_id++) {
+               if(dnsc_read_from_file(
+                               head->str,
+                               (char *)(env->signed_certs + signed_cert_id),
+                               sizeof(struct SignedCert)) != 0) {
+                       fatal_exit("dnsc_parse_certs: failed to load %s", head->str);
+               }
+               verbose(VERB_OPS, "Loaded cert %s", head->str);
+       }
+       return signed_cert_id;
+}
+
+/**
+ * Helper function to convert a binary key into a printable fingerprint.
+ * \param[in] fingerprint the buffer in which to write the printable key.
+ * \param[in] key the key to convert.
+ */
+void
+dnsc_key_to_fingerprint(char fingerprint[80U], const uint8_t * const key)
+{
+    const size_t fingerprint_size = 80U;
+    size_t       fingerprint_pos = (size_t) 0U;
+    size_t       key_pos = (size_t) 0U;
+
+    for (;;) {
+        assert(fingerprint_size > fingerprint_pos);
+        snprintf(&fingerprint[fingerprint_pos],
+                        fingerprint_size - fingerprint_pos, "%02X%02X",
+                        key[key_pos], key[key_pos + 1U]);
+        key_pos += 2U;
+        if (key_pos >= crypto_box_PUBLICKEYBYTES) {
+            break;
+        }
+        fingerprint[fingerprint_pos + 4U] = ':';
+        fingerprint_pos += 5U;
+    }
+}
+
+/**
+ * Find the keypair matching a DNSCrypt query.
+ * \param[in] dnscenv The DNSCrypt enviroment, which contains the list of keys
+ * supported by the server.
+ * \param[in] buffer The encrypted DNS query.
+ * \return a KeyPair * if we found a key pair matching the query, NULL otherwise.
+ */
+static const KeyPair *
+dnsc_find_keypair(struct dnsc_env* dnscenv, struct sldns_buffer* buffer)
+{
+       const KeyPair *keypairs = dnscenv->keypairs;
+       struct dnscrypt_query_header *dnscrypt_header;
+
+       if (sldns_buffer_limit(buffer) < DNSCRYPT_QUERY_HEADER_SIZE) {
+               return NULL;
+       }
+       dnscrypt_header = (struct dnscrypt_query_header *)sldns_buffer_begin(buffer);
+       size_t i;
+       for (i = 0U; i < dnscenv->keypairs_count; i++) {
+               if (memcmp(keypairs[i].crypt_publickey, dnscrypt_header->magic_query,
+                   DNSCRYPT_MAGIC_HEADER_LEN) == 0) {
+                       return &keypairs[i];
+               }
+       }
+       return NULL;
+}
+
+/**
+ * Insert local-zone and local-data into configuration.
+ * In order to be able to serve certs over TXT, we can reuse the local-zone and
+ * local-data config option. The zone and qname are infered from the
+ * provider_name and the content of the TXT record from the certificate content.
+ * returns the number of certtificate TXT record that were loaded.
+ * < 0 in case of error.
+ */
+static int
+dnsc_load_local_data(struct dnsc_env* dnscenv, struct config_file *cfg)
+{
+    int i, j;
+       // Insert 'local-zone: "2.dnscrypt-cert.example.com" deny'
+    if(!cfg_str2list_insert(&cfg->local_zones,
+                            strdup(dnscenv->provider_name),
+                            strdup("deny"))) {
+        log_err("Could not load dnscrypt local-zone: %s deny",
+                dnscenv->provider_name);
+        return -1;
+    }
+
+    // Add local data entry of type:
+    // 2.dnscrypt-cert.example.com 86400 IN TXT "DNSC......"
+    for(i=0; i<dnscenv->signed_certs_count; i++) {
+        const char *ttl_class_type = " 86400 IN TXT \"";
+        struct SignedCert *cert = dnscenv->signed_certs + i;
+        uint16_t rrlen = strlen(dnscenv->provider_name) +
+                         strlen(ttl_class_type) +
+                         4 * sizeof(struct SignedCert) + // worst case scenario
+                         1 + // trailing double quote
+                         1;
+        char *rr = malloc(rrlen);
+        if(!rr) {
+            log_err("Could not allocate memory");
+            return -2;
+        }
+        int c;
+        snprintf(rr, rrlen - 1, "%s 86400 IN TXT \"", dnscenv->provider_name);
+        for(j=0; j<sizeof(struct SignedCert); j++) {
+            c = (int)*((const uint8_t *) cert + j);
+            if (isprint(c) && c != '"' && c != '\\') {
+                snprintf(rr + strlen(rr), rrlen - 1 - strlen(rr), "%c", c);
+            } else {
+                snprintf(rr + strlen(rr), rrlen - 1 - strlen(rr), "\\%03d", c);
+            }
+        }
+        snprintf(rr + strlen(rr), rrlen - 1 - strlen(rr), "\"");
+        cfg_strlist_insert(&cfg->local_data, strdup(rr));
+        free(rr);
+    }
+    return dnscenv->signed_certs_count;
+}
+
+/**
+ * Parse the secret key files from `dnscrypt-secret-key` config and populates
+ * a list of secret/public keys supported by dnscrypt listener.
+ * \param[in] env The dnsc_env structure which will hold the keypairs.
+ * \param[in] cfg The config with the secret key file paths.
+ */
+static int
+dnsc_parse_keys(struct dnsc_env *env, struct config_file *cfg)
+{
+       struct config_strlist *head;
+       size_t keypair_id;
+
+       env->keypairs_count = 0U;
+       for (head = cfg->dnscrypt_secret_key; head; head = head->next) {
+               env->keypairs_count++;
+       }
+       env->keypairs = sodium_allocarray(env->keypairs_count,
+                                                                                 sizeof *env->keypairs);
+
+       keypair_id = 0U;
+       for(head = cfg->dnscrypt_secret_key; head; head = head->next, keypair_id++) {
+               char fingerprint[80];
+               if(dnsc_read_from_file(
+                               head->str,
+                               (char *)(env->keypairs[keypair_id].crypt_secretkey),
+                               crypto_box_SECRETKEYBYTES) != 0) {
+                       fatal_exit("dnsc_parse_keys: failed to load %s", head->str);
+               }
+               verbose(VERB_OPS, "Loaded key %s", head->str);
+               if (crypto_scalarmult_base(env->keypairs[keypair_id].crypt_publickey,
+                                                                  env->keypairs[keypair_id].crypt_secretkey) != 0) {
+                       fatal_exit("dnsc_parse_keys: could not generate public key from %s", head->str);
+               }
+               dnsc_key_to_fingerprint(fingerprint, env->keypairs[keypair_id].crypt_publickey);
+               verbose(VERB_OPS, "Crypt public key fingerprint for %s: %s", head->str, fingerprint);
+       }
+       return keypair_id;
+}
+
+
+/**
+ * #########################################################
+ * ############# Publicly accessible functions #############
+ * #########################################################
+ */
+
+int
+dnsc_handle_curved_request(struct dnsc_env* dnscenv,
+                           struct comm_reply* repinfo)
+{
+    struct comm_point* c = repinfo->c;
+
+    repinfo->is_dnscrypted = 0;
+    if( !c->dnscrypt ) {
+        return 1;
+    }
+    // Attempt to decrypt the query. If it is not crypted, we may still need
+    // to serve the certificate.
+    verbose(VERB_ALGO, "handle request called on DNSCrypt socket");
+    if ((repinfo->keypair = dnsc_find_keypair(dnscenv, c->buffer)) != NULL) {
+        if(dnscrypt_server_uncurve(repinfo->keypair,
+                                   repinfo->client_nonce,
+                                   repinfo->nmkey,
+                                   c->buffer) != 0){
+            // TODO: Bump counter!
+            verbose(VERB_ALGO, "dnscrypt: Failed to uncurve");
+            comm_point_drop_reply(repinfo);
+            return 0;
+        }
+        repinfo->is_dnscrypted = 1;
+        sldns_buffer_rewind(c->buffer);
+    }
+    return 1;
+}
+
+int
+dnsc_handle_uncurved_request(struct comm_reply *repinfo)
+{
+    if(!repinfo->c->dnscrypt) {
+        return 1;
+    }
+    sldns_buffer_copy(repinfo->c->dnscrypt_buffer, repinfo->c->buffer);
+    if(!repinfo->is_dnscrypted) {
+        return 1;
+    }
+       if(dnscrypt_server_curve(repinfo->keypair,
+                             repinfo->client_nonce,
+                             repinfo->nmkey,
+                             repinfo->c->dnscrypt_buffer,
+                             repinfo->c->type == comm_udp,
+                             repinfo->max_udp_size) != 0){
+               verbose(VERB_ALGO, "dnscrypt: Failed to curve cached missed answer");
+               comm_point_drop_reply(repinfo);
+               return 0;
+       }
+    return 1;
+}
+
+struct dnsc_env *
+dnsc_create(void)
+{
+       struct dnsc_env *env;
+       if (sodium_init() == -1) {
+               fatal_exit("dnsc_create: could not initialize libsodium.");
+       }
+       env = (struct dnsc_env *) calloc(1, sizeof(struct dnsc_env));
+       return env;
+}
+
+int
+dnsc_apply_cfg(struct dnsc_env *env, struct config_file *cfg)
+{
+       if(dnsc_parse_certs(env, cfg) <= 0) {
+               fatal_exit("dnsc_apply_cfg: no cert file loaded");
+       }
+       if(dnsc_parse_keys(env, cfg) <= 0) {
+               fatal_exit("dnsc_apply_cfg: no key file loaded");
+       }
+       randombytes_buf(env->hash_key, sizeof env->hash_key);
+       env->provider_name = cfg->dnscrypt_provider;
+
+       if(dnsc_load_local_data(env, cfg) <= 0) {
+               fatal_exit("dnsc_apply_cfg: could not load local data");
+       }
+       return 0;
+}
diff --git a/dnscrypt/dnscrypt.h b/dnscrypt/dnscrypt.h
new file mode 100644 (file)
index 0000000..aa1d9f2
--- /dev/null
@@ -0,0 +1,93 @@
+#ifndef UNBOUND_DNSCRYPT_H
+#define UNBOUND_DNSCRYPT_H
+
+#define DNSCRYPT_MAGIC_HEADER_LEN 8U
+#define DNSCRYPT_MAGIC_RESPONSE  "r6fnvWj8"
+
+#ifndef DNSCRYPT_MAX_PADDING
+# define DNSCRYPT_MAX_PADDING 256U
+#endif
+#ifndef DNSCRYPT_BLOCK_SIZE
+# define DNSCRYPT_BLOCK_SIZE 64U
+#endif
+#ifndef DNSCRYPT_MIN_PAD_LEN
+# define DNSCRYPT_MIN_PAD_LEN 8U
+#endif
+
+#define crypto_box_HALF_NONCEBYTES (crypto_box_NONCEBYTES / 2U)
+
+#include "config.h"
+#include "dnscrypt/cert.h"
+
+#define DNSCRYPT_QUERY_HEADER_SIZE \
+    (DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_PUBLICKEYBYTES + crypto_box_HALF_NONCEBYTES + crypto_box_MACBYTES)
+#define DNSCRYPT_RESPONSE_HEADER_SIZE \
+    (DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_NONCEBYTES + crypto_box_MACBYTES)
+
+#define DNSCRYPT_REPLY_HEADER_SIZE \
+    (DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_HALF_NONCEBYTES * 2 + crypto_box_MACBYTES)
+
+struct sldns_buffer;
+struct config_file;
+struct comm_reply;
+
+typedef struct KeyPair_ {
+    uint8_t crypt_publickey[crypto_box_PUBLICKEYBYTES];
+    uint8_t crypt_secretkey[crypto_box_SECRETKEYBYTES];
+} KeyPair;
+
+struct dnsc_env {
+       struct SignedCert *signed_certs;
+       size_t signed_certs_count;
+       uint8_t provider_publickey[crypto_sign_ed25519_PUBLICKEYBYTES];
+       uint8_t provider_secretkey[crypto_sign_ed25519_SECRETKEYBYTES];
+       KeyPair *keypairs;
+       size_t keypairs_count;
+       uint64_t nonce_ts_last;
+       unsigned char hash_key[crypto_shorthash_KEYBYTES];
+       char * provider_name;
+};
+
+struct dnscrypt_query_header {
+    uint8_t magic_query[DNSCRYPT_MAGIC_HEADER_LEN];
+    uint8_t publickey[crypto_box_PUBLICKEYBYTES];
+    uint8_t nonce[crypto_box_HALF_NONCEBYTES];
+    uint8_t mac[crypto_box_MACBYTES];
+};
+
+/**
+ * Initialize DNSCrypt enviroment.
+ * Initialize sodium library and allocate the dnsc_env structure.
+ * \return an unitialized struct dnsc_env.
+ */
+struct dnsc_env * dnsc_create(void);
+
+/**
+ * Apply configuration.
+ * Read certificates and secret keys from configuration. Initialize hashkey and
+ * provider name as well as loading cert TXT records.
+ * In case of issue applying configuration, this function fatals.
+ * \param[in] env the struct dnsc_env to populate.
+ * \param[in] cfg the config_file struct with dnscrypt options.
+ * \return 0 on success.
+ */
+int dnsc_apply_cfg(struct dnsc_env *env, struct config_file *cfg);
+
+/**
+ * handle a crypted dnscrypt request.
+ * Determine wether or not a query is coming over the dnscrypt listener and
+ * attempt to uncurve it or detect if it is a certificate query.
+ * return 0 in case of failure.
+ */
+int dnsc_handle_curved_request(struct dnsc_env* dnscenv,
+                               struct comm_reply* repinfo);
+/**
+ * handle an unencrypted dnscrypt request.
+ * Determine wether or not a query is going over the dnscrypt channel and
+ * attempt to curve it unless it was not crypted like when  it is a
+ * certificate query.
+ * \return 0 in case of failure.
+ */
+
+int dnsc_handle_uncurved_request(struct comm_reply *repinfo);
+#endif
diff --git a/dnscrypt/dnscrypt.m4 b/dnscrypt/dnscrypt.m4
new file mode 100644 (file)
index 0000000..077d282
--- /dev/null
@@ -0,0 +1,25 @@
+# dnscrypt.m4
+
+# dnsc_DNSCRYPT([action-if-true], [action-if-false])
+# --------------------------------------------------------------------------
+# Check for required dnscrypt libraries and add dnscrypt configure args.
+AC_DEFUN([dnsc_DNSCRYPT],
+[
+  AC_ARG_ENABLE([dnscrypt],
+    AS_HELP_STRING([--enable-dnscrypt],
+                   [Enable dnscrypt support (requires libsodium)]),
+    [opt_dnscrypt=$enableval], [opt_dnscrypt=no])
+
+  if test "x$opt_dnscrypt" != "xno"; then
+    AC_ARG_WITH([libsodium], AC_HELP_STRING([--with-libsodium=path],
+       [Path where libsodium is installed, for dnscrypt]), [
+       CFLAGS="$CFLAGS -I$withval/include"
+       LDFLAGS="$LDFLAGS -L$withval/lib"
+    ])
+    AC_SEARCH_LIBS([sodium_init], [sodium], [],
+      AC_MSG_ERROR([The sodium library was not found. Please install sodium!]))
+    $1
+  else
+    $2
+  fi
+])
diff --git a/dnscrypt/dnscrypt_config.h.in b/dnscrypt/dnscrypt_config.h.in
new file mode 100644 (file)
index 0000000..d9f38a8
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef UNBOUND_DNSCRYPT_CONFIG_H
+#define UNBOUND_DNSCRYPT_CONFIG_H
+
+/*
+ * Process this file (dnscrypt_config.h.in) with AC_CONFIG_FILES to generate
+ * dnscrypt_config.h.
+ *
+ * This file exists so that USE_DNSCRYPT can be used without including config.h.
+ */
+
+#if @ENABLE_DNSCRYPT@ /* ENABLE_DNSCRYPT */
+# ifndef USE_DNSCRYPT
+#  define USE_DNSCRYPT 1
+# endif
+#endif
+
+#endif /* UNBOUND_DNSCRYPT_CONFIG_H */
index fdb376c909c9f5e65cbde642b7cd29b308be8bc9..4b6975d14d338032b1e57e5e600fe3dc3aec3ac7 100644 (file)
@@ -1,3 +1,7 @@
+20 March 2017: Wouter
+       - #1217. DNSCrypt support, with --enable-dnscrypt, libsodium and then
+         enabled in the config file from Manu Bretelle.
+
 17 March 2017: Wouter
        - Patch for view functionality for local-data-ptr from Björn Ketelaars.
        - Fix #1237 - Wrong resolving in chain, for norec queries that get
index e43713a583b4cf540e01d400a96af4aed43ac3bd..26a4542d84f2576d39c87fc7ee90ec708792f434 100644 (file)
@@ -787,3 +787,23 @@ remote-control:
 # view:
 #      name: "anotherview"
 #      local-zone: "example.com" refuse
+
+# DNSCrypt
+# Caveats:
+# 1. the keys/certs cannot be produced by unbound. You can use dnscrypt-wrapper
+#   for this: https://github.com/cofyc/dnscrypt-wrapper/blob/master/README.md#usage
+# 2. dnscrypt channel attaches to an interface. you MUST set interfaces to
+#   listen on `dnscrypt-port` with the follo0wing snippet:
+# server:
+#     interface: 0.0.0.0@443
+#     interface: ::0@443
+#
+# Finally, `dnscrypt` config has its own section.
+# dnscrypt:
+#     dnscrypt-enable: yes
+#     dnscrypt-port: 443
+#     dnscrypt-provider: 2.dnscrypt-cert.example.com.
+#     dnscrypt-secret-key: /path/unbound-conf/keys1/1.key
+#     dnscrypt-secret-key: /path/unbound-conf/keys2/1.key
+#     dnscrypt-provider-cert: /path/unbound-conf/keys1/1.cert
+#     dnscrypt-provider-cert: /path/unbound-conf/keys2/1.cert
index 10fcf4a8f0f613f237022766f2d6a5aa22d54ffd..772032cd01b82914ab084dd5bad81a5bdba16609 100644 (file)
@@ -1445,6 +1445,37 @@ It must be /96 or shorter.  The default prefix is 64:ff9b::/96.
 .B dns64\-synthall: \fI<yes or no>\fR
 Debug option, default no.  If enabled, synthesize all AAAA records
 despite the presence of actual AAAA records.
+.SS "DNSCrypt Options"
+.LP
+The
+.B dnscrypt:
+clause give the settings of the dnscrypt channel. While those options are
+available, they are only meaningful if unbound was compiled with
+\fB\-\-enable\-dnscrypt\fR.
+Currently certificate and secret/public keys cannot be generated by unbound.
+You can use dnscrypt-wrapper to generate those: https://github.com/cofyc/dnscrypt-wrapper/blob/master/README.md#usage
+.TP
+.B dnscrypt\-enable: \fI<yes or no>\fR
+Whether or not the \fBdnscrypt\fR config should be enabled. You may define
+configuration but not activate it.
+The default is no.
+.TP
+.B dnscrypt\-port: \fI<port number>
+On which port should \fBdnscrypt\fR should be activated. Note that you should
+have a matching \fBinterface\fR option defined in the \fBserver\fR section for
+this port.
+.TP
+.B dnscrypt\-provider: \fI<provider name>\fR
+The provider name to use to distribute certificates. This is of the form:
+\fB2.dnscrypt-cert.example.com.\fR. The name \fIMUST\fR end with a dot.
+.TP
+.B dnscrypt\-secret\-key: \fI<path to secret key file>\fR
+Path to the time limited secret key file. This option may be specified multiple
+times.
+.TP
+.B dnscrypt\-provider\-cert: \fI<path to cert file>\fR
+Path to the certificate related to the \fBdnscrypt\-secret\-key\fRs. This option
+may be specified multiple times.
 .SH "MEMORY CONTROL EXAMPLE"
 In the example config settings below memory usage is reduced. Some service
 levels are lower, notable very large data and a high TCP load are no longer
index 0132ce45f7818e92cc1044c83057381f165d5833..a9afaa20da5c453f5fcfc175eef94f4793278a3d 100644 (file)
@@ -1056,15 +1056,24 @@ set_recvpktinfo(int s, int family)
  * @param tcp_mss: maximum segment size of tcp socket. default if zero.
  * @param freebind: set IP_FREEBIND socket option.
  * @param use_systemd: if true, fetch sockets from systemd.
+ * @param dnscrypt_port: dnscrypt service port number
  * @return: returns false on error.
  */
 static int
 ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp, 
        struct addrinfo *hints, const char* port, struct listen_port** list,
        size_t rcv, size_t snd, int ssl_port, int* reuseport, int transparent,
-       int tcp_mss, int freebind, int use_systemd)
+       int tcp_mss, int freebind, int use_systemd, int dnscrypt_port)
 {
        int s, noip6=0;
+#ifdef USE_DNSCRYPT
+       int is_dnscrypt = ((strchr(ifname, '@') && 
+                       atoi(strchr(ifname, '@')+1) == dnscrypt_port) ||
+                       (!strchr(ifname, '@') && atoi(port) == dnscrypt_port));
+#else
+       int is_dnscrypt = 0;
+#endif
+
        if(!do_udp && !do_tcp)
                return 0;
        if(do_auto) {
@@ -1086,7 +1095,8 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
 #endif
                        return 0;
                }
-               if(!port_insert(list, s, listen_type_udpancil)) {
+               if(!port_insert(list, s,
+                  is_dnscrypt?listen_type_udpancil_dnscrypt:listen_type_udpancil)) {
 #ifndef USE_WINSOCK
                        close(s);
 #else
@@ -1105,7 +1115,8 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
                        }
                        return 0;
                }
-               if(!port_insert(list, s, listen_type_udp)) {
+               if(!port_insert(list, s,
+                  is_dnscrypt?listen_type_udp_dnscrypt:listen_type_udp)) {
 #ifndef USE_WINSOCK
                        close(s);
 #else
@@ -1130,7 +1141,7 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
                if(is_ssl)
                        verbose(VERB_ALGO, "setup TCP for SSL service");
                if(!port_insert(list, s, is_ssl?listen_type_ssl:
-                       listen_type_tcp)) {
+                       (is_dnscrypt?listen_type_tcp_dnscrypt:listen_type_tcp))) {
 #ifndef USE_WINSOCK
                        close(s);
 #else
@@ -1172,6 +1183,9 @@ listen_create(struct comm_base* base, struct listen_port* ports,
                return NULL;
        front->cps = NULL;
        front->udp_buff = sldns_buffer_new(bufsize);
+#ifdef USE_DNSCRYPT
+    front->dnscrypt_udp_buff = NULL;
+#endif
        if(!front->udp_buff) {
                free(front);
                return NULL;
@@ -1180,17 +1194,20 @@ listen_create(struct comm_base* base, struct listen_port* ports,
        /* create comm points as needed */
        while(ports) {
                struct comm_point* cp = NULL;
-               if(ports->ftype == listen_type_udp) 
+               if(ports->ftype == listen_type_udp ||
+                  ports->ftype == listen_type_udp_dnscrypt)
                        cp = comm_point_create_udp(base, ports->fd, 
                                front->udp_buff, cb, cb_arg);
-               else if(ports->ftype == listen_type_tcp)
+               else if(ports->ftype == listen_type_tcp ||
+                               ports->ftype == listen_type_tcp_dnscrypt)
                        cp = comm_point_create_tcp(base, ports->fd, 
                                tcp_accept_count, bufsize, cb, cb_arg);
                else if(ports->ftype == listen_type_ssl) {
                        cp = comm_point_create_tcp(base, ports->fd, 
                                tcp_accept_count, bufsize, cb, cb_arg);
                        cp->ssl = sslctx;
-               } else if(ports->ftype == listen_type_udpancil) 
+               } else if(ports->ftype == listen_type_udpancil ||
+                                 ports->ftype == listen_type_udpancil_dnscrypt)
                        cp = comm_point_create_udp_ancil(base, ports->fd, 
                                front->udp_buff, cb, cb_arg);
                if(!cp) {
@@ -1200,6 +1217,21 @@ listen_create(struct comm_base* base, struct listen_port* ports,
                }
                cp->dtenv = dtenv;
                cp->do_not_close = 1;
+#ifdef USE_DNSCRYPT
+               if (ports->ftype == listen_type_udp_dnscrypt ||
+                       ports->ftype == listen_type_tcp_dnscrypt ||
+                       ports->ftype == listen_type_udpancil_dnscrypt) {
+                       cp->dnscrypt = 1;
+            cp->dnscrypt_buffer = sldns_buffer_new(bufsize);
+            if(!cp->dnscrypt_buffer) {
+                log_err("can't alloc dnscrypt_buffer");
+                comm_point_delete(cp);
+                listen_delete(front);
+                return NULL;
+            }
+            front->dnscrypt_udp_buff = cp->dnscrypt_buffer;
+        }
+#endif
                if(!listen_cp_insert(cp, front)) {
                        log_err("malloc failed");
                        comm_point_delete(cp);
@@ -1235,6 +1267,12 @@ listen_delete(struct listen_dnsport* front)
        if(!front) 
                return;
        listen_list_delete(front->cps);
+#ifdef USE_DNSCRYPT
+    if(front->dnscrypt_udp_buff &&
+       front->udp_buff != front->dnscrypt_udp_buff) {
+        sldns_buffer_free(front->dnscrypt_udp_buff);
+    }
+#endif
        sldns_buffer_free(front->udp_buff);
        free(front);
 }
@@ -1278,7 +1316,8 @@ listening_ports_open(struct config_file* cfg, int* reuseport)
                                cfg->so_rcvbuf, cfg->so_sndbuf,
                                cfg->ssl_port, reuseport,
                                cfg->ip_transparent,
-                               cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd)) {
+                               cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd,
+                               cfg->dnscrypt_port)) {
                                listening_ports_free(list);
                                return NULL;
                        }
@@ -1291,7 +1330,8 @@ listening_ports_open(struct config_file* cfg, int* reuseport)
                                cfg->so_rcvbuf, cfg->so_sndbuf,
                                cfg->ssl_port, reuseport,
                                cfg->ip_transparent,
-                               cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd)) {
+                               cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd,
+                               cfg->dnscrypt_port)) {
                                listening_ports_free(list);
                                return NULL;
                        }
@@ -1306,7 +1346,8 @@ listening_ports_open(struct config_file* cfg, int* reuseport)
                                cfg->so_rcvbuf, cfg->so_sndbuf,
                                cfg->ssl_port, reuseport,
                                cfg->ip_transparent,
-                               cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd)) {
+                               cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd,
+                               cfg->dnscrypt_port)) {
                                listening_ports_free(list);
                                return NULL;
                        }
@@ -1319,7 +1360,8 @@ listening_ports_open(struct config_file* cfg, int* reuseport)
                                cfg->so_rcvbuf, cfg->so_sndbuf,
                                cfg->ssl_port, reuseport,
                                cfg->ip_transparent,
-                               cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd)) {
+                               cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd,
+                               cfg->dnscrypt_port)) {
                                listening_ports_free(list);
                                return NULL;
                        }
@@ -1350,6 +1392,12 @@ size_t listen_get_mem(struct listen_dnsport* listen)
        size_t s = sizeof(*listen) + sizeof(*listen->base) + 
                sizeof(*listen->udp_buff) + 
                sldns_buffer_capacity(listen->udp_buff);
+#ifdef USE_DNSCRYPT
+    s += sizeof(*listen->dnscrypt_udp_buff);
+    if(listen->udp_buff != listen->dnscrypt_udp_buff){
+        s += sldns_buffer_capacity(listen->dnscrypt_udp_buff);
+    }
+#endif
        struct listen_list* p;
        for(p = listen->cps; p; p = p->next) {
                s += sizeof(*p);
index 93d2ef7148e2c3a36be7d36bbe0ce8037628a7b8..fac0f79709245cab5b46cf07617de9a0b2db9d1f 100644 (file)
@@ -59,7 +59,9 @@ struct listen_dnsport {
        /** buffer shared by UDP connections, since there is only one
            datagram at any time. */
        struct sldns_buffer* udp_buff;
-
+#ifdef USE_DNSCRYPT
+       struct sldns_buffer* dnscrypt_udp_buff;
+#endif
        /** list of comm points used to get incoming events */
        struct listen_list* cps;
 };
@@ -85,7 +87,14 @@ enum listen_type {
        /** udp ipv6 (v4mapped) for use with ancillary data */
        listen_type_udpancil,
        /** ssl over tcp type */
-       listen_type_ssl
+       listen_type_ssl,
+       /** udp type  + dnscrypt*/
+       listen_type_udp_dnscrypt,
+       /** tcp type + dnscrypt */
+       listen_type_tcp_dnscrypt,
+       /** udp ipv6 (v4mapped) for use with ancillary data + dnscrypt*/
+       listen_type_udpancil_dnscrypt
+
 };
 
 /**
index c3b95e13dc8adeea8f5c4cdeb5777c54cd74c684..d1aadf8a198c67c6556e2feb92cd1af54c53221b 100644 (file)
@@ -469,6 +469,30 @@ sldns_buffer_write_at(sldns_buffer *buffer, size_t at, const void *data, size_t
        memcpy(buffer->_data + at, data, count);
 }
 
+/**
+ * set the given byte to the buffer at the specified position
+ * \param[in] buffer the buffer
+ * \param[in] at the position (in number of bytes) to write the data at
+ * \param[in] c the byte to set to the buffer
+ * \param[in] count the number of bytes of bytes to write
+ */
+
+INLINE void
+sldns_buffer_set_at(sldns_buffer *buffer, size_t at, int c, size_t count)
+{
+    if (!buffer->_vfixed)
+        assert(sldns_buffer_available_at(buffer, at, count));
+    else if (sldns_buffer_remaining_at(buffer, at) == 0)
+        return;
+    else if (count > sldns_buffer_remaining_at(buffer, at)) {
+        memset(buffer->_data + at, c,
+            sldns_buffer_remaining_at(buffer, at));
+        return;
+    }
+       memset(buffer->_data + at, c, count);
+}
+
+
 /**
  * writes count bytes of data to the current position of the buffer
  * \param[in] buffer the buffer
index 6ea12cd2f336744b428c126aabb24ff751d3a226..e356d4fc312ca31e1eb48a79b9d2bb396cb25b52 100755 (executable)
@@ -9,6 +9,7 @@ NEED_CURL='06-ianaports.tpkg root_anchor.tpkg'
 NEED_WHOAMI='07-confroot.tpkg'
 NEED_IPV6='fwd_ancil.tpkg fwd_tcp_tc6.tpkg stub_udp6.tpkg edns_cache.tpkg'
 NEED_NOMINGW='tcp_sigpipe.tpkg 07-confroot.tpkg 08-host-lib.tpkg fwd_ancil.tpkg'
+NEED_DNSCRYPT_PROXY='dnscrypt_queries.tpkg'
 
 # test if dig and ldns-testns are available.
 test_tool_avail "dig"
@@ -39,6 +40,7 @@ for test in `ls *.tpkg`; do
        skip_if_in_list $test "$NEED_XXD" "xxd"
        skip_if_in_list $test "$NEED_NC" "nc"
        skip_if_in_list $test "$NEED_WHOAMI" "whoami"
+       skip_if_in_list $test "$NEED_DNSCRYPT_PROXY" "dnscrypt-proxy"
 
        if echo $NEED_IPV6 | grep $test >/dev/null; then
                if test "$HAVE_IPV6" = no; then
index ad957ed7e5cbf87a5fcb331c13f1cc6d8b7d6f44..416f4bd88129a9cac571c31bff8e62858c5412b7 100644 (file)
@@ -260,6 +260,11 @@ config_create(void)
        cfg->qname_minimisation_strict = 0;
        cfg->shm_enable = 0;
        cfg->shm_key = 11777;
+    cfg->dnscrypt = 0;
+       cfg->dnscrypt_port = 0;
+       cfg->dnscrypt_provider = NULL;
+       cfg->dnscrypt_provider_cert = NULL;
+       cfg->dnscrypt_secret_key = NULL;
        return cfg;
 error_exit:
        config_delete(cfg); 
@@ -944,6 +949,8 @@ config_read(struct config_file* cfg, const char* filename, const char* chroot)
        ub_c_parse();
        fclose(in);
 
+    if(!cfg->dnscrypt) cfg->dnscrypt_port = 0;
+
        if(cfg_parser->errors != 0) {
                fprintf(stderr, "read %s failed: %d errors in configuration file\n",
                        fname, cfg_parser->errors);
index 2cb9a854f347ae4784b736c3b10c88ea1503d6bb..18919a65da006bd81b94feadd1db2d2ee3856c1d 100644 (file)
@@ -434,6 +434,18 @@ struct config_file {
        int shm_enable;
        /* SHM data - key for the shm */
        int shm_key;
+
+       /** DNSCrypt */
+       /** true to enable dnscrypt */
+       int dnscrypt;
+       /** port on which to provide dnscrypt service */
+       int dnscrypt_port;
+       /** provider name 2.dnscrypt-cert.example.com */
+       char* dnscrypt_provider;
+       /** dnscrypt secret keys 1.key */
+       struct config_strlist* dnscrypt_secret_key;
+       /** dnscrypt provider certs 1.cert */
+       struct config_strlist* dnscrypt_provider_cert;
 };
 
 /** from cfg username, after daemonise setup performed */
index bd157229346c3a971ca7f1390fa675eea83c6d4b..1c59d535a01da1b112da59e20dc596aca70943a8 100644 (file)
@@ -404,6 +404,12 @@ ratelimit-factor{COLON}            { YDVAR(1, VAR_RATELIMIT_FACTOR) }
 response-ip-tag{COLON}         { YDVAR(2, VAR_RESPONSE_IP_TAG) }
 response-ip{COLON}             { YDVAR(2, VAR_RESPONSE_IP) }
 response-ip-data{COLON}                { YDVAR(2, VAR_RESPONSE_IP_DATA) }
+dnscrypt{COLON}                        { YDVAR(0, VAR_DNSCRYPT) }
+dnscrypt-enable{COLON}         { YDVAR(1, VAR_DNSCRYPT_ENABLE) }
+dnscrypt-port{COLON}           { YDVAR(1, VAR_DNSCRYPT_PORT) }
+dnscrypt-provider{COLON}       { YDVAR(1, VAR_DNSCRYPT_PROVIDER) }
+dnscrypt-secret-key{COLON}     { YDVAR(1, VAR_DNSCRYPT_SECRET_KEY) }
+dnscrypt-provider-cert{COLON}  { YDVAR(1, VAR_DNSCRYPT_PROVIDER_CERT) }
 <INITIAL,val>{NEWLINE}         { LEXOUT(("NL\n")); cfg_parser->line++; }
 
        /* Quoted strings. Strip leading and ending quotes */
index 25f6b9ac5497866c870d3e31385f5bd76cdb39e6..b2ecc0edc9470ead732ebb2ed6990cefc53cb411 100644 (file)
@@ -139,13 +139,16 @@ extern struct config_parser_state* cfg_parser;
 %token VAR_VIEW_FIRST VAR_SERVE_EXPIRED VAR_FAKE_DSA VAR_FAKE_SHA1
 %token VAR_LOG_IDENTITY VAR_HIDE_TRUSTANCHOR
 %token VAR_USE_SYSTEMD VAR_SHM_ENABLE VAR_SHM_KEY
+%token VAR_DNSCRYPT VAR_DNSCRYPT_ENABLE VAR_DNSCRYPT_PORT VAR_DNSCRYPT_PROVIDER
+%token VAR_DNSCRYPT_SECRET_KEY VAR_DNSCRYPT_PROVIDER_CERT
 
 %%
 toplevelvars: /* empty */ | toplevelvars toplevelvar ;
 toplevelvar: serverstart contents_server | stubstart contents_stub |
        forwardstart contents_forward | pythonstart contents_py | 
        rcstart contents_rc | dtstart contents_dt | viewstart 
-       contents_view
+       contents_view |
+       dnscstart contents_dnsc
        ;
 
 /* server: declaration */
@@ -2128,6 +2131,58 @@ server_response_ip_data: VAR_RESPONSE_IP_DATA STRING_ARG STRING_ARG
                                fatal_exit("out of memory adding response-ip-data");
        }
        ;
+dnscstart: VAR_DNSCRYPT
+       {
+               OUTYY(("\nP(dnscrypt:)\n"));
+               OUTYY(("\nP(dnscrypt:)\n"));
+       }
+       ;
+contents_dnsc: contents_dnsc content_dnsc
+       | ;
+content_dnsc:
+       dnsc_dnscrypt_enable | dnsc_dnscrypt_port | dnsc_dnscrypt_provider |
+       dnsc_dnscrypt_secret_key | dnsc_dnscrypt_provider_cert
+       ;
+dnsc_dnscrypt_enable: VAR_DNSCRYPT_ENABLE STRING_ARG
+       {
+               OUTYY(("P(dnsc_dnscrypt_enable:%s)\n", $2));
+               if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+                       yyerror("expected yes or no.");
+               else cfg_parser->cfg->dnscrypt = (strcmp($2, "yes")==0);
+       }
+       ;
+
+dnsc_dnscrypt_port: VAR_DNSCRYPT_PORT STRING_ARG
+       {
+               OUTYY(("P(dnsc_dnscrypt_port:%s)\n", $2));
+
+               if(atoi($2) == 0)
+                       yyerror("port number expected");
+               else cfg_parser->cfg->dnscrypt_port = atoi($2);
+               free($2);
+       }
+       ;
+dnsc_dnscrypt_provider: VAR_DNSCRYPT_PROVIDER STRING_ARG
+       {
+               OUTYY(("P(dnsc_dnscrypt_provider:%s)\n", $2));
+               free(cfg_parser->cfg->dnscrypt_provider);
+               cfg_parser->cfg->dnscrypt_provider = $2;
+       }
+       ;
+dnsc_dnscrypt_provider_cert: VAR_DNSCRYPT_PROVIDER_CERT STRING_ARG
+       {
+               OUTYY(("P(dnsc_dnscrypt_provider_cert:%s)\n", $2));
+               if(!cfg_strlist_insert(&cfg_parser->cfg->dnscrypt_provider_cert, $2))
+                       fatal_exit("out of memory adding dnscrypt-provider-cert");
+       }
+       ;
+dnsc_dnscrypt_secret_key: VAR_DNSCRYPT_SECRET_KEY STRING_ARG
+       {
+               OUTYY(("P(dnsc_dnscrypt_secret_key:%s)\n", $2));
+               if(!cfg_strlist_insert(&cfg_parser->cfg->dnscrypt_secret_key, $2))
+                       fatal_exit("out of memory adding dnscrypt-secret-key");
+       }
+       ;
 %%
 
 /* parse helper routines could be here */
index 8e66b9045fa199187cf179c6c93096e33f51ef8d..dbbcc4d1ed7b9d07c226fdca34eff682f9c1db05 100644 (file)
@@ -47,6 +47,7 @@
 #include "sldns/pkthdr.h"
 #include "sldns/sbuffer.h"
 #include "dnstap/dnstap.h"
+#include "dnscrypt/dnscrypt.h"
 #ifdef HAVE_OPENSSL_SSL_H
 #include <openssl/ssl.h>
 #endif
@@ -665,6 +666,7 @@ comm_point_udp_callback(int fd, short event, void* arg)
        struct comm_reply rep;
        ssize_t rcv;
        int i;
+    struct sldns_buffer *buffer;
 
        rep.c = (struct comm_point*)arg;
        log_assert(rep.c->type == comm_udp);
@@ -701,7 +703,12 @@ comm_point_udp_callback(int fd, short event, void* arg)
                fptr_ok(fptr_whitelist_comm_point(rep.c->callback));
                if((*rep.c->callback)(rep.c, rep.c->cb_arg, NETEVENT_NOERROR, &rep)) {
                        /* send back immediate reply */
-                       (void)comm_point_send_udp_msg(rep.c, rep.c->buffer,
+#ifdef USE_DNSCRYPT
+            buffer = rep.c->dnscrypt_buffer;
+#else
+            buffer = rep.c->buffer;
+#endif
+                       (void)comm_point_send_udp_msg(rep.c, buffer,
                                (struct sockaddr*)&rep.addr, rep.addrlen);
                }
                if(rep.c->fd != fd) /* commpoint closed to -1 or reused for
@@ -717,6 +724,10 @@ setup_tcp_handler(struct comm_point* c, int fd, int cur, int max)
        log_assert(c->type == comm_tcp);
        log_assert(c->fd == -1);
        sldns_buffer_clear(c->buffer);
+#ifdef USE_DNSCRYPT
+    if (c->dnscrypt)
+        sldns_buffer_clear(c->dnscrypt_buffer);
+#endif
        c->tcp_is_reading = 1;
        c->tcp_byte_count = 0;
        c->tcp_timeout_msec = TCP_QUERY_TIMEOUT;
@@ -1311,6 +1322,10 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c)
 {
        ssize_t r;
        log_assert(c->type == comm_tcp);
+    struct sldns_buffer *buffer = c->buffer;
+#ifdef USE_DNSCRYPT
+    buffer = c->dnscrypt_buffer;
+#endif
        if(c->tcp_is_reading && !c->ssl)
                return 0;
        log_assert(fd != -1);
@@ -1364,15 +1379,15 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c)
        if(c->tcp_do_fastopen == 1) {
                /* this form of sendmsg() does both a connect() and send() so need to
                   look for various flavours of error*/
-               uint16_t len = htons(sldns_buffer_limit(c->buffer));
+               uint16_t len = htons(sldns_buffer_limit(buffer));
                struct msghdr msg;
                struct iovec iov[2];
                c->tcp_do_fastopen = 0;
                memset(&msg, 0, sizeof(msg));
                iov[0].iov_base = (uint8_t*)&len + c->tcp_byte_count;
                iov[0].iov_len = sizeof(uint16_t) - c->tcp_byte_count;
-               iov[1].iov_base = sldns_buffer_begin(c->buffer);
-               iov[1].iov_len = sldns_buffer_limit(c->buffer);
+               iov[1].iov_base = sldns_buffer_begin(buffer);
+               iov[1].iov_len = sldns_buffer_limit(buffer);
                log_assert(iov[0].iov_len > 0);
                log_assert(iov[1].iov_len > 0);
                msg.msg_name = &c->repinfo.addr;
@@ -1400,9 +1415,9 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c)
                        c->tcp_byte_count += r;
                        if(c->tcp_byte_count < sizeof(uint16_t))
                                return 1;
-                       sldns_buffer_set_position(c->buffer, c->tcp_byte_count - 
+                       sldns_buffer_set_position(buffer, c->tcp_byte_count - 
                                sizeof(uint16_t));
-                       if(sldns_buffer_remaining(c->buffer) == 0) {
+                       if(sldns_buffer_remaining(buffer) == 0) {
                                tcp_callback_writer(c);
                                return 1;
                        }
@@ -1411,13 +1426,13 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c)
 #endif /* USE_MSG_FASTOPEN */
 
        if(c->tcp_byte_count < sizeof(uint16_t)) {
-               uint16_t len = htons(sldns_buffer_limit(c->buffer));
+               uint16_t len = htons(sldns_buffer_limit(buffer));
 #ifdef HAVE_WRITEV
                struct iovec iov[2];
                iov[0].iov_base = (uint8_t*)&len + c->tcp_byte_count;
                iov[0].iov_len = sizeof(uint16_t) - c->tcp_byte_count;
-               iov[1].iov_base = sldns_buffer_begin(c->buffer);
-               iov[1].iov_len = sldns_buffer_limit(c->buffer);
+               iov[1].iov_base = sldns_buffer_begin(buffer);
+               iov[1].iov_len = sldns_buffer_limit(buffer);
                log_assert(iov[0].iov_len > 0);
                log_assert(iov[1].iov_len > 0);
                r = writev(fd, iov, 2);
@@ -1459,16 +1474,16 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c)
                c->tcp_byte_count += r;
                if(c->tcp_byte_count < sizeof(uint16_t))
                        return 1;
-               sldns_buffer_set_position(c->buffer, c->tcp_byte_count - 
+               sldns_buffer_set_position(buffer, c->tcp_byte_count - 
                        sizeof(uint16_t));
-               if(sldns_buffer_remaining(c->buffer) == 0) {
+               if(sldns_buffer_remaining(buffer) == 0) {
                        tcp_callback_writer(c);
                        return 1;
                }
        }
-       log_assert(sldns_buffer_remaining(c->buffer) > 0);
-       r = send(fd, (void*)sldns_buffer_current(c->buffer), 
-               sldns_buffer_remaining(c->buffer), 0);
+       log_assert(sldns_buffer_remaining(buffer) > 0);
+       r = send(fd, (void*)sldns_buffer_current(buffer), 
+               sldns_buffer_remaining(buffer), 0);
        if(r == -1) {
 #ifndef USE_WINSOCK
                if(errno == EINTR || errno == EAGAIN)
@@ -1487,9 +1502,9 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c)
 #endif
                return 0;
        }
-       sldns_buffer_skip(c->buffer, r);
+       sldns_buffer_skip(buffer, r);
 
-       if(sldns_buffer_remaining(c->buffer) == 0) {
+       if(sldns_buffer_remaining(buffer) == 0) {
                tcp_callback_writer(c);
        }
        
@@ -1503,6 +1518,20 @@ comm_point_tcp_handle_callback(int fd, short event, void* arg)
        log_assert(c->type == comm_tcp);
        ub_comm_base_now(c->ev->base);
 
+#ifdef USE_DNSCRYPT
+       /* Initialize if this is a dnscrypt socket */
+       if(c->tcp_parent) {
+               c->dnscrypt = c->tcp_parent->dnscrypt;
+       }
+    if(c->dnscrypt && c->dnscrypt_buffer == c->buffer) {
+        c->dnscrypt_buffer = sldns_buffer_new(sldns_buffer_capacity(c->buffer));
+        if(!c->dnscrypt_buffer) {
+            log_err("Could not allocate dnscrypt buffer");
+            return;
+        }
+    }
+#endif
+
        if(event&UB_EV_READ) {
                if(!comm_point_tcp_handle_read(fd, c, 0)) {
                        reclaim_tcp_handler(c);
@@ -1604,6 +1633,10 @@ comm_point_create_udp(struct comm_base *base, int fd, sldns_buffer* buffer,
        c->tcp_check_nb_connect = 0;
 #ifdef USE_MSG_FASTOPEN
        c->tcp_do_fastopen = 0;
+#endif
+#ifdef USE_DNSCRYPT
+       c->dnscrypt = 0;
+       c->dnscrypt_buffer = buffer;
 #endif
        c->inuse = 0;
        c->callback = callback;
@@ -1655,6 +1688,10 @@ comm_point_create_udp_ancil(struct comm_base *base, int fd,
        c->type = comm_udp;
        c->tcp_do_close = 0;
        c->do_not_close = 0;
+#ifdef USE_DNSCRYPT
+    c->dnscrypt = 0;
+    c->dnscrypt_buffer = buffer;
+#endif
        c->inuse = 0;
        c->tcp_do_toggle_rw = 0;
        c->tcp_check_nb_connect = 0;
@@ -1725,6 +1762,12 @@ comm_point_create_tcp_handler(struct comm_base *base,
        c->tcp_check_nb_connect = 0;
 #ifdef USE_MSG_FASTOPEN
        c->tcp_do_fastopen = 0;
+#endif
+#ifdef USE_DNSCRYPT
+    c->dnscrypt = 0;
+    // We don't know just yet if this is a dnscrypt channel. Allocation
+    // will be done when handling the callback.
+    c->dnscrypt_buffer = c->buffer;
 #endif
        c->repinfo.c = c;
        c->callback = callback;
@@ -1788,6 +1831,10 @@ comm_point_create_tcp(struct comm_base *base, int fd, int num, size_t bufsize,
        c->tcp_check_nb_connect = 0;
 #ifdef USE_MSG_FASTOPEN
        c->tcp_do_fastopen = 0;
+#endif
+#ifdef USE_DNSCRYPT
+       c->dnscrypt = 0;
+       c->dnscrypt_buffer = NULL;
 #endif
        c->callback = NULL;
        c->cb_arg = NULL;
@@ -1856,6 +1903,10 @@ comm_point_create_tcp_out(struct comm_base *base, size_t bufsize,
        c->tcp_check_nb_connect = 1;
 #ifdef USE_MSG_FASTOPEN
        c->tcp_do_fastopen = 1;
+#endif
+#ifdef USE_DNSCRYPT
+       c->dnscrypt = 0;
+       c->dnscrypt_buffer = c->buffer;
 #endif
        c->repinfo.c = c;
        c->callback = callback;
@@ -1913,6 +1964,10 @@ comm_point_create_local(struct comm_base *base, int fd, size_t bufsize,
        c->tcp_check_nb_connect = 0;
 #ifdef USE_MSG_FASTOPEN
        c->tcp_do_fastopen = 0;
+#endif
+#ifdef USE_DNSCRYPT
+       c->dnscrypt = 0;
+       c->dnscrypt_buffer = c->buffer;
 #endif
        c->callback = callback;
        c->cb_arg = callback_arg;
@@ -1969,6 +2024,10 @@ comm_point_create_raw(struct comm_base* base, int fd, int writing,
        c->tcp_check_nb_connect = 0;
 #ifdef USE_MSG_FASTOPEN
        c->tcp_do_fastopen = 0;
+#endif
+#ifdef USE_DNSCRYPT
+       c->dnscrypt = 0;
+       c->dnscrypt_buffer = c->buffer;
 #endif
        c->callback = callback;
        c->cb_arg = callback_arg;
@@ -2034,8 +2093,14 @@ comm_point_delete(struct comm_point* c)
                free(c->tcp_handlers);
        }
        free(c->timeout);
-       if(c->type == comm_tcp || c->type == comm_local)
+       if(c->type == comm_tcp || c->type == comm_local) {
                sldns_buffer_free(c->buffer);
+#ifdef USE_DNSCRYPT
+        if(c->dnscrypt && c->dnscrypt_buffer != c->buffer) {
+            sldns_buffer_free(c->dnscrypt_buffer);
+        }
+#endif
+    }
        ub_event_free(c->ev->ev);
        free(c->ev);
        free(c);
@@ -2045,13 +2110,20 @@ void
 comm_point_send_reply(struct comm_reply *repinfo)
 {
        log_assert(repinfo && repinfo->c);
+    struct sldns_buffer* buffer = repinfo->c->buffer;
+#ifdef USE_DNSCRYPT
+    buffer = repinfo->c->dnscrypt_buffer;
+    if(!dnsc_handle_uncurved_request(repinfo)) {
+        return;
+    }
+#endif
        if(repinfo->c->type == comm_udp) {
                if(repinfo->srctype)
                        comm_point_send_udp_msg_if(repinfo->c, 
-                       repinfo->c->buffer, (struct sockaddr*)&repinfo->addr, 
+                       buffer, (struct sockaddr*)&repinfo->addr, 
                        repinfo->addrlen, repinfo);
                else
-                       comm_point_send_udp_msg(repinfo->c, repinfo->c->buffer,
+                       comm_point_send_udp_msg(repinfo->c, buffer,
                        (struct sockaddr*)&repinfo->addr, repinfo->addrlen);
 #ifdef USE_DNSTAP
                if(repinfo->c->dtenv != NULL &&
@@ -2160,8 +2232,15 @@ size_t comm_point_get_mem(struct comm_point* c)
        s = sizeof(*c) + sizeof(*c->ev);
        if(c->timeout) 
                s += sizeof(*c->timeout);
-       if(c->type == comm_tcp || c->type == comm_local)
+       if(c->type == comm_tcp || c->type == comm_local) {
                s += sizeof(*c->buffer) + sldns_buffer_capacity(c->buffer);
+#ifdef USE_DNSCRYPT
+        s += sizeof(*c->dnscrypt_buffer);
+        if(c->buffer != c->dnscrypt_buffer) {
+            s += sldns_buffer_capacity(c->dnscrypt_buffer);
+        }
+#endif
+    }
        if(c->type == comm_tcp_accept) {
                int i;
                for(i=0; i<c->max_tcp_count; i++)
index 2ce716b850e95ff7e646b44e7f5a653f69ccaef5..cb8eb86b9f747fee6a93cbb77c61f41136a412dd 100644 (file)
@@ -60,6 +60,8 @@
 #ifndef NET_EVENT_H
 #define NET_EVENT_H
 
+#include "dnscrypt/dnscrypt.h"
+
 struct sldns_buffer;
 struct comm_point;
 struct comm_reply;
@@ -114,6 +116,13 @@ struct comm_reply {
        socklen_t addrlen;
        /** return type 0 (none), 4(IP4), 6(IP6) */
        int srctype;
+       /* DnsCrypt context */
+#ifdef USE_DNSCRYPT
+       uint8_t client_nonce[crypto_box_HALF_NONCEBYTES];
+       uint8_t nmkey[crypto_box_BEFORENMBYTES];
+       const KeyPair *keypair;
+       int is_dnscrypted;
+#endif
        /** the return source interface data */
        union {
 #ifdef IPV6_PKTINFO
@@ -127,6 +136,8 @@ struct comm_reply {
        }       
                /** variable with return source data */
                pktinfo;
+    /** max udp size for udp packets */
+    size_t max_udp_size;
 };
 
 /** 
@@ -236,6 +247,12 @@ struct comm_point {
        int tcp_do_fastopen;
 #endif
 
+#ifdef USE_DNSCRYPT
+    /** Is this a dnscrypt channel */
+       int dnscrypt;
+       /** encrypted buffer pointer. Either to perthread, or own buffer or NULL */
+       struct sldns_buffer* dnscrypt_buffer;
+#endif
        /** number of queries outstanding on this socket, used by
         * outside network for udp ports */
        int inuse;