summaryrefslogtreecommitdiffstats
path: root/contrib/librdns
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@highsecure.ru>2015-02-21 18:11:12 +0000
committerVsevolod Stakhov <vsevolod@highsecure.ru>2015-02-21 18:11:12 +0000
commit6e121a026ffd681418cc8e43647afacdd13f149a (patch)
treedc02aa9d0c2a46afc53886f498844bfaa958124d /contrib/librdns
parentf9d2d85d1473ae53c13c99747ef809649f5137bf (diff)
downloadrspamd-6e121a026ffd681418cc8e43647afacdd13f149a.tar.gz
rspamd-6e121a026ffd681418cc8e43647afacdd13f149a.zip
Move ucl and rdns to contrib.
Diffstat (limited to 'contrib/librdns')
-rw-r--r--contrib/librdns/CMakeLists.txt10
-rw-r--r--contrib/librdns/compression.c154
-rw-r--r--contrib/librdns/compression.h45
-rw-r--r--contrib/librdns/curve.c472
-rw-r--r--contrib/librdns/dns_private.h262
-rw-r--r--contrib/librdns/logger.c53
-rw-r--r--contrib/librdns/logger.h42
-rw-r--r--contrib/librdns/packet.c271
-rw-r--r--contrib/librdns/packet.h71
-rw-r--r--contrib/librdns/parse.c419
-rw-r--r--contrib/librdns/parse.h65
-rw-r--r--contrib/librdns/punycode.c297
-rw-r--r--contrib/librdns/punycode.h59
-rw-r--r--contrib/librdns/rdns.h372
-rw-r--r--contrib/librdns/rdns_curve.h75
-rw-r--r--contrib/librdns/rdns_ev.h232
-rw-r--r--contrib/librdns/rdns_event.h231
-rw-r--r--contrib/librdns/ref.h71
-rw-r--r--contrib/librdns/resolver.c782
-rw-r--r--contrib/librdns/upstream.h257
-rw-r--r--contrib/librdns/util.c523
-rw-r--r--contrib/librdns/util.h62
22 files changed, 4825 insertions, 0 deletions
diff --git a/contrib/librdns/CMakeLists.txt b/contrib/librdns/CMakeLists.txt
new file mode 100644
index 000000000..f2cd9a3e1
--- /dev/null
+++ b/contrib/librdns/CMakeLists.txt
@@ -0,0 +1,10 @@
+SET(LIBRDNSSRC util.c
+ logger.c
+ compression.c
+ punycode.c
+ curve.c
+ parse.c
+ packet.c
+ resolver.c)
+
+ADD_LIBRARY(rdns STATIC ${LIBRDNSSRC}) \ No newline at end of file
diff --git a/contrib/librdns/compression.c b/contrib/librdns/compression.c
new file mode 100644
index 000000000..ac31a92c3
--- /dev/null
+++ b/contrib/librdns/compression.c
@@ -0,0 +1,154 @@
+/* Copyright (c) 2014, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "compression.h"
+#include "logger.h"
+
+static struct rdns_compression_entry *
+rdns_can_compress (const char *pos, struct rdns_compression_entry *comp)
+{
+ struct rdns_compression_entry *res;
+
+ HASH_FIND_STR (comp, pos, res);
+
+ return res;
+}
+
+static unsigned int
+rdns_calculate_label_len (const char *pos, const char *end)
+{
+ const char *p = pos;
+ unsigned int res = 0;
+
+ while (p != end) {
+ if (*p == '.') {
+ break;
+ }
+ res ++;
+ p ++;
+ }
+ return res;
+}
+
+static void
+rdns_add_compressed (const char *pos, const char *end,
+ struct rdns_compression_entry **comp, int offset)
+{
+ struct rdns_compression_entry *new;
+
+ assert (offset >= 0);
+ new = malloc (sizeof (*new));
+ if (new != NULL) {
+ new->label = pos;
+ new->offset = offset;
+ HASH_ADD_KEYPTR (hh, *comp, pos, (end - pos), new);
+ }
+}
+
+void
+rnds_compression_free (struct rdns_compression_entry *comp)
+{
+ struct rdns_compression_entry *cur, *tmp;
+
+ if (comp) {
+ free (comp->hh.tbl->buckets);
+ free (comp->hh.tbl);
+
+ HASH_ITER (hh, comp, cur, tmp) {
+ free (cur);
+ }
+ }
+}
+
+bool
+rdns_write_name_compressed (struct rdns_request *req,
+ const char *name, unsigned int namelen,
+ struct rdns_compression_entry **comp)
+{
+ uint8_t *target = req->packet + req->pos;
+ const char *pos = name, *end = name + namelen;
+ unsigned int remain = req->packet_len - req->pos - 5, label_len;
+ struct rdns_compression_entry *head = NULL, *test;
+ struct rdns_resolver *resolver = req->resolver;
+ uint16_t pointer;
+
+ if (comp != NULL) {
+ head = *comp;
+ }
+
+ while (pos < end && remain > 0) {
+ if (head != NULL) {
+ test = rdns_can_compress (pos, head);
+ if (test != NULL) {
+ if (remain < 2) {
+ rdns_info ("no buffer remain for constructing query");
+ return false;
+ }
+
+ pointer = htons ((uint16_t)test->offset) | DNS_COMPRESSION_BITS;
+ memcpy (target, &pointer, sizeof (pointer));
+ req->pos += 2;
+
+ return true;
+ }
+ }
+
+
+ label_len = rdns_calculate_label_len (pos, end);
+ if (label_len == 0) {
+ /* We have empty label it is allowed only if pos == end - 1 */
+ if (pos == end - 1) {
+ break;
+ }
+ else {
+ rdns_err ("double dots in the name requested");
+ return false;
+ }
+ }
+ else if (label_len > DNS_D_MAXLABEL) {
+ rdns_err ("too large label: %d", (int)label_len);
+ return false;
+ }
+
+ if (label_len + 1 > remain) {
+ rdns_info ("no buffer remain for constructing query, strip %d to %d",
+ (int)label_len, (int)remain);
+ label_len = remain - 1;
+ }
+
+ if (comp != NULL) {
+ rdns_add_compressed (pos, end, comp, target - req->packet);
+ }
+ /* Write label as is */
+ *target++ = (uint8_t)label_len;
+ memcpy (target, pos, label_len);
+ target += label_len;
+ pos += label_len + 1;
+ }
+
+ /* Termination label */
+ *target++ = '\0';
+ req->pos = target - req->packet;
+
+ return true;
+}
diff --git a/contrib/librdns/compression.h b/contrib/librdns/compression.h
new file mode 100644
index 000000000..adae2f3ef
--- /dev/null
+++ b/contrib/librdns/compression.h
@@ -0,0 +1,45 @@
+/* Copyright (c) 2014, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef COMPRESSION_H_
+#define COMPRESSION_H_
+
+#include "uthash.h"
+#include "dns_private.h"
+
+struct rdns_compression_entry {
+ const char *label; /**< label packed */
+ unsigned int offset; /**< offset in the packet */
+ UT_hash_handle hh; /**< hash handle */
+};
+
+/**
+ * Try to compress name passed or write it 'as is'
+ * @return
+ */
+bool rdns_write_name_compressed (struct rdns_request *req,
+ const char *name, unsigned int namelen,
+ struct rdns_compression_entry **comp);
+
+void rnds_compression_free (struct rdns_compression_entry *comp);
+
+#endif /* COMPRESSION_H_ */
diff --git a/contrib/librdns/curve.c b/contrib/librdns/curve.c
new file mode 100644
index 000000000..4c2a64135
--- /dev/null
+++ b/contrib/librdns/curve.c
@@ -0,0 +1,472 @@
+/*
+ * Copyright (c) 2014, Vsevolod Stakhov
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include "rdns.h"
+#include "dns_private.h"
+#include "rdns_curve.h"
+#include "ottery.h"
+#include "ref.h"
+#include "logger.h"
+
+#ifdef TWEETNACL
+
+#include <tweetnacl.h>
+
+void
+randombytes(uint8_t *data, uint64_t len)
+{
+ ottery_rand_bytes (data, len);
+}
+void sodium_memzero (uint8_t *data, uint64_t len)
+{
+ volatile uint8_t *p = data;
+
+ while (len--) {
+ *p = '\0';
+ }
+}
+void sodium_init(void)
+{
+
+}
+
+ssize_t rdns_curve_send (struct rdns_request *req, void *plugin_data);
+ssize_t rdns_curve_recv (struct rdns_io_channel *ioc, void *buf, size_t len,
+ void *plugin_data, struct rdns_request **req_out);
+void rdns_curve_finish_request (struct rdns_request *req, void *plugin_data);
+void rdns_curve_dtor (struct rdns_resolver *resolver, void *plugin_data);
+
+struct rdns_curve_entry {
+ char *name;
+ unsigned char pk[crypto_box_PUBLICKEYBYTES];
+ UT_hash_handle hh;
+};
+
+struct rdns_curve_nm_entry {
+ unsigned char k[crypto_box_BEFORENMBYTES];
+ struct rdns_curve_entry *entry;
+ struct rdns_curve_nm_entry *prev, *next;
+};
+
+struct rdns_curve_client_key {
+ unsigned char pk[crypto_box_PUBLICKEYBYTES];
+ unsigned char sk[crypto_box_SECRETKEYBYTES];
+ struct rdns_curve_nm_entry *nms;
+ uint64_t counter;
+ unsigned int uses;
+ ref_entry_t ref;
+};
+
+struct rdns_curve_request {
+ struct rdns_request *req;
+ struct rdns_curve_client_key *key;
+ struct rdns_curve_entry *entry;
+ struct rdns_curve_nm_entry *nm;
+ unsigned char nonce[crypto_box_NONCEBYTES];
+ UT_hash_handle hh;
+};
+
+struct rdns_curve_ctx {
+ struct rdns_curve_entry *entries;
+ struct rdns_curve_client_key *cur_key;
+ struct rdns_curve_request *requests;
+ double key_refresh_interval;
+ void *key_refresh_event;
+ struct rdns_resolver *resolver;
+};
+
+static struct rdns_curve_client_key *
+rdns_curve_client_key_new (struct rdns_curve_ctx *ctx)
+{
+ struct rdns_curve_client_key *new;
+ struct rdns_curve_nm_entry *nm;
+ struct rdns_curve_entry *entry, *tmp;
+
+ new = calloc (1, sizeof (struct rdns_curve_client_key));
+ crypto_box_keypair (new->pk, new->sk);
+
+ HASH_ITER (hh, ctx->entries, entry, tmp) {
+ nm = calloc (1, sizeof (struct rdns_curve_nm_entry));
+ nm->entry = entry;
+ crypto_box_beforenm (nm->k, entry->pk, new->sk);
+
+ DL_APPEND (new->nms, nm);
+ }
+
+ new->counter = ottery_rand_uint64 ();
+
+ return new;
+}
+
+static struct rdns_curve_nm_entry *
+rdns_curve_find_nm (struct rdns_curve_client_key *key, struct rdns_curve_entry *entry)
+{
+ struct rdns_curve_nm_entry *nm;
+
+ DL_FOREACH (key->nms, nm) {
+ if (nm->entry == entry) {
+ return nm;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+rdns_curve_client_key_free (struct rdns_curve_client_key *key)
+{
+ struct rdns_curve_nm_entry *nm, *tmp;
+
+ DL_FOREACH_SAFE (key->nms, nm, tmp) {
+ sodium_memzero (nm->k, sizeof (nm->k));
+ free (nm);
+ }
+ sodium_memzero (key->sk, sizeof (key->sk));
+ free (key);
+}
+
+struct rdns_curve_ctx*
+rdns_curve_ctx_new (double key_refresh_interval)
+{
+ struct rdns_curve_ctx *new;
+
+ new = calloc (1, sizeof (struct rdns_curve_ctx));
+ new->key_refresh_interval = key_refresh_interval;
+
+ return new;
+}
+
+void
+rdns_curve_ctx_add_key (struct rdns_curve_ctx *ctx,
+ const char *name, const unsigned char *pubkey)
+{
+ struct rdns_curve_entry *entry;
+ bool success = true;
+
+ entry = malloc (sizeof (struct rdns_curve_entry));
+ if (entry != NULL) {
+ entry->name = strdup (name);
+ if (entry->name == NULL) {
+ success = false;
+ }
+ memcpy (entry->pk, pubkey, sizeof (entry->pk));
+ if (success) {
+ HASH_ADD_KEYPTR (hh, ctx->entries, entry->name, strlen (entry->name), entry);
+ }
+ }
+}
+
+#define rdns_curve_write_hex(in, out, offset, base) do { \
+ *(out) |= ((in)[(offset)] - (base)) << ((1 - offset) * 4); \
+} while (0)
+
+static bool
+rdns_curve_hex_to_byte (const char *in, unsigned char *out)
+{
+ int i;
+
+ for (i = 0; i <= 1; i ++) {
+ if (in[i] >= '0' && in[i] <= '9') {
+ rdns_curve_write_hex (in, out, i, '0');
+ }
+ else if (in[i] >= 'a' && in[i] <= 'f') {
+ rdns_curve_write_hex (in, out, i, 'a' - 10);
+ }
+ else if (in[i] >= 'A' && in[i] <= 'F') {
+ rdns_curve_write_hex (in, out, i, 'A' - 10);
+ }
+ else {
+ return false;
+ }
+ }
+ return true;
+}
+
+#undef rdns_curve_write_hex
+
+unsigned char *
+rdns_curve_key_from_hex (const char *hex)
+{
+ unsigned int len = strlen (hex), i;
+ unsigned char *res = NULL;
+
+ if (len == crypto_box_PUBLICKEYBYTES * 2) {
+ res = calloc (1, crypto_box_PUBLICKEYBYTES);
+ for (i = 0; i < crypto_box_PUBLICKEYBYTES; i ++) {
+ if (!rdns_curve_hex_to_byte (&hex[i * 2], &res[i])) {
+ free (res);
+ return NULL;
+ }
+ }
+ }
+
+ return res;
+}
+
+void
+rdns_curve_ctx_destroy (struct rdns_curve_ctx *ctx)
+{
+ struct rdns_curve_entry *entry, *tmp;
+
+ HASH_ITER (hh, ctx->entries, entry, tmp) {
+ free (entry->name);
+ free (entry);
+ }
+
+ free (ctx);
+}
+
+static void
+rdns_curve_refresh_key_callback (void *user_data)
+{
+ struct rdns_curve_ctx *ctx = user_data;
+ struct rdns_resolver *resolver;
+
+ resolver = ctx->resolver;
+ rdns_info ("refresh dnscurve keys");
+ REF_RELEASE (ctx->cur_key);
+ ctx->cur_key = rdns_curve_client_key_new (ctx);
+ REF_INIT_RETAIN (ctx->cur_key, rdns_curve_client_key_free);
+}
+
+void
+rdns_curve_register_plugin (struct rdns_resolver *resolver,
+ struct rdns_curve_ctx *ctx)
+{
+ struct rdns_plugin *plugin;
+
+ if (!resolver->async_binded) {
+ return;
+ }
+
+ plugin = calloc (1, sizeof (struct rdns_plugin));
+ if (plugin != NULL) {
+ plugin->data = ctx;
+ plugin->type = RDNS_PLUGIN_CURVE;
+ plugin->cb.curve_plugin.send_cb = rdns_curve_send;
+ plugin->cb.curve_plugin.recv_cb = rdns_curve_recv;
+ plugin->cb.curve_plugin.finish_cb = rdns_curve_finish_request;
+ plugin->dtor = rdns_curve_dtor;
+ sodium_init ();
+ ctx->cur_key = rdns_curve_client_key_new (ctx);
+ REF_INIT_RETAIN (ctx->cur_key, rdns_curve_client_key_free);
+
+ if (ctx->key_refresh_interval > 0) {
+ ctx->key_refresh_event = resolver->async->add_periodic (
+ resolver->async->data, ctx->key_refresh_interval,
+ rdns_curve_refresh_key_callback, ctx);
+ }
+ ctx->resolver = resolver;
+ rdns_resolver_register_plugin (resolver, plugin);
+ }
+}
+
+ssize_t
+rdns_curve_send (struct rdns_request *req, void *plugin_data)
+{
+ struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *)plugin_data;
+ struct rdns_curve_entry *entry;
+ struct iovec iov[4];
+ unsigned char *m;
+ static const char qmagic[] = "Q6fnvWj8";
+ struct rdns_curve_request *creq;
+ struct rdns_curve_nm_entry *nm;
+ ssize_t ret, boxed_len;
+
+ /* Check for key */
+ HASH_FIND_STR (ctx->entries, req->io->srv->name, entry);
+ if (entry != NULL) {
+ nm = rdns_curve_find_nm (ctx->cur_key, entry);
+ creq = malloc (sizeof (struct rdns_curve_request));
+ if (creq == NULL) {
+ return -1;
+ }
+
+ boxed_len = req->pos + crypto_box_ZEROBYTES;
+ m = malloc (boxed_len);
+ if (m == NULL) {
+ return -1;
+ }
+
+ /* Ottery is faster than sodium native PRG that uses /dev/random only */
+ memcpy (creq->nonce, &ctx->cur_key->counter, sizeof (uint64_t));
+ ottery_rand_bytes (creq->nonce + sizeof (uint64_t), 12 - sizeof (uint64_t));
+ sodium_memzero (creq->nonce + 12, crypto_box_NONCEBYTES - 12);
+
+ sodium_memzero (m, crypto_box_ZEROBYTES);
+ memcpy (m + crypto_box_ZEROBYTES, req->packet, req->pos);
+
+ if (crypto_box_afternm (m, m, boxed_len,
+ creq->nonce, nm->k) == -1) {
+ sodium_memzero (m, boxed_len);
+ free (m);
+ return -1;
+ }
+
+ creq->key = ctx->cur_key;
+ REF_RETAIN (ctx->cur_key);
+ creq->entry = entry;
+ creq->req = req;
+ creq->nm = nm;
+ HASH_ADD_KEYPTR (hh, ctx->requests, creq->nonce, 12, creq);
+ req->curve_plugin_data = creq;
+
+ ctx->cur_key->counter ++;
+ ctx->cur_key->uses ++;
+
+ /* Now form a dnscurve packet */
+ iov[0].iov_base = (void *)qmagic;
+ iov[0].iov_len = sizeof (qmagic) - 1;
+ iov[1].iov_base = ctx->cur_key->pk;
+ iov[1].iov_len = sizeof (ctx->cur_key->pk);
+ iov[2].iov_base = creq->nonce;
+ iov[2].iov_len = 12;
+ iov[3].iov_base = m + crypto_box_BOXZEROBYTES;
+ iov[3].iov_len = boxed_len - crypto_box_BOXZEROBYTES;
+
+ ret = writev (req->io->sock, iov, sizeof (iov) / sizeof (iov[0]));
+ sodium_memzero (m, boxed_len);
+ free (m);
+ }
+ else {
+ ret = write (req->io->sock, req->packet, req->pos);
+ req->curve_plugin_data = NULL;
+ }
+
+ return ret;
+}
+
+ssize_t
+rdns_curve_recv (struct rdns_io_channel *ioc, void *buf, size_t len, void *plugin_data,
+ struct rdns_request **req_out)
+{
+ struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *)plugin_data;
+ ssize_t ret, boxlen;
+ static const char rmagic[] = "R6fnvWJ8";
+ unsigned char *p, *box;
+ unsigned char enonce[crypto_box_NONCEBYTES];
+ struct rdns_curve_request *creq;
+ struct rdns_resolver *resolver;
+
+ resolver = ctx->resolver;
+ ret = read (ioc->sock, buf, len);
+
+ if (ret <= 0 || ret < 64) {
+ /* Definitely not a DNSCurve packet */
+ return ret;
+ }
+
+ if (memcmp (buf, rmagic, sizeof (rmagic) - 1) == 0) {
+ /* Likely DNSCurve packet */
+ p = ((unsigned char *)buf) + 8;
+ HASH_FIND (hh, ctx->requests, p, 12, creq);
+ if (creq == NULL) {
+ rdns_info ("unable to find nonce in the internal hash");
+ return ret;
+ }
+ memcpy (enonce, p, crypto_box_NONCEBYTES);
+ p += crypto_box_NONCEBYTES;
+ boxlen = ret - crypto_box_NONCEBYTES +
+ crypto_box_BOXZEROBYTES -
+ sizeof (rmagic) + 1;
+ if (boxlen < 0) {
+ return ret;
+ }
+ box = malloc (boxlen);
+ sodium_memzero (box, crypto_box_BOXZEROBYTES);
+ memcpy (box + crypto_box_BOXZEROBYTES, p,
+ boxlen - crypto_box_BOXZEROBYTES);
+
+ if (crypto_box_open_afternm (box, box, boxlen, enonce, creq->nm->k) != -1) {
+ memcpy (buf, box + crypto_box_ZEROBYTES,
+ boxlen - crypto_box_ZEROBYTES);
+ ret = boxlen - crypto_box_ZEROBYTES;
+ *req_out = creq->req;
+ }
+ else {
+ rdns_info ("unable open cryptobox of size %d", (int)boxlen);
+ }
+ free (box);
+ }
+
+ return ret;
+}
+
+void
+rdns_curve_finish_request (struct rdns_request *req, void *plugin_data)
+{
+ struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *)plugin_data;
+ struct rdns_curve_request *creq = req->curve_plugin_data;
+
+ if (creq != NULL) {
+ REF_RELEASE (creq->key);
+ HASH_DELETE (hh, ctx->requests, creq);
+ }
+}
+
+void
+rdns_curve_dtor (struct rdns_resolver *resolver, void *plugin_data)
+{
+ struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *)plugin_data;
+
+ if (ctx->key_refresh_event != NULL) {
+ resolver->async->del_periodic (resolver->async->data,
+ ctx->key_refresh_event);
+ }
+ REF_RELEASE (ctx->cur_key);
+}
+
+#else
+
+/* Fake functions */
+struct rdns_curve_ctx* rdns_curve_ctx_new (double rekey_interval)
+{
+ return NULL;
+}
+void rdns_curve_ctx_add_key (struct rdns_curve_ctx *ctx,
+ const char *name, const unsigned char *pubkey)
+{
+
+}
+void rdns_curve_ctx_destroy (struct rdns_curve_ctx *ctx)
+{
+
+}
+void rdns_curve_register_plugin (struct rdns_resolver *resolver,
+ struct rdns_curve_ctx *ctx)
+{
+
+}
+
+unsigned char *
+rdns_curve_key_from_hex (const char *hex)
+{
+ return NULL;
+}
+#endif
diff --git a/contrib/librdns/dns_private.h b/contrib/librdns/dns_private.h
new file mode 100644
index 000000000..d23f8454d
--- /dev/null
+++ b/contrib/librdns/dns_private.h
@@ -0,0 +1,262 @@
+/* Copyright (c) 2014, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DNS_PRIVATE_H_
+#define DNS_PRIVATE_H_
+
+#include "uthash.h"
+#include "utlist.h"
+#include "rdns.h"
+#include "upstream.h"
+#include "ref.h"
+
+static const unsigned base = 36;
+static const unsigned t_min = 1;
+static const unsigned t_max = 26;
+static const unsigned skew = 38;
+static const unsigned damp = 700;
+static const unsigned initial_n = 128;
+static const unsigned initial_bias = 72;
+
+static const int dns_port = 53;
+static const int default_io_cnt = 8;
+
+#define UDP_PACKET_SIZE 4096
+
+#define DNS_COMPRESSION_BITS 0xC0
+
+#define DNS_D_MAXLABEL 63 /* + 1 '\0' */
+#define DNS_D_MAXNAME 255 /* + 1 '\0' */
+
+#define RESOLV_CONF "/etc/resolv.conf"
+
+/**
+ * Represents DNS server
+ */
+struct rdns_server {
+ char *name;
+ unsigned int port;
+ unsigned int io_cnt;
+
+ struct rdns_io_channel **io_channels;
+ upstream_entry_t up;
+};
+
+struct rdns_request {
+ struct rdns_resolver *resolver;
+ struct rdns_async_context *async;
+ struct rdns_io_channel *io;
+ struct rdns_reply *reply;
+ enum rdns_request_type type;
+
+ double timeout;
+ unsigned int retransmits;
+
+ int id;
+ struct rdns_request_name *requested_names;
+ unsigned int qcount;
+
+ enum {
+ RDNS_REQUEST_NEW = 0,
+ RDNS_REQUEST_REGISTERED = 1,
+ RDNS_REQUEST_SENT,
+ RDNS_REQUEST_REPLIED
+ } state;
+
+ uint8_t *packet;
+ off_t pos;
+ unsigned int packet_len;
+
+ dns_callback_type func;
+ void *arg;
+
+ void *async_event;
+
+#ifdef TWEETNACL
+ void *curve_plugin_data;
+#endif
+
+ UT_hash_handle hh;
+ ref_entry_t ref;
+};
+
+/**
+ * IO channel for a specific DNS server
+ */
+struct rdns_io_channel {
+ struct rdns_server *srv;
+ struct rdns_resolver *resolver;
+ int sock; /**< persistent socket */
+ bool active;
+ void *async_io; /** async opaque ptr */
+ struct rdns_request *requests; /**< requests in flight */
+ uint64_t uses;
+ ref_entry_t ref;
+};
+
+
+struct rdns_resolver {
+ struct rdns_server *servers;
+ struct rdns_io_channel *io_channels; /**< hash of io chains indexed by socket */
+ struct rdns_async_context *async; /** async callbacks */
+ void *periodic; /** periodic event for resolver */
+
+ struct rdns_plugin *curve_plugin;
+
+ rdns_log_function logger;
+ void *log_data;
+ enum rdns_log_level log_level;
+
+ uint64_t max_ioc_uses;
+ void *refresh_ioc_periodic;
+
+ bool async_binded;
+ bool initialized;
+ ref_entry_t ref;
+};
+
+struct dns_header;
+struct dns_query;
+
+/* Internal DNS structs */
+
+struct dns_header {
+ unsigned int qid :16;
+
+#if BYTE_ORDER == BIG_ENDIAN
+ unsigned int qr:1;
+ unsigned int opcode:4;
+ unsigned int aa:1;
+ unsigned int tc:1;
+ unsigned int rd:1;
+
+ unsigned int ra:1;
+ unsigned int unused:3;
+ unsigned int rcode:4;
+#else
+ unsigned int rd :1;
+ unsigned int tc :1;
+ unsigned int aa :1;
+ unsigned int opcode :4;
+ unsigned int qr :1;
+
+ unsigned int rcode :4;
+ unsigned int unused :3;
+ unsigned int ra :1;
+#endif
+
+ unsigned int qdcount :16;
+ unsigned int ancount :16;
+ unsigned int nscount :16;
+ unsigned int arcount :16;
+};
+
+enum dns_section {
+ DNS_S_QD = 0x01,
+#define DNS_S_QUESTION DNS_S_QD
+
+ DNS_S_AN = 0x02,
+#define DNS_S_ANSWER DNS_S_AN
+
+ DNS_S_NS = 0x04,
+#define DNS_S_AUTHORITY DNS_S_NS
+
+ DNS_S_AR = 0x08,
+#define DNS_S_ADDITIONAL DNS_S_AR
+
+ DNS_S_ALL = 0x0f
+};
+/* enum dns_section */
+
+enum dns_opcode {
+ DNS_OP_QUERY = 0,
+ DNS_OP_IQUERY = 1,
+ DNS_OP_STATUS = 2,
+ DNS_OP_NOTIFY = 4,
+ DNS_OP_UPDATE = 5,
+};
+/* dns_opcode */
+
+enum dns_class {
+ DNS_C_IN = 1,
+
+ DNS_C_ANY = 255
+};
+/* enum dns_class */
+
+struct dns_query {
+ char *qname;
+ unsigned int qtype :16;
+ unsigned int qclass :16;
+};
+
+enum dns_type {
+ DNS_T_A = RDNS_REQUEST_A,
+ DNS_T_NS = RDNS_REQUEST_NS,
+ DNS_T_CNAME = 5,
+ DNS_T_SOA = RDNS_REQUEST_SOA,
+ DNS_T_PTR = RDNS_REQUEST_PTR,
+ DNS_T_MX = RDNS_REQUEST_MX,
+ DNS_T_TXT = RDNS_REQUEST_TXT,
+ DNS_T_AAAA = RDNS_REQUEST_AAAA,
+ DNS_T_SRV = RDNS_REQUEST_SRV,
+ DNS_T_OPT = 41,
+ DNS_T_SSHFP = 44,
+ DNS_T_TLSA = RDNS_REQUEST_TLSA,
+ DNS_T_SPF = RDNS_REQUEST_SPF,
+ DNS_T_ALL = RDNS_REQUEST_ANY
+};
+/* enum dns_type */
+
+static const char dns_rcodes[][32] = {
+ [RDNS_RC_NOERROR] = "no error",
+ [RDNS_RC_FORMERR] = "query format error",
+ [RDNS_RC_SERVFAIL] = "server fail",
+ [RDNS_RC_NXDOMAIN] = "no records with this name",
+ [RDNS_RC_NOTIMP] = "not implemented",
+ [RDNS_RC_REFUSED] = "query refused",
+ [RDNS_RC_YXDOMAIN] = "YXDOMAIN",
+ [RDNS_RC_YXRRSET] = "YXRRSET",
+ [RDNS_RC_NXRRSET] = "NXRRSET",
+ [RDNS_RC_NOTAUTH] = "not authorized",
+ [RDNS_RC_NOTZONE] = "no such zone",
+ [RDNS_RC_TIMEOUT] = "query timed out",
+ [RDNS_RC_NETERR] = "network error",
+ [RDNS_RC_NOREC] = "requested record is not found"
+};
+
+static const char dns_types[][16] = {
+ [RDNS_REQUEST_A] = "A request",
+ [RDNS_REQUEST_NS] = "NS request",
+ [RDNS_REQUEST_PTR] = "PTR request",
+ [RDNS_REQUEST_MX] = "MX request",
+ [RDNS_REQUEST_TXT] = "TXT request",
+ [RDNS_REQUEST_SRV] = "SRV request",
+ [RDNS_REQUEST_SPF] = "SPF request",
+ [RDNS_REQUEST_AAAA] = "AAAA request",
+ [RDNS_REQUEST_TLSA] = "TLSA request",
+ [RDNS_REQUEST_ANY] = "ANY request"
+};
+
+
+#endif /* DNS_PRIVATE_H_ */
diff --git a/contrib/librdns/logger.c b/contrib/librdns/logger.c
new file mode 100644
index 000000000..c9ed2d926
--- /dev/null
+++ b/contrib/librdns/logger.c
@@ -0,0 +1,53 @@
+/* Copyright (c) 2014, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include "dns_private.h"
+#include "logger.h"
+
+void
+rdns_logger_internal (void *log_data, enum rdns_log_level level,
+ const char *function, const char *format,
+ va_list args)
+{
+ struct rdns_resolver *resolver = log_data;
+
+ if (level <= resolver->log_level) {
+ fprintf (stderr, "rdns: %s: ", function);
+ vfprintf (stderr, format, args);
+ fprintf (stderr, "\n");
+ }
+}
+
+void rdns_logger_helper (struct rdns_resolver *resolver,
+ enum rdns_log_level level,
+ const char *function, const char *format, ...)
+{
+ va_list va;
+
+ if (resolver->logger != NULL) {
+ va_start (va, format);
+ resolver->logger (resolver->log_data, level, function, format, va);
+ va_end (va);
+ }
+}
diff --git a/contrib/librdns/logger.h b/contrib/librdns/logger.h
new file mode 100644
index 000000000..91108f4e3
--- /dev/null
+++ b/contrib/librdns/logger.h
@@ -0,0 +1,42 @@
+/* Copyright (c) 2014, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef LOGGER_H_
+#define LOGGER_H_
+
+#include <stdarg.h>
+#include "dns_private.h"
+
+void rdns_logger_internal (void *log_data, enum rdns_log_level level,
+ const char *function, const char *format,
+ va_list args);
+
+void rdns_logger_helper (struct rdns_resolver *resolver,
+ enum rdns_log_level level,
+ const char *function, const char *format, ...);
+
+#define rdns_err(...) do { rdns_logger_helper (resolver, RDNS_LOG_ERROR, __FUNCTION__, __VA_ARGS__); } while (0)
+#define rdns_warn(...) do { rdns_logger_helper (resolver, RDNS_LOG_WARNING, __FUNCTION__, __VA_ARGS__); } while (0)
+#define rdns_info(...) do { rdns_logger_helper (resolver, RDNS_LOG_INFO, __FUNCTION__, __VA_ARGS__); } while (0)
+#define rdns_debug(...) do { rdns_logger_helper (resolver, RDNS_LOG_DEBUG, __FUNCTION__, __VA_ARGS__); } while (0)
+
+#endif /* LOGGER_H_ */
diff --git a/contrib/librdns/packet.c b/contrib/librdns/packet.c
new file mode 100644
index 000000000..88e51dfba
--- /dev/null
+++ b/contrib/librdns/packet.c
@@ -0,0 +1,271 @@
+/* Copyright (c) 2014, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "rdns.h"
+#include "dns_private.h"
+#include "punycode.h"
+#include "packet.h"
+#include "util.h"
+#include "logger.h"
+#include "compression.h"
+
+void
+rdns_allocate_packet (struct rdns_request* req, unsigned int namelen)
+{
+ namelen += 96 + 2 + 4 + 11; /* EDNS0 RR */
+ req->packet = malloc (namelen);
+ req->pos = 0;
+ req->packet_len = namelen;
+}
+
+
+void
+rdns_make_dns_header (struct rdns_request *req, unsigned int qcount)
+{
+ struct dns_header *header;
+
+ /* Set DNS header values */
+ header = (struct dns_header *)req->packet;
+ memset (header, 0 , sizeof (struct dns_header));
+ header->qid = rdns_permutor_generate_id ();
+ header->rd = 1;
+ header->qdcount = htons (qcount);
+ header->arcount = htons (1);
+ req->pos += sizeof (struct dns_header);
+ req->id = header->qid;
+}
+
+static bool
+rdns_maybe_punycode_label (const uint8_t *begin,
+ uint8_t const **dot, size_t *label_len)
+{
+ bool ret = false;
+ const uint8_t *p = begin;
+
+ *dot = NULL;
+
+ while (*p) {
+ if (*p == '.') {
+ *dot = p;
+ break;
+ }
+ else if ((*p) & 0x80) {
+ ret = true;
+ }
+ p ++;
+ }
+
+ if (*p) {
+ *label_len = p - begin;
+ }
+ else {
+ *label_len = p - begin;
+ }
+
+ return ret;
+}
+
+bool
+rdns_format_dns_name (struct rdns_resolver *resolver, const char *in,
+ size_t inlen,
+ char **out, size_t *outlen)
+{
+ const uint8_t *dot;
+ const uint8_t *p = in, *end = in + inlen;
+ char *o;
+ int labels = 0;
+ size_t label_len, olen, remain;
+ uint32_t *uclabel;
+ size_t punylabel_len, uclabel_len;
+ char tmp_label[DNS_D_MAXLABEL];
+ bool need_encode = false;
+
+ if (inlen == 0) {
+ inlen = strlen (in);
+ }
+
+ /* Check for non-ascii characters */
+ while (p != end) {
+ if (*p >= 0x80) {
+ need_encode = true;
+ }
+ else if (*p == '.') {
+ labels ++;
+ }
+ p ++;
+ }
+
+ if (!need_encode) {
+ *out = malloc (inlen + 1);
+ if (*out == NULL) {
+ return false;
+ }
+ o = *out;
+ memcpy (o, in, inlen);
+ o[inlen] = '\0';
+ *outlen = inlen;
+
+ return true;
+ }
+
+ /* We need to encode */
+
+ p = in;
+ olen = inlen + 1 + sizeof ("xn--") * labels;
+ *out = malloc (olen);
+
+ if (*out == NULL) {
+ return false;
+ }
+
+ o = *out;
+ remain = olen;
+
+ while (p != end) {
+ /* Check label for unicode characters */
+ if (rdns_maybe_punycode_label (p, &dot, &label_len)) {
+ /* Convert to ucs4 */
+ if (rdns_utf8_to_ucs4 (p, label_len, &uclabel, &uclabel_len) == 0) {
+ punylabel_len = DNS_D_MAXLABEL;
+
+ rdns_punycode_label_toascii (uclabel, uclabel_len,
+ tmp_label, &punylabel_len);
+ if (remain >= punylabel_len + 1) {
+ memcpy (o, tmp_label, punylabel_len);
+ o += punylabel_len;
+ *o++ = '.';
+ remain -= punylabel_len + 1;
+ }
+ else {
+ rdns_info ("no buffer remain for punycoding query");
+ free (*out);
+ return false;
+ }
+
+ free (uclabel);
+
+ if (dot) {
+ p = dot + 1;
+ }
+ else {
+ break;
+ }
+ }
+ else {
+ break;
+ }
+ }
+ else {
+ if (dot) {
+ if (label_len > DNS_D_MAXLABEL) {
+ rdns_info ("dns name component is longer than 63 bytes, should be stripped");
+ label_len = DNS_D_MAXLABEL;
+ }
+ if (remain < label_len + 1) {
+ rdns_info ("no buffer remain for punycoding query");
+ return false;
+ }
+ if (label_len == 0) {
+ /* Two dots in order, skip this */
+ rdns_info ("name contains two or more dots in a row, replace it with one dot");
+ p = dot + 1;
+ continue;
+ }
+ memcpy (o, p, label_len);
+ o += label_len;
+ *o++ = '.';
+ remain -= label_len + 1;
+ p = dot + 1;
+ }
+ else {
+ if (label_len == 0) {
+ /* If name is ended with dot */
+ break;
+ }
+ if (label_len > DNS_D_MAXLABEL) {
+ rdns_info ("dns name component is longer than 63 bytes, should be stripped");
+ label_len = DNS_D_MAXLABEL;
+ }
+ if (remain < label_len + 1) {
+ rdns_info ("no buffer remain for punycoding query");
+ return false;
+ }
+ memcpy (o, p, label_len);
+ o += label_len;
+ *o++ = '.';
+ remain -= label_len + 1;
+ p = dot + 1;
+ break;
+ }
+ }
+ if (remain == 0) {
+ rdns_info ("no buffer remain for punycoding query");
+ return false;
+ }
+ }
+ *o = '\0';
+
+ *outlen = o - *out;
+
+ return true;
+}
+
+bool
+rdns_add_rr (struct rdns_request *req, const char *name, unsigned int len,
+ enum dns_type type, struct rdns_compression_entry **comp)
+{
+ uint16_t *p;
+
+ if (!rdns_write_name_compressed (req, name, len, comp)) {
+ return false;
+ }
+ p = (uint16_t *)(req->packet + req->pos);
+ *p++ = htons (type);
+ *p = htons (DNS_C_IN);
+ req->pos += sizeof (uint16_t) * 2;
+
+ return true;
+}
+
+bool
+rdns_add_edns0 (struct rdns_request *req)
+{
+ uint8_t *p8;
+ uint16_t *p16;
+
+ p8 = (uint8_t *)(req->packet + req->pos);
+ *p8 = '\0'; /* Name is root */
+ p16 = (uint16_t *)(req->packet + req->pos + 1);
+ *p16++ = htons (DNS_T_OPT);
+ /* UDP packet length */
+ *p16++ = htons (UDP_PACKET_SIZE);
+ /* Extended rcode 00 00 */
+ *p16++ = 0;
+ /* Z 10000000 00000000 to allow dnssec, disabled currently */
+ *p16++ = 0;
+ /* Length */
+ *p16 = 0;
+ req->pos += sizeof (uint8_t) + sizeof (uint16_t) * 5;
+
+ return true;
+}
diff --git a/contrib/librdns/packet.h b/contrib/librdns/packet.h
new file mode 100644
index 000000000..f4c09b900
--- /dev/null
+++ b/contrib/librdns/packet.h
@@ -0,0 +1,71 @@
+/* Copyright (c) 2014, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef PACKET_H_
+#define PACKET_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "dns_private.h"
+
+struct rdns_compression_entry;
+
+/**
+ * Allocate dns packet suitable to handle up to `namelen` name
+ * @param req request
+ * @param namelen requested name
+ */
+void rdns_allocate_packet (struct rdns_request* req, unsigned int namelen);
+
+/**
+ * Add basic header to the dns packet
+ * @param req
+ */
+void rdns_make_dns_header (struct rdns_request *req, unsigned int qcount);
+
+
+/**
+ * Format DNS name of the packet
+ * @param req request
+ * @param name name string
+ * @param namelen length of name
+ */
+bool rdns_format_dns_name (struct rdns_resolver *resolver,
+ const char *name, size_t namelen,
+ char **out, size_t *outlen);
+
+/**
+ * Add a resource record to the DNS packet
+ * @param req request
+ * @param name requested name
+ * @param type type of resource record
+ */
+bool rdns_add_rr (struct rdns_request *req, const char *name, unsigned int len,
+ enum dns_type type, struct rdns_compression_entry **comp);
+
+/**
+ * Add EDNS0 section
+ * @param req
+ */
+bool rdns_add_edns0 (struct rdns_request *req);
+
+#endif /* PACKET_H_ */
diff --git a/contrib/librdns/parse.c b/contrib/librdns/parse.c
new file mode 100644
index 000000000..e9527eaa4
--- /dev/null
+++ b/contrib/librdns/parse.c
@@ -0,0 +1,419 @@
+/* Copyright (c) 2014, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "rdns.h"
+#include "dns_private.h"
+#include "parse.h"
+#include "logger.h"
+
+static uint8_t *
+rdns_decompress_label (uint8_t *begin, uint16_t *len, uint16_t max)
+{
+ uint16_t offset = (*len);
+
+ if (offset > max) {
+ return NULL;
+ }
+ *len = *(begin + offset);
+ return begin + offset;
+}
+
+#define UNCOMPRESS_DNS_OFFSET(p) (((*(p)) ^ DNS_COMPRESSION_BITS) << 8) + *((p) + 1)
+
+uint8_t *
+rdns_request_reply_cmp (struct rdns_request *req, uint8_t *in, int len)
+{
+ uint8_t *p, *c, *l1, *l2;
+ uint16_t len1, len2;
+ int decompressed = 0;
+ struct rdns_resolver *resolver = req->resolver;
+
+ /* QR format:
+ * labels - len:octets
+ * null label - 0
+ * class - 2 octets
+ * type - 2 octets
+ */
+
+ /* In p we would store current position in reply and in c - position in request */
+ p = in;
+ c = req->packet + req->pos;
+
+ for (;;) {
+ /* Get current label */
+ len1 = *p;
+ len2 = *c;
+ if (p - in > len) {
+ rdns_info ("invalid dns reply");
+ return NULL;
+ }
+ /* This may be compressed, so we need to decompress it */
+ if (len1 & DNS_COMPRESSION_BITS) {
+ len1 = UNCOMPRESS_DNS_OFFSET(p);
+ l1 = rdns_decompress_label (in, &len1, len);
+ if (l1 == NULL) {
+ return NULL;
+ }
+ decompressed ++;
+ l1 ++;
+ p += 2;
+ }
+ else {
+ l1 = ++p;
+ p += len1;
+ }
+ if (len2 & DNS_COMPRESSION_BITS) {
+ len2 = UNCOMPRESS_DNS_OFFSET(c);
+ l2 = rdns_decompress_label (c, &len2, len);
+ if (l2 == NULL) {
+ rdns_info ("invalid DNS pointer, cannot decompress");
+ return NULL;
+ }
+ decompressed ++;
+ l2 ++;
+ c += 2;
+ }
+ else {
+ l2 = ++c;
+ c += len2;
+ }
+ if (len1 != len2) {
+ return NULL;
+ }
+ if (len1 == 0) {
+ break;
+ }
+
+ if (memcmp (l1, l2, len1) != 0) {
+ return NULL;
+ }
+ if (decompressed == 2) {
+ break;
+ }
+ }
+
+ /* p now points to the end of QR section */
+ /* Compare class and type */
+ if (memcmp (p, c, sizeof (uint16_t) * 2) == 0) {
+ req->pos = c - req->packet + sizeof (uint16_t) * 2;
+ return p + sizeof (uint16_t) * 2;
+ }
+ return NULL;
+}
+
+#define MAX_RECURSION_LEVEL 10
+
+bool
+rdns_parse_labels (struct rdns_resolver *resolver,
+ uint8_t *in, char **target, uint8_t **pos, struct rdns_reply *rep,
+ int *remain, bool make_name)
+{
+ uint16_t namelen = 0;
+ uint8_t *p = *pos, *begin = *pos, *l, *t, *end = *pos + *remain, *new_pos = *pos;
+ uint16_t llen;
+ int length = *remain, new_remain = *remain;
+ int ptrs = 0, labels = 0;
+ bool got_compression = false;
+
+ /* First go through labels and calculate name length */
+ while (p - begin < length) {
+ if (ptrs > MAX_RECURSION_LEVEL) {
+ rdns_info ("dns pointers are nested too much");
+ return false;
+ }
+ llen = *p;
+ if (llen == 0) {
+ if (!got_compression) {
+ /* In case of compression we have already decremented the processing position */
+ new_remain -= sizeof (uint8_t);
+ new_pos += sizeof (uint8_t);
+ }
+ break;
+ }
+ else if ((llen & DNS_COMPRESSION_BITS)) {
+ if (end - p > 1) {
+ ptrs ++;
+ llen = UNCOMPRESS_DNS_OFFSET(p);
+ l = rdns_decompress_label (in, &llen, end - in);
+ if (l == NULL) {
+ rdns_info ("invalid DNS pointer");
+ return false;
+ }
+ if (!got_compression) {
+ /* Our label processing is finished actually */
+ new_remain -= sizeof (uint16_t);
+ new_pos += sizeof (uint16_t);
+ got_compression = true;
+ }
+ if (l < in || l > begin + length) {
+ rdns_info ("invalid pointer in DNS packet");
+ return false;
+ }
+ begin = l;
+ length = end - begin;
+ p = l + *l + 1;
+ namelen += *l;
+ labels ++;
+ }
+ else {
+ rdns_info ("DNS packet has incomplete compressed label, input length: %d bytes, remain: %d",
+ *remain, new_remain);
+ return false;
+ }
+ }
+ else {
+ namelen += llen;
+ p += llen + 1;
+ labels ++;
+ if (!got_compression) {
+ new_remain -= llen + 1;
+ new_pos += llen + 1;
+ }
+ }
+ }
+
+ if (!make_name) {
+ goto end;
+ }
+ *target = malloc (namelen + labels + 3);
+ t = (uint8_t *)*target;
+ p = *pos;
+ begin = *pos;
+ length = *remain;
+ /* Now copy labels to name */
+ while (p - begin < length) {
+ llen = *p;
+ if (llen == 0) {
+ break;
+ }
+ else if (llen & DNS_COMPRESSION_BITS) {
+ llen = UNCOMPRESS_DNS_OFFSET(p);
+ l = rdns_decompress_label (in, &llen, end - in);
+ begin = l;
+ length = end - begin;
+ p = l + *l + 1;
+ memcpy (t, l + 1, *l);
+ t += *l;
+ *t ++ = '.';
+ }
+ else {
+ memcpy (t, p + 1, *p);
+ t += *p;
+ *t ++ = '.';
+ p += *p + 1;
+ }
+ }
+ if (t > (uint8_t *)*target) {
+ *(t - 1) = '\0';
+ }
+ else {
+ /* Handle empty labels */
+ **target = '\0';
+ }
+end:
+ *remain = new_remain;
+ *pos = new_pos;
+
+ return true;
+}
+
+#define GET8(x) do {(x) = ((*p)); p += sizeof (uint8_t); *remain -= sizeof (uint8_t); } while(0)
+#define GET16(x) do {(x) = ((*p) << 8) + *(p + 1); p += sizeof (uint16_t); *remain -= sizeof (uint16_t); } while(0)
+#define GET32(x) do {(x) = ((*p) << 24) + ((*(p + 1)) << 16) + ((*(p + 2)) << 8) + *(p + 3); p += sizeof (uint32_t); *remain -= sizeof (uint32_t); } while(0)
+#define SKIP(type) do { p += sizeof(type); *remain -= sizeof(type); } while (0)
+
+int
+rdns_parse_rr (struct rdns_resolver *resolver,
+ uint8_t *in, struct rdns_reply_entry *elt, uint8_t **pos,
+ struct rdns_reply *rep, int *remain)
+{
+ uint8_t *p = *pos, parts;
+ uint16_t type, datalen, txtlen, copied;
+ int32_t ttl;
+ bool parsed = false;
+
+ /* Skip the whole name */
+ if (! rdns_parse_labels (resolver, in, NULL, &p, rep, remain, false)) {
+ rdns_info ("bad RR name");
+ return -1;
+ }
+ if (*remain < (int)sizeof (uint16_t) * 6) {
+ rdns_info ("stripped dns reply: %d bytes remain", *remain);
+ return -1;
+ }
+ GET16 (type);
+ /* Skip class */
+ SKIP (uint16_t);
+ GET32 (ttl);
+ GET16 (datalen);
+ elt->type = type;
+ /* Now p points to RR data */
+ switch (type) {
+ case DNS_T_A:
+ if (!(datalen & 0x3) && datalen <= *remain) {
+ memcpy (&elt->content.a.addr, p, sizeof (struct in_addr));
+ p += datalen;
+ *remain -= datalen;
+ parsed = true;
+ }
+ else {
+ rdns_info ("corrupted A record");
+ return -1;
+ }
+ break;
+ case DNS_T_AAAA:
+ if (datalen == sizeof (struct in6_addr) && datalen <= *remain) {
+ memcpy (&elt->content.aaa.addr, p, sizeof (struct in6_addr));
+ p += datalen;
+ *remain -= datalen;
+ parsed = true;
+ }
+ else {
+ rdns_info ("corrupted AAAA record");
+ return -1;
+ }
+ break;
+ case DNS_T_PTR:
+ if (! rdns_parse_labels (resolver, in, &elt->content.ptr.name, &p,
+ rep, remain, true)) {
+ rdns_info ("invalid labels in PTR record");
+ return -1;
+ }
+ parsed = true;
+ break;
+ case DNS_T_NS:
+ if (! rdns_parse_labels (resolver, in, &elt->content.ns.name, &p,
+ rep, remain, true)) {
+ rdns_info ("invalid labels in NS record");
+ return -1;
+ }
+ parsed = true;
+ break;
+ case DNS_T_SOA:
+ if (! rdns_parse_labels (resolver, in, &elt->content.soa.mname, &p,
+ rep, remain, true)) {
+ rdns_info ("invalid labels in NS record");
+ return -1;
+ }
+ if (! rdns_parse_labels (resolver, in, &elt->content.soa.admin, &p,
+ rep, remain, true)) {
+ rdns_info ("invalid labels in NS record");
+ return -1;
+ }
+ GET32 (elt->content.soa.serial);
+ GET32 (elt->content.soa.refresh);
+ GET32 (elt->content.soa.retry);
+ GET32 (elt->content.soa.expire);
+ GET32 (elt->content.soa.minimum);
+ parsed = true;
+ break;
+ case DNS_T_MX:
+ GET16 (elt->content.mx.priority);
+ if (! rdns_parse_labels (resolver, in, &elt->content.mx.name, &p,
+ rep, remain, true)) {
+ rdns_info ("invalid labels in MX record");
+ return -1;
+ }
+ parsed = true;
+ break;
+ case DNS_T_TXT:
+ case DNS_T_SPF:
+ elt->content.txt.data = malloc (datalen + 1);
+ if (elt->content.txt.data == NULL) {
+ rdns_err ("failed to allocate %d bytes for TXT record", (int)datalen + 1);
+ return -1;
+ }
+ /* Now we should compose data from parts */
+ copied = 0;
+ parts = 0;
+ while (copied + parts < datalen) {
+ txtlen = *p;
+ if (txtlen + copied + parts <= datalen) {
+ parts ++;
+ memcpy (elt->content.txt.data + copied, p + 1, txtlen);
+ copied += txtlen;
+ p += txtlen + 1;
+ *remain -= txtlen + 1;
+ }
+ else {
+ break;
+ }
+ }
+ *(elt->content.txt.data + copied) = '\0';
+ parsed = true;
+ elt->type = RDNS_REQUEST_TXT;
+ break;
+ case DNS_T_SRV:
+ if (p - *pos > (int)(*remain - sizeof (uint16_t) * 3)) {
+ rdns_info ("stripped dns reply while reading SRV record");
+ return -1;
+ }
+ GET16 (elt->content.srv.priority);
+ GET16 (elt->content.srv.weight);
+ GET16 (elt->content.srv.port);
+ if (! rdns_parse_labels (resolver, in, &elt->content.srv.target,
+ &p, rep, remain, true)) {
+ rdns_info ("invalid labels in SRV record");
+ return -1;
+ }
+ parsed = true;
+ break;
+ case DNS_T_TLSA:
+ if (p - *pos > (int)(*remain - sizeof (uint8_t) * 3) || datalen <= 3) {
+ rdns_info ("stripped dns reply while reading TLSA record");
+ return -1;
+ }
+ GET8 (elt->content.tlsa.usage);
+ GET8 (elt->content.tlsa.selector);
+ GET8 (elt->content.tlsa.match_type);
+ datalen -= 3;
+ elt->content.tlsa.data = malloc (datalen);
+ if (elt->content.tlsa.data == NULL) {
+ rdns_err ("failed to allocate %d bytes for TLSA record", (int)datalen + 1);
+ return -1;
+ }
+ elt->content.tlsa.datalen = datalen;
+ memcpy (elt->content.tlsa.data, p, datalen);
+ p += datalen;
+ *remain -= datalen;
+ parsed = true;
+ break;
+ case DNS_T_CNAME:
+ /* Skip cname records */
+ p += datalen;
+ *remain -= datalen;
+ break;
+ default:
+ rdns_debug ("unexpected RR type: %d", type);
+ p += datalen;
+ *remain -= datalen;
+ break;
+ }
+ *pos = p;
+
+ if (parsed) {
+ elt->ttl = ttl;
+ return 1;
+ }
+ return 0;
+}
diff --git a/contrib/librdns/parse.h b/contrib/librdns/parse.h
new file mode 100644
index 000000000..ed8cd7057
--- /dev/null
+++ b/contrib/librdns/parse.h
@@ -0,0 +1,65 @@
+/* Copyright (c) 2014, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef PARSE_H_
+#define PARSE_H_
+
+#include "dns_private.h"
+
+/**
+ * Compare request and reply checking names
+ * @param req request object
+ * @param in incoming packet
+ * @param len length of the incoming packet
+ * @return new position in the incoming packet or NULL if request is not equal to reply
+ */
+uint8_t * rdns_request_reply_cmp (struct rdns_request *req, uint8_t *in, int len);
+
+/**
+ * Parse labels in the packet
+ * @param in incoming packet
+ * @param target target to write the parsed label (out)
+ * @param pos output position in the packet (it/out)
+ * @param rep dns reply
+ * @param remain remaining bytes (in/out)
+ * @param make_name create name or just skip to the next label
+ * @return true if a label has been successfully parsed
+ */
+bool rdns_parse_labels (struct rdns_resolver *resolver,
+ uint8_t *in, char **target,
+ uint8_t **pos, struct rdns_reply *rep,
+ int *remain, bool make_name);
+
+/**
+ * Parse resource record
+ * @param in incoming packet
+ * @param elt new reply entry
+ * @param pos output position in the packet (it/out)
+ * @param rep dns reply
+ * @param remain remaining bytes (in/out)
+ * @return 1 if rr has been parsed, 0 if rr has been skipped and -1 if there was a parsing error
+ */
+int rdns_parse_rr (struct rdns_resolver *resolver,
+ uint8_t *in, struct rdns_reply_entry *elt, uint8_t **pos,
+ struct rdns_reply *rep, int *remain);
+
+#endif /* PARSE_H_ */
diff --git a/contrib/librdns/punycode.c b/contrib/librdns/punycode.c
new file mode 100644
index 000000000..6ce348495
--- /dev/null
+++ b/contrib/librdns/punycode.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2014, Vsevolod Stakhov
+ * Copyright (c) 2004, 2006, 2007, 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "dns_private.h"
+
+/* Punycode utility */
+static unsigned int
+digit (unsigned n)
+{
+ static const char ascii[] = "abcdefghijklmnopqrstuvwxyz0123456789";
+ return ascii[n];
+}
+
+static unsigned int
+adapt (unsigned int delta, unsigned int numpoints, int first)
+{
+ unsigned int k;
+
+ if (first) {
+ delta = delta / damp;
+ }
+ else {
+ delta /= 2;
+ }
+ delta += delta / numpoints;
+ k = 0;
+ while (delta > ((base - t_min) * t_max) / 2) {
+ delta /= base - t_min;
+ k += base;
+ }
+ return k + (((base - t_min + 1) * delta) / (delta + skew));
+}
+
+/**
+ * Convert an UCS4 string to a puny-coded DNS label string suitable
+ * when combined with delimiters and other labels for DNS lookup.
+ *
+ * @param in an UCS4 string to convert
+ * @param in_len the length of in.
+ * @param out the resulting puny-coded string. The string is not NULL
+ * terminated.
+ * @param out_len before processing out_len should be the length of
+ * the out variable, after processing it will be the length of the out
+ * string.
+ *
+ * @return returns 0 on success, an wind error code otherwise
+ */
+
+bool
+rdns_punycode_label_toascii (const uint32_t *in, size_t in_len, char *out,
+ size_t *out_len)
+{
+ unsigned int n = initial_n;
+ unsigned int delta = 0;
+ unsigned int bias = initial_bias;
+ unsigned int h = 0;
+ unsigned int b;
+ unsigned int i;
+ unsigned int o = 0;
+ unsigned int m;
+
+ for (i = 0; i < in_len; ++i) {
+ if (in[i] < 0x80) {
+ ++h;
+ if (o >= *out_len) {
+ return false;
+ }
+ out[o++] = in[i];
+ }
+ }
+ b = h;
+ if (b > 0) {
+ if (o >= *out_len) {
+ return false;
+ }
+ out[o++] = 0x2D;
+ }
+ /* is this string punycoded */
+ if (h < in_len) {
+ if (o + 4 >= *out_len) {
+ return false;
+ }
+ memmove (out + 4, out, o);
+ memcpy (out, "xn--", 4);
+ o += 4;
+ }
+
+ while (h < in_len) {
+ m = (unsigned int) -1;
+ for (i = 0; i < in_len; ++i) {
+
+ if (in[i] < m && in[i] >= n) {
+ m = in[i];
+ }
+ }
+ delta += (m - n) * (h + 1);
+ n = m;
+ for (i = 0; i < in_len; ++i) {
+ if (in[i] < n) {
+ ++delta;
+ }
+ else if (in[i] == n) {
+ unsigned int q = delta;
+ unsigned int k;
+ for (k = base;; k += base) {
+ unsigned int t;
+ if (k <= bias) {
+ t = t_min;
+ }
+ else if (k >= bias + t_max) {
+ t = t_max;
+ }
+ else {
+ t = k - bias;
+ }
+ if (q < t) {
+ break;
+ }
+ if (o >= *out_len) {
+ return -1;
+ }
+ out[o++] = digit (t + ((q - t) % (base - t)));
+ q = (q - t) / (base - t);
+ }
+ if (o >= *out_len) {
+ return -1;
+ }
+ out[o++] = digit (q);
+ /* output */
+ bias = adapt (delta, h + 1, h == b);
+ delta = 0;
+ ++h;
+ }
+ }
+ ++delta;
+ ++n;
+ }
+
+ *out_len = o;
+ return true;
+}
+
+static int
+utf8toutf32 (const unsigned char **pp, uint32_t *out, size_t *remain)
+{
+ const unsigned char *p = *pp;
+ unsigned c = *p;
+ size_t reduce;
+
+ if (c & 0x80) {
+ if ((c & 0xE0) == 0xC0 && *remain >= 2) {
+ const unsigned c2 = *++p;
+ reduce = 2;
+ if ((c2 & 0xC0) == 0x80) {
+ *out = ((c & 0x1F) << 6) | (c2 & 0x3F);
+ }
+ else {
+ return -1;
+ }
+ }
+ else if ((c & 0xF0) == 0xE0 && *remain >= 3) {
+ const unsigned c2 = *++p;
+ if ((c2 & 0xC0) == 0x80) {
+ const unsigned c3 = *++p;
+ reduce = 3;
+ if ((c3 & 0xC0) == 0x80) {
+ *out = ((c & 0x0F) << 12) | ((c2 & 0x3F) << 6)
+ | (c3 & 0x3F);
+ }
+ else {
+ return -1;
+ }
+ }
+ else {
+ return -1;
+ }
+ }
+ else if ((c & 0xF8) == 0xF0 && *remain >= 4) {
+ const unsigned c2 = *++p;
+ if ((c2 & 0xC0) == 0x80) {
+ const unsigned c3 = *++p;
+ if ((c3 & 0xC0) == 0x80) {
+ const unsigned c4 = *++p;
+ reduce = 4;
+ if ((c4 & 0xC0) == 0x80) {
+ *out = ((c & 0x07) << 18) | ((c2 & 0x3F) << 12)
+ | ((c3 & 0x3F) << 6) | (c4 & 0x3F);
+ }
+ else {
+ return -1;
+ }
+ }
+ else {
+ return -1;
+ }
+ }
+ else {
+ return -1;
+ }
+ }
+ else {
+ return -1;
+ }
+ }
+ else {
+ *out = c;
+ reduce = 1;
+ }
+
+ *pp = ++p;
+ *remain -= reduce;
+
+ return 0;
+}
+
+/**
+ * Convert an UTF-8 string to an UCS4 string.
+ *
+ * @param in an UTF-8 string to convert.
+ * @param out the resulting UCS4 string
+ * @param out_len before processing out_len should be the length of
+ * the out variable, after processing it will be the length of the out
+ * string.
+ *
+ * @return returns 0 on success, an -1 otherwise
+ * @ingroup wind
+ */
+
+int
+rdns_utf8_to_ucs4 (const char *in, size_t in_len, uint32_t **out, size_t *out_len)
+{
+ const unsigned char *p;
+ size_t remain = in_len, olen = 0;
+ int ret;
+ uint32_t *res;
+
+ p = (const unsigned char *)in;
+ while (remain > 0) {
+ uint32_t u;
+
+ ret = utf8toutf32 (&p, &u, &remain);
+ if (ret != 0) {
+ return ret;
+ }
+
+ olen ++;
+ }
+ res = malloc (olen * sizeof (uint32_t));
+ if (res == NULL) {
+ return -1;
+ }
+
+ p = (const unsigned char *)in;
+ remain = in_len;
+ olen = 0;
+ while (remain > 0) {
+ uint32_t u;
+
+ (void)utf8toutf32 (&p, &u, &remain);
+ res[olen++] = u;
+ }
+
+ *out_len = olen;
+ *out = res;
+ return 0;
+}
diff --git a/contrib/librdns/punycode.h b/contrib/librdns/punycode.h
new file mode 100644
index 000000000..300fb92a6
--- /dev/null
+++ b/contrib/librdns/punycode.h
@@ -0,0 +1,59 @@
+/* Copyright (c) 2014, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef PUNYCODE_H_
+#define PUNYCODE_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+/**
+ * Convert an UCS4 string to a puny-coded DNS label string suitable
+ * when combined with delimiters and other labels for DNS lookup.
+ *
+ * @param in an UCS4 string to convert
+ * @param in_len the length of in.
+ * @param out the resulting puny-coded string. The string is not NULL
+ * terminated.
+ * @param out_len before processing out_len should be the length of
+ * the out variable, after processing it will be the length of the out
+ * string.
+ *
+ * @return returns 0 on success, an wind error code otherwise
+ */
+bool rdns_punycode_label_toascii (const uint32_t *in, size_t in_len, char *out, size_t *out_len);
+/**
+ * Convert an UTF-8 string to an UCS4 string.
+ *
+ * @param in an UTF-8 string to convert.
+ * @param out the resulting UCS4 string
+ * @param out_len before processing out_len should be the length of
+ * the out variable, after processing it will be the length of the out
+ * string.
+ *
+ * @return returns 0 on success, an -1 otherwise
+ * @ingroup wind
+ */
+
+int rdns_utf8_to_ucs4 (const char *in, size_t in_len, uint32_t **out, size_t *out_len);
+
+#endif /* PUNYCODE_H_ */
diff --git a/contrib/librdns/rdns.h b/contrib/librdns/rdns.h
new file mode 100644
index 000000000..a1cfad4d3
--- /dev/null
+++ b/contrib/librdns/rdns.h
@@ -0,0 +1,372 @@
+/*
+ * Copyright (c) 2013-2014, Vsevolod Stakhov
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef RDNS_H
+#define RDNS_H
+
+#include <sys/types.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct rdns_reply;
+struct rdns_request;
+struct rdns_io_channel;
+
+typedef void (*dns_callback_type) (struct rdns_reply *reply, void *arg);
+
+enum rdns_request_type {
+ RDNS_REQUEST_A = 1,
+ RDNS_REQUEST_NS = 2,
+ RDNS_REQUEST_SOA = 6,
+ RDNS_REQUEST_PTR = 12,
+ RDNS_REQUEST_MX = 15,
+ RDNS_REQUEST_TXT = 16,
+ RDNS_REQUEST_SRV = 33,
+ RDNS_REQUEST_SPF = 99,
+ RDNS_REQUEST_AAAA = 28,
+ RDNS_REQUEST_TLSA = 52,
+ RDNS_REQUEST_ANY = 255
+};
+
+union rdns_reply_element_un {
+ struct {
+ struct in_addr addr;
+ } a;
+ struct {
+ struct in6_addr addr;
+ } aaa;
+ struct {
+ char *name;
+ } ptr;
+ struct {
+ char *name;
+ } ns;
+ struct {
+ char *name;
+ uint16_t priority;
+ } mx;
+ struct {
+ char *data;
+ } txt;
+ struct {
+ uint16_t priority;
+ uint16_t weight;
+ uint16_t port;
+ char *target;
+ } srv;
+ struct {
+ char *mname;
+ char *admin;
+ uint32_t serial;
+ int32_t refresh;
+ int32_t retry;
+ int32_t expire;
+ uint32_t minimum;
+ } soa;
+ struct {
+ uint8_t usage;
+ uint8_t selector;
+ uint8_t match_type;
+ uint16_t datalen;
+ uint8_t *data;
+ } tlsa;
+};
+
+struct rdns_reply_entry {
+ union rdns_reply_element_un content;
+ uint16_t type;
+ int32_t ttl;
+ struct rdns_reply_entry *prev, *next;
+};
+
+
+enum dns_rcode {
+ RDNS_RC_NOERROR = 0,
+ RDNS_RC_FORMERR = 1,
+ RDNS_RC_SERVFAIL = 2,
+ RDNS_RC_NXDOMAIN = 3,
+ RDNS_RC_NOTIMP = 4,
+ RDNS_RC_REFUSED = 5,
+ RDNS_RC_YXDOMAIN = 6,
+ RDNS_RC_YXRRSET = 7,
+ RDNS_RC_NXRRSET = 8,
+ RDNS_RC_NOTAUTH = 9,
+ RDNS_RC_NOTZONE = 10,
+ RDNS_RC_TIMEOUT = 11,
+ RDNS_RC_NETERR = 12,
+ RDNS_RC_NOREC = 13
+};
+
+struct rdns_reply {
+ struct rdns_request *request;
+ struct rdns_resolver *resolver;
+ struct rdns_reply_entry *entries;
+ const char *requested_name;
+ enum dns_rcode code;
+};
+
+typedef void (*rdns_periodic_callback)(void *user_data);
+
+struct rdns_async_context {
+ void *data;
+ void* (*add_read)(void *priv_data, int fd, void *user_data);
+ void (*del_read)(void *priv_data, void *ev_data);
+ void* (*add_write)(void *priv_data, int fd, void *user_data);
+ void (*del_write)(void *priv_data, void *ev_data);
+ void* (*add_timer)(void *priv_data, double after, void *user_data);
+ void (*repeat_timer)(void *priv_data, void *ev_data);
+ void (*del_timer)(void *priv_data, void *ev_data);
+ void* (*add_periodic)(void *priv_data, double after,
+ rdns_periodic_callback cb, void *user_data);
+ void (*del_periodic)(void *priv_data, void *ev_data);
+ void (*cleanup)(void *priv_data);
+};
+
+/**
+ * Type of rdns plugin
+ */
+enum rdns_plugin_type {
+ RDNS_PLUGIN_CURVE = 0//!< use the specified plugin instead of send/recv functions
+};
+
+typedef ssize_t (*rdns_network_send_callback) (struct rdns_request *req, void *plugin_data);
+typedef ssize_t (*rdns_network_recv_callback) (struct rdns_io_channel *ioc, void *buf,
+ size_t len, void *plugin_data, struct rdns_request **req_out);
+typedef void (*rdns_network_finish_callback) (struct rdns_request *req, void *plugin_data);
+typedef void (*rdns_plugin_dtor_callback) (struct rdns_resolver *resolver, void *plugin_data);
+
+struct rdns_plugin {
+ enum rdns_plugin_type type;
+ union {
+ struct {
+ rdns_network_send_callback send_cb;
+ rdns_network_recv_callback recv_cb;
+ rdns_network_finish_callback finish_cb;
+ } curve_plugin;
+ } cb;
+ rdns_plugin_dtor_callback dtor;
+ void *data;
+};
+
+/*
+ * RDNS logger types
+ */
+/*
+ * These types are somehow compatible with glib
+ */
+enum rdns_log_level {
+ RDNS_LOG_ERROR = 1 << 3,
+ RDNS_LOG_WARNING = 1 << 4,
+ RDNS_LOG_INFO = 1 << 6,
+ RDNS_LOG_DEBUG = 1 << 7
+};
+typedef void (*rdns_log_function) (
+ void *log_data, //!< opaque data pointer
+ enum rdns_log_level level, //!< level of message
+ const char *function, //!< calling function
+ const char *format, //!< format
+ va_list args //!< set of arguments
+ );
+
+struct rdns_request_name {
+ char *name;
+ enum rdns_request_type type;
+ unsigned int len;
+};
+
+/*
+ * RDNS API
+ */
+
+/**
+ * Create DNS resolver structure
+ */
+struct rdns_resolver *rdns_resolver_new (void);
+
+/**
+ * Bind resolver to specified async context
+ * @param ctx
+ */
+void rdns_resolver_async_bind (struct rdns_resolver *resolver,
+ struct rdns_async_context *ctx);
+
+/**
+ * Add new DNS server definition to the resolver
+ * @param resolver resolver object
+ * @param name name of DNS server (should be ipv4 or ipv6 address)
+ * @param priority priority (can be 0 for fair round-robin)
+ * @param io_cnt a number of sockets that are simultaneously opened to this server
+ * @return true if a server has been added to resolver
+ */
+bool rdns_resolver_add_server (struct rdns_resolver *resolver,
+ const char *name, unsigned int port,
+ int priority, unsigned int io_cnt);
+
+
+/**
+ * Load nameservers definition from resolv.conf file
+ * @param resolver resolver object
+ * @param path path to resolv.conf file (/etc/resolv.conf typically)
+ * @return true if resolv.conf has been parsed
+ */
+bool rdns_resolver_parse_resolv_conf (struct rdns_resolver *resolver,
+ const char *path);
+
+/**
+ * Set an external logger function to log messages from the resolver
+ * @param resolver resolver object
+ * @param logger logger callback
+ * @param log_data opaque data
+ */
+void rdns_resolver_set_logger (struct rdns_resolver *resolver,
+ rdns_log_function logger, void *log_data);
+
+/**
+ * Set log level for an internal logger (stderr one)
+ * @param resolver resolver object
+ * @param level desired log level
+ */
+void rdns_resolver_set_log_level (struct rdns_resolver *resolver,
+ enum rdns_log_level level);
+
+
+/**
+ * Set maximum number of dns requests to be sent to a socket to be refreshed
+ * @param resolver resolver object
+ * @param max_ioc_uses unsigned count of socket usage limit
+ * @param check_time specifies how often to check for sockets and refresh them
+ */
+void rdns_resolver_set_max_io_uses (struct rdns_resolver *resolver,
+ uint64_t max_ioc_uses, double check_time);
+
+/**
+ * Register new plugin for rdns resolver
+ * @param resolver
+ * @param plugin
+ */
+void rdns_resolver_register_plugin (struct rdns_resolver *resolver,
+ struct rdns_plugin *plugin);
+
+/**
+ * Init DNS resolver
+ * @param resolver
+ * @return
+ */
+bool rdns_resolver_init (struct rdns_resolver *resolver);
+
+/**
+ * Decrease refcount for a resolver and free it if refcount is 0
+ * @param resolver
+ */
+void rdns_resolver_release (struct rdns_resolver *resolver);
+
+/**
+ * Make a DNS request
+ * @param resolver resolver object
+ * @param cb callback to call on resolve completing
+ * @param ud user data for callback
+ * @param timeout timeout in seconds
+ * @param repeats how much time to retransmit query
+ * @param queries how much RR queries to send
+ * @param ... -> queries in format: <query_type>[,type_argument[,type_argument...]]
+ * @return opaque request object or NULL
+ */
+struct rdns_request* rdns_make_request_full (
+ struct rdns_resolver *resolver,
+ dns_callback_type cb,
+ void *cbdata,
+ double timeout,
+ unsigned int repeats,
+ unsigned int queries,
+ ...
+ );
+
+/**
+ * Get textual presentation of DNS error code
+ */
+const char *rdns_strerror (enum dns_rcode rcode);
+
+/**
+ * Get textual presentation of DNS request type
+ */
+const char *rdns_strtype (enum rdns_request_type type);
+
+/**
+ * Increase refcount for a request
+ * @param req
+ * @return
+ */
+struct rdns_request* rdns_request_retain (struct rdns_request *req);
+
+/**
+ * Decrease refcount for a request and free it if refcount is 0
+ * @param req
+ */
+void rdns_request_release (struct rdns_request *req);
+
+/**
+ * Check whether a request contains `type` request
+ * @param req request object
+ * @param type check for a specified type
+ * @return true if `type` has been requested
+ */
+bool rdns_request_has_type (struct rdns_request *req, enum rdns_request_type type);
+
+/**
+ * Return requested name for a request
+ * @param req request object
+ * @return requested name as it was passed to `rdns_make_request`
+ */
+const struct rdns_request_name* rdns_request_get_name (struct rdns_request *req,
+ unsigned int *count);
+
+/**
+ * Return PTR string for a request (ipv4 or ipv6) addresses
+ * @param str string representation of IP address
+ * @return name to resolve or NULL if `str` is not an IP address; caller must free result when it is unused
+ */
+char * rdns_generate_ptr_from_str (const char *str);
+
+/*
+ * Private functions used by async libraries as callbacks
+ */
+
+void rdns_process_read (int fd, void *arg);
+void rdns_process_timer (void *arg);
+void rdns_process_retransmit (int fd, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/contrib/librdns/rdns_curve.h b/contrib/librdns/rdns_curve.h
new file mode 100644
index 000000000..365e91bb1
--- /dev/null
+++ b/contrib/librdns/rdns_curve.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2014, Vsevolod Stakhov
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef RDNS_CURVE_H_
+#define RDNS_CURVE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct rdns_curve_ctx;
+
+/**
+ * Create new dnscurve ctx
+ * @return
+ */
+struct rdns_curve_ctx* rdns_curve_ctx_new (double rekey_interval);
+
+/**
+ * Add key for server `name`
+ * @param ctx curve context
+ * @param name name of server (ip address)
+ * @param pubkey pubkey bytes (must be `RDSN_CURVE_PUBKEY_LEN`)
+ */
+void rdns_curve_ctx_add_key (struct rdns_curve_ctx *ctx,
+ const char *name, const unsigned char *pubkey);
+
+/**
+ * Destroy curve context
+ * @param ctx
+ */
+void rdns_curve_ctx_destroy (struct rdns_curve_ctx *ctx);
+
+
+/**
+ * Register DNSCurve plugin (libsodium should be enabled for this)
+ * @param resolver
+ * @param ctx
+ */
+void rdns_curve_register_plugin (struct rdns_resolver *resolver,
+ struct rdns_curve_ctx *ctx);
+
+/**
+ * Create DNSCurve key from the base16 encoded string
+ * @param hex input hex (must be NULL terminated)
+ * @return a key or NULL (not NULL terminated)
+ */
+unsigned char * rdns_curve_key_from_hex (const char *hex);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RDNS_CURVE_H_ */
diff --git a/contrib/librdns/rdns_ev.h b/contrib/librdns/rdns_ev.h
new file mode 100644
index 000000000..1b3554bc1
--- /dev/null
+++ b/contrib/librdns/rdns_ev.h
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2014, Vsevolod Stakhov
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef RDNS_EV_H_
+#define RDNS_EV_H_
+
+#include <ev.h>
+#include <stdlib.h>
+#include <string.h>
+#include "rdns.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static void* rdns_libev_add_read (void *priv_data, int fd, void *user_data);
+static void rdns_libev_del_read(void *priv_data, void *ev_data);
+static void* rdns_libev_add_write (void *priv_data, int fd, void *user_data);
+static void rdns_libev_del_write (void *priv_data, void *ev_data);
+static void* rdns_libev_add_timer (void *priv_data, double after, void *user_data);
+static void* rdns_libev_add_periodic (void *priv_data, double after,
+ rdns_periodic_callback cb, void *user_data);
+static void rdns_libev_del_periodic (void *priv_data, void *ev_data);
+static void rdns_libev_repeat_timer (void *priv_data, void *ev_data);
+static void rdns_libev_del_timer (void *priv_data, void *ev_data);
+
+struct rdns_ev_periodic_cbdata {
+ ev_timer *ev;
+ rdns_periodic_callback cb;
+ void *cbdata;
+};
+
+static void
+rdns_bind_libev (struct rdns_resolver *resolver, struct ev_loop *loop)
+{
+ static struct rdns_async_context ev_ctx = {
+ .data = NULL,
+ .add_read = rdns_libev_add_read,
+ .del_read = rdns_libev_del_read,
+ .add_write = rdns_libev_add_write,
+ .del_write = rdns_libev_del_write,
+ .add_timer = rdns_libev_add_timer,
+ .repeat_timer = rdns_libev_repeat_timer,
+ .del_timer = rdns_libev_del_timer,
+ .add_periodic = rdns_libev_add_periodic,
+ .del_periodic = rdns_libev_del_periodic,
+ .cleanup = NULL
+ }, *nctx;
+ void *ptr;
+
+ /* XXX: never got freed */
+ ptr = malloc (sizeof (struct rdns_async_context));
+ if (ptr != NULL) {
+ nctx = (struct rdns_async_context *)ptr;
+ memcpy (ptr, (void *)&ev_ctx, sizeof (struct rdns_async_context));
+ nctx->data = (void*)loop;
+ rdns_resolver_async_bind (resolver, nctx);
+ }
+}
+
+static void
+rdns_libev_read_event (struct ev_loop *loop, ev_io *ev, int revents)
+{
+ rdns_process_read (ev->fd, ev->data);
+}
+
+static void
+rdns_libev_write_event (struct ev_loop *loop, ev_io *ev, int revents)
+{
+ rdns_process_retransmit (ev->fd, ev->data);
+}
+
+static void
+rdns_libev_timer_event (struct ev_loop *loop, ev_timer *ev, int revents)
+{
+ rdns_process_timer (ev->data);
+}
+
+static void
+rdns_libev_periodic_event (struct ev_loop *loop, ev_timer *ev, int revents)
+{
+ struct rdns_ev_periodic_cbdata *cbdata = (struct rdns_ev_periodic_cbdata *)
+ ev->data;
+ cbdata->cb (cbdata->cbdata);
+}
+
+static void*
+rdns_libev_add_read (void *priv_data, int fd, void *user_data)
+{
+ ev_io *ev;
+ void *ptr;
+
+ ptr = malloc (sizeof (ev_io));
+ if (ptr != NULL) {
+ ev = (ev_io *)ptr;
+ ev_io_init (ev, rdns_libev_read_event, fd, EV_READ);
+ ev->data = user_data;
+ ev_io_start ((struct ev_loop *)priv_data, ev);
+ }
+ return ptr;
+}
+
+static void
+rdns_libev_del_read (void *priv_data, void *ev_data)
+{
+ ev_io *ev = (ev_io*)ev_data;
+ if (ev != NULL) {
+ ev_io_stop ((struct ev_loop *)priv_data, ev);
+ free ((void *)ev);
+ }
+}
+static void*
+rdns_libev_add_write (void *priv_data, int fd, void *user_data)
+{
+ ev_io *ev;
+
+ ev = (ev_io *)malloc (sizeof (ev_io));
+ if (ev != NULL) {
+ ev_io_init (ev, rdns_libev_write_event, fd, EV_WRITE);
+ ev->data = user_data;
+ ev_io_start ((struct ev_loop *)priv_data, ev);
+ }
+ return (void *)ev;
+}
+
+static void
+rdns_libev_del_write (void *priv_data, void *ev_data)
+{
+ ev_io *ev = (ev_io *)ev_data;
+ if (ev != NULL) {
+ ev_io_stop ((struct ev_loop *)priv_data, ev);
+ free ((void *)ev);
+ }
+}
+
+static void*
+rdns_libev_add_timer (void *priv_data, double after, void *user_data)
+{
+ ev_timer *ev;
+ ev = (ev_timer *)malloc (sizeof (ev_timer));
+ if (ev != NULL) {
+ ev_timer_init (ev, rdns_libev_timer_event, after, after);
+ ev->data = user_data;
+ ev_timer_start ((struct ev_loop *)priv_data, ev);
+ }
+ return (void *)ev;
+}
+
+static void*
+rdns_libev_add_periodic (void *priv_data, double after,
+ rdns_periodic_callback cb, void *user_data)
+{
+ ev_timer *ev;
+ struct rdns_ev_periodic_cbdata *cbdata = NULL;
+
+ ev = (ev_timer *)malloc (sizeof (ev_timer));
+ if (ev != NULL) {
+ cbdata = (struct rdns_ev_periodic_cbdata *)
+ malloc (sizeof (struct rdns_ev_periodic_cbdata));
+ if (cbdata != NULL) {
+ cbdata->cb = cb;
+ cbdata->cbdata = user_data;
+ cbdata->ev = ev;
+ ev_timer_init (ev, rdns_libev_periodic_event, after, after);
+ ev->data = cbdata;
+ ev_timer_start ((struct ev_loop *)priv_data, ev);
+ }
+ else {
+ free ((void *)ev);
+ return NULL;
+ }
+ }
+ return (void *)cbdata;
+}
+
+static void
+rdns_libev_del_periodic (void *priv_data, void *ev_data)
+{
+ struct rdns_ev_periodic_cbdata *cbdata = (struct rdns_ev_periodic_cbdata *)
+ ev_data;
+ if (cbdata != NULL) {
+ ev_timer_stop ((struct ev_loop *)priv_data, (ev_timer *)cbdata->ev);
+ free ((void *)cbdata->ev);
+ free ((void *)cbdata);
+ }
+}
+
+static void
+rdns_libev_repeat_timer (void *priv_data, void *ev_data)
+{
+ ev_timer *ev = (ev_timer *)ev_data;
+ if (ev != NULL) {
+ ev_timer_again ((struct ev_loop *)priv_data, ev);
+ }
+}
+
+static void
+rdns_libev_del_timer (void *priv_data, void *ev_data)
+{
+ ev_timer *ev = (ev_timer *)ev_data;
+ if (ev != NULL) {
+ ev_timer_stop ((struct ev_loop *)priv_data, ev);
+ free ((void *)ev);
+ }
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RDNS_EV_H_ */
diff --git a/contrib/librdns/rdns_event.h b/contrib/librdns/rdns_event.h
new file mode 100644
index 000000000..b3fc64aed
--- /dev/null
+++ b/contrib/librdns/rdns_event.h
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2014, Vsevolod Stakhov
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef RDNS_EVENT_H_
+#define RDNS_EVENT_H_
+
+#include <event.h>
+#include <stdlib.h>
+#include <string.h>
+#include "rdns.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static void* rdns_libevent_add_read (void *priv_data, int fd, void *user_data);
+static void rdns_libevent_del_read(void *priv_data, void *ev_data);
+static void* rdns_libevent_add_write (void *priv_data, int fd, void *user_data);
+static void rdns_libevent_del_write (void *priv_data, void *ev_data);
+static void* rdns_libevent_add_timer (void *priv_data, double after, void *user_data);
+static void* rdns_libevent_add_periodic (void *priv_data, double after,
+ rdns_periodic_callback cb, void *user_data);
+static void rdns_libevent_del_periodic (void *priv_data, void *ev_data);
+static void rdns_libevent_repeat_timer (void *priv_data, void *ev_data);
+static void rdns_libevent_del_timer (void *priv_data, void *ev_data);
+
+struct rdns_event_periodic_cbdata {
+ struct event *ev;
+ rdns_periodic_callback cb;
+ void *cbdata;
+};
+
+static void
+rdns_bind_libevent (struct rdns_resolver *resolver, struct event_base *ev_base)
+{
+ struct rdns_async_context ev_ctx = {
+ .add_read = rdns_libevent_add_read,
+ .del_read = rdns_libevent_del_read,
+ .add_write = rdns_libevent_add_write,
+ .del_write = rdns_libevent_del_write,
+ .add_timer = rdns_libevent_add_timer,
+ .add_periodic = rdns_libevent_add_periodic,
+ .del_periodic = rdns_libevent_del_periodic,
+ .repeat_timer = rdns_libevent_repeat_timer,
+ .del_timer = rdns_libevent_del_timer,
+ .cleanup = NULL
+ }, *nctx;
+
+ /* XXX: never got freed */
+ nctx = malloc (sizeof (struct rdns_async_context));
+ if (nctx != NULL) {
+ memcpy (nctx, &ev_ctx, sizeof (struct rdns_async_context));
+ nctx->data = ev_base;
+ }
+ rdns_resolver_async_bind (resolver, nctx);
+}
+
+static void
+rdns_libevent_read_event (int fd, short what, void *ud)
+{
+ rdns_process_read (fd, ud);
+}
+
+static void
+rdns_libevent_write_event (int fd, short what, void *ud)
+{
+ rdns_process_retransmit (fd, ud);
+}
+
+static void
+rdns_libevent_timer_event (int fd, short what, void *ud)
+{
+ rdns_process_timer (ud);
+}
+
+static void
+rdns_libevent_periodic_event (int fd, short what, void *ud)
+{
+ struct rdns_event_periodic_cbdata *cbdata = ud;
+ cbdata->cb (cbdata->cbdata);
+}
+
+static void*
+rdns_libevent_add_read (void *priv_data, int fd, void *user_data)
+{
+ struct event *ev;
+ ev = malloc (sizeof (struct event));
+ if (ev != NULL) {
+ event_set (ev, fd, EV_READ | EV_PERSIST, rdns_libevent_read_event, user_data);
+ event_base_set (priv_data, ev);
+ event_add (ev, NULL);
+ }
+ return ev;
+}
+
+static void
+rdns_libevent_del_read(void *priv_data, void *ev_data)
+{
+ struct event *ev = ev_data;
+ if (ev != NULL) {
+ event_del (ev);
+ free (ev);
+ }
+}
+static void*
+rdns_libevent_add_write (void *priv_data, int fd, void *user_data)
+{
+ struct event *ev;
+ ev = malloc (sizeof (struct event));
+ if (ev != NULL) {
+ event_set (ev, fd, EV_WRITE | EV_PERSIST,
+ rdns_libevent_write_event, user_data);
+ event_base_set (priv_data, ev);
+ event_add (ev, NULL);
+ }
+ return ev;
+}
+
+static void
+rdns_libevent_del_write (void *priv_data, void *ev_data)
+{
+ struct event *ev = ev_data;
+ if (ev != NULL) {
+ event_del (ev);
+ free (ev);
+ }
+}
+
+#define rdns_event_double_to_tv(dbl, tv) do { \
+ (tv)->tv_sec = (int)(dbl); \
+ (tv)->tv_usec = ((dbl) - (int)(dbl))*1000*1000; \
+} while(0)
+
+static void*
+rdns_libevent_add_timer (void *priv_data, double after, void *user_data)
+{
+ struct event *ev;
+ struct timeval tv;
+ ev = malloc (sizeof (struct event));
+ if (ev != NULL) {
+ rdns_event_double_to_tv (after, &tv);
+ event_set (ev, -1, EV_TIMEOUT|EV_PERSIST, rdns_libevent_timer_event, user_data);
+ event_base_set (priv_data, ev);
+ event_add (ev, &tv);
+ }
+ return ev;
+}
+
+static void*
+rdns_libevent_add_periodic (void *priv_data, double after,
+ rdns_periodic_callback cb, void *user_data)
+{
+ struct event *ev;
+ struct timeval tv;
+ struct rdns_event_periodic_cbdata *cbdata = NULL;
+
+ ev = malloc (sizeof (struct event));
+ if (ev != NULL) {
+ cbdata = malloc (sizeof (struct rdns_event_periodic_cbdata));
+ if (cbdata != NULL) {
+ rdns_event_double_to_tv (after, &tv);
+ cbdata->cb = cb;
+ cbdata->cbdata = user_data;
+ cbdata->ev = ev;
+ event_set (ev, -1, EV_TIMEOUT|EV_PERSIST, rdns_libevent_periodic_event, cbdata);
+ event_base_set (priv_data, ev);
+ event_add (ev, &tv);
+ }
+ else {
+ free (ev);
+ return NULL;
+ }
+ }
+ return cbdata;
+}
+
+static void
+rdns_libevent_del_periodic (void *priv_data, void *ev_data)
+{
+ struct rdns_event_periodic_cbdata *cbdata = ev_data;
+ if (cbdata != NULL) {
+ event_del (cbdata->ev);
+ free (cbdata->ev);
+ free (cbdata);
+ }
+}
+
+static void
+rdns_libevent_repeat_timer (void *priv_data, void *ev_data)
+{
+ /* XXX: libevent hides timeval, so timeouts are persistent here */
+}
+
+#undef rdns_event_double_to_tv
+
+static void
+rdns_libevent_del_timer (void *priv_data, void *ev_data)
+{
+ struct event *ev = ev_data;
+ if (ev != NULL) {
+ event_del (ev);
+ free (ev);
+ }
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RDNS_EV_H_ */
diff --git a/contrib/librdns/ref.h b/contrib/librdns/ref.h
new file mode 100644
index 000000000..a8016b16d
--- /dev/null
+++ b/contrib/librdns/ref.h
@@ -0,0 +1,71 @@
+/* Copyright (c) 2014, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef REF_H_
+#define REF_H_
+
+/**
+ * @file ref.h
+ * A set of macros to handle refcounts
+ */
+
+typedef void (*ref_dtor_cb_t)(void *data);
+
+typedef struct ref_entry_s {
+ unsigned int refcount;
+ ref_dtor_cb_t dtor;
+} ref_entry_t;
+
+#define REF_INIT(obj, dtor_cb) do { \
+ (obj)->ref.refcount = 0; \
+ (obj)->ref.dtor = (ref_dtor_cb_t)(dtor_cb); \
+} while (0)
+
+#define REF_INIT_RETAIN(obj, dtor_cb) do { \
+ (obj)->ref.refcount = 1; \
+ (obj)->ref.dtor = (ref_dtor_cb_t)(dtor_cb); \
+} while (0)
+
+#ifdef HAVE_ATOMIC_BUILTINS
+#define REF_RETAIN(obj) do { \
+ __sync_add_and_fetch (&(obj)->ref.refcount, 1); \
+} while (0)
+
+#define REF_RELEASE(obj) do { \
+ unsigned int rc = __sync_sub_and_fetch (&(obj)->ref.refcount, 1); \
+ if (rc == 0 && (obj)->ref.dtor) { \
+ (obj)->ref.dtor (obj); \
+ } \
+} while (0)
+#else
+#define REF_RETAIN(obj) do { \
+ (obj)->ref.refcount ++; \
+} while (0)
+
+#define REF_RELEASE(obj) do { \
+ if (--(obj)->ref.refcount == 0 && (obj)->ref.dtor) { \
+ (obj)->ref.dtor (obj); \
+ } \
+} while (0)
+#endif
+
+#endif /* REF_H_ */
diff --git a/contrib/librdns/resolver.c b/contrib/librdns/resolver.c
new file mode 100644
index 000000000..9741f7bf9
--- /dev/null
+++ b/contrib/librdns/resolver.c
@@ -0,0 +1,782 @@
+/*
+ * Copyright (c) 2014, Vsevolod Stakhov
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#include "rdns.h"
+#include "dns_private.h"
+#include "ottery.h"
+#include "util.h"
+#include "packet.h"
+#include "parse.h"
+#include "logger.h"
+#include "compression.h"
+
+static int
+rdns_send_request (struct rdns_request *req, int fd, bool new_req)
+{
+ int r;
+ struct rdns_server *serv = req->io->srv;
+ struct rdns_resolver *resolver = req->resolver;
+ struct rdns_request *tmp;
+ struct dns_header *header;
+ const int max_id_cycles = 32;
+
+ /* Find ID collision */
+ if (new_req) {
+ r = 0;
+ HASH_FIND_INT (req->io->requests, &req->id, tmp);
+ while (tmp != NULL) {
+ /* Check for unique id */
+ header = (struct dns_header *)req->packet;
+ header->qid = rdns_permutor_generate_id ();
+ req->id = header->qid;
+ if (++r > max_id_cycles) {
+ return -1;
+ }
+ HASH_FIND_INT (req->io->requests, &req->id, tmp);
+ }
+ }
+
+ if (resolver->curve_plugin == NULL) {
+ r = send (fd, req->packet, req->pos, 0);
+ }
+ else {
+ r = resolver->curve_plugin->cb.curve_plugin.send_cb (req,
+ resolver->curve_plugin->data);
+ }
+ if (r == -1) {
+ if (errno == EAGAIN || errno == EINTR) {
+ if (new_req) {
+ /* Write when socket is ready */
+ HASH_ADD_INT (req->io->requests, id, req);
+ req->async_event = resolver->async->add_write (resolver->async->data,
+ fd, req);
+ }
+ /*
+ * If request is already processed then the calling function
+ * should take care about events processing
+ */
+ return 0;
+ }
+ else {
+ rdns_debug ("send failed: %s for server %s", strerror (errno), serv->name);
+ return -1;
+ }
+ }
+
+ if (new_req) {
+ /* Add request to hash table */
+ HASH_ADD_INT (req->io->requests, id, req);
+ /* Fill timeout */
+ req->async_event = resolver->async->add_timer (resolver->async->data,
+ req->timeout, req);
+ req->state = RDNS_REQUEST_SENT;
+ }
+
+ return 1;
+}
+
+
+static struct rdns_reply *
+rdns_make_reply (struct rdns_request *req, enum dns_rcode rcode)
+{
+ struct rdns_reply *rep;
+
+ rep = malloc (sizeof (struct rdns_reply));
+ if (rep != NULL) {
+ rep->request = req;
+ rep->resolver = req->resolver;
+ rep->entries = NULL;
+ rep->code = rcode;
+ req->reply = rep;
+ }
+
+ return rep;
+}
+
+static struct rdns_request *
+rdns_find_dns_request (uint8_t *in, struct rdns_io_channel *ioc)
+{
+ struct dns_header *header = (struct dns_header *)in;
+ struct rdns_request *req;
+ int id;
+ struct rdns_resolver *resolver = ioc->resolver;
+
+ id = header->qid;
+ HASH_FIND_INT (ioc->requests, &id, req);
+ if (req == NULL) {
+ /* No such requests found */
+ rdns_debug ("DNS request with id %d has not been found for IO channel", (int)id);
+ }
+
+ return req;
+}
+
+static bool
+rdns_parse_reply (uint8_t *in, int r, struct rdns_request *req,
+ struct rdns_reply **_rep)
+{
+ struct dns_header *header = (struct dns_header *)in;
+ struct rdns_reply *rep;
+ struct rdns_reply_entry *elt;
+ uint8_t *pos, *npos;
+ struct rdns_resolver *resolver = req->resolver;
+ uint16_t qdcount;
+ int type;
+ bool found = false;
+
+ int i, t;
+
+ /* First check header fields */
+ if (header->qr == 0) {
+ rdns_info ("got request while waiting for reply");
+ return false;
+ }
+
+ qdcount = ntohs (header->qdcount);
+
+ if (qdcount != req->qcount) {
+ rdns_info ("request has %d queries, reply has %d queries", (int)req->qcount, (int)header->qdcount);
+ return false;
+ }
+
+ /*
+ * Now we have request and query data is now at the end of header, so compare
+ * request QR section and reply QR section
+ */
+ req->pos = sizeof (struct dns_header);
+ pos = in + sizeof (struct dns_header);
+ t = r - sizeof (struct dns_header);
+ for (i = 0; i < (int)qdcount; i ++) {
+ if ((npos = rdns_request_reply_cmp (req, pos,t)) == NULL) {
+ rdns_info ("DNS request with id %d is for different query, ignoring", (int)req->id);
+ return false;
+ }
+ t -= npos - pos;
+ pos = npos;
+ }
+ /*
+ * Now pos is in answer section, so we should extract data and form reply
+ */
+ rep = rdns_make_reply (req, header->rcode);
+
+ if (rep == NULL) {
+ rdns_warn ("Cannot allocate memory for reply");
+ return false;
+ }
+
+ type = req->requested_names[0].type;
+
+ if (rep->code == RDNS_RC_NOERROR) {
+ r -= pos - in;
+ /* Extract RR records */
+ for (i = 0; i < ntohs (header->ancount); i ++) {
+ elt = malloc (sizeof (struct rdns_reply_entry));
+ t = rdns_parse_rr (resolver, in, elt, &pos, rep, &r);
+ if (t == -1) {
+ free (elt);
+ rdns_debug ("incomplete reply");
+ break;
+ }
+ else if (t == 1) {
+ DL_APPEND (rep->entries, elt);
+ if (elt->type == type) {
+ found = true;
+ }
+ }
+ else {
+ rdns_debug ("no matching reply for %s",
+ req->requested_names[0].name);
+ free (elt);
+ }
+ }
+ }
+
+ if (!found && type != RDNS_REQUEST_ANY) {
+ /* We have not found the requested RR type */
+ rep->code = RDNS_RC_NOREC;
+ }
+
+ *_rep = rep;
+ return true;
+}
+
+static void
+rdns_request_unschedule (struct rdns_request *req)
+{
+ req->async->del_timer (req->async->data,
+ req->async_event);
+ /* Remove from id hashes */
+ HASH_DEL (req->io->requests, req);
+}
+
+void
+rdns_process_read (int fd, void *arg)
+{
+ struct rdns_io_channel *ioc = arg;
+ struct rdns_resolver *resolver;
+ struct rdns_request *req = NULL;
+ ssize_t r;
+ struct rdns_reply *rep;
+ uint8_t in[UDP_PACKET_SIZE];
+
+ resolver = ioc->resolver;
+
+ /* First read packet from socket */
+ if (resolver->curve_plugin == NULL) {
+ r = read (fd, in, sizeof (in));
+ if (r > (int)(sizeof (struct dns_header) + sizeof (struct dns_query))) {
+ req = rdns_find_dns_request (in, ioc);
+ }
+ }
+ else {
+ r = resolver->curve_plugin->cb.curve_plugin.recv_cb (ioc, in,
+ sizeof (in), resolver->curve_plugin->data, &req);
+ if (req == NULL &&
+ r > (int)(sizeof (struct dns_header) + sizeof (struct dns_query))) {
+ req = rdns_find_dns_request (in, ioc);
+ }
+ }
+
+ if (req != NULL) {
+ if (rdns_parse_reply (in, r, req, &rep)) {
+ UPSTREAM_OK (req->io->srv);
+ req->state = RDNS_REQUEST_REPLIED;
+ rdns_request_unschedule (req);
+ req->func (rep, req->arg);
+ REF_RELEASE (req);
+ }
+ }
+ else {
+ /* Still want to increase uses */
+ ioc->uses ++;
+ }
+}
+
+void
+rdns_process_timer (void *arg)
+{
+ struct rdns_request *req = (struct rdns_request *)arg;
+ struct rdns_reply *rep;
+ int r;
+ bool renew = false;
+ struct rdns_resolver *resolver;
+ struct rdns_server *serv = NULL;
+
+ req->retransmits --;
+ resolver = req->resolver;
+
+ if (req->retransmits == 0) {
+ UPSTREAM_FAIL (req->io->srv, time (NULL));
+ rep = rdns_make_reply (req, RDNS_RC_TIMEOUT);
+ req->state = RDNS_REQUEST_REPLIED;
+ rdns_request_unschedule (req);
+ req->func (rep, req->arg);
+ REF_RELEASE (req);
+
+ return;
+ }
+
+ if (!req->io->active) {
+ /* Do not reschedule IO requests on inactive sockets */
+ rdns_debug ("reschedule request with id: %d", (int)req->id);
+ rdns_request_unschedule (req);
+ REF_RELEASE (req->io);
+
+ UPSTREAM_SELECT_ROUND_ROBIN (resolver->servers, serv);
+
+ if (serv == NULL) {
+ rdns_warn ("cannot find suitable server for request");
+ rep = rdns_make_reply (req, RDNS_RC_SERVFAIL);
+ req->state = RDNS_REQUEST_REPLIED;
+ req->func (rep, req->arg);
+ REF_RELEASE (req);
+ }
+
+ /* Select random IO channel */
+ req->io = serv->io_channels[ottery_rand_uint32 () % serv->io_cnt];
+ req->io->uses ++;
+ REF_RETAIN (req->io);
+ renew = true;
+ }
+
+ r = rdns_send_request (req, req->io->sock, renew);
+ if (r == 0) {
+ /* Retransmit one more time */
+ req->async->del_timer (req->async->data,
+ req->async_event);
+ req->async_event = req->async->add_write (req->async->data,
+ req->io->sock, req);
+ req->state = RDNS_REQUEST_REGISTERED;
+ }
+ else if (r == -1) {
+ UPSTREAM_FAIL (req->io->srv, time (NULL));
+ rep = rdns_make_reply (req, RDNS_RC_NETERR);
+ req->state = RDNS_REQUEST_REPLIED;
+ rdns_request_unschedule (req);
+ req->func (rep, req->arg);
+ REF_RELEASE (req);
+ }
+ else {
+ req->async->repeat_timer (req->async->data, req->async_event);
+ }
+}
+
+static void
+rdns_process_periodic (void *arg)
+{
+ struct rdns_resolver *resolver = (struct rdns_resolver*)arg;
+
+ UPSTREAM_RESCAN (resolver->servers, time (NULL));
+}
+
+static void
+rdns_process_ioc_refresh (void *arg)
+{
+ struct rdns_resolver *resolver = (struct rdns_resolver*)arg;
+ struct rdns_server *serv;
+ struct rdns_io_channel *ioc, *nioc;
+ unsigned int i;
+
+ if (resolver->max_ioc_uses > 0) {
+ UPSTREAM_FOREACH (resolver->servers, serv) {
+ for (i = 0; i < serv->io_cnt; i ++) {
+ ioc = serv->io_channels[i];
+ if (ioc->uses > resolver->max_ioc_uses) {
+ /* Schedule IOC removing */
+ nioc = calloc (1, sizeof (struct rdns_io_channel));
+ if (nioc == NULL) {
+ rdns_err ("calloc fails to allocate rdns_io_channel");
+ continue;
+ }
+ nioc->sock = rdns_make_client_socket (serv->name, serv->port,
+ SOCK_DGRAM);
+ if (nioc->sock == -1) {
+ rdns_err ("cannot open socket to %s: %s", serv->name,
+ strerror (errno));
+ free (nioc);
+ continue;
+ }
+ nioc->srv = serv;
+ nioc->active = true;
+ nioc->resolver = resolver;
+ nioc->async_io = resolver->async->add_read (resolver->async->data,
+ nioc->sock, nioc);
+ REF_INIT_RETAIN (nioc, rdns_ioc_free);
+ serv->io_channels[i] = nioc;
+ rdns_debug ("scheduled io channel for server %s to be refreshed after "
+ "%lu usages", serv->name, (unsigned long)ioc->uses);
+ ioc->active = false;
+ REF_RELEASE (ioc);
+ }
+ }
+ }
+ }
+}
+
+void
+rdns_process_retransmit (int fd, void *arg)
+{
+ struct rdns_request *req = (struct rdns_request *)arg;
+ struct rdns_resolver *resolver;
+ struct rdns_reply *rep;
+ int r;
+
+ resolver = req->resolver;
+
+ resolver->async->del_write (resolver->async->data,
+ req->async_event);
+
+ r = rdns_send_request (req, fd, false);
+
+ if (r == 0) {
+ /* Retransmit one more time */
+ req->async_event = req->async->add_write (req->async->data,
+ fd, req);
+ req->state = RDNS_REQUEST_REGISTERED;
+ }
+ else if (r == -1) {
+ UPSTREAM_FAIL (req->io->srv, time (NULL));
+ rep = rdns_make_reply (req, RDNS_RC_NETERR);
+ req->state = RDNS_REQUEST_REPLIED;
+ req->func (rep, req->arg);
+ REF_RELEASE (req);
+ }
+ else {
+ req->async_event = req->async->add_timer (req->async->data,
+ req->timeout, req);
+ req->state = RDNS_REQUEST_SENT;
+ }
+}
+
+struct rdns_request*
+rdns_make_request_full (
+ struct rdns_resolver *resolver,
+ dns_callback_type cb,
+ void *cbdata,
+ double timeout,
+ unsigned int repeats,
+ unsigned int queries,
+ ...
+ )
+{
+ va_list args;
+ struct rdns_request *req;
+ struct rdns_server *serv;
+ int r, type;
+ unsigned int i, tlen = 0, clen = 0, cur;
+ size_t olen;
+ const char *cur_name, *last_name = NULL;
+ struct rdns_compression_entry *comp = NULL;
+
+ if (!resolver->initialized) {
+ return NULL;
+ }
+
+ req = malloc (sizeof (struct rdns_request));
+ if (req == NULL) {
+ return NULL;
+ }
+
+ req->resolver = resolver;
+ req->func = cb;
+ req->arg = cbdata;
+ req->reply = NULL;
+ req->qcount = queries;
+ req->io = NULL;
+ req->state = RDNS_REQUEST_NEW;
+ req->packet = NULL;
+ req->requested_names = calloc (queries, sizeof (struct rdns_request_name));
+ if (req->requested_names == NULL) {
+ free (req);
+ return NULL;
+ }
+
+ req->type = 0;
+#ifdef TWEETNACL
+ req->curve_plugin_data = NULL;
+#endif
+ REF_INIT_RETAIN (req, rdns_request_free);
+
+ /* Calculate packet's total length based on records count */
+ va_start (args, queries);
+ for (i = 0; i < queries * 2; i += 2) {
+ cur = i / 2;
+ cur_name = va_arg (args, const char *);
+ if (cur_name != NULL) {
+ last_name = cur_name;
+ clen = strlen (cur_name);
+ if (clen == 0) {
+ rdns_info ("got empty name to resolve");
+ rdns_request_free (req);
+ return NULL;
+ }
+ tlen += clen;
+ }
+ else if (last_name == NULL) {
+ rdns_info ("got NULL as the first name to resolve");
+ rdns_request_free (req);
+ return NULL;
+ }
+
+ if (!rdns_format_dns_name (resolver, last_name, clen,
+ &req->requested_names[cur].name, &olen)) {
+ rdns_request_free (req);
+ return NULL;
+ }
+
+ type = va_arg (args, int);
+ req->requested_names[cur].type = type;
+ req->requested_names[cur].len = olen;
+ }
+ va_end (args);
+
+ rdns_allocate_packet (req, tlen);
+ rdns_make_dns_header (req, queries);
+
+ for (i = 0; i < queries; i ++) {
+ cur_name = req->requested_names[i].name;
+ clen = req->requested_names[i].len;
+ type = req->requested_names[i].type;
+ if (queries > 1) {
+ if (!rdns_add_rr (req, cur_name, clen, type, &comp)) {
+ REF_RELEASE (req);
+ rnds_compression_free (comp);
+ return NULL;
+ }
+ }
+ else {
+ if (!rdns_add_rr (req, cur_name, clen, type, NULL)) {
+ REF_RELEASE (req);
+ rnds_compression_free (comp);
+ return NULL;
+ }
+ }
+ }
+
+ rnds_compression_free (comp);
+
+ /* Add EDNS RR */
+ rdns_add_edns0 (req);
+
+ req->retransmits = repeats;
+ req->timeout = timeout;
+ req->state = RDNS_REQUEST_NEW;
+ req->async = resolver->async;
+
+ UPSTREAM_SELECT_ROUND_ROBIN (resolver->servers, serv);
+
+ if (serv == NULL) {
+ rdns_warn ("cannot find suitable server for request");
+ REF_RELEASE (req);
+ return NULL;
+ }
+
+ /* Select random IO channel */
+ req->io = serv->io_channels[ottery_rand_uint32 () % serv->io_cnt];
+ req->io->uses ++;
+
+ /* Now send request to server */
+ r = rdns_send_request (req, req->io->sock, true);
+
+ if (r == -1) {
+ REF_RELEASE (req);
+ return NULL;
+ }
+
+ REF_RETAIN (req->io);
+ REF_RETAIN (req->resolver);
+
+ return req;
+}
+
+bool
+rdns_resolver_init (struct rdns_resolver *resolver)
+{
+ unsigned int i;
+ struct rdns_server *serv;
+ struct rdns_io_channel *ioc;
+
+ if (!resolver->async_binded) {
+ return false;
+ }
+
+ if (resolver->servers == NULL) {
+ return false;
+ }
+
+ /* Now init io channels to all servers */
+ UPSTREAM_FOREACH (resolver->servers, serv) {
+ serv->io_channels = calloc (serv->io_cnt, sizeof (struct rdns_io_channel *));
+ for (i = 0; i < serv->io_cnt; i ++) {
+ ioc = calloc (1, sizeof (struct rdns_io_channel));
+ if (ioc == NULL) {
+ rdns_err ("cannot allocate memory for the resolver");
+ return false;
+ }
+ ioc->sock = rdns_make_client_socket (serv->name, serv->port, SOCK_DGRAM);
+ ioc->active = true;
+ if (ioc->sock == -1) {
+ rdns_err ("cannot open socket to %s:%d %s", serv->name, serv->port, strerror (errno));
+ free (ioc);
+ return false;
+ }
+ else {
+ ioc->srv = serv;
+ ioc->resolver = resolver;
+ ioc->async_io = resolver->async->add_read (resolver->async->data,
+ ioc->sock, ioc);
+ REF_INIT_RETAIN (ioc, rdns_ioc_free);
+ serv->io_channels[i] = ioc;
+ }
+ }
+ }
+
+ if (resolver->async->add_periodic) {
+ resolver->periodic = resolver->async->add_periodic (resolver->async->data,
+ UPSTREAM_REVIVE_TIME, rdns_process_periodic, resolver);
+ }
+
+ resolver->initialized = true;
+
+ return true;
+}
+
+void
+rdns_resolver_register_plugin (struct rdns_resolver *resolver,
+ struct rdns_plugin *plugin)
+{
+ if (resolver != NULL && plugin != NULL) {
+ /* XXX: support only network plugin now, and only a single one */
+ if (plugin->type == RDNS_PLUGIN_CURVE) {
+ resolver->curve_plugin = plugin;
+ }
+ }
+}
+
+bool
+rdns_resolver_add_server (struct rdns_resolver *resolver,
+ const char *name, unsigned int port,
+ int priority, unsigned int io_cnt)
+{
+ struct rdns_server *serv;
+ union {
+ struct in_addr v4;
+ struct in6_addr v6;
+ } addr;
+
+ if (inet_pton (AF_INET, name, &addr) == 0 &&
+ inet_pton (AF_INET6, name, &addr) == 0) {
+ /* Invalid IP */
+ return false;
+ }
+
+ if (io_cnt == 0) {
+ return false;
+ }
+ if (port == 0 || port > UINT16_MAX) {
+ return false;
+ }
+
+ serv = calloc (1, sizeof (struct rdns_server));
+ if (serv == NULL) {
+ return false;
+ }
+ serv->name = strdup (name);
+ if (serv->name == NULL) {
+ free (serv);
+ return false;
+ }
+
+ serv->io_cnt = io_cnt;
+ serv->port = port;
+
+ UPSTREAM_ADD (resolver->servers, serv, priority);
+
+ return true;
+}
+
+void
+rdns_resolver_set_logger (struct rdns_resolver *resolver,
+ rdns_log_function logger, void *log_data)
+{
+ resolver->logger = logger;
+ resolver->log_data = log_data;
+}
+
+void
+rdns_resolver_set_log_level (struct rdns_resolver *resolver,
+ enum rdns_log_level level)
+{
+ resolver->log_level = level;
+}
+
+
+void
+rdns_resolver_set_max_io_uses (struct rdns_resolver *resolver,
+ uint64_t max_ioc_uses, double check_time)
+{
+ if (resolver->refresh_ioc_periodic != NULL) {
+ resolver->async->del_periodic (resolver->async->data,
+ resolver->refresh_ioc_periodic);
+ resolver->refresh_ioc_periodic = NULL;
+ }
+
+ resolver->max_ioc_uses = max_ioc_uses;
+ if (check_time > 0.0 && resolver->async->add_periodic) {
+ resolver->refresh_ioc_periodic =
+ resolver->async->add_periodic (resolver->async->data,
+ check_time, rdns_process_ioc_refresh, resolver);
+ }
+}
+
+static void
+rdns_resolver_free (struct rdns_resolver *resolver)
+{
+ struct rdns_server *serv, *stmp;
+ struct rdns_io_channel *ioc;
+ unsigned int i;
+
+ if (resolver->initialized) {
+ if (resolver->periodic != NULL) {
+ resolver->async->del_periodic (resolver->async->data, resolver->periodic);
+ }
+ if (resolver->refresh_ioc_periodic != NULL) {
+ resolver->async->del_periodic (resolver->async->data,
+ resolver->refresh_ioc_periodic);
+ }
+ if (resolver->curve_plugin != NULL && resolver->curve_plugin->dtor != NULL) {
+ resolver->curve_plugin->dtor (resolver, resolver->curve_plugin->data);
+ }
+ /* Stop IO watch on all IO channels */
+ UPSTREAM_FOREACH_SAFE (resolver->servers, serv, stmp) {
+ for (i = 0; i < serv->io_cnt; i ++) {
+ ioc = serv->io_channels[i];
+ REF_RELEASE (ioc);
+ }
+ serv->io_cnt = 0;
+ UPSTREAM_DEL (resolver->servers, serv);
+ free (serv->io_channels);
+ free (serv->name);
+ free (serv);
+ }
+ }
+ free (resolver->async);
+ free (resolver);
+}
+
+
+struct rdns_resolver *
+rdns_resolver_new (void)
+{
+ struct rdns_resolver *new;
+
+ new = calloc (1, sizeof (struct rdns_resolver));
+
+ REF_INIT_RETAIN (new, rdns_resolver_free);
+
+ new->logger = rdns_logger_internal;
+ new->log_data = new;
+
+ return new;
+}
+
+void
+rdns_resolver_async_bind (struct rdns_resolver *resolver,
+ struct rdns_async_context *ctx)
+{
+ if (resolver != NULL && ctx != NULL) {
+ resolver->async = ctx;
+ resolver->async_binded = true;
+ }
+}
diff --git a/contrib/librdns/upstream.h b/contrib/librdns/upstream.h
new file mode 100644
index 000000000..9646f89aa
--- /dev/null
+++ b/contrib/librdns/upstream.h
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2014, Vsevolod Stakhov
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UPSTREAM_H_
+#define UPSTREAM_H_
+
+#include <time.h>
+#include <stdio.h>
+
+/**
+ * @file upstream.h
+ * The basic macros to define upstream objects
+ */
+
+#ifndef upstream_fatal
+#define upstream_fatal(msg) do { perror (msg); exit (-1); } while (0)
+#endif
+
+#ifndef upstream_malloc
+#define upstream_malloc(size) malloc (size)
+#endif
+
+#ifndef upstream_free
+#define upstream_free(size, ptr) free (ptr)
+#endif
+
+struct upstream_entry_s;
+struct upstream_common_data {
+ void **upstreams;
+ unsigned int allocated_nelts;
+ unsigned int nelts;
+ unsigned int alive;
+};
+
+typedef struct upstream_entry_s {
+ unsigned short errors; /**< errors for this upstream */
+ unsigned short dead;
+ unsigned short priority;
+ unsigned short weight;
+ time_t time; /**< time of marking */
+ void *parent; /**< parent object */
+ struct upstream_common_data *common; /**< common data */
+ void *next; /**< link to the next */
+} upstream_entry_t;
+
+/*
+ * Here we define some reasonable defaults:
+ * if an upstream has more than `UPSTREAM_MAX_ERRORS` in the period of time
+ * of `UPSTREAM_ERROR_TIME` then we shut it down for `UPSTREAM_REVIVE_TIME`.
+ * In this particular case times are 10 seconds for 10 errors and revive in
+ * 30 seconds.
+ */
+#ifndef UPSTREAM_REVIVE_TIME
+#define UPSTREAM_REVIVE_TIME 30
+#endif
+#ifndef UPSTREAM_ERROR_TIME
+#define UPSTREAM_ERROR_TIME 10
+#endif
+#ifndef UPSTREAM_MAX_ERRORS
+#define UPSTREAM_MAX_ERRORS 10
+#endif
+
+#define UPSTREAM_FAIL(u, now) do { \
+ if ((u)->up.time != 0) { \
+ if ((now) - (u)->up.time >= UPSTREAM_ERROR_TIME) { \
+ if ((u)->up.errors >= UPSTREAM_MAX_ERRORS) { \
+ (u)->up.dead = 1; \
+ (u)->up.time = now; \
+ (u)->up.common->alive --; \
+ } \
+ else { \
+ (u)->up.errors = 1; \
+ (u)->up.time = (now); \
+ } \
+ } \
+ else { \
+ (u)->up.errors ++; \
+ } \
+ } \
+ else { \
+ (u)->up.errors ++; \
+ (u)->up.time = (now); \
+ } \
+} while (0)
+
+#define UPSTREAM_OK(u) do { \
+ (u)->up.errors = 0; \
+ (u)->up.time = 0; \
+} while (0)
+
+#define UPSTREAM_ADD(head, u, priority) do { \
+ if (head == NULL) { \
+ struct upstream_common_data *cd; \
+ cd = upstream_malloc (sizeof (struct upstream_common_data)); \
+ if (cd == NULL) { \
+ upstream_fatal ("malloc failed"); \
+ } \
+ cd->upstreams = upstream_malloc (sizeof (void *) * 8); \
+ if (cd == NULL) { \
+ upstream_fatal ("malloc failed"); \
+ } \
+ cd->allocated_nelts = 8; \
+ cd->nelts = 1; \
+ cd->alive = 1; \
+ cd->upstreams[0] = (u); \
+ (u)->up.common = cd; \
+ } \
+ else { \
+ struct upstream_common_data *cd = (head)->up.common; \
+ (u)->up.common = cd; \
+ if (cd->nelts == cd->allocated_nelts) { \
+ void **nup; \
+ nup = upstream_malloc (sizeof (void *) * cd->nelts * 2); \
+ if (nup == NULL) { \
+ upstream_fatal ("malloc failed"); \
+ } \
+ memcpy (nup, cd->upstreams, cd->nelts * sizeof (void *)); \
+ upstream_free (cd->nelts * sizeof (void *), cd->upstreams); \
+ cd->upstreams = nup; \
+ cd->allocated_nelts *= 2; \
+ } \
+ cd->upstreams[cd->nelts++] = (u); \
+ cd->alive ++; \
+ } \
+ (u)->up.next = (head); \
+ (head) = (u); \
+ if (priority > 0) { \
+ (u)->up.priority = (u)->up.weight = (priority); \
+ } \
+ else { \
+ (u)->up.priority = (u)->up.weight = 65535; \
+ } \
+ (u)->up.time = 0; \
+ (u)->up.errors = 0; \
+ (u)->up.dead = 0; \
+ (u)->up.parent = (u); \
+} while (0)
+
+#define UPSTREAM_DEL(head, u) do { \
+ if (head != NULL) { \
+ struct upstream_common_data *cd = (head)->up.common; \
+ if ((u)->up.next != NULL) { \
+ (head) = (u)->up.next; \
+ cd->nelts --; \
+ cd->alive --; \
+ } \
+ else { \
+ upstream_free (cd->allocated_nelts * sizeof (void *), \
+ cd->upstreams); \
+ upstream_free (sizeof (struct upstream_common_data), cd); \
+ (head) = NULL; \
+ } \
+ } \
+} while (0)
+
+#define UPSTREAM_FOREACH(head, u) for ((u) = (head); (u) != NULL; (u) = (u)->up.next)
+#define UPSTREAM_FOREACH_SAFE(head, u, tmp) \
+ for ((u) = (head); \
+ (u) != NULL && ((tmp = (u)->up.next) || true); \
+ (u) = (tmp))
+
+#define UPSTREAM_REVIVE_ALL(head) do { \
+ __typeof(head) elt = (head); \
+ while (elt != NULL) { \
+ elt->up.dead = 0; \
+ elt->up.errors = 0; \
+ elt->up.time = 0; \
+ elt = elt->up.next; \
+ } \
+ (head)->up.common->alive = (head)->up.common->nelts; \
+} while (0)
+
+#define UPSTREAM_RESCAN(head, now) do { \
+ __typeof(head) elt = (head); \
+ if ((head)->up.common->alive == 0) { \
+ UPSTREAM_REVIVE_ALL((head)); \
+ } \
+ else { \
+ while (elt != NULL) { \
+ if (elt->up.dead) { \
+ if ((now) - elt->up.time >= UPSTREAM_REVIVE_TIME) { \
+ elt->up.dead = 0; \
+ elt->up.errors = 0; \
+ elt->up.weight = elt->up.priority; \
+ (head)->up.common->alive ++; \
+ } \
+ } \
+ else { \
+ if ((now) - elt->up.time >= UPSTREAM_ERROR_TIME && \
+ elt->up.errors >= UPSTREAM_MAX_ERRORS) { \
+ elt->up.dead = 1; \
+ elt->up.time = now; \
+ (head)->up.common->alive --; \
+ } \
+ } \
+ elt = elt->up.next; \
+ } \
+ } \
+} while (0)
+
+#define UPSTREAM_SELECT_ROUND_ROBIN(head, selected) do { \
+ __typeof(head) elt = (head); \
+ (selected) = NULL; \
+ int alive = 0; \
+ unsigned max_weight = 0; \
+ if ((head)->up.common->alive == 0){ \
+ UPSTREAM_REVIVE_ALL(head); \
+ } \
+ while (elt != NULL) { \
+ if (!elt->up.dead) { \
+ if (elt->up.weight > max_weight) { \
+ max_weight = elt->up.weight; \
+ (selected) = elt; \
+ } \
+ alive ++; \
+ } \
+ elt = elt->up.next; \
+ } \
+ if (max_weight == 0) { \
+ elt = (head); \
+ while (elt != NULL) { \
+ elt->up.weight = elt->up.priority; \
+ if (!elt->up.dead) { \
+ if (elt->up.priority > max_weight) { \
+ max_weight = elt->up.priority; \
+ (selected) = elt; \
+ } \
+ } \
+ elt = elt->up.next; \
+ } \
+ } \
+ (selected)->up.weight --; \
+} while (0)
+
+#endif /* UPSTREAM_H_ */
diff --git a/contrib/librdns/util.c b/contrib/librdns/util.c
new file mode 100644
index 000000000..7d9dc98d4
--- /dev/null
+++ b/contrib/librdns/util.c
@@ -0,0 +1,523 @@
+/* Copyright (c) 2014, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "ottery.h"
+#include "util.h"
+#include "logger.h"
+
+static int
+rdns_make_socket_nonblocking (int fd)
+{
+ int ofl;
+
+ ofl = fcntl (fd, F_GETFL, 0);
+
+ if (fcntl (fd, F_SETFL, ofl | O_NONBLOCK) == -1) {
+ return -1;
+ }
+ return 0;
+}
+
+static int
+rdns_make_inet_socket (int type, struct addrinfo *addr)
+{
+ int fd, r, s_error;
+ socklen_t optlen;
+ struct addrinfo *cur;
+
+ cur = addr;
+ while (cur) {
+ /* Create socket */
+ fd = socket (cur->ai_family, type, 0);
+ if (fd == -1) {
+ goto out;
+ }
+
+ if (rdns_make_socket_nonblocking (fd) < 0) {
+ goto out;
+ }
+
+ /* Set close on exec */
+ if (fcntl (fd, F_SETFD, FD_CLOEXEC) == -1) {
+ goto out;
+ }
+
+ r = connect (fd, cur->ai_addr, cur->ai_addrlen);
+
+ if (r == -1) {
+ if (errno != EINPROGRESS) {
+ goto out;
+ }
+ }
+ else {
+ /* Still need to check SO_ERROR on socket */
+ optlen = sizeof (s_error);
+ getsockopt (fd, SOL_SOCKET, SO_ERROR, (void *)&s_error, &optlen);
+ if (s_error) {
+ errno = s_error;
+ goto out;
+ }
+ }
+ break;
+out:
+ if (fd != -1) {
+ close (fd);
+ }
+ fd = -1;
+ cur = cur->ai_next;
+ }
+ return (fd);
+}
+
+static int
+rdns_make_unix_socket (const char *path, struct sockaddr_un *addr, int type)
+{
+ int fd = -1, s_error, r, serrno;
+ socklen_t optlen;
+
+ if (path == NULL) {
+ return -1;
+ }
+
+ addr->sun_family = AF_UNIX;
+
+ memset (addr->sun_path, 0, sizeof (addr->sun_path));
+ memccpy (addr->sun_path, path, 0, sizeof (addr->sun_path) - 1);
+#ifdef FREEBSD
+ addr->sun_len = SUN_LEN (addr);
+#endif
+
+ fd = socket (PF_LOCAL, type, 0);
+
+ if (fd == -1) {
+ return -1;
+ }
+
+ if (rdns_make_socket_nonblocking (fd) < 0) {
+ goto out;
+ }
+
+ /* Set close on exec */
+ if (fcntl (fd, F_SETFD, FD_CLOEXEC) == -1) {
+ goto out;
+ }
+
+ r = connect (fd, (struct sockaddr *)addr, SUN_LEN (addr));
+
+ if (r == -1) {
+ if (errno != EINPROGRESS) {
+ goto out;
+ }
+ }
+ else {
+ /* Still need to check SO_ERROR on socket */
+ optlen = sizeof (s_error);
+ getsockopt (fd, SOL_SOCKET, SO_ERROR, (void *)&s_error, &optlen);
+ if (s_error) {
+ errno = s_error;
+ goto out;
+ }
+ }
+
+ return (fd);
+
+ out:
+ serrno = errno;
+ if (fd != -1) {
+ close (fd);
+ }
+ errno = serrno;
+ return (-1);
+}
+
+/**
+ * Make a universal socket
+ * @param credits host, ip or path to unix socket
+ * @param port port (used for network sockets)
+ * @param async make this socket asynced
+ * @param is_server make this socket as server socket
+ * @param try_resolve try name resolution for a socket (BLOCKING)
+ */
+int
+rdns_make_client_socket (const char *credits, uint16_t port,
+ int type)
+{
+ struct sockaddr_un un;
+ struct stat st;
+ struct addrinfo hints, *res;
+ int r;
+ char portbuf[8];
+
+ if (*credits == '/') {
+ r = stat (credits, &st);
+ if (r == -1) {
+ /* Unix socket doesn't exists it must be created first */
+ errno = ENOENT;
+ return -1;
+ }
+ else {
+ if ((st.st_mode & S_IFSOCK) == 0) {
+ /* Path is not valid socket */
+ errno = EINVAL;
+ return -1;
+ }
+ else {
+ return rdns_make_unix_socket (credits, &un, type);
+ }
+ }
+ }
+ else {
+ /* TCP related part */
+ memset (&hints, 0, sizeof (hints));
+ hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
+ hints.ai_socktype = type; /* Type of the socket */
+ hints.ai_flags = 0;
+ hints.ai_protocol = 0; /* Any protocol */
+ hints.ai_canonname = NULL;
+ hints.ai_addr = NULL;
+ hints.ai_next = NULL;
+
+ hints.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV;
+
+ snprintf (portbuf, sizeof (portbuf), "%d", (int)port);
+ if ((r = getaddrinfo (credits, portbuf, &hints, &res)) == 0) {
+ r = rdns_make_inet_socket (type, res);
+ freeaddrinfo (res);
+ return r;
+ }
+ else {
+ return -1;
+ }
+ }
+
+ /* Not reached */
+ return -1;
+}
+
+const char *
+rdns_strerror (enum dns_rcode rcode)
+{
+ rcode &= 0xf;
+ static char numbuf[16];
+
+ if ('\0' == dns_rcodes[rcode][0]) {
+ snprintf (numbuf, sizeof (numbuf), "UNKNOWN: %d", (int)rcode);
+ return numbuf;
+ }
+ return dns_rcodes[rcode];
+}
+
+const char *
+rdns_strtype (enum rdns_request_type type)
+{
+ return dns_types[type];
+}
+
+uint16_t
+rdns_permutor_generate_id (void)
+{
+ uint16_t id;
+
+ id = ottery_rand_unsigned ();
+
+ return id;
+}
+
+
+void
+rdns_reply_free (struct rdns_reply *rep)
+{
+ struct rdns_reply_entry *entry, *tmp;
+
+ LL_FOREACH_SAFE (rep->entries, entry, tmp) {
+ switch (entry->type) {
+ case RDNS_REQUEST_PTR:
+ free (entry->content.ptr.name);
+ break;
+ case RDNS_REQUEST_NS:
+ free (entry->content.ns.name);
+ break;
+ case RDNS_REQUEST_MX:
+ free (entry->content.mx.name);
+ break;
+ case RDNS_REQUEST_TXT:
+ case RDNS_REQUEST_SPF:
+ free (entry->content.txt.data);
+ break;
+ case RDNS_REQUEST_SRV:
+ free (entry->content.srv.target);
+ break;
+ case RDNS_REQUEST_TLSA:
+ free (entry->content.tlsa.data);
+ break;
+ case RDNS_REQUEST_SOA:
+ free (entry->content.soa.mname);
+ free (entry->content.soa.admin);
+ break;
+ }
+ free (entry);
+ }
+ free (rep);
+}
+
+void
+rdns_request_free (struct rdns_request *req)
+{
+ unsigned int i;
+
+ if (req != NULL) {
+ if (req->packet != NULL) {
+ free (req->packet);
+ }
+ for (i = 0; i < req->qcount; i ++) {
+ free (req->requested_names[i].name);
+ }
+ if (req->requested_names != NULL) {
+ free (req->requested_names);
+ }
+ if (req->reply != NULL) {
+ rdns_reply_free (req->reply);
+ }
+ if (req->state >= RDNS_REQUEST_SENT &&
+ req->state < RDNS_REQUEST_REPLIED) {
+ /* Remove timer */
+ req->async->del_timer (req->async->data,
+ req->async_event);
+ /* Remove from id hashes */
+ HASH_DEL (req->io->requests, req);
+ }
+ else if (req->state == RDNS_REQUEST_REGISTERED) {
+ /* Remove retransmit event */
+ req->async->del_write (req->async->data,
+ req->async_event);
+ }
+#ifdef TWEETNACL
+ if (req->curve_plugin_data != NULL) {
+ req->resolver->curve_plugin->cb.curve_plugin.finish_cb (
+ req, req->resolver->curve_plugin->data);
+ }
+#endif
+ if (req->io != NULL && req->state > RDNS_REQUEST_NEW) {
+ REF_RELEASE (req->io);
+ REF_RELEASE (req->resolver);
+ }
+
+ free (req);
+ }
+}
+
+void
+rdns_ioc_free (struct rdns_io_channel *ioc)
+{
+ struct rdns_request *req, *rtmp;
+
+ HASH_ITER (hh, ioc->requests, req, rtmp) {
+ REF_RELEASE (req);
+ }
+ ioc->resolver->async->del_read (ioc->resolver->async->data,
+ ioc->async_io);
+ close (ioc->sock);
+ free (ioc);
+}
+
+void
+rdns_resolver_release (struct rdns_resolver *resolver)
+{
+ REF_RELEASE (resolver);
+}
+
+struct rdns_request*
+rdns_request_retain (struct rdns_request *req)
+{
+ REF_RETAIN (req);
+ return req;
+}
+
+void
+rdns_request_release (struct rdns_request *req)
+{
+ REF_RELEASE (req);
+}
+
+static bool
+rdns_resolver_conf_process_line (struct rdns_resolver *resolver, char *line)
+{
+ char *p, *c;
+ bool has_obrace = false;
+ unsigned int port = dns_port;
+
+ if (strncmp (line, "nameserver", sizeof ("nameserver") - 1) == 0) {
+ p = line + sizeof ("nameserver") - 1;
+ /* Skip spaces */
+ while (*p == ' ' || *p == '\t') {
+ p ++;
+ }
+ if (*p == '[') {
+ has_obrace = true;
+ p ++;
+ }
+ if (isxdigit (*p) || *p == ':') {
+ c = p;
+ while (isxdigit (*p) || *p == ':' || *p == '.') {
+ p ++;
+ }
+ if (has_obrace && *p != ']') {
+ return false;
+ }
+ else if (*p != '\0' && *p != '\n') {
+ return false;
+ }
+ *p = '\0';
+ if (has_obrace) {
+ p ++;
+ if (*p == ':') {
+ /* Maybe we have a port definition */
+ port = strtoul (p + 1, NULL, 10);
+ if (port == 0 || port > UINT16_MAX) {
+ return false;
+ }
+ }
+ }
+
+ return rdns_resolver_add_server (resolver, c, port, 0, default_io_cnt);
+ }
+ else {
+ return false;
+ }
+ }
+ /* XXX: skip unknown resolv.conf lines */
+
+ return true;
+}
+
+bool
+rdns_resolver_parse_resolv_conf (struct rdns_resolver *resolver, const char *path)
+{
+ FILE *in;
+ char buf[BUFSIZ];
+
+ in = fopen (path, "r");
+
+ if (in == NULL) {
+ return false;
+ }
+
+ while (!feof (in)) {
+ if (fgets (buf, sizeof (buf), in) == NULL) {
+ break;
+ }
+ if (!rdns_resolver_conf_process_line (resolver, buf)) {
+ rdns_warn ("rdns_resolver_parse_resolv_conf: cannot parse line: %s", buf);
+ fclose (in);
+ return false;
+ }
+ }
+
+ fclose (in);
+ return true;
+}
+
+bool
+rdns_request_has_type (struct rdns_request *req, enum rdns_request_type type)
+{
+ unsigned int i;
+
+ for (i = 0; i < req->qcount; i ++) {
+ if (req->requested_names[i].type == type) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+const struct rdns_request_name *
+rdns_request_get_name (struct rdns_request *req, unsigned int *count)
+{
+
+ if (count != NULL) {
+ *count = req->qcount;
+ }
+ return req->requested_names;
+}
+
+char *
+rdns_generate_ptr_from_str (const char *str)
+{
+ union {
+ struct in_addr v4;
+ struct in6_addr v6;
+ } addr;
+ char *res = NULL;
+ unsigned char *bytes;
+ size_t len;
+
+ if (inet_pton (AF_INET, str, &addr.v4) == 1) {
+ bytes = (unsigned char *)&addr.v4;
+
+ len = 4 * 4 + sizeof ("in-addr.arpa");
+ res = malloc (len);
+ if (res) {
+ snprintf (res, len, "%u.%u.%u.%u.in-addr.arpa",
+ (unsigned)bytes[3]&0xFF,
+ (unsigned)bytes[2]&0xFF,
+ (unsigned)bytes[1]&0xFF,
+ (unsigned)bytes[0]&0xFF);
+ }
+ }
+ else if (inet_pton (AF_INET6, str, &addr.v6) == 1) {
+ bytes = (unsigned char *)&addr.v6;
+
+ len = 2*32 + sizeof ("ip6.arpa");
+ res = malloc (len);
+ if (res) {
+ snprintf(res, len,
+ "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x."
+ "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.ip6.arpa",
+ bytes[15]&0xF, bytes[15] >> 4, bytes[14]&0xF, bytes[14] >> 4,
+ bytes[13]&0xF, bytes[13] >> 4, bytes[12]&0xF, bytes[12] >> 4,
+ bytes[11]&0xF, bytes[11] >> 4, bytes[10]&0xF, bytes[10] >> 4,
+ bytes[9]&0xF, bytes[9] >> 4, bytes[8]&0xF, bytes[8] >> 4,
+ bytes[7]&0xF, bytes[7] >> 4, bytes[6]&0xF, bytes[6] >> 4,
+ bytes[5]&0xF, bytes[5] >> 4, bytes[4]&0xF, bytes[4] >> 4,
+ bytes[3]&0xF, bytes[3] >> 4, bytes[2]&0xF, bytes[2] >> 4,
+ bytes[1]&0xF, bytes[1] >> 4, bytes[0]&0xF, bytes[0] >> 4);
+ }
+ }
+
+ return res;
+}
diff --git a/contrib/librdns/util.h b/contrib/librdns/util.h
new file mode 100644
index 000000000..035f49a19
--- /dev/null
+++ b/contrib/librdns/util.h
@@ -0,0 +1,62 @@
+/* Copyright (c) 2014, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef UTIL_H_
+#define UTIL_H_
+
+#include "dns_private.h"
+
+/**
+ * Make a universal socket
+ * @param credits host, ip or path to unix socket
+ * @param port port (used for network sockets)
+ * @param type of socket (SOCK_STREAM or SOCK_DGRAM)
+ */
+int
+rdns_make_client_socket (const char *credits, uint16_t port,
+ int type);
+
+/**
+ * Generate new random DNS id
+ * @return dns id
+ */
+uint16_t rdns_permutor_generate_id (void);
+
+
+/**
+ * Free IO channel
+ */
+void rdns_ioc_free (struct rdns_io_channel *ioc);
+
+/**
+ * Free request
+ * @param req
+ */
+void rdns_request_free (struct rdns_request *req);
+
+/**
+ * Free reply
+ * @param rep
+ */
+void rdns_reply_free (struct rdns_reply *rep);
+
+#endif /* UTIL_H_ */