/config.log
/config.status
/dnstap/dnstap_config.h
+/dnscrypt/dnscrypt_config.h
/doc/example.conf
/doc/libunbound.3
/doc/unbound-anchor.8
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@
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 \
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
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 \
-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)
/* 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
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])
]
)
+# 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
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
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");
struct dt_env;
#endif
+#include "dnscrypt/dnscrypt_config.h"
+#ifdef USE_DNSCRYPT
+struct dnsc_env;
+#endif
+
/**
* Structure holding worker list.
* Holds globally visible information.
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
};
/**
#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>
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,
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;
}
--- /dev/null
+#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
--- /dev/null
+
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+# 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
+])
--- /dev/null
+#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 */
+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
# 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
.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
* @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) {
#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
}
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
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
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;
/* 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) {
}
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);
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);
}
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;
}
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;
}
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;
}
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;
}
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);
/** 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;
};
/** 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
+
};
/**
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
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"
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
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);
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);
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 */
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 */
%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 */
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 */
#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
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);
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
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;
{
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);
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;
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;
}
#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);
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)
#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);
}
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);
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;
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;
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;
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;
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;
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;
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;
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);
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 &&
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++)
#ifndef NET_EVENT_H
#define NET_EVENT_H
+#include "dnscrypt/dnscrypt.h"
+
struct sldns_buffer;
struct comm_point;
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
}
/** variable with return source data */
pktinfo;
+ /** max udp size for udp packets */
+ size_t max_udp_size;
};
/**
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;