From 2878d950c11526094dc2faeb2caeeb1004f1690d Mon Sep 17 00:00:00 2001
From: Tobias Markmann <tm@ayena.de>
Date: Mon, 12 Mar 2012 18:32:02 +0100
Subject: Adding basic unbound support.


diff --git a/3rdParty/Ldns/SConscript b/3rdParty/Ldns/SConscript
new file mode 100644
index 0000000..336656d
--- /dev/null
+++ b/3rdParty/Ldns/SConscript
@@ -0,0 +1,68 @@
+Import("env")
+
+if env.get("LDNS_BUNDLED", False) :
+
+	if env["PLATFORM"] == "win32" :
+		cppflags = ["/I" + Dir("#/3rdParty/Ldns/src/include").abspath]
+	else :
+		cppflags = [("-isystem", Dir("#/3rdParty/Ldns/src/include").abspath)]
+
+
+################################################################################
+# Flags
+################################################################################
+    
+	if env["SCONS_STAGE"] == "flags" :
+		env["LDNS_FLAGS"] = {
+				"CPPPATH": [Dir("src/include")],
+				"CPPFLAGS": cppflags,
+				"LIBPATH": [env.Dir(".")],
+				"LIBS": ["Swiften_Ldns"],
+			}
+
+################################################################################
+# Build
+################################################################################
+
+	if env["SCONS_STAGE"] == "build" :
+		myenv = env.Clone()
+		myenv.Append(CPPDEFINES = [("LDNS_STATICLIB")])
+		myenv.Append(CPPPATH = ["src/include", "."])
+
+		myenv.Install("include", [
+            
+			])
+		myenv.StaticLibrary("Swiften_Ldns", [
+            "src/src/compat/b32_ntop.c",
+            "src/src/compat/b32_pton.c",
+            "src/src/compat/b64_ntop.c",
+            "src/src/compat/b64_pton.c",
+            "src/src/buffer.c",
+            "src/src/dname.c",
+            "src/src/dnssec_sign.c",
+            "src/src/dnssec_verify.c",
+            "src/src/dnssec_zone.c",
+            "src/src/dnssec.c",
+            "src/src/error.c",
+            "src/src/higher.c",
+            "src/src/host2str.c",
+            "src/src/host2wire.c",
+            "src/src/keys.c",
+            "src/src/linktest.c",
+            "src/src/net.c",
+            "src/src/packet.c",
+            "src/src/parse.c",
+            "src/src/rbtree.c",
+            "src/src/rdata.c",
+            "src/src/resolver.c",
+            "src/src/rr_functions.c",
+            "src/src/rr.c",
+            "src/src/sha1.c",
+            "src/src/sha2.c",
+            "src/src/str2host.c",
+            "src/src/tsig.c",
+            "src/src/update.c",
+            "src/src/util.c",
+            "src/src/wire2host.c",
+            "src/src/zone.c",
+			])
diff --git a/3rdParty/Ldns/src/LICENSE b/3rdParty/Ldns/src/LICENSE
new file mode 100644
index 0000000..6d4c6be
--- /dev/null
+++ b/3rdParty/Ldns/src/LICENSE
@@ -0,0 +1,26 @@
+Copyright (c) 2005,2006, NLnetLabs
+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.
+    * Neither the name of NLnetLabs 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
diff --git a/3rdParty/Ldns/src/include/ldns/buffer.h b/3rdParty/Ldns/src/include/ldns/buffer.h
new file mode 100644
index 0000000..03df14c
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/buffer.h
@@ -0,0 +1,644 @@
+/*
+ * buffer.h -- generic memory buffer.
+ *
+ * Copyright (c) 2005-2008, NLnet Labs. All rights reserved.
+ *
+ * See LICENSE for the license.
+ *
+ *
+ * The buffer module implements a generic buffer.  The API is based on
+ * the java.nio.Buffer interface.
+ */
+
+#ifndef LDNS_BUFFER_H
+#define LDNS_BUFFER_H
+
+#include <assert.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include <ldns/error.h>
+#include <ldns/common.h>
+
+#include "ldns/util.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * number of initial bytes in buffer of
+ * which we cannot tell the size before hand
+ */
+#define LDNS_MIN_BUFLEN	512
+
+/**
+ * \file buffer.h
+ *
+ * This file contains the definition of ldns_buffer, and functions to manipulate those.
+ */
+
+/** 
+ * implementation of buffers to ease operations
+ *
+ * ldns_buffers can contain arbitrary information, per octet. You can write
+ * to the current end of a buffer, read from the current position, and
+ * access any data within it.
+ *
+ * Example use of buffers is in the source code of \ref host2str.c
+ */
+struct ldns_struct_buffer
+{
+	/** The current position used for reading/writing */ 
+	size_t   _position;
+
+	/** The read/write limit */
+	size_t   _limit;
+
+	/** The amount of data the buffer can contain */
+	size_t   _capacity;
+
+	/** The data contained in the buffer */
+	uint8_t *_data;
+
+	/** If the buffer is fixed it cannot be resized */
+	unsigned _fixed : 1;
+
+	/** The current state of the buffer. If writing to the buffer fails
+	 * for any reason, this value is changed. This way, you can perform
+	 * multiple writes in sequence and check for success afterwards. */
+	ldns_status _status;
+};
+typedef struct ldns_struct_buffer ldns_buffer;
+
+
+#ifdef NDEBUG
+INLINE void
+ldns_buffer_invariant(ldns_buffer *ATTR_UNUSED(buffer))
+{
+}
+#else
+INLINE void
+ldns_buffer_invariant(ldns_buffer *buffer)
+{
+	assert(buffer != NULL);
+	assert(buffer->_position <= buffer->_limit);
+	assert(buffer->_limit <= buffer->_capacity);
+	assert(buffer->_data != NULL);
+}
+#endif
+
+/**
+ * creates a new buffer with the specified capacity.
+ *
+ * \param[in] capacity the size (in bytes) to allocate for the buffer
+ * \return the created buffer
+ */
+ldns_buffer *ldns_buffer_new(size_t capacity);
+
+/**
+ * creates a buffer with the specified data.  The data IS copied
+ * and MEMORY allocations are done.  The buffer is not fixed and can
+ * be resized using buffer_reserve().
+ *
+ * \param[in] buffer pointer to the buffer to put the data in
+ * \param[in] data the data to encapsulate in the buffer
+ * \param[in] size the size of the data
+ */
+void ldns_buffer_new_frm_data(ldns_buffer *buffer, void *data, size_t size);
+
+/**
+ * clears the buffer and make it ready for writing.  The buffer's limit
+ * is set to the capacity and the position is set to 0.
+ * \param[in] buffer the buffer to clear
+ */
+INLINE void ldns_buffer_clear(ldns_buffer *buffer)
+{
+	ldns_buffer_invariant(buffer);
+
+	/* reset status here? */
+
+	buffer->_position = 0;
+	buffer->_limit = buffer->_capacity;
+}
+
+/**
+ * makes the buffer ready for reading the data that has been written to
+ * the buffer.  The buffer's limit is set to the current position and
+ * the position is set to 0.
+ *
+ * \param[in] buffer the buffer to flip
+ * \return void
+ */
+INLINE void ldns_buffer_flip(ldns_buffer *buffer)
+{
+	ldns_buffer_invariant(buffer);
+
+	buffer->_limit = buffer->_position;
+	buffer->_position = 0;
+}
+
+/**
+ * make the buffer ready for re-reading the data.  The buffer's
+ * position is reset to 0.
+ * \param[in] buffer the buffer to rewind
+ */
+INLINE void ldns_buffer_rewind(ldns_buffer *buffer)
+{
+	ldns_buffer_invariant(buffer);
+
+	buffer->_position = 0;
+}
+
+/**
+ * returns the current position in the buffer (as a number of bytes)
+ * \param[in] buffer the buffer
+ * \return the current position
+ */
+INLINE size_t
+ldns_buffer_position(ldns_buffer *buffer)
+{
+	return buffer->_position;
+}
+
+/**
+ * sets the buffer's position to MARK.  The position must be less than
+ * or equal to the buffer's limit.
+ * \param[in] buffer the buffer
+ * \param[in] mark the mark to use
+ */
+INLINE void
+ldns_buffer_set_position(ldns_buffer *buffer, size_t mark)
+{
+	assert(mark <= buffer->_limit);
+	buffer->_position = mark;
+}
+
+/**
+ * changes the buffer's position by COUNT bytes.  The position must not
+ * be moved behind the buffer's limit or before the beginning of the
+ * buffer.
+ * \param[in] buffer the buffer
+ * \param[in] count the count to use
+ */
+INLINE void
+ldns_buffer_skip(ldns_buffer *buffer, ssize_t count)
+{
+	assert(buffer->_position + count <= buffer->_limit);
+	buffer->_position += count;
+}
+
+/**
+ * returns the maximum size of the buffer
+ * \param[in] buffer
+ * \return the size
+ */
+INLINE size_t
+ldns_buffer_limit(ldns_buffer *buffer)
+{
+	return buffer->_limit;
+}
+
+/**
+ * changes the buffer's limit.  If the buffer's position is greater
+ * than the new limit the position is set to the limit.
+ * \param[in] buffer the buffer
+ * \param[in] limit the new limit
+ */
+INLINE void
+ldns_buffer_set_limit(ldns_buffer *buffer, size_t limit)
+{
+	assert(limit <= buffer->_capacity);
+	buffer->_limit = limit;
+	if (buffer->_position > buffer->_limit)
+		buffer->_position = buffer->_limit;
+}
+
+/**
+ * returns the number of bytes the buffer can hold.
+ * \param[in] buffer the buffer
+ * \return the number of bytes
+ */
+INLINE size_t
+ldns_buffer_capacity(ldns_buffer *buffer)
+{
+	return buffer->_capacity;
+}
+
+/**
+ * changes the buffer's capacity.  The data is reallocated so any
+ * pointers to the data may become invalid.  The buffer's limit is set
+ * to the buffer's new capacity.
+ * \param[in] buffer the buffer
+ * \param[in] capacity the capacity to use
+ * \return whether this failed or succeeded
+ */
+bool ldns_buffer_set_capacity(ldns_buffer *buffer, size_t capacity);
+
+/**
+ * ensures BUFFER can contain at least AMOUNT more bytes.  The buffer's
+ * capacity is increased if necessary using buffer_set_capacity().
+ *
+ * The buffer's limit is always set to the (possibly increased)
+ * capacity.
+ * \param[in] buffer the buffer
+ * \param[in] amount amount to use
+ * \return whether this failed or succeeded
+ */
+bool ldns_buffer_reserve(ldns_buffer *buffer, size_t amount);
+
+/**
+ * returns a pointer to the data at the indicated position.
+ * \param[in] buffer the buffer
+ * \param[in] at position
+ * \return the pointer to the data
+ */
+INLINE uint8_t *
+ldns_buffer_at(const ldns_buffer *buffer, size_t at)
+{
+	assert(at <= buffer->_limit);
+	return buffer->_data + at;
+}
+
+/**
+ * returns a pointer to the beginning of the buffer (the data at
+ * position 0).
+ * \param[in] buffer the buffer
+ * \return the pointer
+ */
+INLINE uint8_t *
+ldns_buffer_begin(const ldns_buffer *buffer)
+{
+	return ldns_buffer_at(buffer, 0);
+}
+
+/**
+ * returns a pointer to the end of the buffer (the data at the buffer's
+ * limit).
+ * \param[in] buffer the buffer
+ * \return the pointer
+ */
+INLINE uint8_t *
+ldns_buffer_end(ldns_buffer *buffer)
+{
+	return ldns_buffer_at(buffer, buffer->_limit);
+}
+
+/**
+ * returns a pointer to the data at the buffer's current position.
+ * \param[in] buffer the buffer
+ * \return the pointer
+ */
+INLINE uint8_t *
+ldns_buffer_current(ldns_buffer *buffer)
+{
+	return ldns_buffer_at(buffer, buffer->_position);
+}
+
+/**
+ * returns the number of bytes remaining between the indicated position and
+ * the limit.
+ * \param[in] buffer the buffer
+ * \param[in] at indicated position
+ * \return number of bytes
+ */
+INLINE size_t
+ldns_buffer_remaining_at(ldns_buffer *buffer, size_t at)
+{
+	ldns_buffer_invariant(buffer);
+	assert(at <= buffer->_limit);
+	return buffer->_limit - at;
+}
+
+/**
+ * returns the number of bytes remaining between the buffer's position and
+ * limit.
+ * \param[in] buffer the buffer
+ * \return the number of bytes
+ */
+INLINE size_t
+ldns_buffer_remaining(ldns_buffer *buffer)
+{
+	return ldns_buffer_remaining_at(buffer, buffer->_position);
+}
+
+/**
+ * checks if the buffer has at least COUNT more bytes available.
+ * Before reading or writing the caller needs to ensure enough space
+ * is available!
+ * \param[in] buffer the buffer
+ * \param[in] at indicated position
+ * \param[in] count how much is available
+ * \return true or false (as int?)
+ */
+INLINE int
+ldns_buffer_available_at(ldns_buffer *buffer, size_t at, size_t count)
+{
+	return count <= ldns_buffer_remaining_at(buffer, at);
+}
+
+/**
+ * checks if the buffer has count bytes available at the current position
+ * \param[in] buffer the buffer
+ * \param[in] count how much is available
+ * \return true or false (as int?)
+ */
+INLINE int
+ldns_buffer_available(ldns_buffer *buffer, size_t count)
+{
+	return ldns_buffer_available_at(buffer, buffer->_position, count);
+}
+
+/**
+ * writes the given data to the buffer at the specified position
+ * \param[in] buffer the buffer
+ * \param[in] at the position (in number of bytes) to write the data at
+ * \param[in] data pointer to the data to write to the buffer
+ * \param[in] count the number of bytes of data to write
+ */
+INLINE void
+ldns_buffer_write_at(ldns_buffer *buffer, size_t at, const void *data, size_t count)
+{
+	assert(ldns_buffer_available_at(buffer, at, count));
+	memcpy(buffer->_data + at, data, count);
+}
+
+/**
+ * writes count bytes of data to the current position of the buffer
+ * \param[in] buffer the buffer
+ * \param[in] data the data to write
+ * \param[in] count the lenght of the data to write
+ */
+INLINE void
+ldns_buffer_write(ldns_buffer *buffer, const void *data, size_t count)
+{
+	ldns_buffer_write_at(buffer, buffer->_position, data, count);
+	buffer->_position += count;
+}
+
+/**
+ * copies the given (null-delimited) string to the specified position at the buffer
+ * \param[in] buffer the buffer
+ * \param[in] at the position in the buffer
+ * \param[in] str the string to write
+ */
+INLINE void
+ldns_buffer_write_string_at(ldns_buffer *buffer, size_t at, const char *str)
+{
+	ldns_buffer_write_at(buffer, at, str, strlen(str));
+}
+
+/**
+ * copies the given (null-delimited) string to the current position at the buffer
+ * \param[in] buffer the buffer
+ * \param[in] str the string to write
+ */
+INLINE void
+ldns_buffer_write_string(ldns_buffer *buffer, const char *str)
+{
+	ldns_buffer_write(buffer, str, strlen(str));
+}
+
+/**
+ * writes the given byte of data at the given position in the buffer
+ * \param[in] buffer the buffer
+ * \param[in] at the position in the buffer
+ * \param[in] data the 8 bits to write
+ */
+INLINE void
+ldns_buffer_write_u8_at(ldns_buffer *buffer, size_t at, uint8_t data)
+{
+	assert(ldns_buffer_available_at(buffer, at, sizeof(data)));
+	buffer->_data[at] = data;
+}
+
+/**
+ * writes the given byte of data at the current position in the buffer
+ * \param[in] buffer the buffer
+ * \param[in] data the 8 bits to write
+ */
+INLINE void
+ldns_buffer_write_u8(ldns_buffer *buffer, uint8_t data)
+{
+	ldns_buffer_write_u8_at(buffer, buffer->_position, data);
+	buffer->_position += sizeof(data);
+}
+
+/**
+ * writes the given 2 byte integer at the given position in the buffer
+ * \param[in] buffer the buffer
+ * \param[in] at the position in the buffer
+ * \param[in] data the 16 bits to write
+ */
+INLINE void
+ldns_buffer_write_u16_at(ldns_buffer *buffer, size_t at, uint16_t data)
+{
+	assert(ldns_buffer_available_at(buffer, at, sizeof(data)));
+	ldns_write_uint16(buffer->_data + at, data);
+}
+
+/**
+ * writes the given 2 byte integer at the current position in the buffer
+ * \param[in] buffer the buffer
+ * \param[in] data the 16 bits to write
+ */
+INLINE void
+ldns_buffer_write_u16(ldns_buffer *buffer, uint16_t data)
+{
+	ldns_buffer_write_u16_at(buffer, buffer->_position, data);
+	buffer->_position += sizeof(data);
+}
+
+/**
+ * writes the given 4 byte integer at the given position in the buffer
+ * \param[in] buffer the buffer
+ * \param[in] at the position in the buffer
+ * \param[in] data the 32 bits to write
+ */
+INLINE void
+ldns_buffer_write_u32_at(ldns_buffer *buffer, size_t at, uint32_t data)
+{
+	assert(ldns_buffer_available_at(buffer, at, sizeof(data)));
+	ldns_write_uint32(buffer->_data + at, data);
+}
+
+/**
+ * writes the given 4 byte integer at the current position in the buffer
+ * \param[in] buffer the buffer
+ * \param[in] data the 32 bits to write
+ */
+INLINE void
+ldns_buffer_write_u32(ldns_buffer *buffer, uint32_t data)
+{
+	ldns_buffer_write_u32_at(buffer, buffer->_position, data);
+	buffer->_position += sizeof(data);
+}
+
+/**
+ * copies count bytes of data at the given position to the given data-array
+ * \param[in] buffer the buffer
+ * \param[in] at the position in the buffer to start
+ * \param[out] data buffer to copy to
+ * \param[in] count the length of the data to copy
+ */
+INLINE void
+ldns_buffer_read_at(ldns_buffer *buffer, size_t at, void *data, size_t count)
+{
+	assert(ldns_buffer_available_at(buffer, at, count));
+	memcpy(data, buffer->_data + at, count);
+}
+
+/**
+ * copies count bytes of data at the current position to the given data-array
+ * \param[in] buffer the buffer
+ * \param[out] data buffer to copy to
+ * \param[in] count the length of the data to copy
+ */
+INLINE void
+ldns_buffer_read(ldns_buffer *buffer, void *data, size_t count)
+{
+	ldns_buffer_read_at(buffer, buffer->_position, data, count);
+	buffer->_position += count;
+}
+
+/**
+ * returns the byte value at the given position in the buffer
+ * \param[in] buffer the buffer
+ * \param[in] at the position in the buffer
+ * \return 1 byte integer
+ */
+INLINE uint8_t
+ldns_buffer_read_u8_at(ldns_buffer *buffer, size_t at)
+{
+	assert(ldns_buffer_available_at(buffer, at, sizeof(uint8_t)));
+	return buffer->_data[at];
+}
+
+/**
+ * returns the byte value at the current position in the buffer
+ * \param[in] buffer the buffer
+ * \return 1 byte integer
+ */
+INLINE uint8_t
+ldns_buffer_read_u8(ldns_buffer *buffer)
+{
+	uint8_t result = ldns_buffer_read_u8_at(buffer, buffer->_position);
+	buffer->_position += sizeof(uint8_t);
+	return result;
+}
+
+/**
+ * returns the 2-byte integer value at the given position in the buffer
+ * \param[in] buffer the buffer
+ * \param[in] at position in the buffer
+ * \return 2 byte integer
+ */
+INLINE uint16_t
+ldns_buffer_read_u16_at(ldns_buffer *buffer, size_t at)
+{
+	assert(ldns_buffer_available_at(buffer, at, sizeof(uint16_t)));
+	return ldns_read_uint16(buffer->_data + at);
+}
+
+/**
+ * returns the 2-byte integer value at the current position in the buffer
+ * \param[in] buffer the buffer
+ * \return 2 byte integer
+ */
+INLINE uint16_t
+ldns_buffer_read_u16(ldns_buffer *buffer)
+{
+	uint16_t result = ldns_buffer_read_u16_at(buffer, buffer->_position);
+	buffer->_position += sizeof(uint16_t);
+	return result;
+}
+
+/**
+ * returns the 4-byte integer value at the given position in the buffer
+ * \param[in] buffer the buffer
+ * \param[in] at position in the buffer
+ * \return 4 byte integer
+ */
+INLINE uint32_t
+ldns_buffer_read_u32_at(ldns_buffer *buffer, size_t at)
+{
+	assert(ldns_buffer_available_at(buffer, at, sizeof(uint32_t)));
+	return ldns_read_uint32(buffer->_data + at);
+}
+
+/**
+ * returns the 4-byte integer value at the current position in the buffer
+ * \param[in] buffer the buffer
+ * \return 4 byte integer
+ */
+INLINE uint32_t
+ldns_buffer_read_u32(ldns_buffer *buffer)
+{
+	uint32_t result = ldns_buffer_read_u32_at(buffer, buffer->_position);
+	buffer->_position += sizeof(uint32_t);
+	return result;
+}
+
+/**
+ * returns the status of the buffer
+ * \param[in] buffer
+ * \return the status
+ */
+INLINE ldns_status
+ldns_buffer_status(ldns_buffer *buffer)
+{
+	return buffer->_status;
+}
+
+/**
+ * returns true if the status of the buffer is LDNS_STATUS_OK, false otherwise
+ * \param[in] buffer the buffer
+ * \return true or false
+ */
+INLINE bool
+ldns_buffer_status_ok(ldns_buffer *buffer)
+{
+	if (buffer) {
+		return ldns_buffer_status(buffer) == LDNS_STATUS_OK;
+	} else {
+		return false;
+	}
+}
+
+/**
+ * prints to the buffer, increasing the capacity if required using
+ * buffer_reserve(). The buffer's position is set to the terminating '\\0'
+ * Returns the number of characters written (not including the
+ * terminating '\\0') or -1 on failure.
+ */
+int ldns_buffer_printf(ldns_buffer *buffer, const char *format, ...);
+/*	ATTR_FORMAT(printf, 2, 3);*/
+
+/**
+ * frees the buffer.
+ * \param[in] *buffer the buffer to be freed
+ * \return void
+ */
+void ldns_buffer_free(ldns_buffer *buffer);
+
+/**
+ * Makes the buffer fixed and returns a pointer to the data.  The
+ * caller is responsible for free'ing the result.
+ * \param[in] *buffer the buffer to be exported
+ * \return void
+ */
+void *ldns_buffer_export(ldns_buffer *buffer);
+
+/**
+ * Copy contents of the other buffer to this buffer. Silently truncated
+ * if this buffer is too small.
+ * \param[out] *result resulting buffer which is copied to.
+ * \param[in] *from what to copy to result.
+ */
+void ldns_buffer_copy(ldns_buffer* result, ldns_buffer* from);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LDNS_BUFFER_H */
diff --git a/3rdParty/Ldns/src/include/ldns/common.h b/3rdParty/Ldns/src/include/ldns/common.h
new file mode 100644
index 0000000..ed3847f
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/common.h
@@ -0,0 +1,69 @@
+/**
+ * \file common.h
+ *
+ * Common definitions for LDNS
+ */
+
+/**
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2004-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+#ifndef LDNS_COMMON_H
+#define LDNS_COMMON_H
+
+/*
+ * The build configuration that is used in the distributed headers,
+ * as detected and determined by the auto configure script.
+ */
+#define LDNS_BUILD_CONFIG_HAVE_SSL         1
+#define LDNS_BUILD_CONFIG_USE_ECDSA        0
+#define LDNS_BUILD_CONFIG_HAVE_INTTYPES_H  1
+#define LDNS_BUILD_CONFIG_HAVE_ATTR_FORMAT 1
+#define LDNS_BUILD_CONFIG_HAVE_ATTR_UNUSED 1
+
+/*
+ * HAVE_STDBOOL_H is not available when distributed as a library, but no build 
+ * configuration variables may be used (like those above) because the header
+ * is sometimes only available when using special compiler flags to enable the
+ * c99 environment. Because we cannot force the usage of this flag, we have to
+ * provide a default type. Below what is suggested by the autoconf manual.
+ */
+/*@ignore@*/
+/* splint barfs on this construct */
+#ifdef HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# ifndef HAVE__BOOL
+#  ifdef __cplusplus
+typedef bool _Bool;
+#  else
+#   define _Bool signed char
+#  endif
+# endif
+# define bool _Bool
+# define false 0
+# define true 1
+# define __bool_true_false_are_defined 1
+#endif
+/*@end@*/
+
+#if LDNS_BUILD_CONFIG_HAVE_ATTR_FORMAT
+#define ATTR_FORMAT(archetype, string_index, first_to_check) \
+    __attribute__ ((format (archetype, string_index, first_to_check)))
+#else /* !LDNS_BUILD_CONFIG_HAVE_ATTR_FORMAT */
+#define ATTR_FORMAT(archetype, string_index, first_to_check) /* empty */
+#endif /* !LDNS_BUILD_CONFIG_HAVE_ATTR_FORMAT */
+
+#if defined(__cplusplus)
+#define ATTR_UNUSED(x)
+#elif LDNS_BUILD_CONFIG_HAVE_ATTR_UNUSED
+#define ATTR_UNUSED(x)  x __attribute__((unused))
+#else /* !LDNS_BUILD_CONFIG_HAVE_ATTR_UNUSED */
+#define ATTR_UNUSED(x)  x
+#endif /* !LDNS_BUILD_CONFIG_HAVE_ATTR_UNUSED */
+
+#endif /* LDNS_COMMON_H */
diff --git a/3rdParty/Ldns/src/include/ldns/common.h.in b/3rdParty/Ldns/src/include/ldns/common.h.in
new file mode 100644
index 0000000..98470ee
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/common.h.in
@@ -0,0 +1,69 @@
+/**
+ * \file common.h
+ *
+ * Common definitions for LDNS
+ */
+
+/**
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2004-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+#ifndef LDNS_COMMON_H
+#define LDNS_COMMON_H
+
+/*
+ * The build configuration that is used in the distributed headers,
+ * as detected and determined by the auto configure script.
+ */
+#define LDNS_BUILD_CONFIG_HAVE_SSL         @ldns_build_config_have_ssl@
+#define LDNS_BUILD_CONFIG_USE_ECDSA        @ldns_build_config_use_ecdsa@
+#define LDNS_BUILD_CONFIG_HAVE_INTTYPES_H  @ldns_build_config_have_inttypes_h@
+#define LDNS_BUILD_CONFIG_HAVE_ATTR_FORMAT @ldns_build_config_have_attr_format@
+#define LDNS_BUILD_CONFIG_HAVE_ATTR_UNUSED @ldns_build_config_have_attr_unused@
+
+/*
+ * HAVE_STDBOOL_H is not available when distributed as a library, but no build 
+ * configuration variables may be used (like those above) because the header
+ * is sometimes only available when using special compiler flags to enable the
+ * c99 environment. Because we cannot force the usage of this flag, we have to
+ * provide a default type. Below what is suggested by the autoconf manual.
+ */
+/*@ignore@*/
+/* splint barfs on this construct */
+#ifdef HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# ifndef HAVE__BOOL
+#  ifdef __cplusplus
+typedef bool _Bool;
+#  else
+#   define _Bool signed char
+#  endif
+# endif
+# define bool _Bool
+# define false 0
+# define true 1
+# define __bool_true_false_are_defined 1
+#endif
+/*@end@*/
+
+#if LDNS_BUILD_CONFIG_HAVE_ATTR_FORMAT
+#define ATTR_FORMAT(archetype, string_index, first_to_check) \
+    __attribute__ ((format (archetype, string_index, first_to_check)))
+#else /* !LDNS_BUILD_CONFIG_HAVE_ATTR_FORMAT */
+#define ATTR_FORMAT(archetype, string_index, first_to_check) /* empty */
+#endif /* !LDNS_BUILD_CONFIG_HAVE_ATTR_FORMAT */
+
+#if defined(__cplusplus)
+#define ATTR_UNUSED(x)
+#elif LDNS_BUILD_CONFIG_HAVE_ATTR_UNUSED
+#define ATTR_UNUSED(x)  x __attribute__((unused))
+#else /* !LDNS_BUILD_CONFIG_HAVE_ATTR_UNUSED */
+#define ATTR_UNUSED(x)  x
+#endif /* !LDNS_BUILD_CONFIG_HAVE_ATTR_UNUSED */
+
+#endif /* LDNS_COMMON_H */
diff --git a/3rdParty/Ldns/src/include/ldns/config.h b/3rdParty/Ldns/src/include/ldns/config.h
new file mode 100644
index 0000000..e271415
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/config.h
@@ -0,0 +1,556 @@
+/* ldns/config.h.  Generated from config.h.in by configure.  */
+/* ldns/config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define if building universal (internal helper macro) */
+/* #undef AC_APPLE_UNIVERSAL_BUILD */
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#define HAVE_ARPA_INET_H 1
+
+/* Whether the C compiler accepts the "format" attribute */
+#define HAVE_ATTR_FORMAT 1
+
+/* Whether the C compiler accepts the "unused" attribute */
+#define HAVE_ATTR_UNUSED 1
+
+/* Define to 1 if you have the `b32_ntop' function. */
+/* #undef HAVE_B32_NTOP */
+
+/* Define to 1 if you have the `b32_pton' function. */
+/* #undef HAVE_B32_PTON */
+
+/* Define to 1 if you have the `b64_ntop' function. */
+/* #undef HAVE_B64_NTOP */
+
+/* Define to 1 if you have the `b64_pton' function. */
+/* #undef HAVE_B64_PTON */
+
+/* Define to 1 if you have the `bzero' function. */
+#define HAVE_BZERO 1
+
+/* Define to 1 if you have the `calloc' function. */
+#define HAVE_CALLOC 1
+
+/* Define to 1 if you have the `ctime_r' function. */
+#define HAVE_CTIME_R 1
+
+/* Define to 1 if you have the declaration of `NID_secp384r1', and to 0 if you
+   don't. */
+/* #undef HAVE_DECL_NID_SECP384R1 */
+
+/* Define to 1 if you have the declaration of `NID_X9_62_prime256v1', and to 0
+   if you don't. */
+/* #undef HAVE_DECL_NID_X9_62_PRIME256V1 */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the `endprotoent' function. */
+#define HAVE_ENDPROTOENT 1
+
+/* Define to 1 if you have the `endservent' function. */
+#define HAVE_ENDSERVENT 1
+
+/* Define to 1 if you have the `EVP_sha256' function. */
+#define HAVE_EVP_SHA256 1
+
+/* Define to 1 if you have the `fcntl' function. */
+#define HAVE_FCNTL 1
+
+/* Define to 1 if you have the `fork' function. */
+#define HAVE_FORK 1
+
+/* Whether getaddrinfo is available */
+#define HAVE_GETADDRINFO 1
+
+/* Define to 1 if you have the <getopt.h> header file. */
+#define HAVE_GETOPT_H 1
+
+/* Define to 1 if you have the `gmtime_r' function. */
+#define HAVE_GMTIME_R 1
+
+/* If you have HMAC_CTX_init */
+#define HAVE_HMAC_CTX_INIT 1
+
+/* Define to 1 if you have the `inet_aton' function. */
+#define HAVE_INET_ATON 1
+
+/* Define to 1 if you have the `inet_ntop' function. */
+#define HAVE_INET_NTOP 1
+
+/* Define to 1 if you have the `inet_pton' function. */
+#define HAVE_INET_PTON 1
+
+/* Define to 1 if the system has the type `intptr_t'. */
+#define HAVE_INTPTR_T 1
+
+/* define if you have inttypes.h */
+#define HAVE_INTTYPES_H 1
+
+/* if the function 'ioctlsocket' is available */
+/* #undef HAVE_IOCTLSOCKET */
+
+/* Define to 1 if you have the `isascii' function. */
+#define HAVE_ISASCII 1
+
+/* Define to 1 if you have the `isblank' function. */
+#define HAVE_ISBLANK 1
+
+/* Define to 1 if you have the `pcap' library (-lpcap). */
+/* #undef HAVE_LIBPCAP */
+
+/* Define to 1 if you have the `localtime_r' function. */
+#define HAVE_LOCALTIME_R 1
+
+/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
+   to 0 otherwise. */
+#define HAVE_MALLOC 1
+
+/* Define to 1 if you have the `memmove' function. */
+#define HAVE_MEMMOVE 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `memset' function. */
+#define HAVE_MEMSET 1
+
+/* Define to 1 if you have the <netdb.h> header file. */
+#define HAVE_NETDB_H 1
+
+/* Define to 1 if you have the <netinet/if_ether.h> header file. */
+/* #undef HAVE_NETINET_IF_ETHER_H */
+
+/* Define to 1 if you have the <netinet/igmp.h> header file. */
+/* #undef HAVE_NETINET_IGMP_H */
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#define HAVE_NETINET_IN_H 1
+
+/* Define to 1 if you have the <netinet/in_systm.h> header file. */
+/* #undef HAVE_NETINET_IN_SYSTM_H */
+
+/* Define to 1 if you have the <netinet/ip6.h> header file. */
+/* #undef HAVE_NETINET_IP6_H */
+
+/* Define to 1 if you have the <netinet/ip_compat.h> header file. */
+/* #undef HAVE_NETINET_IP_COMPAT_H */
+
+/* Define to 1 if you have the <netinet/ip.h> header file. */
+/* #undef HAVE_NETINET_IP_H */
+
+/* Define to 1 if you have the <netinet/udp.h> header file. */
+/* #undef HAVE_NETINET_UDP_H */
+
+/* Define to 1 if you have the <net/ethernet.h> header file. */
+/* #undef HAVE_NET_ETHERNET_H */
+
+/* Define to 1 if you have the <net/if.h> header file. */
+/* #undef HAVE_NET_IF_H */
+
+/* Define to 1 if you have the <openssl/err.h> header file. */
+#define HAVE_OPENSSL_ERR_H 1
+
+/* Define to 1 if you have the <openssl/rand.h> header file. */
+#define HAVE_OPENSSL_RAND_H 1
+
+/* Define to 1 if you have the <openssl/ssl.h> header file. */
+#define HAVE_OPENSSL_SSL_H 1
+
+/* Define to 1 if you have the <pcap.h> header file. */
+/* #undef HAVE_PCAP_H */
+
+/* If available, contains the Python version number currently in use. */
+/* #undef HAVE_PYTHON */
+
+/* Define to 1 if you have the `random' function. */
+#define HAVE_RANDOM 1
+
+/* Define to 1 if your system has a GNU libc compatible `realloc' function,
+   and to 0 otherwise. */
+#define HAVE_REALLOC 1
+
+/* Define to 1 if you have the `sleep' function. */
+#define HAVE_SLEEP 1
+
+/* Define to 1 if you have the `snprintf' function. */
+#define HAVE_SNPRINTF 1
+
+/* Define if you have the SSL libraries installed. */
+#define HAVE_SSL /**/
+
+/* Define to 1 if you have the <stdarg.h> header file. */
+#define HAVE_STDARG_H 1
+
+/* Define to 1 if stdbool.h conforms to C99. */
+#define HAVE_STDBOOL_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strlcpy' function. */
+#define HAVE_STRLCPY 1
+
+/* Define to 1 if you have the `strtoul' function. */
+#define HAVE_STRTOUL 1
+
+/* Define if you have SWIG libraries and header files. */
+/* #undef HAVE_SWIG */
+
+/* Define to 1 if you have the <sys/mount.h> header file. */
+#define HAVE_SYS_MOUNT_H 1
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* define if you have sys/socket.h */
+#define HAVE_SYS_SOCKET_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* define if you have sys/types.h */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the `timegm' function. */
+#define HAVE_TIMEGM 1
+
+/* Define to 1 if you have the <time.h> header file. */
+#define HAVE_TIME_H 1
+
+/* define if you have unistd.h */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `vfork' function. */
+#define HAVE_VFORK 1
+
+/* Define to 1 if you have the <vfork.h> header file. */
+/* #undef HAVE_VFORK_H */
+
+/* Define to 1 if you have the <winsock2.h> header file. */
+/* #undef HAVE_WINSOCK2_H */
+
+/* Define to 1 if `fork' works. */
+#define HAVE_WORKING_FORK 1
+
+/* Define to 1 if `vfork' works. */
+#define HAVE_WORKING_VFORK 1
+
+/* Define to 1 if you have the <ws2tcpip.h> header file. */
+/* #undef HAVE_WS2TCPIP_H */
+
+/* Define to 1 if the system has the type `_Bool'. */
+#define HAVE__BOOL 1
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#define LT_OBJDIR ".libs/"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "libdns@nlnetlabs.nl"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "ldns"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "ldns 1.6.12"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "libdns"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "1.6.12"
+
+/* The size of `time_t', as computed by sizeof. */
+#define SIZEOF_TIME_T 8
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* System configuration dir */
+#define SYSCONFDIR sysconfdir
+
+/* Define this to enable ECDSA support. */
+/* #undef USE_ECDSA */
+
+/* Define this to enable GOST support. */
+/* #undef USE_GOST */
+
+/* Define this to enable SHA256 and SHA512 support. */
+#define USE_SHA2 1
+
+/* Enable extensions on AIX 3, Interix.  */
+#ifndef _ALL_SOURCE
+# define _ALL_SOURCE 1
+#endif
+/* Enable GNU extensions on systems that have them.  */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+/* Enable threading extensions on Solaris.  */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+/* Enable extensions on HP NonStop.  */
+#ifndef _TANDEM_SOURCE
+# define _TANDEM_SOURCE 1
+#endif
+/* Enable general extensions on Solaris.  */
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__ 1
+#endif
+
+
+/* Whether the windows socket API is used */
+/* #undef USE_WINSOCK */
+
+/* the version of the windows API enabled */
+#define WINVER 0x0502
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+   significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+#  define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+/* #  undef WORDS_BIGENDIAN */
+# endif
+#endif
+
+/* Define to 1 if on MINIX. */
+/* #undef _MINIX */
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+   this defined. */
+/* #undef _POSIX_1_SOURCE */
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+/* #undef _POSIX_SOURCE */
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* in_addr_t */
+/* #undef in_addr_t */
+
+/* in_port_t */
+/* #undef in_port_t */
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+   calls it, or to nothing if 'inline' is not supported under any name.  */
+#ifndef __cplusplus
+/* #undef inline */
+#endif
+
+/* Define to `short' if <sys/types.h> does not define. */
+/* #undef int16_t */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef int32_t */
+
+/* Define to `long long' if <sys/types.h> does not define. */
+/* #undef int64_t */
+
+/* Define to `char' if <sys/types.h> does not define. */
+/* #undef int8_t */
+
+/* Define to the type of a signed integer type wide enough to hold a pointer,
+   if such a type exists, and if the system does not define it. */
+/* #undef intptr_t */
+
+/* Define to rpl_malloc if the replacement function should be used. */
+/* #undef malloc */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef pid_t */
+
+/* Define to rpl_realloc if the replacement function should be used. */
+/* #undef realloc */
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to 'int' if not defined */
+/* #undef socklen_t */
+
+/* Fallback member name for socket family in struct sockaddr_storage */
+/* #undef ss_family */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef ssize_t */
+
+/* Define to `unsigned short' if <sys/types.h> does not define. */
+/* #undef uint16_t */
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef uint32_t */
+
+/* Define to `unsigned long long' if <sys/types.h> does not define. */
+/* #undef uint64_t */
+
+/* Define to `unsigned char' if <sys/types.h> does not define. */
+/* #undef uint8_t */
+
+/* Define as `fork' if `vfork' does not work. */
+/* #undef vfork */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+
+#ifndef LITTLE_ENDIAN
+#define LITTLE_ENDIAN 1234
+#endif
+
+#ifndef BIG_ENDIAN
+#define BIG_ENDIAN 4321
+#endif
+
+#ifndef BYTE_ORDER
+#ifdef WORDS_BIGENDIAN
+#define BYTE_ORDER BIG_ENDIAN
+#else
+#define BYTE_ORDER LITTLE_ENDIAN
+#endif /* WORDS_BIGENDIAN */
+#endif /* BYTE_ORDER */
+
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_WINSOCK2_H
+#include <winsock2.h>
+#endif
+
+#ifdef HAVE_WS2TCPIP_H
+#include <ws2tcpip.h>
+#endif
+
+
+/* detect if we need to cast to unsigned int for FD_SET to avoid warnings */
+#ifdef HAVE_WINSOCK2_H
+#define FD_SET_T (u_int)
+#else
+#define FD_SET_T 
+#endif
+
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef B64_PTON
+int ldns_b64_ntop(uint8_t const *src, size_t srclength,
+	 	  char *target, size_t targsize);
+/**
+ * calculates the size needed to store the result of b64_ntop
+ */
+/*@unused@*/
+static inline size_t ldns_b64_ntop_calculate_size(size_t srcsize)
+{
+	return ((((srcsize + 2) / 3) * 4) + 1);
+}
+#endif /* !B64_PTON */
+#ifndef B64_NTOP
+int ldns_b64_pton(char const *src, uint8_t *target, size_t targsize);
+/**
+ * calculates the size needed to store the result of ldns_b64_pton
+ */
+/*@unused@*/
+static inline size_t ldns_b64_pton_calculate_size(size_t srcsize)
+{
+	return (((((srcsize + 3) / 4) * 3)) + 1);
+}
+#endif /* !B64_NTOP */
+
+#ifndef HAVE_SLEEP
+/* use windows sleep, in millisecs, instead */
+#define sleep(x) Sleep((x)*1000)
+#endif
+
+#ifndef HAVE_RANDOM
+#define srandom(x) srand(x)
+#define random(x) rand(x)
+#endif
+
+#ifndef HAVE_TIMEGM
+#include <time.h>
+time_t timegm (struct tm *tm);
+#endif /* !TIMEGM */
+#ifndef HAVE_GMTIME_R
+struct tm *gmtime_r(const time_t *timep, struct tm *result);
+#endif
+#ifndef HAVE_LOCALTIME_R
+struct tm *localtime_r(const time_t *timep, struct tm *result);
+#endif
+#ifndef HAVE_ISBLANK
+int isblank(int c);
+#endif /* !HAVE_ISBLANK */
+#ifndef HAVE_ISASCII
+int isascii(int c);
+#endif /* !HAVE_ISASCII */
+#ifndef HAVE_SNPRINTF
+#include <stdarg.h>
+int snprintf (char *str, size_t count, const char *fmt, ...);
+int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
+#endif /* HAVE_SNPRINTF */
+#ifndef HAVE_INET_PTON
+int inet_pton(int af, const char* src, void* dst);
+#endif /* HAVE_INET_PTON */
+#ifndef HAVE_INET_NTOP
+const char *inet_ntop(int af, const void *src, char *dst, size_t size);
+#endif
+#ifndef HAVE_INET_ATON
+int inet_aton(const char *cp, struct in_addr *addr);
+#endif
+#ifndef HAVE_MEMMOVE
+void *memmove(void *dest, const void *src, size_t n);
+#endif
+#ifndef HAVE_STRLCPY
+size_t strlcpy(char *dst, const char *src, size_t siz);
+#endif
+#ifdef __cplusplus
+}
+#endif
+#ifndef HAVE_GETADDRINFO
+#include "compat/fake-rfc2553.h"
+#endif
+#ifndef HAVE_STRTOUL
+#define strtoul (unsigned long)strtol
+#endif
+
diff --git a/3rdParty/Ldns/src/include/ldns/config.h.in b/3rdParty/Ldns/src/include/ldns/config.h.in
new file mode 100644
index 0000000..3393e99
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/config.h.in
@@ -0,0 +1,555 @@
+/* ldns/config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define if building universal (internal helper macro) */
+#undef AC_APPLE_UNIVERSAL_BUILD
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#undef HAVE_ARPA_INET_H
+
+/* Whether the C compiler accepts the "format" attribute */
+#undef HAVE_ATTR_FORMAT
+
+/* Whether the C compiler accepts the "unused" attribute */
+#undef HAVE_ATTR_UNUSED
+
+/* Define to 1 if you have the `b32_ntop' function. */
+#undef HAVE_B32_NTOP
+
+/* Define to 1 if you have the `b32_pton' function. */
+#undef HAVE_B32_PTON
+
+/* Define to 1 if you have the `b64_ntop' function. */
+#undef HAVE_B64_NTOP
+
+/* Define to 1 if you have the `b64_pton' function. */
+#undef HAVE_B64_PTON
+
+/* Define to 1 if you have the `bzero' function. */
+#undef HAVE_BZERO
+
+/* Define to 1 if you have the `calloc' function. */
+#undef HAVE_CALLOC
+
+/* Define to 1 if you have the `ctime_r' function. */
+#undef HAVE_CTIME_R
+
+/* Define to 1 if you have the declaration of `NID_secp384r1', and to 0 if you
+   don't. */
+#undef HAVE_DECL_NID_SECP384R1
+
+/* Define to 1 if you have the declaration of `NID_X9_62_prime256v1', and to 0
+   if you don't. */
+#undef HAVE_DECL_NID_X9_62_PRIME256V1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the `endprotoent' function. */
+#undef HAVE_ENDPROTOENT
+
+/* Define to 1 if you have the `endservent' function. */
+#undef HAVE_ENDSERVENT
+
+/* Define to 1 if you have the `EVP_sha256' function. */
+#undef HAVE_EVP_SHA256
+
+/* Define to 1 if you have the `fcntl' function. */
+#undef HAVE_FCNTL
+
+/* Define to 1 if you have the `fork' function. */
+#undef HAVE_FORK
+
+/* Whether getaddrinfo is available */
+#undef HAVE_GETADDRINFO
+
+/* Define to 1 if you have the <getopt.h> header file. */
+#undef HAVE_GETOPT_H
+
+/* Define to 1 if you have the `gmtime_r' function. */
+#undef HAVE_GMTIME_R
+
+/* If you have HMAC_CTX_init */
+#undef HAVE_HMAC_CTX_INIT
+
+/* Define to 1 if you have the `inet_aton' function. */
+#undef HAVE_INET_ATON
+
+/* Define to 1 if you have the `inet_ntop' function. */
+#undef HAVE_INET_NTOP
+
+/* Define to 1 if you have the `inet_pton' function. */
+#undef HAVE_INET_PTON
+
+/* Define to 1 if the system has the type `intptr_t'. */
+#undef HAVE_INTPTR_T
+
+/* define if you have inttypes.h */
+#undef HAVE_INTTYPES_H
+
+/* if the function 'ioctlsocket' is available */
+#undef HAVE_IOCTLSOCKET
+
+/* Define to 1 if you have the `isascii' function. */
+#undef HAVE_ISASCII
+
+/* Define to 1 if you have the `isblank' function. */
+#undef HAVE_ISBLANK
+
+/* Define to 1 if you have the `pcap' library (-lpcap). */
+#undef HAVE_LIBPCAP
+
+/* Define to 1 if you have the `localtime_r' function. */
+#undef HAVE_LOCALTIME_R
+
+/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
+   to 0 otherwise. */
+#undef HAVE_MALLOC
+
+/* Define to 1 if you have the `memmove' function. */
+#undef HAVE_MEMMOVE
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the `memset' function. */
+#undef HAVE_MEMSET
+
+/* Define to 1 if you have the <netdb.h> header file. */
+#undef HAVE_NETDB_H
+
+/* Define to 1 if you have the <netinet/if_ether.h> header file. */
+#undef HAVE_NETINET_IF_ETHER_H
+
+/* Define to 1 if you have the <netinet/igmp.h> header file. */
+#undef HAVE_NETINET_IGMP_H
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#undef HAVE_NETINET_IN_H
+
+/* Define to 1 if you have the <netinet/in_systm.h> header file. */
+#undef HAVE_NETINET_IN_SYSTM_H
+
+/* Define to 1 if you have the <netinet/ip6.h> header file. */
+#undef HAVE_NETINET_IP6_H
+
+/* Define to 1 if you have the <netinet/ip_compat.h> header file. */
+#undef HAVE_NETINET_IP_COMPAT_H
+
+/* Define to 1 if you have the <netinet/ip.h> header file. */
+#undef HAVE_NETINET_IP_H
+
+/* Define to 1 if you have the <netinet/udp.h> header file. */
+#undef HAVE_NETINET_UDP_H
+
+/* Define to 1 if you have the <net/ethernet.h> header file. */
+#undef HAVE_NET_ETHERNET_H
+
+/* Define to 1 if you have the <net/if.h> header file. */
+#undef HAVE_NET_IF_H
+
+/* Define to 1 if you have the <openssl/err.h> header file. */
+#undef HAVE_OPENSSL_ERR_H
+
+/* Define to 1 if you have the <openssl/rand.h> header file. */
+#undef HAVE_OPENSSL_RAND_H
+
+/* Define to 1 if you have the <openssl/ssl.h> header file. */
+#undef HAVE_OPENSSL_SSL_H
+
+/* Define to 1 if you have the <pcap.h> header file. */
+#undef HAVE_PCAP_H
+
+/* If available, contains the Python version number currently in use. */
+#undef HAVE_PYTHON
+
+/* Define to 1 if you have the `random' function. */
+#undef HAVE_RANDOM
+
+/* Define to 1 if your system has a GNU libc compatible `realloc' function,
+   and to 0 otherwise. */
+#undef HAVE_REALLOC
+
+/* Define to 1 if you have the `sleep' function. */
+#undef HAVE_SLEEP
+
+/* Define to 1 if you have the `snprintf' function. */
+#undef HAVE_SNPRINTF
+
+/* Define if you have the SSL libraries installed. */
+#undef HAVE_SSL
+
+/* Define to 1 if you have the <stdarg.h> header file. */
+#undef HAVE_STDARG_H
+
+/* Define to 1 if stdbool.h conforms to C99. */
+#undef HAVE_STDBOOL_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `strlcpy' function. */
+#undef HAVE_STRLCPY
+
+/* Define to 1 if you have the `strtoul' function. */
+#undef HAVE_STRTOUL
+
+/* Define if you have SWIG libraries and header files. */
+#undef HAVE_SWIG
+
+/* Define to 1 if you have the <sys/mount.h> header file. */
+#undef HAVE_SYS_MOUNT_H
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#undef HAVE_SYS_PARAM_H
+
+/* define if you have sys/socket.h */
+#undef HAVE_SYS_SOCKET_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* define if you have sys/types.h */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the `timegm' function. */
+#undef HAVE_TIMEGM
+
+/* Define to 1 if you have the <time.h> header file. */
+#undef HAVE_TIME_H
+
+/* define if you have unistd.h */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if you have the `vfork' function. */
+#undef HAVE_VFORK
+
+/* Define to 1 if you have the <vfork.h> header file. */
+#undef HAVE_VFORK_H
+
+/* Define to 1 if you have the <winsock2.h> header file. */
+#undef HAVE_WINSOCK2_H
+
+/* Define to 1 if `fork' works. */
+#undef HAVE_WORKING_FORK
+
+/* Define to 1 if `vfork' works. */
+#undef HAVE_WORKING_VFORK
+
+/* Define to 1 if you have the <ws2tcpip.h> header file. */
+#undef HAVE_WS2TCPIP_H
+
+/* Define to 1 if the system has the type `_Bool'. */
+#undef HAVE__BOOL
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#undef LT_OBJDIR
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* The size of `time_t', as computed by sizeof. */
+#undef SIZEOF_TIME_T
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* System configuration dir */
+#undef SYSCONFDIR
+
+/* Define this to enable ECDSA support. */
+#undef USE_ECDSA
+
+/* Define this to enable GOST support. */
+#undef USE_GOST
+
+/* Define this to enable SHA256 and SHA512 support. */
+#undef USE_SHA2
+
+/* Enable extensions on AIX 3, Interix.  */
+#ifndef _ALL_SOURCE
+# undef _ALL_SOURCE
+#endif
+/* Enable GNU extensions on systems that have them.  */
+#ifndef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+/* Enable threading extensions on Solaris.  */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# undef _POSIX_PTHREAD_SEMANTICS
+#endif
+/* Enable extensions on HP NonStop.  */
+#ifndef _TANDEM_SOURCE
+# undef _TANDEM_SOURCE
+#endif
+/* Enable general extensions on Solaris.  */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
+
+
+/* Whether the windows socket API is used */
+#undef USE_WINSOCK
+
+/* the version of the windows API enabled */
+#undef WINVER
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+   significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+#  define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+#  undef WORDS_BIGENDIAN
+# endif
+#endif
+
+/* Define to 1 if on MINIX. */
+#undef _MINIX
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+   this defined. */
+#undef _POSIX_1_SOURCE
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+#undef _POSIX_SOURCE
+
+/* Define to empty if `const' does not conform to ANSI C. */
+#undef const
+
+/* in_addr_t */
+#undef in_addr_t
+
+/* in_port_t */
+#undef in_port_t
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+   calls it, or to nothing if 'inline' is not supported under any name.  */
+#ifndef __cplusplus
+#undef inline
+#endif
+
+/* Define to `short' if <sys/types.h> does not define. */
+#undef int16_t
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef int32_t
+
+/* Define to `long long' if <sys/types.h> does not define. */
+#undef int64_t
+
+/* Define to `char' if <sys/types.h> does not define. */
+#undef int8_t
+
+/* Define to the type of a signed integer type wide enough to hold a pointer,
+   if such a type exists, and if the system does not define it. */
+#undef intptr_t
+
+/* Define to rpl_malloc if the replacement function should be used. */
+#undef malloc
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef pid_t
+
+/* Define to rpl_realloc if the replacement function should be used. */
+#undef realloc
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef size_t
+
+/* Define to 'int' if not defined */
+#undef socklen_t
+
+/* Fallback member name for socket family in struct sockaddr_storage */
+#undef ss_family
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef ssize_t
+
+/* Define to `unsigned short' if <sys/types.h> does not define. */
+#undef uint16_t
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef uint32_t
+
+/* Define to `unsigned long long' if <sys/types.h> does not define. */
+#undef uint64_t
+
+/* Define to `unsigned char' if <sys/types.h> does not define. */
+#undef uint8_t
+
+/* Define as `fork' if `vfork' does not work. */
+#undef vfork
+
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+
+#ifndef LITTLE_ENDIAN
+#define LITTLE_ENDIAN 1234
+#endif
+
+#ifndef BIG_ENDIAN
+#define BIG_ENDIAN 4321
+#endif
+
+#ifndef BYTE_ORDER
+#ifdef WORDS_BIGENDIAN
+#define BYTE_ORDER BIG_ENDIAN
+#else
+#define BYTE_ORDER LITTLE_ENDIAN
+#endif /* WORDS_BIGENDIAN */
+#endif /* BYTE_ORDER */
+
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_WINSOCK2_H
+#include <winsock2.h>
+#endif
+
+#ifdef HAVE_WS2TCPIP_H
+#include <ws2tcpip.h>
+#endif
+
+
+/* detect if we need to cast to unsigned int for FD_SET to avoid warnings */
+#ifdef HAVE_WINSOCK2_H
+#define FD_SET_T (u_int)
+#else
+#define FD_SET_T 
+#endif
+
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef B64_PTON
+int ldns_b64_ntop(uint8_t const *src, size_t srclength,
+	 	  char *target, size_t targsize);
+/**
+ * calculates the size needed to store the result of b64_ntop
+ */
+/*@unused@*/
+static inline size_t ldns_b64_ntop_calculate_size(size_t srcsize)
+{
+	return ((((srcsize + 2) / 3) * 4) + 1);
+}
+#endif /* !B64_PTON */
+#ifndef B64_NTOP
+int ldns_b64_pton(char const *src, uint8_t *target, size_t targsize);
+/**
+ * calculates the size needed to store the result of ldns_b64_pton
+ */
+/*@unused@*/
+static inline size_t ldns_b64_pton_calculate_size(size_t srcsize)
+{
+	return (((((srcsize + 3) / 4) * 3)) + 1);
+}
+#endif /* !B64_NTOP */
+
+#ifndef HAVE_SLEEP
+/* use windows sleep, in millisecs, instead */
+#define sleep(x) Sleep((x)*1000)
+#endif
+
+#ifndef HAVE_RANDOM
+#define srandom(x) srand(x)
+#define random(x) rand(x)
+#endif
+
+#ifndef HAVE_TIMEGM
+#include <time.h>
+time_t timegm (struct tm *tm);
+#endif /* !TIMEGM */
+#ifndef HAVE_GMTIME_R
+struct tm *gmtime_r(const time_t *timep, struct tm *result);
+#endif
+#ifndef HAVE_LOCALTIME_R
+struct tm *localtime_r(const time_t *timep, struct tm *result);
+#endif
+#ifndef HAVE_ISBLANK
+int isblank(int c);
+#endif /* !HAVE_ISBLANK */
+#ifndef HAVE_ISASCII
+int isascii(int c);
+#endif /* !HAVE_ISASCII */
+#ifndef HAVE_SNPRINTF
+#include <stdarg.h>
+int snprintf (char *str, size_t count, const char *fmt, ...);
+int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
+#endif /* HAVE_SNPRINTF */
+#ifndef HAVE_INET_PTON
+int inet_pton(int af, const char* src, void* dst);
+#endif /* HAVE_INET_PTON */
+#ifndef HAVE_INET_NTOP
+const char *inet_ntop(int af, const void *src, char *dst, size_t size);
+#endif
+#ifndef HAVE_INET_ATON
+int inet_aton(const char *cp, struct in_addr *addr);
+#endif
+#ifndef HAVE_MEMMOVE
+void *memmove(void *dest, const void *src, size_t n);
+#endif
+#ifndef HAVE_STRLCPY
+size_t strlcpy(char *dst, const char *src, size_t siz);
+#endif
+#ifdef __cplusplus
+}
+#endif
+#ifndef HAVE_GETADDRINFO
+#include "compat/fake-rfc2553.h"
+#endif
+#ifndef HAVE_STRTOUL
+#define strtoul (unsigned long)strtol
+#endif
+
diff --git a/3rdParty/Ldns/src/include/ldns/dname.h b/3rdParty/Ldns/src/include/ldns/dname.h
new file mode 100644
index 0000000..a91f075
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/dname.h
@@ -0,0 +1,201 @@
+/*
+ * dname.h
+ *
+ * dname definitions
+ *
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2004-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+/**
+ * \file dname.h
+ *
+ * dname contains function to read and manipulate domain names.
+ *
+ * Example domain names are "www.nlnetlabs.nl." and "." (the root)
+ *
+ * If a domain name ends with a dot ("."), it is called a Fully Qualified
+ * Domain Name (FQDN). In certain places (for instance when reading a zone
+ * file), an origin (which is just another domain name) non-FQDNs will be
+ * placed after the current. For instance, if i have a zone file where the
+ * origin has been set to "nl.", and my file contains the name
+ * "www.nlnetlabs", it will result in "www.nlnetlabs.nl.". Internally, dnames are
+ * always absolute (the dot is added when it is missing and there is no origin).
+ *
+ * An FQDN is also
+ * known as an absolute domain name, therefore the function to check this is
+ * called \ref ldns_dname_str_absolute
+ *
+ * Domain names are stored in \ref ldns_rdf structures, with the type
+ * \ref LDNS_RDF_TYPE_DNAME
+ *
+ * This module is *NOT* about the RR type called DNAME.
+ */
+
+
+#ifndef LDNS_DNAME_H
+#define LDNS_DNAME_H
+
+#include <ldns/common.h>
+#include <ldns/rdata.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LDNS_DNAME_NORMALIZE        tolower
+
+/**
+ * concatenates two dnames together
+ * \param[in] rd1 the leftside
+ * \param[in] rd2 the rightside
+ * \return a new rdf with leftside/rightside
+ */
+ldns_rdf *ldns_dname_cat_clone(const ldns_rdf *rd1, const ldns_rdf *rd2);
+
+/**
+ * concatenates rd2 after rd1 (rd2 is copied, rd1 is modified)
+ * \param[in] rd1 the leftside
+ * \param[in] rd2 the rightside
+ * \return LDNS_STATUS_OK on success
+ */
+ldns_status 	ldns_dname_cat(ldns_rdf *rd1, ldns_rdf *rd2);
+
+/**
+ * Returns a clone of the given dname with the labels
+ * reversed
+ * \param[in] d the dname to reverse
+ * \return clone of the dname with the labels reversed.
+ */
+ldns_rdf *ldns_dname_reverse(const ldns_rdf *d);
+
+/**
+ * Clones the given dname from the nth label on
+ * \param[in] d The dname to clone
+ * \param[in] n the label nr to clone from, if this is 0, the complete
+ *              dname is cloned
+ * \return A newly allocated *rdf structure, containing the cloned dname,
+ *         or NULL if either d was NULL, not a dname, or if n >=
+ *         label_count
+ */
+ldns_rdf *
+ldns_dname_clone_from(const ldns_rdf *d, uint16_t n);
+
+/**
+ * chop one label off the left side of a dname. so
+ * wwww.nlnetlabs.nl, becomes nlnetlabs.nl
+ * This new name is a clone and must be freed with ldns_deep_free()
+ * \param[in] d the dname to chop
+ * \return the remaining dname
+ */
+ldns_rdf *ldns_dname_left_chop(const ldns_rdf *d);
+
+/**
+ * count the number of labels inside a LDNS_RDF_DNAME type rdf.
+ * \param[in] *r the rdf
+ * \return the number of labels
+ */
+uint8_t  ldns_dname_label_count(const ldns_rdf *r);
+
+/**
+ * creates a new dname rdf from a string.
+ * \param[in] str string to use
+ * \return ldns_rdf* or NULL in case of an error
+ */
+ldns_rdf *ldns_dname_new_frm_str(const char *str);
+
+/**
+ * Create a new dname rdf from a string
+ * \param[in] s the size of the new dname
+ * \param[in] *data pointer to the actual data
+ * \return ldns_rdf*
+ */
+ldns_rdf *ldns_dname_new(uint16_t s, void *data);
+
+/**
+ * Create a new dname rdf from data (the data is copied)
+ * \param[in] size the size of the data
+ * \param[in] *data pointer to the actual data
+ * \return ldns_rdf*
+ */
+ldns_rdf *ldns_dname_new_frm_data(uint16_t size, const void *data);
+
+/**
+ * Put a dname into canonical fmt - ie. lowercase it
+ * \param[in] rdf the dname to lowercase
+ * \return void
+ */
+void ldns_dname2canonical(const ldns_rdf *rdf);
+
+/**
+ * test wether the name sub falls under parent (i.e. is a subdomain
+ * of parent). This function will return false if the given dnames are
+ * equal.
+ * \param[in] sub the name to test
+ * \param[in] parent the parent's name
+ * \return true if sub falls under parent, otherwise false
+ */
+bool ldns_dname_is_subdomain(const ldns_rdf *sub, const ldns_rdf *parent);
+
+/**
+ * Compares the two dname rdf's according to the algorithm for ordering
+ * in RFC4034 Section 6.
+ * \param[in] dname1 First dname rdf to compare
+ * \param[in] dname2 Second dname rdf to compare
+ * \return -1 if dname1 comes before dname2, 1 if dname1 comes after dname2, and 0 if they are equal.
+ */
+int ldns_dname_compare(const ldns_rdf *dname1, const ldns_rdf *dname2);
+
+/**
+ * Checks whether the dname matches the given wildcard
+ * \param[in] dname The dname to check
+ * \param[in] wildcard The wildcard to check with
+ * \return 1 If the wildcard matches, OR if 'wildcard' is not a wildcard and
+ *           the names are *exactly* the same
+ *         0 If the wildcard does not match, or if it is not a wildcard and
+ *           the names are not the same
+ */
+int ldns_dname_match_wildcard(const ldns_rdf *dname, const ldns_rdf *wildcard);
+
+/**
+ * check if middle lays in the interval defined by prev and next
+ * prev <= middle < next. This is usefull for nsec checking
+ * \param[in] prev the previous dname
+ * \param[in] middle the dname to check
+ * \param[in] next the next dname
+ * return 0 on error or unknown, -1 when middle is in the interval, +1 when not
+ */
+int ldns_dname_interval(const ldns_rdf *prev, const ldns_rdf *middle, const ldns_rdf *next);
+
+/**
+ * Checks whether the given dname string is absolute (i.e. ends with a '.')
+ * \param[in] *dname_str a string representing the dname
+ * \return true or false
+ */
+bool ldns_dname_str_absolute(const char *dname_str);
+
+/**
+ * look inside the rdf and if it is an LDNS_RDF_TYPE_DNAME
+ * try and retrieve a specific label. The labels are numbered
+ * starting from 0 (left most).
+ * \param[in] rdf the rdf to look in
+ * \param[in] labelpos return the label with this number
+ * \return a ldns_rdf* with the label as name or NULL on error
+ */
+ldns_rdf * ldns_dname_label(const ldns_rdf *rdf, uint8_t labelpos);
+
+/**
+ * Check if dname is a wildcard, starts with *.
+ * \param[in] dname: the rdf to look in
+ * \return true if a wildcard, false if not.
+ */
+int ldns_dname_is_wildcard(const ldns_rdf* dname);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* LDNS_DNAME_H */
diff --git a/3rdParty/Ldns/src/include/ldns/dnssec.h b/3rdParty/Ldns/src/include/ldns/dnssec.h
new file mode 100644
index 0000000..9e602b5
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/dnssec.h
@@ -0,0 +1,522 @@
+/*
+ * dnssec.h -- defines for the Domain Name System (SEC) (DNSSEC)
+ *
+ * Copyright (c) 2005-2008, NLnet Labs. All rights reserved.
+ *
+ * See LICENSE for the license.
+ *
+ * A bunch of defines that are used in the DNS
+ */
+
+/**
+ * \file dnssec.h
+ *
+ * This module contains base functions for DNSSEC operations
+ * (RFC4033 t/m RFC4035).
+ * 
+ * Since those functions heavily rely op cryptographic operations,
+ * this module is dependent on openssl.
+ * 
+ */
+ 
+
+#ifndef LDNS_DNSSEC_H
+#define LDNS_DNSSEC_H
+
+#include <ldns/common.h>
+#if LDNS_BUILD_CONFIG_HAVE_SSL
+#include <openssl/ssl.h>
+#include <openssl/evp.h>
+#endif /* LDNS_BUILD_CONFIG_HAVE_SSL */
+#include <ldns/packet.h>
+#include <ldns/keys.h>
+#include <ldns/zone.h>
+#include <ldns/resolver.h>
+#include <ldns/dnssec_zone.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LDNS_MAX_KEYLEN		2048
+#define LDNS_DNSSEC_KEYPROTO	3
+/* default time before sigs expire */
+#define LDNS_DEFAULT_EXP_TIME	2419200 /* 4 weeks */
+
+/** return values for the old-signature callback */
+#define LDNS_SIGNATURE_LEAVE_ADD_NEW 0
+#define LDNS_SIGNATURE_LEAVE_NO_ADD 1
+#define LDNS_SIGNATURE_REMOVE_ADD_NEW 2
+#define LDNS_SIGNATURE_REMOVE_NO_ADD 3
+
+/**
+ * Returns the first RRSIG rr that corresponds to the rrset 
+ * with the given name and type
+ * 
+ * \param[in] name The dname of the RRset covered by the RRSIG to find
+ * \param[in] type The type of the RRset covered by the RRSIG to find
+ * \param[in] rrs List of rrs to search in
+ * \returns Pointer to the first RRsig ldns_rr found, or NULL if it is
+ * not present
+ */
+ldns_rr *ldns_dnssec_get_rrsig_for_name_and_type(const ldns_rdf *name,
+									    const ldns_rr_type type,
+									    const ldns_rr_list *rrs);
+
+/**
+ * Returns the DNSKEY that corresponds to the given RRSIG rr from the list, if
+ * any
+ *
+ * \param[in] rrsig The rrsig to find the DNSKEY for
+ * \param[in] rrs The rr list to find the key in
+ * \return The DNSKEY that corresponds to the given RRSIG, or NULL if it was
+ *         not found.
+ */
+ldns_rr *ldns_dnssec_get_dnskey_for_rrsig(const ldns_rr *rrsig, const ldns_rr_list *rrs);
+
+/**
+ * Returns the rdata field that contains the bitmap of the covered types of
+ * the given NSEC record
+ *
+ * \param[in] nsec The nsec to get the covered type bitmap of
+ * \return An ldns_rdf containing the bitmap, or NULL on error
+ */
+ldns_rdf *ldns_nsec_get_bitmap(ldns_rr *nsec);
+
+
+#define LDNS_NSEC3_MAX_ITERATIONS 65535
+
+/**
+ * Returns the dname of the closest (provable) encloser
+ */
+ldns_rdf *
+ldns_dnssec_nsec3_closest_encloser(ldns_rdf *qname,
+							ldns_rr_type qtype,
+							ldns_rr_list *nsec3s);
+
+/**
+ * Checks whether the packet contains rrsigs
+ */
+bool
+ldns_dnssec_pkt_has_rrsigs(const ldns_pkt *pkt);
+
+/**
+ * Returns a ldns_rr_list containing the signatures covering the given name
+ * and type
+ */
+ldns_rr_list *ldns_dnssec_pkt_get_rrsigs_for_name_and_type(const ldns_pkt *pkt, ldns_rdf *name, ldns_rr_type type);
+
+/**
+ * Returns a ldns_rr_list containing the signatures covering the given type
+ */
+ldns_rr_list *ldns_dnssec_pkt_get_rrsigs_for_type(const ldns_pkt *pkt, ldns_rr_type type);
+
+/** 
+ * calculates a keytag of a key for use in DNSSEC.
+ *
+ * \param[in] key the key as an RR to use for the calc.
+ * \return the keytag
+ */
+uint16_t ldns_calc_keytag(const ldns_rr *key);
+
+/**
+ * Calculates keytag of DNSSEC key, operates on wireformat rdata.
+ * \param[in] key the key as uncompressed wireformat rdata.
+ * \param[in] keysize length of key data.
+ * \return the keytag
+ */
+uint16_t ldns_calc_keytag_raw(uint8_t* key, size_t keysize);
+
+#if LDNS_BUILD_CONFIG_HAVE_SSL
+/**
+ * converts a buffer holding key material to a DSA key in openssl.
+ *
+ * \param[in] key the key to convert
+ * \return a DSA * structure with the key material
+ */
+DSA *ldns_key_buf2dsa(ldns_buffer *key);
+/**
+ * Like ldns_key_buf2dsa, but uses raw buffer.
+ * \param[in] key the uncompressed wireformat of the key.
+ * \param[in] len length of key data
+ * \return a DSA * structure with the key material
+ */
+DSA *ldns_key_buf2dsa_raw(unsigned char* key, size_t len);
+
+/**
+ * Utility function to calculate hash using generic EVP_MD pointer.
+ * \param[in] data the data to hash.
+ * \param[in] len  length of data.
+ * \param[out] dest the destination of the hash, must be large enough.
+ * \param[in] md the message digest to use.
+ * \return true if worked, false on failure.
+ */
+int ldns_digest_evp(unsigned char* data, unsigned int len, 
+	unsigned char* dest, const EVP_MD* md);
+
+/**
+ * Converts a holding buffer with key material to EVP PKEY in openssl.
+ * Only available if ldns was compiled with GOST.
+ * \param[in] key data to convert
+ * \param[in] keylen length of the key data
+ * \return the key or NULL on error.
+ */
+EVP_PKEY* ldns_gost2pkey_raw(unsigned char* key, size_t keylen);
+
+/**
+ * Converts a holding buffer with key material to EVP PKEY in openssl.
+ * Only available if ldns was compiled with ECDSA.
+ * \param[in] key data to convert
+ * \param[in] keylen length of the key data
+ * \param[in] algo precise algorithm to initialize ECC group values.
+ * \return the key or NULL on error.
+ */
+EVP_PKEY* ldns_ecdsa2pkey_raw(unsigned char* key, size_t keylen, uint8_t algo);
+
+#endif /* LDNS_BUILD_CONFIG_HAVE_SSL */
+
+#if LDNS_BUILD_CONFIG_HAVE_SSL
+/**
+ * converts a buffer holding key material to a RSA key in openssl.
+ *
+ * \param[in] key the key to convert
+ * \return a RSA * structure with the key material
+ */
+RSA *ldns_key_buf2rsa(ldns_buffer *key);
+
+/**
+ * Like ldns_key_buf2rsa, but uses raw buffer.
+ * \param[in] key the uncompressed wireformat of the key.
+ * \param[in] len length of key data
+ * \return a RSA * structure with the key material
+ */
+RSA *ldns_key_buf2rsa_raw(unsigned char* key, size_t len);
+#endif /* LDNS_BUILD_CONFIG_HAVE_SSL */
+
+/** 
+ * returns a new DS rr that represents the given key rr.
+ *
+ * \param[in] *key the key to convert
+ * \param[in] h the hash to use LDNS_SHA1/LDNS_SHA256
+ * \return ldns_rr* a new rr pointer to a DS
+ */
+ldns_rr *ldns_key_rr2ds(const ldns_rr *key, ldns_hash h);
+
+/**
+ * Create the type bitmap for an NSEC(3) record
+ */
+ldns_rdf *
+ldns_dnssec_create_nsec_bitmap(ldns_rr_type rr_type_list[],
+						 size_t size,
+						 ldns_rr_type nsec_type);
+
+/**
+ * returns whether a rrset of the given type is found in the rrsets.
+ *
+ * \param[in] rrsets the rrsets to be tested
+ * \param[in] type the type to test for
+ * \return int 1 if the type was found, 0 otherwise.
+ */
+int
+ldns_dnssec_rrsets_contains_type (ldns_dnssec_rrsets *rrsets, ldns_rr_type type);
+
+/**
+ * Creates NSEC
+ */
+ldns_rr *
+ldns_dnssec_create_nsec(ldns_dnssec_name *from,
+				    ldns_dnssec_name *to,
+				    ldns_rr_type nsec_type);
+
+
+/**
+ * Creates NSEC3
+ */
+ldns_rr *
+ldns_dnssec_create_nsec3(ldns_dnssec_name *from,
+					ldns_dnssec_name *to,
+					ldns_rdf *zone_name,
+					uint8_t algorithm,
+					uint8_t flags,
+					uint16_t iterations,
+					uint8_t salt_length,
+					uint8_t *salt);
+
+/**
+ * Create a NSEC record
+ * \param[in] cur_owner the current owner which should be taken as the starting point
+ * \param[in] next_owner the rrlist which the nsec rr should point to 
+ * \param[in] rrs all rrs from the zone, to find all RR types of cur_owner in
+ * \return a ldns_rr with the nsec record in it
+ */
+ldns_rr * ldns_create_nsec(ldns_rdf *cur_owner, ldns_rdf *next_owner, ldns_rr_list *rrs);
+
+/**
+ * Calculates the hashed name using the given parameters
+ * \param[in] *name The owner name to calculate the hash for 
+ * \param[in] algorithm The hash algorithm to use
+ * \param[in] iterations The number of hash iterations to use
+ * \param[in] salt_length The length of the salt in bytes
+ * \param[in] salt The salt to use
+ * \return The hashed owner name rdf, without the domain name
+ */
+ldns_rdf *ldns_nsec3_hash_name(ldns_rdf *name, uint8_t algorithm, uint16_t iterations, uint8_t salt_length, uint8_t *salt);
+
+/**
+ * Sets all the NSEC3 options. The rr to set them in must be initialized with _new() and
+ * type LDNS_RR_TYPE_NSEC3
+ * \param[in] *rr The RR to set the values in
+ * \param[in] algorithm The NSEC3 hash algorithm 
+ * \param[in] flags The flags field 
+ * \param[in] iterations The number of hash iterations
+ * \param[in] salt_length The length of the salt in bytes 
+ * \param[in] salt The salt bytes
+ */
+void ldns_nsec3_add_param_rdfs(ldns_rr *rr,
+						 uint8_t algorithm,
+						 uint8_t flags,
+						 uint16_t iterations,
+						 uint8_t salt_length,
+						 uint8_t *salt);
+
+/* this will NOT return the NSEC3  completed, you will have to run the
+   finalize function on the rrlist later! */
+ldns_rr *
+ldns_create_nsec3(ldns_rdf *cur_owner,
+                  ldns_rdf *cur_zone,
+                  ldns_rr_list *rrs,
+                  uint8_t algorithm,
+                  uint8_t flags,
+                  uint16_t iterations,
+                  uint8_t salt_length,
+                  uint8_t *salt,
+                  bool emptynonterminal);
+
+/**
+ * Returns the hash algorithm used in the given NSEC3 RR
+ * \param[in] *nsec3_rr The RR to read from
+ * \return The algorithm identifier, or 0 on error
+ */
+uint8_t ldns_nsec3_algorithm(const ldns_rr *nsec3_rr);
+
+/**
+ * Returns flags field
+ */
+uint8_t
+ldns_nsec3_flags(const ldns_rr *nsec3_rr);
+
+/**
+ * Returns true if the opt-out flag has been set in the given NSEC3 RR
+ * \param[in] *nsec3_rr The RR to read from
+ * \return true if the RR has type NSEC3 and the opt-out bit has been set, false otherwise
+ */
+bool ldns_nsec3_optout(const ldns_rr *nsec3_rr);
+
+/**
+ * Returns the number of hash iterations used in the given NSEC3 RR
+ * \param[in] *nsec3_rr The RR to read from
+ * \return The number of iterations
+ */
+uint16_t ldns_nsec3_iterations(const ldns_rr *nsec3_rr);
+
+/**
+ * Returns the salt used in the given NSEC3 RR
+ * \param[in] *nsec3_rr The RR to read from
+ * \return The salt rdf, or NULL on error
+ */
+ldns_rdf *ldns_nsec3_salt(const ldns_rr *nsec3_rr);
+
+/**
+ * Returns the length of the salt used in the given NSEC3 RR
+ * \param[in] *nsec3_rr The RR to read from
+ * \return The length of the salt in bytes
+ */
+uint8_t ldns_nsec3_salt_length(const ldns_rr *nsec3_rr);
+
+/**
+ * Returns the salt bytes used in the given NSEC3 RR
+ * \param[in] *nsec3_rr The RR to read from
+ * \return The salt in bytes, this is alloced, so you need to free it
+ */
+uint8_t *ldns_nsec3_salt_data(const ldns_rr *nsec3_rr);
+
+/**
+ * Returns the first label of the next ownername in the NSEC3 chain (ie. without the domain)
+ * \param[in] nsec3_rr The RR to read from
+ * \return The first label of the next owner name in the NSEC3 chain, or NULL on error 
+ */
+ldns_rdf *ldns_nsec3_next_owner(const ldns_rr *nsec3_rr);
+
+/**
+ * Returns the bitmap specifying the covered types of the given NSEC3 RR
+ * \param[in] *nsec3_rr The RR to read from
+ * \return The covered type bitmap rdf
+ */
+ldns_rdf *ldns_nsec3_bitmap(const ldns_rr *nsec3_rr);
+
+/**
+ * Calculates the hashed name using the parameters of the given NSEC3 RR
+ * \param[in] *nsec The RR to use the parameters from
+ * \param[in] *name The owner name to calculate the hash for 
+ * \return The hashed owner name rdf, without the domain name
+ */
+ldns_rdf *ldns_nsec3_hash_name_frm_nsec3(const ldns_rr *nsec, ldns_rdf *name);
+
+/**
+ * Checks coverage of NSEC RR type bitmap
+ * \param[in] nsec_bitmap The NSEC bitmap rdata field to check
+ * \param[in] type The type to check
+ * \return true if the NSEC RR covers the type
+ */
+bool ldns_nsec_bitmap_covers_type(const ldns_rdf *nsec_bitmap, ldns_rr_type type);
+
+/**
+ * Checks coverage of NSEC(3) RR name span
+ * Remember that nsec and name must both be in canonical form (ie use
+ * \ref ldns_rr2canonical and \ref ldns_dname2canonical prior to calling this
+ * function)
+ *
+ * \param[in] nsec The NSEC RR to check
+ * \param[in] name The owner dname to check, if the nsec record is a NSEC3 record, this should be the hashed name
+ * \return true if the NSEC RR covers the owner name
+ */
+bool ldns_nsec_covers_name(const ldns_rr *nsec, const ldns_rdf *name);
+
+#if LDNS_BUILD_CONFIG_HAVE_SSL
+/**
+ * verify a packet 
+ * \param[in] p the packet
+ * \param[in] t the rr set type to check
+ * \param[in] o the rr set name to check
+ * \param[in] k list of keys
+ * \param[in] s list of sigs (may be null)
+ * \param[out] good_keys keys which validated the packet
+ * \return status 
+ * 
+ */
+ldns_status ldns_pkt_verify(ldns_pkt *p, ldns_rr_type t, ldns_rdf *o, ldns_rr_list *k, ldns_rr_list *s, ldns_rr_list *good_keys);
+
+/**
+ * verify a packet 
+ * \param[in] p the packet
+ * \param[in] t the rr set type to check
+ * \param[in] o the rr set name to check
+ * \param[in] k list of keys
+ * \param[in] s list of sigs (may be null)
+ * \param[in] check_time the time for which the validation is performed
+ * \param[out] good_keys keys which validated the packet
+ * \return status 
+ * 
+ */
+ldns_status ldns_pkt_verify_time(ldns_pkt *p, ldns_rr_type t, ldns_rdf *o, ldns_rr_list *k, ldns_rr_list *s, time_t check_time, ldns_rr_list *good_keys);
+
+#endif
+
+/**
+ * chains nsec3 list
+ */
+ldns_status
+ldns_dnssec_chain_nsec3_list(ldns_rr_list *nsec3_rrs);
+
+/**
+ * compare for nsec3 sort
+ */
+int
+qsort_rr_compare_nsec3(const void *a, const void *b);
+
+/**
+ * sort nsec3 list
+ */
+void
+ldns_rr_list_sort_nsec3(ldns_rr_list *unsorted);
+
+/** 
+ * Default callback function to always leave present signatures, and
+ * add new ones
+ * \param[in] sig The signature to check for removal (unused)
+ * \param[in] n Optional argument (unused)
+ * \return LDNS_SIGNATURE_LEAVE_ADD_NEW
+ */
+int ldns_dnssec_default_add_to_signatures(ldns_rr *sig, void *n);
+/** 
+ * Default callback function to always leave present signatures, and
+ * add no new ones for the keys of these signatures
+ * \param[in] sig The signature to check for removal (unused)
+ * \param[in] n Optional argument (unused)
+ * \return LDNS_SIGNATURE_LEAVE_NO_ADD
+ */
+int ldns_dnssec_default_leave_signatures(ldns_rr *sig, void *n);
+/** 
+ * Default callback function to always remove present signatures, but
+ * add no new ones
+ * \param[in] sig The signature to check for removal (unused)
+ * \param[in] n Optional argument (unused)
+ * \return LDNS_SIGNATURE_REMOVE_NO_ADD
+ */
+int ldns_dnssec_default_delete_signatures(ldns_rr *sig, void *n);
+/** 
+ * Default callback function to always leave present signatures, and
+ * add new ones
+ * \param[in] sig The signature to check for removal (unused)
+ * \param[in] n Optional argument (unused)
+ * \return LDNS_SIGNATURE_REMOVE_ADD_NEW
+ */
+int ldns_dnssec_default_replace_signatures(ldns_rr *sig, void *n);
+
+#if LDNS_BUILD_CONFIG_HAVE_SSL
+/**
+ * Converts the DSA signature from ASN1 representation (RFC2459, as 
+ * used by OpenSSL) to raw signature data as used in DNS (rfc2536)
+ *
+ * \param[in] sig The signature in RFC2459 format
+ * \param[in] sig_len The length of the signature
+ * \return a new rdf with the signature
+ */
+ldns_rdf *
+ldns_convert_dsa_rrsig_asn12rdf(const ldns_buffer *sig,
+						  const long sig_len);
+
+/**
+ * Converts the RRSIG signature RDF (in rfc2536 format) to a buffer
+ * with the signature in rfc2459 format
+ *
+ * \param[out] target_buffer buffer to place the signature data
+ * \param[in] sig_rdf The signature rdf to convert
+ * \return LDNS_STATUS_OK on success, error code otherwise
+ */
+ldns_status
+ldns_convert_dsa_rrsig_rdf2asn1(ldns_buffer *target_buffer,
+						  const ldns_rdf *sig_rdf);
+
+/**
+ * Converts the ECDSA signature from ASN1 representation (as 
+ * used by OpenSSL) to raw signature data as used in DNS
+ * This routine is only present if ldns is compiled with ecdsa support.
+ *
+ * \param[in] sig The signature in ASN1 format
+ * \param[in] sig_len The length of the signature
+ * \return a new rdf with the signature
+ */
+ldns_rdf *
+ldns_convert_ecdsa_rrsig_asn12rdf(const ldns_buffer *sig, const long sig_len);
+
+/**
+ * Converts the RRSIG signature RDF (from DNS) to a buffer with the 
+ * signature in ASN1 format as openssl uses it.
+ * This routine is only present if ldns is compiled with ecdsa support.
+ *
+ * \param[out] target_buffer buffer to place the signature data in ASN1.
+ * \param[in] sig_rdf The signature rdf to convert
+ * \return LDNS_STATUS_OK on success, error code otherwise
+ */
+ldns_status
+ldns_convert_ecdsa_rrsig_rdf2asn1(ldns_buffer *target_buffer,
+        const ldns_rdf *sig_rdf);
+
+#endif /* LDNS_BUILD_CONFIG_HAVE_SSL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LDNS_DNSSEC_H */
diff --git a/3rdParty/Ldns/src/include/ldns/dnssec_sign.h b/3rdParty/Ldns/src/include/ldns/dnssec_sign.h
new file mode 100644
index 0000000..e77cb69
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/dnssec_sign.h
@@ -0,0 +1,383 @@
+/** dnssec_verify */
+
+#ifndef LDNS_DNSSEC_SIGN_H
+#define LDNS_DNSSEC_SIGN_H
+
+#include <ldns/dnssec.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* sign functions */
+
+/** Sign flag that makes DNSKEY type signed by all keys, not only by SEP keys*/
+#define LDNS_SIGN_DNSKEY_WITH_ZSK 1
+
+/**
+ * Create an empty RRSIG RR (i.e. without the actual signature data)
+ * \param[in] rrset The RRset to create the signature for
+ * \param[in] key The key that will create the signature
+ * \return signature rr
+ */
+ldns_rr *
+ldns_create_empty_rrsig(ldns_rr_list *rrset,
+                        ldns_key *key);
+
+/**
+ * Sign the buffer which contains the wiredata of an rrset, and the
+ * corresponding empty rrsig rr with the given key
+ * \param[in] sign_buf the buffer with data to sign
+ * \param[in] key the key to sign with
+ * \return an rdata field with the signature data
+ */
+ldns_rdf *
+ldns_sign_public_buffer(ldns_buffer *sign_buf, ldns_key *key);
+
+/**
+ * Sign an rrset
+ * \param[in] rrset the rrset
+ * \param[in] keys the keys to use
+ * \return a rr_list with the signatures
+ */
+ldns_rr_list *ldns_sign_public(ldns_rr_list *rrset, ldns_key_list *keys);
+
+#if LDNS_BUILD_CONFIG_HAVE_SSL
+/**
+ * Sign a buffer with the DSA key (hash with SHA1)
+ * \param[in] to_sign buffer with the data
+ * \param[in] key the key to use
+ * \return a ldns_rdf with the signed data
+ */
+ldns_rdf *ldns_sign_public_dsa(ldns_buffer *to_sign, DSA *key);
+
+/**
+ * Sign data with EVP (general method for different algorithms)
+ *
+ * \param[in] to_sign The ldns_buffer containing raw data that is
+ *                    to be signed
+ * \param[in] key The EVP_PKEY key structure to sign with
+ * \param[in] digest_type The digest algorithm to use in the creation of
+ *                        the signature
+ * \return ldns_rdf for the RRSIG ldns_rr
+ */
+ldns_rdf *ldns_sign_public_evp(ldns_buffer *to_sign,
+						 EVP_PKEY *key,
+						 const EVP_MD *digest_type);
+
+/**
+ * Sign a buffer with the RSA key (hash with SHA1)
+ * \param[in] to_sign buffer with the data
+ * \param[in] key the key to use
+ * \return a ldns_rdf with the signed data
+ */
+ldns_rdf *ldns_sign_public_rsasha1(ldns_buffer *to_sign, RSA *key);
+
+/**
+ * Sign a buffer with the RSA key (hash with MD5)
+ * \param[in] to_sign buffer with the data
+ * \param[in] key the key to use
+ * \return a ldns_rdf with the signed data
+ */
+ldns_rdf *ldns_sign_public_rsamd5(ldns_buffer *to_sign, RSA *key);
+#endif /* LDNS_BUILD_CONFIG_HAVE_SSL */
+
+/**
+ * Marks the names in the zone that are occluded. Those names will be skipped
+ * when walking the tree with the ldns_dnssec_name_node_next_nonglue()
+ * function. But watch out! Names that are partially occluded (like glue with
+ * the same name as the delegation) will not be marked and should specifically 
+ * be taken into account seperately.
+ *
+ * When glue_list is given (not NULL), in the process of marking the names, all
+ * glue resource records will be pushed to that list, even glue at the delegation name.
+ *
+ * \param[in] zone the zone in which to mark the names
+ * \param[in] glue_list the list to which to push the glue rrs
+ * \return LDNS_STATUS_OK on success, an error code otherwise
+ */
+ldns_status
+ldns_dnssec_zone_mark_and_get_glue(
+		ldns_dnssec_zone *zone, ldns_rr_list *glue_list);
+
+/**
+ * Marks the names in the zone that are occluded. Those names will be skipped
+ * when walking the tree with the ldns_dnssec_name_node_next_nonglue()
+ * function. But watch out! Names that are partially occluded (like glue with
+ * the same name as the delegation) will not be marked and should specifically 
+ * be taken into account seperately.
+ *
+ * \param[in] zone the zone in which to mark the names
+ * \return LDNS_STATUS_OK on succesful completion
+ */
+ldns_status
+ldns_dnssec_zone_mark_glue(ldns_dnssec_zone *zone);
+
+/**
+ * Finds the first dnssec_name node in the rbtree that is not occluded.
+ * It *does* return names that are partially occluded.
+ *
+ * \param[in] node the first node to check
+ * \return the first node that has not been marked as glue, or NULL
+ * if not found (TODO: make that LDNS_RBTREE_NULL?)
+ */
+ldns_rbnode_t *ldns_dnssec_name_node_next_nonglue(ldns_rbnode_t *node);
+
+/**
+ * Adds NSEC records to the given dnssec_zone
+ *
+ * \param[in] zone the zone to add the records to
+ * \param[in] new_rrs ldns_rr's created by this function are
+ *            added to this rr list, so the caller can free them later
+ * \return LDNS_STATUS_OK on success, an error code otherwise
+ */
+ldns_status ldns_dnssec_zone_create_nsecs(ldns_dnssec_zone *zone,
+								  ldns_rr_list *new_rrs);
+
+/**
+ * Adds NSEC3 records to the zone
+ */
+ldns_status
+ldns_dnssec_zone_create_nsec3s(ldns_dnssec_zone *zone,
+						 ldns_rr_list *new_rrs,
+						 uint8_t algorithm,
+						 uint8_t flags,
+						 uint16_t iterations,
+						 uint8_t salt_length,
+						 uint8_t *salt);
+
+/**
+ * remove signatures if callback function tells to
+ * 
+ * \param[in] signatures list of signatures to check, and
+ *            possibly remove, depending on the value of the
+ *            callback
+ * \param[in] key_list these are marked to be used or not,
+ *            on the return value of the callback
+ * \param[in] func this function is called to specify what to
+ *            do with each signature (and corresponding key)
+ * \param[in] arg Optional argument for the callback function
+ * \returns pointer to the new signatures rrs (the original
+ *          passed to this function may have been removed)
+ */
+ldns_dnssec_rrs *ldns_dnssec_remove_signatures(ldns_dnssec_rrs *signatures,
+									  ldns_key_list *key_list,
+									  int (*func)(ldns_rr *, void *),
+									  void *arg);
+
+/**
+ * Adds signatures to the zone
+ *
+ * \param[in] zone the zone to add RRSIG Resource Records to
+ * \param[in] new_rrs the RRSIG RRs that are created are also
+ *            added to this list, so the caller can free them
+ *            later
+ * \param[in] key_list list of keys to sign with.
+ * \param[in] func Callback function to decide what keys to
+ *            use and what to do with old signatures
+ * \param[in] arg Optional argument for the callback function
+ * \param[in] flags option flags for signing process. 0 makes DNSKEY
+ * RRset signed with the minimal key set, that is only SEP keys are used
+ * for signing. If there are no SEP keys available, non-SEP keys will
+ * be used. LDNS_SIGN_DNSKEY_WITH_ZSK makes DNSKEY type signed with all
+ * keys. 0 is the default.
+ * \return LDNS_STATUS_OK on success, error otherwise
+ */
+ldns_status ldns_dnssec_zone_create_rrsigs_flg(ldns_dnssec_zone *zone,
+					ldns_rr_list *new_rrs,
+					ldns_key_list *key_list,
+					int (*func)(ldns_rr *, void*),
+					void *arg,
+					int flags);
+
+/**
+ * Adds signatures to the zone
+ *
+ * \param[in] zone the zone to add RRSIG Resource Records to
+ * \param[in] new_rrs the RRSIG RRs that are created are also
+ *            added to this list, so the caller can free them
+ *            later
+ * \param[in] key_list list of keys to sign with.
+ * \param[in] func Callback function to decide what keys to
+ *            use and what to do with old signatures
+ * \param[in] arg Optional argument for the callback function
+ * \return LDNS_STATUS_OK on success, error otherwise
+ */
+ldns_status ldns_dnssec_zone_create_rrsigs(ldns_dnssec_zone *zone,
+								   ldns_rr_list *new_rrs,
+								   ldns_key_list *key_list,
+								   int (*func)(ldns_rr *, void*),
+								   void *arg);
+
+/**
+ * signs the given zone with the given keys
+ * 
+ * \param[in] zone the zone to sign
+ * \param[in] key_list the list of keys to sign the zone with
+ * \param[in] new_rrs newly created resource records are added to this list, to free them later
+ * \param[in] func callback function that decides what to do with old signatures
+ *            This function takes an ldns_rr* and an optional void *arg argument, and returns one of four values:
+ * LDNS_SIGNATURE_LEAVE_ADD_NEW:
+ * leave the signature and add a new one for the corresponding key
+ * LDNS_SIGNATURE_REMOVE_ADD_NEW:
+ * remove the signature and replace is with a new one from the same key
+ * LDNS_SIGNATURE_LEAVE_NO_ADD:
+ * leave the signature and do not add a new one with the corresponding key
+ * LDNS_SIGNATURE_REMOVE_NO_ADD:
+ * remove the signature and do not replace 
+ *
+ * \param[in] arg optional argument for the callback function
+ * \param[in] flags option flags for signing process. 0 makes DNSKEY
+ * RRset signed with the minimal key set, that is only SEP keys are used
+ * for signing. If there are no SEP keys available, non-SEP keys will
+ * be used. LDNS_SIGN_DNSKEY_WITH_ZSK makes DNSKEY type signed with all
+ * keys. 0 is the default.
+ * \return LDNS_STATUS_OK on success, an error code otherwise
+ */
+ldns_status ldns_dnssec_zone_sign_flg(ldns_dnssec_zone *zone,
+					ldns_rr_list *new_rrs,
+					ldns_key_list *key_list,
+					int (*func)(ldns_rr *, void *),
+					void *arg, 
+					int flags);
+
+/**
+ * signs the given zone with the given new zone, with NSEC3
+ *
+ * \param[in] zone the zone to sign
+ * \param[in] key_list the list of keys to sign the zone with
+ * \param[in] new_rrs newly created resource records are added to this list, to free them later
+ * \param[in] func callback function that decides what to do with old signatures
+ * \param[in] arg optional argument for the callback function
+ * \param[in] algorithm the NSEC3 hashing algorithm to use
+ * \param[in] flags NSEC3 flags
+ * \param[in] iterations the number of NSEC3 hash iterations to use
+ * \param[in] salt_length the length (in octets) of the NSEC3 salt
+ * \param[in] salt the NSEC3 salt data
+ * \param[in] signflags option flags for signing process. 0 is the default.
+ * \return LDNS_STATUS_OK on success, an error code otherwise
+ */
+ldns_status ldns_dnssec_zone_sign_nsec3_flg(ldns_dnssec_zone *zone,
+				ldns_rr_list *new_rrs,
+				ldns_key_list *key_list,
+				int (*func)(ldns_rr *, void *),
+				void *arg,
+				uint8_t algorithm,
+				uint8_t flags,
+				uint16_t iterations,
+				uint8_t salt_length,
+				uint8_t *salt,
+				int signflags);
+
+/**
+ * signs the given zone with the given new zone, with NSEC3
+ *
+ * \param[in] zone the zone to sign
+ * \param[in] key_list the list of keys to sign the zone with
+ * \param[in] new_rrs newly created resource records are added to this list, to free them later
+ * \param[in] func callback function that decides what to do with old signatures
+ * \param[in] arg optional argument for the callback function
+ * \param[in] algorithm the NSEC3 hashing algorithm to use
+ * \param[in] flags NSEC3 flags
+ * \param[in] iterations the number of NSEC3 hash iterations to use
+ * \param[in] salt_length the length (in octets) of the NSEC3 salt
+ * \param[in] salt the NSEC3 salt data
+ * \param[in] signflags option flags for signing process. 0 is the default.
+ * \param[out] map a referenced rbtree pointer variable. The newly created 
+ *                 rbtree will contain mappings from hashed owner names to the 
+ *                 unhashed name.
+ * \return LDNS_STATUS_OK on success, an error code otherwise
+ */
+ldns_status ldns_dnssec_zone_sign_nsec3_flg_mkmap(ldns_dnssec_zone *zone,
+				ldns_rr_list *new_rrs,
+				ldns_key_list *key_list,
+				int (*func)(ldns_rr *, void *),
+				void *arg,
+				uint8_t algorithm,
+				uint8_t flags,
+				uint16_t iterations,
+				uint8_t salt_length,
+				uint8_t *salt,
+				int signflags,
+				ldns_rbtree_t **map
+				);
+
+
+/**
+ * signs the given zone with the given keys
+ * 
+ * \param[in] zone the zone to sign
+ * \param[in] key_list the list of keys to sign the zone with
+ * \param[in] new_rrs newly created resource records are added to this list, to free them later
+ * \param[in] func callback function that decides what to do with old signatures
+ *            This function takes an ldns_rr* and an optional void *arg argument, and returns one of four values:
+ * LDNS_SIGNATURE_LEAVE_ADD_NEW:
+ * leave the signature and add a new one for the corresponding key
+ * LDNS_SIGNATURE_REMOVE_ADD_NEW:
+ * remove the signature and replace is with a new one from the same key
+ * LDNS_SIGNATURE_LEAVE_NO_ADD:
+ * leave the signature and do not add a new one with the corresponding key
+ * LDNS_SIGNATURE_REMOVE_NO_ADD:
+ * remove the signature and do not replace 
+ *
+ * \param[in] arg optional argument for the callback function
+ * \return LDNS_STATUS_OK on success, an error code otherwise
+ */
+ldns_status ldns_dnssec_zone_sign(ldns_dnssec_zone *zone,
+						    ldns_rr_list *new_rrs,
+						    ldns_key_list *key_list,
+						    int (*func)(ldns_rr *, void *),
+						    void *arg);
+
+/**
+ * signs the given zone with the given new zone, with NSEC3
+ *
+ * \param[in] zone the zone to sign
+ * \param[in] key_list the list of keys to sign the zone with
+ * \param[in] new_rrs newly created resource records are added to this list, to free them later
+ * \param[in] func callback function that decides what to do with old signatures
+ * \param[in] arg optional argument for the callback function
+ * \param[in] algorithm the NSEC3 hashing algorithm to use
+ * \param[in] flags NSEC3 flags
+ * \param[in] iterations the number of NSEC3 hash iterations to use
+ * \param[in] salt_length the length (in octets) of the NSEC3 salt
+ * \param[in] salt the NSEC3 salt data
+ * \return LDNS_STATUS_OK on success, an error code otherwise
+ */
+ldns_status ldns_dnssec_zone_sign_nsec3(ldns_dnssec_zone *zone,
+								ldns_rr_list *new_rrs,
+								ldns_key_list *key_list,
+								int (*func)(ldns_rr *, void *),
+								void *arg,
+								uint8_t algorithm,
+								uint8_t flags,
+								uint16_t iterations,
+								uint8_t salt_length,
+								uint8_t *salt);
+
+/**
+ * Signs the zone, and returns a newly allocated signed zone
+ * \param[in] zone the zone to sign
+ * \param[in] key_list list of keys to sign with
+ * \return signed zone
+ */
+ldns_zone *ldns_zone_sign(const ldns_zone *zone, ldns_key_list *key_list);
+
+/**
+ * Signs the zone with NSEC3, and returns a newly allocated signed zone
+ * \param[in] zone the zone to sign
+ * \param[in] key_list list of keys to sign with
+ * \param[in] algorithm the NSEC3 hashing algorithm to use
+ * \param[in] flags NSEC3 flags
+ * \param[in] iterations the number of NSEC3 hash iterations to use
+ * \param[in] salt_length the length (in octets) of the NSEC3 salt
+ * \param[in] salt the NSEC3 salt data
+ * \return signed zone
+ */
+ldns_zone *ldns_zone_sign_nsec3(ldns_zone *zone, ldns_key_list *key_list, uint8_t algorithm, uint8_t flags, uint16_t iterations, uint8_t salt_length, uint8_t *salt);
+ 
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/3rdParty/Ldns/src/include/ldns/dnssec_verify.h b/3rdParty/Ldns/src/include/ldns/dnssec_verify.h
new file mode 100644
index 0000000..1350f48
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/dnssec_verify.h
@@ -0,0 +1,873 @@
+/** dnssec_verify */
+
+#ifndef LDNS_DNSSEC_VERIFY_H
+#define LDNS_DNSSEC_VERIFY_H
+
+#define LDNS_DNSSEC_TRUST_TREE_MAX_PARENTS 10
+
+#include <ldns/dnssec.h>
+#include <ldns/host2str.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Chain structure that contains all DNSSEC data needed to
+ * verify an rrset
+ */
+typedef struct ldns_dnssec_data_chain_struct ldns_dnssec_data_chain;
+struct ldns_dnssec_data_chain_struct
+{
+	ldns_rr_list *rrset;
+	ldns_rr_list *signatures;
+	ldns_rr_type parent_type;
+	ldns_dnssec_data_chain *parent;
+	ldns_pkt_rcode packet_rcode;
+	ldns_rr_type packet_qtype;
+	bool packet_nodata;
+};
+
+/**
+ * Creates a new dnssec_chain structure
+ * \return ldns_dnssec_data_chain *
+ */
+ldns_dnssec_data_chain *ldns_dnssec_data_chain_new();
+
+/**
+ * Frees a dnssec_data_chain structure
+ *
+ * \param[in] *chain The chain to free
+ */
+void ldns_dnssec_data_chain_free(ldns_dnssec_data_chain *chain);
+
+/**
+ * Frees a dnssec_data_chain structure, and all data
+ * contained therein
+ *
+ * \param[in] *chain The dnssec_data_chain to free
+ */
+void ldns_dnssec_data_chain_deep_free(ldns_dnssec_data_chain *chain);
+
+/**
+ * Prints the dnssec_data_chain to the given file stream
+ * 
+ * \param[in] *out The file stream to print to
+ * \param[in] *chain The dnssec_data_chain to print
+ */
+void ldns_dnssec_data_chain_print(FILE *out, const ldns_dnssec_data_chain *chain);
+
+/**
+ * Prints the dnssec_data_chain to the given file stream
+ * 
+ * \param[in] *out The file stream to print to
+ * \param[in] *fmt The format of the textual representation
+ * \param[in] *chain The dnssec_data_chain to print
+ */
+void ldns_dnssec_data_chain_print_fmt(FILE *out, 
+		const ldns_output_format *fmt,
+		const ldns_dnssec_data_chain *chain);
+
+/**
+ * Build an ldns_dnssec_data_chain, which contains all
+ * DNSSEC data that is needed to derive the trust tree later
+ *
+ * The data_set will be cloned
+ *
+ * \param[in] *res resolver structure for further needed queries
+ * \param[in] qflags resolution flags
+ * \param[in] *data_set The original rrset where the chain ends
+ * \param[in] *pkt optional, can contain the original packet
+ * (and hence the sigs and maybe the key)
+ * \param[in] *orig_rr The original Resource Record
+ *
+ * \return the DNSSEC data chain
+ */
+ldns_dnssec_data_chain *ldns_dnssec_build_data_chain(ldns_resolver *res,
+										   const uint16_t qflags,
+										   const ldns_rr_list *data_set,
+										   const ldns_pkt *pkt,
+										   ldns_rr *orig_rr);
+
+/**
+ * Tree structure that contains the relation of DNSSEC data,
+ * and their cryptographic status.
+ *
+ * This tree is derived from a data_chain, and can be used
+ * to look whether there is a connection between an RRSET
+ * and a trusted key. The tree only contains pointers to the
+ * data_chain, and therefore one should *never* free() the
+ * data_chain when there is still a trust tree derived from
+ * that chain.
+ *
+ * Example tree:
+ *     key   key    key
+ *       \    |    /
+ *        \   |   /
+ *         \  |  /
+ *            ds
+ *            |
+ *           key
+ *            |
+ *           key
+ *            |
+ *            rr
+ *
+ * For each signature there is a parent; if the parent
+ * pointer is null, it couldn't be found and there was no
+ * denial; otherwise is a tree which contains either a
+ * DNSKEY, a DS, or a NSEC rr
+ */
+typedef struct ldns_dnssec_trust_tree_struct ldns_dnssec_trust_tree;
+struct ldns_dnssec_trust_tree_struct
+{
+	ldns_rr *rr;
+	/* the complete rrset this rr was in */
+	ldns_rr_list *rrset;
+	ldns_dnssec_trust_tree *parents[LDNS_DNSSEC_TRUST_TREE_MAX_PARENTS];
+	ldns_status parent_status[LDNS_DNSSEC_TRUST_TREE_MAX_PARENTS];
+	/** for debugging, add signatures too (you might want
+	    those if they contain errors) */
+	ldns_rr *parent_signature[LDNS_DNSSEC_TRUST_TREE_MAX_PARENTS];
+	size_t parent_count;
+};
+
+/**
+ * Creates a new (empty) dnssec_trust_tree structure
+ *
+ * \return ldns_dnssec_trust_tree *
+ */
+ldns_dnssec_trust_tree *ldns_dnssec_trust_tree_new();
+
+/**
+ * Frees the dnssec_trust_tree recursively
+ *
+ * There is no deep free; all data in the trust tree
+ * consists of pointers to a data_chain
+ *
+ * \param[in] tree The tree to free
+ */
+void ldns_dnssec_trust_tree_free(ldns_dnssec_trust_tree *tree);
+
+/**
+ * returns the depth of the trust tree
+ *
+ * \param[in] tree tree to calculate the depth of
+ * \return The depth of the tree
+ */
+size_t ldns_dnssec_trust_tree_depth(ldns_dnssec_trust_tree *tree);
+
+/**
+ * Prints the dnssec_trust_tree structure to the given file
+ * stream.
+ *
+ * If a link status is not LDNS_STATUS_OK; the status and
+ * relevant signatures are printed too
+ *
+ * \param[in] *out The file stream to print to
+ * \param[in] tree The trust tree to print
+ * \param[in] tabs Prepend each line with tabs*2 spaces
+ * \param[in] extended If true, add little explanation lines to the output
+ */
+void ldns_dnssec_trust_tree_print(FILE *out,
+	       	ldns_dnssec_trust_tree *tree,
+		size_t tabs,
+		bool extended);
+
+/**
+ * Prints the dnssec_trust_tree structure to the given file
+ * stream.
+ *
+ * If a link status is not LDNS_STATUS_OK; the status and
+ * relevant signatures are printed too
+ *
+ * \param[in] *out The file stream to print to
+ * \param[in] *fmt The format of the textual representation
+ * \param[in] tree The trust tree to print
+ * \param[in] tabs Prepend each line with tabs*2 spaces
+ * \param[in] extended If true, add little explanation lines to the output
+ */
+void ldns_dnssec_trust_tree_print_fmt(FILE *out,
+		const ldns_output_format *fmt,
+	       	ldns_dnssec_trust_tree *tree,
+		size_t tabs,
+		bool extended);
+
+/**
+ * Adds a trust tree as a parent for the given trust tree
+ *
+ * \param[in] *tree The tree to add the parent to
+ * \param[in] *parent The parent tree to add
+ * \param[in] *parent_signature The RRSIG relevant to this parent/child
+ *                              connection
+ * \param[in] parent_status The DNSSEC status for this parent, child and RRSIG
+ * \return LDNS_STATUS_OK if the addition succeeds, error otherwise
+ */
+ldns_status ldns_dnssec_trust_tree_add_parent(ldns_dnssec_trust_tree *tree,
+									 const ldns_dnssec_trust_tree *parent,
+									 const ldns_rr *parent_signature,
+									 const ldns_status parent_status);
+
+/**
+ * Generates a dnssec_trust_ttree for the given rr from the
+ * given data_chain
+ *
+ * This does not clone the actual data; Don't free the
+ * data_chain before you are done with this tree
+ *
+ * \param[in] *data_chain The chain to derive the trust tree from
+ * \param[in] *rr The RR this tree will be about
+ * \return ldns_dnssec_trust_tree *
+ */
+ldns_dnssec_trust_tree *ldns_dnssec_derive_trust_tree(
+                            ldns_dnssec_data_chain *data_chain,
+					   ldns_rr *rr);
+
+/**
+ * Generates a dnssec_trust_ttree for the given rr from the
+ * given data_chain
+ *
+ * This does not clone the actual data; Don't free the
+ * data_chain before you are done with this tree
+ *
+ * \param[in] *data_chain The chain to derive the trust tree from
+ * \param[in] *rr The RR this tree will be about
+ * \param[in] check_time the time for which the validation is performed
+ * \return ldns_dnssec_trust_tree *
+ */
+ldns_dnssec_trust_tree *ldns_dnssec_derive_trust_tree_time(
+		ldns_dnssec_data_chain *data_chain, 
+		ldns_rr *rr, time_t check_time);
+
+/**
+ * Sub function for derive_trust_tree that is used for a 'normal' rrset
+ *
+ * \param[in] new_tree The trust tree that we are building
+ * \param[in] data_chain The data chain containing the data for the trust tree
+ * \param[in] cur_sig_rr The currently relevant signature
+ */
+void ldns_dnssec_derive_trust_tree_normal_rrset(
+         ldns_dnssec_trust_tree *new_tree,
+	    ldns_dnssec_data_chain *data_chain,
+	    ldns_rr *cur_sig_rr);
+
+/**
+ * Sub function for derive_trust_tree that is used for a 'normal' rrset
+ *
+ * \param[in] new_tree The trust tree that we are building
+ * \param[in] data_chain The data chain containing the data for the trust tree
+ * \param[in] cur_sig_rr The currently relevant signature
+ * \param[in] check_time the time for which the validation is performed
+ */
+void ldns_dnssec_derive_trust_tree_normal_rrset_time(
+         ldns_dnssec_trust_tree *new_tree,
+	    ldns_dnssec_data_chain *data_chain,
+	    ldns_rr *cur_sig_rr, time_t check_time);
+
+
+/**
+ * Sub function for derive_trust_tree that is used for DNSKEY rrsets
+ *
+ * \param[in] new_tree The trust tree that we are building
+ * \param[in] data_chain The data chain containing the data for the trust tree
+ * \param[in] cur_rr The currently relevant DNSKEY RR
+ * \param[in] cur_sig_rr The currently relevant signature
+ */
+void ldns_dnssec_derive_trust_tree_dnskey_rrset(
+         ldns_dnssec_trust_tree *new_tree,
+	    ldns_dnssec_data_chain *data_chain,
+	    ldns_rr *cur_rr,
+	    ldns_rr *cur_sig_rr);
+
+/**
+ * Sub function for derive_trust_tree that is used for DNSKEY rrsets
+ *
+ * \param[in] new_tree The trust tree that we are building
+ * \param[in] data_chain The data chain containing the data for the trust tree
+ * \param[in] cur_rr The currently relevant DNSKEY RR
+ * \param[in] cur_sig_rr The currently relevant signature
+ * \param[in] check_time the time for which the validation is performed
+ */
+void ldns_dnssec_derive_trust_tree_dnskey_rrset_time(
+         ldns_dnssec_trust_tree *new_tree,
+	    ldns_dnssec_data_chain *data_chain,
+	    ldns_rr *cur_rr, ldns_rr *cur_sig_rr,
+	    time_t check_time);
+
+
+/**
+ * Sub function for derive_trust_tree that is used for DNSKEY rrsets
+ *
+ * \param[in] new_tree The trust tree that we are building
+ * \param[in] data_chain The data chain containing the data for the trust tree
+ * \param[in] cur_rr The currently relevant DNSKEY RR
+ * \param[in] cur_sig_rr The currently relevant signature
+ * \param[in] check_time the time for which the validation is performed
+ */
+void ldns_dnssec_derive_trust_tree_dnskey_rrset_time(
+         ldns_dnssec_trust_tree *new_tree,
+	    ldns_dnssec_data_chain *data_chain,
+	    ldns_rr *cur_rr, ldns_rr *cur_sig_rr,
+	    time_t check_time);
+
+
+/**
+ * Sub function for derive_trust_tree that is used for DS rrsets
+ *
+ * \param[in] new_tree The trust tree that we are building
+ * \param[in] data_chain The data chain containing the data for the trust tree
+ * \param[in] cur_rr The currently relevant DS RR
+ */
+void ldns_dnssec_derive_trust_tree_ds_rrset(
+         ldns_dnssec_trust_tree *new_tree,
+	    ldns_dnssec_data_chain *data_chain,
+	    ldns_rr *cur_rr);
+
+/**
+ * Sub function for derive_trust_tree that is used for DS rrsets
+ *
+ * \param[in] new_tree The trust tree that we are building
+ * \param[in] data_chain The data chain containing the data for the trust tree
+ * \param[in] cur_rr The currently relevant DS RR
+ * \param[in] check_time the time for which the validation is performed
+ */
+void ldns_dnssec_derive_trust_tree_ds_rrset_time(
+         ldns_dnssec_trust_tree *new_tree,
+	    ldns_dnssec_data_chain *data_chain,
+	    ldns_rr *cur_rr, time_t check_time);
+
+/**
+ * Sub function for derive_trust_tree that is used when there are no
+ * signatures
+ *
+ * \param[in] new_tree The trust tree that we are building
+ * \param[in] data_chain The data chain containing the data for the trust tree
+ */
+void ldns_dnssec_derive_trust_tree_no_sig(
+         ldns_dnssec_trust_tree *new_tree,
+	    ldns_dnssec_data_chain *data_chain);
+
+/**
+ * Sub function for derive_trust_tree that is used when there are no
+ * signatures
+ *
+ * \param[in] new_tree The trust tree that we are building
+ * \param[in] data_chain The data chain containing the data for the trust tree
+ * \param[in] check_time the time for which the validation is performed
+ */
+void ldns_dnssec_derive_trust_tree_no_sig_time(
+         ldns_dnssec_trust_tree *new_tree,
+	    ldns_dnssec_data_chain *data_chain,
+	    time_t check_time);
+
+
+/**
+ * Returns OK if there is a trusted path in the tree to one of 
+ * the DNSKEY or DS RRs in the given list
+ *
+ * \param *tree The trust tree so search
+ * \param *keys A ldns_rr_list of DNSKEY and DS rrs to look for
+ * \return LDNS_STATUS_OK if there is a trusted path to one of
+ *                        the keys, or the *first* error encountered
+ *                        if there were no paths
+ */
+ldns_status ldns_dnssec_trust_tree_contains_keys(
+			 ldns_dnssec_trust_tree *tree,
+			 ldns_rr_list *keys);
+
+/**
+ * Verifies a list of signatures for one rrset.
+ *
+ * \param[in] rrset the rrset to verify
+ * \param[in] rrsig a list of signatures to check
+ * \param[in] keys a list of keys to check with
+ * \param[out] good_keys  if this is a (initialized) list, the pointer to keys
+ *                        from keys that validate one of the signatures
+ *                        are added to it
+ * \return status LDNS_STATUS_OK if there is at least one correct key
+ */
+ldns_status ldns_verify(ldns_rr_list *rrset,
+				    ldns_rr_list *rrsig,
+				    const ldns_rr_list *keys,
+				    ldns_rr_list *good_keys);	
+
+/**
+ * Verifies a list of signatures for one rrset.
+ *
+ * \param[in] rrset the rrset to verify
+ * \param[in] rrsig a list of signatures to check
+ * \param[in] keys a list of keys to check with
+ * \param[in] check_time the time for which the validation is performed
+ * \param[out] good_keys  if this is a (initialized) list, the pointer to keys
+ *                        from keys that validate one of the signatures
+ *                        are added to it
+ * \return status LDNS_STATUS_OK if there is at least one correct key
+ */
+ldns_status ldns_verify_time(ldns_rr_list *rrset,
+				    ldns_rr_list *rrsig,
+				    const ldns_rr_list *keys,
+				    time_t check_time,
+				    ldns_rr_list *good_keys);	
+
+
+/**
+ * Verifies a list of signatures for one rrset, but disregard the time.
+ * Inception and Expiration are not checked.
+ *
+ * \param[in] rrset the rrset to verify
+ * \param[in] rrsig a list of signatures to check
+ * \param[in] keys a list of keys to check with
+ * \param[out] good_keys  if this is a (initialized) list, the pointer to keys
+ *                        from keys that validate one of the signatures
+ *                        are added to it
+ * \return status LDNS_STATUS_OK if there is at least one correct key
+ */
+ldns_status ldns_verify_notime(ldns_rr_list *rrset,
+				    ldns_rr_list *rrsig,
+				    const ldns_rr_list *keys,
+				    ldns_rr_list *good_keys);	
+
+/**
+ * Tries to build an authentication chain from the given 
+ * keys down to the queried domain.
+ *
+ * If we find a valid trust path, return the valid keys for the domain.
+ * 
+ * \param[in] res the current resolver
+ * \param[in] domain the domain we want valid keys for
+ * \param[in] keys the current set of trusted keys
+ * \param[out] status pointer to the status variable where the result
+ *                    code will be stored
+ * \return the set of trusted keys for the domain, or NULL if no 
+ *         trust path could be built.
+ */
+ldns_rr_list *ldns_fetch_valid_domain_keys(const ldns_resolver * res,
+								   const ldns_rdf * domain,
+								   const ldns_rr_list * keys,
+								   ldns_status *status);
+
+/**
+ * Tries to build an authentication chain from the given 
+ * keys down to the queried domain.
+ *
+ * If we find a valid trust path, return the valid keys for the domain.
+ * 
+ * \param[in] res the current resolver
+ * \param[in] domain the domain we want valid keys for
+ * \param[in] keys the current set of trusted keys
+ * \param[in] check_time the time for which the validation is performed
+ * \param[out] status pointer to the status variable where the result
+ *                    code will be stored
+ * \return the set of trusted keys for the domain, or NULL if no 
+ *         trust path could be built.
+ */
+ldns_rr_list *ldns_fetch_valid_domain_keys_time(const ldns_resolver * res,
+		const ldns_rdf * domain, const ldns_rr_list * keys,
+		time_t check_time, ldns_status *status);
+
+
+/**
+ * Validates the DNSKEY RRset for the given domain using the provided 
+ * trusted keys.
+ *
+ * \param[in] res the current resolver
+ * \param[in] domain the domain we want valid keys for
+ * \param[in] keys the current set of trusted keys
+ * \return the set of trusted keys for the domain, or NULL if the RRSET
+ *         could not be validated
+ */
+ldns_rr_list *ldns_validate_domain_dnskey (const ldns_resolver *res,
+								   const ldns_rdf *domain,
+								   const ldns_rr_list *keys);
+
+/**
+ * Validates the DNSKEY RRset for the given domain using the provided 
+ * trusted keys.
+ *
+ * \param[in] res the current resolver
+ * \param[in] domain the domain we want valid keys for
+ * \param[in] keys the current set of trusted keys
+ * \param[in] check_time the time for which the validation is performed
+ * \return the set of trusted keys for the domain, or NULL if the RRSET
+ *         could not be validated
+ */
+ldns_rr_list *ldns_validate_domain_dnskey_time(
+		const ldns_resolver *res, const ldns_rdf *domain, 
+		const ldns_rr_list *keys, time_t check_time);
+
+
+/**
+ * Validates the DS RRset for the given domain using the provided trusted keys.
+ *
+ * \param[in] res the current resolver
+ * \param[in] domain the domain we want valid keys for
+ * \param[in] keys the current set of trusted keys
+ * \return the set of trusted keys for the domain, or NULL if the RRSET could not be validated
+ */
+ldns_rr_list *ldns_validate_domain_ds(const ldns_resolver *res,
+							   const ldns_rdf *
+							   domain,
+							   const ldns_rr_list * keys);
+
+/**
+ * Validates the DS RRset for the given domain using the provided trusted keys.
+ *
+ * \param[in] res the current resolver
+ * \param[in] domain the domain we want valid keys for
+ * \param[in] keys the current set of trusted keys
+ * \param[in] check_time the time for which the validation is performed
+ * \return the set of trusted keys for the domain, or NULL if the RRSET could not be validated
+ */
+ldns_rr_list *ldns_validate_domain_ds_time(
+		const ldns_resolver *res, const ldns_rdf *domain, 
+		const ldns_rr_list * keys, time_t check_time);
+
+
+/**
+ * Verifies a list of signatures for one RRset using a valid trust path.
+ *
+ * \param[in] res the current resolver
+ * \param[in] rrset the rrset to verify
+ * \param[in] rrsigs a list of signatures to check
+ * \param[out] validating_keys  if this is a (initialized) list, the
+ *                              keys from keys that validate one of
+ *                              the signatures are added to it
+ * \return status LDNS_STATUS_OK if there is at least one correct key
+ */
+ldns_status ldns_verify_trusted(ldns_resolver *res,
+						  ldns_rr_list *rrset,
+						  ldns_rr_list *rrsigs,
+						  ldns_rr_list *validating_keys);
+
+/**
+ * Verifies a list of signatures for one RRset using a valid trust path.
+ *
+ * \param[in] res the current resolver
+ * \param[in] rrset the rrset to verify
+ * \param[in] rrsigs a list of signatures to check
+ * \param[in] check_time the time for which the validation is performed
+ * \param[out] validating_keys  if this is a (initialized) list, the
+ *                              keys from keys that validate one of
+ *                              the signatures are added to it
+ * \return status LDNS_STATUS_OK if there is at least one correct key
+ */
+ldns_status ldns_verify_trusted_time(
+		ldns_resolver *res, ldns_rr_list *rrset, 
+		ldns_rr_list *rrsigs, time_t check_time,
+		ldns_rr_list *validating_keys);
+
+
+/**
+ * denial is not just a river in egypt
+ *
+ * \param[in] rr The (query) RR to check the denial of existence for
+ * \param[in] nsecs The list of NSEC RRs that are supposed to deny the
+ *                  existence of the RR
+ * \param[in] rrsigs The RRSIG RR covering the NSEC RRs
+ * \return LDNS_STATUS_OK if the NSEC RRs deny the existence, error code
+ *                        containing the reason they do not otherwise
+ */
+ldns_status ldns_dnssec_verify_denial(ldns_rr *rr,
+							   ldns_rr_list *nsecs,
+							   ldns_rr_list *rrsigs);
+
+/**
+ * Denial of existence using NSEC3 records
+ * Since NSEC3 is a bit more complicated than normal denial, some
+ * context arguments are needed
+ *
+ * \param[in] rr The (query) RR to check the denial of existence for
+ * \param[in] nsecs The list of NSEC3 RRs that are supposed to deny the
+ *                  existence of the RR
+ * \param[in] rrsigs The RRSIG rr covering the NSEC RRs
+ * \param[in] packet_rcode The RCODE value of the packet that provided the
+ *                         NSEC3 RRs
+ * \param[in] packet_qtype The original query RR type
+ * \param[in] packet_nodata True if the providing packet had an empty ANSWER
+ *                          section
+ * \return LDNS_STATUS_OK if the NSEC3 RRs deny the existence, error code
+ *                        containing the reason they do not otherwise
+ */
+ldns_status ldns_dnssec_verify_denial_nsec3(ldns_rr *rr,
+								    ldns_rr_list *nsecs,
+								    ldns_rr_list *rrsigs,
+								    ldns_pkt_rcode packet_rcode,
+								    ldns_rr_type packet_qtype,
+								    bool packet_nodata);
+
+/**
+ * Same as ldns_status ldns_dnssec_verify_denial_nsec3 but also returns
+ * the nsec rr that matched.
+ *
+ * \param[in] rr The (query) RR to check the denial of existence for
+ * \param[in] nsecs The list of NSEC3 RRs that are supposed to deny the
+ *                  existence of the RR
+ * \param[in] rrsigs The RRSIG rr covering the NSEC RRs
+ * \param[in] packet_rcode The RCODE value of the packet that provided the
+ *                         NSEC3 RRs
+ * \param[in] packet_qtype The original query RR type
+ * \param[in] packet_nodata True if the providing packet had an empty ANSWER
+ *                          section
+ * \param[in] match On match, the given (reference to a) pointer will be set 
+ *                  to point to the matching nsec resource record.
+ * \return LDNS_STATUS_OK if the NSEC3 RRs deny the existence, error code
+ *                        containing the reason they do not otherwise
+ */
+ldns_status ldns_dnssec_verify_denial_nsec3_match(ldns_rr *rr,
+						  ldns_rr_list *nsecs,
+						  ldns_rr_list *rrsigs,
+						  ldns_pkt_rcode packet_rcode,
+						  ldns_rr_type packet_qtype,
+						  bool packet_nodata,
+						  ldns_rr **match);
+/**
+ * Verifies the already processed data in the buffers
+ * This function should probably not be used directly.
+ *
+ * \param[in] rawsig_buf Buffer containing signature data to use
+ * \param[in] verify_buf Buffer containing data to verify
+ * \param[in] key_buf Buffer containing key data to use
+ * \param[in] algo Signing algorithm
+ * \return status LDNS_STATUS_OK if the data verifies. Error if not.
+ */
+ldns_status ldns_verify_rrsig_buffers(ldns_buffer *rawsig_buf,
+							   ldns_buffer *verify_buf,
+							   ldns_buffer *key_buf,
+							   uint8_t algo);
+
+/**
+ * Like ldns_verify_rrsig_buffers, but uses raw data.
+ *
+ * \param[in] sig signature data to use
+ * \param[in] siglen length of signature data to use
+ * \param[in] verify_buf Buffer containing data to verify
+ * \param[in] key key data to use
+ * \param[in] keylen length of key data to use
+ * \param[in] algo Signing algorithm
+ * \return status LDNS_STATUS_OK if the data verifies. Error if not.
+ */
+ldns_status ldns_verify_rrsig_buffers_raw(unsigned char* sig,
+								  size_t siglen, 
+								  ldns_buffer *verify_buf,
+								  unsigned char* key,
+								  size_t keylen, 
+								  uint8_t algo);
+
+/**
+ * Verifies an rrsig. All keys in the keyset are tried.
+ * \param[in] rrset the rrset to check
+ * \param[in] rrsig the signature of the rrset
+ * \param[in] keys the keys to try
+ * \param[out] good_keys  if this is a (initialized) list, the pointer to keys
+ *                        from keys that validate one of the signatures
+ *                        are added to it
+ * \return a list of keys which validate the rrsig + rrset. Returns
+ * status LDNS_STATUS_OK if at least one key matched. Else an error.
+ */
+ldns_status ldns_verify_rrsig_keylist(ldns_rr_list *rrset,
+							   ldns_rr *rrsig,
+							   const ldns_rr_list *keys,
+							   ldns_rr_list *good_keys);
+
+/**
+ * Verifies an rrsig. All keys in the keyset are tried.
+ * \param[in] rrset the rrset to check
+ * \param[in] rrsig the signature of the rrset
+ * \param[in] keys the keys to try
+ * \param[in] check_time the time for which the validation is performed
+ * \param[out] good_keys  if this is a (initialized) list, the pointer to keys
+ *                        from keys that validate one of the signatures
+ *                        are added to it
+ * \return a list of keys which validate the rrsig + rrset. Returns
+ * status LDNS_STATUS_OK if at least one key matched. Else an error.
+ */
+ldns_status ldns_verify_rrsig_keylist_time(
+		ldns_rr_list *rrset, ldns_rr *rrsig, 
+		const ldns_rr_list *keys, time_t check_time,
+	       	ldns_rr_list *good_keys);
+
+
+/**
+ * Verifies an rrsig. All keys in the keyset are tried. Time is not checked.
+ * \param[in] rrset the rrset to check
+ * \param[in] rrsig the signature of the rrset
+ * \param[in] keys the keys to try
+ * \param[out] good_keys  if this is a (initialized) list, the pointer to keys
+ *                        from keys that validate one of the signatures
+ *                        are added to it
+ * \return a list of keys which validate the rrsig + rrset. Returns
+ * status LDNS_STATUS_OK if at least one key matched. Else an error.
+ */
+ldns_status ldns_verify_rrsig_keylist_notime(ldns_rr_list *rrset,
+							   ldns_rr *rrsig,
+							   const ldns_rr_list *keys,
+							   ldns_rr_list *good_keys);
+
+/**
+ * verify an rrsig with 1 key
+ * \param[in] rrset the rrset
+ * \param[in] rrsig the rrsig to verify
+ * \param[in] key the key to use
+ * \return status message wether verification succeeded.
+ */
+ldns_status ldns_verify_rrsig(ldns_rr_list *rrset,
+						ldns_rr *rrsig,
+						ldns_rr *key);
+
+
+/**
+ * verify an rrsig with 1 key
+ * \param[in] rrset the rrset
+ * \param[in] rrsig the rrsig to verify
+ * \param[in] key the key to use
+ * \param[in] check_time the time for which the validation is performed
+ * \return status message wether verification succeeded.
+ */
+ldns_status ldns_verify_rrsig_time(
+		ldns_rr_list *rrset, ldns_rr *rrsig, 
+		ldns_rr *key, time_t check_time);
+
+
+#if LDNS_BUILD_CONFIG_HAVE_SSL
+/**
+ * verifies a buffer with signature data for a buffer with rrset data 
+ * with an EVP_PKEY
+ *
+ * \param[in] sig the signature data
+ * \param[in] rrset the rrset data, sorted and processed for verification
+ * \param[in] key the EVP key structure
+ * \param[in] digest_type The digest type of the signature
+ */
+ldns_status ldns_verify_rrsig_evp(ldns_buffer *sig,
+						    ldns_buffer *rrset,
+						    EVP_PKEY *key,
+						    const EVP_MD *digest_type);
+
+/**
+ * Like ldns_verify_rrsig_evp, but uses raw signature data.
+ * \param[in] sig the signature data, wireformat uncompressed
+ * \param[in] siglen length of the signature data
+ * \param[in] rrset the rrset data, sorted and processed for verification
+ * \param[in] key the EVP key structure
+ * \param[in] digest_type The digest type of the signature
+ */
+ldns_status ldns_verify_rrsig_evp_raw(unsigned char *sig,
+							   size_t siglen,
+							   ldns_buffer *rrset,
+							   EVP_PKEY *key,
+							   const EVP_MD *digest_type);
+#endif
+
+/**
+ * verifies a buffer with signature data (DSA) for a buffer with rrset data 
+ * with a buffer with key data.
+ *
+ * \param[in] sig the signature data
+ * \param[in] rrset the rrset data, sorted and processed for verification
+ * \param[in] key the key data
+ */
+ldns_status ldns_verify_rrsig_dsa(ldns_buffer *sig,
+						    ldns_buffer *rrset,
+						    ldns_buffer *key);
+
+/**
+ * verifies a buffer with signature data (RSASHA1) for a buffer with rrset data 
+ * with a buffer with key data.
+ *
+ * \param[in] sig the signature data
+ * \param[in] rrset the rrset data, sorted and processed for verification
+ * \param[in] key the key data
+ */
+ldns_status ldns_verify_rrsig_rsasha1(ldns_buffer *sig,
+							   ldns_buffer *rrset,
+							   ldns_buffer *key);
+
+/**
+ * verifies a buffer with signature data (RSAMD5) for a buffer with rrset data 
+ * with a buffer with key data.
+ *
+ * \param[in] sig the signature data
+ * \param[in] rrset the rrset data, sorted and processed for verification
+ * \param[in] key the key data
+ */
+ldns_status ldns_verify_rrsig_rsamd5(ldns_buffer *sig,
+							  ldns_buffer *rrset,
+							  ldns_buffer *key);
+
+/**
+ * Like ldns_verify_rrsig_dsa, but uses raw signature and key data.
+ * \param[in] sig raw uncompressed wireformat signature data
+ * \param[in] siglen length of signature data
+ * \param[in] rrset ldns buffer with prepared rrset data.
+ * \param[in] key raw uncompressed wireformat key data
+ * \param[in] keylen length of key data
+ */
+ldns_status ldns_verify_rrsig_dsa_raw(unsigned char* sig,
+							   size_t siglen,
+							   ldns_buffer* rrset,
+							   unsigned char* key,
+							   size_t keylen);
+
+/**
+ * Like ldns_verify_rrsig_rsasha1, but uses raw signature and key data.
+ * \param[in] sig raw uncompressed wireformat signature data
+ * \param[in] siglen length of signature data
+ * \param[in] rrset ldns buffer with prepared rrset data.
+ * \param[in] key raw uncompressed wireformat key data
+ * \param[in] keylen length of key data
+ */
+ldns_status ldns_verify_rrsig_rsasha1_raw(unsigned char* sig,
+								  size_t siglen,
+								  ldns_buffer* rrset,
+								  unsigned char* key,
+								  size_t keylen);
+
+/**
+ * Like ldns_verify_rrsig_rsasha256, but uses raw signature and key data.
+ * \param[in] sig raw uncompressed wireformat signature data
+ * \param[in] siglen length of signature data
+ * \param[in] rrset ldns buffer with prepared rrset data.
+ * \param[in] key raw uncompressed wireformat key data
+ * \param[in] keylen length of key data
+ */
+
+ldns_status ldns_verify_rrsig_rsasha256_raw(unsigned char* sig,
+								    size_t siglen,
+								    ldns_buffer* rrset,
+								    unsigned char* key,
+								    size_t keylen);
+
+/**
+ * Like ldns_verify_rrsig_rsasha512, but uses raw signature and key data.
+ * \param[in] sig raw uncompressed wireformat signature data
+ * \param[in] siglen length of signature data
+ * \param[in] rrset ldns buffer with prepared rrset data.
+ * \param[in] key raw uncompressed wireformat key data
+ * \param[in] keylen length of key data
+ */
+ldns_status ldns_verify_rrsig_rsasha512_raw(unsigned char* sig,
+								    size_t siglen,
+								    ldns_buffer* rrset,
+								    unsigned char* key,
+								    size_t keylen);
+
+/**
+ * Like ldns_verify_rrsig_rsamd5, but uses raw signature and key data.
+ * \param[in] sig raw uncompressed wireformat signature data
+ * \param[in] siglen length of signature data
+ * \param[in] rrset ldns buffer with prepared rrset data.
+ * \param[in] key raw uncompressed wireformat key data
+ * \param[in] keylen length of key data
+ */
+ldns_status ldns_verify_rrsig_rsamd5_raw(unsigned char* sig,
+								 size_t siglen,
+								 ldns_buffer* rrset,
+								 unsigned char* key,
+								 size_t keylen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/3rdParty/Ldns/src/include/ldns/dnssec_zone.h b/3rdParty/Ldns/src/include/ldns/dnssec_zone.h
new file mode 100644
index 0000000..e2dd402
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/dnssec_zone.h
@@ -0,0 +1,440 @@
+/*
+ * special zone file structures and functions for better dnssec handling
+ *
+ * A zone contains a SOA dnssec_zone_rrset, and an AVL tree of 'normal'
+ * dnssec_zone_rrsets, indexed by name and type
+ */
+
+#ifndef LDNS_DNSSEC_ZONE_H
+#define LDNS_DNSSEC_ZONE_H
+ 
+#include <ldns/ldns.h>
+#include <ldns/rbtree.h>
+#include <ldns/host2str.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Singly linked list of rrs
+ */
+typedef struct ldns_struct_dnssec_rrs ldns_dnssec_rrs;
+struct ldns_struct_dnssec_rrs
+{
+	ldns_rr *rr;
+	ldns_dnssec_rrs *next;
+};
+
+/**
+ * Singly linked list of RRsets
+ */
+typedef struct ldns_struct_dnssec_rrsets ldns_dnssec_rrsets;
+struct ldns_struct_dnssec_rrsets
+{
+	ldns_dnssec_rrs *rrs;
+	ldns_rr_type type;
+	ldns_dnssec_rrs *signatures;
+	ldns_dnssec_rrsets *next;
+};
+
+/**
+ * Structure containing all resource records for a domain name
+ * Including the derived NSEC3, if present
+ */
+typedef struct ldns_struct_dnssec_name ldns_dnssec_name;
+struct ldns_struct_dnssec_name
+{
+	/**
+	 * pointer to a dname containing the name.
+	 * Usually points to the owner name of the first RR of the first RRset
+	 */
+	ldns_rdf *name;
+	/** 
+	 * Usually, the name is a pointer to the owner name of the first rr for
+	 * this name, but sometimes there is no actual data to point to, 
+	 * for instance in
+	 * names representing empty nonterminals. If so, set alloced to true to
+	 * indicate that this data must also be freed when the name is freed
+	 */
+	bool name_alloced;
+	/**
+	 * The rrsets for this name
+	 */
+	ldns_dnssec_rrsets *rrsets;
+	/**
+	 * NSEC pointing to the next name (or NSEC3 pointing to the next NSEC3)
+	 */
+	ldns_rr *nsec;
+	/**
+	 * signatures for the NSEC record
+	 */
+	ldns_dnssec_rrs *nsec_signatures;
+	/**
+	 * Unlike what the name is_glue suggests, this field is set to true by
+	 * ldns_dnssec_zone_mark_glue() or ldns_dnssec_zone_mark_and_get_glue()
+	 * when the name, this dnssec_name struct represents, is occluded.
+	 * Names that contain other occluded rrsets and records with glue on
+	 * the delegation point will NOT have this bool set to true.
+	 * This field should NOT be read directly, but only via the 
+	 * ldns_dnssec_name_is_glue() function!
+	 */
+	bool is_glue;
+	/**
+	 * pointer to store the hashed name (only used when in an NSEC3 zone
+	 */
+	ldns_rdf *hashed_name;
+};
+
+/**
+ * Structure containing a dnssec zone
+ */
+struct ldns_struct_dnssec_zone {
+	/** points to the name containing the SOA RR */
+	ldns_dnssec_name *soa;
+	/** tree of ldns_dnssec_names */
+	ldns_rbtree_t *names;
+};
+typedef struct ldns_struct_dnssec_zone ldns_dnssec_zone;
+
+/**
+ * Creates a new entry for 1 pointer to an rr and 1 pointer to the next rrs
+ * \return the allocated data
+ */
+ldns_dnssec_rrs *ldns_dnssec_rrs_new();
+
+/**
+ * Frees the list of rrs, but *not* the individual ldns_rr records
+ * contained in the list
+ * 
+ * \param[in] rrs the data structure to free
+ */
+void ldns_dnssec_rrs_free(ldns_dnssec_rrs *rrs);
+
+/**
+ * Frees the list of rrs, and the individual ldns_rr records
+ * contained in the list
+ * 
+ * \param[in] rrs the data structure to free
+ */
+void ldns_dnssec_rrs_deep_free(ldns_dnssec_rrs *rrs);
+
+/**
+ * Adds an RR to the list of RRs. The list will remain ordered
+ *
+ * \param[in] rrs the list to add to
+ * \param[in] rr the RR to add
+ * \return LDNS_STATUS_OK on success
+ */
+ldns_status ldns_dnssec_rrs_add_rr(ldns_dnssec_rrs *rrs, ldns_rr *rr);
+
+/**
+ * Prints the given rrs to the file descriptor
+ *
+ * \param[in] out the file descriptor to print to
+ * \param[in] rrs the list of RRs to print
+ */
+void ldns_dnssec_rrs_print(FILE *out, ldns_dnssec_rrs *rrs);
+
+/**
+ * Prints the given rrs to the file descriptor
+ *
+ * \param[in] out the file descriptor to print to
+ * \param[in] fmt the format of the textual representation
+ * \param[in] rrs the list of RRs to print
+ */
+void ldns_dnssec_rrs_print_fmt(FILE *out, 
+		const ldns_output_format *fmt, ldns_dnssec_rrs *rrs);
+
+/**
+ * Creates a new list (entry) of RRsets
+ * \return the newly allocated structure
+ */
+ldns_dnssec_rrsets *ldns_dnssec_rrsets_new();
+
+/**
+ * Frees the list of rrsets and their rrs, but *not* the ldns_rr
+ * records in the sets
+ *
+ * \param[in] rrsets the data structure to free
+ */
+void ldns_dnssec_rrsets_free(ldns_dnssec_rrsets *rrsets);
+
+/**
+ * Frees the list of rrsets and their rrs, and the ldns_rr
+ * records in the sets
+ *
+ * \param[in] rrsets the data structure to free
+ */
+void ldns_dnssec_rrsets_deep_free(ldns_dnssec_rrsets *rrsets);
+
+/**
+ * Returns the rr type of the rrset (that is head of the given list)
+ *
+ * \param[in] rrsets the rrset to get the type of
+ * \return the rr type
+ */
+ldns_rr_type ldns_dnssec_rrsets_type(ldns_dnssec_rrsets *rrsets);
+
+/**
+ * Sets the RR type of the rrset (that is head of the given list)
+ *
+ * \param[in] rrsets the rrset to set the type of
+ * \param[in] type the type to set
+ * \return LDNS_STATUS_OK on success
+ */
+ldns_status ldns_dnssec_rrsets_set_type(ldns_dnssec_rrsets *rrsets,
+					   ldns_rr_type type);
+
+/**
+ * Add an ldns_rr to the corresponding RRset in the given list of RRsets.
+ * If it is not present, add it as a new RRset with 1 record.
+ *
+ * \param[in] rrsets the list of rrsets to add the RR to
+ * \param[in] rr the rr to add to the list of rrsets
+ * \return LDNS_STATUS_OK on success
+ */
+ldns_status ldns_dnssec_rrsets_add_rr(ldns_dnssec_rrsets *rrsets, ldns_rr *rr);
+
+/**
+ * Print the given list of rrsets to the fiven file descriptor
+ * 
+ * \param[in] out the file descriptor to print to
+ * \param[in] rrsets the list of RRsets to print
+ * \param[in] follow if set to false, only print the first RRset
+ */ 
+void ldns_dnssec_rrsets_print(FILE *out,
+		ldns_dnssec_rrsets *rrsets,
+		bool follow);
+
+/**
+ * Print the given list of rrsets to the fiven file descriptor
+ * 
+ * \param[in] out the file descriptor to print to
+ * \param[in] fmt the format of the textual representation
+ * \param[in] rrsets the list of RRsets to print
+ * \param[in] follow if set to false, only print the first RRset
+ */ 
+void ldns_dnssec_rrsets_print_fmt(FILE *out,
+		const ldns_output_format *fmt,
+		ldns_dnssec_rrsets *rrsets,
+		bool follow);
+
+
+/**
+ * Create a new data structure for a dnssec name
+ * \return the allocated structure
+ */
+ldns_dnssec_name *ldns_dnssec_name_new();
+
+/**
+ * Create a new data structure for a dnssec name for the given RR
+ *
+ * \param[in] rr the RR to derive properties from, and to add to the name
+ */
+ldns_dnssec_name *ldns_dnssec_name_new_frm_rr(ldns_rr *rr);
+
+/**
+ * Frees the name structure and its rrs and rrsets.
+ * Individual ldns_rr records therein are not freed
+ *
+ * \param[in] name the structure to free
+ */
+void ldns_dnssec_name_free(ldns_dnssec_name *name);
+
+/**
+ * Frees the name structure and its rrs and rrsets.
+ * Individual ldns_rr records contained in the name are also freed
+ *
+ * \param[in] name the structure to free
+ */
+void ldns_dnssec_name_deep_free(ldns_dnssec_name *name);
+
+/**
+ * Returns the domain name of the given dnssec_name structure
+ *
+ * \param[in] name the dnssec name to get the domain name from
+ * \return the domain name
+ */
+ldns_rdf *ldns_dnssec_name_name(ldns_dnssec_name *name);
+
+
+/**
+ * Sets the domain name of the given dnssec_name structure
+ *
+ * \param[in] name the dnssec name to set the domain name of
+ * \param[in] dname the domain name to set it to. This data is *not* copied.
+ */
+void ldns_dnssec_name_set_name(ldns_dnssec_name *name,
+						 ldns_rdf *dname);
+/**
+ * Returns if dnssec_name structure is marked as glue.
+ * The ldns_dnssec_zone_mark_glue() function has to be called on a zone before
+ * using this function.
+ * Only names that have only glue rrsets will be marked.
+ * Names that have other occluded rrsets and names containing glue on the 
+ * delegation point will NOT be marked!
+ *
+ * \param[in] name the dnssec name to get the domain name from
+ * \return true if the structure is marked as glue, false otherwise.
+ */
+bool ldns_dnssec_name_is_glue(ldns_dnssec_name *name);
+
+/**
+ * Sets the NSEC(3) RR of the given dnssec_name structure
+ *
+ * \param[in] name the dnssec name to set the domain name of
+ * \param[in] nsec the nsec rr to set it to. This data is *not* copied.
+ */
+void ldns_dnssec_name_set_nsec(ldns_dnssec_name *name, ldns_rr *nsec);
+
+/**
+ * Compares the domain names of the two arguments in their
+ * canonical ordening.
+ * 
+ * \param[in] a The first dnssec_name to compare
+ * \param[in] b The second dnssec_name to compare
+ * \return -1 if the domain name of a comes before that of b in canonical
+ *            ordening, 1 if it is the other way around, and 0 if they are
+ *            equal
+ */
+int ldns_dnssec_name_cmp(const void *a, const void *b);
+
+/**
+ * Inserts the given rr at the right place in the current dnssec_name
+ * No checking is done whether the name matches
+ *
+ * \param[in] name The ldns_dnssec_name to add the RR to
+ * \param[in] rr The RR to add
+ * \return LDNS_STATUS_OK on success, error code otherwise
+ */
+ldns_status ldns_dnssec_name_add_rr(ldns_dnssec_name *name,
+							 ldns_rr *rr);
+
+/**
+ * Find the RRset with the given type in within this name structure
+ *
+ * \param[in] name the name to find the RRset in
+ * \param[in] type the type of the RRset to find
+ * \return the RRset, or NULL if not present
+ */
+ldns_dnssec_rrsets *ldns_dnssec_name_find_rrset(ldns_dnssec_name *name,
+									   ldns_rr_type type);
+
+/**
+ * Find the RRset with the given name and type in the zone
+ *
+ * \param[in] zone the zone structure to find the RRset in
+ * \param[in] dname the domain name of the RRset to find
+ * \param[in] type the type of the RRset to find
+ * \return the RRset, or NULL if not present
+ */
+ldns_dnssec_rrsets *ldns_dnssec_zone_find_rrset(ldns_dnssec_zone *zone,
+									   ldns_rdf *dname,
+									   ldns_rr_type type);
+
+/**
+ * Prints the RRs in the  dnssec name structure to the given
+ * file descriptor
+ *
+ * \param[in] out the file descriptor to print to
+ * \param[in] name the name structure to print the contents of
+ */
+void ldns_dnssec_name_print(FILE *out, ldns_dnssec_name *name);
+
+/**
+ * Prints the RRs in the  dnssec name structure to the given
+ * file descriptor
+ *
+ * \param[in] out the file descriptor to print to
+ * \param[in] fmt the format of the textual representation
+ * \param[in] name the name structure to print the contents of
+ */
+void ldns_dnssec_name_print_fmt(FILE *out, 
+		const ldns_output_format *fmt, ldns_dnssec_name *name);
+
+/**
+ * Creates a new dnssec_zone structure
+ * \return the allocated structure
+ */
+ldns_dnssec_zone *ldns_dnssec_zone_new();
+
+/**
+ * Frees the given zone structure, and its rbtree of dnssec_names
+ * Individual ldns_rr RRs within those names are *not* freed
+ * \param[in] *zone the zone to free
+ */ 
+void ldns_dnssec_zone_free(ldns_dnssec_zone *zone);
+
+/**
+ * Frees the given zone structure, and its rbtree of dnssec_names
+ * Individual ldns_rr RRs within those names are also freed
+ * \param[in] *zone the zone to free
+ */ 
+void ldns_dnssec_zone_deep_free(ldns_dnssec_zone *zone);
+
+/**
+ * Adds the given RR to the zone.
+ * It find whether there is a dnssec_name with that name present.
+ * If so, add it to that, if not create a new one. 
+ * Special handling of NSEC and RRSIG provided
+ *
+ * \param[in] zone the zone to add the RR to
+ * \param[in] rr The RR to add
+ * \return LDNS_STATUS_OK on success, an error code otherwise
+ */
+ldns_status ldns_dnssec_zone_add_rr(ldns_dnssec_zone *zone,
+							 ldns_rr *rr);
+
+/**
+ * Prints the rbtree of ldns_dnssec_name structures to the file descriptor
+ *
+ * \param[in] out the file descriptor to print the names to
+ * \param[in] tree the tree of ldns_dnssec_name structures to print
+ * \param[in] print_soa if true, print SOA records, if false, skip them
+ */
+void ldns_dnssec_zone_names_print(FILE *out, ldns_rbtree_t *tree, bool print_soa);
+
+/**
+ * Prints the rbtree of ldns_dnssec_name structures to the file descriptor
+ *
+ * \param[in] out the file descriptor to print the names to
+ * \param[in] fmt the format of the textual representation
+ * \param[in] tree the tree of ldns_dnssec_name structures to print
+ * \param[in] print_soa if true, print SOA records, if false, skip them
+ */
+void ldns_dnssec_zone_names_print_fmt(FILE *out, const ldns_output_format *fmt,
+		ldns_rbtree_t *tree, bool print_soa);
+
+/**
+ * Prints the complete zone to the given file descriptor
+ *
+ * \param[in] out the file descriptor to print to
+ * \param[in] zone the dnssec_zone to print
+ */
+void ldns_dnssec_zone_print(FILE *out, ldns_dnssec_zone *zone);
+
+/**
+ * Prints the complete zone to the given file descriptor
+ *
+ * \param[in] out the file descriptor to print to
+ * \param[in] fmt the format of the textual representation
+ * \param[in] zone the dnssec_zone to print
+ */
+void ldns_dnssec_zone_print_fmt(FILE *out, 
+		const ldns_output_format *fmt, ldns_dnssec_zone *zone);
+
+/**
+ * Adds explicit dnssec_name structures for the empty nonterminals
+ * in this zone. (this is needed for NSEC3 generation)
+ *
+ * \param[in] zone the zone to check for empty nonterminals
+ * return LDNS_STATUS_OK on success.
+ */
+ldns_status ldns_dnssec_zone_add_empty_nonterminals(ldns_dnssec_zone *zone);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/3rdParty/Ldns/src/include/ldns/error.h b/3rdParty/Ldns/src/include/ldns/error.h
new file mode 100644
index 0000000..99d4f0b
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/error.h
@@ -0,0 +1,121 @@
+/**
+ * \file error.h
+ *
+ * Defines error numbers and functions to translate those to a readable string.
+ *
+ */
+ 
+/**
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2005-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+#ifndef LDNS_ERROR_H
+#define LDNS_ERROR_H
+
+#include <ldns/util.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum ldns_enum_status {
+	LDNS_STATUS_OK,	
+	LDNS_STATUS_EMPTY_LABEL,
+	LDNS_STATUS_LABEL_OVERFLOW,
+	LDNS_STATUS_DOMAINNAME_OVERFLOW,
+	LDNS_STATUS_DOMAINNAME_UNDERFLOW,
+	LDNS_STATUS_DDD_OVERFLOW,
+	LDNS_STATUS_PACKET_OVERFLOW,
+	LDNS_STATUS_INVALID_POINTER,
+	LDNS_STATUS_MEM_ERR,
+	LDNS_STATUS_INTERNAL_ERR,
+	LDNS_STATUS_SSL_ERR,
+	LDNS_STATUS_ERR,
+	LDNS_STATUS_INVALID_INT,
+	LDNS_STATUS_INVALID_IP4,
+	LDNS_STATUS_INVALID_IP6,
+	LDNS_STATUS_INVALID_STR,
+	LDNS_STATUS_INVALID_B32_EXT,
+	LDNS_STATUS_INVALID_B64,
+	LDNS_STATUS_INVALID_HEX,
+	LDNS_STATUS_INVALID_TIME,
+	LDNS_STATUS_NETWORK_ERR,
+	LDNS_STATUS_ADDRESS_ERR,
+	LDNS_STATUS_FILE_ERR,
+	LDNS_STATUS_UNKNOWN_INET,
+	LDNS_STATUS_NOT_IMPL,
+	LDNS_STATUS_NULL,
+	LDNS_STATUS_CRYPTO_UNKNOWN_ALGO, 
+	LDNS_STATUS_CRYPTO_ALGO_NOT_IMPL, 	
+	LDNS_STATUS_CRYPTO_NO_RRSIG,
+	LDNS_STATUS_CRYPTO_NO_DNSKEY,
+	LDNS_STATUS_CRYPTO_NO_TRUSTED_DNSKEY,
+	LDNS_STATUS_CRYPTO_NO_DS,
+	LDNS_STATUS_CRYPTO_NO_TRUSTED_DS,
+	LDNS_STATUS_CRYPTO_NO_MATCHING_KEYTAG_DNSKEY,
+	LDNS_STATUS_CRYPTO_VALIDATED,
+	LDNS_STATUS_CRYPTO_BOGUS,
+	LDNS_STATUS_CRYPTO_SIG_EXPIRED,
+	LDNS_STATUS_CRYPTO_SIG_NOT_INCEPTED,
+	LDNS_STATUS_CRYPTO_TSIG_BOGUS,
+	LDNS_STATUS_CRYPTO_TSIG_ERR,
+	LDNS_STATUS_CRYPTO_EXPIRATION_BEFORE_INCEPTION,
+	LDNS_STATUS_CRYPTO_TYPE_COVERED_ERR,
+	LDNS_STATUS_ENGINE_KEY_NOT_LOADED,
+	LDNS_STATUS_NSEC3_ERR,
+	LDNS_STATUS_RES_NO_NS,
+	LDNS_STATUS_RES_QUERY,
+	LDNS_STATUS_WIRE_INCOMPLETE_HEADER,
+	LDNS_STATUS_WIRE_INCOMPLETE_QUESTION,
+	LDNS_STATUS_WIRE_INCOMPLETE_ANSWER,
+	LDNS_STATUS_WIRE_INCOMPLETE_AUTHORITY,
+	LDNS_STATUS_WIRE_INCOMPLETE_ADDITIONAL,
+	LDNS_STATUS_NO_DATA,
+	LDNS_STATUS_CERT_BAD_ALGORITHM,
+	LDNS_STATUS_SYNTAX_TYPE_ERR,
+	LDNS_STATUS_SYNTAX_CLASS_ERR,
+	LDNS_STATUS_SYNTAX_TTL_ERR,
+	LDNS_STATUS_SYNTAX_INCLUDE_ERR_NOTIMPL,
+	LDNS_STATUS_SYNTAX_RDATA_ERR,
+	LDNS_STATUS_SYNTAX_DNAME_ERR,
+	LDNS_STATUS_SYNTAX_VERSION_ERR,
+	LDNS_STATUS_SYNTAX_ALG_ERR,
+	LDNS_STATUS_SYNTAX_KEYWORD_ERR,
+	LDNS_STATUS_SYNTAX_TTL,
+	LDNS_STATUS_SYNTAX_ORIGIN,
+	LDNS_STATUS_SYNTAX_INCLUDE,
+	LDNS_STATUS_SYNTAX_EMPTY,
+	LDNS_STATUS_SYNTAX_ITERATIONS_OVERFLOW,
+	LDNS_STATUS_SYNTAX_MISSING_VALUE_ERR,
+	LDNS_STATUS_SYNTAX_INTEGER_OVERFLOW,
+	LDNS_STATUS_SYNTAX_BAD_ESCAPE,
+	LDNS_STATUS_SOCKET_ERROR,
+	LDNS_STATUS_SYNTAX_ERR,
+	LDNS_STATUS_DNSSEC_EXISTENCE_DENIED,
+	LDNS_STATUS_DNSSEC_NSEC_RR_NOT_COVERED,
+	LDNS_STATUS_DNSSEC_NSEC_WILDCARD_NOT_COVERED,
+	LDNS_STATUS_DNSSEC_NSEC3_ORIGINAL_NOT_FOUND,
+	LDNS_STATUS_MISSING_RDATA_FIELDS_RRSIG,
+	LDNS_STATUS_MISSING_RDATA_FIELDS_KEY
+};
+typedef enum ldns_enum_status ldns_status;
+
+extern ldns_lookup_table ldns_error_str[];
+
+/**
+ * look up a descriptive text by each error. This function
+ * could use a better name
+ * \param[in] err ldns_status number
+ * \return the string for that error
+ */
+const char *ldns_get_errorstr_by_id(ldns_status err);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LDNS_ERROR_H */
diff --git a/3rdParty/Ldns/src/include/ldns/higher.h b/3rdParty/Ldns/src/include/ldns/higher.h
new file mode 100644
index 0000000..597e134
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/higher.h
@@ -0,0 +1,113 @@
+/**
+ * \file higher.h
+ *
+ * Specifies some higher level functions that could
+ * be useful for certain applications
+ */
+
+/*
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2005-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+#ifndef LDNS_HIGHER_H
+#define LDNS_HIGHER_H
+
+#include <ldns/resolver.h>
+#include <ldns/rdata.h>
+#include <ldns/rr.h>
+#include <ldns/host2str.h>
+#include <ldns/tsig.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Ask the resolver about name
+ * and return all address records
+ * \param[in] r the resolver to use
+ * \param[in] name the name to look for
+ * \param[in] c the class to use
+ * \param[in] flags give some optional flags to the query
+ */
+ldns_rr_list *ldns_get_rr_list_addr_by_name(ldns_resolver *r, ldns_rdf *name, ldns_rr_class c, uint16_t flags);
+
+/**
+ * ask the resolver about the address
+ * and return the name
+ * \param[in] r the resolver to use
+ * \param[in] addr the addr to look for
+ * \param[in] c the class to use
+ * \param[in] flags give some optional flags to the query
+ */
+ldns_rr_list *ldns_get_rr_list_name_by_addr(ldns_resolver *r, ldns_rdf *addr, ldns_rr_class c, uint16_t flags);
+
+/**
+ * wade through fp (a /etc/hosts like file)
+ * and return a rr_list containing all the 
+ * defined hosts in there
+ * \param[in] fp the file pointer to use
+ * \return ldns_rr_list * with the names
+ */
+ldns_rr_list *ldns_get_rr_list_hosts_frm_fp(FILE *fp);
+
+/**
+ * wade through fp (a /etc/hosts like file)
+ * and return a rr_list containing all the 
+ * defined hosts in there
+ * \param[in] fp the file pointer to use
+ * \param[in] line_nr pointer to an integer containing the current line number (for debugging purposes)
+ * \return ldns_rr_list * with the names
+ */
+ldns_rr_list *ldns_get_rr_list_hosts_frm_fp_l(FILE *fp, int *line_nr);
+
+/**
+ * wade through fp (a /etc/hosts like file)
+ * and return a rr_list containing all the 
+ * defined hosts in there
+ * \param[in] filename the filename to use (NULL for /etc/hosts)
+ * \return ldns_rr_list * with the names
+ */
+ldns_rr_list *ldns_get_rr_list_hosts_frm_file(char *filename);
+
+/**
+ * This function is a wrapper function for ldns_get_rr_list_name_by_addr
+ * and ldns_get_rr_list_addr_by_name. It's name is from the getaddrinfo() 
+ * library call. It tries to mimic that call, but without the lowlevel
+ * stuff.
+ * \param[in] res The resolver. If this value is NULL then a resolver will
+ * be created by ldns_getaddrinfo.
+ * \param[in] node the name or ip address to look up
+ * \param[in] c the class to look in
+ * \param[out] list put the found RR's in this list
+ * \return the number of RR found.
+ */
+uint16_t ldns_getaddrinfo(ldns_resolver *res, ldns_rdf *node, ldns_rr_class c, ldns_rr_list **list);
+
+/**
+ * Check if t is enumerated in the nsec type rdata
+ * \param[in] nsec the NSEC Record to look in
+ * \param[in] t the type to check for
+ * \return true when t is found, otherwise return false
+ */
+bool ldns_nsec_type_check(ldns_rr *nsec, ldns_rr_type t);
+
+/**
+ * Print a number of rdf's of the RR. The rdfnum-list must 
+ * be ended by -1, otherwise unpredictable things might happen.
+ * rdfs may be printed multiple times
+ * \param[in] fp FILE * to write to
+ * \param[in] r RR to write
+ * \param[in] rdfnum a list of rdf to print.
+ */
+void ldns_print_rr_rdf(FILE *fp, ldns_rr *r, int rdfnum, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LDNS_HIGHER_H */
diff --git a/3rdParty/Ldns/src/include/ldns/host2str.h b/3rdParty/Ldns/src/include/ldns/host2str.h
new file mode 100644
index 0000000..f0a14a4
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/host2str.h
@@ -0,0 +1,759 @@
+/**
+ * host2str.h -  txt presentation of RRs
+ *
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2005-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+/**
+ * \file
+ *
+ * Contains functions to translate the main structures to their text
+ * representation, as well as functions to print them.
+ */
+
+#ifndef LDNS_HOST2STR_H
+#define LDNS_HOST2STR_H
+
+#include <ldns/common.h>
+#include <ldns/error.h>
+#include <ldns/rr.h>
+#include <ldns/rdata.h>
+#include <ldns/packet.h>
+#include <ldns/buffer.h>
+#include <ldns/resolver.h>
+#include <ldns/zone.h>
+#include <ctype.h>
+
+#include "ldns/util.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LDNS_APL_IP4            1
+#define LDNS_APL_IP6            2
+#define LDNS_APL_MASK           0x7f
+#define LDNS_APL_NEGATION       0x80
+
+/** 
+ * Represent a NULL pointer (in stead of a pointer to a ldns_rr as "; (null)" 
+ * as opposed to outputting nothing at all in such a case.
+ */
+#define LDNS_COMMENT_NULLS		0x0001
+/** Show key id with DNSKEY RR's as comment */
+#define LDNS_COMMENT_KEY_ID		0x0002
+/** Show if a DNSKEY is a ZSK or KSK as comment */
+#define LDNS_COMMENT_KEY_TYPE		0x0004
+/** Show DNSKEY key size as comment */
+#define LDNS_COMMENT_KEY_SIZE		0x0008
+/** Show key id, type and size as comment for DNSKEY RR's */
+#define LDNS_COMMENT_KEY		(LDNS_COMMENT_KEY_ID  \
+					|LDNS_COMMENT_KEY_TYPE\
+					|LDNS_COMMENT_KEY_SIZE)
+/** Provide bubblebabble representation for DS RR's as comment */
+#define LDNS_COMMENT_BUBBLEBABBLE	0x0010
+/** Show when a NSEC3 RR has the optout flag set as comment */
+#define LDNS_COMMENT_FLAGS		0x0020
+/** Show the unhashed owner and next owner names for NSEC3 RR's as comment */
+#define LDNS_COMMENT_NSEC3_CHAIN	0x0040
+/** Print mark up */
+#define LDNS_COMMENT_LAYOUT		0x0080
+/** Also comment KEY_ID with RRSIGS **/
+#define LDNS_COMMENT_RRSIGS		0x0100
+
+/**
+ * Output format specifier
+ *
+ * Determines how Packets, Resource Records and Resource record data fiels are
+ * formatted when printing or converting to string.
+ * Currently it is only used to specify what aspects of a Resource Record are
+ * annotated in the comment section of the textual representation the record.
+ * This is speciefed with flags and potential exra data (such as for example
+ * a lookup map of hashes to real names for annotation NSEC3 records).
+ */
+struct ldns_struct_output_format
+{
+	/** Specification of how RR's should be formatted in text */
+	int   flags;
+	/** Potential extra data to be used with formatting RR's in text */
+	void *data;
+};
+typedef struct ldns_struct_output_format ldns_output_format;
+
+/**
+ * Standard output format record that disables commenting in the textual 
+ * representation of Resource Records completely.
+ */
+extern const ldns_output_format *ldns_output_format_nocomments;
+/**
+ * Standard output format record that annotated only DNSKEY RR's with commenti
+ * text.
+ */
+extern const ldns_output_format *ldns_output_format_onlykeyids;
+/**
+ * The default output format record. Same as ldns_output_format_onlykeyids.
+ */
+extern const ldns_output_format *ldns_output_format_default;
+/**
+ * Standard output format record that shows all DNSKEY related information in
+ * the comment text, plus the optout flag when set with NSEC3's, plus the
+ * bubblebabble representation of DS RR's.
+ */
+extern const ldns_output_format *ldns_output_format_bubblebabble;
+
+/**
+ * Converts an ldns packet opcode value to its mnemonic, and adds that
+ * to the output buffer
+ * \param[in] *output the buffer to add the data to
+ * \param[in] opcode to find the string representation of
+ * \return LDNS_STATUS_OK on success, or a buffer failure mode on error
+ */
+ldns_status
+ldns_pkt_opcode2buffer_str(ldns_buffer *output, ldns_pkt_opcode opcode);
+
+/**
+ * Converts an ldns packet rcode value to its mnemonic, and adds that
+ * to the output buffer
+ * \param[in] *output the buffer to add the data to
+ * \param[in] rcode to find the string representation of
+ * \return LDNS_STATUS_OK on success, or a buffer failure mode on error
+ */
+ldns_status
+ldns_pkt_rcode2buffer_str(ldns_buffer *output, ldns_pkt_rcode rcode);
+
+/**
+ * Converts an ldns algorithm type to its mnemonic, and adds that
+ * to the output buffer
+ * \param[in] *output the buffer to add the data to
+ * \param[in] algorithm to find the string representation of
+ * \return LDNS_STATUS_OK on success, or a buffer failure mode on error
+ */
+ldns_status
+ldns_algorithm2buffer_str(ldns_buffer *output,
+                          ldns_algorithm algorithm);
+
+/**
+ * Converts an ldns certificate algorithm type to its mnemonic, 
+ * and adds that to the output buffer
+ * \param[in] *output the buffer to add the data to
+ * \param[in] cert_algorithm to find the string representation of
+ * \return LDNS_STATUS_OK on success, or a buffer failure mode on error
+ */
+ldns_status
+ldns_cert_algorithm2buffer_str(ldns_buffer *output,
+                               ldns_cert_algorithm cert_algorithm);
+
+
+/**
+ * Converts a packet opcode to its mnemonic and returns that as
+ * an allocated null-terminated string.
+ * Remember to free it.
+ *
+ * \param[in] opcode the opcode to convert to text
+ * \return null terminated char * data, or NULL on error
+ */
+char *ldns_pkt_opcode2str(ldns_pkt_opcode opcode);
+
+/**
+ * Converts a packet rcode to its mnemonic and returns that as
+ * an allocated null-terminated string.
+ * Remember to free it.
+ *
+ * \param[in] rcode the rcode to convert to text
+ * \return null terminated char * data, or NULL on error
+ */
+char *ldns_pkt_rcode2str(ldns_pkt_rcode rcode);
+
+/**
+ * Converts a signing algorithms to its mnemonic and returns that as
+ * an allocated null-terminated string.
+ * Remember to free it.
+ *
+ * \param[in] algorithm the algorithm to convert to text
+ * \return null terminated char * data, or NULL on error
+ */
+char *ldns_pkt_algorithm2str(ldns_algorithm algorithm);
+
+/**
+ * Converts a cert algorithm to its mnemonic and returns that as
+ * an allocated null-terminated string.
+ * Remember to free it.
+ *
+ * \param[in] cert_algorithm to convert to text
+ * \return null terminated char * data, or NULL on error
+ */
+char *ldns_pkt_cert_algorithm2str(ldns_cert_algorithm cert_algorithm);
+
+/** 
+ * Converts an LDNS_RDF_TYPE_A rdata element to string format and adds it to the output buffer 
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_a(ldns_buffer *output, const ldns_rdf *rdf);
+
+/** 
+ * Converts an LDNS_RDF_TYPE_AAAA rdata element to string format and adds it to the output buffer 
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_aaaa(ldns_buffer *output, const ldns_rdf *rdf);
+
+/** 
+ * Converts an LDNS_RDF_TYPE_STR rdata element to string format and adds it to the output buffer 
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_str(ldns_buffer *output, const ldns_rdf *rdf);
+
+/** 
+ * Converts an LDNS_RDF_TYPE_B64 rdata element to string format and adds it to the output buffer 
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_b64(ldns_buffer *output, const ldns_rdf *rdf);
+
+/** 
+ * Converts an LDNS_RDF_TYPE_B32_EXT rdata element to string format and adds it to the output buffer 
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_b32_ext(ldns_buffer *output, const ldns_rdf *rdf);
+
+/** 
+ * Converts an LDNS_RDF_TYPE_HEX rdata element to string format and adds it to the output buffer 
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_hex(ldns_buffer *output, const ldns_rdf *rdf);
+
+/** 
+ * Converts an LDNS_RDF_TYPE_TYPE rdata element to string format and adds it to the output buffer 
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_type(ldns_buffer *output, const ldns_rdf *rdf);
+
+/** 
+ * Converts an LDNS_RDF_TYPE_CLASS rdata element to string format and adds it to the output buffer 
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_class(ldns_buffer *output, const ldns_rdf *rdf);
+
+/** 
+ * Converts an LDNS_RDF_TYPE_ALG rdata element to string format and adds it to the output buffer 
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_alg(ldns_buffer *output, const ldns_rdf *rdf);
+
+/**
+ * Converts an ldns_rr_type value to its string representation,
+ * and places it in the given buffer
+ * \param[in] *output The buffer to add the data to
+ * \param[in] type the ldns_rr_type to convert
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rr_type2buffer_str(ldns_buffer *output,
+                                    const ldns_rr_type type);
+
+/**
+ * Converts an ldns_rr_type value to its string representation,
+ * and returns that string. For unknown types, the string
+ * "TYPE<id>" is returned. This function allocates data that must be
+ * freed by the caller
+ * \param[in] type the ldns_rr_type to convert
+ * \return a newly allocated string
+ */
+char *ldns_rr_type2str(const ldns_rr_type type);
+
+/**
+ * Converts an ldns_rr_class value to its string representation,
+ * and places it in the given buffer
+ * \param[in] *output The buffer to add the data to
+ * \param[in] klass the ldns_rr_class to convert
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rr_class2buffer_str(ldns_buffer *output,
+                                     const ldns_rr_class klass);
+
+/**
+ * Converts an ldns_rr_class value to its string representation,
+ * and returns that string. For unknown types, the string
+ * "CLASS<id>" is returned. This function allocates data that must be
+ * freed by the caller
+ * \param[in] klass the ldns_rr_class to convert
+ * \return a newly allocated string
+ */
+char *ldns_rr_class2str(const ldns_rr_class klass);
+
+
+/** 
+ * Converts an LDNS_RDF_TYPE_CERT rdata element to string format and adds it to the output buffer 
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_cert_alg(ldns_buffer *output, const ldns_rdf *rdf);
+
+/** 
+ * Converts an LDNS_RDF_TYPE_LOC rdata element to string format and adds it to the output buffer 
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_loc(ldns_buffer *output, const ldns_rdf *rdf);
+
+/** 
+ * Converts an LDNS_RDF_TYPE_UNKNOWN rdata element to string format and adds it to the output buffer 
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_unknown(ldns_buffer *output, const ldns_rdf *rdf);
+
+/** 
+ * Converts an LDNS_RDF_TYPE_NSAP rdata element to string format and adds it to the output buffer 
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_nsap(ldns_buffer *output, const ldns_rdf *rdf);
+
+/** 
+ * Converts an LDNS_RDF_TYPE_ATMA rdata element to string format and adds it to the output buffer 
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_atma(ldns_buffer *output, const ldns_rdf *rdf);
+
+/** 
+ * Converts an LDNS_RDF_TYPE_WKS rdata element to string format and adds it to the output buffer 
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_wks(ldns_buffer *output, const ldns_rdf *rdf);
+
+/** 
+ * Converts an LDNS_RDF_TYPE_NSEC rdata element to string format and adds it to the output buffer 
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_nsec(ldns_buffer *output, const ldns_rdf *rdf);
+
+/** 
+ * Converts an LDNS_RDF_TYPE_PERIOD rdata element to string format and adds it to the output buffer 
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_period(ldns_buffer *output, const ldns_rdf *rdf);
+
+/** 
+ * Converts an LDNS_RDF_TYPE_TSIGTIME rdata element to string format and adds it to the output buffer 
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_tsigtime(ldns_buffer *output, const ldns_rdf *rdf);
+
+/** 
+ * Converts an LDNS_RDF_TYPE_APL rdata element to string format and adds it to the output buffer 
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_apl(ldns_buffer *output, const ldns_rdf *rdf);
+
+/** 
+ * Converts an LDNS_RDF_TYPE_INT16_DATA rdata element to string format and adds it to the output buffer 
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_int16_data(ldns_buffer *output, const ldns_rdf *rdf);
+
+/** 
+ * Converts an LDNS_RDF_TYPE_IPSECKEY rdata element to string format and adds it to the output buffer 
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_ipseckey(ldns_buffer *output, const ldns_rdf *rdf);
+
+/** 
+ * Converts an LDNS_RDF_TYPE_TSIG rdata element to string format and adds it to the output buffer 
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_tsig(ldns_buffer *output, const ldns_rdf *rdf);
+
+
+/**
+ * Converts the data in the rdata field to presentation
+ * format (as char *) and appends it to the given buffer
+ *
+ * \param[in] output pointer to the buffer to append the data to
+ * \param[in] rdf the pointer to the rdafa field containing the data
+ * \return status
+ */
+ldns_status ldns_rdf2buffer_str(ldns_buffer *output, const ldns_rdf *rdf);
+
+/**
+ * Converts the data in the resource record to presentation
+ * format (as char *) and appends it to the given buffer.
+ * The presentation format of DNSKEY record is annotated with comments giving
+ * the id, type and size of the key.
+ *
+ * \param[in] output pointer to the buffer to append the data to
+ * \param[in] rr the pointer to the rr field to convert
+ * \return status
+ */
+ldns_status ldns_rr2buffer_str(ldns_buffer *output, const ldns_rr *rr);
+
+/**
+ * Converts the data in the resource record to presentation
+ * format (as char *) and appends it to the given buffer.
+ * The presentation format is annotated with comments giving
+ * additional information on the record.
+ *
+ * \param[in] output pointer to the buffer to append the data to
+ * \param[in] fmt how to format the textual representation of the 
+ *            resource record.
+ * \param[in] rr the pointer to the rr field to convert
+ * \return status
+ */
+ldns_status ldns_rr2buffer_str_fmt(ldns_buffer *output, 
+		const ldns_output_format *fmt, const ldns_rr *rr);
+
+/**
+ * Converts the data in the DNS packet to presentation
+ * format (as char *) and appends it to the given buffer
+ *
+ * \param[in] output pointer to the buffer to append the data to
+ * \param[in] pkt the pointer to the packet to convert
+ * \return status
+ */
+ldns_status ldns_pkt2buffer_str(ldns_buffer *output, const ldns_pkt *pkt);
+
+/**
+ * Converts the data in the DNS packet to presentation
+ * format (as char *) and appends it to the given buffer
+ *
+ * \param[in] output pointer to the buffer to append the data to
+ * \param[in] fmt how to format the textual representation of the packet
+ * \param[in] pkt the pointer to the packet to convert
+ * \return status
+ */
+ldns_status ldns_pkt2buffer_str_fmt(ldns_buffer *output,
+		const ldns_output_format *fmt, const ldns_pkt *pkt);
+
+/** 
+ * Converts an LDNS_RDF_TYPE_NSEC3_SALT rdata element to string format and adds it to the output buffer 
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_nsec3_salt(ldns_buffer *output, const ldns_rdf *rdf);
+
+
+/**
+ * Converts the data in the DNS packet to presentation
+ * format (as char *) and appends it to the given buffer
+ *
+ * \param[in] output pointer to the buffer to append the data to
+ * \param[in] k the pointer to the private key to convert
+ * \return status
+ */
+ldns_status ldns_key2buffer_str(ldns_buffer *output, const ldns_key *k);
+
+/**
+ * Converts an LDNS_RDF_TYPE_INT8 rdata element to string format and adds it to the output buffer
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_int8(ldns_buffer *output, const ldns_rdf *rdf);
+
+/**
+ * Converts an LDNS_RDF_TYPE_INT16 rdata element to string format and adds it to the output buffer
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_int16(ldns_buffer *output, const ldns_rdf *rdf);
+
+/**
+ * Converts an LDNS_RDF_TYPE_INT32 rdata element to string format and adds it to the output buffer
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_int32(ldns_buffer *output, const ldns_rdf *rdf);
+
+/**
+ * Converts an LDNS_RDF_TYPE_TIME rdata element to string format and adds it to the output buffer
+ * \param[in] *rdf The rdata to convert
+ * \param[in] *output The buffer to add the data to
+ * \return LDNS_STATUS_OK on success, and error status on failure
+ */
+ldns_status ldns_rdf2buffer_str_time(ldns_buffer *output, const ldns_rdf *rdf);
+
+/**
+ * Converts the data in the rdata field to presentation format and
+ * returns that as a char *.
+ * Remember to free it.
+ *
+ * \param[in] rdf The rdata field to convert
+ * \return null terminated char * data, or NULL on error
+ */
+char *ldns_rdf2str(const ldns_rdf *rdf);
+
+/**
+ * Converts the data in the resource record to presentation format and
+ * returns that as a char *.
+ * Remember to free it.
+ *
+ * \param[in] rr The rdata field to convert
+ * \return null terminated char * data, or NULL on error
+ */
+char *ldns_rr2str(const ldns_rr *rr);
+
+/**
+ * Converts the data in the resource record to presentation format and
+ * returns that as a char *.
+ * Remember to free it.
+ *
+ * \param[in] fmt how to format the resource record
+ * \param[in] rr The rdata field to convert
+ * \return null terminated char * data, or NULL on error
+ */
+char *ldns_rr2str_fmt(const ldns_output_format *fmt, const ldns_rr *rr);
+
+/**
+ * Converts the data in the DNS packet to presentation format and
+ * returns that as a char *.
+ * Remember to free it.
+ *
+ * \param[in] pkt The rdata field to convert
+ * \return null terminated char * data, or NULL on error
+ */
+char *ldns_pkt2str(const ldns_pkt *pkt);
+
+/**
+ * Converts the data in the DNS packet to presentation format and
+ * returns that as a char *.
+ * Remember to free it.
+ *
+ * \param[in] fmt how to format the packet
+ * \param[in] pkt The rdata field to convert
+ * \return null terminated char * data, or NULL on error
+ */
+char *ldns_pkt2str_fmt(const ldns_output_format *fmt, const ldns_pkt *pkt);
+
+/**
+ * Converts a private key to the test presentation fmt and
+ * returns that as a char *.
+ * Remember to free it.
+ *
+ * \param[in] k the key to convert to text
+ * \return null terminated char * data, or NULL on error
+ */
+char *ldns_key2str(const ldns_key *k);
+
+/**
+ * Converts a list of resource records to presentation format
+ * and returns that as a char *.
+ * Remember to free it.
+ *
+ * \param[in] rr_list the rr_list to convert to text
+ * \return null terminated char * data, or NULL on error
+ */
+char *ldns_rr_list2str(const ldns_rr_list *rr_list);
+
+/**
+ * Converts a list of resource records to presentation format
+ * and returns that as a char *.
+ * Remember to free it.
+ *
+ * \param[in] fmt how to format the list of resource records
+ * \param[in] rr_list the rr_list to convert to text
+ * \return null terminated char * data, or NULL on error
+ */
+char *ldns_rr_list2str_fmt(
+		const ldns_output_format *fmt, const ldns_rr_list *rr_list);
+
+/**
+ * Returns the data in the buffer as a null terminated char * string
+ * Buffer data must be char * type, and must be freed by the caller
+ *
+ * \param[in] buffer buffer containing char * data
+ * \return null terminated char * data, or NULL on error
+ */
+char *ldns_buffer2str(ldns_buffer *buffer);
+
+/**
+ * Prints the data in the rdata field to the given file stream
+ * (in presentation format)
+ *
+ * \param[in] output the file stream to print to
+ * \param[in] rdf the rdata field to print
+ * \return void
+ */
+void ldns_rdf_print(FILE *output, const ldns_rdf *rdf);
+
+/**
+ * Prints the data in the resource record to the given file stream
+ * (in presentation format)
+ *
+ * \param[in] output the file stream to print to
+ * \param[in] rr the resource record to print
+ * \return void
+ */
+void ldns_rr_print(FILE *output, const ldns_rr *rr);
+
+/**
+ * Prints the data in the resource record to the given file stream
+ * (in presentation format)
+ *
+ * \param[in] output the file stream to print to
+ * \param[in] fmt format of the textual representation
+ * \param[in] rr the resource record to print
+ * \return void
+ */
+void ldns_rr_print_fmt(FILE *output, 
+		const ldns_output_format *fmt, const ldns_rr *rr);
+
+/**
+ * Prints the data in the DNS packet to the given file stream
+ * (in presentation format)
+ *
+ * \param[in] output the file stream to print to
+ * \param[in] pkt the packet to print
+ * \return void
+ */
+void ldns_pkt_print(FILE *output, const ldns_pkt *pkt);
+
+/**
+ * Prints the data in the DNS packet to the given file stream
+ * (in presentation format)
+ *
+ * \param[in] output the file stream to print to
+ * \param[in] fmt format of the textual representation
+ * \param[in] pkt the packet to print
+ * \return void
+ */
+void ldns_pkt_print_fmt(FILE *output, 
+		const ldns_output_format *fmt, const ldns_pkt *pkt);
+
+/**
+ * Converts a rr_list to presentation format and appends it to
+ * the output buffer
+ * \param[in] output the buffer to append output to
+ * \param[in] list the ldns_rr_list to print
+ * \return ldns_status
+ */
+ldns_status ldns_rr_list2buffer_str(ldns_buffer *output, const ldns_rr_list *list);
+
+/**
+ * Converts a rr_list to presentation format and appends it to
+ * the output buffer
+ * \param[in] output the buffer to append output to
+ * \param[in] fmt format of the textual representation
+ * \param[in] list the ldns_rr_list to print
+ * \return ldns_status
+ */
+ldns_status ldns_rr_list2buffer_str_fmt(ldns_buffer *output, 
+		const ldns_output_format *fmt, const ldns_rr_list *list);
+
+/**
+ * Converts the header of a packet to presentation format and appends it to
+ * the output buffer
+ * \param[in] output the buffer to append output to
+ * \param[in] pkt the packet to convert the header of
+ * \return ldns_status
+ */
+ldns_status ldns_pktheader2buffer_str(ldns_buffer *output, const ldns_pkt *pkt);
+
+/**
+ * print a rr_list to output
+ * \param[in] output the fd to print to
+ * \param[in] list the rr_list to print
+ */
+void ldns_rr_list_print(FILE *output, const ldns_rr_list *list);
+
+/**
+ * print a rr_list to output
+ * \param[in] output the fd to print to
+ * \param[in] fmt format of the textual representation
+ * \param[in] list the rr_list to print
+ */
+void ldns_rr_list_print_fmt(FILE *output, 
+		const ldns_output_format *fmt, const ldns_rr_list *list);
+
+/**
+ * Print a resolver (in sofar that is possible) state
+ * to output.
+ * \param[in] output the fd to print to
+ * \param[in] r the resolver to print
+ */
+void ldns_resolver_print(FILE *output, const ldns_resolver *r);
+
+/**
+ * Print a resolver (in sofar that is possible) state
+ * to output.
+ * \param[in] output the fd to print to
+ * \param[in] fmt format of the textual representation
+ * \param[in] r the resolver to print
+ */
+void ldns_resolver_print_fmt(FILE *output, 
+		const ldns_output_format *fmt, const ldns_resolver *r);
+
+/**
+ * Print a zone structure * to output. Note the SOA record
+ * is included in this output
+ * \param[in] output the fd to print to
+ * \param[in] z the zone to print
+ */
+void ldns_zone_print(FILE *output, const ldns_zone *z);
+
+/**
+ * Print a zone structure * to output. Note the SOA record
+ * is included in this output
+ * \param[in] output the fd to print to
+ * \param[in] fmt format of the textual representation
+ * \param[in] z the zone to print
+ */
+void ldns_zone_print_fmt(FILE *output, 
+		const ldns_output_format *fmt, const ldns_zone *z);
+
+/**
+ * Print the ldns_rdf containing a dname to the buffer
+ * \param[in] output the buffer to print to
+ * \param[in] dname the dname to print
+ * \return ldns_status message if the printing succeeded
+ */
+ldns_status ldns_rdf2buffer_str_dname(ldns_buffer *output, const ldns_rdf *dname);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LDNS_HOST2STR_H */
diff --git a/3rdParty/Ldns/src/include/ldns/host2wire.h b/3rdParty/Ldns/src/include/ldns/host2wire.h
new file mode 100644
index 0000000..5eafe9d
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/host2wire.h
@@ -0,0 +1,164 @@
+/*
+ * host2wire.h - 2wire conversion routines
+ *
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2005-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+/**
+ * \file
+ *
+ * Contains all functions to translate the main structures to wire format
+ */
+
+#ifndef LDNS_HOST2WIRE_H
+#define LDNS_HOST2WIRE_H
+
+#include <ldns/common.h>
+#include <ldns/error.h>
+#include <ldns/rr.h>
+#include <ldns/rdata.h>
+#include <ldns/packet.h>
+#include <ldns/buffer.h>
+#include <ctype.h>
+
+#include "ldns/util.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Copies the dname data to the buffer in wire format
+ * \param[out] *buffer buffer to append the result to
+ * \param[in] *name rdata dname to convert
+ * \return ldns_status
+ */
+ldns_status ldns_dname2buffer_wire(ldns_buffer *buffer, const ldns_rdf *name);
+
+/**
+ * Copies the rdata data to the buffer in wire format
+ * \param[out] *output buffer to append the result to
+ * \param[in] *rdf rdata to convert
+ * \return ldns_status
+ */
+ldns_status ldns_rdf2buffer_wire(ldns_buffer *output, const ldns_rdf *rdf);
+
+/**
+ * Copies the rdata data to the buffer in wire format
+ * If the rdata is a dname, the letters will be lowercased
+ * during the conversion
+ * \param[out] *output buffer to append the result to
+ * \param[in] *rdf rdata to convert
+ * \return ldns_status
+ */
+ldns_status ldns_rdf2buffer_wire_canonical(ldns_buffer *output,
+								   const ldns_rdf *rdf);
+
+/**
+ * Copies the rr data to the buffer in wire format
+ * \param[out] *output buffer to append the result to
+ * \param[in] *rr resource record to convert
+ * \param[in] section the section in the packet this rr is supposed to be in
+ *            (to determine whether to add rdata or not)
+ * \return ldns_status
+ */
+ldns_status ldns_rr2buffer_wire(ldns_buffer *output,
+						  const ldns_rr *rr,
+						  int section);
+
+/**
+ * Copies the rr data to the buffer in wire format, in canonical format
+ * according to RFC3597 (every dname in rdata fields of RR's mentioned in
+ * that RFC will be lowercased)
+ * \param[out] *output buffer to append the result to
+ * \param[in] *rr resource record to convert
+ * \param[in] section the section in the packet this rr is supposed to be in
+ *            (to determine whether to add rdata or not)
+ * \return ldns_status
+ */
+ldns_status ldns_rr2buffer_wire_canonical(ldns_buffer *output,
+								  const ldns_rr *rr,
+								  int section);
+
+
+/**
+ * Converts a rrsig to wireformat BUT EXCLUDE the rrsig rdata
+ * This is needed in DNSSEC verification
+ * \param[out] output buffer to append the result to
+ * \param[in] sigrr signature rr to operate on
+ * \return ldns_status
+ */
+ldns_status ldns_rrsig2buffer_wire(ldns_buffer *output, const ldns_rr *sigrr);
+
+/**
+ * Converts an rr's rdata to wireformat, while excluding
+ * the ownername and all the stuff before the rdata.
+ * This is needed in DNSSEC keytag calculation, the ds
+ * calcalution from the key and maybe elsewhere.
+ *
+ * \param[out] *output buffer where to put the result
+ * \param[in] *rr rr to operate on
+ * \return ldns_status
+ */
+ldns_status ldns_rr_rdata2buffer_wire(ldns_buffer *output, const ldns_rr *rr);
+
+/**
+ * Copies the packet data to the buffer in wire format
+ * \param[out] *output buffer to append the result to
+ * \param[in] *pkt packet to convert
+ * \return ldns_status
+ */
+ldns_status ldns_pkt2buffer_wire(ldns_buffer *output, const ldns_pkt *pkt);
+
+/**
+ * Copies the rr_list data to the buffer in wire format
+ * \param[out] *output buffer to append the result to
+ * \param[in] *rrlist rr_list to to convert
+ * \return ldns_status
+ */
+ldns_status ldns_rr_list2buffer_wire(ldns_buffer *output, const ldns_rr_list *rrlist);
+
+/**
+ * Allocates an array of uint8_t at dest, and puts the wireformat of the
+ * given rdf in that array. The result_size value contains the
+ * length of the array, if it succeeds, and 0 otherwise (in which case
+ * the function also returns NULL)
+ *
+ * \param[out] dest pointer to the array of bytes to be created
+ * \param[in] rdf the rdata field to convert
+ * \param[out] size the size of the converted result
+ */
+ldns_status ldns_rdf2wire(uint8_t **dest, const ldns_rdf *rdf, size_t *size);
+
+/**
+ * Allocates an array of uint8_t at dest, and puts the wireformat of the
+ * given rr in that array. The result_size value contains the
+ * length of the array, if it succeeds, and 0 otherwise (in which case
+ * the function also returns NULL)
+ *
+ * If the section argument is LDNS_SECTION_QUESTION, data like ttl and rdata
+ * are not put into the result
+ *
+ * \param[out] dest pointer to the array of bytes to be created
+ * \param[in] rr the rr to convert
+ * \param[out] size the size of the converted result
+ */
+ldns_status ldns_rr2wire(uint8_t **dest, const ldns_rr *rr, int, size_t *size);
+
+/**
+ * Allocates an array of uint8_t at dest, and puts the wireformat of the
+ * given packet in that array. The result_size value contains the
+ * length of the array, if it succeeds, and 0 otherwise (in which case
+ * the function also returns NULL)
+ */
+ldns_status ldns_pkt2wire(uint8_t **dest, const ldns_pkt *p, size_t *size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LDNS_HOST2WIRE_H */
diff --git a/3rdParty/Ldns/src/include/ldns/keys.h b/3rdParty/Ldns/src/include/ldns/keys.h
new file mode 100644
index 0000000..ad3ff25
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/keys.h
@@ -0,0 +1,615 @@
+/*
+ * 
+ * keys.h
+ *
+ * priv key definitions
+ *
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2005-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+/**
+ * \file
+ *
+ * Addendum to \ref dnssec.h, this module contains key and algorithm definitions and functions.
+ */
+ 
+
+#ifndef LDNS_KEYS_H
+#define LDNS_KEYS_H
+
+#include <ldns/common.h>
+#if LDNS_BUILD_CONFIG_HAVE_SSL
+#include <openssl/ssl.h>
+#endif /* LDNS_BUILD_CONFIG_HAVE_SSL */
+#include <ldns/dnssec.h>
+#include <ldns/util.h>
+#include <errno.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern ldns_lookup_table ldns_signing_algorithms[];
+
+#define LDNS_KEY_ZONE_KEY 0x0100   /* rfc 4034 */
+#define LDNS_KEY_SEP_KEY 0x0001    /* rfc 4034 */
+#define LDNS_KEY_REVOKE_KEY 0x0080 /* rfc 5011 */
+
+/**
+ * Algorithms used in dns
+ */
+enum ldns_enum_algorithm
+{
+        LDNS_RSAMD5             = 1,   /* RFC 4034,4035 */
+        LDNS_DH                 = 2,
+        LDNS_DSA                = 3,
+        LDNS_ECC                = 4,
+        LDNS_RSASHA1            = 5,
+        LDNS_DSA_NSEC3          = 6,
+        LDNS_RSASHA1_NSEC3      = 7,
+        LDNS_RSASHA256          = 8,   /* RFC 5702 */
+        LDNS_RSASHA512          = 10,  /* RFC 5702 */
+        LDNS_ECC_GOST           = 12,  /* RFC 5933 */
+#if LDNS_BUILD_CONFIG_USE_ECDSA
+	/* this ifdef has to be removed once it is no longer experimental,
+	 * to be able to use these values outside of the ldns library itself */
+        LDNS_ECDSAP256SHA256    = 13,  /* draft-hoffman-dnssec-ecdsa */
+        LDNS_ECDSAP384SHA384    = 14,  /* EXPERIMENTAL */
+#endif
+        LDNS_INDIRECT           = 252,
+        LDNS_PRIVATEDNS         = 253,
+        LDNS_PRIVATEOID         = 254
+};
+typedef enum ldns_enum_algorithm ldns_algorithm;
+
+/**
+ * Hashing algorithms used in the DS record
+ */
+enum ldns_enum_hash
+{
+        LDNS_SHA1               = 1,  /* RFC 4034 */
+        LDNS_SHA256             = 2,  /* RFC 4509 */
+        LDNS_HASH_GOST          = 3   /* RFC 5933 */
+#if LDNS_BUILD_CONFIG_USE_ECDSA
+	/* this ifdef has to be removed once it is no longer experimental,
+	 * to be able to use these values outside of the ldns library itself */
+        ,LDNS_SHA384             = 4   /* draft-hoffman-dnssec-ecdsa EXPERIMENTAL */
+#endif
+};
+typedef enum ldns_enum_hash ldns_hash;
+
+/**
+ * Algorithms used in dns for signing
+ */
+enum ldns_enum_signing_algorithm
+{
+	LDNS_SIGN_RSAMD5	 = LDNS_RSAMD5,
+	LDNS_SIGN_RSASHA1	 = LDNS_RSASHA1,
+	LDNS_SIGN_DSA		 = LDNS_DSA,
+	LDNS_SIGN_RSASHA1_NSEC3  = LDNS_RSASHA1_NSEC3,
+	LDNS_SIGN_RSASHA256	 = LDNS_RSASHA256,
+	LDNS_SIGN_RSASHA512	 = LDNS_RSASHA512,
+	LDNS_SIGN_DSA_NSEC3	 = LDNS_DSA_NSEC3,
+	LDNS_SIGN_ECC_GOST       = LDNS_ECC_GOST,
+#if LDNS_BUILD_CONFIG_USE_ECDSA
+	/* this ifdef has to be removed once it is no longer experimental,
+	 * to be able to use these values outside of the ldns library itself */
+        LDNS_SIGN_ECDSAP256SHA256 = LDNS_ECDSAP256SHA256,
+        LDNS_SIGN_ECDSAP384SHA384 = LDNS_ECDSAP384SHA384,
+#endif
+	LDNS_SIGN_HMACMD5	 = 157,	/* not official! This type is for TSIG, not DNSSEC */
+	LDNS_SIGN_HMACSHA1	 = 158,	/* not official! This type is for TSIG, not DNSSEC */
+	LDNS_SIGN_HMACSHA256 = 159  /* ditto */
+};
+typedef enum ldns_enum_signing_algorithm ldns_signing_algorithm;
+
+/**
+ * General key structure, can contain all types of keys that
+ * are used in DNSSEC. Mostly used to store private keys, since
+ * public keys can also be stored in a \ref ldns_rr with type
+ * \ref LDNS_RR_TYPE_DNSKEY.
+ *
+ * This structure can also store some variables that influence the
+ * signatures generated by signing with this key, for instance the
+ * inception date.
+ */
+struct ldns_struct_key {
+	ldns_signing_algorithm _alg;
+	/** Whether to use this key when signing */
+	bool _use;
+	/** Storage pointers for the types of keys supported */
+	/* TODO remove unions? */
+	struct {
+#if LDNS_BUILD_CONFIG_HAVE_SSL
+#ifndef S_SPLINT_S
+		/* The key can be an OpenSSL EVP Key
+		 */
+		EVP_PKEY *key;
+#endif
+#endif /* LDNS_BUILD_CONFIG_HAVE_SSL */
+		/**
+		 * The key can be an HMAC key
+		 */
+		struct {
+			unsigned char *key;
+			size_t size;
+		} hmac;
+		/** the key structure can also just point to some external
+		 *  key data
+		 */
+		void *external_key;
+	} _key;
+	/** Depending on the key we can have extra data */
+	union {
+                /** Some values that influence generated signatures */
+		struct {
+			/** The TTL of the rrset that is currently signed */
+			uint32_t orig_ttl;
+			/** The inception date of signatures made with this key. */
+			uint32_t inception;
+			/** The expiration date of signatures made with this key. */
+			uint32_t expiration;
+			/** The keytag of this key. */
+			uint16_t keytag;
+			/** The dnssec key flags as specified in RFC4035, like ZSK and KSK */
+			uint16_t flags;
+		}  dnssec;
+	} _extra;
+	/** Owner name of the key */
+	ldns_rdf *_pubkey_owner;
+};
+typedef struct ldns_struct_key ldns_key;
+
+/**
+ * Same as rr_list, but now for keys 
+ */
+struct ldns_struct_key_list
+{
+	size_t _key_count;
+	ldns_key **_keys;
+};
+typedef struct ldns_struct_key_list ldns_key_list;
+
+
+/**
+ * Creates a new empty key list
+ * \return a new ldns_key_list structure pointer
+ */
+ldns_key_list *ldns_key_list_new();
+
+/** 
+ * Creates a new empty key structure
+ * \return a new ldns_key * structure
+ */
+ldns_key *ldns_key_new();
+
+/**
+ * Creates a new key based on the algorithm
+ *
+ * \param[in] a The algorithm to use
+ * \param[in] size the number of bytes for the keysize
+ * \return a new ldns_key structure with the key
+ */
+ldns_key *ldns_key_new_frm_algorithm(ldns_signing_algorithm a, uint16_t size);
+
+/**
+ * Creates a new priv key based on the 
+ * contents of the file pointed by fp.
+ *
+ * The file should be in Private-key-format v1.x.
+ *
+ * \param[out] k the new ldns_key structure
+ * \param[in] fp the file pointer to use
+ * \return an error or LDNS_STATUS_OK
+ */
+ldns_status ldns_key_new_frm_fp(ldns_key **k, FILE *fp);
+
+/**
+ * Creates a new private key based on the 
+ * contents of the file pointed by fp
+ *
+ * The file should be in Private-key-format v1.x.
+ *
+ * \param[out] k the new ldns_key structure
+ * \param[in] fp the file pointer to use
+ * \param[in] line_nr pointer to an integer containing the current line number (for debugging purposes)
+ * \return an error or LDNS_STATUS_OK
+ */
+ldns_status ldns_key_new_frm_fp_l(ldns_key **k, FILE *fp, int *line_nr);
+
+#if LDNS_BUILD_CONFIG_HAVE_SSL
+/**
+ * Read the key with the given id from the given engine and store it
+ * in the given ldns_key structure. The algorithm type is set
+ */
+ldns_status ldns_key_new_frm_engine(ldns_key **key, ENGINE *e, char *key_id, ldns_algorithm);
+
+
+/**
+ * frm_fp helper function. This function parses the
+ * remainder of the (RSA) priv. key file generated from bind9
+ * \param[in] fp the file to parse
+ * \return NULL on failure otherwise a RSA structure
+ */
+RSA *ldns_key_new_frm_fp_rsa(FILE *fp);
+#endif /* LDNS_BUILD_CONFIG_HAVE_SSL */
+
+#if LDNS_BUILD_CONFIG_HAVE_SSL
+/**
+ * frm_fp helper function. This function parses the
+ * remainder of the (RSA) priv. key file generated from bind9
+ * \param[in] fp the file to parse
+ * \param[in] line_nr pointer to an integer containing the current line number (for debugging purposes)
+ * \return NULL on failure otherwise a RSA structure
+ */
+RSA *ldns_key_new_frm_fp_rsa_l(FILE *fp, int *line_nr);
+#endif /* LDNS_BUILD_CONFIG_HAVE_SSL */
+
+#if LDNS_BUILD_CONFIG_HAVE_SSL
+/**
+ * frm_fp helper function. This function parses the
+ * remainder of the (DSA) priv. key file
+ * \param[in] fp the file to parse
+ * \return NULL on failure otherwise a RSA structure
+ */
+DSA *ldns_key_new_frm_fp_dsa(FILE *fp);
+#endif /* LDNS_BUILD_CONFIG_HAVE_SSL */
+
+#if LDNS_BUILD_CONFIG_HAVE_SSL
+/**
+ * frm_fp helper function. This function parses the
+ * remainder of the (DSA) priv. key file
+ * \param[in] fp the file to parse
+ * \param[in] line_nr pointer to an integer containing the current line number (for debugging purposes)
+ * \return NULL on failure otherwise a RSA structure
+ */
+DSA *ldns_key_new_frm_fp_dsa_l(FILE *fp, int *line_nr);
+#endif /* LDNS_BUILD_CONFIG_HAVE_SSL */
+
+#if LDNS_BUILD_CONFIG_HAVE_SSL
+/**
+ * frm_fp helper function. This function parses the
+ * remainder of the (HMAC-MD5) key file
+ * This function allocated a buffer that needs to be freed
+ * \param[in] fp the file to parse
+ * \param[out] hmac_size the number of bits in the resulting buffer
+ * \return NULL on failure otherwise a newly allocated char buffer
+ */
+unsigned char *ldns_key_new_frm_fp_hmac(FILE *fp, size_t *hmac_size);
+#endif
+
+#if LDNS_BUILD_CONFIG_HAVE_SSL
+/**
+ * frm_fp helper function. This function parses the
+ * remainder of the (HMAC-MD5) key file
+ * This function allocated a buffer that needs to be freed
+ * \param[in] fp the file to parse
+ * \param[in] line_nr pointer to an integer containing the current line number (for error reporting purposes)
+ * \param[out] hmac_size the number of bits in the resulting buffer
+ * \return NULL on failure otherwise a newly allocated char buffer
+ */
+unsigned char *ldns_key_new_frm_fp_hmac_l(FILE *fp, int *line_nr, size_t *hmac_size);
+#endif /* LDNS_BUILD_CONFIG_HAVE_SSL */
+
+/* acces write functions */
+/**
+ * Set the key's algorithm
+ * \param[in] k the key
+ * \param[in] l the algorithm
+ */
+void ldns_key_set_algorithm(ldns_key *k, ldns_signing_algorithm l);
+#if LDNS_BUILD_CONFIG_HAVE_SSL
+/**
+ * Set the key's evp key
+ * \param[in] k the key
+ * \param[in] e the evp key
+ */
+void ldns_key_set_evp_key(ldns_key *k, EVP_PKEY *e);
+
+/**
+ * Set the key's rsa data
+ * \param[in] k the key
+ * \param[in] r the rsa data
+ */
+void ldns_key_set_rsa_key(ldns_key *k, RSA *r);
+/**
+ * Set the key's dsa data
+ * \param[in] k the key
+ * \param[in] d the dsa data
+ */
+void ldns_key_set_dsa_key(ldns_key *k, DSA *d);
+
+/** 
+ * Get the PKEY id for GOST, loads GOST into openssl as a side effect.
+ * Only available if GOST is compiled into the library and openssl.
+ * \return the gost id for EVP_CTX creation.
+ */
+int ldns_key_EVP_load_gost_id(void);
+
+/** Release the engine reference held for the GOST engine. */
+void ldns_key_EVP_unload_gost(void);
+#endif /* LDNS_BUILD_CONFIG_HAVE_SSL */
+
+/**
+ * Set the key's hmac data
+ * \param[in] k the key
+ * \param[in] hmac the raw key data
+ */
+void ldns_key_set_hmac_key(ldns_key *k, unsigned char *hmac);
+
+/**
+ * Set the key id data. This is used if the key points to
+ * some externally stored key data
+ * 
+ * Only the pointer is set, the data there is not copied,
+ * and must be freed manually; ldns_key_deep_free() does 
+ * *not* free this data
+ * \param[in] key the key
+ * \param[in] external_key key id data
+ */
+void ldns_key_set_external_key(ldns_key *key, void *external_key);
+
+/**
+ * Set the key's hmac size
+ * \param[in] k the key
+ * \param[in] hmac_size the size of the hmac data
+ */
+void ldns_key_set_hmac_size(ldns_key *k, size_t hmac_size);
+/**
+ * Set the key's original ttl
+ * \param[in] k the key
+ * \param[in] t the ttl
+ */
+void ldns_key_set_origttl(ldns_key *k, uint32_t t);
+/**
+ * Set the key's inception date (seconds after epoch)
+ * \param[in] k the key
+ * \param[in] i the inception
+ */
+void ldns_key_set_inception(ldns_key *k, uint32_t i);
+/**
+ * Set the key's expiration date (seconds after epoch)
+ * \param[in] k the key
+ * \param[in] e the expiration
+ */
+void ldns_key_set_expiration(ldns_key *k, uint32_t e);
+/**
+ * Set the key's pubkey owner
+ * \param[in] k the key
+ * \param[in] r the owner
+ */
+void ldns_key_set_pubkey_owner(ldns_key *k, ldns_rdf *r);
+/**
+ * Set the key's key tag
+ * \param[in] k the key
+ * \param[in] tag the keytag
+ */
+void ldns_key_set_keytag(ldns_key *k, uint16_t tag);
+/**
+ * Set the key's flags
+ * \param[in] k the key
+ * \param[in] flags the flags
+ */
+void ldns_key_set_flags(ldns_key *k, uint16_t flags);
+/**
+ * Set the keylist's key count to count
+ * \param[in] key the key
+ * \param[in] count the cuont
+ */
+void ldns_key_list_set_key_count(ldns_key_list *key, size_t count);
+
+/**     
+ * pushes a key to a keylist
+ * \param[in] key_list the key_list to push to 
+ * \param[in] key the key to push 
+ * \return false on error, otherwise true
+ */      
+bool ldns_key_list_push_key(ldns_key_list *key_list, ldns_key *key);
+
+/**
+ * returns the number of keys in the key list
+ * \param[in] key_list the key_list
+ * \return the numbers of keys in the list
+ */
+size_t ldns_key_list_key_count(const ldns_key_list *key_list);
+
+/**
+ * returns a pointer to the key in the list at the given position
+ * \param[in] key the key
+ * \param[in] nr the position in the list
+ * \return the key
+ */
+ldns_key *ldns_key_list_key(const ldns_key_list *key, size_t nr);
+
+#if LDNS_BUILD_CONFIG_HAVE_SSL
+/**
+ * returns the (openssl) RSA struct contained in the key
+ * \param[in] k the key to look in
+ * \return the RSA * structure in the key
+ */
+RSA *ldns_key_rsa_key(const ldns_key *k);
+/**
+ * returns the (openssl) EVP struct contained in the key
+ * \param[in] k the key to look in
+ * \return the RSA * structure in the key
+ */
+EVP_PKEY *ldns_key_evp_key(const ldns_key *k);
+#endif /* LDNS_BUILD_CONFIG_HAVE_SSL */
+
+/**
+ * returns the (openssl) DSA struct contained in the key
+ */
+#if LDNS_BUILD_CONFIG_HAVE_SSL
+DSA *ldns_key_dsa_key(const ldns_key *k);
+#endif /* LDNS_BUILD_CONFIG_HAVE_SSL */
+
+/**
+ * return the signing alg of the key
+ * \param[in] k the key
+ * \return the algorithm
+ */
+ldns_signing_algorithm ldns_key_algorithm(const ldns_key *k);
+/**
+ * set the use flag
+ * \param[in] k the key
+ * \param[in] v the boolean value to set the _use field to
+ */
+void ldns_key_set_use(ldns_key *k, bool v);
+/**
+ * return the use flag
+ * \param[in] k the key
+ * \return the boolean value of the _use field
+ */
+bool ldns_key_use(const ldns_key *k);
+/**
+ * return the hmac key data
+ * \param[in] k the key
+ * \return the hmac key data
+ */
+unsigned char *ldns_key_hmac_key(const ldns_key *k);
+/**
+ * return the key id key data
+ * \param[in] k the key
+ * \return the key id data
+ */
+void *ldns_key_external_key(const ldns_key *k);
+/**
+ * return the hmac key size
+ * \param[in] k the key
+ * \return the hmac key size
+ */
+size_t ldns_key_hmac_size(const ldns_key *k);
+/**
+ * return the original ttl of the key
+ * \param[in] k the key
+ * \return the original ttl
+ */
+uint32_t ldns_key_origttl(const ldns_key *k);
+/**
+ * return the key's inception date
+ * \param[in] k the key
+ * \return the inception date
+ */
+uint32_t ldns_key_inception(const ldns_key *k);
+/**
+ * return the key's expiration date
+ * \param[in] k the key
+ * \return the experiration date
+ */
+uint32_t ldns_key_expiration(const ldns_key *k);
+/**
+ * return the keytag
+ * \param[in] k the key
+ * \return the keytag
+ */
+uint16_t ldns_key_keytag(const ldns_key *k);
+/**
+ * return the public key's owner
+ * \param[in] k the key
+ * \return the owner
+ */
+ldns_rdf *ldns_key_pubkey_owner(const ldns_key *k);
+/**
+ * Set the 'use' flag for all keys in the list
+ * \param[in] keys The key_list
+ * \param[in] v The value to set the use flags to
+ */
+void
+ldns_key_list_set_use(ldns_key_list *keys, bool v);
+
+/**
+ * return the flag of the key
+ * \param[in] k the key
+ * \return the flag
+ */
+uint16_t ldns_key_flags(const ldns_key *k);
+
+/**     
+ * pops the last rr from a keylist
+ * \param[in] key_list the rr_list to pop from
+ * \return NULL if nothing to pop. Otherwise the popped RR
+ */
+ldns_key *ldns_key_list_pop_key(ldns_key_list *key_list);
+
+/** 
+ * converts a ldns_key to a public key rr
+ * If the key data exists at an external point, the corresponding
+ * rdata field must still be added with ldns_rr_rdf_push() to the
+ * result rr of this function
+ *
+ * \param[in] k the ldns_key to convert
+ * \return ldns_rr representation of the key
+ */
+ldns_rr *ldns_key2rr(const ldns_key *k);
+
+/**
+ * print a private key to the file ouput
+ * 
+ * \param[in] output the FILE descriptor where to print to
+ * \param[in] k the ldns_key to print
+ */
+void ldns_key_print(FILE *output, const ldns_key *k);
+
+/**
+ * frees a key structure, but not its internal data structures
+ *
+ * \param[in] key the key object to free
+ */
+void ldns_key_free(ldns_key *key);
+
+/**
+ * frees a key structure and all its internal data structures, except
+ * the data set by ldns_key_set_external_key()
+ *
+ * \param[in] key the key object to free
+ */
+void ldns_key_deep_free(ldns_key *key);
+
+/**
+ * Frees a key list structure
+ * \param[in] key_list the key list object to free
+ */
+void ldns_key_list_free(ldns_key_list *key_list);
+
+/**
+ * Instantiates a DNSKEY or DS RR from file.
+ * \param[in] filename the file to read the record from
+ * \return the corresponding RR, or NULL if the parsing failed
+ */
+ldns_rr * ldns_read_anchor_file(const char *filename);
+
+/**
+ * Returns the 'default base name' for key files;
+ * IE. K\<zone\>+\<alg\>+\<keytag\>
+ * (without the .key or .private)
+ * The memory for this is allocated by this function,
+ * and should be freed by the caller
+ * 
+ * \param[in] key the key to get the file name from
+ * \returns A string containing the file base name
+ */
+char *ldns_key_get_file_base_name(ldns_key *key);
+
+/**
+ * See if a key algorithm is supported
+ * \param[in] algo the signing algorithm number.
+ * \returns true if supported.
+ */
+int ldns_key_algo_supported(int algo);
+
+/**
+ * Get signing algorithm by name.  Comparison is case insensitive.
+ * \param[in] name string with the name.
+ * \returns 0 on parse failure or the algorithm number.
+ */
+ldns_signing_algorithm ldns_get_signing_algorithm_by_name(const char* name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LDNS_KEYS_H */
diff --git a/3rdParty/Ldns/src/include/ldns/ldns.h b/3rdParty/Ldns/src/include/ldns/ldns.h
new file mode 100644
index 0000000..6f57733
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/ldns.h
@@ -0,0 +1,155 @@
+/*
+ * dns.h -- defines for the Domain Name System
+ *
+ * Copyright (c) 2005-2008, NLnet Labs. All rights reserved.
+ *
+ * See LICENSE for the license.
+ *
+ * This library was created by:
+ * Jelte Jansen, Erik Rozendaal and Miek Gieben
+ *
+ * A bunch of defines that are used in the DNS.
+ */
+
+
+/**
+\mainpage LDNS Documentation
+
+\section introduction Introduction
+
+The goal of ldns is to simplify DNS programming, it supports recent RFCs
+like the DNSSEC documents, and allow developers to easily create software
+conforming to current RFCs, and experimental software for current Internet
+drafts. A secondary benefit of using ldns is speed, because ldns is written
+in C, and although it is not optimized for performance, it should be a lot
+faster than Perl.
+
+The first main tool to use ldns is Drill, from which part of the library was
+derived. From version 1.0.0 on, drill is included in the ldns release
+and will not be distributed seperately anymore. The library also includes some
+other examples and tools to show how it can be used. These can be found in the
+examples/ directory in the tarball.
+
+ldns depends on OpenSSL for it's cryptographic functions.
+Feature list
+
+  - Transparent IPv4 and IPv6 support (overridable if necessary),
+  - TSIG support,
+  - DNSSEC support; signing and verification,
+  - small size,
+  - online documentation as well as manual pages. 
+
+If you want to send us patches please use the code from subversion (trunk). 
+
+\section using_ldns Using ldns
+
+Almost all interaction between an application and ldns goes through the ldns
+data structures (\ref ldns_rr, \ref ldns_pkt, etc.). These are input or
+output to the functions of ldns. For example, \ref ldns_zone_new_frm_fp
+reads a zone from a \c FILE pointer, and returns an \ref ldns_zone
+structure.
+
+
+Let's use Drill as an example. Drill is a tool much like dig, whose most
+basic function is to send 1 query to a nameserver and print the response.
+
+To be able to do this, drill uses the resolver module of ldns, which acts as
+a stub resolver. The resolver module uses the net module to actually send
+the query that drill requested. It then uses the wire2host module to
+translate the response and place it in ldns' internal structures. These are
+passed back to drill, which then uses the host2str module to print the
+response in presentation format.
+
+\section gettingstarted Getting Started
+
+See the \ref design page for a very high level description of the design
+choices made for ldns. 
+
+For an overview of the functions and types ldns provides, you can check out
+the \ref ldns ldns header file descriptions.
+
+If you want to see some libdns action, you can read our tutorials:
+  - \ref tutorial1_mx
+  - \ref tutorial2_zone
+  - \ref tutorial3_signzone
+
+Or you can just use the menu above to browse through the API docs.
+
+<div style="visibility:hidden;">
+\image html LogoInGradientBar2-y100.png
+</div>
+*/
+
+/**
+ * \file ldns.h
+ *
+ * Including this file will include all ldns files, and define some lookup tables.
+ */
+
+#ifndef LDNS_DNS_H
+#define LDNS_DNS_H
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <ldns/util.h>
+#include <ldns/buffer.h>
+#include <ldns/common.h>
+#include <ldns/dname.h>
+#include <ldns/dnssec.h>
+#include <ldns/dnssec_verify.h>
+#include <ldns/dnssec_sign.h>
+#include <ldns/error.h>
+#include <ldns/higher.h>
+#include <ldns/host2str.h>
+#include <ldns/host2wire.h>
+#include <ldns/net.h>
+#include <ldns/packet.h>
+#include <ldns/rdata.h>
+#include <ldns/resolver.h>
+#include <ldns/rr.h>
+#include <ldns/str2host.h>
+#include <ldns/tsig.h>
+#include <ldns/update.h>
+#include <ldns/wire2host.h>
+#include <ldns/rr_functions.h>
+#include <ldns/keys.h>
+#include <ldns/parse.h>
+#include <ldns/zone.h>
+#include <ldns/dnssec_zone.h>
+#include <ldns/rbtree.h>
+#include <ldns/sha1.h>
+#include <ldns/sha2.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LDNS_IP4ADDRLEN      (32/8)
+#define LDNS_IP6ADDRLEN      (128/8)
+#define LDNS_PORT	53
+#define LDNS_ROOT_LABEL_STR     "."
+#define LDNS_DEFAULT_TTL	3600
+
+/* lookup tables for standard DNS stuff  */
+
+/** Taken from RFC 2538, section 2.1.  */
+extern ldns_lookup_table ldns_certificate_types[];
+/** Taken from RFC 2535, section 7.  */
+extern ldns_lookup_table ldns_algorithms[];
+/** Taken from RFC 2538.  */
+extern ldns_lookup_table ldns_cert_algorithms[];
+/** rr types  */
+extern ldns_lookup_table ldns_rr_classes[];
+/** Response codes */
+extern ldns_lookup_table ldns_rcodes[];
+/** Operation codes */
+extern ldns_lookup_table ldns_opcodes[];
+/** EDNS flags */
+extern ldns_lookup_table ldns_edns_flags[];
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LDNS_DNS_H */
diff --git a/3rdParty/Ldns/src/include/ldns/net.h b/3rdParty/Ldns/src/include/ldns/net.h
new file mode 100644
index 0000000..cd7bc4b
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/net.h
@@ -0,0 +1,208 @@
+/*
+ * net.h
+ *
+ * DNS Resolver definitions
+ *
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2005-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+#ifndef LDNS_NET_H
+#define LDNS_NET_H
+
+#include <ldns/ldns.h>
+#include <sys/socket.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LDNS_DEFAULT_TIMEOUT_SEC 5
+#define LDNS_DEFAULT_TIMEOUT_USEC 0
+
+/**
+ * \file
+ *
+ * Contains functions to send and receive packets over a network.
+ */
+
+/**
+ * Sends a buffer to an ip using udp and return the respons as a ldns_pkt
+ * \param[in] qbin the ldns_buffer to be send
+ * \param[in] to the ip addr to send to
+ * \param[in] tolen length of the ip addr
+ * \param[in] timeout the timeout value for the network
+ * \param[out] answersize size of the packet
+ * \param[out] result packet with the answer
+ * \return status
+ */
+ldns_status ldns_udp_send(uint8_t **result, ldns_buffer *qbin, const struct sockaddr_storage *to, socklen_t tolen, struct timeval timeout, size_t *answersize);
+
+/**
+ * Send an udp query and don't wait for an answer but return
+ * the socket
+ * \param[in] qbin the ldns_buffer to be send
+ * \param[in] to the ip addr to send to
+ * \param[in] tolen length of the ip addr
+ * \param[in] timeout *unused*, was the timeout value for the network
+ * \return the socket used
+ */
+
+int ldns_udp_bgsend(ldns_buffer *qbin, const struct sockaddr_storage *to, socklen_t tolen, struct timeval timeout);
+
+/**
+ * Send an tcp query and don't wait for an answer but return
+ * the socket
+ * \param[in] qbin the ldns_buffer to be send
+ * \param[in] to the ip addr to send to
+ * \param[in] tolen length of the ip addr
+ * \param[in] timeout the timeout value for the connect attempt
+ * \return the socket used
+ */
+int ldns_tcp_bgsend(ldns_buffer *qbin, const struct sockaddr_storage *to, socklen_t tolen, struct timeval timeout);
+
+/**
+ * Sends a buffer to an ip using tcp and return the respons as a ldns_pkt
+ * \param[in] qbin the ldns_buffer to be send
+ * \param[in] qbin the ldns_buffer to be send
+ * \param[in] to the ip addr to send to
+ * \param[in] tolen length of the ip addr
+ * \param[in] timeout the timeout value for the network
+ * \param[out] answersize size of the packet
+ * \param[out] result packet with the answer
+ * \return status
+ */
+ldns_status ldns_tcp_send(uint8_t **result, ldns_buffer *qbin, const struct sockaddr_storage *to, socklen_t tolen, struct timeval timeout, size_t *answersize);
+
+/**
+ * Sends ptk to the nameserver at the resolver object. Returns the data
+ * as a ldns_pkt
+ * 
+ * \param[out] pkt packet received from the nameserver
+ * \param[in] r the resolver to use 
+ * \param[in] query_pkt the query to send
+ * \return status
+ */
+ldns_status ldns_send(ldns_pkt **pkt, ldns_resolver *r, const ldns_pkt *query_pkt);
+
+/**
+ * Sends and ldns_buffer (presumably containing a packet to the nameserver at the resolver object. Returns the data
+ * as a ldns_pkt
+ * 
+ * \param[out] pkt packet received from the nameserver
+ * \param[in] r the resolver to use 
+ * \param[in] qb the buffer to send
+ * \param[in] tsig_mac the tsig MAC to authenticate the response with (NULL to do no TSIG authentication)
+ * \return status
+ */
+ldns_status ldns_send_buffer(ldns_pkt **pkt, ldns_resolver *r, ldns_buffer *qb, ldns_rdf *tsig_mac);
+
+/**
+ * Create a tcp socket to the specified address
+ * \param[in] to ip and family
+ * \param[in] tolen length of to
+ * \param[in] timeout timeout for the connect attempt
+ * \return a socket descriptor
+ */
+int ldns_tcp_connect(const struct sockaddr_storage *to, socklen_t tolen, struct timeval timeout);
+
+/**
+ * Create a udp socket to the specified address
+ * \param[in] to ip and family
+ * \param[in] timeout *unused*, was timeout for the socket
+ * \return a socket descriptor
+ */
+int ldns_udp_connect(const struct sockaddr_storage *to, struct timeval timeout);
+
+/**
+ * send a query via tcp to a server. Don't want for the answer
+ *
+ * \param[in] qbin the buffer to send
+ * \param[in] sockfd the socket to use
+ * \param[in] to which ip to send it
+ * \param[in] tolen socketlen
+ * \return number of bytes sent
+ */
+ssize_t ldns_tcp_send_query(ldns_buffer *qbin, int sockfd, const struct sockaddr_storage *to, socklen_t tolen);
+
+/**
+ * send a query via udp to a server. Don;t want for the answer
+ *
+ * \param[in] qbin the buffer to send
+ * \param[in] sockfd the socket to use
+ * \param[in] to which ip to send it
+ * \param[in] tolen socketlen
+ * \return number of bytes sent
+ */
+ssize_t ldns_udp_send_query(ldns_buffer *qbin, int sockfd, const struct sockaddr_storage *to, socklen_t tolen);
+
+/**
+ * Gives back a raw packet from the wire and reads the header data from the given
+ * socket. Allocates the data (of size size) itself, so don't forget to free
+ *
+ * \param[in] sockfd the socket to read from
+ * \param[out] size the number of bytes that are read
+ * \param[in] timeout the time allowed between packets.
+ * \return the data read
+ */
+uint8_t *ldns_tcp_read_wire_timeout(int sockfd, size_t *size, struct timeval timeout);
+
+/**
+ * This routine may block. Use ldns_tcp_read_wire_timeout, it checks timeouts.
+ * Gives back a raw packet from the wire and reads the header data from the given
+ * socket. Allocates the data (of size size) itself, so don't forget to free
+ *
+ * \param[in] sockfd the socket to read from
+ * \param[out] size the number of bytes that are read
+ * \return the data read
+ */
+uint8_t *ldns_tcp_read_wire(int sockfd, size_t *size);
+
+/**
+ * Gives back a raw packet from the wire and reads the header data from the given
+ * socket. Allocates the data (of size size) itself, so don't forget to free
+ *
+ * \param[in] sockfd the socket to read from
+ * \param[in] fr the address of the client (if applicable)
+ * \param[in] *frlen the lenght of the client's addr (if applicable)
+ * \param[out] size the number of bytes that are read
+ * \return the data read
+ */
+uint8_t *ldns_udp_read_wire(int sockfd, size_t *size, struct sockaddr_storage *fr, socklen_t *frlen);
+
+/**
+ * returns the native sockaddr representation from the rdf.
+ * \param[in] rd the ldns_rdf to operate on
+ * \param[in] port what port to use. 0 means; use default (53)
+ * \param[out] size what is the size of the sockaddr_storage
+ * \return struct sockaddr* the address in the format so other
+ * functions can use it (sendto)
+ */
+struct sockaddr_storage * ldns_rdf2native_sockaddr_storage(const ldns_rdf *rd, uint16_t port, size_t *size);
+
+/**
+ * returns an rdf with the sockaddr info. works for ip4 and ip6
+ * \param[in] sock the struct sockaddr_storage to convert
+ * \param[in] port what port was used. When NULL this is not set
+ * \return ldns_rdf* wth the address
+ */
+ldns_rdf * ldns_sockaddr_storage2rdf(struct sockaddr_storage *sock, uint16_t *port);
+
+/**
+ * Prepares the resolver for an axfr query
+ * The query is sent and the answers can be read with ldns_axfr_next
+ * \param[in] resolver the resolver to use
+ * \param[in] domain the domain to exfr
+ * \param[in] c the class to use
+ * \return ldns_status the status of the transfer
+ */
+ldns_status ldns_axfr_start(ldns_resolver *resolver, ldns_rdf *domain, ldns_rr_class c);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* LDNS_NET_H */
diff --git a/3rdParty/Ldns/src/include/ldns/net.h.in b/3rdParty/Ldns/src/include/ldns/net.h.in
new file mode 100644
index 0000000..cd4cfde
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/net.h.in
@@ -0,0 +1,208 @@
+/*
+ * net.h
+ *
+ * DNS Resolver definitions
+ *
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2005-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+#ifndef LDNS_NET_H
+#define LDNS_NET_H
+
+#include <ldns/ldns.h>
+@include_sys_socket_h@
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LDNS_DEFAULT_TIMEOUT_SEC 5
+#define LDNS_DEFAULT_TIMEOUT_USEC 0
+
+/**
+ * \file
+ *
+ * Contains functions to send and receive packets over a network.
+ */
+
+/**
+ * Sends a buffer to an ip using udp and return the respons as a ldns_pkt
+ * \param[in] qbin the ldns_buffer to be send
+ * \param[in] to the ip addr to send to
+ * \param[in] tolen length of the ip addr
+ * \param[in] timeout the timeout value for the network
+ * \param[out] answersize size of the packet
+ * \param[out] result packet with the answer
+ * \return status
+ */
+ldns_status ldns_udp_send(uint8_t **result, ldns_buffer *qbin, const struct sockaddr_storage *to, socklen_t tolen, struct timeval timeout, size_t *answersize);
+
+/**
+ * Send an udp query and don't wait for an answer but return
+ * the socket
+ * \param[in] qbin the ldns_buffer to be send
+ * \param[in] to the ip addr to send to
+ * \param[in] tolen length of the ip addr
+ * \param[in] timeout *unused*, was the timeout value for the network
+ * \return the socket used
+ */
+
+int ldns_udp_bgsend(ldns_buffer *qbin, const struct sockaddr_storage *to, socklen_t tolen, struct timeval timeout);
+
+/**
+ * Send an tcp query and don't wait for an answer but return
+ * the socket
+ * \param[in] qbin the ldns_buffer to be send
+ * \param[in] to the ip addr to send to
+ * \param[in] tolen length of the ip addr
+ * \param[in] timeout the timeout value for the connect attempt
+ * \return the socket used
+ */
+int ldns_tcp_bgsend(ldns_buffer *qbin, const struct sockaddr_storage *to, socklen_t tolen, struct timeval timeout);
+
+/**
+ * Sends a buffer to an ip using tcp and return the respons as a ldns_pkt
+ * \param[in] qbin the ldns_buffer to be send
+ * \param[in] qbin the ldns_buffer to be send
+ * \param[in] to the ip addr to send to
+ * \param[in] tolen length of the ip addr
+ * \param[in] timeout the timeout value for the network
+ * \param[out] answersize size of the packet
+ * \param[out] result packet with the answer
+ * \return status
+ */
+ldns_status ldns_tcp_send(uint8_t **result, ldns_buffer *qbin, const struct sockaddr_storage *to, socklen_t tolen, struct timeval timeout, size_t *answersize);
+
+/**
+ * Sends ptk to the nameserver at the resolver object. Returns the data
+ * as a ldns_pkt
+ * 
+ * \param[out] pkt packet received from the nameserver
+ * \param[in] r the resolver to use 
+ * \param[in] query_pkt the query to send
+ * \return status
+ */
+ldns_status ldns_send(ldns_pkt **pkt, ldns_resolver *r, const ldns_pkt *query_pkt);
+
+/**
+ * Sends and ldns_buffer (presumably containing a packet to the nameserver at the resolver object. Returns the data
+ * as a ldns_pkt
+ * 
+ * \param[out] pkt packet received from the nameserver
+ * \param[in] r the resolver to use 
+ * \param[in] qb the buffer to send
+ * \param[in] tsig_mac the tsig MAC to authenticate the response with (NULL to do no TSIG authentication)
+ * \return status
+ */
+ldns_status ldns_send_buffer(ldns_pkt **pkt, ldns_resolver *r, ldns_buffer *qb, ldns_rdf *tsig_mac);
+
+/**
+ * Create a tcp socket to the specified address
+ * \param[in] to ip and family
+ * \param[in] tolen length of to
+ * \param[in] timeout timeout for the connect attempt
+ * \return a socket descriptor
+ */
+int ldns_tcp_connect(const struct sockaddr_storage *to, socklen_t tolen, struct timeval timeout);
+
+/**
+ * Create a udp socket to the specified address
+ * \param[in] to ip and family
+ * \param[in] timeout *unused*, was timeout for the socket
+ * \return a socket descriptor
+ */
+int ldns_udp_connect(const struct sockaddr_storage *to, struct timeval timeout);
+
+/**
+ * send a query via tcp to a server. Don't want for the answer
+ *
+ * \param[in] qbin the buffer to send
+ * \param[in] sockfd the socket to use
+ * \param[in] to which ip to send it
+ * \param[in] tolen socketlen
+ * \return number of bytes sent
+ */
+ssize_t ldns_tcp_send_query(ldns_buffer *qbin, int sockfd, const struct sockaddr_storage *to, socklen_t tolen);
+
+/**
+ * send a query via udp to a server. Don;t want for the answer
+ *
+ * \param[in] qbin the buffer to send
+ * \param[in] sockfd the socket to use
+ * \param[in] to which ip to send it
+ * \param[in] tolen socketlen
+ * \return number of bytes sent
+ */
+ssize_t ldns_udp_send_query(ldns_buffer *qbin, int sockfd, const struct sockaddr_storage *to, socklen_t tolen);
+
+/**
+ * Gives back a raw packet from the wire and reads the header data from the given
+ * socket. Allocates the data (of size size) itself, so don't forget to free
+ *
+ * \param[in] sockfd the socket to read from
+ * \param[out] size the number of bytes that are read
+ * \param[in] timeout the time allowed between packets.
+ * \return the data read
+ */
+uint8_t *ldns_tcp_read_wire_timeout(int sockfd, size_t *size, struct timeval timeout);
+
+/**
+ * This routine may block. Use ldns_tcp_read_wire_timeout, it checks timeouts.
+ * Gives back a raw packet from the wire and reads the header data from the given
+ * socket. Allocates the data (of size size) itself, so don't forget to free
+ *
+ * \param[in] sockfd the socket to read from
+ * \param[out] size the number of bytes that are read
+ * \return the data read
+ */
+uint8_t *ldns_tcp_read_wire(int sockfd, size_t *size);
+
+/**
+ * Gives back a raw packet from the wire and reads the header data from the given
+ * socket. Allocates the data (of size size) itself, so don't forget to free
+ *
+ * \param[in] sockfd the socket to read from
+ * \param[in] fr the address of the client (if applicable)
+ * \param[in] *frlen the lenght of the client's addr (if applicable)
+ * \param[out] size the number of bytes that are read
+ * \return the data read
+ */
+uint8_t *ldns_udp_read_wire(int sockfd, size_t *size, struct sockaddr_storage *fr, socklen_t *frlen);
+
+/**
+ * returns the native sockaddr representation from the rdf.
+ * \param[in] rd the ldns_rdf to operate on
+ * \param[in] port what port to use. 0 means; use default (53)
+ * \param[out] size what is the size of the sockaddr_storage
+ * \return struct sockaddr* the address in the format so other
+ * functions can use it (sendto)
+ */
+struct sockaddr_storage * ldns_rdf2native_sockaddr_storage(const ldns_rdf *rd, uint16_t port, size_t *size);
+
+/**
+ * returns an rdf with the sockaddr info. works for ip4 and ip6
+ * \param[in] sock the struct sockaddr_storage to convert
+ * \param[in] port what port was used. When NULL this is not set
+ * \return ldns_rdf* wth the address
+ */
+ldns_rdf * ldns_sockaddr_storage2rdf(struct sockaddr_storage *sock, uint16_t *port);
+
+/**
+ * Prepares the resolver for an axfr query
+ * The query is sent and the answers can be read with ldns_axfr_next
+ * \param[in] resolver the resolver to use
+ * \param[in] domain the domain to exfr
+ * \param[in] c the class to use
+ * \return ldns_status the status of the transfer
+ */
+ldns_status ldns_axfr_start(ldns_resolver *resolver, ldns_rdf *domain, ldns_rr_class c);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* LDNS_NET_H */
diff --git a/3rdParty/Ldns/src/include/ldns/packet.h b/3rdParty/Ldns/src/include/ldns/packet.h
new file mode 100644
index 0000000..687a6a2
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/packet.h
@@ -0,0 +1,855 @@
+/*
+ * packet.h
+ *
+ * DNS packet definitions
+ *
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2005-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+/**
+ * \file
+ *
+ * Contains the definition of ldns_pkt and its parts, as well
+ * as functions to manipulate those.
+ */
+
+
+#ifndef LDNS_PACKET_H
+#define LDNS_PACKET_H
+
+#define LDNS_MAX_PACKETLEN         65535
+
+/* allow flags to be given to mk_query */
+#define LDNS_QR		1       /* QueRy - query flag */
+#define LDNS_AA		2       /* Authoritative Answer - server flag */
+#define LDNS_TC		4       /* TrunCated - server flag */
+#define LDNS_RD		8       /* Recursion Desired - query flag */
+#define LDNS_CD		16      /* Checking Disabled - query flag */
+#define LDNS_RA		32      /* Recursion Available - server flag */
+#define LDNS_AD		64      /* Authenticated Data - server flag */
+
+#include <ldns/error.h>
+#include <ldns/common.h>
+#include <ldns/rr.h>
+#include <sys/time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* opcodes for pkt's */
+enum ldns_enum_pkt_opcode {
+	LDNS_PACKET_QUERY = 0,
+	LDNS_PACKET_IQUERY = 1,
+	LDNS_PACKET_STATUS = 2, /* there is no 3?? DNS is weird */
+	LDNS_PACKET_NOTIFY = 4,
+	LDNS_PACKET_UPDATE = 5
+};
+typedef enum ldns_enum_pkt_opcode ldns_pkt_opcode;
+
+/* rcodes for pkts */
+enum ldns_enum_pkt_rcode {
+	LDNS_RCODE_NOERROR = 0,
+	LDNS_RCODE_FORMERR = 1,
+	LDNS_RCODE_SERVFAIL = 2,
+	LDNS_RCODE_NXDOMAIN = 3,
+	LDNS_RCODE_NOTIMPL = 4,
+	LDNS_RCODE_REFUSED = 5,
+	LDNS_RCODE_YXDOMAIN = 6,
+	LDNS_RCODE_YXRRSET = 7,
+	LDNS_RCODE_NXRRSET = 8,
+	LDNS_RCODE_NOTAUTH = 9,
+	LDNS_RCODE_NOTZONE = 10
+};
+typedef enum ldns_enum_pkt_rcode ldns_pkt_rcode;
+
+/**
+ *  Header of a dns packet
+ *
+ * Contains the information about the packet itself, as specified in RFC1035
+<pre>
+4.1.1. Header section format
+
+The header contains the following fields:
+
+                                    1  1  1  1  1  1
+      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                      ID                       |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    QDCOUNT                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    ANCOUNT                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    NSCOUNT                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    ARCOUNT                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+ID              A 16 bit identifier assigned by the program that
+                generates any kind of query.  This identifier is copied
+                the corresponding reply and can be used by the requester
+                to match up replies to outstanding queries.
+
+QR              A one bit field that specifies whether this message is a
+                query (0), or a response (1).
+
+OPCODE          A four bit field that specifies kind of query in this
+                message.  This value is set by the originator of a query
+                and copied into the response.  The values are:
+
+                0               a standard query (QUERY)
+
+                1               an inverse query (IQUERY)
+
+                2               a server status request (STATUS)
+
+                3-15            reserved for future use
+
+AA              Authoritative Answer - this bit is valid in responses,
+                and specifies that the responding name server is an
+                authority for the domain name in question section.
+
+                Note that the contents of the answer section may have
+                multiple owner names because of aliases.  The AA bit
+
+                corresponds to the name which matches the query name, or
+                the first owner name in the answer section.
+
+TC              TrunCation - specifies that this message was truncated
+                due to length greater than that permitted on the
+                transmission channel.
+
+RD              Recursion Desired - this bit may be set in a query and
+                is copied into the response.  If RD is set, it directs
+                the name server to pursue the query recursively.
+                Recursive query support is optional.
+
+RA              Recursion Available - this be is set or cleared in a
+                response, and denotes whether recursive query support is
+                available in the name server.
+
+Z               Reserved for future use.  Must be zero in all queries
+                and responses.
+
+RCODE           Response code - this 4 bit field is set as part of
+                responses.  The values have the following
+                interpretation:
+
+                0               No error condition
+
+                1               Format error - The name server was
+                                unable to interpret the query.
+
+                2               Server failure - The name server was
+                                unable to process this query due to a
+                                problem with the name server.
+
+                3               Name Error - Meaningful only for
+                                responses from an authoritative name
+                                server, this code signifies that the
+                                domain name referenced in the query does
+                                not exist.
+
+                4               Not Implemented - The name server does
+                                not support the requested kind of query.
+
+                5               Refused - The name server refuses to
+                                perform the specified operation for
+                                policy reasons.  For example, a name
+                                server may not wish to provide the
+                                information to the particular requester,
+                                or a name server may not wish to perform
+                                a particular operation (e.g., zone
+
+                                transfer) for particular data.
+
+                6-15            Reserved for future use.
+
+QDCOUNT         an unsigned 16 bit integer specifying the number of
+                entries in the question section.
+
+ANCOUNT         an unsigned 16 bit integer specifying the number of
+                resource records in the answer section.
+
+NSCOUNT         an unsigned 16 bit integer specifying the number of name
+                server resource records in the authority records
+                section.
+
+ARCOUNT         an unsigned 16 bit integer specifying the number of
+                resource records in the additional records section.
+
+</pre>
+ */
+struct ldns_struct_hdr
+{
+	/**  Id of a packet */
+	uint16_t _id;
+	/**  Query bit (0=query, 1=answer) */
+	bool _qr;
+	/**  Authoritative answer */
+	bool _aa;
+	/**  Packet truncated */
+	bool _tc;
+	/**  Recursion desired */
+	bool _rd;
+	/**  Checking disabled */
+	bool _cd;
+	/**  Recursion available */
+	bool _ra;
+	/**  Authentic data */
+	bool _ad;
+	/**  Query type */
+	ldns_pkt_opcode _opcode;	 /* XXX 8 bits? */
+	/**  Response code */
+	uint8_t _rcode;
+	/**  question sec */
+	uint16_t _qdcount;
+	/**  answer sec */
+	uint16_t _ancount;
+	/**  auth sec */
+	uint16_t _nscount;
+	/**  add sec */
+	uint16_t _arcount;
+};
+typedef struct ldns_struct_hdr ldns_hdr;
+
+/**
+ * DNS packet
+ *
+ * This structure contains a complete DNS packet (either a query or an answer)
+ *
+ * It is the complete representation of what you actually send to a
+ * nameserver, and what it sends back (assuming you are the client here).
+ */
+struct ldns_struct_pkt
+{
+	/** Header section */
+	ldns_hdr *_header;
+	/* extra items needed in a packet */
+	/** The size of the wire format of the packet in octets */
+	ldns_rdf *_answerfrom;
+        /** Timestamp of the time the packet was sent or created */
+	struct timeval timestamp;
+	/** The duration of the query this packet is an answer to */
+	uint32_t _querytime;
+	/** The size of the wire format of the packet in octets */
+	size_t _size;
+	/** Optional tsig rr */
+	ldns_rr *_tsig_rr;
+	/** EDNS0 available buffer size, see RFC2671 */
+	uint16_t _edns_udp_size;
+	/** EDNS0 Extended rcode */
+	uint8_t _edns_extended_rcode;
+	/** EDNS Version */
+	uint8_t _edns_version;
+	/** Reserved EDNS data bits */
+	uint16_t _edns_z;
+	/** Arbitrary EDNS rdata */
+	ldns_rdf *_edns_data;
+	/**  Question section */
+	ldns_rr_list	*_question;
+	/**  Answer section */
+	ldns_rr_list	*_answer;
+	/**  Authority section */
+	ldns_rr_list	*_authority;
+	/**  Additional section */
+	ldns_rr_list	*_additional;
+};
+typedef struct ldns_struct_pkt ldns_pkt;
+
+/**
+ * The sections of a packet
+ */
+enum ldns_enum_pkt_section {
+	LDNS_SECTION_QUESTION = 0,
+	LDNS_SECTION_ANSWER = 1,
+	LDNS_SECTION_AUTHORITY = 2,
+	LDNS_SECTION_ADDITIONAL = 3,
+	/** bogus section, if not interested */
+	LDNS_SECTION_ANY = 4,
+	/** used to get all non-question rrs from a packet */
+	LDNS_SECTION_ANY_NOQUESTION = 5
+};
+typedef enum ldns_enum_pkt_section ldns_pkt_section;	
+
+/**
+ * The different types of packets
+ */
+enum ldns_enum_pkt_type {
+	LDNS_PACKET_QUESTION,
+	LDNS_PACKET_REFERRAL,
+	LDNS_PACKET_ANSWER,
+	LDNS_PACKET_NXDOMAIN,
+	LDNS_PACKET_NODATA,
+	LDNS_PACKET_UNKNOWN
+};
+typedef enum ldns_enum_pkt_type ldns_pkt_type;
+
+/* prototypes */
+
+/* read */
+
+/**
+ * Read the packet id
+ * \param[in] p the packet
+ * \return the packet id
+ */
+uint16_t ldns_pkt_id(const ldns_pkt *p);
+/**
+ * Read the packet's qr bit
+ * \param[in] p the packet
+ * \return value of the bit
+ */
+bool ldns_pkt_qr(const ldns_pkt *p);
+/**
+ * Read the packet's aa bit
+ * \param[in] p the packet
+ * \return value of the bit
+ */
+bool ldns_pkt_aa(const ldns_pkt *p);
+/**
+ * Read the packet's tc bit
+ * \param[in] p the packet
+ * \return value of the bit
+ */
+bool ldns_pkt_tc(const ldns_pkt *p);
+/**
+ * Read the packet's rd bit
+ * \param[in] p the packet
+ * \return value of the bit
+ */
+bool ldns_pkt_rd(const ldns_pkt *p);
+/**
+ * Read the packet's cd bit
+ * \param[in] p the packet
+ * \return value of the bit
+ */
+bool ldns_pkt_cd(const ldns_pkt *p);
+/**
+ * Read the packet's ra bit
+ * \param[in] p the packet
+ * \return value of the bit
+ */
+bool ldns_pkt_ra(const ldns_pkt *p);
+/**
+ * Read the packet's ad bit
+ * \param[in] p the packet
+ * \return value of the bit
+ */
+bool ldns_pkt_ad(const ldns_pkt *p);
+/**
+ * Read the packet's code
+ * \param[in] p the packet
+ * \return the opcode
+ */
+ldns_pkt_opcode ldns_pkt_get_opcode(const ldns_pkt *p);
+/**
+ * Return the packet's respons code
+ * \param[in] p the packet
+ * \return the respons code
+ */
+ldns_pkt_rcode ldns_pkt_get_rcode(const ldns_pkt *p);
+/**
+ * Return the packet's qd count 
+ * \param[in] p the packet
+ * \return the qd count
+ */
+uint16_t ldns_pkt_qdcount(const ldns_pkt *p);
+/**
+ * Return the packet's an count
+ * \param[in] p the packet
+ * \return the an count
+ */
+uint16_t ldns_pkt_ancount(const ldns_pkt *p);
+/**
+ * Return the packet's ns count
+ * \param[in] p the packet
+ * \return the ns count
+ */
+uint16_t ldns_pkt_nscount(const ldns_pkt *p);
+/**
+ * Return the packet's ar count
+ * \param[in] p the packet
+ * \return the ar count
+ */
+uint16_t ldns_pkt_arcount(const ldns_pkt *p);
+
+/** 
+ * Return the packet's answerfrom
+ * \param[in] p packet
+ * \return the name of the server
+ */
+ldns_rdf *ldns_pkt_answerfrom(const ldns_pkt *p);
+
+/**
+ * Return the packet's timestamp
+ * \param[in] p the packet
+ * \return the timestamp
+ */
+struct timeval ldns_pkt_timestamp(const ldns_pkt *p);
+/**
+ * Return the packet's querytime
+ * \param[in] p the packet
+ * \return the querytime
+ */
+uint32_t ldns_pkt_querytime(const ldns_pkt *p);
+
+/**
+ * Return the packet's size in bytes
+ * \param[in] p the packet
+ * \return the size
+ */
+size_t ldns_pkt_size(const ldns_pkt *p);
+
+/**
+ * Return the packet's tsig pseudo rr's
+ * \param[in] p the packet
+ * \return the tsig rr
+ */
+ldns_rr *ldns_pkt_tsig(const ldns_pkt *p);
+
+/**
+ * Return the packet's question section
+ * \param[in] p the packet
+ * \return the section
+ */
+ldns_rr_list *ldns_pkt_question(const ldns_pkt *p);
+/**
+ * Return the packet's answer section
+ * \param[in] p the packet
+ * \return the section
+ */
+ldns_rr_list *ldns_pkt_answer(const ldns_pkt *p);
+/**
+ * Return the packet's authority section
+ * \param[in] p the packet
+ * \return the section
+ */
+ldns_rr_list *ldns_pkt_authority(const ldns_pkt *p);
+/**
+ * Return the packet's additional section
+ * \param[in] p the packet
+ * \return the section
+ */
+ldns_rr_list *ldns_pkt_additional(const ldns_pkt *p);
+/**
+ * Return the packet's question, answer, authority and additional sections
+ * concatenated, in a new rr_list clone.
+ * \param[in] p the packet
+ * \return the rrs
+ */
+ldns_rr_list *ldns_pkt_all(const ldns_pkt *p);
+/**
+ * Return the packet's answer, authority and additional sections concatenated, 
+ * in a new rr_list clone.  Like ldns_pkt_all but without the questions.
+ * \param[in] p the packet
+ * \return the rrs except the question rrs
+ */
+ldns_rr_list *ldns_pkt_all_noquestion(const ldns_pkt *p);
+
+/**
+ * return all the rr_list's in the packet. Clone the lists, instead
+ * of returning pointers. 
+ * \param[in] p the packet to look in
+ * \param[in] s what section(s) to return
+ * \return ldns_rr_list with the rr's or NULL if none were found
+ */
+ldns_rr_list *ldns_pkt_get_section_clone(const ldns_pkt *p, ldns_pkt_section s);
+
+/**
+ * return all the rr with a specific name from a packet. Optionally
+ * specify from which section in the packet
+ * \param[in] p the packet
+ * \param[in] r the name
+ * \param[in] s the packet's section
+ * \return a list with the rr's or NULL if none were found
+ */
+ldns_rr_list *ldns_pkt_rr_list_by_name(ldns_pkt *p, ldns_rdf *r, ldns_pkt_section s);
+/**
+ * return all the rr with a specific type from a packet. Optionally
+ * specify from which section in the packet
+ * \param[in] p the packet
+ * \param[in] t the type
+ * \param[in] s the packet's section
+ * \return a list with the rr's or NULL if none were found
+ */
+ldns_rr_list *ldns_pkt_rr_list_by_type(const ldns_pkt *p, ldns_rr_type t, ldns_pkt_section s);
+/**
+ * return all the rr with a specific type and type from a packet. Optionally
+ * specify from which section in the packet
+ * \param[in] packet the packet
+ * \param[in] ownername the name
+ * \param[in] type the type
+ * \param[in] sec the packet's section
+ * \return a list with the rr's or NULL if none were found
+ */
+ldns_rr_list *ldns_pkt_rr_list_by_name_and_type(const ldns_pkt *packet, const ldns_rdf *ownername, ldns_rr_type type, ldns_pkt_section sec);
+
+
+/**
+ * check to see if an rr exist in the packet
+ * \param[in] pkt the packet to examine
+ * \param[in] sec in which section to look
+ * \param[in] rr the rr to look for
+ */
+bool ldns_pkt_rr(ldns_pkt *pkt, ldns_pkt_section sec, ldns_rr *rr);
+
+
+/**
+ * sets the flags in a packet.
+ * \param[in] pkt the packet to operate on
+ * \param[in] flags ORed values: LDNS_QR| LDNS_AR for instance
+ * \return true on success otherwise false
+ */
+bool ldns_pkt_set_flags(ldns_pkt *pkt, uint16_t flags);
+
+/**
+ * Set the packet's id
+ * \param[in] p the packet
+ * \param[in] id the id to set
+ */
+void ldns_pkt_set_id(ldns_pkt *p, uint16_t id);
+/**
+ * Set the packet's id to a random value
+ * \param[in] p the packet
+ */
+void ldns_pkt_set_random_id(ldns_pkt *p);
+/**
+ * Set the packet's qr bit
+ * \param[in] p the packet
+ * \param[in] b the value to set (boolean)
+ */
+void ldns_pkt_set_qr(ldns_pkt *p, bool b);
+/**
+ * Set the packet's aa bit
+ * \param[in] p the packet
+ * \param[in] b the value to set (boolean)
+ */
+void ldns_pkt_set_aa(ldns_pkt *p, bool b);
+/**
+ * Set the packet's tc bit
+ * \param[in] p the packet
+ * \param[in] b the value to set (boolean)
+ */
+void ldns_pkt_set_tc(ldns_pkt *p, bool b);
+/**
+ * Set the packet's rd bit
+ * \param[in] p the packet
+ * \param[in] b the value to set (boolean)
+ */
+void ldns_pkt_set_rd(ldns_pkt *p, bool b);
+/**
+ * Set the packet's cd bit
+ * \param[in] p the packet
+ * \param[in] b the value to set (boolean)
+ */
+void ldns_pkt_set_cd(ldns_pkt *p, bool b);
+/**
+ * Set the packet's ra bit
+ * \param[in] p the packet
+ * \param[in] b the value to set (boolean)
+ */
+void ldns_pkt_set_ra(ldns_pkt *p, bool b);
+/**
+ * Set the packet's ad bit
+ * \param[in] p the packet
+ * \param[in] b the value to set (boolean)
+ */
+void ldns_pkt_set_ad(ldns_pkt *p, bool b);
+
+/**
+ * Set the packet's opcode
+ * \param[in] p the packet
+ * \param[in] c the opcode
+ */
+void ldns_pkt_set_opcode(ldns_pkt *p, ldns_pkt_opcode c);
+/**
+ * Set the packet's respons code
+ * \param[in] p the packet
+ * \param[in] c the rcode
+ */
+void ldns_pkt_set_rcode(ldns_pkt *p, uint8_t c);
+/**
+ * Set the packet's qd count
+ * \param[in] p the packet
+ * \param[in] c the count
+ */
+void ldns_pkt_set_qdcount(ldns_pkt *p, uint16_t c);
+/**
+ * Set the packet's an count
+ * \param[in] p the packet
+ * \param[in] c the count
+ */
+void ldns_pkt_set_ancount(ldns_pkt *p, uint16_t c);
+/**
+ * Set the packet's ns count
+ * \param[in] p the packet
+ * \param[in] c the count
+ */
+void ldns_pkt_set_nscount(ldns_pkt *p, uint16_t c);
+/**
+ * Set the packet's arcount
+ * \param[in] p the packet
+ * \param[in] c the count
+ */
+void ldns_pkt_set_arcount(ldns_pkt *p, uint16_t c);
+/**
+ * Set the packet's answering server
+ * \param[in] p the packet
+ * \param[in] r the address
+ */
+void ldns_pkt_set_answerfrom(ldns_pkt *p, ldns_rdf *r);
+/**
+ * Set the packet's query time
+ * \param[in] p the packet
+ * \param[in] t the querytime in msec
+ */
+void ldns_pkt_set_querytime(ldns_pkt *p, uint32_t t);
+/**
+ * Set the packet's size
+ * \param[in] p the packet
+ * \param[in] s the size
+ */
+void ldns_pkt_set_size(ldns_pkt *p, size_t s);
+
+/**
+ * Set the packet's timestamp
+ * \param[in] p the packet
+ * \param[in] timeval the timestamp
+ */
+void ldns_pkt_set_timestamp(ldns_pkt *p, struct timeval timeval);
+/**
+ * Set a packet's section count to x
+ * \param[in] p the packet
+ * \param[in] s the section
+ * \param[in] x the section count
+ */
+void ldns_pkt_set_section_count(ldns_pkt *p, ldns_pkt_section s, uint16_t x);
+/**
+ * Set the packet's tsig rr
+ * \param[in] p the packet
+ * \param[in] t the tsig rr
+ */
+void ldns_pkt_set_tsig(ldns_pkt *p, ldns_rr *t);
+
+/**
+ * looks inside the packet to determine
+ * what kind of packet it is, AUTH, NXDOMAIN, REFERRAL, etc.
+ * \param[in] p the packet to examine
+ * \return the type of packet
+ */
+ldns_pkt_type ldns_pkt_reply_type(ldns_pkt *p);
+
+/**
+ * return the packet's edns udp size
+ * \param[in] packet the packet
+ * \return the size
+ */
+uint16_t ldns_pkt_edns_udp_size(const ldns_pkt *packet);
+/**
+ * return the packet's edns extended rcode
+ * \param[in] packet the packet
+ * \return the rcode
+ */
+uint8_t ldns_pkt_edns_extended_rcode(const ldns_pkt *packet);
+/**
+ * return the packet's edns version
+ * \param[in] packet the packet
+ * \return the version
+ */
+uint8_t ldns_pkt_edns_version(const ldns_pkt *packet);
+/**
+ * return the packet's edns z value
+ * \param[in] packet the packet
+ * \return the z value
+ */
+uint16_t ldns_pkt_edns_z(const ldns_pkt *packet);
+/**
+ * return the packet's edns data
+ * \param[in] packet the packet
+ * \return the data
+ */
+ldns_rdf *ldns_pkt_edns_data(const ldns_pkt *packet);
+
+/**
+ * return the packet's edns do bit
+ * \param[in] packet the packet
+ * \return the bit's value
+ */
+bool ldns_pkt_edns_do(const ldns_pkt *packet);
+/**
+ * Set the packet's edns do bit
+ * \param[in] packet the packet
+ * \param[in] value the bit's new value
+ */
+void ldns_pkt_set_edns_do(ldns_pkt *packet, bool value);
+
+/**
+ * returns true if this packet needs and EDNS rr to be sent.
+ * At the moment the only reason is an expected packet
+ * size larger than 512 bytes, but for instance dnssec would
+ * be a good reason too.
+ *
+ * \param[in] packet the packet to check
+ * \return true if packet needs edns rr
+ */
+bool ldns_pkt_edns(const ldns_pkt *packet);
+
+/**
+ * Set the packet's edns udp size
+ * \param[in] packet the packet
+ * \param[in] s the size
+ */
+void ldns_pkt_set_edns_udp_size(ldns_pkt *packet, uint16_t s);
+/**
+ * Set the packet's edns extended rcode
+ * \param[in] packet the packet
+ * \param[in] c the code
+ */
+void ldns_pkt_set_edns_extended_rcode(ldns_pkt *packet, uint8_t c);
+/**
+ * Set the packet's edns version
+ * \param[in] packet the packet
+ * \param[in] v the version
+ */
+void ldns_pkt_set_edns_version(ldns_pkt *packet, uint8_t v);
+/**
+ * Set the packet's edns z value
+ * \param[in] packet the packet
+ * \param[in] z the value
+ */
+void ldns_pkt_set_edns_z(ldns_pkt *packet, uint16_t z);
+/**
+ * Set the packet's edns data
+ * \param[in] packet the packet
+ * \param[in] data the data
+ */
+void ldns_pkt_set_edns_data(ldns_pkt *packet, ldns_rdf *data);
+
+/**
+ * allocates and initializes a ldns_pkt structure.
+ * \return pointer to the new packet
+ */
+ldns_pkt *ldns_pkt_new();
+
+/**
+ * frees the packet structure and all data that it contains.
+ * \param[in] packet The packet structure to free
+ * \return void
+ */
+void ldns_pkt_free(ldns_pkt *packet);
+
+/**
+ * creates a query packet for the given name, type, class.
+ * \param[out] p the packet to be returned
+ * \param[in] rr_name the name to query for (as string)
+ * \param[in] rr_type the type to query for
+ * \param[in] rr_class the class to query for
+ * \param[in] flags packet flags
+ * \return LDNS_STATUS_OK or a ldns_status mesg with the error
+ */
+ldns_status ldns_pkt_query_new_frm_str(ldns_pkt **p, const char *rr_name, ldns_rr_type rr_type, ldns_rr_class rr_class , uint16_t flags);
+
+/**
+ * creates a packet with a query in it for the given name, type and class.
+ * \param[in] rr_name the name to query for
+ * \param[in] rr_type the type to query for
+ * \param[in] rr_class the class to query for
+ * \param[in] flags packet flags
+ * \return ldns_pkt* a pointer to the new pkt
+ */
+ldns_pkt *ldns_pkt_query_new(ldns_rdf *rr_name, ldns_rr_type rr_type, ldns_rr_class rr_class, uint16_t flags);
+
+/**
+ * clones the given packet, creating a fully allocated copy
+ *
+ * \param[in] pkt the packet to clone
+ * \return ldns_pkt* pointer to the new packet
+ */
+ldns_pkt *ldns_pkt_clone(ldns_pkt *pkt);
+
+/**
+ * directly set the additional section
+ * \param[in] p packet to operate on
+ * \param[in] rr rrlist to set
+ */
+void ldns_pkt_set_additional(ldns_pkt *p, ldns_rr_list *rr);
+
+/**
+ * directly set the answer section
+ * \param[in] p packet to operate on
+ * \param[in] rr rrlist to set
+ */
+void ldns_pkt_set_answer(ldns_pkt *p, ldns_rr_list *rr);
+
+/**
+ * directly set the question section
+ * \param[in] p packet to operate on
+ * \param[in] rr rrlist to set
+ */
+void ldns_pkt_set_question(ldns_pkt *p, ldns_rr_list *rr);
+
+/**
+ * directly set the auhority section
+ * \param[in] p packet to operate on
+ * \param[in] rr rrlist to set
+ */
+void ldns_pkt_set_authority(ldns_pkt *p, ldns_rr_list *rr);
+
+/**
+ * push an rr on a packet
+ * \param[in] packet packet to operate on
+ * \param[in] section where to put it
+ * \param[in] rr rr to push
+ * \return a boolean which is true when the rr was added
+ */
+bool ldns_pkt_push_rr(ldns_pkt *packet, ldns_pkt_section section, ldns_rr *rr);
+
+/**
+ * push an rr on a packet, provided the RR is not there.
+ * \param[in] pkt packet to operate on
+ * \param[in] sec where to put it
+ * \param[in] rr rr to push
+ * \return a boolean which is true when the rr was added
+ */
+bool ldns_pkt_safe_push_rr(ldns_pkt *pkt, ldns_pkt_section sec, ldns_rr *rr);
+
+/**
+ * push a rr_list on a packet
+ * \param[in] packet packet to operate on
+ * \param[in] section where to put it
+ * \param[in] list the rr_list to push
+ * \return a boolean which is true when the rr was added
+ */
+bool ldns_pkt_push_rr_list(ldns_pkt *packet, ldns_pkt_section section, ldns_rr_list *list);
+
+/**
+ * push an rr_list to a packet, provided the RRs are not already there.
+ * \param[in] pkt packet to operate on
+ * \param[in] sec where to put it
+ * \param[in] list the rr_list to push
+ * \return a boolean which is true when the rr was added
+ */
+bool ldns_pkt_safe_push_rr_list(ldns_pkt *pkt, ldns_pkt_section sec, ldns_rr_list *list);
+
+/**
+ * check if a packet is empty
+ * \param[in] p packet
+ * \return true: empty, false: not empty
+ */
+bool ldns_pkt_empty(ldns_pkt *p);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* LDNS_PACKET_H */
diff --git a/3rdParty/Ldns/src/include/ldns/parse.h b/3rdParty/Ldns/src/include/ldns/parse.h
new file mode 100644
index 0000000..0e9034c
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/parse.h
@@ -0,0 +1,167 @@
+/*
+ * parse.h 
+ *
+ * a Net::DNS like library for C
+ * LibDNS Team @ NLnet Labs
+ * (c) NLnet Labs, 2005-2006
+ * See the file LICENSE for the license
+ */
+
+#ifndef LDNS_PARSE_H
+#define LDNS_PARSE_H
+
+#include <ldns/common.h>
+#include <ldns/buffer.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LDNS_PARSE_SKIP_SPACE		"\f\n\r\v"
+#define LDNS_PARSE_NORMAL		" \f\n\r\t\v"
+#define LDNS_PARSE_NO_NL		" \t"
+#define LDNS_MAX_LINELEN		10230
+#define LDNS_MAX_KEYWORDLEN		32
+
+
+/**
+ * \file
+ *
+ * Contains some low-level parsing functions, mostly used in the _frm_str
+ * family of functions.
+ */
+ 
+/**
+ * different type of directives in zone files
+ * We now deal with $TTL, $ORIGIN and $INCLUDE.
+ * The latter is not implemented in ldns (yet)
+ */
+enum ldns_enum_directive
+{
+	LDNS_DIR_TTL,
+	LDNS_DIR_ORIGIN,
+	LDNS_DIR_INCLUDE
+};
+typedef enum ldns_enum_directive ldns_directive;
+
+/** 
+ * returns a token/char from the stream F.
+ * This function deals with ( and ) in the stream,
+ * and ignores them when encountered
+ * \param[in] *f the file to read from
+ * \param[out] *token the read token is put here
+ * \param[in] *delim chars at which the parsing should stop
+ * \param[in] *limit how much to read. If 0 the builtin maximum is used
+ * \return 0 on error of EOF of the stream F.  Otherwise return the length of what is read
+ */
+ssize_t ldns_fget_token(FILE *f, char *token, const char *delim, size_t limit);
+
+/** 
+ * returns a token/char from the stream F.
+ * This function deals with ( and ) in the stream,
+ * and ignores when it finds them.
+ * \param[in] *f the file to read from
+ * \param[out] *token the token is put here
+ * \param[in] *delim chars at which the parsing should stop
+ * \param[in] *limit how much to read. If 0 use builtin maximum
+ * \param[in] line_nr pointer to an integer containing the current line number (for debugging purposes)
+ * \return 0 on error of EOF of F otherwise return the length of what is read
+ */
+ssize_t ldns_fget_token_l(FILE *f, char *token, const char *delim, size_t limit, int *line_nr);
+
+/**
+ * returns a token/char from the buffer b.
+ * This function deals with ( and ) in the buffer,
+ * and ignores when it finds them.
+ * \param[in] *b the buffer to read from
+ * \param[out] *token the token is put here
+ * \param[in] *delim chars at which the parsing should stop
+ * \param[in] *limit how much to read. If 0 the builtin maximum is used
+ * \returns 0 on error of EOF of b. Otherwise return the length of what is read
+ */
+ssize_t ldns_bget_token(ldns_buffer *b, char *token, const char *delim, size_t limit);
+
+/*
+ * searches for keyword and delim in a file. Gives everything back
+ * after the keyword + k_del until we hit d_del
+ * \param[in] f file pointer to read from
+ * \param[in] keyword keyword to look for
+ * \param[in] k_del keyword delimeter 
+ * \param[out] data the data found 
+ * \param[in] d_del the data delimeter
+ * \param[in] data_limit maximum size the the data buffer
+ * \return the number of character read
+ */
+ssize_t ldns_fget_keyword_data(FILE *f, const char *keyword, const char *k_del, char *data, const char *d_del, size_t data_limit);
+
+/*
+ * searches for keyword and delim. Gives everything back
+ * after the keyword + k_del until we hit d_del
+ * \param[in] f file pointer to read from
+ * \param[in] keyword keyword to look for
+ * \param[in] k_del keyword delimeter 
+ * \param[out] data the data found 
+ * \param[in] d_del the data delimeter
+ * \param[in] data_limit maximum size the the data buffer
+ * \param[in] line_nr pointer to an integer containing the current line number (for
+debugging purposes)
+ * \return the number of character read
+ */
+ssize_t ldns_fget_keyword_data_l(FILE *f, const char *keyword, const char *k_del, char *data, const char *d_del, size_t data_limit, int *line_nr);
+
+/*
+ * searches for keyword and delim in a buffer. Gives everything back
+ * after the keyword + k_del until we hit d_del
+ * \param[in] b buffer pointer to read from
+ * \param[in] keyword keyword to look for
+ * \param[in] k_del keyword delimeter 
+ * \param[out] data the data found 
+ * \param[in] d_del the data delimeter
+ * \param[in] data_limit maximum size the the data buffer
+ * \return the number of character read
+ */
+ssize_t ldns_bget_keyword_data(ldns_buffer *b, const char *keyword, const char *k_del, char *data, const char *d_del, size_t data_limit);
+
+/**
+ * returns the next character from a buffer. Advances the position pointer with 1.
+ * When end of buffer is reached returns EOF. This is the buffer's equivalent
+ * for getc().
+ * \param[in] *buffer buffer to read from
+ * \return EOF on failure otherwise return the character
+ */
+int ldns_bgetc(ldns_buffer *buffer);
+
+/**
+ * skips all of the characters in the given string in the buffer, moving
+ * the position to the first character that is not in *s.
+ * \param[in] *buffer buffer to use
+ * \param[in] *s characters to skip
+ * \return void
+ */
+void ldns_bskipcs(ldns_buffer *buffer, const char *s);
+
+/**
+ * skips all of the characters in the given string in the fp, moving
+ * the position to the first character that is not in *s.
+ * \param[in] *fp file to use
+ * \param[in] *s characters to skip
+ * \return void
+ */
+void ldns_fskipcs(FILE *fp, const char *s);
+
+
+/**
+ * skips all of the characters in the given string in the fp, moving
+ * the position to the first character that is not in *s.
+ * \param[in] *fp file to use
+ * \param[in] *s characters to skip
+ * \param[in] line_nr pointer to an integer containing the current line number (for debugging purposes)
+ * \return void
+ */
+void ldns_fskipcs_l(FILE *fp, const char *s, int *line_nr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LDNS_PARSE_H */
diff --git a/3rdParty/Ldns/src/include/ldns/rbtree.h b/3rdParty/Ldns/src/include/ldns/rbtree.h
new file mode 100644
index 0000000..98bd880
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/rbtree.h
@@ -0,0 +1,230 @@
+/*
+ * rbtree.h -- generic red-black tree
+ *
+ * Copyright (c) 2001-2008, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * 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.
+ *
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ *
+ */
+
+/**
+ * \file
+ * Red black tree. Implementation taken from NSD 3.0.5, adjusted for use
+ * in unbound (memory allocation, logging and so on).
+ */
+
+#ifndef LDNS_RBTREE_H_
+#define	LDNS_RBTREE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * This structure must be the first member of the data structure in
+ * the rbtree.  This allows easy casting between an rbnode_t and the
+ * user data (poor man's inheritance).
+ * Or you can use the data pointer member to get to your data item.
+ */
+typedef struct ldns_rbnode_t ldns_rbnode_t;
+/**
+ * The rbnode_t struct definition.
+ */
+struct ldns_rbnode_t {
+	/** parent in rbtree, RBTREE_NULL for root */
+	ldns_rbnode_t   *parent;
+	/** left node (smaller items) */
+	ldns_rbnode_t   *left;
+	/** right node (larger items) */
+	ldns_rbnode_t   *right;
+	/** pointer to sorting key */
+	const void *key;
+	/** pointer to data */
+	const void *data;
+	/** colour of this node */
+	uint8_t	    color;
+};
+
+/** The nullpointer, points to empty node */
+#define	LDNS_RBTREE_NULL &ldns_rbtree_null_node
+/** the global empty node */
+extern	ldns_rbnode_t	ldns_rbtree_null_node;
+
+/** An entire red black tree */
+typedef struct ldns_rbtree_t ldns_rbtree_t;
+/** definition for tree struct */
+struct ldns_rbtree_t {
+	/** The root of the red-black tree */
+	ldns_rbnode_t    *root;
+
+	/** The number of the nodes in the tree */
+	size_t       count;
+
+	/**
+	 * Key compare function. <0,0,>0 like strcmp.
+	 * Return 0 on two NULL ptrs.
+	 */
+	int (*cmp) (const void *, const void *);
+};
+
+/**
+ * Create new tree (malloced) with given key compare function.
+ * @param cmpf: compare function (like strcmp) takes pointers to two keys.
+ * @return: new tree, empty.
+ */
+ldns_rbtree_t *ldns_rbtree_create(int (*cmpf)(const void *, const void *));
+
+/**
+ * Free the complete tree (but not its keys)
+ * @param rbtree The tree to free
+ */
+void ldns_rbtree_free(ldns_rbtree_t *rbtree);
+
+/**
+ * Init a new tree (malloced by caller) with given key compare function.
+ * @param rbtree: uninitialised memory for new tree, returned empty.
+ * @param cmpf: compare function (like strcmp) takes pointers to two keys.
+ */
+void ldns_rbtree_init(ldns_rbtree_t *rbtree, int (*cmpf)(const void *, const void *));
+
+/**
+ * Insert data into the tree.
+ * @param rbtree: tree to insert to.
+ * @param data: element to insert.
+ * @return: data ptr or NULL if key already present.
+ */
+ldns_rbnode_t *ldns_rbtree_insert(ldns_rbtree_t *rbtree, ldns_rbnode_t *data);
+
+/**
+ * Insert data into the tree (reversed arguments, for use as callback)
+ * \param[in] data element to insert
+ * \param[out] rbtree tree to insert in to
+ * \return data ptr or NULL if key is already present
+ */
+void ldns_rbtree_insert_vref(ldns_rbnode_t *data, void *rbtree);
+
+/**
+ * Delete element from tree.
+ * @param rbtree: tree to delete from.
+ * @param key: key of item to delete.
+ * @return: node that is now unlinked from the tree. User to delete it.
+ * returns 0 if node not present
+ */
+ldns_rbnode_t *ldns_rbtree_delete(ldns_rbtree_t *rbtree, const void *key);
+
+/**
+ * Find key in tree. Returns NULL if not found.
+ * @param rbtree: tree to find in.
+ * @param key: key that must match.
+ * @return: node that fits or NULL.
+ */
+ldns_rbnode_t *ldns_rbtree_search(ldns_rbtree_t *rbtree, const void *key);
+
+/**
+ * Find, but match does not have to be exact.
+ * @param rbtree: tree to find in.
+ * @param key: key to find position of.
+ * @param result: set to the exact node if present, otherwise to element that
+ *   precedes the position of key in the tree. NULL if no smaller element.
+ * @return: true if exact match in result. Else result points to <= element,
+ * or NULL if key is smaller than the smallest key.
+ */
+int ldns_rbtree_find_less_equal(ldns_rbtree_t *rbtree, const void *key,
+	ldns_rbnode_t **result);
+
+/**
+ * Returns first (smallest) node in the tree
+ * @param rbtree: tree
+ * @return: smallest element or NULL if tree empty.
+ */
+ldns_rbnode_t *ldns_rbtree_first(ldns_rbtree_t *rbtree);
+
+/**
+ * Returns last (largest) node in the tree
+ * @param rbtree: tree
+ * @return: largest element or NULL if tree empty.
+ */
+ldns_rbnode_t *ldns_rbtree_last(ldns_rbtree_t *rbtree);
+
+/**
+ * Returns next larger node in the tree
+ * @param rbtree: tree
+ * @return: next larger element or NULL if no larger in tree.
+ */
+ldns_rbnode_t *ldns_rbtree_next(ldns_rbnode_t *rbtree);
+
+/**
+ * Returns previous smaller node in the tree
+ * @param rbtree: tree
+ * @return: previous smaller element or NULL if no previous in tree.
+ */
+ldns_rbnode_t *ldns_rbtree_previous(ldns_rbnode_t *rbtree);
+
+/**
+ * split off 'elements' number of elements from the start
+ * of the name tree and return a new tree containing those
+ * elements
+ */
+ldns_rbtree_t *ldns_rbtree_split(ldns_rbtree_t *tree, size_t elements);
+
+/**
+ * add all node from the second tree to the first (removing them from the
+ * second), and fix up nsec(3)s if present
+ */
+void ldns_rbtree_join(ldns_rbtree_t *tree1, ldns_rbtree_t *tree2);
+
+/**
+ * Call with node=variable of struct* with rbnode_t as first element.
+ * with type is the type of a pointer to that struct.
+ */
+#define LDNS_RBTREE_FOR(node, type, rbtree) \
+	for(node=(type)ldns_rbtree_first(rbtree); \
+		(ldns_rbnode_t*)node != LDNS_RBTREE_NULL; \
+		node = (type)ldns_rbtree_next((ldns_rbnode_t*)node))
+
+/**
+ * Call function for all elements in the redblack tree, such that
+ * leaf elements are called before parent elements. So that all
+ * elements can be safely free()d.
+ * Note that your function must not remove the nodes from the tree.
+ * Since that may trigger rebalances of the rbtree.
+ * @param tree: the tree
+ * @param func: function called with element and user arg.
+ * 	The function must not alter the rbtree.
+ * @param arg: user argument.
+ */
+void ldns_traverse_postorder(ldns_rbtree_t* tree,
+	void (*func)(ldns_rbnode_t*, void*), void* arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UTIL_RBTREE_H_ */
diff --git a/3rdParty/Ldns/src/include/ldns/rdata.h b/3rdParty/Ldns/src/include/ldns/rdata.h
new file mode 100644
index 0000000..90dcbf1
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/rdata.h
@@ -0,0 +1,385 @@
+/*
+ * rdata.h
+ *
+ * rdata definitions
+ *
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2005-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+
+/**
+ * \file
+ *
+ * Defines ldns_rdf and functions to manipulate those.
+ */
+
+
+#ifndef LDNS_RDATA_H
+#define LDNS_RDATA_H
+
+#include <ldns/common.h>
+#include <ldns/error.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LDNS_MAX_RDFLEN	8192
+
+#define LDNS_RDF_SIZE_BYTE              1
+#define LDNS_RDF_SIZE_WORD              2
+#define LDNS_RDF_SIZE_DOUBLEWORD        4
+#define LDNS_RDF_SIZE_6BYTES            6
+#define LDNS_RDF_SIZE_16BYTES           16
+
+#define LDNS_NSEC3_VARS_OPTOUT_MASK 0x01
+
+/**
+ * The different types of RDATA fields.
+ */
+enum ldns_enum_rdf_type
+{
+	/** none */
+	LDNS_RDF_TYPE_NONE,
+	/** domain name */
+	LDNS_RDF_TYPE_DNAME,
+	/** 8 bits */
+	LDNS_RDF_TYPE_INT8,
+	/** 16 bits */
+	LDNS_RDF_TYPE_INT16,
+	/** 32 bits */
+	LDNS_RDF_TYPE_INT32,
+	/** A record */
+	LDNS_RDF_TYPE_A,
+	/** AAAA record */
+	LDNS_RDF_TYPE_AAAA,
+	/** txt string */
+	LDNS_RDF_TYPE_STR,
+	/** apl data */
+	LDNS_RDF_TYPE_APL,
+	/** b32 string */
+	LDNS_RDF_TYPE_B32_EXT,
+	/** b64 string */
+	LDNS_RDF_TYPE_B64,
+	/** hex string */
+	LDNS_RDF_TYPE_HEX,
+	/** nsec type codes */
+	LDNS_RDF_TYPE_NSEC,
+	/** a RR type */
+	LDNS_RDF_TYPE_TYPE,
+	/** a class */
+	LDNS_RDF_TYPE_CLASS,
+	/** certificate algorithm */
+	LDNS_RDF_TYPE_CERT_ALG,
+	/** a key algorithm */
+	LDNS_RDF_TYPE_ALG,
+	/** unknown types */
+	LDNS_RDF_TYPE_UNKNOWN,
+	/** time (32 bits) */
+	LDNS_RDF_TYPE_TIME,
+	/** period */
+	LDNS_RDF_TYPE_PERIOD,
+	/** tsig time 48 bits */
+	LDNS_RDF_TYPE_TSIGTIME,
+	LDNS_RDF_TYPE_TSIG,
+	/** variable length any type rdata where the length
+	    is specified by the first 2 bytes */
+	LDNS_RDF_TYPE_INT16_DATA,
+	/** protocol and port bitmaps */
+	LDNS_RDF_TYPE_SERVICE,
+	/** location data */
+	LDNS_RDF_TYPE_LOC,
+	/** well known services */
+	LDNS_RDF_TYPE_WKS,
+	/** NSAP */
+	LDNS_RDF_TYPE_NSAP,
+	/** ATMA */
+	LDNS_RDF_TYPE_ATMA,
+	/** IPSECKEY */
+	LDNS_RDF_TYPE_IPSECKEY,
+	/** nsec3 hash salt */
+	LDNS_RDF_TYPE_NSEC3_SALT,
+	/** nsec3 base32 string (with length byte on wire */
+	LDNS_RDF_TYPE_NSEC3_NEXT_OWNER
+};
+typedef enum ldns_enum_rdf_type ldns_rdf_type;
+
+/**
+ * algorithms used in CERT rrs
+ */
+enum ldns_enum_cert_algorithm
+{
+        LDNS_CERT_PKIX		= 1,
+        LDNS_CERT_SPKI		= 2,
+        LDNS_CERT_PGP		= 3,
+        LDNS_CERT_IPKIX         = 4,
+        LDNS_CERT_ISPKI         = 5,
+        LDNS_CERT_IPGP          = 6,
+        LDNS_CERT_ACPKIX        = 7,
+        LDNS_CERT_IACPKIX       = 8,
+        LDNS_CERT_URI		= 253,
+        LDNS_CERT_OID		= 254
+};
+typedef enum ldns_enum_cert_algorithm ldns_cert_algorithm;
+
+
+
+/**
+ * Resource record data field.
+ *
+ * The data is a network ordered array of bytes, which size is specified by
+ * the (16-bit) size field. To correctly parse it, use the type
+ * specified in the (16-bit) type field with a value from \ref ldns_rdf_type.
+ */
+struct ldns_struct_rdf
+{
+	/** The size of the data (in octets) */
+	size_t _size;
+	/** The type of the data */
+	ldns_rdf_type _type;
+	/** Pointer to the data (raw octets) */
+	void  *_data;
+};
+typedef struct ldns_struct_rdf ldns_rdf;
+
+/* prototypes */
+
+/* write access functions */
+
+/**
+ * sets the size of the rdf.
+ * \param[in] *rd the rdf to operate on
+ * \param[in] size the new size
+ * \return void
+ */
+void ldns_rdf_set_size(ldns_rdf *rd, size_t size);
+
+/**
+ * sets the size of the rdf.
+ * \param[in] *rd the rdf to operate on
+ * \param[in] type the new type
+ * \return void
+ */
+void ldns_rdf_set_type(ldns_rdf *rd, ldns_rdf_type type);
+
+/**
+ * sets the size of the rdf.
+ * \param[in] *rd the rdf to operate on
+ * \param[in] *data pointer to the new data
+ * \return void
+ */
+void ldns_rdf_set_data(ldns_rdf *rd, void *data);
+
+/* read access */
+
+/**
+ * returns the size of the rdf.
+ * \param[in] *rd the rdf to read from
+ * \return uint16_t with the size
+ */
+size_t ldns_rdf_size(const ldns_rdf *rd);
+
+/**
+ * returns the type of the rdf. We need to insert _get_
+ * here to prevent conflict the the rdf_type TYPE.
+ * \param[in] *rd the rdf to read from
+ * \return ldns_rdf_type with the type
+ */
+ldns_rdf_type ldns_rdf_get_type(const ldns_rdf *rd);
+
+/**
+ * returns the data of the rdf.
+ * \param[in] *rd the rdf to read from
+ * \return uint8_t* pointer to the rdf's data
+ */
+uint8_t *ldns_rdf_data(const ldns_rdf *rd);
+
+/* creator functions */
+
+/**
+ * allocates a new rdf structure and fills it.
+ * This function DOES NOT copy the contents from
+ * the buffer, unlinke ldns_rdf_new_frm_data()
+ * \param[in] type type of the rdf
+ * \param[in] size size of the buffer
+ * \param[in] data pointer to the buffer to be copied
+ * \return the new rdf structure or NULL on failure
+ */
+ldns_rdf *ldns_rdf_new(ldns_rdf_type type, size_t size, void *data);
+
+/**
+ * allocates a new rdf structure and fills it.
+ * This function _does_ copy the contents from
+ * the buffer, unlinke ldns_rdf_new()
+ * \param[in] type type of the rdf
+ * \param[in] size size of the buffer
+ * \param[in] data pointer to the buffer to be copied
+ * \return the new rdf structure or NULL on failure
+ */
+ldns_rdf *ldns_rdf_new_frm_data(ldns_rdf_type type, size_t size, const void *data);
+
+/**
+ * creates a new rdf from a string.
+ * \param[in] type   type to use
+ * \param[in] str string to use
+ * \return ldns_rdf* or NULL in case of an error
+ */
+ldns_rdf *ldns_rdf_new_frm_str(ldns_rdf_type type, const char *str);
+
+/**
+ * creates a new rdf from a file containing a string.
+ * \param[out] r the new rdf
+ * \param[in] type   type to use
+ * \param[in] fp the file pointer  to use
+ * \return LDNS_STATUS_OK or the error
+ */
+ldns_status ldns_rdf_new_frm_fp(ldns_rdf **r, ldns_rdf_type type, FILE *fp);
+
+/**
+ * creates a new rdf from a file containing a string.
+ * \param[out] r the new rdf
+ * \param[in] type   type to use
+ * \param[in] fp the file pointer  to use
+ * \param[in] line_nr pointer to an integer containing the current line number (for debugging purposes)
+ * \return LDNS_STATUS_OK or the error
+ */
+ldns_status ldns_rdf_new_frm_fp_l(ldns_rdf **r, ldns_rdf_type type, FILE *fp, int *line_nr);
+
+/* destroy functions */
+
+/**
+ * frees a rdf structure, leaving the
+ * data pointer intact.
+ * \param[in] rd the pointer to be freed
+ * \return void
+ */
+void ldns_rdf_free(ldns_rdf *rd);
+
+/**
+ * frees a rdf structure _and_ frees the
+ * data. rdf should be created with _new_frm_data
+ * \param[in] rd the rdf structure to be freed
+ * \return void
+ */
+void ldns_rdf_deep_free(ldns_rdf *rd);
+
+/* conversion functions */
+
+/**
+ * returns the rdf containing the native uint8_t repr.
+ * \param[in] type the ldns_rdf type to use
+ * \param[in] value the uint8_t to use
+ * \return ldns_rdf* with the converted value
+ */
+ldns_rdf *ldns_native2rdf_int8(ldns_rdf_type type, uint8_t value);
+
+/**
+ * returns the rdf containing the native uint16_t representation.
+ * \param[in] type the ldns_rdf type to use
+ * \param[in] value the uint16_t to use
+ * \return ldns_rdf* with the converted value
+ */
+ldns_rdf *ldns_native2rdf_int16(ldns_rdf_type type, uint16_t value);
+
+/**
+ * returns an rdf that contains the given int32 value.
+ *
+ * Because multiple rdf types can contain an int32, the
+ * type must be specified
+ * \param[in] type the ldns_rdf type to use
+ * \param[in] value the uint32_t to use
+ * \return ldns_rdf* with the converted value
+ */
+ldns_rdf *ldns_native2rdf_int32(ldns_rdf_type type, uint32_t value);
+
+/**
+ * returns an int16_data rdf that contains the data in the
+ * given array, preceded by an int16 specifying the length.
+ *
+ * The memory is copied, and an LDNS_RDF_TYPE_INT16DATA is returned
+ * \param[in] size the size of the data
+ * \param[in] *data pointer to the actual data
+ * \return ldns_rd* the rdf with the data
+ */
+ldns_rdf *ldns_native2rdf_int16_data(size_t size, uint8_t *data);
+
+/**
+ * reverses an rdf, only actually useful for AAAA and A records.
+ * The returned rdf has the type LDNS_RDF_TYPE_DNAME!
+ * \param[in] *rd rdf to be reversed
+ * \return the reversed rdf (a newly created rdf)
+ */
+ldns_rdf *ldns_rdf_address_reverse(ldns_rdf *rd);
+
+/**
+ * returns the native uint8_t representation from the rdf.
+ * \param[in] rd the ldns_rdf to operate on
+ * \return uint8_t the value extracted
+ */
+uint8_t 	ldns_rdf2native_int8(const ldns_rdf *rd);
+
+/**
+ * returns the native uint16_t representation from the rdf.
+ * \param[in] rd the ldns_rdf to operate on
+ * \return uint16_t the value extracted
+ */
+uint16_t	ldns_rdf2native_int16(const ldns_rdf *rd);
+
+/**
+ * returns the native uint32_t representation from the rdf.
+ * \param[in] rd the ldns_rdf to operate on
+ * \return uint32_t the value extracted
+ */
+uint32_t ldns_rdf2native_int32(const ldns_rdf *rd);
+
+/**
+ * returns the native time_t representation from the rdf.
+ * \param[in] rd the ldns_rdf to operate on
+ * \return time_t the value extracted (32 bits currently)
+ */
+time_t ldns_rdf2native_time_t(const ldns_rdf *rd);
+
+/**
+ * converts a ttl value (like 5d2h) to a long.
+ * \param[in] nptr the start of the string
+ * \param[out] endptr points to the last char in case of error
+ * \return the convert duration value
+ */
+uint32_t ldns_str2period(const char *nptr, const char **endptr);
+
+/**
+ * removes \\DDD, \\[space] and other escapes from the input.
+ * See RFC 1035, section 5.1.
+ * \param[in] word what to check
+ * \param[in] length the string
+ * \return ldns_status mesg
+ */
+ldns_status ldns_octet(char *word, size_t *length);
+
+/**
+ * clones a rdf structure. The data is copied.
+ * \param[in] rd rdf to be copied
+ * \return a new rdf structure
+ */
+ldns_rdf *ldns_rdf_clone(const ldns_rdf *rd);
+
+/**
+ * compares two rdf's on their wire formats.
+ * (To order dnames according to rfc4034, use ldns_dname_compare)
+ * \param[in] rd1 the first one
+ * \param[in] rd2 the second one
+ * \return 0 if equal
+ * \return -1 if rd1 comes before rd2
+ * \return +1 if rd2 comes before rd1
+ */
+int ldns_rdf_compare(const ldns_rdf *rd1, const ldns_rdf *rd2);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* LDNS_RDATA_H */
diff --git a/3rdParty/Ldns/src/include/ldns/resolver.h b/3rdParty/Ldns/src/include/ldns/resolver.h
new file mode 100644
index 0000000..f887aaf
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/resolver.h
@@ -0,0 +1,721 @@
+/*
+ * resolver.h
+ *
+ * DNS Resolver definitions
+ *
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2005-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+/**
+ * \file
+ *
+ * Defines the  ldns_resolver structure, a stub resolver that can send queries and parse answers.
+ *
+ */
+
+#ifndef LDNS_RESOLVER_H
+#define LDNS_RESOLVER_H
+
+#include <ldns/error.h>
+#include <ldns/common.h>
+#include <ldns/rr.h>
+#include <ldns/tsig.h>
+#include <ldns/rdata.h>
+#include <ldns/packet.h>
+#include <sys/time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Default location of the resolv.conf file */
+#define LDNS_RESOLV_CONF	"/etc/resolv.conf"
+/** Default location of the hosts file */
+#define LDNS_RESOLV_HOSTS	"/etc/hosts"
+
+#define LDNS_RESOLV_KEYWORD     -1
+#define LDNS_RESOLV_DEFDOMAIN	0
+#define LDNS_RESOLV_NAMESERVER	1
+#define LDNS_RESOLV_SEARCH	2
+#define LDNS_RESOLV_SORTLIST	3
+#define LDNS_RESOLV_OPTIONS	4
+#define LDNS_RESOLV_ANCHOR	5
+#define LDNS_RESOLV_KEYWORDS    6
+
+#define LDNS_RESOLV_INETANY		0
+#define LDNS_RESOLV_INET		1
+#define LDNS_RESOLV_INET6		2
+
+#define LDNS_RESOLV_RTT_INF             0       /* infinity */
+#define LDNS_RESOLV_RTT_MIN             1       /* reachable */
+
+/**
+ * DNS stub resolver structure
+ */
+struct ldns_struct_resolver
+{
+	/**  Port to send queries to */
+	uint16_t _port;
+
+	/** Array of nameservers to query (IP addresses or dnames) */
+	ldns_rdf **_nameservers;
+	/** Number of nameservers in \c _nameservers */
+	size_t _nameserver_count; /* how many do we have */
+
+	/**  Round trip time; 0 -> infinity. Unit: ms? */
+	size_t *_rtt;
+
+	/**  Wether or not to be recursive */
+	bool _recursive;
+
+	/**  Print debug information */
+	bool _debug;
+
+	/**  Default domain to add to non fully qualified domain names */
+	ldns_rdf *_domain;
+
+	/**  Searchlist array, add the names in this array if a query cannot be found */
+	ldns_rdf **_searchlist;
+
+	/** Number of entries in the searchlist array */
+	size_t _searchlist_count;
+
+	/**  Number of times to retry before giving up */
+	uint8_t _retry;
+	/**  Time to wait before retrying */
+	uint8_t _retrans;
+	/**  Use new fallback mechanism (try EDNS, then do TCP) */
+	bool _fallback;
+
+	/**  Whether to do DNSSEC */
+	bool _dnssec;
+	/**  Whether to set the CD bit on DNSSEC requests */
+	bool _dnssec_cd;
+	/** Optional trust anchors for complete DNSSEC validation */
+	ldns_rr_list * _dnssec_anchors;
+	/**  Whether to use tcp or udp (tcp if the value is true)*/
+	bool _usevc;
+	/**  Whether to ignore the tc bit */
+	bool _igntc;
+	/**  Whether to use ip6, 0->does not matter, 1 is IPv4, 2 is IPv6 */
+	uint8_t _ip6;
+	/**  If true append the default domain */
+	bool _defnames;
+	/**  If true apply the search list */
+	bool _dnsrch;
+	/**  Timeout for socket connections */
+	struct timeval _timeout;
+	/**  Only try the first nameserver, and return with an error directly if it fails */
+	bool _fail;
+	/**  Randomly choose a nameserver */
+	bool _random;
+	/** Keep some things to make AXFR possible */
+	int _socket;
+	/** Count the number of LDNS_RR_TYPE_SOA RRs we have seen so far
+	 * (the second one signifies the end of the AXFR)
+	 */
+	int _axfr_soa_count;
+	/* when axfring we get complete packets from the server
+	   but we want to give the caller 1 rr at a time, so
+	   keep the current pkt */
+        /** Packet currently handled when doing part of an AXFR */
+	ldns_pkt *_cur_axfr_pkt;
+	/** Counter for within the AXFR packets */
+	uint16_t _axfr_i;
+	/* EDNS0 available buffer size */
+	uint16_t _edns_udp_size;
+
+	/* Optional tsig key for signing queries,
+	outgoing messages are signed if and only if both are set
+	*/
+	/** Name of the key to use with TSIG, if _tsig_keyname and _tsig_keydata both contain values, outgoing messages are automatically signed with TSIG. */
+	char *_tsig_keyname;
+	/** Secret key data to use with TSIG, if _tsig_keyname and _tsig_keydata both contain values, outgoing messages are automatically signed with TSIG. */
+	char *_tsig_keydata;
+	/** TSIG signing algorithm */
+	char *_tsig_algorithm;
+};
+typedef struct ldns_struct_resolver ldns_resolver;
+
+/* prototypes */
+/* read access functions */
+
+/**
+ * Get the port the resolver should use
+ * \param[in] r the resolver
+ * \return the port number
+ */
+uint16_t ldns_resolver_port(const ldns_resolver *r);
+
+/**
+ * Is the resolver set to recurse
+ * \param[in] r the resolver
+ * \return true if so, otherwise false
+ */
+bool ldns_resolver_recursive(const ldns_resolver *r);
+
+/**
+ * Get the debug status of the resolver
+ * \param[in] r the resolver
+ * \return true if so, otherwise false
+ */
+bool ldns_resolver_debug(const ldns_resolver *r);
+
+/**
+ * Get the number of retries
+ * \param[in] r the resolver
+ * \return the number of retries
+ */
+uint8_t ldns_resolver_retry(const ldns_resolver *r);
+
+/**
+ * Get the retransmit interval
+ * \param[in] r the resolver
+ * \return the retransmit interval
+ */
+uint8_t ldns_resolver_retrans(const ldns_resolver *r);
+
+/**
+ * Get the truncation fallback status
+ * \param[in] r the resolver
+ * \return whether the truncation fallback mechanism is used
+ */
+bool ldns_resolver_fallback(const ldns_resolver *r);
+
+/**
+ * Does the resolver use ip6 or ip4
+ * \param[in] r the resolver
+ * \return 0: both, 1: ip4, 2:ip6
+ */
+uint8_t ldns_resolver_ip6(const ldns_resolver *r);
+
+/**
+ * Get the resolver's udp size
+ * \param[in] r the resolver
+ * \return the udp mesg size
+ */
+uint16_t ldns_resolver_edns_udp_size(const ldns_resolver *r);
+/**
+ * Does the resolver use tcp or udp
+ * \param[in] r the resolver
+ * \return true: tcp, false: udp
+ */
+bool ldns_resolver_usevc(const ldns_resolver *r);
+/**
+ * Does the resolver only try the first nameserver
+ * \param[in] r the resolver
+ * \return true: yes, fail, false: no, try the others
+ */
+bool ldns_resolver_fail(const ldns_resolver *r);
+/**
+ * Does the resolver apply default domain name
+ * \param[in] r the resolver
+ * \return true: yes, false: no
+ */
+bool ldns_resolver_defnames(const ldns_resolver *r);
+/**
+ * Does the resolver apply search list
+ * \param[in] r the resolver
+ * \return true: yes, false: no
+ */
+bool ldns_resolver_dnsrch(const ldns_resolver *r);
+/**
+ * Does the resolver do DNSSEC
+ * \param[in] r the resolver
+ * \return true: yes, false: no
+ */
+bool ldns_resolver_dnssec(const ldns_resolver *r);
+/**
+ * Does the resolver set the CD bit
+ * \param[in] r the resolver
+ * \return true: yes, false: no
+ */
+bool ldns_resolver_dnssec_cd(const ldns_resolver *r);
+/**
+ * Get the resolver's DNSSEC anchors
+ * \param[in] r the resolver
+ * \return an rr_list containg trusted DNSSEC anchors
+ */
+ldns_rr_list * ldns_resolver_dnssec_anchors(const ldns_resolver *r);
+/**
+ * Does the resolver ignore the TC bit (truncated)
+ * \param[in] r the resolver
+ * \return true: yes, false: no
+ */
+bool ldns_resolver_igntc(const ldns_resolver *r);
+/**
+ * Does the resolver randomize the nameserver before usage
+ * \param[in] r the resolver
+ * \return true: yes, false: no
+ */
+bool ldns_resolver_random(const ldns_resolver *r);
+/**
+ * How many nameserver are configured in the resolver
+ * \param[in] r the resolver
+ * \return number of nameservers
+ */
+size_t ldns_resolver_nameserver_count(const ldns_resolver *r);
+/**
+ * What is the default dname to add to relative queries
+ * \param[in] r the resolver
+ * \return the dname which is added
+ */
+ldns_rdf *ldns_resolver_domain(const ldns_resolver *r);
+/**
+ * What is the timeout on socket connections
+ * \param[in] r the resolver
+ * \return the timeout as struct timeval
+ */
+struct timeval ldns_resolver_timeout(const ldns_resolver *r);
+/**
+ * What is the searchlist as used by the resolver
+ * \param[in] r the resolver
+ * \return a ldns_rdf pointer to a list of the addresses
+ */
+ldns_rdf** ldns_resolver_searchlist(const ldns_resolver *r);
+/**
+ * Return the configured nameserver ip address
+ * \param[in] r the resolver
+ * \return a ldns_rdf pointer to a list of the addresses
+ */
+ldns_rdf** ldns_resolver_nameservers(const ldns_resolver *r);
+/**
+ * Return the used round trip times for the nameservers
+ * \param[in] r the resolver
+ * \return a size_t* pointer to the list.
+ * yet)
+ */
+size_t * ldns_resolver_rtt(const ldns_resolver *r);
+/**
+ * Return the used round trip time for a specific nameserver
+ * \param[in] r the resolver
+ * \param[in] pos the index to the nameserver
+ * \return the rrt, 0: infinite, >0: undefined (as of * yet)
+ */
+size_t ldns_resolver_nameserver_rtt(const ldns_resolver *r, size_t pos);
+/**
+ * Return the tsig keyname as used by the nameserver
+ * \param[in] r the resolver
+ * \return the name used.
+ */
+char *ldns_resolver_tsig_keyname(const ldns_resolver *r);
+/**
+ * Return the tsig algorithm as used by the nameserver
+ * \param[in] r the resolver
+ * \return the algorithm used.
+ */
+char *ldns_resolver_tsig_algorithm(const ldns_resolver *r);
+/**
+ * Return the tsig keydata as used by the nameserver
+ * \param[in] r the resolver
+ * \return the keydata used.
+ */
+char *ldns_resolver_tsig_keydata(const ldns_resolver *r);
+/**
+ * pop the last nameserver from the resolver.
+ * \param[in] r the resolver
+ * \return the popped address or NULL if empty
+ */
+ldns_rdf* ldns_resolver_pop_nameserver(ldns_resolver *r);
+
+/**
+ * Return the resolver's searchlist count
+ * \param[in] r the resolver
+ * \return the searchlist count
+ */
+size_t ldns_resolver_searchlist_count(const ldns_resolver *r);
+
+/* write access function */
+/**
+ * Set the port the resolver should use
+ * \param[in] r the resolver
+ * \param[in] p the port number
+ */
+void ldns_resolver_set_port(ldns_resolver *r, uint16_t p);
+
+/**
+ * Set the resolver recursion
+ * \param[in] r the resolver
+ * \param[in] b true: set to recurse, false: unset
+ */
+void ldns_resolver_set_recursive(ldns_resolver *r, bool b);
+
+/**
+ * Set the resolver debugging
+ * \param[in] r the resolver
+ * \param[in] b true: debug on: false debug off
+ */
+void ldns_resolver_set_debug(ldns_resolver *r, bool b);
+
+/**
+ * Incremental the resolver's nameserver count.
+ * \param[in] r the resolver
+ */
+void ldns_resolver_incr_nameserver_count(ldns_resolver *r);
+
+/**
+ * Decrement the resolver's nameserver count.
+ * \param[in] r the resolver
+ */
+void ldns_resolver_dec_nameserver_count(ldns_resolver *r);
+
+/**
+ * Set the resolver's nameserver count directly.
+ * \param[in] r the resolver
+ * \param[in] c the nameserver count
+ */
+void ldns_resolver_set_nameserver_count(ldns_resolver *r, size_t c);
+
+/**
+ * Set the resolver's nameserver count directly by using an rdf list
+ * \param[in] r the resolver
+ * \param[in] rd the resolver addresses
+ */
+void ldns_resolver_set_nameservers(ldns_resolver *r, ldns_rdf **rd);
+
+/**
+ * Set the resolver's default domain. This gets appended when no
+ * absolute name is given
+ * \param[in] r the resolver
+ * \param[in] rd the name to append
+ */
+void ldns_resolver_set_domain(ldns_resolver *r, ldns_rdf *rd);
+
+/**
+ * Set the resolver's socket time out when talking to remote hosts
+ * \param[in] r the resolver
+ * \param[in] timeout the timeout to use
+ */
+void ldns_resolver_set_timeout(ldns_resolver *r, struct timeval timeout);
+
+/**
+ * Push a new rd to the resolver's searchlist
+ * \param[in] r the resolver
+ * \param[in] rd to push
+ */
+void ldns_resolver_push_searchlist(ldns_resolver *r, ldns_rdf *rd);
+
+/**
+ * Whether the resolver uses the name set with _set_domain
+ * \param[in] r the resolver
+ * \param[in] b true: use the defaults, false: don't use them
+ */
+void ldns_resolver_set_defnames(ldns_resolver *r, bool b);
+
+/**
+ * Whether the resolver uses a virtual circuit (TCP)
+ * \param[in] r the resolver
+ * \param[in] b true: use TCP, false: don't use TCP
+ */
+void ldns_resolver_set_usevc(ldns_resolver *r, bool b);
+
+/**
+ * Whether the resolver uses the searchlist
+ * \param[in] r the resolver
+ * \param[in] b true: use the list, false: don't use the list
+ */
+void ldns_resolver_set_dnsrch(ldns_resolver *r, bool b);
+
+/**
+ * Whether the resolver uses DNSSEC
+ * \param[in] r the resolver
+ * \param[in] b true: use DNSSEC, false: don't use DNSSEC
+ */
+void ldns_resolver_set_dnssec(ldns_resolver *r, bool b);
+
+/**
+ * Whether the resolver uses the checking disable bit
+ * \param[in] r the resolver
+ * \param[in] b true: enable , false: don't use TCP
+ */
+void ldns_resolver_set_dnssec_cd(ldns_resolver *r, bool b);
+/**
+ * Set the resolver's DNSSEC anchor list directly. RRs should be of type DS or DNSKEY.
+ * \param[in] r the resolver
+ * \param[in] l the list of RRs to use as trust anchors
+ */
+void ldns_resolver_set_dnssec_anchors(ldns_resolver *r, ldns_rr_list * l);
+
+/**
+ * Push a new trust anchor to the resolver. It must be a DS or DNSKEY rr
+ * \param[in] r the resolver.
+ * \param[in] rr the RR to add as a trust anchor.
+ * \return a status
+ */
+ldns_status ldns_resolver_push_dnssec_anchor(ldns_resolver *r, ldns_rr *rr);
+
+/**
+ * Set the resolver retrans timeout (in seconds)
+ * \param[in] r the resolver
+ * \param[in] re the retransmission interval in seconds
+ */
+void ldns_resolver_set_retrans(ldns_resolver *r, uint8_t re);
+
+/**
+ * Set whether the resolvers truncation fallback mechanism is used
+ * when ldns_resolver_query() is called.
+ * \param[in] r the resolver
+ * \param[in] fallback whether to use the fallback mechanism
+ */
+void ldns_resolver_set_fallback(ldns_resolver *r, bool fallback);
+
+/**
+ * Set the resolver retry interval (in seconds)
+ * \param[in] r the resolver
+ * \param[in] re the retry interval
+ */
+void ldns_resolver_set_retry(ldns_resolver *r, uint8_t re);
+
+/**
+ * Whether the resolver uses ip6
+ * \param[in] r the resolver
+ * \param[in] i 0: no pref, 1: ip4, 2: ip6
+ */
+void ldns_resolver_set_ip6(ldns_resolver *r, uint8_t i);
+
+/**
+ * Whether or not to fail after one failed query
+ * \param[in] r the resolver
+ * \param[in] b true: yes fail, false: continue with next nameserver
+ */
+void ldns_resolver_set_fail(ldns_resolver *r, bool b);
+
+/**
+ * Whether or not to ignore the TC bit
+ * \param[in] r the resolver
+ * \param[in] b true: yes ignore, false: don't ignore
+ */
+void ldns_resolver_set_igntc(ldns_resolver *r, bool b);
+
+/**
+ * Set maximum udp size
+ * \param[in] r the resolver
+ * \param[in] s the udp max size
+ */
+void ldns_resolver_set_edns_udp_size(ldns_resolver *r, uint16_t s);
+
+/**
+ * Set the tsig key name
+ * \param[in] r the resolver
+ * \param[in] tsig_keyname the tsig key name
+ */
+void ldns_resolver_set_tsig_keyname(ldns_resolver *r, char *tsig_keyname);
+
+/**
+ * Set the tsig algorithm
+ * \param[in] r the resolver
+ * \param[in] tsig_algorithm the tsig algorithm
+ */
+void ldns_resolver_set_tsig_algorithm(ldns_resolver *r, char *tsig_algorithm);
+
+/**
+ * Set the tsig key data
+ * \param[in] r the resolver
+ * \param[in] tsig_keydata the key data
+ */
+void ldns_resolver_set_tsig_keydata(ldns_resolver *r, char *tsig_keydata);
+
+/**
+ * Set round trip time for all nameservers. Note this currently
+ * differentiates between: unreachable and reachable.
+ * \param[in] r the resolver
+ * \param[in] rtt a list with the times
+ */
+void ldns_resolver_set_rtt(ldns_resolver *r, size_t *rtt);
+
+/**
+ * Set round trip time for a specific nameserver. Note this
+ * currently differentiates between: unreachable and reachable.
+ * \param[in] r the resolver
+ * \param[in] pos the nameserver position
+ * \param[in] value the rtt
+ */
+void ldns_resolver_set_nameserver_rtt(ldns_resolver *r, size_t pos, size_t value);
+
+/**
+ * Should the nameserver list be randomized before each use
+ * \param[in] r the resolver
+ * \param[in] b: true: randomize, false: don't
+ */
+void ldns_resolver_set_random(ldns_resolver *r, bool b);
+
+/**
+ * Push a new nameserver to the resolver. It must be an IP
+ * address v4 or v6.
+ * \param[in] r the resolver
+ * \param[in] n the ip address
+ * \return ldns_status a status
+ */
+ldns_status ldns_resolver_push_nameserver(ldns_resolver *r, ldns_rdf *n);
+
+/**
+ * Push a new nameserver to the resolver. It must be an
+ * A or AAAA RR record type
+ * \param[in] r the resolver
+ * \param[in] rr the resource record
+ * \return ldns_status a status
+ */
+ldns_status ldns_resolver_push_nameserver_rr(ldns_resolver *r, ldns_rr *rr);
+
+/**
+ * Push a new nameserver rr_list to the resolver.
+ * \param[in] r the resolver
+ * \param[in] rrlist the rr_list to push
+ * \return ldns_status a status
+ */
+ldns_status ldns_resolver_push_nameserver_rr_list(ldns_resolver *r, ldns_rr_list *rrlist);
+
+/**
+ * Send the query for using the resolver and take the search list into account
+ * The search algorithm is as follows:
+ * If the name is absolute, try it as-is, otherwise apply the search list
+ * \param[in] *r operate using this resolver
+ * \param[in] *rdf query for this name
+ * \param[in] t query for this type (may be 0, defaults to A)
+ * \param[in] c query for this class (may be 0, default to IN)
+ * \param[in] flags the query flags
+ * \return ldns_pkt* a packet with the reply from the nameserver
+ */
+ldns_pkt* ldns_resolver_search(const ldns_resolver *r, const ldns_rdf *rdf, ldns_rr_type t, ldns_rr_class c, uint16_t flags);
+
+/**
+ * Form a query packet from a resolver and name/type/class combo
+ * \param[out] **q a pointer to a ldns_pkt pointer (initialized by this function)
+ * \param[in] *r operate using this resolver
+ * \param[in] *name query for this name
+ * \param[in] t query for this type (may be 0, defaults to A)
+ * \param[in] c query for this class (may be 0, default to IN)
+ * \param[in] f the query flags
+ * \return ldns_pkt* a packet with the reply from the nameserver
+ */
+ldns_status ldns_resolver_prepare_query_pkt(ldns_pkt **q, ldns_resolver *r, const  ldns_rdf *name, ldns_rr_type t, ldns_rr_class c, uint16_t f);
+
+/**
+ * Send the query for name as-is
+ * \param[out] **answer a pointer to a ldns_pkt pointer (initialized by this function)
+ * \param[in] *r operate using this resolver
+ * \param[in] *name query for this name
+ * \param[in] t query for this type (may be 0, defaults to A)
+ * \param[in] c query for this class (may be 0, default to IN)
+ * \param[in] flags the query flags
+ * \return ldns_pkt* a packet with the reply from the nameserver
+ */
+ldns_status ldns_resolver_send(ldns_pkt **answer, ldns_resolver *r, const ldns_rdf *name, ldns_rr_type t, ldns_rr_class c, uint16_t flags);
+
+/**
+ * Send the given packet to a nameserver
+ * \param[out] **answer a pointer to a ldns_pkt pointer (initialized by this function)
+ * \param[in] *r operate using this resolver
+ * \param[in] *query_pkt query
+ */
+ldns_status ldns_resolver_send_pkt(ldns_pkt **answer, ldns_resolver *r, ldns_pkt *query_pkt);
+
+/**
+ * Send a query to a nameserver
+ * \param[in] *r operate using this resolver
+ * \param[in] *name query for this name
+ * \param[in] *t query for this type (may be 0, defaults to A)
+ * \param[in] *c query for this class (may be 0, default to IN)
+ * \param[in] flags the query flags
+ * \return ldns_pkt* a packet with the reply from the nameserver
+ * if _defnames is true the default domain will be added
+ */
+ldns_pkt* ldns_resolver_query(const ldns_resolver *r, const ldns_rdf *name, ldns_rr_type t, ldns_rr_class c, uint16_t flags);
+
+
+/**
+ * Create a new resolver structure
+ * \return ldns_resolver* pointer to new strcture
+ */
+ldns_resolver* ldns_resolver_new(void);
+
+/**
+ * Create a resolver structure from a file like /etc/resolv.conf
+ * \param[out] r the new resolver
+ * \param[in] fp file pointer to create new resolver from
+ *      if NULL use /etc/resolv.conf
+ * \return LDNS_STATUS_OK or the error
+ */
+ldns_status ldns_resolver_new_frm_fp(ldns_resolver **r, FILE *fp);
+
+/**
+ * Create a resolver structure from a file like /etc/resolv.conf
+ * \param[out] r the new resolver
+ * \param[in] fp file pointer to create new resolver from
+ *      if NULL use /etc/resolv.conf
+ * \param[in] line_nr pointer to an integer containing the current line number (for debugging purposes)
+ * \return LDNS_STATUS_OK or the error
+ */
+ldns_status ldns_resolver_new_frm_fp_l(ldns_resolver **r, FILE *fp, int *line_nr);
+
+/**
+ * Configure a resolver by means of a resolv.conf file
+ * The file may be NULL in which case there will  be
+ * looked the RESOLV_CONF (defaults to /etc/resolv.conf
+ * \param[out] r the new resolver
+ * \param[in] filename the filename to use
+ * \return LDNS_STATUS_OK or the error
+ */
+ldns_status ldns_resolver_new_frm_file(ldns_resolver **r, const char *filename);
+
+/**
+ * Frees the allocated space for this resolver. Only frees the resolver pionter! You should probably be using _deep_free.
+ * \param res resolver to free
+ */
+void ldns_resolver_free(ldns_resolver *res);
+
+/**
+ * Frees the allocated space for this resolver and all it's data
+ * \param res resolver to free
+ */
+void ldns_resolver_deep_free(ldns_resolver *res);
+
+/**
+ * Get the next stream of RRs in a AXFR
+ * \param[in] resolver the resolver to use. First ldns_axfr_start() must be
+ * called
+ * \return ldns_rr the next RR from the AXFR stream
+ * After you get this returned RR (not NULL: on error), then check if 
+ * ldns_axfr_complete() is true to see if the zone transfer has completed.
+ */
+ldns_rr* ldns_axfr_next(ldns_resolver *resolver);
+
+/**
+ * Returns true if the axfr transfer has completed (i.e. 2 SOA RRs and no errors were encountered
+ * \param[in] resolver the resolver that is used
+ * \return bool true if axfr transfer was completed without error
+ */
+bool ldns_axfr_complete(const ldns_resolver *resolver);
+
+/**
+ * Returns a pointer to the last ldns_pkt that was sent by the server in the AXFR transfer
+ * uasable for instance to get the error code on failure
+ * \param[in] res the resolver that was used in the axfr transfer
+ * \return ldns_pkt the last packet sent
+ */
+ldns_pkt *ldns_axfr_last_pkt(const ldns_resolver *res);
+
+/**
+ * Randomize the nameserver list in the resolver
+ * \param[in] r the resolver
+ */
+void ldns_resolver_nameservers_randomize(ldns_resolver *r);
+
+/**
+ * Returns true if at least one of the provided keys is a trust anchor
+ * \param[in] r the current resolver
+ * \param[in] keys the keyset to check
+ * \param[out] trusted_keys the subset of trusted keys in the 'keys' rrset
+ * \return true if at least one of the provided keys is a configured trust anchor
+ */
+bool ldns_resolver_trusted_key(const ldns_resolver *r, ldns_rr_list * keys, ldns_rr_list * trusted_keys);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* LDNS_RESOLVER_H */
diff --git a/3rdParty/Ldns/src/include/ldns/rr.h b/3rdParty/Ldns/src/include/ldns/rr.h
new file mode 100644
index 0000000..2e1dd76
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/rr.h
@@ -0,0 +1,886 @@
+/*
+ * rr.h -  resource record definitions
+ *
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2005-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+/**
+ * \file
+ *
+ * Contains the definition of ldns_rr and functions to manipulate those.
+ */
+
+
+#ifndef LDNS_RR_H
+#define LDNS_RR_H
+
+#include <ldns/common.h>
+#include <ldns/rdata.h>
+#include <ldns/buffer.h>
+#include <ldns/error.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Maximum length of a dname label */
+#define LDNS_MAX_LABELLEN     63
+/** Maximum length of a complete dname */
+#define LDNS_MAX_DOMAINLEN    255
+/** Maximum number of pointers in 1 dname */
+#define LDNS_MAX_POINTERS	65535
+/** The bytes TTL, CLASS and length use up in an rr */
+#define LDNS_RR_OVERHEAD	10
+
+/* The first fields are 'common' and can be referenced instantly */
+#define LDNS_RDATA_FIELD_DESCRIPTORS_COMMON 52
+
+
+
+/**
+ *  The different RR classes.
+ */
+enum ldns_enum_rr_class
+{
+	/** the Internet */
+	LDNS_RR_CLASS_IN 	= 1,
+	/** Chaos class */
+	LDNS_RR_CLASS_CH	= 3,
+	/** Hesiod (Dyer 87) */
+	LDNS_RR_CLASS_HS	= 4,
+    /** None class, dynamic update */
+    LDNS_RR_CLASS_NONE      = 254,
+	/** Any class */
+	LDNS_RR_CLASS_ANY	= 255,
+
+	LDNS_RR_CLASS_FIRST     = 0,
+	LDNS_RR_CLASS_LAST      = 65535,
+	LDNS_RR_CLASS_COUNT     = LDNS_RR_CLASS_LAST - LDNS_RR_CLASS_FIRST + 1
+};
+typedef enum ldns_enum_rr_class ldns_rr_class;
+
+/**
+ *  Used to specify whether compression is allowed.
+ */
+enum ldns_enum_rr_compress
+{
+	/** compression is allowed */
+	LDNS_RR_COMPRESS,
+	LDNS_RR_NO_COMPRESS
+};
+typedef enum ldns_enum_rr_compress ldns_rr_compress;
+
+/**
+ * The different RR types.
+ */
+enum ldns_enum_rr_type
+{
+	/**  a host address */
+	LDNS_RR_TYPE_A = 1,
+	/**  an authoritative name server */
+	LDNS_RR_TYPE_NS = 2,
+	/**  a mail destination (Obsolete - use MX) */
+	LDNS_RR_TYPE_MD = 3,
+	/**  a mail forwarder (Obsolete - use MX) */
+	LDNS_RR_TYPE_MF = 4,
+	/**  the canonical name for an alias */
+	LDNS_RR_TYPE_CNAME = 5,
+	/**  marks the start of a zone of authority */
+	LDNS_RR_TYPE_SOA = 6,
+	/**  a mailbox domain name (EXPERIMENTAL) */
+	LDNS_RR_TYPE_MB = 7,
+	/**  a mail group member (EXPERIMENTAL) */
+	LDNS_RR_TYPE_MG = 8,
+	/**  a mail rename domain name (EXPERIMENTAL) */
+	LDNS_RR_TYPE_MR = 9,
+	/**  a null RR (EXPERIMENTAL) */
+	LDNS_RR_TYPE_NULL = 10,
+	/**  a well known service description */
+	LDNS_RR_TYPE_WKS = 11,
+	/**  a domain name pointer */
+	LDNS_RR_TYPE_PTR = 12,
+	/**  host information */
+	LDNS_RR_TYPE_HINFO = 13,
+	/**  mailbox or mail list information */
+	LDNS_RR_TYPE_MINFO = 14,
+	/**  mail exchange */
+	LDNS_RR_TYPE_MX = 15,
+	/**  text strings */
+	LDNS_RR_TYPE_TXT = 16,
+	/**  RFC1183 */
+	LDNS_RR_TYPE_RP = 17,
+	/**  RFC1183 */
+	LDNS_RR_TYPE_AFSDB = 18,
+	/**  RFC1183 */
+	LDNS_RR_TYPE_X25 = 19,
+	/**  RFC1183 */
+	LDNS_RR_TYPE_ISDN = 20,
+	/**  RFC1183 */
+	LDNS_RR_TYPE_RT = 21,
+	/**  RFC1706 */
+	LDNS_RR_TYPE_NSAP = 22,
+	/**  RFC1348 */
+	LDNS_RR_TYPE_NSAP_PTR = 23,
+	/**  2535typecode */
+	LDNS_RR_TYPE_SIG = 24,
+	/**  2535typecode */
+	LDNS_RR_TYPE_KEY = 25,
+	/**  RFC2163 */
+	LDNS_RR_TYPE_PX = 26,
+	/**  RFC1712 */
+	LDNS_RR_TYPE_GPOS = 27,
+	/**  ipv6 address */
+	LDNS_RR_TYPE_AAAA = 28,
+	/**  LOC record  RFC1876 */
+	LDNS_RR_TYPE_LOC = 29,
+	/**  2535typecode */
+	LDNS_RR_TYPE_NXT = 30,
+	/**  draft-ietf-nimrod-dns-01.txt */
+	LDNS_RR_TYPE_EID = 31,
+	/**  draft-ietf-nimrod-dns-01.txt */
+	LDNS_RR_TYPE_NIMLOC = 32,
+	/**  SRV record RFC2782 */
+	LDNS_RR_TYPE_SRV = 33,
+	/**  http://www.jhsoft.com/rfc/af-saa-0069.000.rtf */
+	LDNS_RR_TYPE_ATMA = 34,
+	/**  RFC2915 */
+	LDNS_RR_TYPE_NAPTR = 35,
+	/**  RFC2230 */
+	LDNS_RR_TYPE_KX = 36,
+	/**  RFC2538 */
+	LDNS_RR_TYPE_CERT = 37,
+	/**  RFC2874 */
+	LDNS_RR_TYPE_A6 = 38,
+	/**  RFC2672 */
+	LDNS_RR_TYPE_DNAME = 39,
+	/**  dnsind-kitchen-sink-02.txt */
+	LDNS_RR_TYPE_SINK = 40,
+	/**  Pseudo OPT record... */
+	LDNS_RR_TYPE_OPT = 41,
+	/**  RFC3123 */
+	LDNS_RR_TYPE_APL = 42,
+	/**  draft-ietf-dnsext-delegation */
+	LDNS_RR_TYPE_DS = 43,
+	/**  SSH Key Fingerprint */
+	LDNS_RR_TYPE_SSHFP = 44,
+	/**  draft-richardson-ipseckey-rr-11.txt */
+	LDNS_RR_TYPE_IPSECKEY = 45,
+	/**  draft-ietf-dnsext-dnssec-25 */
+	LDNS_RR_TYPE_RRSIG = 46,
+	LDNS_RR_TYPE_NSEC = 47,
+	LDNS_RR_TYPE_DNSKEY = 48,
+	LDNS_RR_TYPE_DHCID = 49,
+
+	LDNS_RR_TYPE_NSEC3 = 50,
+	LDNS_RR_TYPE_NSEC3PARAM = 51,
+	LDNS_RR_TYPE_NSEC3PARAMS = 51,
+
+        /** draft-ietf-dnsop-trust-history */
+        LDNS_RR_TYPE_TALINK = 58,
+
+	LDNS_RR_TYPE_SPF = 99,
+
+	LDNS_RR_TYPE_UINFO = 100,
+	LDNS_RR_TYPE_UID = 101,
+	LDNS_RR_TYPE_GID = 102,
+	LDNS_RR_TYPE_UNSPEC = 103,
+
+	LDNS_RR_TYPE_TSIG = 250,
+	LDNS_RR_TYPE_IXFR = 251,
+	LDNS_RR_TYPE_AXFR = 252,
+	/**  A request for mailbox-related records (MB, MG or MR) */
+	LDNS_RR_TYPE_MAILB = 253,
+	/**  A request for mail agent RRs (Obsolete - see MX) */
+	LDNS_RR_TYPE_MAILA = 254,
+	/**  any type (wildcard) */
+	LDNS_RR_TYPE_ANY = 255,
+
+	/* RFC 4431, 5074, DNSSEC Lookaside Validation */
+	LDNS_RR_TYPE_DLV = 32769,
+
+	/* type codes from nsec3 experimental phase
+	LDNS_RR_TYPE_NSEC3 = 65324,
+	LDNS_RR_TYPE_NSEC3PARAMS = 65325, */
+	LDNS_RR_TYPE_FIRST = 0,
+	LDNS_RR_TYPE_LAST  = 65535,
+	LDNS_RR_TYPE_COUNT = LDNS_RR_TYPE_LAST - LDNS_RR_TYPE_FIRST + 1
+};
+typedef enum ldns_enum_rr_type ldns_rr_type;
+
+/**
+ * Resource Record
+ *
+ * This is the basic DNS element that contains actual data
+ *
+ * From RFC1035:
+ * <pre>
+3.2.1. Format
+
+All RRs have the same top level format shown below:
+
+                                    1  1  1  1  1  1
+      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                                               |
+    /                                               /
+    /                      NAME                     /
+    |                                               |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                      TYPE                     |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                     CLASS                     |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                      TTL                      |
+    |                                               |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                   RDLENGTH                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
+    /                     RDATA                     /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+NAME            an owner name, i.e., the name of the node to which this
+                resource record pertains.
+
+TYPE            two octets containing one of the RR TYPE codes.
+
+CLASS           two octets containing one of the RR CLASS codes.
+
+TTL             a 32 bit signed integer that specifies the time interval
+                that the resource record may be cached before the source
+                of the information should again be consulted.  Zero
+                values are interpreted to mean that the RR can only be
+                used for the transaction in progress, and should not be
+                cached.  For example, SOA records are always distributed
+                with a zero TTL to prohibit caching.  Zero values can
+                also be used for extremely volatile data.
+
+RDLENGTH        an unsigned 16 bit integer that specifies the length in
+                octets of the RDATA field.
+
+RDATA           a variable length string of octets that describes the
+                resource.  The format of this information varies
+                according to the TYPE and CLASS of the resource record.
+ * </pre>
+ *
+ * The actual amount and type of rdata fields depend on the RR type of the
+ * RR, and can be found by using \ref ldns_rr_descriptor functions.
+ */
+struct ldns_struct_rr
+{
+	/**  Owner name, uncompressed */
+	ldns_rdf	*_owner;
+	/**  Time to live  */
+	uint32_t	_ttl;
+	/**  Number of data fields */
+	size_t	        _rd_count;
+	/**  the type of the RR. A, MX etc. */
+	ldns_rr_type	_rr_type;
+	/**  Class of the resource record.  */
+	ldns_rr_class	_rr_class;
+	/* everything in the rdata is in network order */
+	/**  The array of rdata's */
+	ldns_rdf	 **_rdata_fields;
+	/**  question rr [it would be nicer if thous is after _rd_count]
+		 ABI change: Fix this in next major release
+	 */
+	bool		_rr_question;
+};
+typedef struct ldns_struct_rr ldns_rr;
+
+/**
+ * List or Set of Resource Records
+ *
+ * Contains a list of rr's <br>
+ * No official RFC-like checks are made
+ */
+struct ldns_struct_rr_list
+{
+	size_t _rr_count;
+	size_t _rr_capacity;
+	ldns_rr **_rrs;
+};
+typedef struct ldns_struct_rr_list ldns_rr_list;
+
+/**
+ * Contains all information about resource record types.
+ *
+ * This structure contains, for all rr types, the rdata fields that are defined.
+ */
+struct ldns_struct_rr_descriptor
+{
+	/** Type of the RR that is described here */
+	ldns_rr_type    _type;
+	/** Textual name of the RR type.  */
+	const char *_name;
+	/** Minimum number of rdata fields in the RRs of this type.  */
+	uint8_t     _minimum;
+	/** Maximum number of rdata fields in the RRs of this type.  */
+	uint8_t     _maximum;
+	/** Wireformat specification for the rr, i.e. the types of rdata fields in their respective order. */
+	const ldns_rdf_type *_wireformat;
+	/** Special rdf types */
+	ldns_rdf_type _variable;
+	/** Specifies whether compression can be used for dnames in this RR type. */
+	ldns_rr_compress _compress;
+	/** The number of DNAMEs in the _wireformat string, for parsing. */
+	uint8_t _dname_count;
+};
+typedef struct ldns_struct_rr_descriptor ldns_rr_descriptor;
+
+/**
+ * creates a new rr structure.
+ * \return ldns_rr *
+ */
+ldns_rr* ldns_rr_new(void);
+
+/**
+ * creates a new rr structure, based on the given type.
+ * alloc enough space to hold all the rdf's
+ */
+ldns_rr* ldns_rr_new_frm_type(ldns_rr_type t);
+
+/**
+ * frees an RR structure
+ * \param[in] *rr the RR to be freed
+ * \return void
+ */
+void ldns_rr_free(ldns_rr *rr);
+
+/**
+ * creates an rr from a string.
+ * The string should be a fully filled-in rr, like
+ * ownername &lt;space&gt; TTL &lt;space&gt; CLASS &lt;space&gt;
+ * TYPE &lt;space&gt; RDATA.
+ * \param[out] n the rr to return
+ * \param[in] str the string to convert
+ * \param[in] default_ttl default ttl value for the rr.
+ *            If 0 DEF_TTL will be used
+ * \param[in] origin when the owner is relative add this.
+ *	The caller must ldns_rdf_deep_free it.
+ * \param[out] prev the previous ownername. if this value is not NULL,
+ * the function overwrites this with the ownername found in this
+ * string. The caller must then ldns_rdf_deep_free it.
+ * \return a status msg describing an error or LDNS_STATUS_OK
+ */
+ldns_status ldns_rr_new_frm_str(ldns_rr **n, const char *str,
+                                uint32_t default_ttl, ldns_rdf *origin,
+                                ldns_rdf **prev);
+
+/**
+ * creates an rr for the question section from a string, i.e.
+ * without RDATA fields
+ * Origin and previous RR functionality are the same as in
+ * ldns_rr_new_frm_str()
+ * \param[out] n the rr to return
+ * \param[in] str the string to convert
+ * \param[in] origin when the owner is relative add this.
+ *	The caller must ldns_rdf_deep_free it.
+ * \param prev the previous ownername. the function overwrite this with
+ * the current found ownername. The caller must ldns_rdf_deep_free it.
+ * \return a status msg describing an error or LDNS_STATUS_OK
+ */
+ldns_status ldns_rr_new_question_frm_str(ldns_rr **n, const char *str,
+                                ldns_rdf *origin, ldns_rdf **prev);
+
+/**
+ * creates a new rr from a file containing a string.
+ * \param[out] rr the new rr
+ * \param[in] fp the file pointer to use
+ * \param[in] default_ttl pointer to a default ttl for the rr. If NULL DEF_TTL will be used
+ *            the pointer will be updated if the file contains a $TTL directive
+ * \param[in] origin when the owner is relative add this
+ * 	      the pointer will be updated if the file contains a $ORIGIN directive
+ *	      The caller must ldns_rdf_deep_free it.
+ * \param[in] prev when the owner is whitespaces use this as the * ownername
+ *            the pointer will be updated after the call
+ *	      The caller must ldns_rdf_deep_free it.
+ * \return a ldns_status with an error or LDNS_STATUS_OK
+ */
+ldns_status ldns_rr_new_frm_fp(ldns_rr **rr, FILE *fp, uint32_t *default_ttl, ldns_rdf **origin, ldns_rdf **prev);
+
+/**
+ * creates a new rr from a file containing a string.
+ * \param[out] rr the new rr
+ * \param[in] fp the file pointer to use
+ * \param[in] default_ttl a default ttl for the rr. If NULL DEF_TTL will be used
+ *            the pointer will be updated if the file contains a $TTL directive
+ * \param[in] origin when the owner is relative add this
+ * 	      the pointer will be updated if the file contains a $ORIGIN directive
+ *	      The caller must ldns_rdf_deep_free it.
+ * \param[in] line_nr pointer to an integer containing the current line number (for debugging purposes)
+ * \param[in] prev when the owner is whitespaces use this as the * ownername
+ *            the pointer will be updated after the call
+ *	      The caller must ldns_rdf_deep_free it.
+ * \return a ldns_status with an error or LDNS_STATUS_OK
+ */
+ldns_status ldns_rr_new_frm_fp_l(ldns_rr **rr, FILE *fp, uint32_t *default_ttl, ldns_rdf **origin, ldns_rdf **prev, int *line_nr);
+
+/**
+ * sets the owner in the rr structure.
+ * \param[in] *rr rr to operate on
+ * \param[in] *owner set to this owner
+ * \return void
+ */
+void ldns_rr_set_owner(ldns_rr *rr, ldns_rdf *owner);
+
+/**
+ * sets the question flag in the rr structure.
+ * \param[in] *rr rr to operate on
+ * \param[in] question question flag
+ * \return void
+ */
+void ldns_rr_set_question(ldns_rr *rr, bool question);
+
+/**
+ * sets the ttl in the rr structure.
+ * \param[in] *rr rr to operate on
+ * \param[in] ttl set to this ttl
+ * \return void
+ */
+void ldns_rr_set_ttl(ldns_rr *rr, uint32_t ttl);
+
+/**
+ * sets the rd_count in the rr.
+ * \param[in] *rr rr to operate on
+ * \param[in] count set to this count
+ * \return void
+ */
+void ldns_rr_set_rd_count(ldns_rr *rr, size_t count);
+
+/**
+ * sets the type in the rr.
+ * \param[in] *rr rr to operate on
+ * \param[in] rr_type set to this type
+ * \return void
+ */
+void ldns_rr_set_type(ldns_rr *rr, ldns_rr_type rr_type);
+
+/**
+ * sets the class in the rr.
+ * \param[in] *rr rr to operate on
+ * \param[in] rr_class set to this class
+ * \return void
+ */
+void ldns_rr_set_class(ldns_rr *rr, ldns_rr_class rr_class);
+
+/**
+ * sets a rdf member, it will be set on the
+ * position given. The old value is returned, like pop.
+ * \param[in] *rr the rr to operate on
+ * \param[in] *f the rdf to set
+ * \param[in] position the position the set the rdf
+ * \return  the old value in the rr, NULL on failyre
+ */
+ldns_rdf* ldns_rr_set_rdf(ldns_rr *rr, const ldns_rdf *f, size_t position);
+
+/**
+ * sets rd_field member, it will be
+ * placed in the next available spot.
+ * \param[in] *rr rr to operate on
+ * \param[in] *f the data field member to set
+ * \return bool
+ */
+bool ldns_rr_push_rdf(ldns_rr *rr, const ldns_rdf *f);
+
+/**
+ * removes a rd_field member, it will be
+ * popped from the last position.
+ * \param[in] *rr rr to operate on
+ * \return rdf which was popped (null if nothing)
+ */
+ldns_rdf* ldns_rr_pop_rdf(ldns_rr *rr);
+
+/**
+ * returns the rdata field member counter.
+ * \param[in] *rr rr to operate on
+ * \param[in] nr the number of the rdf to return
+ * \return ldns_rdf *
+ */
+ldns_rdf* ldns_rr_rdf(const ldns_rr *rr, size_t nr);
+
+/**
+ * returns the owner name of an rr structure.
+ * \param[in] *rr rr to operate on
+ * \return ldns_rdf *
+ */
+ldns_rdf* ldns_rr_owner(const ldns_rr *rr);
+
+/**
+ * returns the question flag of an rr structure.
+ * \param[in] *rr rr to operate on
+ * \return bool true if question
+ */
+bool ldns_rr_is_question(const ldns_rr *rr);
+
+/**
+ * returns the ttl of an rr structure.
+ * \param[in] *rr the rr to read from
+ * \return the ttl of the rr
+ */
+uint32_t ldns_rr_ttl(const ldns_rr *rr);
+
+/**
+ * returns the rd_count of an rr structure.
+ * \param[in] *rr the rr to read from
+ * \return the rd count of the rr
+ */
+size_t ldns_rr_rd_count(const ldns_rr *rr);
+
+/**
+ * returns the type of the rr.
+ * \param[in] *rr the rr to read from
+ * \return the type of the rr
+ */
+ldns_rr_type ldns_rr_get_type(const ldns_rr *rr);
+
+/**
+ * returns the class of the rr.
+ * \param[in] *rr the rr to read from
+ * \return the class of the rr
+ */
+ldns_rr_class ldns_rr_get_class(const ldns_rr *rr);
+
+/* rr_lists */
+
+/**
+ * returns the number of rr's in an rr_list.
+ * \param[in] rr_list  the rr_list to read from
+ * \return the number of rr's
+ */
+size_t ldns_rr_list_rr_count(const ldns_rr_list *rr_list);
+
+/**
+ * sets the number of rr's in an rr_list.
+ * \param[in] rr_list the rr_list to set the count on
+ * \param[in] count the number of rr in this list
+ * \return void
+ */
+void ldns_rr_list_set_rr_count(ldns_rr_list *rr_list, size_t count);
+
+/**
+ * set a rr on a specific index in a ldns_rr_list
+ * \param[in] rr_list the rr_list to use
+ * \param[in] r the rr to set
+ * \param[in] count index into the rr_list
+ * \return the old rr which was stored in the rr_list, or
+ * NULL is the index was too large
+ * set a specific rr */
+ldns_rr * ldns_rr_list_set_rr(ldns_rr_list *rr_list, const ldns_rr *r, size_t count);
+
+/**
+ * returns a specific rr of an rrlist.
+ * \param[in] rr_list the rr_list to read from
+ * \param[in] nr return this rr
+ * \return the rr at position nr
+ */
+ldns_rr* ldns_rr_list_rr(const ldns_rr_list *rr_list, size_t nr);
+
+/**
+ * creates a new rr_list structure.
+ * \return a new rr_list structure
+ */
+ldns_rr_list* ldns_rr_list_new();
+
+/**
+ * frees an rr_list structure.
+ * \param[in] rr_list the list to free
+ */
+void ldns_rr_list_free(ldns_rr_list *rr_list);
+
+/**
+ * frees an rr_list structure and all rrs contained therein.
+ * \param[in] rr_list the list to free
+ */
+void ldns_rr_list_deep_free(ldns_rr_list *rr_list);
+
+/**
+ * concatenates two ldns_rr_lists together. This modifies
+ * *left (to extend it and add the pointers from *right).
+ * \param[in] left the leftside
+ * \param[in] right the rightside
+ * \return a left with right concatenated to it
+ */
+bool ldns_rr_list_cat(ldns_rr_list *left, ldns_rr_list *right);
+
+/**
+ * concatenates two ldns_rr_lists together, but makes clones of the rr's 
+ * (instead of pointer copying).
+ * \param[in] left the leftside
+ * \param[in] right the rightside
+ * \return a new rr_list with leftside/rightside concatenated
+ */
+ldns_rr_list* ldns_rr_list_cat_clone(ldns_rr_list *left, ldns_rr_list *right);
+
+/**
+ * pushes an rr to an rrlist.
+ * \param[in] rr_list the rr_list to push to 
+ * \param[in] rr the rr to push 
+ * \return false on error, otherwise true
+ */
+bool ldns_rr_list_push_rr(ldns_rr_list *rr_list, const ldns_rr *rr);
+
+/**
+ * pushes an rr_list to an rrlist.
+ * \param[in] rr_list the rr_list to push to 
+ * \param[in] push_list the rr_list to push 
+ * \return false on error, otherwise true
+ */
+bool ldns_rr_list_push_rr_list(ldns_rr_list *rr_list, const ldns_rr_list *push_list);
+
+/**
+ * pops the last rr from an rrlist.
+ * \param[in] rr_list the rr_list to pop from
+ * \return NULL if nothing to pop. Otherwise the popped RR
+ */
+ldns_rr* ldns_rr_list_pop_rr(ldns_rr_list *rr_list);
+
+/**
+ * pops an  rr_list of size s from an rrlist.
+ * \param[in] rr_list the rr_list to pop from
+ * \param[in] size the number of rr's to pop 
+ * \return NULL if nothing to pop. Otherwise the popped rr_list
+ */
+ldns_rr_list* ldns_rr_list_pop_rr_list(ldns_rr_list *rr_list, size_t size);
+
+/**
+ * returns true if the given rr is one of the rrs in the
+ * list, or if it is equal to one
+ * \param[in] rr_list the rr_list to check
+ * \param[in] rr the rr to check
+ * \return true if rr_list contains rr, false otherwise
+ */
+bool ldns_rr_list_contains_rr(const ldns_rr_list *rr_list, ldns_rr *rr); 
+
+/**
+ * checks if an rr_list is a rrset.
+ * \param[in] rr_list the rr_list to check
+ * \return true if it is an rrset otherwise false
+ */
+bool ldns_is_rrset(ldns_rr_list *rr_list);
+
+/**
+ * pushes an rr to an rrset (which really are rr_list's).
+ * \param[in] *rr_list the rrset to push the rr to
+ * \param[in] *rr the rr to push
+ * \return true if the push succeeded otherwise false
+ */
+bool ldns_rr_set_push_rr(ldns_rr_list *rr_list, ldns_rr *rr);
+
+/**
+ * pops the last rr from an rrset. This function is there only
+ * for the symmetry.
+ * \param[in] rr_list the rr_list to pop from
+ * \return NULL if nothing to pop. Otherwise the popped RR
+ *
+ */
+ldns_rr* ldns_rr_set_pop_rr(ldns_rr_list *rr_list);
+
+/**
+ * pops the first rrset from the list,
+ * the list must be sorted, so that all rr's from each rrset
+ * are next to each other
+ */
+ldns_rr_list *ldns_rr_list_pop_rrset(ldns_rr_list *rr_list);
+
+
+/**
+ * retrieves a rrtype by looking up its name.
+ * \param[in] name a string with the name
+ * \return the type which corresponds with the name
+ */
+ldns_rr_type ldns_get_rr_type_by_name(const char *name);
+
+/**
+ * retrieves a class by looking up its name.
+ * \param[in] name string with the name
+ * \return the cass which corresponds with the name
+ */
+ldns_rr_class ldns_get_rr_class_by_name(const char *name);
+
+/**
+ * clones a rr and all its data
+ * \param[in] rr the rr to clone
+ * \return the new rr or NULL on failure
+ */
+ldns_rr* ldns_rr_clone(const ldns_rr *rr);
+
+/**
+ * clones an rrlist.
+ * \param[in] rrlist the rrlist to clone
+ * \return the cloned rr list
+ */
+ldns_rr_list* ldns_rr_list_clone(const ldns_rr_list *rrlist);
+
+/**
+ * sorts an rr_list (canonical wire format). the sorting is done inband.
+ * \param[in] unsorted the rr_list to be sorted
+ * \return void
+ */
+void ldns_rr_list_sort(ldns_rr_list *unsorted);
+
+/**
+ * compares two rrs. The TTL is not looked at.
+ * \param[in] rr1 the first one
+ * \param[in] rr2 the second one
+ * \return 0 if equal
+ *         -1 if rr1 comes before rr2
+ *         +1 if rr2 comes before rr1
+ */
+int ldns_rr_compare(const ldns_rr *rr1, const ldns_rr *rr2);
+
+/**
+ * compares two rrs, up to the rdata.
+ * \param[in] rr1 the first one
+ * \param[in] rr2 the second one
+ * \return 0 if equal
+ *         -1 if rr1 comes before rr2
+ *         +1 if rr2 comes before rr1
+ */
+int ldns_rr_compare_no_rdata(const ldns_rr *rr1, const ldns_rr *rr2);
+
+/**
+ * compares the wireformat of two rrs, contained in the given buffers.
+ * \param[in] rr1_buf the first one
+ * \param[in] rr2_buf the second one
+ * \return 0 if equal
+ *         -1 if rr1_buf comes before rr2_buf
+ *         +1 if rr2_buf comes before rr1_buf
+ */
+int ldns_rr_compare_wire(ldns_buffer *rr1_buf, ldns_buffer *rr2_buf);
+
+/**
+ * returns true of the given rr's are equal.
+ * Also returns true if one record is a DS that represents the
+ * same DNSKEY record as the other record
+ * \param[in] rr1 the first rr
+ * \param[in] rr2 the second rr
+ * \return true if equal otherwise false
+ */
+bool ldns_rr_compare_ds(const ldns_rr *rr1, const ldns_rr *rr2);
+
+/**
+ * compares two rr listss.
+ * \param[in] rrl1 the first one
+ * \param[in] rrl2 the second one
+ * \return 0 if equal
+ *         -1 if rrl1 comes before rrl2
+ *         +1 if rrl2 comes before rrl1
+ */
+int ldns_rr_list_compare(const ldns_rr_list *rrl1, const ldns_rr_list *rrl2);
+
+/** 
+ * calculates the uncompressed size of an RR.
+ * \param[in] r the rr to operate on
+ * \return size of the rr
+ */
+size_t ldns_rr_uncompressed_size(const ldns_rr *r);
+
+/** 
+ * converts each dname in a rr to its canonical form.
+ * \param[in] rr the rr to work on
+ * \return void
+ */
+void ldns_rr2canonical(ldns_rr *rr);
+
+/** 
+ * converts each dname in each rr in a rr_list to its canonical form.
+ * \param[in] rr_list the rr_list to work on
+ * \return void
+ */
+void ldns_rr_list2canonical(ldns_rr_list *rr_list);
+
+/** 
+ * counts the number of labels of the ownername.
+ * \param[in] rr count the labels of this rr
+ * \return the number of labels
+ */
+uint8_t ldns_rr_label_count(ldns_rr *rr);
+
+/**
+ * returns the resource record descriptor for the given rr type.
+ *
+ * \param[in] type the type value of the rr type
+ *\return the ldns_rr_descriptor for this type
+ */
+const ldns_rr_descriptor *ldns_rr_descript(uint16_t type);
+
+/**
+ * returns the minimum number of rdata fields of the rr type this descriptor describes.
+ *
+ * \param[in]  descriptor for an rr type
+ * \return the minimum number of rdata fields
+ */
+size_t ldns_rr_descriptor_minimum(const ldns_rr_descriptor *descriptor);
+
+/**
+ * returns the maximum number of rdata fields of the rr type this descriptor describes.
+ *
+ * \param[in]  descriptor for an rr type
+ * \return the maximum number of rdata fields
+ */
+size_t ldns_rr_descriptor_maximum(const ldns_rr_descriptor *descriptor);
+
+/**
+ * returns the rdf type for the given rdata field number of the rr type for the given descriptor.
+ *
+ * \param[in] descriptor for an rr type
+ * \param[in] field the field number
+ * \return the rdf type for the field
+ */
+ldns_rdf_type ldns_rr_descriptor_field_type(const ldns_rr_descriptor *descriptor, size_t field);
+
+/**
+ * Return the rr_list which matches the rdf at position field. Think
+ * type-covered stuff for RRSIG
+ * 
+ * \param[in] l the rr_list to look in
+ * \param[in] r the rdf to use for the comparison
+ * \param[in] pos at which position can we find the rdf
+ * 
+ * \return a new rr list with only the RRs that match 
+ *
+ */
+ldns_rr_list *ldns_rr_list_subtype_by_rdf(ldns_rr_list *l, ldns_rdf *r, size_t pos);
+
+/**
+ * convert an rdf of type LDNS_RDF_TYPE_TYPE to an actual
+ * LDNS_RR_TYPE. This is usefull in the case when inspecting
+ * the rrtype covered field of an RRSIG.
+ * \param[in] rd the rdf to look at
+ * \return a ldns_rr_type with equivalent LDNS_RR_TYPE
+ *
+ */
+ldns_rr_type    ldns_rdf2rr_type(const ldns_rdf *rd);
+
+/**
+ * Returns the type of the first element of the RR
+ * If there are no elements present, 0 is returned
+ * 
+ * \param[in] rr_list The rr list
+ * \return rr_type of the first element, or 0 if the list is empty
+ */
+ldns_rr_type
+ldns_rr_list_type(const ldns_rr_list *rr_list);
+
+/**
+ * Returns the owner domain name rdf of the first element of the RR
+ * If there are no elements present, NULL is returned
+ * 
+ * \param[in] rr_list The rr list
+ * \return dname of the first element, or NULL if the list is empty
+ */
+ldns_rdf *
+ldns_rr_list_owner(const ldns_rr_list *rr_list);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LDNS_RR_H */
diff --git a/3rdParty/Ldns/src/include/ldns/rr_functions.h b/3rdParty/Ldns/src/include/ldns/rr_functions.h
new file mode 100644
index 0000000..3db3b3d
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/rr_functions.h
@@ -0,0 +1,363 @@
+/*
+ * rr_functions.h
+ *
+ * the .h file with defs for the per rr
+ * functions
+ *
+ * a Net::DNS like library for C
+ * 
+ * (c) NLnet Labs, 2005-2006
+ * 
+ * See the file LICENSE for the license
+ */
+#ifndef LDNS_RR_FUNCTIONS_H
+#define LDNS_RR_FUNCTIONS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file
+ *
+ * Defines some extra convenience functions for ldns_rr structures
+ */
+
+/* A / AAAA */
+/**
+ * returns the address of a LDNS_RR_TYPE_A rr
+ * \param[in] r the resource record
+ * \return a ldns_rdf* with the address or NULL on failure
+ */
+ldns_rdf* ldns_rr_a_address(const ldns_rr *r);
+
+/**
+ * sets the address of a LDNS_RR_TYPE_A rr
+ * \param[in] r the rr to use
+ * \param[in] f the address to set
+ * \return true on success, false otherwise
+ */
+bool ldns_rr_a_set_address(ldns_rr *r, ldns_rdf *f);
+
+/* NS */
+/**
+ * returns the name of a LDNS_RR_TYPE_NS rr
+ * \param[in] r the resource record
+ * \return a ldns_rdf* with the name or NULL on failure
+ */
+ldns_rdf* ldns_rr_ns_nsdname(const ldns_rr *r);
+
+/* MX */
+/**
+ * returns the mx pref. of a LDNS_RR_TYPE_MX rr
+ * \param[in] r the resource record
+ * \return a ldns_rdf* with the preference or NULL on failure
+ */
+ldns_rdf* ldns_rr_mx_preference(const ldns_rr *r);
+/**
+ * returns the mx host of a LDNS_RR_TYPE_MX rr
+ * \param[in] r the resource record
+ * \return a ldns_rdf* with the name of the MX host or NULL on failure
+ */
+ldns_rdf* ldns_rr_mx_exchange(const ldns_rr *r);
+
+/* RRSIG */
+/**
+ * returns the type covered of a LDNS_RR_TYPE_RRSIG rr
+ * \param[in] r the resource record
+ * \return a ldns_rdf* with the type covered or NULL on failure
+ */
+ldns_rdf* ldns_rr_rrsig_typecovered(const ldns_rr *r);
+/**
+ * sets the typecovered of a LDNS_RR_TYPE_RRSIG rr
+ * \param[in] r the rr to use
+ * \param[in] f the typecovered to set
+ * \return true on success, false otherwise
+ */
+bool ldns_rr_rrsig_set_typecovered(ldns_rr *r, ldns_rdf *f);
+/**
+ * returns the algorithm of a LDNS_RR_TYPE_RRSIG RR
+ * \param[in] r the resource record
+ * \return a ldns_rdf* with the algorithm or NULL on failure
+ */
+ldns_rdf* ldns_rr_rrsig_algorithm(const ldns_rr *r);
+/**
+ * sets the algorithm of a LDNS_RR_TYPE_RRSIG rr
+ * \param[in] r the rr to use
+ * \param[in] f the algorithm to set
+ * \return true on success, false otherwise
+ */
+bool ldns_rr_rrsig_set_algorithm(ldns_rr *r, ldns_rdf *f);
+/**
+ * returns the number of labels of a LDNS_RR_TYPE_RRSIG RR
+ * \param[in] r the resource record
+ * \return a ldns_rdf* with the number of labels or NULL on failure
+ */
+ldns_rdf *ldns_rr_rrsig_labels(const ldns_rr *r);
+/**
+ * sets the number of labels of a LDNS_RR_TYPE_RRSIG rr
+ * \param[in] r the rr to use
+ * \param[in] f the number of labels to set
+ * \return true on success, false otherwise
+ */
+bool ldns_rr_rrsig_set_labels(ldns_rr *r, ldns_rdf *f);
+/**
+ * returns the original TTL of a LDNS_RR_TYPE_RRSIG RR
+ * \param[in] r the resource record
+ * \return a ldns_rdf* with the original TTL or NULL on failure
+ */
+ldns_rdf* ldns_rr_rrsig_origttl(const ldns_rr *r);
+/**
+ * sets the original TTL of a LDNS_RR_TYPE_RRSIG rr
+ * \param[in] r the rr to use
+ * \param[in] f the original TTL to set
+ * \return true on success, false otherwise
+ */
+bool ldns_rr_rrsig_set_origttl(ldns_rr *r, ldns_rdf *f);
+/**
+ * returns the expiration time of a LDNS_RR_TYPE_RRSIG RR
+ * \param[in] r the resource record
+ * \return a ldns_rdf* with the expiration time or NULL on failure
+ */
+ldns_rdf* ldns_rr_rrsig_expiration(const ldns_rr *r);
+/**
+ * sets the expireation date of a LDNS_RR_TYPE_RRSIG rr
+ * \param[in] r the rr to use
+ * \param[in] f the expireation date to set
+ * \return true on success, false otherwise
+ */
+bool ldns_rr_rrsig_set_expiration(ldns_rr *r, ldns_rdf *f);
+/**
+ * returns the inception time of a LDNS_RR_TYPE_RRSIG RR
+ * \param[in] r the resource record
+ * \return a ldns_rdf* with the inception time or NULL on failure
+ */
+ldns_rdf* ldns_rr_rrsig_inception(const ldns_rr *r);
+/**
+ * sets the inception date of a LDNS_RR_TYPE_RRSIG rr
+ * \param[in] r the rr to use
+ * \param[in] f the inception date to set
+ * \return true on success, false otherwise
+ */
+bool ldns_rr_rrsig_set_inception(ldns_rr *r, ldns_rdf *f);
+/**
+ * returns the keytag of a LDNS_RR_TYPE_RRSIG RR
+ * \param[in] r the resource record
+ * \return a ldns_rdf* with the keytag or NULL on failure
+ */
+ldns_rdf* ldns_rr_rrsig_keytag(const ldns_rr *r);
+/**
+ * sets the keytag of a LDNS_RR_TYPE_RRSIG rr
+ * \param[in] r the rr to use
+ * \param[in] f the keytag to set
+ * \return true on success, false otherwise
+ */
+bool ldns_rr_rrsig_set_keytag(ldns_rr *r, ldns_rdf *f);
+/**
+ * returns the signers name of a LDNS_RR_TYPE_RRSIG RR
+ * \param[in] r the resource record
+ * \return a ldns_rdf* with the signers name or NULL on failure
+ */
+ldns_rdf* ldns_rr_rrsig_signame(const ldns_rr *r);
+/**
+ * sets the signers name of a LDNS_RR_TYPE_RRSIG rr
+ * \param[in] r the rr to use
+ * \param[in] f the signers name to set
+ * \return true on success, false otherwise
+ */
+bool ldns_rr_rrsig_set_signame(ldns_rr *r, ldns_rdf *f);
+/**
+ * returns the signature data of a LDNS_RR_TYPE_RRSIG RR
+ * \param[in] r the resource record
+ * \return a ldns_rdf* with the signature data or NULL on failure
+ */
+ldns_rdf* ldns_rr_rrsig_sig(const ldns_rr *r);
+/**
+ * sets the signature data of a LDNS_RR_TYPE_RRSIG rr
+ * \param[in] r the rr to use
+ * \param[in] f the signature data to set
+ * \return true on success, false otherwise
+ */
+bool ldns_rr_rrsig_set_sig(ldns_rr *r, ldns_rdf *f);
+
+/* DNSKEY */
+/**
+ * returns the flags of a LDNS_RR_TYPE_DNSKEY rr
+ * \param[in] r the resource record
+ * \return a ldns_rdf* with the flags or NULL on failure
+ */
+ldns_rdf* ldns_rr_dnskey_flags(const ldns_rr *r);
+/**
+ * sets the flags of a LDNS_RR_TYPE_DNSKEY rr
+ * \param[in] r the rr to use
+ * \param[in] f the flags to set
+ * \return true on success, false otherwise
+ */
+bool ldns_rr_dnskey_set_flags(ldns_rr *r, ldns_rdf *f);
+/**
+ * returns the protocol of a LDNS_RR_TYPE_DNSKEY rr
+ * \param[in] r the resource record
+ * \return a ldns_rdf* with the protocol or NULL on failure
+ */
+ldns_rdf* ldns_rr_dnskey_protocol(const ldns_rr *r);
+/**
+ * sets the protocol of a LDNS_RR_TYPE_DNSKEY rr
+ * \param[in] r the rr to use
+ * \param[in] f the protocol to set
+ * \return true on success, false otherwise
+ */
+bool ldns_rr_dnskey_set_protocol(ldns_rr *r, ldns_rdf *f);
+/**
+ * returns the algorithm of a LDNS_RR_TYPE_DNSKEY rr
+ * \param[in] r the resource record
+ * \return a ldns_rdf* with the algorithm or NULL on failure
+ */
+ldns_rdf* ldns_rr_dnskey_algorithm(const ldns_rr *r);
+/**
+ * sets the algorithm of a LDNS_RR_TYPE_DNSKEY rr
+ * \param[in] r the rr to use
+ * \param[in] f the algorithm to set
+ * \return true on success, false otherwise
+ */
+bool ldns_rr_dnskey_set_algorithm(ldns_rr *r, ldns_rdf *f);
+/**
+ * returns the key data of a LDNS_RR_TYPE_DNSKEY rr
+ * \param[in] r the resource record
+ * \return a ldns_rdf* with the key data or NULL on failure
+ */
+ldns_rdf* ldns_rr_dnskey_key(const ldns_rr *r);
+/**
+ * sets the key data of a LDNS_RR_TYPE_DNSKEY rr
+ * \param[in] r the rr to use
+ * \param[in] f the key data to set
+ * \return true on success, false otherwise
+ */
+bool ldns_rr_dnskey_set_key(ldns_rr *r, ldns_rdf *f);
+
+/**
+ * get the length of the keydata in bits
+ * \param[in] keydata the raw key data
+ * \param[in] len the length of the keydata
+ * \param[in] alg the cryptographic algorithm this is a key for
+ * \return the keysize in bits, or 0 on error
+ */
+size_t ldns_rr_dnskey_key_size_raw(const unsigned char *keydata,
+                                   const size_t len,
+                                   const ldns_algorithm alg);
+
+/**
+ * get the length of the keydata in bits
+ * \param[in] key the key rr to use
+ * \return the keysize in bits
+ */
+size_t ldns_rr_dnskey_key_size(const ldns_rr *key);
+
+/**
+ * The type of function to be passed to ldns_rr_soa_increment_func,
+ * ldns_rr_soa_increment_func_data or ldns_rr_soa_increment_int.
+ * The function will be called with as the first argument the current serial
+ * number of the SOA RR to be updated, and as the second argument a value
+ * given when calling ldns_rr_soa_increment_func_data or 
+ * ldns_rr_soa_increment_int. With ldns_rr_soa_increment_int the pointer
+ * value holds the integer value passed to ldns_rr_soa_increment_int,
+ * and it should be cast to intptr_t to be used as an integer by the
+ * serial modifying function.
+ */
+typedef uint32_t (*ldns_soa_serial_increment_func_t)(uint32_t, void*);
+
+/**
+ * Function to be used with dns_rr_soa_increment_func_int, to set the soa
+ * serial number. 
+ * \param[in] _ the (unused) current serial number.
+ * \param[in] data the serial number to be set.
+ */
+uint32_t ldns_soa_serial_identity(uint32_t _, void *data);
+
+/**
+ * Function to be used with dns_rr_soa_increment_func, to increment the soa
+ * serial number with one. 
+ * \param[in] s the current serial number.
+ * \param[in] _ unused.
+ */
+uint32_t ldns_soa_serial_increment(uint32_t s, void *_);
+
+/**
+ * Function to be used with dns_rr_soa_increment_func_int, to increment the soa
+ * serial number with a certain amount. 
+ * \param[in] s the current serial number.
+ * \param[in] data the amount to add to the current serial number.
+ */
+uint32_t ldns_soa_serial_increment_by(uint32_t s, void *data);
+
+/**
+ * Function to be used with ldns_rr_soa_increment_func or 
+ * ldns_rr_soa_increment_func_int to set the soa serial to the number of 
+ * seconds since unix epoch (1-1-1970 00:00). 
+ * When data is given (i.e. the function is called via
+ * ldns_rr_soa_increment_func_int), it is used as the current time. 
+ * When the resulting serial number is smaller than the current serial number,
+ * the current serial number is increased by one.
+ * \param[in] s the current serial number.
+ * \param[in] data the time in seconds since 1-1-1970 00:00
+ */
+uint32_t ldns_soa_serial_unixtime(uint32_t s, void *data);
+
+/**
+ * Function to be used with ldns_rr_soa_increment_func or 
+ * ldns_rr_soa_increment_func_int to set the soa serial to the current date
+ * succeeded by a two digit iteration (datecounter).
+ * When data is given (i.e. the function is called via
+ * ldns_rr_soa_increment_func_int), it is used as the current time. 
+ * When the resulting serial number is smaller than the current serial number,
+ * the current serial number is increased by one.
+ * \param[in] s the current serial number.
+ * \param[in] data the time in seconds since 1-1-1970 00:00
+ */
+uint32_t ldns_soa_serial_datecounter(uint32_t s, void *data);
+
+/**
+ * Increment the serial number of the given SOA by one.
+ * \param[in] soa The soa rr to be incremented
+ */
+void ldns_rr_soa_increment(
+		ldns_rr *soa);
+
+/**
+ * Increment the serial number of the given SOA with the given function.
+ * Included functions to be used here are: ldns_rr_soa_increment, 
+ * ldns_soa_serial_unixtime and ldns_soa_serial_datecounter.
+ * \param[in] soa The soa rr to be incremented
+ * \param[in] f the function to use to increment the soa rr.
+ */
+void ldns_rr_soa_increment_func(
+		ldns_rr *soa, ldns_soa_serial_increment_func_t f);
+
+/**
+ * Increment the serial number of the given SOA with the given function
+ * passing it the given data argument.
+ * \param[in] soa The soa rr to be incremented
+ * \param[in] f the function to use to increment the soa rr.
+ * \param[in] data this argument will be passed to f as the second argument.
+ */
+void ldns_rr_soa_increment_func_data(
+		ldns_rr *soa, ldns_soa_serial_increment_func_t f, void *data);
+
+/**
+ * Increment the serial number of the given SOA with the given function
+ * using data as an argument for the function.
+ * Included functions to be used here are: ldns_soa_serial_identity,
+ * ldns_rr_soa_increment_by, ldns_soa_serial_unixtime and 
+ * ldns_soa_serial_datecounter.
+ * \param[in] soa The soa rr to be incremented
+ * \param[in] f the function to use to increment the soa rr.
+ * \param[in] data this argument will be passed to f as the second argument
+ *                 (by casting it to void*).
+ */
+void ldns_rr_soa_increment_func_int(
+		ldns_rr *soa, ldns_soa_serial_increment_func_t f, int data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LDNS_RR_FUNCTIONS_H */
diff --git a/3rdParty/Ldns/src/include/ldns/sha1.h b/3rdParty/Ldns/src/include/ldns/sha1.h
new file mode 100644
index 0000000..d5b1082
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/sha1.h
@@ -0,0 +1,38 @@
+#ifndef LDNS_SHA1_H
+#define LDNS_SHA1_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ 
+#define LDNS_SHA1_BLOCK_LENGTH               64
+#define LDNS_SHA1_DIGEST_LENGTH              20
+
+typedef struct {
+        uint32_t       state[5];
+        uint64_t       count;
+        unsigned char   buffer[LDNS_SHA1_BLOCK_LENGTH];
+} ldns_sha1_ctx;
+  
+void ldns_sha1_init(ldns_sha1_ctx * context);
+void ldns_sha1_transform(uint32_t state[5], const unsigned char buffer[LDNS_SHA1_BLOCK_LENGTH]);
+void ldns_sha1_update(ldns_sha1_ctx *context, const unsigned char *data, unsigned int len);
+void ldns_sha1_final(unsigned char digest[LDNS_SHA1_DIGEST_LENGTH], ldns_sha1_ctx *context);
+
+/**
+ * Convenience function to digest a fixed block of data at once.
+ *
+ * \param[in] data the data to digest
+ * \param[in] data_len the length of data in bytes
+ * \param[out] digest the length of data in bytes
+ *             This pointer MUST have LDNS_SHA1_DIGEST_LENGTH bytes
+ *             available
+ * \return the SHA1 digest of the given data
+ */
+unsigned char *ldns_sha1(unsigned char *data, unsigned int data_len, unsigned char *digest);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LDNS_SHA1_H */
diff --git a/3rdParty/Ldns/src/include/ldns/sha2.h b/3rdParty/Ldns/src/include/ldns/sha2.h
new file mode 100644
index 0000000..238767a
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/sha2.h
@@ -0,0 +1,149 @@
+/*
+ * FILE:	sha2.h
+ * AUTHOR:	Aaron D. Gifford - http://www.aarongifford.com/
+ * 
+ * Copyright (c) 2000-2001, Aaron D. Gifford
+ * All rights reserved.
+ *
+ * Modified by Jelte Jansen to fit in ldns, and not clash with any
+ * system-defined SHA code.
+ * Changes:
+ *  - Renamed (external) functions and constants to fit ldns style
+ *  - Removed uintXX vs. u_intXX smartness, since ldns needs uintXX
+ *    anyway
+ *  - BYTE ORDER check replaced by simple ifdef as defined or not by
+ *    configure.ac
+ *  - Removed _End and _Data functions
+ *  - Added ldns_shaX(data, len, digest) functions
+ * 
+ * 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 copyright holder nor the names of contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``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 AUTHOR OR CONTRIBUTOR(S) 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.
+ *
+ * $Id: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $
+ */
+
+#ifndef __LDNS_SHA2_H__
+#define __LDNS_SHA2_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * Import u_intXX_t size_t type definitions from system headers.  You
+ * may need to change this, or define these things yourself in this
+ * file.
+ */
+#include <sys/types.h>
+
+#if LDNS_BUILD_CONFIG_HAVE_INTTYPES_H
+
+#include <inttypes.h>
+
+#endif /* LDNS_BUILD_CONFIG_HAVE_INTTYPES_H */
+
+
+/*** SHA-256/384/512 Various Length Definitions ***********************/
+#define LDNS_SHA256_BLOCK_LENGTH		64
+#define LDNS_SHA256_DIGEST_LENGTH		32
+#define LDNS_SHA256_DIGEST_STRING_LENGTH	(LDNS_SHA256_DIGEST_LENGTH * 2 + 1)
+#define LDNS_SHA384_BLOCK_LENGTH		128
+#define LDNS_SHA384_DIGEST_LENGTH		48
+#define LDNS_SHA384_DIGEST_STRING_LENGTH	(LDNS_SHA384_DIGEST_LENGTH * 2 + 1)
+#define LDNS_SHA512_BLOCK_LENGTH		128
+#define LDNS_SHA512_DIGEST_LENGTH		64
+#define LDNS_SHA512_DIGEST_STRING_LENGTH	(LDNS_SHA512_DIGEST_LENGTH * 2 + 1)
+
+
+/*** SHA-256/384/512 Context Structures *******************************/
+
+typedef struct _ldns_sha256_CTX {
+	uint32_t	state[8];
+	uint64_t	bitcount;
+	uint8_t	buffer[LDNS_SHA256_BLOCK_LENGTH];
+} ldns_sha256_CTX;
+typedef struct _ldns_sha512_CTX {
+	uint64_t	state[8];
+	uint64_t	bitcount[2];
+	uint8_t	buffer[LDNS_SHA512_BLOCK_LENGTH];
+} ldns_sha512_CTX;
+
+typedef ldns_sha512_CTX ldns_sha384_CTX;
+
+
+/*** SHA-256/384/512 Function Prototypes ******************************/
+void ldns_sha256_init(ldns_sha256_CTX *);
+void ldns_sha256_update(ldns_sha256_CTX*, const uint8_t*, size_t);
+void ldns_sha256_final(uint8_t[LDNS_SHA256_DIGEST_LENGTH], ldns_sha256_CTX*);
+
+void ldns_sha384_init(ldns_sha384_CTX*);
+void ldns_sha384_update(ldns_sha384_CTX*, const uint8_t*, size_t);
+void ldns_sha384_final(uint8_t[LDNS_SHA384_DIGEST_LENGTH], ldns_sha384_CTX*);
+
+void ldns_sha512_init(ldns_sha512_CTX*);
+void ldns_sha512_update(ldns_sha512_CTX*, const uint8_t*, size_t);
+void ldns_sha512_final(uint8_t[LDNS_SHA512_DIGEST_LENGTH], ldns_sha512_CTX*);
+
+/**
+ * Convenience function to digest a fixed block of data at once.
+ *
+ * \param[in] data the data to digest
+ * \param[in] data_len the length of data in bytes
+ * \param[out] digest the length of data in bytes
+ *             This pointer MUST have LDNS_SHA256_DIGEST_LENGTH bytes
+ *             available
+ * \return the SHA1 digest of the given data
+ */
+unsigned char *ldns_sha256(unsigned char *data, unsigned int data_len, unsigned char *digest);
+
+/**
+ * Convenience function to digest a fixed block of data at once.
+ *
+ * \param[in] data the data to digest
+ * \param[in] data_len the length of data in bytes
+ * \param[out] digest the length of data in bytes
+ *             This pointer MUST have LDNS_SHA384_DIGEST_LENGTH bytes
+ *             available
+ * \return the SHA1 digest of the given data
+ */
+unsigned char *ldns_sha384(unsigned char *data, unsigned int data_len, unsigned char *digest);
+
+/**
+ * Convenience function to digest a fixed block of data at once.
+ *
+ * \param[in] data the data to digest
+ * \param[in] data_len the length of data in bytes
+ * \param[out] digest the length of data in bytes
+ *             This pointer MUST have LDNS_SHA512_DIGEST_LENGTH bytes
+ *             available
+ * \return the SHA1 digest of the given data
+ */
+unsigned char *ldns_sha512(unsigned char *data, unsigned int data_len, unsigned char *digest);
+
+#ifdef	__cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __LDNS_SHA2_H__ */
diff --git a/3rdParty/Ldns/src/include/ldns/str2host.h b/3rdParty/Ldns/src/include/ldns/str2host.h
new file mode 100644
index 0000000..09416cd
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/str2host.h
@@ -0,0 +1,251 @@
+/**
+ * str2host.h - conversion from str to the host fmt
+ *
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2005-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+#ifndef LDNS_2HOST_H
+#define LDNS_2HOST_H
+
+#include <ldns/common.h>
+#include <ldns/error.h>
+#include <ldns/rr.h>
+#include <ldns/rdata.h>
+#include <ldns/packet.h>
+#include <ldns/buffer.h>
+#include <ctype.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file
+ *
+ * Defines functions to convert dns data in presentation format or text files
+ * to internal structures.
+ */
+
+/**
+ * convert a byte into wireformat
+ * \param[in] rd the rdf where to put the data
+ * \param[in] bytestr the string to be converted
+ * \return ldns_status
+ */
+ldns_status ldns_str2rdf_int8(ldns_rdf **rd, const char *bytestr);
+
+/**
+ * convert a string to a int16 in wireformat
+ * \param[in] rd the rdf where to put the data
+ * \param[in] shortstr the string to be converted
+ * \return ldns_status
+ */
+ldns_status ldns_str2rdf_int16(ldns_rdf **rd, const char *shortstr);
+
+/**
+ * convert a strings into a 4 byte int in wireformat
+ * \param[in] rd the rdf where to put the data
+ * \param[in] longstr the string to be converted
+ * \return ldns_status
+ */
+ldns_status ldns_str2rdf_int32(ldns_rdf **rd, const char *longstr);
+
+/**
+ * convert a time string to a time value in wireformat
+ * \param[in] rd the rdf where to put the data
+ * \param[in] time the string to be converted
+ * \return ldns_status
+ */
+ldns_status ldns_str2rdf_time(ldns_rdf **rd, const char *time);
+
+/* convert string with NSEC3 salt to wireformat) 
+ * \param[in] rd the rdf where to put the data
+ * \param[in] str the string to be converted
+ * return ldns_status
+ */
+ldns_status ldns_str2rdf_nsec3_salt(ldns_rdf **rd, const char *nsec3_salt);
+
+/* convert a time period (think TTL's) to wireformat) 
+ * \param[in] rd the rdf where to put the data
+ * \param[in] str the string to be converted
+ * return ldns_status
+ */
+ldns_status ldns_str2rdf_period(ldns_rdf **rd, const char *str);
+
+/**
+ * convert str with an A record into wireformat
+ * \param[in] rd the rdf where to put the data
+ * \param[in] str the string to be converted
+ * \return ldns_status
+ */
+ldns_status ldns_str2rdf_a(ldns_rdf **rd, const char *str);
+
+/**
+ * convert the str with an AAAA record into wireformat
+ * \param[in] rd the rdf where to put the data
+ * \param[in] str the string to be converted
+ * \return ldns_status
+ */
+ldns_status ldns_str2rdf_aaaa(ldns_rdf **rd, const char *str);
+
+/**
+ * convert a string into wireformat (think txt record)
+ * \param[in] rd the rdf where to put the data
+ * \param[in] str the string to be converted (NULL terminated)
+ * \return ldns_status
+ */
+ldns_status ldns_str2rdf_str(ldns_rdf **rd, const char *str);
+
+/**
+ * convert str with the apl record into wireformat
+ * \param[in] rd the rdf where to put the data
+ * \param[in] str the string to be converted
+ * \return ldns_status
+ */
+ldns_status ldns_str2rdf_apl(ldns_rdf **rd, const char *str);
+
+/**
+ * convert the string with the b64 data into wireformat
+ * \param[in] rd the rdf where to put the data
+ * \param[in] str the string to be converted
+ * \return ldns_status
+ */
+ldns_status ldns_str2rdf_b64(ldns_rdf **rd, const char *str);
+
+/**
+ * convert the string with the b32 ext hex data into wireformat
+ * \param[in] rd the rdf where to put the data
+ * \param[in] str the string to be converted
+ * \return ldns_status
+ */
+ldns_status ldns_str2rdf_b32_ext(ldns_rdf **rd, const char *str);
+
+/**
+ * convert a hex value into wireformat
+ * \param[in] rd the rdf where to put the data
+ * \param[in] str the string to be converted
+ * \return ldns_status
+ */
+ldns_status ldns_str2rdf_hex(ldns_rdf **rd, const char *str);
+
+/**
+ * convert string with nsec into wireformat
+ * \param[in] rd the rdf where to put the data
+ * \param[in] str the string to be converted
+ * \return ldns_status
+ */
+ldns_status ldns_str2rdf_nsec(ldns_rdf **rd, const char *str);
+
+/**
+ * convert a rrtype into wireformat
+ * \param[in] rd the rdf where to put the data
+ * \param[in] str the string to be converted
+ * \return ldns_status
+ */
+ldns_status ldns_str2rdf_type(ldns_rdf **rd, const char *str);
+
+/**
+ * convert string with a classname into wireformat
+ * \param[in] rd the rdf where to put the data
+ * \param[in] str the string to be converted
+ * \return ldns_status
+ */
+ldns_status ldns_str2rdf_class(ldns_rdf **rd, const char *str);
+
+/**
+ * convert an certificate algorithm value into wireformat
+ * \param[in] rd the rdf where to put the data
+ * \param[in] str the string to be converted
+ * \return ldns_status
+ */
+ldns_status ldns_str2rdf_cert_alg(ldns_rdf **rd, const char *str);
+
+/**
+ * convert and algorithm value into wireformat
+ * \param[in] rd the rdf where to put the data
+ * \param[in] str the string to be converted
+ * \return ldns_status
+ */
+ldns_status ldns_str2rdf_alg(ldns_rdf **rd, const char *str);
+
+/**
+ * convert a string with a unknown RR into wireformat
+ * \param[in] rd the rdf where to put the data
+ * \param[in] str the string to be converted
+ * \return ldns_status
+ */
+ldns_status ldns_str2rdf_unknown(ldns_rdf **rd, const char *str);
+
+/**
+ * convert string with a tsig? RR into wireformat
+ * \param[in] rd the rdf where to put the data
+ * \param[in] str the string to be converted
+ * \return ldns_status
+ */
+ldns_status ldns_str2rdf_tsig(ldns_rdf **rd, const char *str);
+
+/**
+ * convert string with a protocol service into wireformat
+ * \param[in] rd the rdf where to put the data
+ * \param[in] str the string to be converted
+ * \return ldns_status
+ */
+ldns_status ldns_str2rdf_service(ldns_rdf **rd, const char *str);
+
+/**
+ * convert a string with a LOC RR into wireformat
+ * \param[in] rd the rdf where to put the data
+ * \param[in] str the string to be converted
+ * \return ldns_status
+ */
+ldns_status ldns_str2rdf_loc(ldns_rdf **rd, const char *str);
+
+/**
+ * convert string with a WKS RR into wireformat
+ * \param[in] rd the rdf where to put the data
+ * \param[in] str the string to be converted
+ * \return ldns_status
+ */
+ldns_status ldns_str2rdf_wks(ldns_rdf **rd, const char *str);
+
+/**
+ * convert a str with a NSAP RR into wireformat
+ * \param[in] rd the rdf where to put the data
+ * \param[in] str the string to be converted
+ * \return ldns_status
+ */
+ldns_status ldns_str2rdf_nsap(ldns_rdf **rd, const char *str);
+
+/**
+ * convert a str with a ATMA RR into wireformat
+ * \param[in] rd the rdf where to put the data
+ * \param[in] str the string to be converted
+ * \return ldns_status
+ */
+ldns_status ldns_str2rdf_atma(ldns_rdf **rd, const char *str);
+
+/**
+ * convert a str with a IPSECKEY RR into wireformat
+ * \param[in] rd the rdf where to put the data
+ * \param[in] str the string to be converted
+ * \return ldns_status
+ */
+ldns_status ldns_str2rdf_ipseckey(ldns_rdf **rd, const char *str);
+
+/**
+ * convert a dname string into wireformat
+ * \param[in] rd the rdf where to put the data
+ * \param[in] str the string to be converted
+ * \return ldns_status
+ */
+ldns_status ldns_str2rdf_dname(ldns_rdf **rd, const char *str);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LDNS_2HOST_H */
diff --git a/3rdParty/Ldns/src/include/ldns/tsig.h b/3rdParty/Ldns/src/include/ldns/tsig.h
new file mode 100644
index 0000000..676045f
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/tsig.h
@@ -0,0 +1,101 @@
+/*
+ * tsig.h -- defines for TSIG [RFC2845]
+ *
+ * Copyright (c) 2005-2008, NLnet Labs. All rights reserved.
+ *
+ * See LICENSE for the license.
+ */
+
+#ifndef LDNS_TSIG_H
+#define LDNS_TSIG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file
+ *
+ * Defines functions for TSIG usage
+ */
+
+
+/**
+ * Contains credentials for TSIG
+*/
+typedef struct ldns_tsig_credentials_struct
+{
+    char *algorithm;
+    char *keyname;
+    char *keydata;
+    /* XXX More eventually. */
+} ldns_tsig_credentials;
+
+char *ldns_tsig_algorithm(ldns_tsig_credentials *);
+char *ldns_tsig_keyname(ldns_tsig_credentials *);
+char *ldns_tsig_keydata(ldns_tsig_credentials *);
+char *ldns_tsig_keyname_clone(ldns_tsig_credentials *);
+char *ldns_tsig_keydata_clone(ldns_tsig_credentials *);
+
+/**
+ * verifies the tsig rr for the given packet and key.
+ * The wire must be given too because tsig does not sign normalized packets.
+ * \param[in] pkt the packet to verify
+ * \param[in] wire needed to verify the mac
+ * \param[in] wire_size size of wire
+ * \param[in] key_name the name of the shared key
+ * \param[in] key_data the key in base 64 format
+ * \param[in] mac original mac
+ * \return true if tsig is correct, false if not, or if tsig is not set
+ */
+bool ldns_pkt_tsig_verify(ldns_pkt *pkt, uint8_t *wire, size_t wire_size, const char *key_name, const char *key_data, ldns_rdf *mac);
+
+/**
+ * verifies the tsig rr for the given packet and key.
+ * The wire must be given too because tsig does not sign normalized packets.
+ * \param[in] pkt the packet to verify
+ * \param[in] wire needed to verify the mac
+ * \param[in] wire_size size of wire
+ * \param[in] key_name the name of the shared key
+ * \param[in] key_data the key in base 64 format
+ * \param[in] mac original mac
+ * \param[in] tsig_timers_only must be zero for the first packet and positive for subsequent packets. If zero, all digest
+   components are used to verify the _mac. If non-zero, only the TSIG timers are used to verify the mac.
+ * \return true if tsig is correct, false if not, or if tsig is not set
+ */
+bool ldns_pkt_tsig_verify_next(ldns_pkt *pkt, uint8_t *wire, size_t wire_size, const char *key_name, const char *key_data, ldns_rdf *mac,
+    int tsig_timers_only);
+
+/**
+ * creates a tsig rr for the given packet and key.
+ * \param[in] pkt the packet to sign
+ * \param[in] key_name the name of the shared key
+ * \param[in] key_data the key in base 64 format
+ * \param[in] fudge seconds of error permitted in time signed
+ * \param[in] algorithm_name the name of the algorithm used
+ * \param[in] query_mac is added to the digest if not NULL (so NULL is for signing queries, not NULL is for signing answers)
+ * \return status (OK if success)
+ */
+ldns_status ldns_pkt_tsig_sign(ldns_pkt *pkt, const char *key_name, const char *key_data, uint16_t fudge,
+    const char *algorithm_name, ldns_rdf *query_mac);
+
+/**
+ * creates a tsig rr for the given packet and key.
+ * \param[in] pkt the packet to sign
+ * \param[in] key_name the name of the shared key
+ * \param[in] key_data the key in base 64 format
+ * \param[in] fudge seconds of error permitted in time signed
+ * \param[in] algorithm_name the name of the algorithm used
+ * \param[in] query_mac is added to the digest if not NULL (so NULL is for signing queries, not NULL is for signing answers)
+ * \param[in] tsig_timers_only must be zero for the first packet and positive for subsequent packets. If zero, all digest
+   components are used to create the query_mac. If non-zero, only the TSIG timers are used to create the query_mac.
+ * \return status (OK if success)
+ */
+ldns_status ldns_pkt_tsig_sign_next(ldns_pkt *pkt, const char *key_name, const char *key_data, uint16_t fudge,
+    const char *algorithm_name, ldns_rdf *query_mac, int tsig_timers_only);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LDNS_TSIG_H */
diff --git a/3rdParty/Ldns/src/include/ldns/update.h b/3rdParty/Ldns/src/include/ldns/update.h
new file mode 100644
index 0000000..d3459d3
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/update.h
@@ -0,0 +1,115 @@
+/*
+ * update.h
+ *
+ * Functions for RFC 2136 Dynamic Update
+ *
+ * Copyright (c) 2005-2008, NLnet Labs. All rights reserved.
+ *
+ * See LICENSE for the license.
+ */
+
+/**
+ * \file
+ *
+ * Defines functions to perform UPDATE queries
+ */
+
+
+#ifndef LDNS_UPDATE_H
+#define LDNS_UPDATE_H
+
+#include <ldns/resolver.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * create an update packet from zone name, class and the rr lists
+ * \param[in] zone_rdf name of the zone
+ * \param[in] clas zone class
+ * \param[in] pr_rrlist list of Prerequisite Section RRs
+ * \param[in] up_rrlist list of Updates Section RRs
+ * \param[in] ad_rrlist list of Additional Data Section RRs (currently unused)
+ * \return the new packet
+ */
+ldns_pkt *ldns_update_pkt_new(ldns_rdf *zone_rdf, ldns_rr_class clas, ldns_rr_list *pr_rrlist, ldns_rr_list *up_rrlist, ldns_rr_list *ad_rrlist);
+
+/**
+ * add tsig credentials to
+ * a packet from a resolver
+ * \param[in] p packet to copy to
+ * \param[in] r resolver to copy from
+ *
+ * \return status wether successfull or not
+ */
+ldns_status ldns_update_pkt_tsig_add(ldns_pkt *p, ldns_resolver *r);
+
+/* access functions */
+
+/**
+ * Get the zo count
+ * \param[in] p the packet
+ * \return the zo count
+ */
+uint16_t ldns_update_zocount(const ldns_pkt *p);
+/**
+ * Get the zo count
+ * \param[in] p the packet
+ * \return the pr count
+ */
+uint16_t ldns_update_prcount(const ldns_pkt *p);
+/**
+ * Get the zo count
+ * \param[in] p the packet
+ * \return the up count
+ */
+uint16_t ldns_update_upcount(const ldns_pkt *p);
+/**
+ * Get the zo count
+ * \param[in] p the packet
+ * \return the ad count
+ */
+uint16_t ldns_update_ad(const ldns_pkt *p);
+/**
+ * Set the zo count
+ * \param[in] p the packet
+ * \param[in] c the zo count to set
+ */
+void ldns_update_set_zo(ldns_pkt *p, uint16_t c);
+/**
+ * Set the pr count
+ * \param[in] p the packet
+ * \param[in] c the pr count to set
+ */
+void ldns_update_set_prcount(ldns_pkt *p, uint16_t c);
+/**
+ * Set the up count
+ * \param[in] p the packet
+ * \param[in] c the up count to set
+ */
+void ldns_update_set_upcount(ldns_pkt *p, uint16_t c);
+/**
+ * Set the ad count
+ * \param[in] p the packet
+ * \param[in] c the ad count to set
+ */
+void ldns_update_set_adcount(ldns_pkt *p, uint16_t c);
+
+/* soa functions that need to be configured */
+/*
+ * Not sure if we want to keep these like this, therefore
+ * not documented
+ */
+ldns_status ldns_update_soa_mname(ldns_rdf *zone, ldns_resolver *r, ldns_rr_class c, ldns_rdf **mname);
+/* 
+ * Not sure if we want to keep these like this, therefore
+ * not documented
+ */
+ldns_status ldns_update_soa_zone_mname(const char *fqdn, ldns_resolver *r, ldns_rr_class c, ldns_rdf **zone_rdf, ldns_rdf **mname_rdf);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* LDNS_UPDATE_H */
diff --git a/3rdParty/Ldns/src/include/ldns/util.h b/3rdParty/Ldns/src/include/ldns/util.h
new file mode 100644
index 0000000..eb7658e
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/util.h
@@ -0,0 +1,367 @@
+/*
+ * util.h
+ *  
+ * helper function header file
+ * 
+ * a Net::DNS like library for C
+ * 
+ * (c) NLnet Labs, 2004
+ * 
+ * See the file LICENSE for the license
+ */
+
+#ifndef _UTIL_H
+#define _UTIL_H
+
+#include <inttypes.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <ldns/common.h>
+#include <time.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define dprintf(X,Y) fprintf(stderr, (X), (Y))
+/* #define	dprintf(X, Y)  */
+
+#define LDNS_VERSION "1.6.12"
+#define LDNS_REVISION ((1<<16)|(6<<8)|(12))
+
+/**
+ * splint static inline workaround
+ */
+#ifdef S_SPLINT_S
+#  define INLINE 
+#else
+#  ifdef SWIG
+#    define INLINE static
+#  else
+#    define INLINE static inline
+#  endif
+#endif
+
+/**
+ * Memory management macros
+ */
+#define LDNS_MALLOC(type)		LDNS_XMALLOC(type, 1)
+
+#define LDNS_XMALLOC(type, count)	((type *) malloc((count) * sizeof(type)))
+
+#define LDNS_CALLOC(type, count)	((type *) calloc((count), sizeof(type)))
+
+#define LDNS_REALLOC(ptr, type)		LDNS_XREALLOC((ptr), type, 1)
+
+#define LDNS_XREALLOC(ptr, type, count)				\
+	((type *) realloc((ptr), (count) * sizeof(type)))
+
+#define LDNS_FREE(ptr) \
+	do { free((ptr)); (ptr) = NULL; } while (0)
+
+#define LDNS_DEP     printf("DEPRECATED FUNCTION!\n");
+
+/*
+ * Copy data allowing for unaligned accesses in network byte order
+ * (big endian).
+ */
+INLINE uint16_t
+ldns_read_uint16(const void *src)
+{
+#ifdef ALLOW_UNALIGNED_ACCESSES
+	return ntohs(*(uint16_t *) src);
+#else
+	uint8_t *p = (uint8_t *) src;
+	return ((uint16_t) p[0] << 8) | (uint16_t) p[1];
+#endif
+}
+
+INLINE uint32_t
+ldns_read_uint32(const void *src)
+{
+#ifdef ALLOW_UNALIGNED_ACCESSES
+	return ntohl(*(uint32_t *) src);
+#else
+	uint8_t *p = (uint8_t *) src;
+	return (  ((uint32_t) p[0] << 24)
+		| ((uint32_t) p[1] << 16)
+		| ((uint32_t) p[2] << 8)
+		|  (uint32_t) p[3]);
+#endif
+}
+
+/*
+ * Copy data allowing for unaligned accesses in network byte order
+ * (big endian).
+ */
+INLINE void
+ldns_write_uint16(void *dst, uint16_t data)
+{
+#ifdef ALLOW_UNALIGNED_ACCESSES
+	* (uint16_t *) dst = htons(data);
+#else
+	uint8_t *p = (uint8_t *) dst;
+	p[0] = (uint8_t) ((data >> 8) & 0xff);
+	p[1] = (uint8_t) (data & 0xff);
+#endif
+}
+
+INLINE void
+ldns_write_uint32(void *dst, uint32_t data)
+{
+#ifdef ALLOW_UNALIGNED_ACCESSES
+	* (uint32_t *) dst = htonl(data);
+#else
+	uint8_t *p = (uint8_t *) dst;
+	p[0] = (uint8_t) ((data >> 24) & 0xff);
+	p[1] = (uint8_t) ((data >> 16) & 0xff);
+	p[2] = (uint8_t) ((data >> 8) & 0xff);
+	p[3] = (uint8_t) (data & 0xff);
+#endif
+}
+
+/* warning. */
+INLINE void
+ldns_write_uint64_as_uint48(void *dst, uint64_t data)
+{
+	uint8_t *p = (uint8_t *) dst;
+	p[0] = (uint8_t) ((data >> 40) & 0xff);
+	p[1] = (uint8_t) ((data >> 32) & 0xff);
+	p[2] = (uint8_t) ((data >> 24) & 0xff);
+	p[3] = (uint8_t) ((data >> 16) & 0xff);
+	p[4] = (uint8_t) ((data >> 8) & 0xff);
+	p[5] = (uint8_t) (data & 0xff);
+}
+
+
+/**
+ * Structure to do a Schwartzian-like transformation, for instance when
+ * sorting. If you need a transformation on the objects that are sorted,
+ * you can sue this to store the transformed values, so you do not
+ * need to do the transformation again for each comparison
+ */
+struct ldns_schwartzian_compare_struct {
+	void *original_object;
+	void *transformed_object;
+};
+
+/** A general purpose lookup table
+ *  
+ *  Lookup tables are arrays of (id, name) pairs,
+ *  So you can for instance lookup the RCODE 3, which is "NXDOMAIN",
+ *  and vice versa. The lookup tables themselves are defined wherever needed,
+ *  for instance in \ref host2str.c
+ */
+struct ldns_struct_lookup_table {
+        int id;
+        const char *name;
+};
+typedef struct ldns_struct_lookup_table ldns_lookup_table;
+  
+/**
+ * Looks up the table entry by name, returns NULL if not found.
+ * \param[in] table the lookup table to search in
+ * \param[in] name what to search for
+ * \return the item found
+ */
+ldns_lookup_table *ldns_lookup_by_name(ldns_lookup_table table[],
+                                       const char *name);
+
+/**
+ * Looks up the table entry by id, returns NULL if not found.
+ * \param[in] table the lookup table to search in
+ * \param[in] id what to search for
+ * \return the item found
+ */
+ldns_lookup_table *ldns_lookup_by_id(ldns_lookup_table table[], int id);
+
+/**
+ * Returns the value of the specified bit
+ * The bits are counted from left to right, so bit #0 is the
+ * left most bit.
+ * \param[in] bits array holding the bits
+ * \param[in] index to the wanted bit
+ * \return 
+ */
+int ldns_get_bit(uint8_t bits[], size_t index);
+
+
+/**
+ * Returns the value of the specified bit
+ * The bits are counted from right to left, so bit #0 is the
+ * right most bit.
+ * \param[in] bits array holding the bits
+ * \param[in] index to the wanted bit
+ * \return 1 or 0 depending no the bit state
+ */
+int ldns_get_bit_r(uint8_t bits[], size_t index);
+
+/**
+ * sets the specified bit in the specified byte to
+ * 1 if value is true, 0 if false
+ * The bits are counted from right to left, so bit #0 is the
+ * right most bit.
+ * \param[in] byte the bit to set the bit in
+ * \param[in] bit_nr the bit to set (0 <= n <= 7)
+ * \param[in] value whether to set the bit to 1 or 0
+ * \return 1 or 0 depending no the bit state
+ */
+void ldns_set_bit(uint8_t *byte, int bit_nr, bool value);
+
+/**
+ * Returns the value of a to the power of b
+ * (or 1 of b < 1)
+ */
+/*@unused@*/
+INLINE long
+ldns_power(long a, long b) {
+	long result = 1;
+	while (b > 0) {
+		if (b & 1) {
+			result *= a;
+			if (b == 1) {
+				return result;
+			}
+		}
+		a *= a;
+		b /= 2;
+	}
+	return result;
+}
+
+/**
+ * Returns the int value of the given (hex) digit
+ * \param[in] ch the hex char to convert
+ * \return the converted decimal value
+ */
+int ldns_hexdigit_to_int(char ch);
+
+/**
+ * Returns the char (hex) representation of the given int
+ * \param[in] ch the int to convert
+ * \return the converted hex char
+ */
+char ldns_int_to_hexdigit(int ch);
+
+/**
+ * Converts a hex string to binary data
+ *
+ * \param[out] data The binary result is placed here.
+ * At least strlen(str)/2 bytes should be allocated
+ * \param[in] str The hex string to convert.
+ * This string should not contain spaces
+ * \return The number of bytes of converted data, or -1 if one of the arguments * is NULL, or -2 if the string length is not an even number
+ */
+int
+ldns_hexstring_to_data(uint8_t *data, const char *str);
+
+/**
+ * Show the internal library version
+ * \return a string with the version in it
+ */
+const char * ldns_version(void);
+
+/**
+ * Convert TM to seconds since epoch (midnight, January 1st, 1970).
+ * Like timegm(3), which is not always available.
+ * \param[in] tm a struct tm* with the date
+ * \return the seconds since epoch
+ */
+time_t mktime_from_utc(const struct tm *tm);
+
+/**
+ * The function interprets time as the number of seconds since epoch
+ * with respect to now using serial arithmitics (rfc1982).
+ * That number of seconds is then converted to broken-out time information.
+ * This is especially usefull when converting the inception and expiration
+ * fields of RRSIG records.
+ *
+ * \param[in] time number of seconds since epoch (midnight, January 1st, 1970)
+ *            to be intepreted as a serial arithmitics number relative to now.
+ * \param[in] now number of seconds since epoch (midnight, January 1st, 1970)
+ *            to which the time value is compared to determine the final value.
+ * \param[out] result the struct with the broken-out time information
+ * \return result on success or NULL on error
+ */
+struct tm * ldns_serial_arithmitics_gmtime_r(int32_t time, time_t now, struct tm *result);
+ 
+/**
+ * Seed the random function.
+ * If the file descriptor is specified, the random generator is seeded with
+ * data from that file. If not, /dev/urandom is used.
+ *
+ * applications should call this if they need entropy data within ldns
+ * If openSSL is available, it is automatically seeded from /dev/urandom
+ * or /dev/random.
+ *
+ * If you need more entropy, or have no openssl available, this function
+ * MUST be called at the start of the program
+ *
+ * If openssl *is* available, this function just adds more entropy
+ *
+ * \param[in] fd a file providing entropy data for the seed
+ * \param[in] size the number of bytes to use as entropy data. If this is 0,
+ *            only the minimal amount is taken (usually 4 bytes)
+ * \return 0 if seeding succeeds, 1 if it fails
+ */
+int ldns_init_random(FILE *fd, unsigned int size);
+
+/**
+ * Get random number.
+ * \return random number.
+ *
+ */
+uint16_t ldns_get_random(void);
+
+/**
+ * Encode data as BubbleBabble
+ *
+ * \param[in] data a pointer to data to be encoded
+ * \param[in] len size the number of bytes of data
+ * \return a string of BubbleBabble
+ */
+char *ldns_bubblebabble(uint8_t *data, size_t len);
+
+#ifndef B32_NTOP
+int ldns_b32_ntop(uint8_t const *src, size_t srclength,
+	     char *target, size_t targsize);
+int b32_ntop(uint8_t const *src, size_t srclength,
+	     char *target, size_t targsize);
+int ldns_b32_ntop_extended_hex(uint8_t const *src, size_t srclength,
+	     char *target, size_t targsize);
+int b32_ntop_extended_hex(uint8_t const *src, size_t srclength,
+	     char *target, size_t targsize);
+/**
+ * calculates the size needed to store the result of b32_ntop
+ */
+/*@unused@*/
+INLINE size_t ldns_b32_ntop_calculate_size(size_t srcsize)
+{
+	size_t result = ((((srcsize / 5) * 8) - 2) + 2);
+	return result;
+}
+#endif /* !B32_NTOP */
+#ifndef B32_PTON
+int ldns_b32_pton(char const *src, size_t hashed_owner_str_len, uint8_t *target, size_t targsize);
+int b32_pton(char const *src, size_t hashed_owner_str_len, uint8_t *target, size_t targsize);
+int ldns_b32_pton_extended_hex(char const *src, size_t hashed_owner_str_len, uint8_t *target, size_t targsize);
+int b32_pton_extended_hex(char const *src, size_t hashed_owner_str_len, uint8_t *target, size_t targsize);
+/**
+ * calculates the size needed to store the result of b32_pton
+ */
+/*@unused@*/
+INLINE size_t ldns_b32_pton_calculate_size(size_t srcsize)
+{
+	size_t result = ((((srcsize) / 8) * 5));
+	return result;
+}
+#endif /* !B32_PTON */
+
+INLINE time_t ldns_time(time_t *t) { return time(t); }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_UTIL_H */
diff --git a/3rdParty/Ldns/src/include/ldns/util.h.in b/3rdParty/Ldns/src/include/ldns/util.h.in
new file mode 100644
index 0000000..f9fb104
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/util.h.in
@@ -0,0 +1,367 @@
+/*
+ * util.h
+ *  
+ * helper function header file
+ * 
+ * a Net::DNS like library for C
+ * 
+ * (c) NLnet Labs, 2004
+ * 
+ * See the file LICENSE for the license
+ */
+
+#ifndef _UTIL_H
+#define _UTIL_H
+
+@include_inttypes_h@
+@include_systypes_h@
+@include_unistd_h@
+#include <ldns/common.h>
+#include <time.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define dprintf(X,Y) fprintf(stderr, (X), (Y))
+/* #define	dprintf(X, Y)  */
+
+#define LDNS_VERSION "@PACKAGE_VERSION@"
+#define LDNS_REVISION ((@LDNS_VERSION_MAJOR@<<16)|(@LDNS_VERSION_MINOR@<<8)|(@LDNS_VERSION_MICRO@))
+
+/**
+ * splint static inline workaround
+ */
+#ifdef S_SPLINT_S
+#  define INLINE 
+#else
+#  ifdef SWIG
+#    define INLINE static
+#  else
+#    define INLINE static inline
+#  endif
+#endif
+
+/**
+ * Memory management macros
+ */
+#define LDNS_MALLOC(type)		LDNS_XMALLOC(type, 1)
+
+#define LDNS_XMALLOC(type, count)	((type *) malloc((count) * sizeof(type)))
+
+#define LDNS_CALLOC(type, count)	((type *) calloc((count), sizeof(type)))
+
+#define LDNS_REALLOC(ptr, type)		LDNS_XREALLOC((ptr), type, 1)
+
+#define LDNS_XREALLOC(ptr, type, count)				\
+	((type *) realloc((ptr), (count) * sizeof(type)))
+
+#define LDNS_FREE(ptr) \
+	do { free((ptr)); (ptr) = NULL; } while (0)
+
+#define LDNS_DEP     printf("DEPRECATED FUNCTION!\n");
+
+/*
+ * Copy data allowing for unaligned accesses in network byte order
+ * (big endian).
+ */
+INLINE uint16_t
+ldns_read_uint16(const void *src)
+{
+#ifdef ALLOW_UNALIGNED_ACCESSES
+	return ntohs(*(uint16_t *) src);
+#else
+	uint8_t *p = (uint8_t *) src;
+	return ((uint16_t) p[0] << 8) | (uint16_t) p[1];
+#endif
+}
+
+INLINE uint32_t
+ldns_read_uint32(const void *src)
+{
+#ifdef ALLOW_UNALIGNED_ACCESSES
+	return ntohl(*(uint32_t *) src);
+#else
+	uint8_t *p = (uint8_t *) src;
+	return (  ((uint32_t) p[0] << 24)
+		| ((uint32_t) p[1] << 16)
+		| ((uint32_t) p[2] << 8)
+		|  (uint32_t) p[3]);
+#endif
+}
+
+/*
+ * Copy data allowing for unaligned accesses in network byte order
+ * (big endian).
+ */
+INLINE void
+ldns_write_uint16(void *dst, uint16_t data)
+{
+#ifdef ALLOW_UNALIGNED_ACCESSES
+	* (uint16_t *) dst = htons(data);
+#else
+	uint8_t *p = (uint8_t *) dst;
+	p[0] = (uint8_t) ((data >> 8) & 0xff);
+	p[1] = (uint8_t) (data & 0xff);
+#endif
+}
+
+INLINE void
+ldns_write_uint32(void *dst, uint32_t data)
+{
+#ifdef ALLOW_UNALIGNED_ACCESSES
+	* (uint32_t *) dst = htonl(data);
+#else
+	uint8_t *p = (uint8_t *) dst;
+	p[0] = (uint8_t) ((data >> 24) & 0xff);
+	p[1] = (uint8_t) ((data >> 16) & 0xff);
+	p[2] = (uint8_t) ((data >> 8) & 0xff);
+	p[3] = (uint8_t) (data & 0xff);
+#endif
+}
+
+/* warning. */
+INLINE void
+ldns_write_uint64_as_uint48(void *dst, uint64_t data)
+{
+	uint8_t *p = (uint8_t *) dst;
+	p[0] = (uint8_t) ((data >> 40) & 0xff);
+	p[1] = (uint8_t) ((data >> 32) & 0xff);
+	p[2] = (uint8_t) ((data >> 24) & 0xff);
+	p[3] = (uint8_t) ((data >> 16) & 0xff);
+	p[4] = (uint8_t) ((data >> 8) & 0xff);
+	p[5] = (uint8_t) (data & 0xff);
+}
+
+
+/**
+ * Structure to do a Schwartzian-like transformation, for instance when
+ * sorting. If you need a transformation on the objects that are sorted,
+ * you can sue this to store the transformed values, so you do not
+ * need to do the transformation again for each comparison
+ */
+struct ldns_schwartzian_compare_struct {
+	void *original_object;
+	void *transformed_object;
+};
+
+/** A general purpose lookup table
+ *  
+ *  Lookup tables are arrays of (id, name) pairs,
+ *  So you can for instance lookup the RCODE 3, which is "NXDOMAIN",
+ *  and vice versa. The lookup tables themselves are defined wherever needed,
+ *  for instance in \ref host2str.c
+ */
+struct ldns_struct_lookup_table {
+        int id;
+        const char *name;
+};
+typedef struct ldns_struct_lookup_table ldns_lookup_table;
+  
+/**
+ * Looks up the table entry by name, returns NULL if not found.
+ * \param[in] table the lookup table to search in
+ * \param[in] name what to search for
+ * \return the item found
+ */
+ldns_lookup_table *ldns_lookup_by_name(ldns_lookup_table table[],
+                                       const char *name);
+
+/**
+ * Looks up the table entry by id, returns NULL if not found.
+ * \param[in] table the lookup table to search in
+ * \param[in] id what to search for
+ * \return the item found
+ */
+ldns_lookup_table *ldns_lookup_by_id(ldns_lookup_table table[], int id);
+
+/**
+ * Returns the value of the specified bit
+ * The bits are counted from left to right, so bit #0 is the
+ * left most bit.
+ * \param[in] bits array holding the bits
+ * \param[in] index to the wanted bit
+ * \return 
+ */
+int ldns_get_bit(uint8_t bits[], size_t index);
+
+
+/**
+ * Returns the value of the specified bit
+ * The bits are counted from right to left, so bit #0 is the
+ * right most bit.
+ * \param[in] bits array holding the bits
+ * \param[in] index to the wanted bit
+ * \return 1 or 0 depending no the bit state
+ */
+int ldns_get_bit_r(uint8_t bits[], size_t index);
+
+/**
+ * sets the specified bit in the specified byte to
+ * 1 if value is true, 0 if false
+ * The bits are counted from right to left, so bit #0 is the
+ * right most bit.
+ * \param[in] byte the bit to set the bit in
+ * \param[in] bit_nr the bit to set (0 <= n <= 7)
+ * \param[in] value whether to set the bit to 1 or 0
+ * \return 1 or 0 depending no the bit state
+ */
+void ldns_set_bit(uint8_t *byte, int bit_nr, bool value);
+
+/**
+ * Returns the value of a to the power of b
+ * (or 1 of b < 1)
+ */
+/*@unused@*/
+INLINE long
+ldns_power(long a, long b) {
+	long result = 1;
+	while (b > 0) {
+		if (b & 1) {
+			result *= a;
+			if (b == 1) {
+				return result;
+			}
+		}
+		a *= a;
+		b /= 2;
+	}
+	return result;
+}
+
+/**
+ * Returns the int value of the given (hex) digit
+ * \param[in] ch the hex char to convert
+ * \return the converted decimal value
+ */
+int ldns_hexdigit_to_int(char ch);
+
+/**
+ * Returns the char (hex) representation of the given int
+ * \param[in] ch the int to convert
+ * \return the converted hex char
+ */
+char ldns_int_to_hexdigit(int ch);
+
+/**
+ * Converts a hex string to binary data
+ *
+ * \param[out] data The binary result is placed here.
+ * At least strlen(str)/2 bytes should be allocated
+ * \param[in] str The hex string to convert.
+ * This string should not contain spaces
+ * \return The number of bytes of converted data, or -1 if one of the arguments * is NULL, or -2 if the string length is not an even number
+ */
+int
+ldns_hexstring_to_data(uint8_t *data, const char *str);
+
+/**
+ * Show the internal library version
+ * \return a string with the version in it
+ */
+const char * ldns_version(void);
+
+/**
+ * Convert TM to seconds since epoch (midnight, January 1st, 1970).
+ * Like timegm(3), which is not always available.
+ * \param[in] tm a struct tm* with the date
+ * \return the seconds since epoch
+ */
+time_t mktime_from_utc(const struct tm *tm);
+
+/**
+ * The function interprets time as the number of seconds since epoch
+ * with respect to now using serial arithmitics (rfc1982).
+ * That number of seconds is then converted to broken-out time information.
+ * This is especially usefull when converting the inception and expiration
+ * fields of RRSIG records.
+ *
+ * \param[in] time number of seconds since epoch (midnight, January 1st, 1970)
+ *            to be intepreted as a serial arithmitics number relative to now.
+ * \param[in] now number of seconds since epoch (midnight, January 1st, 1970)
+ *            to which the time value is compared to determine the final value.
+ * \param[out] result the struct with the broken-out time information
+ * \return result on success or NULL on error
+ */
+struct tm * ldns_serial_arithmitics_gmtime_r(int32_t time, time_t now, struct tm *result);
+ 
+/**
+ * Seed the random function.
+ * If the file descriptor is specified, the random generator is seeded with
+ * data from that file. If not, /dev/urandom is used.
+ *
+ * applications should call this if they need entropy data within ldns
+ * If openSSL is available, it is automatically seeded from /dev/urandom
+ * or /dev/random.
+ *
+ * If you need more entropy, or have no openssl available, this function
+ * MUST be called at the start of the program
+ *
+ * If openssl *is* available, this function just adds more entropy
+ *
+ * \param[in] fd a file providing entropy data for the seed
+ * \param[in] size the number of bytes to use as entropy data. If this is 0,
+ *            only the minimal amount is taken (usually 4 bytes)
+ * \return 0 if seeding succeeds, 1 if it fails
+ */
+int ldns_init_random(FILE *fd, unsigned int size);
+
+/**
+ * Get random number.
+ * \return random number.
+ *
+ */
+uint16_t ldns_get_random(void);
+
+/**
+ * Encode data as BubbleBabble
+ *
+ * \param[in] data a pointer to data to be encoded
+ * \param[in] len size the number of bytes of data
+ * \return a string of BubbleBabble
+ */
+char *ldns_bubblebabble(uint8_t *data, size_t len);
+
+#ifndef B32_NTOP
+int ldns_b32_ntop(uint8_t const *src, size_t srclength,
+	     char *target, size_t targsize);
+int b32_ntop(uint8_t const *src, size_t srclength,
+	     char *target, size_t targsize);
+int ldns_b32_ntop_extended_hex(uint8_t const *src, size_t srclength,
+	     char *target, size_t targsize);
+int b32_ntop_extended_hex(uint8_t const *src, size_t srclength,
+	     char *target, size_t targsize);
+/**
+ * calculates the size needed to store the result of b32_ntop
+ */
+/*@unused@*/
+INLINE size_t ldns_b32_ntop_calculate_size(size_t srcsize)
+{
+	size_t result = ((((srcsize / 5) * 8) - 2) + 2);
+	return result;
+}
+#endif /* !B32_NTOP */
+#ifndef B32_PTON
+int ldns_b32_pton(char const *src, size_t hashed_owner_str_len, uint8_t *target, size_t targsize);
+int b32_pton(char const *src, size_t hashed_owner_str_len, uint8_t *target, size_t targsize);
+int ldns_b32_pton_extended_hex(char const *src, size_t hashed_owner_str_len, uint8_t *target, size_t targsize);
+int b32_pton_extended_hex(char const *src, size_t hashed_owner_str_len, uint8_t *target, size_t targsize);
+/**
+ * calculates the size needed to store the result of b32_pton
+ */
+/*@unused@*/
+INLINE size_t ldns_b32_pton_calculate_size(size_t srcsize)
+{
+	size_t result = ((((srcsize) / 8) * 5));
+	return result;
+}
+#endif /* !B32_PTON */
+
+INLINE time_t ldns_time(time_t *t) { return time(t); }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_UTIL_H */
diff --git a/3rdParty/Ldns/src/include/ldns/wire2host.h b/3rdParty/Ldns/src/include/ldns/wire2host.h
new file mode 100644
index 0000000..53155b3
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/wire2host.h
@@ -0,0 +1,197 @@
+/*
+ * wire2host.h - from wire conversion routines
+ *
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2005-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+/**
+ * \file
+ *
+ * Contains functions that translate dns data from the wire format (as sent
+ * by servers and clients) to the internal structures.
+ */
+ 
+#ifndef LDNS_WIRE2HOST_H
+#define LDNS_WIRE2HOST_H
+
+#include <ldns/rdata.h>
+#include <ldns/common.h>
+#include <ldns/error.h>
+#include <ldns/rr.h>
+#include <ldns/packet.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The length of the header */
+#define	LDNS_HEADER_SIZE	12
+
+/* First octet of flags */
+#define	LDNS_RD_MASK		0x01U
+#define	LDNS_RD_SHIFT	0
+#define	LDNS_RD_WIRE(wirebuf)	(*(wirebuf+2) & LDNS_RD_MASK)
+#define	LDNS_RD_SET(wirebuf)	(*(wirebuf+2) |= LDNS_RD_MASK)
+#define	LDNS_RD_CLR(wirebuf)	(*(wirebuf+2) &= ~LDNS_RD_MASK)
+
+#define LDNS_TC_MASK		0x02U
+#define LDNS_TC_SHIFT	1
+#define	LDNS_TC_WIRE(wirebuf)	(*(wirebuf+2) & LDNS_TC_MASK)
+#define	LDNS_TC_SET(wirebuf)	(*(wirebuf+2) |= LDNS_TC_MASK)
+#define	LDNS_TC_CLR(wirebuf)	(*(wirebuf+2) &= ~LDNS_TC_MASK)
+
+#define	LDNS_AA_MASK		0x04U
+#define	LDNS_AA_SHIFT	2
+#define	LDNS_AA_WIRE(wirebuf)	(*(wirebuf+2) & LDNS_AA_MASK)
+#define	LDNS_AA_SET(wirebuf)	(*(wirebuf+2) |= LDNS_AA_MASK)
+#define	LDNS_AA_CLR(wirebuf)	(*(wirebuf+2) &= ~LDNS_AA_MASK)
+
+#define	LDNS_OPCODE_MASK	0x78U
+#define	LDNS_OPCODE_SHIFT	3
+#define	LDNS_OPCODE_WIRE(wirebuf)	((*(wirebuf+2) & LDNS_OPCODE_MASK) >> LDNS_OPCODE_SHIFT)
+#define	LDNS_OPCODE_SET(wirebuf, opcode) \
+	(*(wirebuf+2) = ((*(wirebuf+2)) & ~LDNS_OPCODE_MASK) | ((opcode) << LDNS_OPCODE_SHIFT))
+
+#define	LDNS_QR_MASK		0x80U
+#define	LDNS_QR_SHIFT	7
+#define	LDNS_QR_WIRE(wirebuf)	(*(wirebuf+2) & LDNS_QR_MASK)
+#define	LDNS_QR_SET(wirebuf)	(*(wirebuf+2) |= LDNS_QR_MASK)
+#define	LDNS_QR_CLR(wirebuf)	(*(wirebuf+2) &= ~LDNS_QR_MASK)
+
+/* Second octet of flags */
+#define	LDNS_RCODE_MASK	0x0fU
+#define	LDNS_RCODE_SHIFT	0
+#define	LDNS_RCODE_WIRE(wirebuf)	(*(wirebuf+3) & LDNS_RCODE_MASK)
+#define	LDNS_RCODE_SET(wirebuf, rcode) \
+	(*(wirebuf+3) = ((*(wirebuf+3)) & ~LDNS_RCODE_MASK) | (rcode))
+
+#define	LDNS_CD_MASK		0x10U
+#define	LDNS_CD_SHIFT	4
+#define	LDNS_CD_WIRE(wirebuf)	(*(wirebuf+3) & LDNS_CD_MASK)
+#define	LDNS_CD_SET(wirebuf)	(*(wirebuf+3) |= LDNS_CD_MASK)
+#define	LDNS_CD_CLR(wirebuf)	(*(wirebuf+3) &= ~LDNS_CD_MASK)
+
+#define	LDNS_AD_MASK		0x20U
+#define	LDNS_AD_SHIFT	5
+#define	LDNS_AD_WIRE(wirebuf)	(*(wirebuf+3) & LDNS_AD_MASK)
+#define	LDNS_AD_SET(wirebuf)	(*(wirebuf+3) |= LDNS_AD_MASK)
+#define	LDNS_AD_CLR(wirebuf)	(*(wirebuf+3) &= ~LDNS_AD_MASK)
+
+#define	LDNS_Z_MASK		0x40U
+#define	LDNS_Z_SHIFT		6
+#define	LDNS_Z_WIRE(wirebuf)	(*(wirebuf+3) & LDNS_Z_MASK)
+#define	LDNS_Z_SET(wirebuf)	(*(wirebuf+3) |= LDNS_Z_MASK)
+#define	LDNS_Z_CLR(wirebuf)	(*(wirebuf+3) &= ~LDNS_Z_MASK)
+
+#define	LDNS_RA_MASK		0x80U
+#define	LDNS_RA_SHIFT	7
+#define	LDNS_RA_WIRE(wirebuf)	(*(wirebuf+3) & LDNS_RA_MASK)
+#define	LDNS_RA_SET(wirebuf)	(*(wirebuf+3) |= LDNS_RA_MASK)
+#define	LDNS_RA_CLR(wirebuf)	(*(wirebuf+3) &= ~LDNS_RA_MASK)
+
+/* Query ID */
+#define	LDNS_ID_WIRE(wirebuf)		(ldns_read_uint16(wirebuf))
+#define	LDNS_ID_SET(wirebuf, id)	(ldns_write_uint16(wirebuf, id))
+
+/* Counter of the question section */
+#define LDNS_QDCOUNT_OFF		4
+/*
+#define	QDCOUNT(wirebuf)		(ntohs(*(uint16_t *)(wirebuf+QDCOUNT_OFF)))
+*/
+#define	LDNS_QDCOUNT(wirebuf)		(ldns_read_uint16(wirebuf+LDNS_QDCOUNT_OFF))
+
+/* Counter of the answer section */
+#define LDNS_ANCOUNT_OFF		6
+#define	LDNS_ANCOUNT(wirebuf)		(ldns_read_uint16(wirebuf+LDNS_ANCOUNT_OFF))
+
+/* Counter of the authority section */
+#define LDNS_NSCOUNT_OFF		8
+#define	LDNS_NSCOUNT(wirebuf)		(ldns_read_uint16(wirebuf+LDNS_NSCOUNT_OFF))
+
+/* Counter of the additional section */
+#define LDNS_ARCOUNT_OFF		10
+#define	LDNS_ARCOUNT(wirebuf)		(ldns_read_uint16(wirebuf+LDNS_ARCOUNT_OFF))
+
+/**
+ * converts the data on the uint8_t bytearray (in wire format) to a DNS packet.
+ * This function will initialize and allocate memory space for the packet 
+ * structure.
+ * 
+ * \param[in] packet pointer to the structure to hold the packet
+ * \param[in] data pointer to the buffer with the data
+ * \param[in] len the length of the data buffer (in bytes)
+ * \return LDNS_STATUS_OK if everything succeeds, error otherwise
+ */
+ldns_status ldns_wire2pkt(ldns_pkt **packet, const uint8_t *data, size_t len);
+
+/**
+ * converts the data on the uint8_t bytearray (in wire format) to a DNS packet.
+ * This function will initialize and allocate memory space for the packet 
+ * structure.
+ * 
+ * \param[in] packet pointer to the structure to hold the packet
+ * \param[in] buffer the buffer with the data
+ * \return LDNS_STATUS_OK if everything succeeds, error otherwise
+ */
+ldns_status ldns_buffer2pkt_wire(ldns_pkt **packet, ldns_buffer *buffer);
+
+/**
+ * converts the data on the uint8_t bytearray (in wire format) to a DNS 
+ * dname rdata field. This function will initialize and allocate memory
+ * space for the dname structure. The length of the wiredata of this rdf 
+ * is added to the *pos value.
+ *
+ * \param[in] dname pointer to the structure to hold the rdata value
+ * \param[in] wire pointer to the buffer with the data
+ * \param[in] max the length of the data buffer (in bytes)
+ * \param[in] pos the position of the rdf in the buffer (ie. the number of bytes 
+ *            from the start of the buffer)
+ * \return LDNS_STATUS_OK if everything succeeds, error otherwise
+ */
+ldns_status ldns_wire2dname(ldns_rdf **dname, const uint8_t *wire, size_t max, size_t *pos);
+
+/**
+ * converts the data on the uint8_t bytearray (in wire format) to DNS 
+ * rdata fields, and adds them to the list of rdfs of the given rr.
+ * This function will initialize and allocate memory space for the dname
+ * structures.
+ * The length of the wiredata of these rdfs is added to the *pos value.
+ *
+ * All rdfs belonging to the RR are read; the rr should have no rdfs
+ * yet. An error is returned if the format cannot be parsed.
+ *
+ * \param[in] rr pointer to the ldns_rr structure to hold the rdata value
+ * \param[in] wire pointer to the buffer with the data
+ * \param[in] max the length of the data buffer (in bytes)
+ * \param[in] pos the position of the rdf in the buffer (ie. the number of bytes 
+ *            from the start of the buffer)
+ * \return LDNS_STATUS_OK if everything succeeds, error otherwise
+ */
+ldns_status ldns_wire2rdf(ldns_rr *rr, const uint8_t *wire, size_t max, size_t *pos);
+
+/**
+ * converts the data on the uint8_t bytearray (in wire format) to a DNS 
+ * resource record.
+ * This function will initialize and allocate memory space for the rr
+ * structure.
+ * The length of the wiredata of this rr is added to the *pos value.
+ * 
+ * \param[in] rr pointer to the structure to hold the rdata value
+ * \param[in] wire pointer to the buffer with the data
+ * \param[in] max the length of the data buffer (in bytes)
+ * \param[in] pos the position of the rr in the buffer (ie. the number of bytes 
+ *            from the start of the buffer)
+ * \param[in] section the section in the packet the rr is meant for
+ * \return LDNS_STATUS_OK if everything succeeds, error otherwise
+ */
+ldns_status ldns_wire2rr(ldns_rr **rr, const uint8_t *wire, size_t max, size_t *pos, ldns_pkt_section section);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LDNS_WIRE2HOST_H */
diff --git a/3rdParty/Ldns/src/include/ldns/zone.h b/3rdParty/Ldns/src/include/ldns/zone.h
new file mode 100644
index 0000000..0d129a0
--- /dev/null
+++ b/3rdParty/Ldns/src/include/ldns/zone.h
@@ -0,0 +1,176 @@
+/**
+ * zone.h
+ *
+ * zone definitions
+ *  - what is it
+ *  - get_glue function
+ *  - search etc
+ *
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2005-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+/**
+ * \file
+ *
+ * Defines the ldns_zone structure and functions to manipulate it.
+ */
+ 
+
+#ifndef LDNS_ZONE_H
+#define LDNS_ZONE_H
+
+#include <ldns/common.h>
+#include <ldns/rdata.h>
+#include <ldns/rr.h>
+#include <ldns/error.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** 
+ * DNS Zone
+ *
+ * A list of RR's with some
+ * extra information which comes from the SOA RR
+ * Note: nothing has been done to make this efficient (yet).
+ */
+struct ldns_struct_zone
+{
+	/** the soa defines a zone */
+	ldns_rr 	*_soa;
+	/* basicly a zone is a list of rr's */
+	ldns_rr_list 	*_rrs;
+	/* we could change this to be a b-tree etc etc todo */
+};
+typedef struct ldns_struct_zone ldns_zone;	
+	
+/**
+ * create a new ldns_zone structure
+ * \return a pointer to a ldns_zone structure
+ */
+ldns_zone * ldns_zone_new(void);
+
+/**
+ * Return the soa record of a zone
+ * \param[in] z the zone to read from
+ * \return the soa record in the zone
+ */
+ldns_rr * ldns_zone_soa(const ldns_zone *z);
+
+/**
+ * Returns the number of resource records in the zone, NOT counting the SOA record
+ * \param[in] z the zone to read from
+ * \return the number of rr's in the zone
+ */
+size_t ldns_zone_rr_count(const ldns_zone *z);
+
+/**
+ * Set the zone's soa record
+ * \param[in] z the zone to put the new soa in
+ * \param[in] soa the soa to set
+ */
+void ldns_zone_set_soa(ldns_zone *z, ldns_rr *soa);
+
+/**
+ * Get a list of a zone's content. Note that the SOA
+ * isn't included in this list. You need to get the 
+ * with ldns_zone_soa.
+ * \param[in] z the zone to read from
+ * \return the rrs from this zone
+ */
+ldns_rr_list * ldns_zone_rrs(const ldns_zone *z);
+
+/**
+ * Set the zone's contents
+ * \param[in] z the zone to put the new soa in
+ * \param[in] rrlist the rrlist to use
+ */
+void ldns_zone_set_rrs(ldns_zone *z, ldns_rr_list *rrlist);
+
+/**
+ * push an rrlist to a zone structure. This function use pointer
+ * copying, so the rr_list structure inside z is modified!
+ * \param[in] z the zone to add to
+ * \param[in] list the list to add
+ * \return a true on succes otherwise falsed
+ */
+bool ldns_zone_push_rr_list(ldns_zone *z, ldns_rr_list *list);
+
+/**
+ * push an single rr to a zone structure. This function use pointer
+ * copying, so the rr_list structure inside z is modified!
+ * \param[in] z the zone to add to
+ * \param[in] rr the rr to add
+ * \return a true on succes otherwise falsed
+ */
+bool ldns_zone_push_rr(ldns_zone *z, ldns_rr *rr);
+
+/**
+ * Retrieve all resource records from the zone that are glue
+ * records. The resulting list does are pointer references
+ * to the zone's data.
+ *
+ * Due to the current zone implementation (as a list of rr's), this
+ * function is extremely slow. Another (probably better) way to do this
+ * is to use an ldns_dnssec_zone structure and the 
+ * ldns_dnssec_mark_and_get_glue() function.
+ *
+ * \param[in] z the zone to look for glue
+ * \return the rr_list with the glue
+ */
+ldns_rr_list *ldns_zone_glue_rr_list(const ldns_zone *z);
+
+/**
+ * Create a new zone from a file
+ * \param[out] z the new zone
+ * \param[in] *fp the filepointer to use
+ * \param[in] *origin the zones' origin
+ * \param[in] ttl default ttl to use
+ * \param[in] c default class to use (IN)
+ *
+ * \return ldns_status mesg with an error or LDNS_STATUS_OK
+ */
+ldns_status ldns_zone_new_frm_fp(ldns_zone **z, FILE *fp, ldns_rdf *origin, uint32_t ttl, ldns_rr_class c);
+
+/**
+ * Create a new zone from a file, keep track of the line numbering
+ * \param[out] z the new zone
+ * \param[in] *fp the filepointer to use
+ * \param[in] *origin the zones' origin
+ * \param[in] ttl default ttl to use
+ * \param[in] c default class to use (IN)
+ * \param[out] line_nr used for error msg, to get to the line number
+ *
+ * \return ldns_status mesg with an error or LDNS_STATUS_OK
+ */
+ldns_status ldns_zone_new_frm_fp_l(ldns_zone **z, FILE *fp, ldns_rdf *origin, uint32_t ttl, ldns_rr_class c, int *line_nr);
+
+/**
+ * Frees the allocated memory for the zone, and the rr_list structure in it
+ * \param[in] zone the zone to free
+ */
+void ldns_zone_free(ldns_zone *zone);
+
+/**
+ * Frees the allocated memory for the zone, the soa rr in it, 
+ * and the rr_list structure in it, including the rr's in that. etc.
+ * \param[in] zone the zone to free
+ */
+void ldns_zone_deep_free(ldns_zone *zone);
+
+/**
+ * Sort the rrs in a zone, with the current impl. this is slow
+ * \param[in] zone the zone to sort
+ */
+void ldns_zone_sort(ldns_zone *zone);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LDNS_ZONE_H */
diff --git a/3rdParty/Ldns/src/src/buffer.c b/3rdParty/Ldns/src/src/buffer.c
new file mode 100644
index 0000000..5a6b0ba
--- /dev/null
+++ b/3rdParty/Ldns/src/src/buffer.c
@@ -0,0 +1,176 @@
+/*
+ * buffer.c -- generic memory buffer .
+ *
+ * Copyright (c) 2001-2008, NLnet Labs. All rights reserved.
+ *
+ * See LICENSE for the license.
+ *
+ */
+
+#include <ldns/config.h>
+
+#include <ldns/ldns.h>
+#include <ldns/buffer.h>
+
+ldns_buffer *
+ldns_buffer_new(size_t capacity)
+{
+	ldns_buffer *buffer = LDNS_MALLOC(ldns_buffer);
+
+	if (!buffer) {
+		return NULL;
+	}
+	
+	buffer->_data = (uint8_t *) LDNS_XMALLOC(uint8_t, capacity);
+	if (!buffer->_data) {
+		LDNS_FREE(buffer);
+		return NULL;
+	}
+	
+	buffer->_position = 0;
+	buffer->_limit = buffer->_capacity = capacity;
+	buffer->_fixed = 0;
+	buffer->_status = LDNS_STATUS_OK;
+	
+	ldns_buffer_invariant(buffer);
+	
+	return buffer;
+}
+
+void
+ldns_buffer_new_frm_data(ldns_buffer *buffer, void *data, size_t size)
+{
+	assert(data != NULL);
+
+	buffer->_position = 0; 
+	buffer->_limit = buffer->_capacity = size;
+	buffer->_fixed = 0;
+	buffer->_data = LDNS_XMALLOC(uint8_t, size);
+	if(!buffer->_data) {
+		buffer->_status = LDNS_STATUS_MEM_ERR;
+		return;
+	}
+	memcpy(buffer->_data, data, size);
+	buffer->_status = LDNS_STATUS_OK;
+	
+	ldns_buffer_invariant(buffer);
+}
+
+bool
+ldns_buffer_set_capacity(ldns_buffer *buffer, size_t capacity)
+{
+	void *data;
+	
+	ldns_buffer_invariant(buffer);
+	assert(buffer->_position <= capacity);
+
+	data = (uint8_t *) LDNS_XREALLOC(buffer->_data, uint8_t, capacity);
+	if (!data) {
+		buffer->_status = LDNS_STATUS_MEM_ERR;
+		return false;
+	} else {
+		buffer->_data = data;
+		buffer->_limit = buffer->_capacity = capacity;
+		return true;
+	}
+}
+
+bool
+ldns_buffer_reserve(ldns_buffer *buffer, size_t amount)
+{
+	ldns_buffer_invariant(buffer);
+	assert(!buffer->_fixed);
+	if (buffer->_capacity < buffer->_position + amount) {
+		size_t new_capacity = buffer->_capacity * 3 / 2;
+
+		if (new_capacity < buffer->_position + amount) {
+			new_capacity = buffer->_position + amount;
+		}
+		if (!ldns_buffer_set_capacity(buffer, new_capacity)) {
+			buffer->_status = LDNS_STATUS_MEM_ERR;
+			return false;
+		}
+	}
+	buffer->_limit = buffer->_capacity;
+	return true;
+}
+
+int
+ldns_buffer_printf(ldns_buffer *buffer, const char *format, ...)
+{
+	va_list args;
+	int written = 0;
+	size_t remaining;
+	
+	if (ldns_buffer_status_ok(buffer)) {
+		ldns_buffer_invariant(buffer);
+		assert(buffer->_limit == buffer->_capacity);
+
+		remaining = ldns_buffer_remaining(buffer);
+		va_start(args, format);
+		written = vsnprintf((char *) ldns_buffer_current(buffer), remaining,
+				    format, args);
+		va_end(args);
+		if (written == -1) {
+			buffer->_status = LDNS_STATUS_INTERNAL_ERR;
+			return -1;
+		} else if ((size_t) written >= remaining) {
+			if (!ldns_buffer_reserve(buffer, (size_t) written + 1)) {
+				buffer->_status = LDNS_STATUS_MEM_ERR;
+				return -1;
+			}
+			va_start(args, format);
+			written = vsnprintf((char *) ldns_buffer_current(buffer),
+			    ldns_buffer_remaining(buffer), format, args);
+			va_end(args);
+			if (written == -1) {
+				buffer->_status = LDNS_STATUS_INTERNAL_ERR;
+				return -1;
+			}
+		}
+		buffer->_position += written;
+	}
+	return written;
+}
+
+void
+ldns_buffer_free(ldns_buffer *buffer)
+{
+	if (!buffer) {
+		return;
+	}
+
+	LDNS_FREE(buffer->_data);
+
+	LDNS_FREE(buffer);
+}
+
+void *
+ldns_buffer_export(ldns_buffer *buffer)
+{
+	buffer->_fixed = 1;
+	return buffer->_data;
+}
+
+int
+ldns_bgetc(ldns_buffer *buffer)
+{
+	if (!ldns_buffer_available_at(buffer, buffer->_position, sizeof(uint8_t))) {
+		ldns_buffer_set_position(buffer, ldns_buffer_limit(buffer));
+		/* ldns_buffer_rewind(buffer);*/
+		return EOF;
+	}
+	return (int)ldns_buffer_read_u8(buffer);
+}
+
+void 
+ldns_buffer_copy(ldns_buffer* result, ldns_buffer* from)
+{
+	size_t tocopy = ldns_buffer_limit(from);
+
+	if(tocopy > ldns_buffer_capacity(result))
+		tocopy = ldns_buffer_capacity(result);
+	ldns_buffer_clear(result);
+	ldns_buffer_write(result, ldns_buffer_begin(from), tocopy);
+	ldns_buffer_flip(result);
+}
diff --git a/3rdParty/Ldns/src/src/compat/b32_ntop.c b/3rdParty/Ldns/src/src/compat/b32_ntop.c
new file mode 100644
index 0000000..038ebdc
--- /dev/null
+++ b/3rdParty/Ldns/src/src/compat/b32_ntop.c
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 1996, 1998 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+ * Portions Copyright (c) 1995 by International Business Machines, Inc.
+ *
+ * International Business Machines, Inc. (hereinafter called IBM) grants
+ * permission under its copyrights to use, copy, modify, and distribute this
+ * Software with or without fee, provided that the above copyright notice and
+ * all paragraphs of this notice appear in all copies, and that the name of IBM
+ * not be used in connection with the marketing of any product incorporating
+ * the Software or modifications thereof, without specific, written prior
+ * permission.
+ *
+ * To the extent it has a right to do so, IBM grants an immunity from suit
+ * under its patents, if any, for the use, sale or manufacture of products to
+ * the extent that such products are used for performing Domain Name System
+ * dynamic updates in TCP/IP networks by means of the Software.  No immunity is
+ * granted for any product per se or for any other function of any product.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE.  IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
+ * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
+ * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+#include <ldns/config.h>
+
+#include <sys/types.h>
+#include <sys/param.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <assert.h>
+
+static const char Base32[] =
+	"abcdefghijklmnopqrstuvwxyz234567";
+/*	"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";*/
+/*       00000000001111111111222222222233
+         01234567890123456789012345678901*/
+static const char Base32_extended_hex[] =
+/*	"0123456789ABCDEFGHIJKLMNOPQRSTUV";*/
+	"0123456789abcdefghijklmnopqrstuv";
+static const char Pad32 = '=';
+
+/* (From RFC3548 and draft-josefsson-rfc3548bis-00.txt)
+5.  Base 32 Encoding
+
+   The Base 32 encoding is designed to represent arbitrary sequences of
+   octets in a form that needs to be case insensitive but need not be
+   humanly readable.
+
+   A 33-character subset of US-ASCII is used, enabling 5 bits to be
+   represented per printable character.  (The extra 33rd character, "=",
+   is used to signify a special processing function.)
+
+   The encoding process represents 40-bit groups of input bits as output
+   strings of 8 encoded characters.  Proceeding from left to right, a
+   40-bit input group is formed by concatenating 5 8bit input groups.
+   These 40 bits are then treated as 8 concatenated 5-bit groups, each
+   of which is translated into a single digit in the base 32 alphabet.
+   When encoding a bit stream via the base 32 encoding, the bit stream
+   must be presumed to be ordered with the most-significant-bit first.
+   That is, the first bit in the stream will be the high-order bit in
+   the first 8bit byte, and the eighth bit will be the low-order bit in
+   the first 8bit byte, and so on.
+
+   Each 5-bit group is used as an index into an array of 32 printable
+   characters.  The character referenced by the index is placed in the
+   output string.  These characters, identified in Table 3, below, are
+   selected from US-ASCII digits and uppercase letters.
+
+                      Table 3: The Base 32 Alphabet
+
+         Value Encoding  Value Encoding  Value Encoding  Value Encoding
+             0 A             9 J            18 S            27 3
+             1 B            10 K            19 T            28 4
+             2 C            11 L            20 U            29 5
+             3 D            12 M            21 V            30 6
+             4 E            13 N            22 W            31 7
+             5 F            14 O            23 X
+             6 G            15 P            24 Y         (pad) =
+             7 H            16 Q            25 Z
+             8 I            17 R            26 2
+
+
+   Special processing is performed if fewer than 40 bits are available
+   at the end of the data being encoded.  A full encoding quantum is
+   always completed at the end of a body.  When fewer than 40 input bits
+   are available in an input group, zero bits are added (on the right)
+   to form an integral number of 5-bit groups.  Padding at the end of
+   the data is performed using the "=" character.  Since all base 32
+   input is an integral number of octets, only the following cases can
+   arise:
+
+   (1) the final quantum of encoding input is an integral multiple of 40
+   bits; here, the final unit of encoded output will be an integral
+   multiple of 8 characters with no "=" padding,
+
+   (2) the final quantum of encoding input is exactly 8 bits; here, the
+   final unit of encoded output will be two characters followed by six
+   "=" padding characters,
+
+   (3) the final quantum of encoding input is exactly 16 bits; here, the
+   final unit of encoded output will be four characters followed by four
+   "=" padding characters,
+
+   (4) the final quantum of encoding input is exactly 24 bits; here, the
+   final unit of encoded output will be five characters followed by
+   three "=" padding characters, or
+
+   (5) the final quantum of encoding input is exactly 32 bits; here, the
+   final unit of encoded output will be seven characters followed by one
+   "=" padding character.
+
+
+6.  Base 32 Encoding with Extended Hex Alphabet
+
+   The following description of base 32 is due to [7].  This encoding
+   should not be regarded as the same as the "base32" encoding, and
+   should not be referred to as only "base32".
+
+   One property with this alphabet, that the base64 and base32 alphabet
+   lack, is that encoded data maintain its sort order when the encoded
+   data is compared bit-wise.
+
+   This encoding is identical to the previous one, except for the
+   alphabet.  The new alphabet is found in table 4.
+
+                     Table 4: The "Extended Hex" Base 32 Alphabet
+
+         Value Encoding  Value Encoding  Value Encoding  Value Encoding
+             0 0             9 9            18 I            27 R
+             1 1            10 A            19 J            28 S
+             2 2            11 B            20 K            29 T
+             3 3            12 C            21 L            30 U
+             4 4            13 D            22 M            31 V
+             5 5            14 E            23 N
+             6 6            15 F            24 O         (pad) =
+             7 7            16 G            25 P
+             8 8            17 H            26 Q
+
+*/
+
+
+int
+ldns_b32_ntop_ar(uint8_t const *src, size_t srclength, char *target, size_t targsize, const char B32_ar[]) {
+	size_t datalength = 0;
+	uint8_t input[5];
+	uint8_t output[8];
+	size_t i;
+        memset(output, 0, 8);
+
+	while (4 < srclength) {
+		input[0] = *src++;
+		input[1] = *src++;
+		input[2] = *src++;
+		input[3] = *src++;
+		input[4] = *src++;
+		srclength -= 5;
+
+		output[0] = (input[0] & 0xf8) >> 3;
+		output[1] = ((input[0] & 0x07) << 2) + ((input[1] & 0xc0) >> 6);
+		output[2] = (input[1] & 0x3e) >> 1;
+		output[3] = ((input[1] & 0x01) << 4) + ((input[2] & 0xf0) >> 4);
+		output[4] = ((input[2] & 0x0f) << 1) + ((input[3] & 0x80) >> 7);
+		output[5] = (input[3] & 0x7c) >> 2;
+		output[6] = ((input[3] & 0x03) << 3) + ((input[4] & 0xe0) >> 5);
+		output[7] = (input[4] & 0x1f);
+
+		assert(output[0] < 32);
+		assert(output[1] < 32);
+		assert(output[2] < 32);
+		assert(output[3] < 32);
+		assert(output[4] < 32);
+		assert(output[5] < 32);
+		assert(output[6] < 32);
+		assert(output[7] < 32);
+
+		if (datalength + 8 > targsize) {
+			return (-1);
+		}
+		target[datalength++] = B32_ar[output[0]];
+		target[datalength++] = B32_ar[output[1]];
+		target[datalength++] = B32_ar[output[2]];
+		target[datalength++] = B32_ar[output[3]];
+		target[datalength++] = B32_ar[output[4]];
+		target[datalength++] = B32_ar[output[5]];
+		target[datalength++] = B32_ar[output[6]];
+		target[datalength++] = B32_ar[output[7]];
+	}
+    
+	/* Now we worry about padding. */
+	if (0 != srclength) {
+		/* Get what's left. */
+		input[0] = input[1] = input[2] = input[3] = input[4] = (uint8_t) '\0';
+		for (i = 0; i < srclength; i++)
+			input[i] = *src++;
+	
+		output[0] = (input[0] & 0xf8) >> 3;
+		assert(output[0] < 32);
+		if (srclength >= 1) {
+			output[1] = ((input[0] & 0x07) << 2) + ((input[1] & 0xc0) >> 6);
+			assert(output[1] < 32);
+			output[2] = (input[1] & 0x3e) >> 1;
+			assert(output[2] < 32);
+		}
+		if (srclength >= 2) {
+			output[3] = ((input[1] & 0x01) << 4) + ((input[2] & 0xf0) >> 4);
+			assert(output[3] < 32);
+		}
+		if (srclength >= 3) {
+			output[4] = ((input[2] & 0x0f) << 1) + ((input[3] & 0x80) >> 7);
+			assert(output[4] < 32);
+			output[5] = (input[3] & 0x7c) >> 2;
+			assert(output[5] < 32);
+		}
+		if (srclength >= 4) {
+			output[6] = ((input[3] & 0x03) << 3) + ((input[4] & 0xe0) >> 5);
+			assert(output[6] < 32);
+		}
+
+
+		if (datalength + 1 > targsize) {
+			return (-2);
+		}
+		target[datalength++] = B32_ar[output[0]];
+		if (srclength >= 1) {
+			if (datalength + 1 > targsize) { return (-2); }
+			target[datalength++] = B32_ar[output[1]];
+			if (srclength == 1 && output[2] == 0) {
+				if (datalength + 1 > targsize) { return (-2); }
+				target[datalength++] = Pad32;
+			} else {
+				if (datalength + 1 > targsize) { return (-2); }
+				target[datalength++] = B32_ar[output[2]];
+			}
+		} else {
+			if (datalength + 1 > targsize) { return (-2); }
+			target[datalength++] = Pad32;
+			if (datalength + 1 > targsize) { return (-2); }
+			target[datalength++] = Pad32;
+		}
+		if (srclength >= 2) {
+			if (datalength + 1 > targsize) { return (-2); }
+			target[datalength++] = B32_ar[output[3]];
+		} else {
+			if (datalength + 1 > targsize) { return (-2); }
+			target[datalength++] = Pad32;
+		}
+		if (srclength >= 3) {
+			if (datalength + 1 > targsize) { return (-2); }
+			target[datalength++] = B32_ar[output[4]];
+			if (srclength == 3 && output[5] == 0) {
+				if (datalength + 1 > targsize) { return (-2); }
+				target[datalength++] = Pad32;
+			} else {
+				if (datalength + 1 > targsize) { return (-2); }
+				target[datalength++] = B32_ar[output[5]];
+			}
+		} else {
+			if (datalength + 1 > targsize) { return (-2); }
+			target[datalength++] = Pad32;
+			if (datalength + 1 > targsize) { return (-2); }
+			target[datalength++] = Pad32;
+		}
+		if (srclength >= 4) {
+			if (datalength + 1 > targsize) { return (-2); }
+			target[datalength++] = B32_ar[output[6]];
+		} else {
+			if (datalength + 1 > targsize) { return (-2); }
+			target[datalength++] = Pad32;
+		}
+		if (datalength + 1 > targsize) { return (-2); }
+		target[datalength++] = Pad32;
+	}
+	if (datalength+1 > targsize) {
+		return (int) (datalength);
+	}
+	target[datalength] = '\0';	/* Returned value doesn't count \0. */
+	return (int) (datalength);
+}
+
+int
+ldns_b32_ntop(uint8_t const *src, size_t srclength, char *target, size_t targsize) {
+	return ldns_b32_ntop_ar(src, srclength, target, targsize, Base32);
+}
+
+/* deprecated, here for backwards compatibility */
+int
+b32_ntop(uint8_t const *src, size_t srclength, char *target, size_t targsize) {
+	return ldns_b32_ntop_ar(src, srclength, target, targsize, Base32);
+}
+
+int
+ldns_b32_ntop_extended_hex(uint8_t const *src, size_t srclength, char *target, size_t targsize) {
+	return ldns_b32_ntop_ar(src, srclength, target, targsize, Base32_extended_hex);
+}
+
+/* deprecated, here for backwards compatibility */
+int
+b32_ntop_extended_hex(uint8_t const *src, size_t srclength, char *target, size_t targsize) {
+	return ldns_b32_ntop_ar(src, srclength, target, targsize, Base32_extended_hex);
+}
+
diff --git a/3rdParty/Ldns/src/src/compat/b32_pton.c b/3rdParty/Ldns/src/src/compat/b32_pton.c
new file mode 100644
index 0000000..9c261e6
--- /dev/null
+++ b/3rdParty/Ldns/src/src/compat/b32_pton.c
@@ -0,0 +1,387 @@
+/*
+ * Copyright (c) 1996, 1998 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+ * Portions Copyright (c) 1995 by International Business Machines, Inc.
+ *
+ * International Business Machines, Inc. (hereinafter called IBM) grants
+ * permission under its copyrights to use, copy, modify, and distribute this
+ * Software with or without fee, provided that the above copyright notice and
+ * all paragraphs of this notice appear in all copies, and that the name of IBM
+ * not be used in connection with the marketing of any product incorporating
+ * the Software or modifications thereof, without specific, written prior
+ * permission.
+ *
+ * To the extent it has a right to do so, IBM grants an immunity from suit
+ * under its patents, if any, for the use, sale or manufacture of products to
+ * the extent that such products are used for performing Domain Name System
+ * dynamic updates in TCP/IP networks by means of the Software.  No immunity is
+ * granted for any product per se or for any other function of any product.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE.  IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
+ * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
+ * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+#include <ldns/config.h>
+
+#include <sys/types.h>
+#include <sys/param.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*	"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";*/
+static const char Base32[] =
+	"abcdefghijklmnopqrstuvwxyz234567";
+/*	"0123456789ABCDEFGHIJKLMNOPQRSTUV";*/
+static const char Base32_extended_hex[] =
+	"0123456789abcdefghijklmnopqrstuv";
+static const char Pad32 = '=';
+
+/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt)
+5.  Base 32 Encoding
+
+   The Base 32 encoding is designed to represent arbitrary sequences of
+   octets in a form that needs to be case insensitive but need not be
+   humanly readable.
+
+   A 33-character subset of US-ASCII is used, enabling 5 bits to be
+   represented per printable character.  (The extra 33rd character, "=",
+   is used to signify a special processing function.)
+
+   The encoding process represents 40-bit groups of input bits as output
+   strings of 8 encoded characters.  Proceeding from left to right, a
+   40-bit input group is formed by concatenating 5 8bit input groups.
+   These 40 bits are then treated as 8 concatenated 5-bit groups, each
+   of which is translated into a single digit in the base 32 alphabet.
+   When encoding a bit stream via the base 32 encoding, the bit stream
+   must be presumed to be ordered with the most-significant-bit first.
+   That is, the first bit in the stream will be the high-order bit in
+   the first 8bit byte, and the eighth bit will be the low-order bit in
+   the first 8bit byte, and so on.
+
+   Each 5-bit group is used as an index into an array of 32 printable
+   characters.  The character referenced by the index is placed in the
+   output string.  These characters, identified in Table 3, below, are
+   selected from US-ASCII digits and uppercase letters.
+
+                      Table 3: The Base 32 Alphabet
+
+         Value Encoding  Value Encoding  Value Encoding  Value Encoding
+             0 A             9 J            18 S            27 3
+             1 B            10 K            19 T            28 4
+             2 C            11 L            20 U            29 5
+             3 D            12 M            21 V            30 6
+             4 E            13 N            22 W            31 7
+             5 F            14 O            23 X
+             6 G            15 P            24 Y         (pad) =
+             7 H            16 Q            25 Z
+             8 I            17 R            26 2
+
+
+   Special processing is performed if fewer than 40 bits are available
+   at the end of the data being encoded.  A full encoding quantum is
+   always completed at the end of a body.  When fewer than 40 input bits
+   are available in an input group, zero bits are added (on the right)
+   to form an integral number of 5-bit groups.  Padding at the end of
+   the data is performed using the "=" character.  Since all base 32
+   input is an integral number of octets, only the following cases can
+   arise:
+
+   (1) the final quantum of encoding input is an integral multiple of 40
+   bits; here, the final unit of encoded output will be an integral
+   multiple of 8 characters with no "=" padding,
+
+   (2) the final quantum of encoding input is exactly 8 bits; here, the
+   final unit of encoded output will be two characters followed by six
+   "=" padding characters,
+
+   (3) the final quantum of encoding input is exactly 16 bits; here, the
+   final unit of encoded output will be four characters followed by four
+   "=" padding characters,
+
+   (4) the final quantum of encoding input is exactly 24 bits; here, the
+   final unit of encoded output will be five characters followed by
+   three "=" padding characters, or
+
+   (5) the final quantum of encoding input is exactly 32 bits; here, the
+   final unit of encoded output will be seven characters followed by one
+   "=" padding character.
+
+
+6.  Base 32 Encoding with Extended Hex Alphabet
+
+   The following description of base 32 is due to [7].  This encoding
+   should not be regarded as the same as the "base32" encoding, and
+   should not be referred to as only "base32".
+
+   One property with this alphabet, that the base32 and base32 alphabet
+   lack, is that encoded data maintain its sort order when the encoded
+   data is compared bit-wise.
+
+   This encoding is identical to the previous one, except for the
+   alphabet.  The new alphabet is found in table 4.
+
+                     Table 4: The "Extended Hex" Base 32 Alphabet
+
+         Value Encoding  Value Encoding  Value Encoding  Value Encoding
+             0 0             9 9            18 I            27 R
+             1 1            10 A            19 J            28 S
+             2 2            11 B            20 K            29 T
+             3 3            12 C            21 L            30 U
+             4 4            13 D            22 M            31 V
+             5 5            14 E            23 N
+             6 6            15 F            24 O         (pad) =
+             7 7            16 G            25 P
+             8 8            17 H            26 Q
+
+
+
+
+*/
+/* skips all whitespace anywhere.
+   converts characters, four at a time, starting at (or after)
+   src from base - 32 numbers into three 8 bit bytes in the target area.
+   it returns the number of data bytes stored at the target, or -1 on error.
+ */
+
+int
+ldns_b32_pton_ar(char const *src, size_t hashed_owner_str_len, uint8_t *target, size_t targsize, const char B32_ar[])
+{
+	int tarindex, state, ch;
+	char *pos;
+	int i = 0;
+
+	state = 0;
+	tarindex = 0;
+	
+	while ((ch = *src++) != '\0' && (i == 0 || i < (int) hashed_owner_str_len)) {
+		i++;
+		ch = tolower(ch);
+		if (isspace((unsigned char)ch))        /* Skip whitespace anywhere. */
+			continue;
+
+		if (ch == Pad32)
+			break;
+
+		pos = strchr(B32_ar, ch);
+		if (pos == 0) {
+			/* A non-base32 character. */
+			return (-ch);
+		}
+
+		switch (state) {
+		case 0:
+			if (target) {
+				if ((size_t)tarindex >= targsize) {
+					return (-2);
+				}
+				target[tarindex] = (pos - B32_ar) << 3;
+			}
+			state = 1;
+			break;
+		case 1:
+			if (target) {
+				if ((size_t)tarindex + 1 >= targsize) {
+					return (-3);
+				}
+				target[tarindex]   |=  (pos - B32_ar) >> 2;
+				target[tarindex+1]  = ((pos - B32_ar) & 0x03)
+							<< 6 ;
+			}
+			tarindex++;
+			state = 2;
+			break;
+		case 2:
+			if (target) {
+				if ((size_t)tarindex + 1 >= targsize) {
+					return (-4);
+				}
+				target[tarindex]   |=  (pos - B32_ar) << 1;
+			}
+			/*tarindex++;*/
+			state = 3;
+			break;
+		case 3:
+			if (target) {
+				if ((size_t)tarindex + 1 >= targsize) {
+					return (-5);
+				}
+				target[tarindex]   |=  (pos - B32_ar) >> 4;
+				target[tarindex+1]  = ((pos - B32_ar) & 0x0f) << 4 ;
+			}
+			tarindex++;
+			state = 4;
+			break;
+		case 4:
+			if (target) {
+				if ((size_t)tarindex + 1 >= targsize) {
+					return (-6);
+				}
+				target[tarindex]   |=  (pos - B32_ar) >> 1;
+				target[tarindex+1]  = ((pos - B32_ar) & 0x01)
+							<< 7 ;
+			}
+			tarindex++;
+			state = 5;
+			break;
+		case 5:
+			if (target) {
+				if ((size_t)tarindex + 1 >= targsize) {
+					return (-7);
+				}
+				target[tarindex]   |=  (pos - B32_ar) << 2;
+			}
+			state = 6;
+			break;
+		case 6:
+			if (target) {
+				if ((size_t)tarindex + 1 >= targsize) {
+					return (-8);
+				}
+				target[tarindex]   |=  (pos - B32_ar) >> 3;
+				target[tarindex+1]  = ((pos - B32_ar) & 0x07)
+							<< 5 ;
+			}
+			tarindex++;
+			state = 7;
+			break;
+		case 7:
+			if (target) {
+				if ((size_t)tarindex + 1 >= targsize) {
+					return (-9);
+				}
+				target[tarindex]   |=  (pos - B32_ar);
+			}
+			tarindex++;
+			state = 0;
+			break;
+		default:
+			abort();
+		}
+	}
+
+	/*
+	 * We are done decoding Base-32 chars.  Let's see if we ended
+	 * on a byte boundary, and/or with erroneous trailing characters.
+	 */
+
+	if (ch == Pad32) {		/* We got a pad char. */
+		ch = *src++;		/* Skip it, get next. */
+		switch (state) {
+		case 0:		/* Invalid = in first position */
+		case 1:		/* Invalid = in second position */
+			return (-10);
+
+		case 2:		/* Valid, means one byte of info */
+		case 3:
+			/* Skip any number of spaces. */
+			for ((void)NULL; ch != '\0'; ch = *src++)
+				if (!isspace((unsigned char)ch))
+					break;
+			/* Make sure there is another trailing = sign. */
+			if (ch != Pad32) {
+				return (-11);
+			}
+			ch = *src++;		/* Skip the = */
+			/* Fall through to "single trailing =" case. */
+			/* FALLTHROUGH */
+
+		case 4:		/* Valid, means two bytes of info */
+		case 5:
+		case 6:
+			/*
+			 * We know this char is an =.  Is there anything but
+			 * whitespace after it?
+			 */
+			for ((void)NULL; ch != '\0'; ch = *src++)
+				if (!(isspace((unsigned char)ch) || ch == '=')) {
+					return (-12);
+				}
+
+		case 7:		/* Valid, means three bytes of info */
+			/*
+			 * We know this char is an =.  Is there anything but
+			 * whitespace after it?
+			 */
+			for ((void)NULL; ch != '\0'; ch = *src++)
+				if (!isspace((unsigned char)ch)) {
+					return (-13);
+				}
+
+			/*
+			 * Now make sure for cases 2 and 3 that the "extra"
+			 * bits that slopped past the last full byte were
+			 * zeros.  If we don't check them, they become a
+			 * subliminal channel.
+			 */
+			if (target && target[tarindex] != 0) {
+				return (-14);
+			}
+		}
+	} else {
+		/*
+		 * We ended by seeing the end of the string.  Make sure we
+		 * have no partial bytes lying around.
+		 */
+		if (state != 0)
+			return (-15);
+	}
+
+	return (tarindex);
+}
+
+int
+ldns_b32_pton(char const *src, size_t hashed_owner_str_len, uint8_t *target, size_t targsize)
+{
+	return ldns_b32_pton_ar(src, hashed_owner_str_len, target, targsize, Base32);
+}
+
+/* deprecated, here for backwards compatibility */
+int
+b32_pton(char const *src, size_t hashed_owner_str_len, uint8_t *target, size_t targsize)
+{
+	return ldns_b32_pton_ar(src, hashed_owner_str_len, target, targsize, Base32);
+}
+
+int
+ldns_b32_pton_extended_hex(char const *src, size_t hashed_owner_str_len, uint8_t *target, size_t targsize)
+{
+	return ldns_b32_pton_ar(src, hashed_owner_str_len, target, targsize, Base32_extended_hex);
+}
+
+/* deprecated, here for backwards compatibility */
+int
+b32_pton_extended_hex(char const *src, size_t hashed_owner_str_len, uint8_t *target, size_t targsize)
+{
+	return ldns_b32_pton_ar(src, hashed_owner_str_len, target, targsize, Base32_extended_hex);
+}
diff --git a/3rdParty/Ldns/src/src/compat/b64_ntop.c b/3rdParty/Ldns/src/src/compat/b64_ntop.c
new file mode 100644
index 0000000..d0b52b5
--- /dev/null
+++ b/3rdParty/Ldns/src/src/compat/b64_ntop.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 1996, 1998 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+ * Portions Copyright (c) 1995 by International Business Machines, Inc.
+ *
+ * International Business Machines, Inc. (hereinafter called IBM) grants
+ * permission under its copyrights to use, copy, modify, and distribute this
+ * Software with or without fee, provided that the above copyright notice and
+ * all paragraphs of this notice appear in all copies, and that the name of IBM
+ * not be used in connection with the marketing of any product incorporating
+ * the Software or modifications thereof, without specific, written prior
+ * permission.
+ *
+ * To the extent it has a right to do so, IBM grants an immunity from suit
+ * under its patents, if any, for the use, sale or manufacture of products to
+ * the extent that such products are used for performing Domain Name System
+ * dynamic updates in TCP/IP networks by means of the Software.  No immunity is
+ * granted for any product per se or for any other function of any product.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE.  IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
+ * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
+ * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+#include <ldns/config.h>
+
+#include <sys/types.h>
+#include <sys/param.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define Assert(Cond) if (!(Cond)) abort()
+
+static const char Base64[] =
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char Pad64 = '=';
+
+/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt)
+   The following encoding technique is taken from RFC 1521 by Borenstein
+   and Freed.  It is reproduced here in a slightly edited form for
+   convenience.
+
+   A 65-character subset of US-ASCII is used, enabling 6 bits to be
+   represented per printable character. (The extra 65th character, "=",
+   is used to signify a special processing function.)
+
+   The encoding process represents 24-bit groups of input bits as output
+   strings of 4 encoded characters. Proceeding from left to right, a
+   24-bit input group is formed by concatenating 3 8-bit input groups.
+   These 24 bits are then treated as 4 concatenated 6-bit groups, each
+   of which is translated into a single digit in the base64 alphabet.
+
+   Each 6-bit group is used as an index into an array of 64 printable
+   characters. The character referenced by the index is placed in the
+   output string.
+
+                         Table 1: The Base64 Alphabet
+
+      Value Encoding  Value Encoding  Value Encoding  Value Encoding
+          0 A            17 R            34 i            51 z
+          1 B            18 S            35 j            52 0
+          2 C            19 T            36 k            53 1
+          3 D            20 U            37 l            54 2
+          4 E            21 V            38 m            55 3
+          5 F            22 W            39 n            56 4
+          6 G            23 X            40 o            57 5
+          7 H            24 Y            41 p            58 6
+          8 I            25 Z            42 q            59 7
+          9 J            26 a            43 r            60 8
+         10 K            27 b            44 s            61 9
+         11 L            28 c            45 t            62 +
+         12 M            29 d            46 u            63 /
+         13 N            30 e            47 v
+         14 O            31 f            48 w         (pad) =
+         15 P            32 g            49 x
+         16 Q            33 h            50 y
+
+   Special processing is performed if fewer than 24 bits are available
+   at the end of the data being encoded.  A full encoding quantum is
+   always completed at the end of a quantity.  When fewer than 24 input
+   bits are available in an input group, zero bits are added (on the
+   right) to form an integral number of 6-bit groups.  Padding at the
+   end of the data is performed using the '=' character.
+
+   Since all base64 input is an integral number of octets, only the
+         -------------------------------------------------                       
+   following cases can arise:
+   
+       (1) the final quantum of encoding input is an integral
+           multiple of 24 bits; here, the final unit of encoded
+	   output will be an integral multiple of 4 characters
+	   with no "=" padding,
+       (2) the final quantum of encoding input is exactly 8 bits;
+           here, the final unit of encoded output will be two
+	   characters followed by two "=" padding characters, or
+       (3) the final quantum of encoding input is exactly 16 bits;
+           here, the final unit of encoded output will be three
+	   characters followed by one "=" padding character.
+   */
+
+int
+ldns_b64_ntop(uint8_t const *src, size_t srclength, char *target, size_t targsize) {
+	size_t datalength = 0;
+	uint8_t input[3];
+	uint8_t output[4];
+	size_t i;
+	
+	if (srclength == 0) {
+		if (targsize > 0) {
+			target[0] = '\0';
+			return 0;
+		} else {
+			return -1;
+		}
+	}
+
+	while (2 < srclength) {
+		input[0] = *src++;
+		input[1] = *src++;
+		input[2] = *src++;
+		srclength -= 3;
+
+		output[0] = input[0] >> 2;
+		output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
+		output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
+		output[3] = input[2] & 0x3f;
+		Assert(output[0] < 64);
+		Assert(output[1] < 64);
+		Assert(output[2] < 64);
+		Assert(output[3] < 64);
+
+		if (datalength + 4 > targsize) {
+			return (-1);
+		}
+		target[datalength++] = Base64[output[0]];
+		target[datalength++] = Base64[output[1]];
+		target[datalength++] = Base64[output[2]];
+		target[datalength++] = Base64[output[3]];
+	}
+    
+	/* Now we worry about padding. */
+	if (0 != srclength) {
+		/* Get what's left. */
+		input[0] = input[1] = input[2] = (uint8_t) '\0';
+		for (i = 0; i < srclength; i++)
+			input[i] = *src++;
+	
+		output[0] = input[0] >> 2;
+		output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
+		output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
+		Assert(output[0] < 64);
+		Assert(output[1] < 64);
+		Assert(output[2] < 64);
+
+		if (datalength + 4 > targsize) {
+			return (-2);
+		}
+		target[datalength++] = Base64[output[0]];
+		target[datalength++] = Base64[output[1]];
+		if (srclength == 1) {
+			target[datalength++] = Pad64;
+		} else {
+			target[datalength++] = Base64[output[2]];
+		}
+		target[datalength++] = Pad64;
+	}
+	if (datalength >= targsize) {
+		return (-3);
+	}
+	target[datalength] = '\0';	/* Returned value doesn't count \0. */
+	return (int) (datalength);
+}
diff --git a/3rdParty/Ldns/src/src/compat/b64_pton.c b/3rdParty/Ldns/src/src/compat/b64_pton.c
new file mode 100644
index 0000000..aa637d2
--- /dev/null
+++ b/3rdParty/Ldns/src/src/compat/b64_pton.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 1996, 1998 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+ * Portions Copyright (c) 1995 by International Business Machines, Inc.
+ *
+ * International Business Machines, Inc. (hereinafter called IBM) grants
+ * permission under its copyrights to use, copy, modify, and distribute this
+ * Software with or without fee, provided that the above copyright notice and
+ * all paragraphs of this notice appear in all copies, and that the name of IBM
+ * not be used in connection with the marketing of any product incorporating
+ * the Software or modifications thereof, without specific, written prior
+ * permission.
+ *
+ * To the extent it has a right to do so, IBM grants an immunity from suit
+ * under its patents, if any, for the use, sale or manufacture of products to
+ * the extent that such products are used for performing Domain Name System
+ * dynamic updates in TCP/IP networks by means of the Software.  No immunity is
+ * granted for any product per se or for any other function of any product.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE.  IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
+ * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
+ * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+#include <ldns/config.h>
+
+#include <sys/types.h>
+#include <sys/param.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define Assert(Cond) if (!(Cond)) abort()
+
+static const char Base64[] =
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char Pad64 = '=';
+
+/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt)
+   The following encoding technique is taken from RFC 1521 by Borenstein
+   and Freed.  It is reproduced here in a slightly edited form for
+   convenience.
+
+   A 65-character subset of US-ASCII is used, enabling 6 bits to be
+   represented per printable character. (The extra 65th character, "=",
+   is used to signify a special processing function.)
+
+   The encoding process represents 24-bit groups of input bits as output
+   strings of 4 encoded characters. Proceeding from left to right, a
+   24-bit input group is formed by concatenating 3 8-bit input groups.
+   These 24 bits are then treated as 4 concatenated 6-bit groups, each
+   of which is translated into a single digit in the base64 alphabet.
+
+   Each 6-bit group is used as an index into an array of 64 printable
+   characters. The character referenced by the index is placed in the
+   output string.
+
+                         Table 1: The Base64 Alphabet
+
+      Value Encoding  Value Encoding  Value Encoding  Value Encoding
+          0 A            17 R            34 i            51 z
+          1 B            18 S            35 j            52 0
+          2 C            19 T            36 k            53 1
+          3 D            20 U            37 l            54 2
+          4 E            21 V            38 m            55 3
+          5 F            22 W            39 n            56 4
+          6 G            23 X            40 o            57 5
+          7 H            24 Y            41 p            58 6
+          8 I            25 Z            42 q            59 7
+          9 J            26 a            43 r            60 8
+         10 K            27 b            44 s            61 9
+         11 L            28 c            45 t            62 +
+         12 M            29 d            46 u            63 /
+         13 N            30 e            47 v
+         14 O            31 f            48 w         (pad) =
+         15 P            32 g            49 x
+         16 Q            33 h            50 y
+
+   Special processing is performed if fewer than 24 bits are available
+   at the end of the data being encoded.  A full encoding quantum is
+   always completed at the end of a quantity.  When fewer than 24 input
+   bits are available in an input group, zero bits are added (on the
+   right) to form an integral number of 6-bit groups.  Padding at the
+   end of the data is performed using the '=' character.
+
+   Since all base64 input is an integral number of octets, only the
+         -------------------------------------------------                       
+   following cases can arise:
+   
+       (1) the final quantum of encoding input is an integral
+           multiple of 24 bits; here, the final unit of encoded
+	   output will be an integral multiple of 4 characters
+	   with no "=" padding,
+       (2) the final quantum of encoding input is exactly 8 bits;
+           here, the final unit of encoded output will be two
+	   characters followed by two "=" padding characters, or
+       (3) the final quantum of encoding input is exactly 16 bits;
+           here, the final unit of encoded output will be three
+	   characters followed by one "=" padding character.
+   */
+
+/* skips all whitespace anywhere.
+   converts characters, four at a time, starting at (or after)
+   src from base - 64 numbers into three 8 bit bytes in the target area.
+   it returns the number of data bytes stored at the target, or -1 on error.
+ */
+
+int
+ldns_b64_pton(char const *src, uint8_t *target, size_t targsize)
+{
+	int tarindex, state, ch;
+	char *pos;
+
+	state = 0;
+	tarindex = 0;
+
+	if (strlen(src) == 0) {
+		return 0;
+	}
+
+	while ((ch = *src++) != '\0') {
+		if (isspace((unsigned char)ch))        /* Skip whitespace anywhere. */
+			continue;
+
+		if (ch == Pad64)
+			break;
+
+		pos = strchr(Base64, ch);
+		if (pos == 0) {
+			/* A non-base64 character. */
+			return (-1);
+		}
+
+		switch (state) {
+		case 0:
+			if (target) {
+				if ((size_t)tarindex >= targsize)
+					return (-1);
+				target[tarindex] = (pos - Base64) << 2;
+			}
+			state = 1;
+			break;
+		case 1:
+			if (target) {
+				if ((size_t)tarindex + 1 >= targsize)
+					return (-1);
+				target[tarindex]   |=  (pos - Base64) >> 4;
+				target[tarindex+1]  = ((pos - Base64) & 0x0f)
+							<< 4 ;
+			}
+			tarindex++;
+			state = 2;
+			break;
+		case 2:
+			if (target) {
+				if ((size_t)tarindex + 1 >= targsize)
+					return (-1);
+				target[tarindex]   |=  (pos - Base64) >> 2;
+				target[tarindex+1]  = ((pos - Base64) & 0x03)
+							<< 6;
+			}
+			tarindex++;
+			state = 3;
+			break;
+		case 3:
+			if (target) {
+				if ((size_t)tarindex >= targsize)
+					return (-1);
+				target[tarindex] |= (pos - Base64);
+			}
+			tarindex++;
+			state = 0;
+			break;
+		default:
+			abort();
+		}
+	}
+
+	/*
+	 * We are done decoding Base-64 chars.  Let's see if we ended
+	 * on a byte boundary, and/or with erroneous trailing characters.
+	 */
+
+	if (ch == Pad64) {		/* We got a pad char. */
+		ch = *src++;		/* Skip it, get next. */
+		switch (state) {
+		case 0:		/* Invalid = in first position */
+		case 1:		/* Invalid = in second position */
+			return (-1);
+
+		case 2:		/* Valid, means one byte of info */
+			/* Skip any number of spaces. */
+			for ((void)NULL; ch != '\0'; ch = *src++)
+				if (!isspace((unsigned char)ch))
+					break;
+			/* Make sure there is another trailing = sign. */
+			if (ch != Pad64)
+				return (-1);
+			ch = *src++;		/* Skip the = */
+			/* Fall through to "single trailing =" case. */
+			/* FALLTHROUGH */
+
+		case 3:		/* Valid, means two bytes of info */
+			/*
+			 * We know this char is an =.  Is there anything but
+			 * whitespace after it?
+			 */
+			for ((void)NULL; ch != '\0'; ch = *src++)
+				if (!isspace((unsigned char)ch))
+					return (-1);
+
+			/*
+			 * Now make sure for cases 2 and 3 that the "extra"
+			 * bits that slopped past the last full byte were
+			 * zeros.  If we don't check them, they become a
+			 * subliminal channel.
+			 */
+			if (target && target[tarindex] != 0)
+				return (-1);
+		}
+	} else {
+		/*
+		 * We ended by seeing the end of the string.  Make sure we
+		 * have no partial bytes lying around.
+		 */
+		if (state != 0)
+			return (-1);
+	}
+
+	return (tarindex);
+}
diff --git a/3rdParty/Ldns/src/src/dname.c b/3rdParty/Ldns/src/src/dname.c
new file mode 100644
index 0000000..0e63ef2
--- /dev/null
+++ b/3rdParty/Ldns/src/src/dname.c
@@ -0,0 +1,567 @@
+/*
+ * dname.c
+ *
+ * dname specific rdata implementations
+ * A dname is a rdf structure with type LDNS_RDF_TYPE_DNAME
+ * It is not a /real/ type! All function must therefor check
+ * for LDNS_RDF_TYPE_DNAME.
+ *
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2004-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+#include <ldns/config.h>
+
+#include <ldns/ldns.h>
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+ldns_rdf *
+ldns_dname_cat_clone(const ldns_rdf *rd1, const ldns_rdf *rd2)
+{
+	ldns_rdf *new;
+	uint16_t new_size;
+	uint8_t *buf;
+	uint16_t left_size;
+
+	if (ldns_rdf_get_type(rd1) != LDNS_RDF_TYPE_DNAME ||
+			ldns_rdf_get_type(rd2) != LDNS_RDF_TYPE_DNAME) {
+		return NULL;
+	}
+
+	/* remove root label if it is present at the end of the left
+	 * rd, by reducing the size with 1
+	 */
+	left_size = ldns_rdf_size(rd1);
+	if (left_size > 0 &&ldns_rdf_data(rd1)[left_size - 1] == 0) {
+		left_size--;
+	}
+
+	/* we overwrite the nullbyte of rd1 */
+	new_size = left_size + ldns_rdf_size(rd2);
+	buf = LDNS_XMALLOC(uint8_t, new_size);
+	if (!buf) {
+		return NULL;
+	}
+
+	/* put the two dname's after each other */
+	memcpy(buf, ldns_rdf_data(rd1), left_size);
+	memcpy(buf + left_size, ldns_rdf_data(rd2), ldns_rdf_size(rd2));
+
+	new = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_DNAME, new_size, buf);
+
+	LDNS_FREE(buf);
+	return new;
+}
+
+ldns_status
+ldns_dname_cat(ldns_rdf *rd1, ldns_rdf *rd2)
+{
+	uint16_t left_size;
+	uint16_t size;
+	uint8_t* newd;
+
+	if (ldns_rdf_get_type(rd1) != LDNS_RDF_TYPE_DNAME ||
+			ldns_rdf_get_type(rd2) != LDNS_RDF_TYPE_DNAME) {
+		return LDNS_STATUS_ERR;
+	}
+
+	/* remove root label if it is present at the end of the left
+	 * rd, by reducing the size with 1
+	 */
+	left_size = ldns_rdf_size(rd1);
+	if (left_size > 0 &&ldns_rdf_data(rd1)[left_size - 1] == 0) {
+		left_size--;
+	}
+        if(left_size == 0) {
+                return LDNS_STATUS_OK;
+        }
+
+	size = left_size + ldns_rdf_size(rd2);
+	newd = LDNS_XREALLOC(ldns_rdf_data(rd1), uint8_t, size);
+	if(!newd) {
+		return LDNS_STATUS_MEM_ERR;
+	}
+
+	ldns_rdf_set_data(rd1, newd);
+	memcpy(ldns_rdf_data(rd1) + left_size, ldns_rdf_data(rd2),
+			ldns_rdf_size(rd2));
+	ldns_rdf_set_size(rd1, size);
+
+	return LDNS_STATUS_OK;
+}
+
+ldns_rdf *
+ldns_dname_reverse(const ldns_rdf *d)
+{
+	ldns_rdf *new;
+	ldns_rdf *tmp;
+	ldns_rdf *d_tmp;
+	ldns_status status;
+
+	d_tmp = ldns_rdf_clone(d);
+
+	new = ldns_dname_new_frm_str(".");
+        if(!new)
+                return NULL;
+
+	while(ldns_dname_label_count(d_tmp) > 0) {
+		tmp = ldns_dname_label(d_tmp, 0);
+		status = ldns_dname_cat(tmp, new);
+                if(status != LDNS_STATUS_OK) {
+                        ldns_rdf_deep_free(new);
+	                ldns_rdf_deep_free(d_tmp);
+                        return NULL;
+                }
+		ldns_rdf_deep_free(new);
+		new = tmp;
+		tmp = ldns_dname_left_chop(d_tmp);
+		ldns_rdf_deep_free(d_tmp);
+		d_tmp = tmp;
+	}
+	ldns_rdf_deep_free(d_tmp);
+
+	return new;
+}
+
+ldns_rdf *
+ldns_dname_clone_from(const ldns_rdf *d, uint16_t n)
+{
+	uint8_t *data;
+	uint8_t label_size;
+	size_t data_size;
+
+	if (!d ||
+	    ldns_rdf_get_type(d) != LDNS_RDF_TYPE_DNAME ||
+	    ldns_dname_label_count(d) < n) {
+		return NULL;
+	}
+
+	data = ldns_rdf_data(d);
+	data_size = ldns_rdf_size(d);
+	while (n > 0) {
+		label_size = data[0] + 1;
+		data += label_size;
+		if (data_size < label_size) {
+			/* this label is very broken */
+			return NULL;
+		}
+		data_size -= label_size;
+		n--;
+	}
+
+	return ldns_dname_new_frm_data(data_size, data);
+}
+
+ldns_rdf *
+ldns_dname_left_chop(const ldns_rdf *d)
+{
+	uint8_t label_pos;
+	ldns_rdf *chop;
+
+	if (!d) {
+		return NULL;
+	}
+
+	if (ldns_rdf_get_type(d) != LDNS_RDF_TYPE_DNAME) {
+		return NULL;
+	}
+	if (ldns_dname_label_count(d) == 0) {
+		/* root label */
+		return NULL;
+	}
+	/* 05blaat02nl00 */
+	label_pos = ldns_rdf_data(d)[0];
+
+	chop = ldns_dname_new_frm_data(ldns_rdf_size(d) - label_pos - 1,
+			ldns_rdf_data(d) + label_pos + 1);
+	return chop;
+}
+
+uint8_t
+ldns_dname_label_count(const ldns_rdf *r)
+{
+        uint16_t src_pos;
+        uint16_t len;
+        uint8_t i;
+        size_t r_size;
+
+	if (!r) {
+		return 0;
+	}
+
+	i = 0;
+	src_pos = 0;
+	r_size = ldns_rdf_size(r);
+
+	if (ldns_rdf_get_type(r) != LDNS_RDF_TYPE_DNAME) {
+		return 0;
+	} else {
+		len = ldns_rdf_data(r)[src_pos]; /* start of the label */
+
+		/* single root label */
+		if (1 == r_size) {
+			return 0;
+		} else {
+			while ((len > 0) && src_pos < r_size) {
+				src_pos++;
+				src_pos += len;
+				len = ldns_rdf_data(r)[src_pos];
+				i++;
+			}
+		}
+	}
+	return i;
+}
+
+ldns_rdf *
+ldns_dname_new(uint16_t s, void *d)
+{
+        ldns_rdf *rd;
+
+        rd = LDNS_MALLOC(ldns_rdf);
+        if (!rd) {
+                return NULL;
+        }
+        ldns_rdf_set_size(rd, s);
+        ldns_rdf_set_type(rd, LDNS_RDF_TYPE_DNAME);
+        ldns_rdf_set_data(rd, d);
+        return rd;
+}
+
+ldns_rdf *
+ldns_dname_new_frm_str(const char *str)
+{
+	return ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, str);
+}
+
+ldns_rdf *
+ldns_dname_new_frm_data(uint16_t size, const void *data)
+{
+	return ldns_rdf_new_frm_data(LDNS_RDF_TYPE_DNAME, size, data);
+}
+
+void
+ldns_dname2canonical(const ldns_rdf *rd)
+{
+	uint8_t *rdd;
+	uint16_t i;
+
+	if (ldns_rdf_get_type(rd) != LDNS_RDF_TYPE_DNAME) {
+		return;
+	}
+
+	rdd = (uint8_t*)ldns_rdf_data(rd);
+	for (i = 0; i < ldns_rdf_size(rd); i++, rdd++) {
+		*rdd = (uint8_t)LDNS_DNAME_NORMALIZE((int)*rdd);
+	}
+}
+
+bool
+ldns_dname_is_subdomain(const ldns_rdf *sub, const ldns_rdf *parent)
+{
+	uint8_t sub_lab;
+	uint8_t par_lab;
+	int8_t i, j;
+	ldns_rdf *tmp_sub = NULL;
+	ldns_rdf *tmp_par = NULL;
+    ldns_rdf *sub_clone;
+    ldns_rdf *parent_clone;
+    bool result = true;
+
+	if (ldns_rdf_get_type(sub) != LDNS_RDF_TYPE_DNAME ||
+			ldns_rdf_get_type(parent) != LDNS_RDF_TYPE_DNAME ||
+			ldns_rdf_compare(sub, parent) == 0) {
+		return false;
+	}
+
+    /* would be nicer if we do not have to clone... */
+    sub_clone = ldns_dname_clone_from(sub, 0);
+    parent_clone = ldns_dname_clone_from(parent, 0);
+    ldns_dname2canonical(sub_clone);
+    ldns_dname2canonical(parent_clone);
+
+	sub_lab = ldns_dname_label_count(sub_clone);
+	par_lab = ldns_dname_label_count(parent_clone);
+
+	/* if sub sits above parent, it cannot be a child/sub domain */
+	if (sub_lab < par_lab) {
+		result = false;
+	} else {
+		/* check all labels the from the parent labels, from right to left.
+		 * When they /all/ match we have found a subdomain
+		 */
+		j = sub_lab - 1; /* we count from zero, thank you */
+		for (i = par_lab -1; i >= 0; i--) {
+			tmp_sub = ldns_dname_label(sub_clone, j);
+			tmp_par = ldns_dname_label(parent_clone, i);
+			if (!tmp_sub || !tmp_par) {
+				/* deep free does null check */
+				ldns_rdf_deep_free(tmp_sub);
+				ldns_rdf_deep_free(tmp_par);
+				result = false;
+				break;
+			}
+
+			if (ldns_rdf_compare(tmp_sub, tmp_par) != 0) {
+				/* they are not equal */
+				ldns_rdf_deep_free(tmp_sub);
+				ldns_rdf_deep_free(tmp_par);
+				result = false;
+				break;
+			}
+			ldns_rdf_deep_free(tmp_sub);
+			ldns_rdf_deep_free(tmp_par);
+			j--;
+		}
+	}
+	ldns_rdf_deep_free(sub_clone);
+	ldns_rdf_deep_free(parent_clone);
+	return result;
+}
+
+int
+ldns_dname_compare(const ldns_rdf *dname1, const ldns_rdf *dname2)
+{
+	size_t lc1, lc2, lc1f, lc2f;
+	size_t i;
+	int result = 0;
+	uint8_t *lp1, *lp2;
+
+	/* see RFC4034 for this algorithm */
+	/* this algorithm assumes the names are normalized to case */
+
+        /* only when both are not NULL we can say anything about them */
+        if (!dname1 && !dname2) {
+                return 0;
+        }
+        if (!dname1 || !dname2) {
+                return -1;
+        }
+	/* asserts must happen later as we are looking in the
+	 * dname, which could be NULL. But this case is handled
+	 * above
+	 */
+	assert(ldns_rdf_get_type(dname1) == LDNS_RDF_TYPE_DNAME);
+	assert(ldns_rdf_get_type(dname2) == LDNS_RDF_TYPE_DNAME);
+
+	lc1 = ldns_dname_label_count(dname1);
+	lc2 = ldns_dname_label_count(dname2);
+
+	if (lc1 == 0 && lc2 == 0) {
+		return 0;
+	}
+	if (lc1 == 0) {
+		return -1;
+	}
+	if (lc2 == 0) {
+		return 1;
+	}
+	lc1--;
+	lc2--;
+	/* we start at the last label */
+	while (true) {
+		/* find the label first */
+		lc1f = lc1;
+		lp1 = ldns_rdf_data(dname1);
+		while (lc1f > 0) {
+			lp1 += *lp1 + 1;
+			lc1f--;
+		}
+
+		/* and find the other one */
+		lc2f = lc2;
+		lp2 = ldns_rdf_data(dname2);
+		while (lc2f > 0) {
+			lp2 += *lp2 + 1;
+			lc2f--;
+		}
+
+		/* now check the label character for character. */
+		for (i = 1; i < (size_t)(*lp1 + 1); i++) {
+			if (i > *lp2) {
+				/* apparently label 1 is larger */
+				result = 1;
+				goto done;
+			}
+			if (LDNS_DNAME_NORMALIZE((int) *(lp1 + i)) <
+			    LDNS_DNAME_NORMALIZE((int) *(lp2 + i))) {
+			    result = -1;
+			    goto done;
+			} else if (LDNS_DNAME_NORMALIZE((int) *(lp1 + i)) >
+			    LDNS_DNAME_NORMALIZE((int) *(lp2 + i))) {
+			    result = 1;
+			    goto done;
+			}
+		}
+		if (*lp1 < *lp2) {
+			/* apparently label 2 is larger */
+			result = -1;
+			goto done;
+		}
+		if (lc1 == 0 && lc2 > 0) {
+			result = -1;
+			goto done;
+		} else if (lc1 > 0 && lc2 == 0) {
+			result = 1;
+			goto done;
+		} else if (lc1 == 0 && lc2 == 0) {
+			result = 0;
+			goto done;
+		}
+		lc1--;
+		lc2--;
+	}
+
+	done:
+	return result;
+}
+
+int
+ldns_dname_is_wildcard(const ldns_rdf* dname)
+{
+	return ( ldns_dname_label_count(dname) > 0 &&
+		 ldns_rdf_data(dname)[0] == 1 &&
+		 ldns_rdf_data(dname)[1] == '*');
+}
+
+int
+ldns_dname_match_wildcard(const ldns_rdf *dname, const ldns_rdf *wildcard)
+{
+	ldns_rdf *wc_chopped;
+	int result;
+	/* check whether it really is a wildcard */
+	if (ldns_dname_is_wildcard(wildcard)) {
+		/* ok, so the dname needs to be a subdomain of the wildcard
+		 * without the *
+		 */
+		wc_chopped = ldns_dname_left_chop(wildcard);
+		result = (int) ldns_dname_is_subdomain(dname, wc_chopped);
+		ldns_rdf_deep_free(wc_chopped);
+	} else {
+		result = (ldns_dname_compare(dname, wildcard) == 0);
+	}
+	return result;
+}
+
+/* nsec test: does prev <= middle < next
+ * -1 = yes
+ * 0 = error/can't tell
+ * 1 = no
+ */
+int
+ldns_dname_interval(const ldns_rdf *prev, const ldns_rdf *middle,
+		const ldns_rdf *next)
+{
+	int prev_check, next_check;
+
+	assert(ldns_rdf_get_type(prev) == LDNS_RDF_TYPE_DNAME);
+	assert(ldns_rdf_get_type(middle) == LDNS_RDF_TYPE_DNAME);
+	assert(ldns_rdf_get_type(next) == LDNS_RDF_TYPE_DNAME);
+
+	prev_check = ldns_dname_compare(prev, middle);
+	next_check = ldns_dname_compare(middle, next);
+	/* <= next. This cannot be the case for nsec, because then we would
+	 * have gotten the nsec of next...
+	 */
+	if (next_check == 0) {
+		return 0;
+	}
+
+			/* <= */
+	if ((prev_check == -1 || prev_check == 0) &&
+			/* < */
+			next_check == -1) {
+		return -1;
+	} else {
+		return 1;
+	}
+}
+
+
+bool
+ldns_dname_str_absolute(const char *dname_str)
+{
+        const char* s;
+	if(dname_str && strcmp(dname_str, ".") == 0)
+		return 1;
+        if(!dname_str || strlen(dname_str) < 2)
+                return 0;
+        if(dname_str[strlen(dname_str) - 1] != '.')
+                return 0;
+        if(dname_str[strlen(dname_str) - 2] != '\\')
+                return 1; /* ends in . and no \ before it */
+        /* so we have the case of ends in . and there is \ before it */
+        for(s=dname_str; *s; s++) {
+                if(*s == '\\') {
+                        if(s[1] && s[2] && s[3] /* check length */
+                                && isdigit(s[1]) && isdigit(s[2]) && 
+                                isdigit(s[3]))
+                                s += 3;
+                        else if(!s[1] || isdigit(s[1])) /* escape of nul,0-9 */
+                                return 0; /* parse error */
+                        else s++; /* another character escaped */
+                }
+                else if(!*(s+1) && *s == '.')
+                        return 1; /* trailing dot, unescaped */
+        }
+        return 0;
+}
+
+ldns_rdf *
+ldns_dname_label(const ldns_rdf *rdf, uint8_t labelpos)
+{
+	uint8_t labelcnt;
+	uint16_t src_pos;
+	uint16_t len;
+	ldns_rdf *tmpnew;
+	size_t s;
+
+	if (ldns_rdf_get_type(rdf) != LDNS_RDF_TYPE_DNAME) {
+		return NULL;
+	}
+
+	labelcnt = 0;
+	src_pos = 0;
+	s = ldns_rdf_size(rdf);
+
+	len = ldns_rdf_data(rdf)[src_pos]; /* label start */
+	while ((len > 0) && src_pos < s) {
+		if (labelcnt == labelpos) {
+			/* found our label */
+			tmpnew = LDNS_MALLOC(ldns_rdf);
+			if (!tmpnew) {
+				return NULL;
+			}
+			tmpnew->_type = LDNS_RDF_TYPE_DNAME;
+			tmpnew->_data = LDNS_XMALLOC(uint8_t, len + 2);
+			if (!tmpnew->_data) {
+				LDNS_FREE(tmpnew);
+				return NULL;
+			}
+			memset(tmpnew->_data, 0, len + 2);
+			memcpy(tmpnew->_data, ldns_rdf_data(rdf) + src_pos, len + 1);
+			tmpnew->_size = len + 2;
+			return tmpnew;
+		}
+		src_pos++;
+		src_pos += len;
+		len = ldns_rdf_data(rdf)[src_pos];
+		labelcnt++;
+	}
+	return NULL;
+}
diff --git a/3rdParty/Ldns/src/src/dnssec.c b/3rdParty/Ldns/src/src/dnssec.c
new file mode 100644
index 0000000..c419437
--- /dev/null
+++ b/3rdParty/Ldns/src/src/dnssec.c
@@ -0,0 +1,1766 @@
+/*
+ * dnssec.c
+ *
+ * contains the cryptographic function needed for DNSSEC in ldns
+ * The crypto library used is openssl
+ *
+ * (c) NLnet Labs, 2004-2008
+ *
+ * See the file LICENSE for the license
+ */
+
+#include <ldns/config.h>
+
+#include <ldns/ldns.h>
+#include <ldns/dnssec.h>
+
+#include <strings.h>
+#include <time.h>
+
+#ifdef HAVE_SSL
+#include <openssl/ssl.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#include <openssl/err.h>
+#include <openssl/md5.h>
+#endif
+
+ldns_rr *
+ldns_dnssec_get_rrsig_for_name_and_type(const ldns_rdf *name,
+                                        const ldns_rr_type type,
+                                        const ldns_rr_list *rrs)
+{
+	size_t i;
+	ldns_rr *candidate;
+
+	if (!name || !rrs) {
+		return NULL;
+	}
+
+	for (i = 0; i < ldns_rr_list_rr_count(rrs); i++) {
+		candidate = ldns_rr_list_rr(rrs, i);
+		if (ldns_rr_get_type(candidate) == LDNS_RR_TYPE_RRSIG) {
+			if (ldns_dname_compare(ldns_rr_owner(candidate),
+			                       name) == 0 &&
+			    ldns_rdf2rr_type(ldns_rr_rrsig_typecovered(candidate))
+			    == type
+			    ) {
+				return candidate;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+ldns_rr *
+ldns_dnssec_get_dnskey_for_rrsig(const ldns_rr *rrsig,
+						   const ldns_rr_list *rrs)
+{
+	size_t i;
+	ldns_rr *candidate;
+
+	if (!rrsig || !rrs) {
+		return NULL;
+	}
+
+	for (i = 0; i < ldns_rr_list_rr_count(rrs); i++) {
+		candidate = ldns_rr_list_rr(rrs, i);
+		if (ldns_rr_get_type(candidate) == LDNS_RR_TYPE_DNSKEY) {
+			if (ldns_dname_compare(ldns_rr_owner(candidate),
+			                       ldns_rr_rrsig_signame(rrsig)) == 0 &&
+			    ldns_rdf2native_int16(ldns_rr_rrsig_keytag(rrsig)) ==
+			    ldns_calc_keytag(candidate)
+			    ) {
+				return candidate;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+ldns_rdf *
+ldns_nsec_get_bitmap(ldns_rr *nsec) {
+	if (ldns_rr_get_type(nsec) == LDNS_RR_TYPE_NSEC) {
+		return ldns_rr_rdf(nsec, 1);
+	} else if (ldns_rr_get_type(nsec) == LDNS_RR_TYPE_NSEC3) {
+		return ldns_rr_rdf(nsec, 5);
+	} else {
+		return NULL;
+	}
+}
+
+/*return the owner name of the closest encloser for name from the list of rrs */
+/* this is NOT the hash, but the original name! */
+ldns_rdf *
+ldns_dnssec_nsec3_closest_encloser(ldns_rdf *qname,
+                                   ATTR_UNUSED(ldns_rr_type qtype),
+                                   ldns_rr_list *nsec3s)
+{
+	/* remember parameters, they must match */
+	uint8_t algorithm;
+	uint32_t iterations;
+	uint8_t salt_length;
+	uint8_t *salt;
+
+	ldns_rdf *sname, *hashed_sname, *tmp;
+	bool flag;
+
+	bool exact_match_found;
+	bool in_range_found;
+
+	ldns_status status;
+	ldns_rdf *zone_name;
+
+	size_t nsec_i;
+	ldns_rr *nsec;
+	ldns_rdf *result = NULL;
+	qtype = qtype;
+
+	if (!qname || !nsec3s || ldns_rr_list_rr_count(nsec3s) < 1) {
+		return NULL;
+	}
+
+	nsec = ldns_rr_list_rr(nsec3s, 0);
+	algorithm = ldns_nsec3_algorithm(nsec);
+	salt_length = ldns_nsec3_salt_length(nsec);
+	salt = ldns_nsec3_salt_data(nsec);
+	iterations = ldns_nsec3_iterations(nsec);
+
+	sname = ldns_rdf_clone(qname);
+
+	flag = false;
+
+	zone_name = ldns_dname_left_chop(ldns_rr_owner(nsec));
+
+	/* algorithm from nsec3-07 8.3 */
+	while (ldns_dname_label_count(sname) > 0) {
+		exact_match_found = false;
+		in_range_found = false;
+
+		hashed_sname = ldns_nsec3_hash_name(sname,
+									 algorithm,
+									 iterations,
+									 salt_length,
+									 salt);
+
+		status = ldns_dname_cat(hashed_sname, zone_name);
+                if(status != LDNS_STATUS_OK) {
+	                LDNS_FREE(salt);
+	                ldns_rdf_deep_free(zone_name);
+	                ldns_rdf_deep_free(sname);
+                        return NULL;
+                }
+
+		for (nsec_i = 0; nsec_i < ldns_rr_list_rr_count(nsec3s); nsec_i++) {
+			nsec = ldns_rr_list_rr(nsec3s, nsec_i);
+
+			/* check values of iterations etc! */
+
+			/* exact match? */
+			if (ldns_dname_compare(ldns_rr_owner(nsec), hashed_sname) == 0) {
+			 	exact_match_found = true;
+			} else if (ldns_nsec_covers_name(nsec, hashed_sname)) {
+				in_range_found = true;
+			}
+
+		}
+		if (!exact_match_found && in_range_found) {
+			flag = true;
+		} else if (exact_match_found && flag) {
+			result = ldns_rdf_clone(sname);
+			/* RFC 5155: 8.3. 2.** "The proof is complete" */
+			ldns_rdf_deep_free(hashed_sname);
+			goto done;
+		} else if (exact_match_found && !flag) {
+			/* error! */
+			ldns_rdf_deep_free(hashed_sname);
+			goto done;
+		} else {
+			flag = false;
+		}
+
+		ldns_rdf_deep_free(hashed_sname);
+		tmp = sname;
+		sname = ldns_dname_left_chop(sname);
+		ldns_rdf_deep_free(tmp);
+	}
+
+	done:
+	LDNS_FREE(salt);
+	ldns_rdf_deep_free(zone_name);
+	ldns_rdf_deep_free(sname);
+
+	return result;
+}
+
+bool
+ldns_dnssec_pkt_has_rrsigs(const ldns_pkt *pkt)
+{
+	size_t i;
+	for (i = 0; i < ldns_pkt_ancount(pkt); i++) {
+		if (ldns_rr_get_type(ldns_rr_list_rr(ldns_pkt_answer(pkt), i)) ==
+		    LDNS_RR_TYPE_RRSIG) {
+			return true;
+		}
+	}
+	for (i = 0; i < ldns_pkt_nscount(pkt); i++) {
+		if (ldns_rr_get_type(ldns_rr_list_rr(ldns_pkt_authority(pkt), i)) ==
+		    LDNS_RR_TYPE_RRSIG) {
+			return true;
+		}
+	}
+	return false;
+}
+
+ldns_rr_list *
+ldns_dnssec_pkt_get_rrsigs_for_name_and_type(const ldns_pkt *pkt,
+									ldns_rdf *name,
+									ldns_rr_type type)
+{
+	uint16_t t_netorder;
+	ldns_rr_list *sigs;
+	ldns_rr_list *sigs_covered;
+	ldns_rdf *rdf_t;
+	
+	sigs = ldns_pkt_rr_list_by_name_and_type(pkt,
+									 name,
+									 LDNS_RR_TYPE_RRSIG,
+									 LDNS_SECTION_ANY_NOQUESTION
+									 );
+
+	t_netorder = htons(type); /* rdf are in network order! */
+	rdf_t = ldns_rdf_new(LDNS_RDF_TYPE_TYPE, LDNS_RDF_SIZE_WORD, &t_netorder);
+	sigs_covered = ldns_rr_list_subtype_by_rdf(sigs, rdf_t, 0);
+	
+	ldns_rdf_free(rdf_t);
+	ldns_rr_list_deep_free(sigs);
+
+	return sigs_covered;
+
+}
+
+ldns_rr_list *
+ldns_dnssec_pkt_get_rrsigs_for_type(const ldns_pkt *pkt, ldns_rr_type type)
+{
+	uint16_t t_netorder;
+	ldns_rr_list *sigs;
+	ldns_rr_list *sigs_covered;
+	ldns_rdf *rdf_t;
+
+	sigs = ldns_pkt_rr_list_by_type(pkt,
+	                                LDNS_RR_TYPE_RRSIG,
+	                                LDNS_SECTION_ANY_NOQUESTION
+							  );
+
+	t_netorder = htons(type); /* rdf are in network order! */
+	rdf_t = ldns_rdf_new(LDNS_RDF_TYPE_TYPE,
+					 2,
+					 &t_netorder);
+	sigs_covered = ldns_rr_list_subtype_by_rdf(sigs, rdf_t, 0);
+
+	ldns_rdf_free(rdf_t);
+	ldns_rr_list_deep_free(sigs);
+
+	return sigs_covered;
+
+}
+
+/* used only on the public key RR */
+uint16_t
+ldns_calc_keytag(const ldns_rr *key)
+{
+	uint16_t ac16;
+	ldns_buffer *keybuf;
+	size_t keysize;
+
+	if (!key) {
+		return 0;
+	}
+
+	if (ldns_rr_get_type(key) != LDNS_RR_TYPE_DNSKEY &&
+	    ldns_rr_get_type(key) != LDNS_RR_TYPE_KEY
+	    ) {
+		return 0;
+	}
+
+	/* rdata to buf - only put the rdata in a buffer */
+	keybuf = ldns_buffer_new(LDNS_MIN_BUFLEN); /* grows */
+	if (!keybuf) {
+		return 0;
+	}
+	(void)ldns_rr_rdata2buffer_wire(keybuf, key);
+	/* the current pos in the buffer is the keysize */
+	keysize= ldns_buffer_position(keybuf);
+
+	ac16 = ldns_calc_keytag_raw(ldns_buffer_begin(keybuf), keysize);
+	ldns_buffer_free(keybuf);
+	return ac16;
+}
+
+uint16_t ldns_calc_keytag_raw(uint8_t* key, size_t keysize)
+{
+	unsigned int i;
+	uint32_t ac32;
+	uint16_t ac16;
+
+	if(keysize < 4) {
+		return 0;
+	}
+	/* look at the algorithm field, copied from 2535bis */
+	if (key[3] == LDNS_RSAMD5) {
+		ac16 = 0;
+		if (keysize > 4) {
+			memmove(&ac16, key + keysize - 3, 2);
+		}
+		ac16 = ntohs(ac16);
+		return (uint16_t) ac16;
+	} else {
+		ac32 = 0;
+		for (i = 0; (size_t)i < keysize; ++i) {
+			ac32 += (i & 1) ? key[i] : key[i] << 8;
+		}
+		ac32 += (ac32 >> 16) & 0xFFFF;
+		return (uint16_t) (ac32 & 0xFFFF);
+	}
+}
+
+#ifdef HAVE_SSL
+DSA *
+ldns_key_buf2dsa(ldns_buffer *key)
+{
+	return ldns_key_buf2dsa_raw((unsigned char*)ldns_buffer_begin(key),
+						   ldns_buffer_position(key));
+}
+
+DSA *
+ldns_key_buf2dsa_raw(unsigned char* key, size_t len)
+{
+	uint8_t T;
+	uint16_t length;
+	uint16_t offset;
+	DSA *dsa;
+	BIGNUM *Q; BIGNUM *P;
+	BIGNUM *G; BIGNUM *Y;
+
+	if(len == 0)
+		return NULL;
+	T = (uint8_t)key[0];
+	length = (64 + T * 8);
+	offset = 1;
+
+	if (T > 8) {
+		return NULL;
+	}
+	if(len < (size_t)1 + SHA_DIGEST_LENGTH + 3*length)
+		return NULL;
+
+	Q = BN_bin2bn(key+offset, SHA_DIGEST_LENGTH, NULL);
+	offset += SHA_DIGEST_LENGTH;
+
+	P = BN_bin2bn(key+offset, (int)length, NULL);
+	offset += length;
+
+	G = BN_bin2bn(key+offset, (int)length, NULL);
+	offset += length;
+
+	Y = BN_bin2bn(key+offset, (int)length, NULL);
+	offset += length;
+
+	/* create the key and set its properties */
+	if(!Q || !P || !G || !Y || !(dsa = DSA_new())) {
+		BN_free(Q);
+		BN_free(P);
+		BN_free(G);
+		BN_free(Y);
+		return NULL;
+	}
+#ifndef S_SPLINT_S
+	dsa->p = P;
+	dsa->q = Q;
+	dsa->g = G;
+	dsa->pub_key = Y;
+#endif /* splint */
+
+	return dsa;
+}
+
+RSA *
+ldns_key_buf2rsa(ldns_buffer *key)
+{
+	return ldns_key_buf2rsa_raw((unsigned char*)ldns_buffer_begin(key),
+						   ldns_buffer_position(key));
+}
+
+RSA *
+ldns_key_buf2rsa_raw(unsigned char* key, size_t len)
+{
+	uint16_t offset;
+	uint16_t exp;
+	uint16_t int16;
+	RSA *rsa;
+	BIGNUM *modulus;
+	BIGNUM *exponent;
+
+	if (len == 0)
+		return NULL;
+	if (key[0] == 0) {
+		if(len < 3)
+			return NULL;
+		/* need some smart comment here XXX*/
+		/* the exponent is too large so it's places
+		 * futher...???? */
+		memmove(&int16, key+1, 2);
+		exp = ntohs(int16);
+		offset = 3;
+	} else {
+		exp = key[0];
+		offset = 1;
+	}
+
+	/* key length at least one */
+	if(len < (size_t)offset + exp + 1)
+		return NULL;
+
+	/* Exponent */
+	exponent = BN_new();
+	if(!exponent) return NULL;
+	(void) BN_bin2bn(key+offset, (int)exp, exponent);
+	offset += exp;
+
+	/* Modulus */
+	modulus = BN_new();
+	if(!modulus) {
+		BN_free(exponent);
+		return NULL;
+	}
+	/* length of the buffer must match the key length! */
+	(void) BN_bin2bn(key+offset, (int)(len - offset), modulus);
+
+	rsa = RSA_new();
+	if(!rsa) {
+		BN_free(exponent);
+		BN_free(modulus);
+		return NULL;
+	}
+#ifndef S_SPLINT_S
+	rsa->n = modulus;
+	rsa->e = exponent;
+#endif /* splint */
+
+	return rsa;
+}
+
+int
+ldns_digest_evp(unsigned char* data, unsigned int len, unsigned char* dest,
+	const EVP_MD* md)
+{
+	EVP_MD_CTX* ctx;
+	ctx = EVP_MD_CTX_create();
+	if(!ctx)
+		return false;
+	if(!EVP_DigestInit_ex(ctx, md, NULL) ||
+		!EVP_DigestUpdate(ctx, data, len) ||
+		!EVP_DigestFinal_ex(ctx, dest, NULL)) {
+		EVP_MD_CTX_destroy(ctx);
+		return false;
+	}
+	EVP_MD_CTX_destroy(ctx);
+	return true;
+}
+#endif /* HAVE_SSL */
+
+ldns_rr *
+ldns_key_rr2ds(const ldns_rr *key, ldns_hash h)
+{
+	ldns_rdf *tmp;
+	ldns_rr *ds;
+	uint16_t keytag;
+	uint8_t  sha1hash;
+	uint8_t *digest;
+	ldns_buffer *data_buf;
+#ifdef USE_GOST
+	const EVP_MD* md = NULL;
+#endif
+
+	if (ldns_rr_get_type(key) != LDNS_RR_TYPE_DNSKEY) {
+		return NULL;
+	}
+
+	ds = ldns_rr_new();
+	if (!ds) {
+		return NULL;
+	}
+	ldns_rr_set_type(ds, LDNS_RR_TYPE_DS);
+	ldns_rr_set_owner(ds, ldns_rdf_clone(
+								  ldns_rr_owner(key)));
+	ldns_rr_set_ttl(ds, ldns_rr_ttl(key));
+	ldns_rr_set_class(ds, ldns_rr_get_class(key));
+
+	switch(h) {
+	default:
+	case LDNS_SHA1:
+		digest = LDNS_XMALLOC(uint8_t, LDNS_SHA1_DIGEST_LENGTH);
+		if (!digest) {
+			ldns_rr_free(ds);
+			return NULL;
+		}
+		break;
+	case LDNS_SHA256:
+		digest = LDNS_XMALLOC(uint8_t, LDNS_SHA256_DIGEST_LENGTH);
+		if (!digest) {
+			ldns_rr_free(ds);
+			return NULL;
+		}
+		break;
+	case LDNS_HASH_GOST:
+#ifdef USE_GOST
+		(void)ldns_key_EVP_load_gost_id();
+		md = EVP_get_digestbyname("md_gost94");
+		if(!md) {
+			ldns_rr_free(ds);
+			return NULL;
+		}
+		digest = LDNS_XMALLOC(uint8_t, EVP_MD_size(md));
+		if (!digest) {
+			ldns_rr_free(ds);
+			return NULL;
+		}
+                break;
+#else
+		/* not implemented */
+		ldns_rr_free(ds);
+		return NULL;
+#endif
+#ifdef USE_ECDSA
+		/* Make similar ``not implemented'' construct as above when 
+		   draft-hoffman-dnssec-ecdsa-04 becomes a standard
+		 */
+	case LDNS_SHA384:
+		digest = LDNS_XMALLOC(uint8_t, SHA384_DIGEST_LENGTH);
+		if (!digest) {
+			ldns_rr_free(ds);
+			return NULL;
+		}
+                break;
+#endif
+	}
+
+	data_buf = ldns_buffer_new(LDNS_MAX_PACKETLEN);
+	if (!data_buf) {
+		LDNS_FREE(digest);
+		ldns_rr_free(ds);
+		return NULL;
+	}
+
+	/* keytag */
+	keytag = htons(ldns_calc_keytag((ldns_rr*)key));
+	tmp = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_INT16,
+						   sizeof(uint16_t),
+						   &keytag);
+	ldns_rr_push_rdf(ds, tmp);
+
+	/* copy the algorithm field */
+	if ((tmp = ldns_rr_rdf(key, 2)) == NULL) {
+		LDNS_FREE(digest);
+		ldns_buffer_free(data_buf);
+		ldns_rr_free(ds);
+		return NULL;
+	} else {
+		ldns_rr_push_rdf(ds, ldns_rdf_clone( tmp )); 
+	}
+
+	/* digest hash type */
+	sha1hash = (uint8_t)h;
+	tmp = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_INT8,
+						   sizeof(uint8_t),
+						   &sha1hash);
+	ldns_rr_push_rdf(ds, tmp);
+
+	/* digest */
+	/* owner name */
+	tmp = ldns_rdf_clone(ldns_rr_owner(key));
+	ldns_dname2canonical(tmp);
+	if (ldns_rdf2buffer_wire(data_buf, tmp) != LDNS_STATUS_OK) {
+		LDNS_FREE(digest);
+		ldns_buffer_free(data_buf);
+		ldns_rr_free(ds);
+		ldns_rdf_deep_free(tmp);
+		return NULL;
+	}
+	ldns_rdf_deep_free(tmp);
+
+	/* all the rdata's */
+	if (ldns_rr_rdata2buffer_wire(data_buf,
+							(ldns_rr*)key) != LDNS_STATUS_OK) {
+		LDNS_FREE(digest);
+		ldns_buffer_free(data_buf);
+		ldns_rr_free(ds);
+		return NULL;
+	}
+	switch(h) {
+	case LDNS_SHA1:
+		(void) ldns_sha1((unsigned char *) ldns_buffer_begin(data_buf),
+		                 (unsigned int) ldns_buffer_position(data_buf),
+		                 (unsigned char *) digest);
+
+		tmp = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_HEX,
+		                            LDNS_SHA1_DIGEST_LENGTH,
+		                            digest);
+		ldns_rr_push_rdf(ds, tmp);
+
+		break;
+	case LDNS_SHA256:
+		(void) ldns_sha256((unsigned char *) ldns_buffer_begin(data_buf),
+		                   (unsigned int) ldns_buffer_position(data_buf),
+		                   (unsigned char *) digest);
+		tmp = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_HEX,
+		                            LDNS_SHA256_DIGEST_LENGTH,
+		                            digest);
+		ldns_rr_push_rdf(ds, tmp);
+		break;
+	case LDNS_HASH_GOST:
+#ifdef USE_GOST
+		if(!ldns_digest_evp((unsigned char *) ldns_buffer_begin(data_buf),
+				(unsigned int) ldns_buffer_position(data_buf),
+				(unsigned char *) digest, md)) {
+			LDNS_FREE(digest);
+			ldns_buffer_free(data_buf);
+			ldns_rr_free(ds);
+			return NULL;
+		}
+		tmp = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_HEX,
+		                            (size_t)EVP_MD_size(md),
+		                            digest);
+		ldns_rr_push_rdf(ds, tmp);
+#endif
+		break;
+#ifdef USE_ECDSA
+	case LDNS_SHA384:
+		(void) SHA384((unsigned char *) ldns_buffer_begin(data_buf),
+		                 (unsigned int) ldns_buffer_position(data_buf),
+		                 (unsigned char *) digest);
+		tmp = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_HEX,
+		                            SHA384_DIGEST_LENGTH,
+		                            digest);
+		ldns_rr_push_rdf(ds, tmp);
+		break;
+#endif
+	}
+
+	LDNS_FREE(digest);
+	ldns_buffer_free(data_buf);
+	return ds;
+}
+
+ldns_rdf *
+ldns_dnssec_create_nsec_bitmap(ldns_rr_type rr_type_list[],
+                               size_t size,
+                               ldns_rr_type nsec_type)
+{
+	size_t i;
+	uint8_t *bitmap;
+	uint16_t bm_len = 0;
+	uint16_t i_type;
+	ldns_rdf *bitmap_rdf;
+
+	uint8_t *data = NULL;
+	uint8_t cur_data[32];
+	uint8_t cur_window = 0;
+	uint8_t cur_window_max = 0;
+	uint16_t cur_data_size = 0;
+
+	if (nsec_type != LDNS_RR_TYPE_NSEC &&
+	    nsec_type != LDNS_RR_TYPE_NSEC3) {
+		return NULL;
+	}
+
+	i_type = 0;
+	for (i = 0; i < size; i++) {
+		if (i_type < rr_type_list[i])
+			i_type = rr_type_list[i];
+	}
+	if (i_type < nsec_type) {
+		i_type = nsec_type;
+	}
+
+	bm_len = i_type / 8 + 2;
+	bitmap = LDNS_XMALLOC(uint8_t, bm_len);
+        if(!bitmap) return NULL;
+	for (i = 0; i < bm_len; i++) {
+		bitmap[i] = 0;
+	}
+
+	for (i = 0; i < size; i++) {
+		i_type = rr_type_list[i];
+		ldns_set_bit(bitmap + (int) i_type / 8,
+				   (int) (7 - (i_type % 8)),
+				   true);
+	}
+
+	/* fold it into windows TODO: can this be done directly? */
+	memset(cur_data, 0, 32);
+	for (i = 0; i < bm_len; i++) {
+		if (i / 32 > cur_window) {
+			/* check, copy, new */
+			if (cur_window_max > 0) {
+				/* this window has stuff, add it */
+				data = LDNS_XREALLOC(data,
+								 uint8_t,
+								 cur_data_size + cur_window_max + 3);
+                                if(!data) {
+                                        LDNS_FREE(bitmap);
+                                        return NULL;
+                                }
+				data[cur_data_size] = cur_window;
+				data[cur_data_size + 1] = cur_window_max + 1;
+				memcpy(data + cur_data_size + 2,
+					  cur_data,
+					  cur_window_max+1);
+				cur_data_size += cur_window_max + 3;
+			}
+			cur_window++;
+			cur_window_max = 0;
+			memset(cur_data, 0, 32);
+		}
+		cur_data[i%32] = bitmap[i];
+		if (bitmap[i] > 0) {
+			cur_window_max = i%32;
+		}
+	}
+	if (cur_window_max > 0 || cur_data[0] != 0) {
+		/* this window has stuff, add it */
+		data = LDNS_XREALLOC(data,
+						 uint8_t,
+						 cur_data_size + cur_window_max + 3);
+                if(!data) {
+                        LDNS_FREE(bitmap);
+                        return NULL;
+                }
+		data[cur_data_size] = cur_window;
+		data[cur_data_size + 1] = cur_window_max + 1;
+		memcpy(data + cur_data_size + 2, cur_data, cur_window_max+1);
+		cur_data_size += cur_window_max + 3;
+	}
+
+	bitmap_rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_NSEC,
+								cur_data_size,
+								data);
+
+	LDNS_FREE(bitmap);
+	LDNS_FREE(data);
+
+	return bitmap_rdf;
+}
+
+int
+ldns_dnssec_rrsets_contains_type(ldns_dnssec_rrsets *rrsets,
+                                 ldns_rr_type type)
+{
+	ldns_dnssec_rrsets *cur_rrset = rrsets;
+	while (cur_rrset) {
+		if (cur_rrset->type == type) {
+			return 1;
+		}
+		cur_rrset = cur_rrset->next;
+	}
+	return 0;
+}
+
+ldns_rr *
+ldns_dnssec_create_nsec(ldns_dnssec_name *from,
+                        ldns_dnssec_name *to,
+                        ldns_rr_type nsec_type)
+{
+	ldns_rr *nsec_rr;
+	ldns_rr_type types[65536];
+	size_t type_count = 0;
+	ldns_dnssec_rrsets *cur_rrsets;
+	int on_delegation_point;
+
+	if (!from || !to || (nsec_type != LDNS_RR_TYPE_NSEC)) {
+		return NULL;
+	}
+
+	nsec_rr = ldns_rr_new();
+	ldns_rr_set_type(nsec_rr, nsec_type);
+	ldns_rr_set_owner(nsec_rr, ldns_rdf_clone(ldns_dnssec_name_name(from)));
+	ldns_rr_push_rdf(nsec_rr, ldns_rdf_clone(ldns_dnssec_name_name(to)));
+
+	on_delegation_point = ldns_dnssec_rrsets_contains_type(
+			from->rrsets, LDNS_RR_TYPE_NS)
+		&& !ldns_dnssec_rrsets_contains_type(
+			from->rrsets, LDNS_RR_TYPE_SOA);
+
+	cur_rrsets = from->rrsets;
+	while (cur_rrsets) {
+		/* Do not include non-authoritative rrsets on the delegation point
+		 * in the type bitmap */
+		if ((on_delegation_point && (
+				cur_rrsets->type == LDNS_RR_TYPE_NS 
+			     || cur_rrsets->type == LDNS_RR_TYPE_DS))
+			|| (!on_delegation_point &&
+				cur_rrsets->type != LDNS_RR_TYPE_RRSIG
+			     && cur_rrsets->type != LDNS_RR_TYPE_NSEC)) {
+
+			types[type_count] = cur_rrsets->type;
+			type_count++;
+		}
+		cur_rrsets = cur_rrsets->next;
+
+	}
+	types[type_count] = LDNS_RR_TYPE_RRSIG;
+	type_count++;
+	types[type_count] = LDNS_RR_TYPE_NSEC;
+	type_count++;
+
+	ldns_rr_push_rdf(nsec_rr, ldns_dnssec_create_nsec_bitmap(types,
+	                               type_count,
+	                               nsec_type));
+
+	return nsec_rr;
+}
+
+ldns_rr *
+ldns_dnssec_create_nsec3(ldns_dnssec_name *from,
+					ldns_dnssec_name *to,
+					ldns_rdf *zone_name,
+					uint8_t algorithm,
+					uint8_t flags,
+					uint16_t iterations,
+					uint8_t salt_length,
+					uint8_t *salt)
+{
+	ldns_rr *nsec_rr;
+	ldns_rr_type types[65536];
+	size_t type_count = 0;
+	ldns_dnssec_rrsets *cur_rrsets;
+	ldns_status status;
+	int on_delegation_point;
+
+	flags = flags;
+
+	if (!from) {
+		return NULL;
+	}
+
+	nsec_rr = ldns_rr_new_frm_type(LDNS_RR_TYPE_NSEC3);
+	ldns_rr_set_owner(nsec_rr,
+	                  ldns_nsec3_hash_name(ldns_dnssec_name_name(from),
+	                  algorithm,
+	                  iterations,
+	                  salt_length,
+	                  salt));
+	status = ldns_dname_cat(ldns_rr_owner(nsec_rr), zone_name);
+        if(status != LDNS_STATUS_OK) {
+                ldns_rr_free(nsec_rr);
+                return NULL;
+        }
+	ldns_nsec3_add_param_rdfs(nsec_rr,
+	                          algorithm,
+	                          flags,
+	                          iterations,
+	                          salt_length,
+	                          salt);
+
+	on_delegation_point = ldns_dnssec_rrsets_contains_type(
+			from->rrsets, LDNS_RR_TYPE_NS)
+		&& !ldns_dnssec_rrsets_contains_type(
+			from->rrsets, LDNS_RR_TYPE_SOA);
+	cur_rrsets = from->rrsets;
+	while (cur_rrsets) {
+		/* Do not include non-authoritative rrsets on the delegation point
+		 * in the type bitmap. Potentionally not skipping insecure
+		 * delegation should have been done earlier, in function
+		 * ldns_dnssec_zone_create_nsec3s, or even earlier in:
+		 * ldns_dnssec_zone_sign_nsec3_flg .
+		 */
+		if ((on_delegation_point && (
+				cur_rrsets->type == LDNS_RR_TYPE_NS
+			     || cur_rrsets->type == LDNS_RR_TYPE_DS))
+			|| (!on_delegation_point &&
+				cur_rrsets->type != LDNS_RR_TYPE_RRSIG)) {
+
+			types[type_count] = cur_rrsets->type;
+			type_count++;
+		}
+		cur_rrsets = cur_rrsets->next;
+	}
+	/* always add rrsig type if this is not an unsigned
+	 * delegation
+	 */
+	if (type_count > 0 &&
+	    !(type_count == 1 && types[0] == LDNS_RR_TYPE_NS)) {
+		types[type_count] = LDNS_RR_TYPE_RRSIG;
+		type_count++;
+	}
+
+	/* leave next rdata empty if they weren't precomputed yet */
+	if (to && to->hashed_name) {
+		(void) ldns_rr_set_rdf(nsec_rr,
+		                       ldns_rdf_clone(to->hashed_name),
+		                       4);
+	} else {
+		(void) ldns_rr_set_rdf(nsec_rr, NULL, 4);
+	}
+
+	ldns_rr_push_rdf(nsec_rr,
+	                 ldns_dnssec_create_nsec_bitmap(types,
+	                 type_count,
+	                 LDNS_RR_TYPE_NSEC3));
+
+	return nsec_rr;
+}
+
+ldns_rr *
+ldns_create_nsec(ldns_rdf *cur_owner, ldns_rdf *next_owner, ldns_rr_list *rrs)
+{
+	/* we do not do any check here - garbage in, garbage out */
+
+	/* the the start and end names - get the type from the
+	 * before rrlist */
+
+	/* inefficient, just give it a name, a next name, and a list of rrs */
+	/* we make 1 big uberbitmap first, then windows */
+	/* todo: make something more efficient :) */
+	uint16_t i;
+	ldns_rr *i_rr;
+	uint16_t i_type;
+
+	ldns_rr *nsec = NULL;
+	ldns_rr_type i_type_list[65536];
+	size_t type_count = 0;
+
+	nsec = ldns_rr_new();
+	ldns_rr_set_type(nsec, LDNS_RR_TYPE_NSEC);
+	ldns_rr_set_owner(nsec, ldns_rdf_clone(cur_owner));
+	ldns_rr_push_rdf(nsec, ldns_rdf_clone(next_owner));
+
+	for (i = 0; i < ldns_rr_list_rr_count(rrs); i++) {
+		i_rr = ldns_rr_list_rr(rrs, i);
+		if (ldns_rdf_compare(cur_owner,
+						 ldns_rr_owner(i_rr)) == 0) {
+			i_type = ldns_rr_get_type(i_rr);
+			if (i_type != LDNS_RR_TYPE_RRSIG && i_type != LDNS_RR_TYPE_NSEC) {
+				if (type_count == 0 || i_type_list[type_count-1] != i_type) {
+					i_type_list[type_count] = i_type;
+					type_count++;
+				}
+			}
+		}
+	}
+
+	i_type_list[type_count] = LDNS_RR_TYPE_RRSIG;
+	type_count++;
+	i_type_list[type_count] = LDNS_RR_TYPE_NSEC;
+	type_count++;
+
+	ldns_rr_push_rdf(nsec,
+				  ldns_dnssec_create_nsec_bitmap(i_type_list,
+						type_count, LDNS_RR_TYPE_NSEC));
+
+	return nsec;
+}
+
+ldns_rdf *
+ldns_nsec3_hash_name(ldns_rdf *name,
+				 uint8_t algorithm,
+				 uint16_t iterations,
+				 uint8_t salt_length,
+				 uint8_t *salt)
+{
+	size_t hashed_owner_str_len;
+	ldns_rdf *cann;
+	ldns_rdf *hashed_owner;
+	unsigned char *hashed_owner_str;
+	char *hashed_owner_b32;
+	size_t hashed_owner_b32_len;
+	uint32_t cur_it;
+	/* define to contain the largest possible hash, which is
+	 * sha1 at the moment */
+	unsigned char hash[LDNS_SHA1_DIGEST_LENGTH];
+	ldns_status status;
+
+	/* TODO: mnemonic list for hash algs SHA-1, default to 1 now (sha1) */
+	if (algorithm != LDNS_SHA1) {
+		return NULL;
+	}
+
+	/* prepare the owner name according to the draft section bla */
+	cann = ldns_rdf_clone(name);
+	if(!cann) {
+		fprintf(stderr, "Memory error\n");
+		return NULL;
+	}
+	ldns_dname2canonical(cann);
+
+	hashed_owner_str_len = salt_length + ldns_rdf_size(cann);
+	hashed_owner_str = LDNS_XMALLOC(unsigned char, hashed_owner_str_len);
+        if(!hashed_owner_str) {
+	        ldns_rdf_deep_free(cann);
+                return NULL;
+        }
+	memcpy(hashed_owner_str, ldns_rdf_data(cann), ldns_rdf_size(cann));
+	memcpy(hashed_owner_str + ldns_rdf_size(cann), salt, salt_length);
+	ldns_rdf_deep_free(cann);
+
+	for (cur_it = iterations + 1; cur_it > 0; cur_it--) {
+		(void) ldns_sha1((unsigned char *) hashed_owner_str,
+		                 (unsigned int) hashed_owner_str_len, hash);
+
+		LDNS_FREE(hashed_owner_str);
+		hashed_owner_str_len = salt_length + LDNS_SHA1_DIGEST_LENGTH;
+		hashed_owner_str = LDNS_XMALLOC(unsigned char, hashed_owner_str_len);
+		if (!hashed_owner_str) {
+			return NULL;
+		}
+		memcpy(hashed_owner_str, hash, LDNS_SHA1_DIGEST_LENGTH);
+		memcpy(hashed_owner_str + LDNS_SHA1_DIGEST_LENGTH, salt, salt_length);
+		hashed_owner_str_len = LDNS_SHA1_DIGEST_LENGTH + salt_length;
+	}
+
+	LDNS_FREE(hashed_owner_str);
+	hashed_owner_str = hash;
+	hashed_owner_str_len = LDNS_SHA1_DIGEST_LENGTH;
+
+	hashed_owner_b32 = LDNS_XMALLOC(char,
+                  ldns_b32_ntop_calculate_size(hashed_owner_str_len) + 1);
+        if(!hashed_owner_b32) {
+                return NULL;
+        }
+        hashed_owner_b32_len = (size_t) ldns_b32_ntop_extended_hex(
+                (uint8_t *) hashed_owner_str,
+                hashed_owner_str_len,
+                hashed_owner_b32,
+                ldns_b32_ntop_calculate_size(hashed_owner_str_len)+1);
+	if (hashed_owner_b32_len < 1) {
+		fprintf(stderr, "Error in base32 extended hex encoding ");
+		fprintf(stderr, "of hashed owner name (name: ");
+		ldns_rdf_print(stderr, name);
+		fprintf(stderr, ", return code: %u)\n",
+		        (unsigned int) hashed_owner_b32_len);
+		LDNS_FREE(hashed_owner_b32);
+		return NULL;
+	}
+	hashed_owner_b32[hashed_owner_b32_len] = '\0';
+
+	status = ldns_str2rdf_dname(&hashed_owner, hashed_owner_b32);
+	if (status != LDNS_STATUS_OK) {
+		fprintf(stderr, "Error creating rdf from %s\n", hashed_owner_b32);
+		LDNS_FREE(hashed_owner_b32);
+		return NULL;
+	}
+
+	LDNS_FREE(hashed_owner_b32);
+	return hashed_owner;
+}
+
+void
+ldns_nsec3_add_param_rdfs(ldns_rr *rr,
+					 uint8_t algorithm,
+					 uint8_t flags,
+					 uint16_t iterations,
+					 uint8_t salt_length,
+					 uint8_t *salt)
+{
+	ldns_rdf *salt_rdf = NULL;
+	uint8_t *salt_data = NULL;
+	ldns_rdf *old;
+
+	old = ldns_rr_set_rdf(rr,
+	                      ldns_rdf_new_frm_data(LDNS_RDF_TYPE_INT8,
+	                                            1, (void*)&algorithm),
+	                      0);
+	if (old) ldns_rdf_deep_free(old);
+
+	old = ldns_rr_set_rdf(rr,
+	                      ldns_rdf_new_frm_data(LDNS_RDF_TYPE_INT8,
+	                                            1, (void*)&flags),
+	                      1);
+	if (old) ldns_rdf_deep_free(old);
+
+	old = ldns_rr_set_rdf(rr,
+                          ldns_native2rdf_int16(LDNS_RDF_TYPE_INT16,
+                                                iterations),
+	                      2);
+	if (old) ldns_rdf_deep_free(old);
+
+	salt_data = LDNS_XMALLOC(uint8_t, salt_length + 1);
+        if(!salt_data) {
+                /* no way to return error */
+                return;
+        }
+	salt_data[0] = salt_length;
+	memcpy(salt_data + 1, salt, salt_length);
+	salt_rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_NSEC3_SALT,
+							   salt_length + 1,
+							   salt_data);
+        if(!salt_rdf) {
+                LDNS_FREE(salt_data);
+                /* no way to return error */
+                return;
+        }
+
+	old = ldns_rr_set_rdf(rr, salt_rdf, 3);
+	if (old) ldns_rdf_deep_free(old);
+	LDNS_FREE(salt_data);
+}
+
+static int
+rr_list_delegation_only(ldns_rdf *origin, ldns_rr_list *rr_list)
+{
+	size_t i;
+	ldns_rr *cur_rr;
+	if (!origin || !rr_list) return 0;
+	for (i = 0; i < ldns_rr_list_rr_count(rr_list); i++) {
+		cur_rr = ldns_rr_list_rr(rr_list, i);
+		if (ldns_dname_compare(ldns_rr_owner(cur_rr), origin) == 0) {
+			return 0;
+		}
+		if (ldns_rr_get_type(cur_rr) != LDNS_RR_TYPE_NS) {
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/* this will NOT return the NSEC3  completed, you will have to run the
+   finalize function on the rrlist later! */
+ldns_rr *
+ldns_create_nsec3(ldns_rdf *cur_owner,
+                  ldns_rdf *cur_zone,
+                  ldns_rr_list *rrs,
+                  uint8_t algorithm,
+                  uint8_t flags,
+                  uint16_t iterations,
+                  uint8_t salt_length,
+                  uint8_t *salt,
+                  bool emptynonterminal)
+{
+	size_t i;
+	ldns_rr *i_rr;
+	uint16_t i_type;
+
+	ldns_rr *nsec = NULL;
+	ldns_rdf *hashed_owner = NULL;
+
+	ldns_status status;
+
+    ldns_rr_type i_type_list[1024];
+	size_t type_count = 0;
+
+	hashed_owner = ldns_nsec3_hash_name(cur_owner,
+								 algorithm,
+								 iterations,
+								 salt_length,
+								 salt);
+	status = ldns_dname_cat(hashed_owner, cur_zone);
+        if(status != LDNS_STATUS_OK)
+                return NULL;
+
+	nsec = ldns_rr_new_frm_type(LDNS_RR_TYPE_NSEC3);
+        if(!nsec)
+                return NULL;
+	ldns_rr_set_type(nsec, LDNS_RR_TYPE_NSEC3);
+	ldns_rr_set_owner(nsec, hashed_owner);
+
+	ldns_nsec3_add_param_rdfs(nsec,
+						 algorithm,
+						 flags,
+						 iterations,
+						 salt_length,
+						 salt);
+	(void) ldns_rr_set_rdf(nsec, NULL, 4);
+
+
+	for (i = 0; i < ldns_rr_list_rr_count(rrs); i++) {
+		i_rr = ldns_rr_list_rr(rrs, i);
+		if (ldns_rdf_compare(cur_owner,
+						 ldns_rr_owner(i_rr)) == 0) {
+			i_type = ldns_rr_get_type(i_rr);
+			if (type_count == 0 || i_type_list[type_count-1] != i_type) {
+				i_type_list[type_count] = i_type;
+				type_count++;
+			}
+		}
+	}
+
+	/* add RRSIG anyway, but only if this is not an ENT or
+	 * an unsigned delegation */
+	if (!emptynonterminal && !rr_list_delegation_only(cur_zone, rrs)) {
+		i_type_list[type_count] = LDNS_RR_TYPE_RRSIG;
+		type_count++;
+	}
+
+	/* and SOA if owner == zone */
+	if (ldns_dname_compare(cur_zone, cur_owner) == 0) {
+		i_type_list[type_count] = LDNS_RR_TYPE_SOA;
+		type_count++;
+	}
+
+	ldns_rr_push_rdf(nsec,
+				  ldns_dnssec_create_nsec_bitmap(i_type_list,
+						type_count, LDNS_RR_TYPE_NSEC3));
+
+	return nsec;
+}
+
+uint8_t
+ldns_nsec3_algorithm(const ldns_rr *nsec3_rr)
+{
+	if (nsec3_rr && 
+	      (ldns_rr_get_type(nsec3_rr) == LDNS_RR_TYPE_NSEC3 ||
+	       ldns_rr_get_type(nsec3_rr) == LDNS_RR_TYPE_NSEC3PARAM)
+	    && (ldns_rr_rdf(nsec3_rr, 0) != NULL)
+	    && ldns_rdf_size(ldns_rr_rdf(nsec3_rr, 0)) > 0) {
+		return ldns_rdf2native_int8(ldns_rr_rdf(nsec3_rr, 0));
+	}
+	return 0;
+}
+
+uint8_t
+ldns_nsec3_flags(const ldns_rr *nsec3_rr)
+{
+	if (nsec3_rr && 
+	      (ldns_rr_get_type(nsec3_rr) == LDNS_RR_TYPE_NSEC3 ||
+	       ldns_rr_get_type(nsec3_rr) == LDNS_RR_TYPE_NSEC3PARAM)
+	    && (ldns_rr_rdf(nsec3_rr, 1) != NULL)
+	    && ldns_rdf_size(ldns_rr_rdf(nsec3_rr, 1)) > 0) {
+		return ldns_rdf2native_int8(ldns_rr_rdf(nsec3_rr, 1));
+	}
+	return 0;
+}
+
+bool
+ldns_nsec3_optout(const ldns_rr *nsec3_rr)
+{
+	return (ldns_nsec3_flags(nsec3_rr) & LDNS_NSEC3_VARS_OPTOUT_MASK);
+}
+
+uint16_t
+ldns_nsec3_iterations(const ldns_rr *nsec3_rr)
+{
+	if (nsec3_rr &&
+	      (ldns_rr_get_type(nsec3_rr) == LDNS_RR_TYPE_NSEC3 ||
+	       ldns_rr_get_type(nsec3_rr) == LDNS_RR_TYPE_NSEC3PARAM)
+	    && (ldns_rr_rdf(nsec3_rr, 2) != NULL)
+	    && ldns_rdf_size(ldns_rr_rdf(nsec3_rr, 2)) > 0) {
+		return ldns_rdf2native_int16(ldns_rr_rdf(nsec3_rr, 2));
+	}
+	return 0;
+	
+}
+
+ldns_rdf *
+ldns_nsec3_salt(const ldns_rr *nsec3_rr)
+{
+	if (nsec3_rr && 
+	      (ldns_rr_get_type(nsec3_rr) == LDNS_RR_TYPE_NSEC3 ||
+	       ldns_rr_get_type(nsec3_rr) == LDNS_RR_TYPE_NSEC3PARAM)
+	    ) {
+		return ldns_rr_rdf(nsec3_rr, 3);
+	}
+	return NULL;
+}
+
+uint8_t
+ldns_nsec3_salt_length(const ldns_rr *nsec3_rr)
+{
+	ldns_rdf *salt_rdf = ldns_nsec3_salt(nsec3_rr);
+	if (salt_rdf && ldns_rdf_size(salt_rdf) > 0) {
+		return (uint8_t) ldns_rdf_data(salt_rdf)[0];
+	}
+	return 0;
+}
+
+/* allocs data, free with LDNS_FREE() */
+uint8_t *
+ldns_nsec3_salt_data(const ldns_rr *nsec3_rr)
+{
+	uint8_t salt_length;
+	uint8_t *salt;
+
+	ldns_rdf *salt_rdf = ldns_nsec3_salt(nsec3_rr);
+	if (salt_rdf && ldns_rdf_size(salt_rdf) > 0) {
+	    	salt_length = ldns_rdf_data(salt_rdf)[0];
+		salt = LDNS_XMALLOC(uint8_t, salt_length);
+                if(!salt) return NULL;
+		memcpy(salt, &ldns_rdf_data(salt_rdf)[1], salt_length);
+		return salt;
+	}
+	return NULL;
+}
+
+ldns_rdf *
+ldns_nsec3_next_owner(const ldns_rr *nsec3_rr)
+{
+	if (!nsec3_rr || ldns_rr_get_type(nsec3_rr) != LDNS_RR_TYPE_NSEC3) {
+		return NULL;
+	} else {
+		return ldns_rr_rdf(nsec3_rr, 4);
+	}
+}
+
+ldns_rdf *
+ldns_nsec3_bitmap(const ldns_rr *nsec3_rr)
+{
+	if (!nsec3_rr || ldns_rr_get_type(nsec3_rr) != LDNS_RR_TYPE_NSEC3) {
+		return NULL;
+	} else {
+		return ldns_rr_rdf(nsec3_rr, 5);
+	}
+}
+
+ldns_rdf *
+ldns_nsec3_hash_name_frm_nsec3(const ldns_rr *nsec, ldns_rdf *name)
+{
+	uint8_t algorithm;
+	uint16_t iterations;
+	uint8_t salt_length;
+	uint8_t *salt = 0;
+
+	ldns_rdf *hashed_owner;
+
+	algorithm = ldns_nsec3_algorithm(nsec);
+	salt_length = ldns_nsec3_salt_length(nsec);
+	salt = ldns_nsec3_salt_data(nsec);
+	iterations = ldns_nsec3_iterations(nsec);
+
+	hashed_owner = ldns_nsec3_hash_name(name,
+								 algorithm,
+								 iterations,
+								 salt_length,
+								 salt);
+
+	LDNS_FREE(salt);
+	return hashed_owner;
+}
+
+bool
+ldns_nsec_bitmap_covers_type(const ldns_rdf *nsec_bitmap, ldns_rr_type type)
+{
+	uint8_t window_block_nr;
+	uint8_t bitmap_length;
+	uint16_t cur_type;
+	uint16_t pos = 0;
+	uint16_t bit_pos;
+	uint8_t *data;
+
+	if (nsec_bitmap == NULL) {
+		return false;
+	}
+	data = ldns_rdf_data(nsec_bitmap);
+	while(pos < ldns_rdf_size(nsec_bitmap)) {
+		window_block_nr = data[pos];
+		bitmap_length = data[pos + 1];
+		pos += 2;
+
+		for (bit_pos = 0; bit_pos < (bitmap_length) * 8; bit_pos++) {
+			if (ldns_get_bit(&data[pos], bit_pos)) {
+				cur_type = 256 * (uint16_t) window_block_nr + bit_pos;
+				if (cur_type == type) {
+					return true;
+				}
+			}
+		}
+
+		pos += (uint16_t) bitmap_length;
+	}
+	return false;
+}
+
+bool
+ldns_nsec_covers_name(const ldns_rr *nsec, const ldns_rdf *name)
+{
+	ldns_rdf *nsec_owner = ldns_rr_owner(nsec);
+	ldns_rdf *hash_next;
+	char *next_hash_str;
+	ldns_rdf *nsec_next = NULL;
+	ldns_status status;
+	ldns_rdf *chopped_dname;
+	bool result;
+
+	if (ldns_rr_get_type(nsec) == LDNS_RR_TYPE_NSEC) {
+		if (ldns_rr_rdf(nsec, 0) != NULL) {
+			nsec_next = ldns_rdf_clone(ldns_rr_rdf(nsec, 0));
+		} else {
+			return false;
+		}
+	} else if (ldns_rr_get_type(nsec) == LDNS_RR_TYPE_NSEC3) {
+		hash_next = ldns_nsec3_next_owner(nsec);
+		next_hash_str = ldns_rdf2str(hash_next);
+		nsec_next = ldns_dname_new_frm_str(next_hash_str);
+		LDNS_FREE(next_hash_str);
+		chopped_dname = ldns_dname_left_chop(nsec_owner);
+		status = ldns_dname_cat(nsec_next, chopped_dname);
+		ldns_rdf_deep_free(chopped_dname);
+		if (status != LDNS_STATUS_OK) {
+			printf("error catting: %s\n", ldns_get_errorstr_by_id(status));
+		}
+	} else {
+		ldns_rdf_deep_free(nsec_next);
+		return false;
+	}
+
+	/* in the case of the last nsec */
+	if(ldns_dname_compare(nsec_owner, nsec_next) > 0) {
+		result = (ldns_dname_compare(nsec_owner, name) <= 0 ||
+				ldns_dname_compare(name, nsec_next) < 0);
+	} else {
+		result = (ldns_dname_compare(nsec_owner, name) <= 0 &&
+		          ldns_dname_compare(name, nsec_next) < 0);
+	}
+
+	ldns_rdf_deep_free(nsec_next);
+	return result;
+}
+
+#ifdef HAVE_SSL
+/* sig may be null - if so look in the packet */
+
+ldns_status
+ldns_pkt_verify_time(ldns_pkt *p, ldns_rr_type t, ldns_rdf *o, 
+		ldns_rr_list *k, ldns_rr_list *s, 
+		time_t check_time, ldns_rr_list *good_keys)
+{
+	ldns_rr_list *rrset;
+	ldns_rr_list *sigs;
+	ldns_rr_list *sigs_covered;
+	ldns_rdf *rdf_t;
+	ldns_rr_type t_netorder;
+
+	if (!k) {
+		return LDNS_STATUS_ERR;
+		/* return LDNS_STATUS_CRYPTO_NO_DNSKEY; */
+	}
+
+	if (t == LDNS_RR_TYPE_RRSIG) {
+		/* we don't have RRSIG(RRSIG) (yet? ;-) ) */
+		return LDNS_STATUS_ERR;
+	}
+
+	if (s) {
+		/* if s is not NULL, the sigs are given to use */
+		sigs = s;
+	} else {
+		/* otherwise get them from the packet */
+		sigs = ldns_pkt_rr_list_by_name_and_type(p, o, LDNS_RR_TYPE_RRSIG,
+									  LDNS_SECTION_ANY_NOQUESTION);
+		if (!sigs) {
+			/* no sigs */
+			return LDNS_STATUS_ERR;
+			/* return LDNS_STATUS_CRYPTO_NO_RRSIG; */
+		}
+	}
+
+	/* rrsig are subtyped, so now we need to find the correct
+	 * sigs for the type t
+	 */
+	t_netorder = htons(t); /* rdf are in network order! */
+	/* a type identifier is a 16-bit number, so the size is 2 bytes */
+	rdf_t = ldns_rdf_new(LDNS_RDF_TYPE_TYPE,
+					 2,
+					 &t_netorder);
+	sigs_covered = ldns_rr_list_subtype_by_rdf(sigs, rdf_t, 0);
+
+	rrset = ldns_pkt_rr_list_by_name_and_type(p,
+									  o,
+									  t,
+									  LDNS_SECTION_ANY_NOQUESTION);
+
+	if (!rrset) {
+		return LDNS_STATUS_ERR;
+	}
+
+	if (!sigs_covered) {
+		return LDNS_STATUS_ERR;
+	}
+
+	return ldns_verify_time(rrset, sigs, k, check_time, good_keys);
+}
+
+ldns_status
+ldns_pkt_verify(ldns_pkt *p, ldns_rr_type t, ldns_rdf *o, 
+		ldns_rr_list *k, ldns_rr_list *s, ldns_rr_list *good_keys)
+{
+	return ldns_pkt_verify_time(p, t, o, k, s, ldns_time(NULL), good_keys);
+}
+#endif /* HAVE_SSL */
+
+ldns_status
+ldns_dnssec_chain_nsec3_list(ldns_rr_list *nsec3_rrs)
+{
+	size_t i;
+	char *next_nsec_owner_str;
+	ldns_rdf *next_nsec_owner_label;
+	ldns_rdf *next_nsec_rdf;
+	ldns_status status = LDNS_STATUS_OK;
+
+	for (i = 0; i < ldns_rr_list_rr_count(nsec3_rrs); i++) {
+		if (i == ldns_rr_list_rr_count(nsec3_rrs) - 1) {
+			next_nsec_owner_label =
+				ldns_dname_label(ldns_rr_owner(ldns_rr_list_rr(nsec3_rrs,
+													  0)), 0);
+			next_nsec_owner_str = ldns_rdf2str(next_nsec_owner_label);
+			if (next_nsec_owner_str[strlen(next_nsec_owner_str) - 1]
+			    == '.') {
+				next_nsec_owner_str[strlen(next_nsec_owner_str) - 1]
+					= '\0';
+			}
+			status = ldns_str2rdf_b32_ext(&next_nsec_rdf,
+									next_nsec_owner_str);
+			if (!ldns_rr_set_rdf(ldns_rr_list_rr(nsec3_rrs, i),
+							 next_nsec_rdf, 4)) {
+				/* todo: error */
+			}
+
+			ldns_rdf_deep_free(next_nsec_owner_label);
+			LDNS_FREE(next_nsec_owner_str);
+		} else {
+			next_nsec_owner_label =
+				ldns_dname_label(ldns_rr_owner(ldns_rr_list_rr(nsec3_rrs,
+													  i + 1)),
+							  0);
+			next_nsec_owner_str = ldns_rdf2str(next_nsec_owner_label);
+			if (next_nsec_owner_str[strlen(next_nsec_owner_str) - 1]
+			    == '.') {
+				next_nsec_owner_str[strlen(next_nsec_owner_str) - 1]
+					= '\0';
+			}
+			status = ldns_str2rdf_b32_ext(&next_nsec_rdf,
+									next_nsec_owner_str);
+			ldns_rdf_deep_free(next_nsec_owner_label);
+			LDNS_FREE(next_nsec_owner_str);
+			if (!ldns_rr_set_rdf(ldns_rr_list_rr(nsec3_rrs, i),
+							 next_nsec_rdf, 4)) {
+				/* todo: error */
+			}
+		}
+	}
+	return status;
+}
+
+int
+qsort_rr_compare_nsec3(const void *a, const void *b)
+{
+	const ldns_rr *rr1 = * (const ldns_rr **) a;
+	const ldns_rr *rr2 = * (const ldns_rr **) b;
+	if (rr1 == NULL && rr2 == NULL) {
+		return 0;
+	}
+	if (rr1 == NULL) {
+		return -1;
+	}
+	if (rr2 == NULL) {
+		return 1;
+	}
+	return ldns_rdf_compare(ldns_rr_owner(rr1), ldns_rr_owner(rr2));
+}
+
+void
+ldns_rr_list_sort_nsec3(ldns_rr_list *unsorted)
+{
+	qsort(unsorted->_rrs,
+	      ldns_rr_list_rr_count(unsorted),
+	      sizeof(ldns_rr *),
+	      qsort_rr_compare_nsec3);
+}
+
+int
+ldns_dnssec_default_add_to_signatures(ldns_rr *sig, void *n)
+{
+	sig = sig;
+	n = n;
+	return LDNS_SIGNATURE_LEAVE_ADD_NEW;
+}
+
+int
+ldns_dnssec_default_leave_signatures(ldns_rr *sig, void *n)
+{
+	sig = sig;
+	n = n;
+	return LDNS_SIGNATURE_LEAVE_NO_ADD;
+}
+
+int
+ldns_dnssec_default_delete_signatures(ldns_rr *sig, void *n)
+{
+	sig = sig;
+	n = n;
+	return LDNS_SIGNATURE_REMOVE_NO_ADD;
+}
+
+int
+ldns_dnssec_default_replace_signatures(ldns_rr *sig, void *n)
+{
+	sig = sig;
+	n = n;
+	return LDNS_SIGNATURE_REMOVE_ADD_NEW;
+}
+
+#ifdef HAVE_SSL
+ldns_rdf *
+ldns_convert_dsa_rrsig_asn12rdf(const ldns_buffer *sig,
+						  const long sig_len)
+{
+	ldns_rdf *sigdata_rdf;
+	DSA_SIG *dsasig;
+	unsigned char *dsasig_data = (unsigned char*)ldns_buffer_begin(sig);
+	size_t byte_offset;
+
+	dsasig = d2i_DSA_SIG(NULL,
+					 (const unsigned char **)&dsasig_data,
+					 sig_len);
+	if (!dsasig) {
+                DSA_SIG_free(dsasig);
+		return NULL;
+	}
+
+	dsasig_data = LDNS_XMALLOC(unsigned char, 41);
+        if(!dsasig_data) {
+                DSA_SIG_free(dsasig);
+                return NULL;
+        }
+	dsasig_data[0] = 0;
+	byte_offset = (size_t) (20 - BN_num_bytes(dsasig->r));
+	if (byte_offset > 20) {
+                DSA_SIG_free(dsasig);
+                LDNS_FREE(dsasig_data);
+		return NULL;
+	}
+	memset(&dsasig_data[1], 0, byte_offset);
+	BN_bn2bin(dsasig->r, &dsasig_data[1 + byte_offset]);
+	byte_offset = (size_t) (20 - BN_num_bytes(dsasig->s));
+	if (byte_offset > 20) {
+                DSA_SIG_free(dsasig);
+                LDNS_FREE(dsasig_data);
+		return NULL;
+	}
+	memset(&dsasig_data[21], 0, byte_offset);
+	BN_bn2bin(dsasig->s, &dsasig_data[21 + byte_offset]);
+
+	sigdata_rdf = ldns_rdf_new(LDNS_RDF_TYPE_B64, 41, dsasig_data);
+        if(!sigdata_rdf) {
+                LDNS_FREE(dsasig_data);
+        }
+	DSA_SIG_free(dsasig);
+
+	return sigdata_rdf;
+}
+
+ldns_status
+ldns_convert_dsa_rrsig_rdf2asn1(ldns_buffer *target_buffer,
+						  const ldns_rdf *sig_rdf)
+{
+	/* the EVP api wants the DER encoding of the signature... */
+	BIGNUM *R, *S;
+	DSA_SIG *dsasig;
+	unsigned char *raw_sig = NULL;
+	int raw_sig_len;
+
+        if(ldns_rdf_size(sig_rdf) < 1 + 2*SHA_DIGEST_LENGTH)
+                return LDNS_STATUS_SYNTAX_RDATA_ERR;
+	/* extract the R and S field from the sig buffer */
+	R = BN_new();
+	if(!R) return LDNS_STATUS_MEM_ERR;
+	(void) BN_bin2bn((unsigned char *) ldns_rdf_data(sig_rdf) + 1,
+	                 SHA_DIGEST_LENGTH, R);
+	S = BN_new();
+	if(!S) {
+		BN_free(R);
+		return LDNS_STATUS_MEM_ERR;
+	}
+	(void) BN_bin2bn((unsigned char *) ldns_rdf_data(sig_rdf) + 21,
+	                 SHA_DIGEST_LENGTH, S);
+
+	dsasig = DSA_SIG_new();
+	if (!dsasig) {
+		BN_free(R);
+		BN_free(S);
+		return LDNS_STATUS_MEM_ERR;
+	}
+
+	dsasig->r = R;
+	dsasig->s = S;
+
+	raw_sig_len = i2d_DSA_SIG(dsasig, &raw_sig);
+	if (raw_sig_len < 0) {
+		DSA_SIG_free(dsasig);
+		free(raw_sig);
+		return LDNS_STATUS_SSL_ERR;
+	}
+	if (ldns_buffer_reserve(target_buffer, (size_t) raw_sig_len)) {
+		ldns_buffer_write(target_buffer, raw_sig, (size_t)raw_sig_len);
+	}
+
+	DSA_SIG_free(dsasig);
+	free(raw_sig);
+
+	return ldns_buffer_status(target_buffer);
+}
+
+#ifdef USE_ECDSA
+#ifndef S_SPLINT_S
+ldns_rdf *
+ldns_convert_ecdsa_rrsig_asn12rdf(const ldns_buffer *sig, const long sig_len)
+{
+        ECDSA_SIG* ecdsa_sig;
+	unsigned char *data = (unsigned char*)ldns_buffer_begin(sig);
+        ldns_rdf* rdf;
+	ecdsa_sig = d2i_ECDSA_SIG(NULL, (const unsigned char **)&data, sig_len);
+        if(!ecdsa_sig) return NULL;
+
+        /* "r | s". */
+        data = LDNS_XMALLOC(unsigned char,
+                BN_num_bytes(ecdsa_sig->r) + BN_num_bytes(ecdsa_sig->s));
+        if(!data) {
+                ECDSA_SIG_free(ecdsa_sig);
+                return NULL;
+        }
+        BN_bn2bin(ecdsa_sig->r, data);
+        BN_bn2bin(ecdsa_sig->s, data+BN_num_bytes(ecdsa_sig->r));
+	rdf = ldns_rdf_new(LDNS_RDF_TYPE_B64, (size_t)(
+		BN_num_bytes(ecdsa_sig->r) + BN_num_bytes(ecdsa_sig->s)), data);
+        ECDSA_SIG_free(ecdsa_sig);
+        return rdf;
+}
+
+ldns_status
+ldns_convert_ecdsa_rrsig_rdf2asn1(ldns_buffer *target_buffer,
+        const ldns_rdf *sig_rdf)
+{
+        ECDSA_SIG* sig;
+	int raw_sig_len;
+        long bnsize = (long)ldns_rdf_size(sig_rdf) / 2;
+        /* if too short, or not even length, do not bother */
+        if(bnsize < 16 || (size_t)bnsize*2 != ldns_rdf_size(sig_rdf))
+                return LDNS_STATUS_ERR;
+        
+        /* use the raw data to parse two evenly long BIGNUMs, "r | s". */
+        sig = ECDSA_SIG_new();
+        if(!sig) return LDNS_STATUS_MEM_ERR;
+        sig->r = BN_bin2bn((const unsigned char*)ldns_rdf_data(sig_rdf),
+                bnsize, sig->r);
+        sig->s = BN_bin2bn((const unsigned char*)ldns_rdf_data(sig_rdf)+bnsize,
+                bnsize, sig->s);
+        if(!sig->r || !sig->s) {
+                ECDSA_SIG_free(sig);
+                return LDNS_STATUS_MEM_ERR;
+        }
+
+	raw_sig_len = i2d_ECDSA_SIG(sig, NULL);
+	if (ldns_buffer_reserve(target_buffer, (size_t) raw_sig_len)) {
+                unsigned char* pp = (unsigned char*)
+			ldns_buffer_current(target_buffer);
+	        raw_sig_len = i2d_ECDSA_SIG(sig, &pp);
+                ldns_buffer_skip(target_buffer, (ssize_t) raw_sig_len);
+	}
+        ECDSA_SIG_free(sig);
+
+	return ldns_buffer_status(target_buffer);
+}
+
+#endif /* S_SPLINT_S */
+#endif /* USE_ECDSA */
+#endif /* HAVE_SSL */
diff --git a/3rdParty/Ldns/src/src/dnssec_sign.c b/3rdParty/Ldns/src/src/dnssec_sign.c
new file mode 100644
index 0000000..1d283bc
--- /dev/null
+++ b/3rdParty/Ldns/src/src/dnssec_sign.c
@@ -0,0 +1,1420 @@
+#include <ldns/config.h>
+
+#include <ldns/ldns.h>
+
+#include <ldns/dnssec.h>
+#include <ldns/dnssec_sign.h>
+
+#include <strings.h>
+#include <time.h>
+
+#ifdef HAVE_SSL
+/* this entire file is rather useless when you don't have
+ * crypto...
+ */
+#include <openssl/ssl.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#include <openssl/err.h>
+#include <openssl/md5.h>
+#endif /* HAVE_SSL */
+
+ldns_rr *
+ldns_create_empty_rrsig(ldns_rr_list *rrset,
+                        ldns_key *current_key)
+{
+	uint32_t orig_ttl;
+	ldns_rr_class orig_class;
+	time_t now;
+	ldns_rr *current_sig;
+	uint8_t label_count;
+	ldns_rdf *signame;
+
+	label_count = ldns_dname_label_count(ldns_rr_owner(ldns_rr_list_rr(rrset,
+	                                                   0)));
+        /* RFC4035 2.2: not counting the leftmost label if it is a wildcard */
+        if(ldns_dname_is_wildcard(ldns_rr_owner(ldns_rr_list_rr(rrset, 0))))
+                label_count --;
+
+	current_sig = ldns_rr_new_frm_type(LDNS_RR_TYPE_RRSIG);
+
+	/* set the type on the new signature */
+	orig_ttl = ldns_rr_ttl(ldns_rr_list_rr(rrset, 0));
+	orig_class = ldns_rr_get_class(ldns_rr_list_rr(rrset, 0));
+
+	ldns_rr_set_ttl(current_sig, orig_ttl);
+	ldns_rr_set_class(current_sig, orig_class);
+	ldns_rr_set_owner(current_sig,
+			  ldns_rdf_clone(
+			       ldns_rr_owner(
+				    ldns_rr_list_rr(rrset,
+						    0))));
+
+	/* fill in what we know of the signature */
+
+	/* set the orig_ttl */
+	(void)ldns_rr_rrsig_set_origttl(
+		   current_sig,
+		   ldns_native2rdf_int32(LDNS_RDF_TYPE_INT32,
+					 orig_ttl));
+	/* the signers name */
+	signame = ldns_rdf_clone(ldns_key_pubkey_owner(current_key));
+	ldns_dname2canonical(signame);
+	(void)ldns_rr_rrsig_set_signame(
+			current_sig,
+			signame);
+	/* label count - get it from the first rr in the rr_list */
+	(void)ldns_rr_rrsig_set_labels(
+			current_sig,
+			ldns_native2rdf_int8(LDNS_RDF_TYPE_INT8,
+			                     label_count));
+	/* inception, expiration */
+	now = time(NULL);
+	if (ldns_key_inception(current_key) != 0) {
+		(void)ldns_rr_rrsig_set_inception(
+				current_sig,
+				ldns_native2rdf_int32(
+				    LDNS_RDF_TYPE_TIME,
+				    ldns_key_inception(current_key)));
+	} else {
+		(void)ldns_rr_rrsig_set_inception(
+				current_sig,
+				ldns_native2rdf_int32(LDNS_RDF_TYPE_TIME, now));
+	}
+	if (ldns_key_expiration(current_key) != 0) {
+		(void)ldns_rr_rrsig_set_expiration(
+				current_sig,
+				ldns_native2rdf_int32(
+				    LDNS_RDF_TYPE_TIME,
+				    ldns_key_expiration(current_key)));
+	} else {
+		(void)ldns_rr_rrsig_set_expiration(
+			     current_sig,
+				ldns_native2rdf_int32(
+				    LDNS_RDF_TYPE_TIME,
+				    now + LDNS_DEFAULT_EXP_TIME));
+	}
+
+	(void)ldns_rr_rrsig_set_keytag(
+		   current_sig,
+		   ldns_native2rdf_int16(LDNS_RDF_TYPE_INT16,
+		                         ldns_key_keytag(current_key)));
+
+	(void)ldns_rr_rrsig_set_algorithm(
+			current_sig,
+			ldns_native2rdf_int8(
+			    LDNS_RDF_TYPE_ALG,
+			    ldns_key_algorithm(current_key)));
+
+	(void)ldns_rr_rrsig_set_typecovered(
+			current_sig,
+			ldns_native2rdf_int16(
+			    LDNS_RDF_TYPE_TYPE,
+			    ldns_rr_get_type(ldns_rr_list_rr(rrset,
+			                                     0))));
+	return current_sig;
+}
+
+#ifdef HAVE_SSL
+ldns_rdf *
+ldns_sign_public_buffer(ldns_buffer *sign_buf, ldns_key *current_key)
+{
+	ldns_rdf *b64rdf = NULL;
+
+	switch(ldns_key_algorithm(current_key)) {
+	case LDNS_SIGN_DSA:
+	case LDNS_SIGN_DSA_NSEC3:
+		b64rdf = ldns_sign_public_evp(
+				   sign_buf,
+				   ldns_key_evp_key(current_key),
+				   EVP_dss1());
+		break;
+	case LDNS_SIGN_RSASHA1:
+	case LDNS_SIGN_RSASHA1_NSEC3:
+		b64rdf = ldns_sign_public_evp(
+				   sign_buf,
+				   ldns_key_evp_key(current_key),
+				   EVP_sha1());
+		break;
+#ifdef USE_SHA2
+	case LDNS_SIGN_RSASHA256:
+		b64rdf = ldns_sign_public_evp(
+				   sign_buf,
+				   ldns_key_evp_key(current_key),
+				   EVP_sha256());
+		break;
+	case LDNS_SIGN_RSASHA512:
+		b64rdf = ldns_sign_public_evp(
+				   sign_buf,
+				   ldns_key_evp_key(current_key),
+				   EVP_sha512());
+		break;
+#endif /* USE_SHA2 */
+#ifdef USE_GOST
+	case LDNS_SIGN_ECC_GOST:
+		b64rdf = ldns_sign_public_evp(
+				   sign_buf,
+				   ldns_key_evp_key(current_key),
+				   EVP_get_digestbyname("md_gost94"));
+		break;
+#endif /* USE_GOST */
+#ifdef USE_ECDSA
+        case LDNS_SIGN_ECDSAP256SHA256:
+       		b64rdf = ldns_sign_public_evp(
+				   sign_buf,
+				   ldns_key_evp_key(current_key),
+				   EVP_sha256());
+                break;
+        case LDNS_SIGN_ECDSAP384SHA384:
+       		b64rdf = ldns_sign_public_evp(
+				   sign_buf,
+				   ldns_key_evp_key(current_key),
+				   EVP_sha384());
+                break;
+#endif
+	case LDNS_SIGN_RSAMD5:
+		b64rdf = ldns_sign_public_evp(
+				   sign_buf,
+				   ldns_key_evp_key(current_key),
+				   EVP_md5());
+		break;
+	default:
+		/* do _you_ know this alg? */
+		printf("unknown algorithm, ");
+		printf("is the one used available on this system?\n");
+		break;
+	}
+
+	return b64rdf;
+}
+
+/**
+ * use this function to sign with a public/private key alg
+ * return the created signatures
+ */
+ldns_rr_list *
+ldns_sign_public(ldns_rr_list *rrset, ldns_key_list *keys)
+{
+	ldns_rr_list *signatures;
+	ldns_rr_list *rrset_clone;
+	ldns_rr *current_sig;
+	ldns_rdf *b64rdf;
+	ldns_key *current_key;
+	size_t key_count;
+	uint16_t i;
+	ldns_buffer *sign_buf;
+	ldns_rdf *new_owner;
+
+	if (!rrset || ldns_rr_list_rr_count(rrset) < 1 || !keys) {
+		return NULL;
+	}
+
+	new_owner = NULL;
+
+	signatures = ldns_rr_list_new();
+
+	/* prepare a signature and add all the know data
+	 * prepare the rrset. Sign this together.  */
+	rrset_clone = ldns_rr_list_clone(rrset);
+	if (!rrset_clone) {
+		return NULL;
+	}
+
+	/* make it canonical */
+	for(i = 0; i < ldns_rr_list_rr_count(rrset_clone); i++) {
+		ldns_rr_set_ttl(ldns_rr_list_rr(rrset_clone, i), 
+			ldns_rr_ttl(ldns_rr_list_rr(rrset, 0)));
+		ldns_rr2canonical(ldns_rr_list_rr(rrset_clone, i));
+	}
+	/* sort */
+	ldns_rr_list_sort(rrset_clone);
+
+	for (key_count = 0;
+		key_count < ldns_key_list_key_count(keys);
+		key_count++) {
+		if (!ldns_key_use(ldns_key_list_key(keys, key_count))) {
+			continue;
+		}
+		sign_buf = ldns_buffer_new(LDNS_MAX_PACKETLEN);
+		if (!sign_buf) {
+			ldns_rr_list_free(rrset_clone);
+			ldns_rr_list_free(signatures);
+			ldns_rdf_free(new_owner);
+			return NULL;
+		}
+		b64rdf = NULL;
+
+		current_key = ldns_key_list_key(keys, key_count);
+		/* sign all RRs with keys that have ZSKbit, !SEPbit.
+		   sign DNSKEY RRs with keys that have ZSKbit&SEPbit */
+		if (ldns_key_flags(current_key) & LDNS_KEY_ZONE_KEY) {
+			current_sig = ldns_create_empty_rrsig(rrset_clone,
+			                                      current_key);
+
+			/* right now, we have: a key, a semi-sig and an rrset. For
+			 * which we can create the sig and base64 encode that and
+			 * add that to the signature */
+
+			if (ldns_rrsig2buffer_wire(sign_buf, current_sig)
+			    != LDNS_STATUS_OK) {
+				ldns_buffer_free(sign_buf);
+				/* ERROR */
+				ldns_rr_list_deep_free(rrset_clone);
+				return NULL;
+			}
+
+			/* add the rrset in sign_buf */
+			if (ldns_rr_list2buffer_wire(sign_buf, rrset_clone)
+			    != LDNS_STATUS_OK) {
+				ldns_buffer_free(sign_buf);
+				ldns_rr_list_deep_free(rrset_clone);
+				return NULL;
+			}
+
+			b64rdf = ldns_sign_public_buffer(sign_buf, current_key);
+
+			if (!b64rdf) {
+				/* signing went wrong */
+				ldns_rr_list_deep_free(rrset_clone);
+				return NULL;
+			}
+
+			ldns_rr_rrsig_set_sig(current_sig, b64rdf);
+
+			/* push the signature to the signatures list */
+			ldns_rr_list_push_rr(signatures, current_sig);
+		}
+		ldns_buffer_free(sign_buf); /* restart for the next key */
+	}
+	ldns_rr_list_deep_free(rrset_clone);
+
+	return signatures;
+}
+
+/**
+ * Sign data with DSA
+ *
+ * \param[in] to_sign The ldns_buffer containing raw data that is
+ *                    to be signed
+ * \param[in] key The DSA key structure to sign with
+ * \return ldns_rdf for the RRSIG ldns_rr
+ */
+ldns_rdf *
+ldns_sign_public_dsa(ldns_buffer *to_sign, DSA *key)
+{
+	unsigned char *sha1_hash;
+	ldns_rdf *sigdata_rdf;
+	ldns_buffer *b64sig;
+
+	DSA_SIG *sig;
+	uint8_t *data;
+	size_t pad;
+
+	b64sig = ldns_buffer_new(LDNS_MAX_PACKETLEN);
+	if (!b64sig) {
+		return NULL;
+	}
+
+	sha1_hash = SHA1((unsigned char*)ldns_buffer_begin(to_sign),
+				  ldns_buffer_position(to_sign), NULL);
+	if (!sha1_hash) {
+		ldns_buffer_free(b64sig);
+		return NULL;
+	}
+
+	sig = DSA_do_sign(sha1_hash, SHA_DIGEST_LENGTH, key);
+        if(!sig) {
+		ldns_buffer_free(b64sig);
+		return NULL;
+        }
+
+	data = LDNS_XMALLOC(uint8_t, 1 + 2 * SHA_DIGEST_LENGTH);
+        if(!data) {
+		ldns_buffer_free(b64sig);
+                DSA_SIG_free(sig);
+		return NULL;
+        }
+
+	data[0] = 1;
+	pad = 20 - (size_t) BN_num_bytes(sig->r);
+	if (pad > 0) {
+		memset(data + 1, 0, pad);
+	}
+	BN_bn2bin(sig->r, (unsigned char *) (data + 1) + pad);
+
+	pad = 20 - (size_t) BN_num_bytes(sig->s);
+	if (pad > 0) {
+		memset(data + 1 + SHA_DIGEST_LENGTH, 0, pad);
+	}
+	BN_bn2bin(sig->s, (unsigned char *) (data + 1 + SHA_DIGEST_LENGTH + pad));
+
+	sigdata_rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64,
+								 1 + 2 * SHA_DIGEST_LENGTH,
+								 data);
+
+	ldns_buffer_free(b64sig);
+	LDNS_FREE(data);
+        DSA_SIG_free(sig);
+
+	return sigdata_rdf;
+}
+
+#ifdef USE_ECDSA
+#ifndef S_SPLINT_S
+static int
+ldns_pkey_is_ecdsa(EVP_PKEY* pkey)
+{
+        EC_KEY* ec;
+        const EC_GROUP* g;
+        if(EVP_PKEY_type(pkey->type) != EVP_PKEY_EC)
+                return 0;
+        ec = EVP_PKEY_get1_EC_KEY(pkey);
+        g = EC_KEY_get0_group(ec);
+        if(!g) {
+                EC_KEY_free(ec);
+                return 0;
+        }
+        if(EC_GROUP_get_curve_name(g) == NID_secp224r1 ||
+                EC_GROUP_get_curve_name(g) == NID_X9_62_prime256v1 ||
+                EC_GROUP_get_curve_name(g) == NID_secp384r1) {
+                EC_KEY_free(ec);
+                return 1;
+        }
+        /* downref the eckey, the original is still inside the pkey */
+        EC_KEY_free(ec);
+        return 0;
+}
+#endif /* splint */
+#endif /* USE_ECDSA */
+
+ldns_rdf *
+ldns_sign_public_evp(ldns_buffer *to_sign,
+				 EVP_PKEY *key,
+				 const EVP_MD *digest_type)
+{
+	unsigned int siglen;
+	ldns_rdf *sigdata_rdf;
+	ldns_buffer *b64sig;
+	EVP_MD_CTX ctx;
+	const EVP_MD *md_type;
+	int r;
+
+	siglen = 0;
+	b64sig = ldns_buffer_new(LDNS_MAX_PACKETLEN);
+	if (!b64sig) {
+		return NULL;
+	}
+
+	/* initializes a signing context */
+	md_type = digest_type;
+	if(!md_type) {
+		/* unknown message difest */
+		ldns_buffer_free(b64sig);
+		return NULL;
+	}
+
+	EVP_MD_CTX_init(&ctx);
+	r = EVP_SignInit(&ctx, md_type);
+	if(r == 1) {
+		r = EVP_SignUpdate(&ctx, (unsigned char*)
+					    ldns_buffer_begin(to_sign),
+					    ldns_buffer_position(to_sign));
+	} else {
+		ldns_buffer_free(b64sig);
+		return NULL;
+	}
+	if(r == 1) {
+		r = EVP_SignFinal(&ctx, (unsigned char*)
+					   ldns_buffer_begin(b64sig), &siglen, key);
+	} else {
+		ldns_buffer_free(b64sig);
+		return NULL;
+	}
+	if(r != 1) {
+		ldns_buffer_free(b64sig);
+		return NULL;
+	}
+
+	/* unfortunately, OpenSSL output is differenct from DNS DSA format */
+#ifndef S_SPLINT_S
+	if (EVP_PKEY_type(key->type) == EVP_PKEY_DSA) {
+		sigdata_rdf = ldns_convert_dsa_rrsig_asn12rdf(b64sig, siglen);
+#ifdef USE_ECDSA
+        } else if(EVP_PKEY_type(key->type) == EVP_PKEY_EC &&
+                ldns_pkey_is_ecdsa(key)) {
+                sigdata_rdf = ldns_convert_ecdsa_rrsig_asn12rdf(b64sig, siglen);
+#endif
+	} else {
+		/* ok output for other types is the same */
+		sigdata_rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64, siglen,
+									 ldns_buffer_begin(b64sig));
+	}
+#endif /* splint */
+	ldns_buffer_free(b64sig);
+	EVP_MD_CTX_cleanup(&ctx);
+	return sigdata_rdf;
+}
+
+ldns_rdf *
+ldns_sign_public_rsasha1(ldns_buffer *to_sign, RSA *key)
+{
+	unsigned char *sha1_hash;
+	unsigned int siglen;
+	ldns_rdf *sigdata_rdf;
+	ldns_buffer *b64sig;
+	int result;
+
+	siglen = 0;
+	b64sig = ldns_buffer_new(LDNS_MAX_PACKETLEN);
+	if (!b64sig) {
+		return NULL;
+	}
+
+	sha1_hash = SHA1((unsigned char*)ldns_buffer_begin(to_sign),
+				  ldns_buffer_position(to_sign), NULL);
+	if (!sha1_hash) {
+		ldns_buffer_free(b64sig);
+		return NULL;
+	}
+
+	result = RSA_sign(NID_sha1, sha1_hash, SHA_DIGEST_LENGTH,
+				   (unsigned char*)ldns_buffer_begin(b64sig),
+				   &siglen, key);
+	if (result != 1) {
+		return NULL;
+	}
+
+	if (result != 1) {
+		return NULL;
+	}
+
+	sigdata_rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64, siglen, 
+								 ldns_buffer_begin(b64sig));
+	ldns_buffer_free(b64sig); /* can't free this buffer ?? */
+	return sigdata_rdf;
+}
+
+ldns_rdf *
+ldns_sign_public_rsamd5(ldns_buffer *to_sign, RSA *key)
+{
+	unsigned char *md5_hash;
+	unsigned int siglen;
+	ldns_rdf *sigdata_rdf;
+	ldns_buffer *b64sig;
+
+	b64sig = ldns_buffer_new(LDNS_MAX_PACKETLEN);
+	if (!b64sig) {
+		return NULL;
+	}
+
+	md5_hash = MD5((unsigned char*)ldns_buffer_begin(to_sign),
+				ldns_buffer_position(to_sign), NULL);
+	if (!md5_hash) {
+		ldns_buffer_free(b64sig);
+		return NULL;
+	}
+
+	RSA_sign(NID_md5, md5_hash, MD5_DIGEST_LENGTH,
+		    (unsigned char*)ldns_buffer_begin(b64sig),
+		    &siglen, key);
+
+	sigdata_rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64, siglen,
+								 ldns_buffer_begin(b64sig));
+	ldns_buffer_free(b64sig);
+	return sigdata_rdf;
+}
+#endif /* HAVE_SSL */
+
+/**
+ * Pushes all rrs from the rrsets of type A and AAAA on gluelist.
+ */
+static ldns_status
+ldns_dnssec_addresses_on_glue_list(
+		ldns_dnssec_rrsets *cur_rrset,
+		ldns_rr_list *glue_list)
+{
+	ldns_dnssec_rrs *cur_rrs;
+	while (cur_rrset) {
+		if (cur_rrset->type == LDNS_RR_TYPE_A 
+				|| cur_rrset->type == LDNS_RR_TYPE_AAAA) {
+			for (cur_rrs = cur_rrset->rrs; 
+					cur_rrs; 
+					cur_rrs = cur_rrs->next) {
+				if (cur_rrs->rr) {
+					if (!ldns_rr_list_push_rr(glue_list, 
+							cur_rrs->rr)) {
+						return LDNS_STATUS_MEM_ERR; 
+						/* ldns_rr_list_push_rr()
+						 * returns false when unable
+						 * to increase the capacity
+						 * of the ldsn_rr_list
+						 */
+					}
+				}
+			}
+		}
+		cur_rrset = cur_rrset->next;
+	}
+	return LDNS_STATUS_OK;
+}
+
+/**
+ * Marks the names in the zone that are occluded. Those names will be skipped
+ * when walking the tree with the ldns_dnssec_name_node_next_nonglue()
+ * function. But watch out! Names that are partially occluded (like glue with
+ * the same name as the delegation) will not be marked and should specifically 
+ * be taken into account seperately.
+ *
+ * When glue_list is given (not NULL), in the process of marking the names, all
+ * glue resource records will be pushed to that list, even glue at delegation names.
+ *
+ * \param[in] zone the zone in which to mark the names
+ * \param[in] glue_list the list to which to push the glue rrs
+ * \return LDNS_STATUS_OK on success, an error code otherwise
+ */
+ldns_status
+ldns_dnssec_zone_mark_and_get_glue(ldns_dnssec_zone *zone, 
+	ldns_rr_list *glue_list)
+{
+	ldns_rbnode_t    *node;
+	ldns_dnssec_name *name;
+	ldns_rdf         *owner;
+	ldns_rdf         *cut = NULL; /* keeps track of zone cuts */
+	/* When the cut is caused by a delegation, below_delegation will be 1.
+	 * When caused by a DNAME, below_delegation will be 0.
+	 */
+	int below_delegation = -1; /* init suppresses comiler warning */
+	ldns_status s;
+
+	if (!zone || !zone->names) {
+		return LDNS_STATUS_NULL;
+	}
+	for (node = ldns_rbtree_first(zone->names); 
+			node != LDNS_RBTREE_NULL; 
+			node = ldns_rbtree_next(node)) {
+		name = (ldns_dnssec_name *) node->data;
+		owner = ldns_dnssec_name_name(name);
+
+		if (cut) { 
+			/* The previous node was a zone cut, or a subdomain
+			 * below a zone cut. Is this node (still) a subdomain
+			 * below the cut? Then the name is occluded. Unless
+			 * the name contains a SOA, after which we are 
+			 * authoritative again.
+			 *
+			 * FIXME! If there are labels in between the SOA and
+			 * the cut, going from the authoritative space (below
+			 * the SOA) up into occluded space again, will not be
+			 * detected with the contruct below!
+			 */
+			if (ldns_dname_is_subdomain(owner, cut) &&
+					!ldns_dnssec_rrsets_contains_type(
+					name->rrsets, LDNS_RR_TYPE_SOA)) {
+
+				if (below_delegation && glue_list) {
+					s = ldns_dnssec_addresses_on_glue_list(
+						name->rrsets, glue_list);
+					if (s != LDNS_STATUS_OK) {
+						return s;
+					}
+				}
+				name->is_glue = true; /* Mark occluded name! */
+				continue;
+			} else {
+				cut = NULL;
+			}
+		}
+
+		/* The node is not below a zone cut. Is it a zone cut itself?
+		 * Everything below a SOA is authoritative of course; Except
+		 * when the name also contains a DNAME :).
+		 */
+		if (ldns_dnssec_rrsets_contains_type(
+				name->rrsets, LDNS_RR_TYPE_NS)
+			    && !ldns_dnssec_rrsets_contains_type(
+				name->rrsets, LDNS_RR_TYPE_SOA)) {
+			cut = owner;
+			below_delegation = 1;
+			if (glue_list) { /* record glue on the zone cut */
+				s = ldns_dnssec_addresses_on_glue_list(
+					name->rrsets, glue_list);
+				if (s != LDNS_STATUS_OK) {
+					return s;
+				}
+			}
+		} else if (ldns_dnssec_rrsets_contains_type(
+				name->rrsets, LDNS_RR_TYPE_DNAME)) {
+			cut = owner;
+			below_delegation = 0;
+		}
+	}
+	return LDNS_STATUS_OK;
+}
+
+/**
+ * Marks the names in the zone that are occluded. Those names will be skipped
+ * when walking the tree with the ldns_dnssec_name_node_next_nonglue()
+ * function. But watch out! Names that are partially occluded (like glue with
+ * the same name as the delegation) will not be marked and should specifically 
+ * be taken into account seperately.
+ *
+ * \param[in] zone the zone in which to mark the names
+ * \return LDNS_STATUS_OK on success, an error code otherwise
+ */
+ldns_status
+ldns_dnssec_zone_mark_glue(ldns_dnssec_zone *zone)
+{
+	return ldns_dnssec_zone_mark_and_get_glue(zone, NULL);
+}
+
+ldns_rbnode_t *
+ldns_dnssec_name_node_next_nonglue(ldns_rbnode_t *node)
+{
+	ldns_rbnode_t *next_node = NULL;
+	ldns_dnssec_name *next_name = NULL;
+	bool done = false;
+
+	if (node == LDNS_RBTREE_NULL) {
+		return NULL;
+	}
+	next_node = node;
+	while (!done) {
+		if (next_node == LDNS_RBTREE_NULL) {
+			return NULL;
+		} else {
+			next_name = (ldns_dnssec_name *)next_node->data;
+			if (!next_name->is_glue) {
+				done = true;
+			} else {
+				next_node = ldns_rbtree_next(next_node);
+			}
+		}
+	}
+	return next_node;
+}
+
+ldns_status
+ldns_dnssec_zone_create_nsecs(ldns_dnssec_zone *zone,
+                              ldns_rr_list *new_rrs)
+{
+
+	ldns_rbnode_t *first_node, *cur_node, *next_node;
+	ldns_dnssec_name *cur_name, *next_name;
+	ldns_rr *nsec_rr;
+	uint32_t nsec_ttl;
+	ldns_dnssec_rrsets *soa;
+
+	/* the TTL of NSEC rrs should be set to the minimum TTL of
+	 * the zone SOA (RFC4035 Section 2.3)
+	 */
+	soa = ldns_dnssec_name_find_rrset(zone->soa, LDNS_RR_TYPE_SOA);
+
+	/* did the caller actually set it? if not,
+	 * fall back to default ttl
+	 */
+	if (soa && soa->rrs && soa->rrs->rr
+			&& (ldns_rr_rdf(soa->rrs->rr, 6) != NULL)) {
+		nsec_ttl = ldns_rdf2native_int32(ldns_rr_rdf(soa->rrs->rr, 6));
+	} else {
+		nsec_ttl = LDNS_DEFAULT_TTL;
+	}
+
+	first_node = ldns_dnssec_name_node_next_nonglue(
+			       ldns_rbtree_first(zone->names));
+	cur_node = first_node;
+	if (cur_node) {
+		next_node = ldns_dnssec_name_node_next_nonglue(
+			           ldns_rbtree_next(cur_node));
+	} else {
+		next_node = NULL;
+	}
+
+	while (cur_node && next_node) {
+		cur_name = (ldns_dnssec_name *)cur_node->data;
+		next_name = (ldns_dnssec_name *)next_node->data;
+		nsec_rr = ldns_dnssec_create_nsec(cur_name,
+		                                  next_name,
+		                                  LDNS_RR_TYPE_NSEC);
+		ldns_rr_set_ttl(nsec_rr, nsec_ttl);
+		if(ldns_dnssec_name_add_rr(cur_name, nsec_rr)!=LDNS_STATUS_OK){
+			ldns_rr_free(nsec_rr);
+			return LDNS_STATUS_ERR;
+		}
+		ldns_rr_list_push_rr(new_rrs, nsec_rr);
+		cur_node = next_node;
+		if (cur_node) {
+			next_node = ldns_dnssec_name_node_next_nonglue(
+                               ldns_rbtree_next(cur_node));
+		}
+	}
+
+	if (cur_node && !next_node) {
+		cur_name = (ldns_dnssec_name *)cur_node->data;
+		next_name = (ldns_dnssec_name *)first_node->data;
+		nsec_rr = ldns_dnssec_create_nsec(cur_name,
+		                                  next_name,
+		                                  LDNS_RR_TYPE_NSEC);
+		ldns_rr_set_ttl(nsec_rr, nsec_ttl);
+		if(ldns_dnssec_name_add_rr(cur_name, nsec_rr)!=LDNS_STATUS_OK){
+			ldns_rr_free(nsec_rr);
+			return LDNS_STATUS_ERR;
+		}
+		ldns_rr_list_push_rr(new_rrs, nsec_rr);
+	} else {
+		printf("error\n");
+	}
+
+	return LDNS_STATUS_OK;
+}
+
+#ifdef HAVE_SSL
+/* in dnssec_zone.c */
+extern int ldns_dname_compare_v(const void *a, const void *b);
+
+ldns_status
+ldns_dnssec_zone_create_nsec3s_mkmap(ldns_dnssec_zone *zone,
+		ldns_rr_list *new_rrs,
+		uint8_t algorithm,
+		uint8_t flags,
+		uint16_t iterations,
+		uint8_t salt_length,
+		uint8_t *salt,
+		ldns_rbtree_t **map)
+{
+	ldns_rbnode_t *first_name_node;
+	ldns_rbnode_t *current_name_node;
+	ldns_dnssec_name *current_name;
+	ldns_status result = LDNS_STATUS_OK;
+	ldns_rr *nsec_rr;
+	ldns_rr_list *nsec3_list;
+	uint32_t nsec_ttl;
+	ldns_dnssec_rrsets *soa;
+	ldns_rbnode_t *hashmap_node;
+
+	if (!zone || !new_rrs || !zone->names) {
+		return LDNS_STATUS_ERR;
+	}
+
+	/* the TTL of NSEC rrs should be set to the minimum TTL of
+	 * the zone SOA (RFC4035 Section 2.3)
+	 */
+	soa = ldns_dnssec_name_find_rrset(zone->soa, LDNS_RR_TYPE_SOA);
+
+	/* did the caller actually set it? if not,
+	 * fall back to default ttl
+	 */
+	if (soa && soa->rrs && soa->rrs->rr
+			&& ldns_rr_rdf(soa->rrs->rr, 6) != NULL) {
+		nsec_ttl = ldns_rdf2native_int32(ldns_rr_rdf(soa->rrs->rr, 6));
+	} else {
+		nsec_ttl = LDNS_DEFAULT_TTL;
+	}
+
+	if (map) {
+		if ((*map = ldns_rbtree_create(ldns_dname_compare_v)) 
+				== NULL) {
+			map = NULL;
+		};
+	}
+	nsec3_list = ldns_rr_list_new();
+
+	first_name_node = ldns_dnssec_name_node_next_nonglue(
+					  ldns_rbtree_first(zone->names));
+
+	current_name_node = first_name_node;
+
+	while (current_name_node &&
+	       current_name_node != LDNS_RBTREE_NULL) {
+		current_name = (ldns_dnssec_name *) current_name_node->data;
+		nsec_rr = ldns_dnssec_create_nsec3(current_name,
+		                                   NULL,
+		                                   zone->soa->name,
+		                                   algorithm,
+		                                   flags,
+		                                   iterations,
+		                                   salt_length,
+		                                   salt);
+		/* by default, our nsec based generator adds rrsigs
+		 * remove the bitmap for empty nonterminals */
+		if (!current_name->rrsets) {
+			ldns_rdf_deep_free(ldns_rr_pop_rdf(nsec_rr));
+		}
+		ldns_rr_set_ttl(nsec_rr, nsec_ttl);
+		result = ldns_dnssec_name_add_rr(current_name, nsec_rr);
+		ldns_rr_list_push_rr(new_rrs, nsec_rr);
+		ldns_rr_list_push_rr(nsec3_list, nsec_rr);
+		if (map) {
+			hashmap_node = LDNS_MALLOC(ldns_rbnode_t);
+			if (hashmap_node && ldns_rr_owner(nsec_rr)) {
+				hashmap_node->key = ldns_dname_label(
+					ldns_rr_owner(nsec_rr), 0);
+				if (hashmap_node->key) {
+					hashmap_node->data = current_name->name;
+					(void) ldns_rbtree_insert(
+							*map, hashmap_node);
+				}
+			}
+		}
+		current_name_node = ldns_dnssec_name_node_next_nonglue(
+		                   ldns_rbtree_next(current_name_node));
+	}
+	if (result != LDNS_STATUS_OK) {
+		return result;
+	}
+
+	ldns_rr_list_sort_nsec3(nsec3_list);
+	result = ldns_dnssec_chain_nsec3_list(nsec3_list);
+	if (result != LDNS_STATUS_OK) {
+		return result;
+	}
+
+	ldns_rr_list_free(nsec3_list);
+	return result;
+}
+
+ldns_status
+ldns_dnssec_zone_create_nsec3s(ldns_dnssec_zone *zone,
+		ldns_rr_list *new_rrs,
+		uint8_t algorithm,
+		uint8_t flags,
+		uint16_t iterations,
+		uint8_t salt_length,
+		uint8_t *salt)
+{
+	return ldns_dnssec_zone_create_nsec3s_mkmap(zone, new_rrs, algorithm,
+		       	flags, iterations, salt_length, salt, NULL);
+
+}
+#endif /* HAVE_SSL */
+
+ldns_dnssec_rrs *
+ldns_dnssec_remove_signatures(ldns_dnssec_rrs *signatures,
+						ldns_key_list *key_list,
+						int (*func)(ldns_rr *, void *),
+						void *arg)
+{
+	ldns_dnssec_rrs *base_rrs = signatures;
+	ldns_dnssec_rrs *cur_rr = base_rrs;
+	ldns_dnssec_rrs *prev_rr = NULL;
+	ldns_dnssec_rrs *next_rr;
+
+	uint16_t keytag;
+	size_t i;
+
+	key_list = key_list;
+
+	if (!cur_rr) {
+		switch(func(NULL, arg)) {
+		case LDNS_SIGNATURE_LEAVE_ADD_NEW:
+		case LDNS_SIGNATURE_REMOVE_ADD_NEW:
+		break;
+		case LDNS_SIGNATURE_LEAVE_NO_ADD:
+		case LDNS_SIGNATURE_REMOVE_NO_ADD:
+		ldns_key_list_set_use(key_list, false);
+		break;
+		default:
+			fprintf(stderr, "[XX] unknown return value from callback\n");
+			break;
+		}
+		return NULL;
+	}
+	(void)func(cur_rr->rr, arg);
+
+	while (cur_rr) {
+		next_rr = cur_rr->next;
+
+		switch (func(cur_rr->rr, arg)) {
+		case  LDNS_SIGNATURE_LEAVE_ADD_NEW:
+			prev_rr = cur_rr;
+			break;
+		case LDNS_SIGNATURE_LEAVE_NO_ADD:
+			keytag = ldns_rdf2native_int16(
+					   ldns_rr_rrsig_keytag(cur_rr->rr));
+			for (i = 0; i < ldns_key_list_key_count(key_list); i++) {
+				if (ldns_key_keytag(ldns_key_list_key(key_list, i)) ==
+				    keytag) {
+					ldns_key_set_use(ldns_key_list_key(key_list, i),
+								  false);
+				}
+			}
+			prev_rr = cur_rr;
+			break;
+		case LDNS_SIGNATURE_REMOVE_NO_ADD:
+			keytag = ldns_rdf2native_int16(
+					   ldns_rr_rrsig_keytag(cur_rr->rr));
+			for (i = 0; i < ldns_key_list_key_count(key_list); i++) {
+				if (ldns_key_keytag(ldns_key_list_key(key_list, i))
+				    == keytag) {
+					ldns_key_set_use(ldns_key_list_key(key_list, i),
+								  false);
+				}
+			}
+			if (prev_rr) {
+				prev_rr->next = next_rr;
+			} else {
+				base_rrs = next_rr;
+			}
+			LDNS_FREE(cur_rr);
+			break;
+		case LDNS_SIGNATURE_REMOVE_ADD_NEW:
+			if (prev_rr) {
+				prev_rr->next = next_rr;
+			} else {
+				base_rrs = next_rr;
+			}
+			LDNS_FREE(cur_rr);
+			break;
+		default:
+			fprintf(stderr, "[XX] unknown return value from callback\n");
+			break;
+		}
+		cur_rr = next_rr;
+	}
+
+	return base_rrs;
+}
+
+#ifdef HAVE_SSL
+ldns_status
+ldns_dnssec_zone_create_rrsigs(ldns_dnssec_zone *zone,
+                               ldns_rr_list *new_rrs,
+                               ldns_key_list *key_list,
+                               int (*func)(ldns_rr *, void*),
+                               void *arg)
+{
+	return ldns_dnssec_zone_create_rrsigs_flg(zone, new_rrs, key_list,
+		func, arg, 0);
+}
+
+/** If there are KSKs use only them and mark ZSKs unused */
+static void
+ldns_key_list_filter_for_dnskey(ldns_key_list *key_list)
+{
+	int saw_ksk = 0;
+	size_t i;
+	for(i=0; i<ldns_key_list_key_count(key_list); i++)
+		if((ldns_key_flags(ldns_key_list_key(key_list, i))&LDNS_KEY_SEP_KEY)) {
+			saw_ksk = 1;
+			break;
+		}
+	if(!saw_ksk)
+		return;
+	for(i=0; i<ldns_key_list_key_count(key_list); i++)
+		if(!(ldns_key_flags(ldns_key_list_key(key_list, i))&LDNS_KEY_SEP_KEY))
+			ldns_key_set_use(ldns_key_list_key(key_list, i), 0);
+}
+
+/** If there are no ZSKs use KSK as ZSK */
+static void
+ldns_key_list_filter_for_non_dnskey(ldns_key_list *key_list)
+{
+	int saw_zsk = 0;
+	size_t i;
+	for(i=0; i<ldns_key_list_key_count(key_list); i++)
+		if(!(ldns_key_flags(ldns_key_list_key(key_list, i))&LDNS_KEY_SEP_KEY)) {
+			saw_zsk = 1;
+			break;
+		}
+	if(!saw_zsk)
+		return;
+	/* else filter all KSKs */
+	for(i=0; i<ldns_key_list_key_count(key_list); i++)
+		if((ldns_key_flags(ldns_key_list_key(key_list, i))&LDNS_KEY_SEP_KEY))
+			ldns_key_set_use(ldns_key_list_key(key_list, i), 0);
+}
+
+ldns_status
+ldns_dnssec_zone_create_rrsigs_flg(ldns_dnssec_zone *zone,
+                               ldns_rr_list *new_rrs,
+                               ldns_key_list *key_list,
+                               int (*func)(ldns_rr *, void*),
+                               void *arg,
+			       int flags)
+{
+	ldns_status result = LDNS_STATUS_OK;
+
+	ldns_rbnode_t *cur_node;
+	ldns_rr_list *rr_list;
+
+	ldns_dnssec_name *cur_name;
+	ldns_dnssec_rrsets *cur_rrset;
+	ldns_dnssec_rrs *cur_rr;
+
+	ldns_rr_list *siglist;
+
+	size_t i;
+
+	int on_delegation_point = 0; /* handle partially occluded names */
+
+	ldns_rr_list *pubkey_list = ldns_rr_list_new();
+	zone = zone;
+	new_rrs = new_rrs;
+	key_list = key_list;
+	for (i = 0; i<ldns_key_list_key_count(key_list); i++) {
+		ldns_rr_list_push_rr(pubkey_list,
+						 ldns_key2rr(ldns_key_list_key(key_list, i)));
+	}
+	/* TODO: callback to see is list should be signed */
+	/* TODO: remove 'old' signatures from signature list */
+	cur_node = ldns_rbtree_first(zone->names);
+	while (cur_node != LDNS_RBTREE_NULL) {
+		cur_name = (ldns_dnssec_name *) cur_node->data;
+
+		if (!cur_name->is_glue) {
+			on_delegation_point = ldns_dnssec_rrsets_contains_type(
+					cur_name->rrsets, LDNS_RR_TYPE_NS)
+				&& !ldns_dnssec_rrsets_contains_type(
+					cur_name->rrsets, LDNS_RR_TYPE_SOA);
+			cur_rrset = cur_name->rrsets;
+			while (cur_rrset) {
+				/* reset keys to use */
+				ldns_key_list_set_use(key_list, true);
+
+				/* walk through old sigs, remove the old,
+				   and mark which keys (not) to use) */
+				cur_rrset->signatures =
+					ldns_dnssec_remove_signatures(cur_rrset->signatures,
+											key_list,
+											func,
+											arg);
+				if(!(flags&LDNS_SIGN_DNSKEY_WITH_ZSK) &&
+					cur_rrset->type == LDNS_RR_TYPE_DNSKEY)
+					ldns_key_list_filter_for_dnskey(key_list);
+
+				if(cur_rrset->type != LDNS_RR_TYPE_DNSKEY)
+					ldns_key_list_filter_for_non_dnskey(key_list);
+
+				/* TODO: just set count to zero? */
+				rr_list = ldns_rr_list_new();
+
+				cur_rr = cur_rrset->rrs;
+				while (cur_rr) {
+					ldns_rr_list_push_rr(rr_list, cur_rr->rr);
+					cur_rr = cur_rr->next;
+				}
+
+				/* only sign non-delegation RRsets */
+				/* (glue should have been marked earlier, 
+				 *  except on the delegation points itself) */
+				if (!on_delegation_point ||
+						ldns_rr_list_type(rr_list) 
+							== LDNS_RR_TYPE_DS ||
+						ldns_rr_list_type(rr_list) 
+							== LDNS_RR_TYPE_NSEC ||
+						ldns_rr_list_type(rr_list) 
+							== LDNS_RR_TYPE_NSEC3) {
+					siglist = ldns_sign_public(rr_list, key_list);
+					for (i = 0; i < ldns_rr_list_rr_count(siglist); i++) {
+						if (cur_rrset->signatures) {
+							result = ldns_dnssec_rrs_add_rr(cur_rrset->signatures,
+											   ldns_rr_list_rr(siglist,
+														    i));
+						} else {
+							cur_rrset->signatures = ldns_dnssec_rrs_new();
+							cur_rrset->signatures->rr =
+								ldns_rr_list_rr(siglist, i);
+							ldns_rr_list_push_rr(new_rrs,
+											 ldns_rr_list_rr(siglist,
+														  i));
+						}
+					}
+					ldns_rr_list_free(siglist);
+				}
+
+				ldns_rr_list_free(rr_list);
+
+				cur_rrset = cur_rrset->next;
+			}
+
+			/* sign the nsec */
+			ldns_key_list_set_use(key_list, true);
+			cur_name->nsec_signatures =
+				ldns_dnssec_remove_signatures(cur_name->nsec_signatures,
+										key_list,
+										func,
+										arg);
+			ldns_key_list_filter_for_non_dnskey(key_list);
+
+			rr_list = ldns_rr_list_new();
+			ldns_rr_list_push_rr(rr_list, cur_name->nsec);
+			siglist = ldns_sign_public(rr_list, key_list);
+
+			for (i = 0; i < ldns_rr_list_rr_count(siglist); i++) {
+				if (cur_name->nsec_signatures) {
+					result = ldns_dnssec_rrs_add_rr(cur_name->nsec_signatures,
+									   ldns_rr_list_rr(siglist, i));
+				} else {
+					cur_name->nsec_signatures = ldns_dnssec_rrs_new();
+					cur_name->nsec_signatures->rr =
+						ldns_rr_list_rr(siglist, i);
+					ldns_rr_list_push_rr(new_rrs,
+									 ldns_rr_list_rr(siglist, i));
+				}
+			}
+
+			ldns_rr_list_free(siglist);
+			ldns_rr_list_free(rr_list);
+		}
+		cur_node = ldns_rbtree_next(cur_node);
+	}
+
+	ldns_rr_list_deep_free(pubkey_list);
+	return result;
+}
+
+ldns_status
+ldns_dnssec_zone_sign(ldns_dnssec_zone *zone,
+				  ldns_rr_list *new_rrs,
+				  ldns_key_list *key_list,
+				  int (*func)(ldns_rr *, void *),
+				  void *arg)
+{
+	return ldns_dnssec_zone_sign_flg(zone, new_rrs, key_list, func, arg, 0);
+}
+
+ldns_status
+ldns_dnssec_zone_sign_flg(ldns_dnssec_zone *zone,
+				  ldns_rr_list *new_rrs,
+				  ldns_key_list *key_list,
+				  int (*func)(ldns_rr *, void *),
+				  void *arg,
+				  int flags)
+{
+	ldns_status result = LDNS_STATUS_OK;
+
+	if (!zone || !new_rrs || !key_list) {
+		return LDNS_STATUS_ERR;
+	}
+
+	/* zone is already sorted */
+	result = ldns_dnssec_zone_mark_glue(zone);
+	if (result != LDNS_STATUS_OK) {
+		return result;
+	}
+
+	/* check whether we need to add nsecs */
+	if (zone->names && !((ldns_dnssec_name *)zone->names->root->data)->nsec) {
+		result = ldns_dnssec_zone_create_nsecs(zone, new_rrs);
+		if (result != LDNS_STATUS_OK) {
+			return result;
+		}
+	}
+
+	result = ldns_dnssec_zone_create_rrsigs_flg(zone,
+					new_rrs,
+					key_list,
+					func,
+					arg,
+					flags);
+
+	return result;
+}
+
+ldns_status
+ldns_dnssec_zone_sign_nsec3(ldns_dnssec_zone *zone,
+					   ldns_rr_list *new_rrs,
+					   ldns_key_list *key_list,
+					   int (*func)(ldns_rr *, void *),
+					   void *arg,
+					   uint8_t algorithm,
+					   uint8_t flags,
+					   uint16_t iterations,
+					   uint8_t salt_length,
+					   uint8_t *salt)
+{
+	return ldns_dnssec_zone_sign_nsec3_flg_mkmap(zone, new_rrs, key_list,
+		func, arg, algorithm, flags, iterations, salt_length, salt, 0,
+	       	NULL);
+}
+
+ldns_status
+ldns_dnssec_zone_sign_nsec3_flg_mkmap(ldns_dnssec_zone *zone,
+		ldns_rr_list *new_rrs,
+		ldns_key_list *key_list,
+		int (*func)(ldns_rr *, void *),
+		void *arg,
+		uint8_t algorithm,
+		uint8_t flags,
+		uint16_t iterations,
+		uint8_t salt_length,
+		uint8_t *salt,
+		int signflags,
+		ldns_rbtree_t **map)
+{
+	ldns_rr *nsec3, *nsec3param;
+	ldns_status result = LDNS_STATUS_OK;
+
+	/* zone is already sorted */
+	result = ldns_dnssec_zone_mark_glue(zone);
+	if (result != LDNS_STATUS_OK) {
+		return result;
+	}
+
+	/* TODO if there are already nsec3s presents and their
+	 * parameters are the same as these, we don't have to recreate
+	 */
+	if (zone->names) {
+		/* add empty nonterminals */
+		result = ldns_dnssec_zone_add_empty_nonterminals(zone);
+		if (result != LDNS_STATUS_OK) {
+			return result;
+		}
+
+		nsec3 = ((ldns_dnssec_name *)zone->names->root->data)->nsec;
+		if (nsec3 && ldns_rr_get_type(nsec3) == LDNS_RR_TYPE_NSEC3) {
+			/* no need to recreate */
+		} else {
+			if (!ldns_dnssec_zone_find_rrset(zone,
+									   zone->soa->name,
+									   LDNS_RR_TYPE_NSEC3PARAM)) {
+				/* create and add the nsec3param rr */
+				nsec3param =
+					ldns_rr_new_frm_type(LDNS_RR_TYPE_NSEC3PARAM);
+				ldns_rr_set_owner(nsec3param,
+							   ldns_rdf_clone(zone->soa->name));
+				ldns_nsec3_add_param_rdfs(nsec3param,
+									 algorithm,
+									 flags,
+									 iterations,
+									 salt_length,
+									 salt);
+				/* always set bit 7 of the flags to zero, according to
+				 * rfc5155 section 11 */
+				ldns_set_bit(ldns_rdf_data(ldns_rr_rdf(nsec3param, 1)), 7, 0);
+				result = ldns_dnssec_zone_add_rr(zone, nsec3param);
+				if (result != LDNS_STATUS_OK) {
+					return result;
+				}
+				ldns_rr_list_push_rr(new_rrs, nsec3param);
+			}
+			result = ldns_dnssec_zone_create_nsec3s_mkmap(zone,
+											new_rrs,
+											algorithm,
+											flags,
+											iterations,
+											salt_length,
+											salt,
+											map);
+			if (result != LDNS_STATUS_OK) {
+				return result;
+			}
+		}
+
+		result = ldns_dnssec_zone_create_rrsigs_flg(zone,
+						new_rrs,
+						key_list,
+						func,
+						arg,
+						signflags);
+	}
+
+	return result;
+}
+
+ldns_status
+ldns_dnssec_zone_sign_nsec3_flg(ldns_dnssec_zone *zone,
+		ldns_rr_list *new_rrs,
+		ldns_key_list *key_list,
+		int (*func)(ldns_rr *, void *),
+		void *arg,
+		uint8_t algorithm,
+		uint8_t flags,
+		uint16_t iterations,
+		uint8_t salt_length,
+		uint8_t *salt,
+		int signflags)
+{
+	return ldns_dnssec_zone_sign_nsec3_flg_mkmap(zone, new_rrs, key_list,
+		func, arg, algorithm, flags, iterations, salt_length, salt,
+		signflags, NULL);
+}
+
+ldns_zone *
+ldns_zone_sign(const ldns_zone *zone, ldns_key_list *key_list)
+{
+	ldns_dnssec_zone *dnssec_zone;
+	ldns_zone *signed_zone;
+	ldns_rr_list *new_rrs;
+	size_t i;
+
+	signed_zone = ldns_zone_new();
+	dnssec_zone = ldns_dnssec_zone_new();
+
+	(void) ldns_dnssec_zone_add_rr(dnssec_zone, ldns_zone_soa(zone));
+	ldns_zone_set_soa(signed_zone, ldns_rr_clone(ldns_zone_soa(zone)));
+
+	for (i = 0; i < ldns_rr_list_rr_count(ldns_zone_rrs(zone)); i++) {
+		(void) ldns_dnssec_zone_add_rr(dnssec_zone,
+								 ldns_rr_list_rr(ldns_zone_rrs(zone),
+											  i));
+		ldns_zone_push_rr(signed_zone,
+					   ldns_rr_clone(ldns_rr_list_rr(ldns_zone_rrs(zone),
+											   i)));
+	}
+
+	new_rrs = ldns_rr_list_new();
+	(void) ldns_dnssec_zone_sign(dnssec_zone,
+						    new_rrs,
+						    key_list,
+						    ldns_dnssec_default_replace_signatures,
+						    NULL);
+
+    	for (i = 0; i < ldns_rr_list_rr_count(new_rrs); i++) {
+		ldns_rr_list_push_rr(ldns_zone_rrs(signed_zone),
+						 ldns_rr_clone(ldns_rr_list_rr(new_rrs, i)));
+	}
+
+	ldns_rr_list_deep_free(new_rrs);
+	ldns_dnssec_zone_free(dnssec_zone);
+
+	return signed_zone;
+}
+
+ldns_zone *
+ldns_zone_sign_nsec3(ldns_zone *zone, ldns_key_list *key_list, uint8_t algorithm, uint8_t flags, uint16_t iterations, uint8_t salt_length, uint8_t *salt)
+{
+	ldns_dnssec_zone *dnssec_zone;
+	ldns_zone *signed_zone;
+	ldns_rr_list *new_rrs;
+	size_t i;
+
+	signed_zone = ldns_zone_new();
+	dnssec_zone = ldns_dnssec_zone_new();
+
+	(void) ldns_dnssec_zone_add_rr(dnssec_zone, ldns_zone_soa(zone));
+	ldns_zone_set_soa(signed_zone, ldns_rr_clone(ldns_zone_soa(zone)));
+
+	for (i = 0; i < ldns_rr_list_rr_count(ldns_zone_rrs(zone)); i++) {
+		(void) ldns_dnssec_zone_add_rr(dnssec_zone,
+								 ldns_rr_list_rr(ldns_zone_rrs(zone),
+											  i));
+		ldns_zone_push_rr(signed_zone, 
+					   ldns_rr_clone(ldns_rr_list_rr(ldns_zone_rrs(zone),
+											   i)));
+	}
+
+	new_rrs = ldns_rr_list_new();
+	(void) ldns_dnssec_zone_sign_nsec3(dnssec_zone,
+								new_rrs,
+								key_list,
+								ldns_dnssec_default_replace_signatures,
+								NULL,
+								algorithm,
+								flags,
+								iterations,
+								salt_length,
+								salt);
+
+    	for (i = 0; i < ldns_rr_list_rr_count(new_rrs); i++) {
+		ldns_rr_list_push_rr(ldns_zone_rrs(signed_zone),
+						 ldns_rr_clone(ldns_rr_list_rr(new_rrs, i)));
+	}
+
+	ldns_rr_list_deep_free(new_rrs);
+	ldns_dnssec_zone_free(dnssec_zone);
+
+	return signed_zone;
+}
+#endif /* HAVE_SSL */
+
+
diff --git a/3rdParty/Ldns/src/src/dnssec_verify.c b/3rdParty/Ldns/src/src/dnssec_verify.c
new file mode 100644
index 0000000..18af5d2
--- /dev/null
+++ b/3rdParty/Ldns/src/src/dnssec_verify.c
@@ -0,0 +1,2642 @@
+#include <ldns/config.h>
+
+#include <ldns/ldns.h>
+
+#include <strings.h>
+#include <time.h>
+
+#ifdef HAVE_SSL
+/* this entire file is rather useless when you don't have
+ * crypto...
+ */
+#include <openssl/ssl.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#include <openssl/err.h>
+#include <openssl/md5.h>
+
+ldns_dnssec_data_chain *
+ldns_dnssec_data_chain_new()
+{
+	ldns_dnssec_data_chain *nc = LDNS_CALLOC(ldns_dnssec_data_chain, 1);
+        if(!nc) return NULL;
+	/* 
+	 * not needed anymore because CALLOC initalizes everything to zero.
+
+	nc->rrset = NULL;
+	nc->parent_type = 0;
+	nc->parent = NULL;
+	nc->signatures = NULL;
+	nc->packet_rcode = 0;
+	nc->packet_qtype = 0;
+	nc->packet_nodata = false;
+
+	 */
+	return nc;
+}
+
+void
+ldns_dnssec_data_chain_free(ldns_dnssec_data_chain *chain)
+{
+	LDNS_FREE(chain);
+}
+
+void
+ldns_dnssec_data_chain_deep_free(ldns_dnssec_data_chain *chain)
+{
+	ldns_rr_list_deep_free(chain->rrset);
+	ldns_rr_list_deep_free(chain->signatures);
+	if (chain->parent) {
+		ldns_dnssec_data_chain_deep_free(chain->parent);
+	}
+	LDNS_FREE(chain);
+}
+
+void
+ldns_dnssec_data_chain_print_fmt(FILE *out, const ldns_output_format *fmt,
+		const ldns_dnssec_data_chain *chain)
+{
+	ldns_lookup_table *rcode;
+	const ldns_rr_descriptor *rr_descriptor;
+	if (chain) {
+		ldns_dnssec_data_chain_print_fmt(out, fmt, chain->parent);
+		if (ldns_rr_list_rr_count(chain->rrset) > 0) {
+			rcode = ldns_lookup_by_id(ldns_rcodes,
+								 (int) chain->packet_rcode);
+			if (rcode) {
+				fprintf(out, ";; rcode: %s\n", rcode->name);
+			}
+
+			rr_descriptor = ldns_rr_descript(chain->packet_qtype);
+			if (rr_descriptor && rr_descriptor->_name) {
+				fprintf(out, ";; qtype: %s\n", rr_descriptor->_name);
+			} else if (chain->packet_qtype != 0) {
+				fprintf(out, "TYPE%u", 
+					   chain->packet_qtype);
+			}
+			if (chain->packet_nodata) {
+				fprintf(out, ";; NODATA response\n");
+			}
+			fprintf(out, "rrset:\n");
+			ldns_rr_list_print_fmt(out, fmt, chain->rrset);
+			fprintf(out, "sigs:\n");
+			ldns_rr_list_print_fmt(out, fmt, chain->signatures);
+			fprintf(out, "---\n");
+		} else {
+			fprintf(out, "<no data>\n");
+		}
+	}
+}
+void
+ldns_dnssec_data_chain_print(FILE *out, const ldns_dnssec_data_chain *chain)
+{
+	ldns_dnssec_data_chain_print_fmt(
+			out, ldns_output_format_default, chain);
+}
+
+
+static void
+ldns_dnssec_build_data_chain_dnskey(ldns_resolver *res,
+					    uint16_t qflags,
+					    const ldns_pkt *pkt,
+					    ldns_rr_list *signatures,
+						ldns_dnssec_data_chain *new_chain,
+						ldns_rdf *key_name,
+						ldns_rr_class c) {
+	ldns_rr_list *keys;
+	ldns_pkt *my_pkt;
+	if (signatures && ldns_rr_list_rr_count(signatures) > 0) {
+		new_chain->signatures = ldns_rr_list_clone(signatures);
+		new_chain->parent_type = 0;
+
+		keys = ldns_pkt_rr_list_by_name_and_type(
+				  pkt,
+				 key_name,
+				 LDNS_RR_TYPE_DNSKEY,
+				 LDNS_SECTION_ANY_NOQUESTION
+			  );
+		if (!keys) {
+			my_pkt = ldns_resolver_query(res,
+									key_name,
+									LDNS_RR_TYPE_DNSKEY,
+									c,
+									qflags);
+			if (my_pkt) {
+			keys = ldns_pkt_rr_list_by_name_and_type(
+					  my_pkt,
+					 key_name,
+					 LDNS_RR_TYPE_DNSKEY,
+					 LDNS_SECTION_ANY_NOQUESTION
+				  );
+			new_chain->parent = ldns_dnssec_build_data_chain(res,
+													qflags,
+													keys,
+													my_pkt,
+													NULL);
+			new_chain->parent->packet_qtype = LDNS_RR_TYPE_DNSKEY;
+			ldns_pkt_free(my_pkt);
+			}
+		} else {
+			new_chain->parent = ldns_dnssec_build_data_chain(res,
+													qflags,
+													keys,
+													pkt,
+													NULL);
+			new_chain->parent->packet_qtype = LDNS_RR_TYPE_DNSKEY;
+		}
+		ldns_rr_list_deep_free(keys);
+	}
+}
+
+static void
+ldns_dnssec_build_data_chain_other(ldns_resolver *res,
+					    uint16_t qflags,
+						ldns_dnssec_data_chain *new_chain,
+						ldns_rdf *key_name,
+						ldns_rr_class c,
+						ldns_rr_list *dss)
+{
+	/* 'self-signed', parent is a DS */
+	
+	/* okay, either we have other keys signing the current one,
+	 * or the current
+	 * one should have a DS record in the parent zone.
+	 * How do we find this out? Try both?
+	 *
+	 * request DNSKEYS for current zone,
+	 * add all signatures to current level
+	 */
+	ldns_pkt *my_pkt;
+	ldns_rr_list *signatures2;
+	
+	new_chain->parent_type = 1;
+
+	my_pkt = ldns_resolver_query(res,
+							key_name,
+							LDNS_RR_TYPE_DS,
+							c,
+							qflags);
+	if (my_pkt) {
+	dss = ldns_pkt_rr_list_by_name_and_type(my_pkt,
+									key_name,
+									LDNS_RR_TYPE_DS,
+									LDNS_SECTION_ANY_NOQUESTION
+									);
+	if (dss) {
+		new_chain->parent = ldns_dnssec_build_data_chain(res,
+												qflags,
+												dss,
+												my_pkt,
+												NULL);
+		new_chain->parent->packet_qtype = LDNS_RR_TYPE_DS;
+		ldns_rr_list_deep_free(dss);
+	}
+	ldns_pkt_free(my_pkt);
+	}
+
+	my_pkt = ldns_resolver_query(res,
+							key_name,
+							LDNS_RR_TYPE_DNSKEY,
+							c,
+							qflags);
+	if (my_pkt) {
+	signatures2 = ldns_pkt_rr_list_by_name_and_type(my_pkt,
+										   key_name,
+										   LDNS_RR_TYPE_RRSIG,
+										   LDNS_SECTION_ANSWER);
+	if (signatures2) {
+		if (new_chain->signatures) {
+			printf("There were already sigs!\n");
+			ldns_rr_list_deep_free(new_chain->signatures);
+			printf("replacing the old sigs\n");
+		}
+		new_chain->signatures = signatures2;
+	}
+	ldns_pkt_free(my_pkt);
+	}
+}
+
+ldns_dnssec_data_chain *
+ldns_dnssec_build_data_chain_nokeyname(ldns_resolver *res,
+                                       uint16_t qflags,
+                                       ldns_rr *orig_rr,
+                                       const ldns_rr_list *rrset,
+                                       ldns_dnssec_data_chain *new_chain)
+{
+	ldns_rdf *possible_parent_name;
+	ldns_pkt *my_pkt;
+	/* apparently we were not able to find a signing key, so
+	   we assume the chain ends here
+	*/
+	/* try parents for auth denial of DS */
+	if (orig_rr) {
+		possible_parent_name = ldns_rr_owner(orig_rr);
+	} else if (rrset && ldns_rr_list_rr_count(rrset) > 0) {
+		possible_parent_name = ldns_rr_owner(ldns_rr_list_rr(rrset, 0));
+	} else {
+		/* no information to go on, give up */
+		return new_chain;
+	}
+
+	my_pkt = ldns_resolver_query(res,
+	              possible_parent_name,
+	              LDNS_RR_TYPE_DS,
+	              LDNS_RR_CLASS_IN,
+	              qflags);
+	if (!my_pkt) {
+		return new_chain;
+	}
+
+	if (ldns_pkt_ancount(my_pkt) > 0) {
+		/* add error, no sigs but DS in parent */
+		/*ldns_pkt_print(stdout, my_pkt);*/
+		ldns_pkt_free(my_pkt);
+	} else {
+		/* are there signatures? */
+		new_chain->parent =  ldns_dnssec_build_data_chain(res, 
+		                          qflags, 
+		                          NULL,
+		                          my_pkt,
+		                          NULL);
+
+		new_chain->parent->packet_qtype = LDNS_RR_TYPE_DS;
+		
+	}
+	return new_chain;
+}
+
+
+ldns_dnssec_data_chain *
+ldns_dnssec_build_data_chain(ldns_resolver *res,
+					    uint16_t qflags,
+					    const ldns_rr_list *rrset,
+					    const ldns_pkt *pkt,
+					    ldns_rr *orig_rr)
+{
+	ldns_rr_list *signatures = NULL;
+	ldns_rr_list *dss = NULL;
+	
+	ldns_rr_list *my_rrset;
+
+	ldns_pkt *my_pkt;
+
+	ldns_rdf *name = NULL, *key_name = NULL;
+	ldns_rr_type type = 0;
+	ldns_rr_class c = 0;
+
+	bool other_rrset = false;
+	
+	ldns_dnssec_data_chain *new_chain = ldns_dnssec_data_chain_new();
+
+	if (!ldns_dnssec_pkt_has_rrsigs(pkt)) {
+		/* hmm. no dnssec data in the packet. go up to try and deny
+		 * DS? */
+		return new_chain;
+	}
+
+	if (orig_rr) {
+		new_chain->rrset = ldns_rr_list_new();
+		ldns_rr_list_push_rr(new_chain->rrset, orig_rr);
+		new_chain->parent = ldns_dnssec_build_data_chain(res,
+											    qflags,
+											    rrset,
+											    pkt,
+											    NULL);
+		new_chain->packet_rcode = ldns_pkt_get_rcode(pkt);
+		new_chain->packet_qtype = ldns_rr_get_type(orig_rr);
+		if (ldns_pkt_ancount(pkt) == 0) {
+			new_chain->packet_nodata = true;
+		}
+		return new_chain;
+	}
+	
+	if (!rrset || ldns_rr_list_rr_count(rrset) < 1) {
+		/* hmm, no data, do we have denial? only works if pkt was given,
+		   otherwise caller has to do the check himself */
+		new_chain->packet_nodata = true;
+		if (pkt) {
+			my_rrset = ldns_pkt_rr_list_by_type(pkt,
+										 LDNS_RR_TYPE_NSEC,
+										 LDNS_SECTION_ANY_NOQUESTION
+										 );
+			if (my_rrset) {
+				if (ldns_rr_list_rr_count(my_rrset) > 0) {
+					type = LDNS_RR_TYPE_NSEC;
+					other_rrset = true;
+				} else {
+					ldns_rr_list_deep_free(my_rrset);
+					my_rrset = NULL;
+				}
+			} else {
+				/* nothing, try nsec3 */
+				my_rrset = ldns_pkt_rr_list_by_type(pkt,
+						     LDNS_RR_TYPE_NSEC3,
+							LDNS_SECTION_ANY_NOQUESTION);
+				if (my_rrset) {
+					if (ldns_rr_list_rr_count(my_rrset) > 0) {
+						type = LDNS_RR_TYPE_NSEC3;
+						other_rrset = true;
+					} else {
+						ldns_rr_list_deep_free(my_rrset);
+						my_rrset = NULL;
+					}
+				} else {
+					/* nothing, stop */
+					/* try parent zone? for denied insecure? */
+					return new_chain;
+				}
+			}
+		} else {
+			return new_chain;
+		}
+	} else {
+		my_rrset = (ldns_rr_list *) rrset;
+	}
+	
+	if (my_rrset && ldns_rr_list_rr_count(my_rrset) > 0) {
+		new_chain->rrset = ldns_rr_list_clone(my_rrset);
+		name = ldns_rr_owner(ldns_rr_list_rr(my_rrset, 0));
+		type = ldns_rr_get_type(ldns_rr_list_rr(my_rrset, 0));
+		c = ldns_rr_get_class(ldns_rr_list_rr(my_rrset, 0));
+	}
+	
+	if (other_rrset) {
+		ldns_rr_list_deep_free(my_rrset);
+	}
+	
+	/* normally there will only be 1 signature 'set'
+	   but there can be more than 1 denial (wildcards)
+	   so check for NSEC
+	*/
+	if (type == LDNS_RR_TYPE_NSEC || type == LDNS_RR_TYPE_NSEC3) {
+		/* just throw in all signatures, the tree builder must sort
+		   this out */
+		if (pkt) {
+			signatures = ldns_dnssec_pkt_get_rrsigs_for_type(pkt, type);
+		} else {
+			my_pkt = ldns_resolver_query(res, name, type, c, qflags);
+			if (my_pkt) {
+			signatures = ldns_dnssec_pkt_get_rrsigs_for_type(pkt, type);
+			ldns_pkt_free(my_pkt);
+			}
+		}
+	} else {
+		if (pkt) {
+			signatures =
+				ldns_dnssec_pkt_get_rrsigs_for_name_and_type(pkt,
+													name,
+													type);
+		}
+		if (!signatures) {
+			my_pkt = ldns_resolver_query(res, name, type, c, qflags);
+			if (my_pkt) {
+			signatures =
+				ldns_dnssec_pkt_get_rrsigs_for_name_and_type(my_pkt,
+													name,
+													type);
+			ldns_pkt_free(my_pkt);
+			}
+		}
+	}
+
+	if (signatures && ldns_rr_list_rr_count(signatures) > 0) {
+		key_name = ldns_rr_rdf(ldns_rr_list_rr(signatures, 0), 7);
+	}
+
+	if (!key_name) {
+		return ldns_dnssec_build_data_chain_nokeyname(res,
+		                                              qflags,
+		                                              orig_rr,
+		                                              rrset,
+		                                              new_chain);
+	}
+
+	if (type != LDNS_RR_TYPE_DNSKEY) {
+		ldns_dnssec_build_data_chain_dnskey(res,
+		                                    qflags,
+		                                    pkt,
+		                                    signatures,
+		                                    new_chain,
+		                                    key_name,
+		                                    c
+		                                    );
+	} else {
+		ldns_dnssec_build_data_chain_other(res,
+		                                   qflags,
+		                                   new_chain,
+		                                   key_name,
+		                                   c,
+		                                   dss
+		                                    
+		                                    );
+	}
+	if (signatures) {
+		ldns_rr_list_deep_free(signatures);
+	}
+
+	return new_chain;
+}
+
+ldns_dnssec_trust_tree *
+ldns_dnssec_trust_tree_new()
+{
+	ldns_dnssec_trust_tree *new_tree = LDNS_XMALLOC(ldns_dnssec_trust_tree,
+										   1);
+        if(!new_tree) return NULL;
+	new_tree->rr = NULL;
+	new_tree->rrset = NULL;
+	new_tree->parent_count = 0;
+
+	return new_tree;
+}
+
+void
+ldns_dnssec_trust_tree_free(ldns_dnssec_trust_tree *tree)
+{
+	size_t i;
+	if (tree) {
+		for (i = 0; i < tree->parent_count; i++) {
+			ldns_dnssec_trust_tree_free(tree->parents[i]);
+		}
+	}
+	LDNS_FREE(tree);
+}
+
+size_t
+ldns_dnssec_trust_tree_depth(ldns_dnssec_trust_tree *tree)
+{
+	size_t result = 0;
+	size_t parent = 0;
+	size_t i;
+	
+	for (i = 0; i < tree->parent_count; i++) {
+		parent = ldns_dnssec_trust_tree_depth(tree->parents[i]);
+		if (parent > result) {
+			result = parent;
+		}
+	}
+	return 1 + result;
+}
+
+/* TODO ldns_ */
+static void
+print_tabs(FILE *out, size_t nr, uint8_t *map, size_t treedepth)
+{
+	size_t i;
+	for (i = 0; i < nr; i++) {
+		if (i == nr - 1) {
+			fprintf(out, "|---");
+		} else if (map && i < treedepth && map[i] == 1) {
+			fprintf(out, "|   ");
+		} else {
+			fprintf(out, "    ");
+		}
+	}
+}
+
+void
+ldns_dnssec_trust_tree_print_sm_fmt(FILE *out, 
+		const ldns_output_format *fmt,
+		ldns_dnssec_trust_tree *tree,
+		size_t tabs,
+		bool extended,
+		uint8_t *sibmap,
+		size_t treedepth)
+{
+	size_t i;
+	const ldns_rr_descriptor *descriptor;
+	bool mapset = false;
+	
+	if (!sibmap) {
+		treedepth = ldns_dnssec_trust_tree_depth(tree);
+		sibmap = malloc(treedepth);
+                if(!sibmap)
+                        return; /* mem err */
+		memset(sibmap, 0, treedepth);
+		mapset = true;
+	}
+	
+	if (tree) {
+		if (tree->rr) {
+			print_tabs(out, tabs, sibmap, treedepth);
+			ldns_rdf_print(out, ldns_rr_owner(tree->rr));
+			descriptor = ldns_rr_descript(ldns_rr_get_type(tree->rr));
+
+			if (descriptor->_name) {
+				fprintf(out, " (%s", descriptor->_name);
+			} else {
+				fprintf(out, " (TYPE%d", 
+					   ldns_rr_get_type(tree->rr));
+			}
+			if (tabs > 0) {
+				if (ldns_rr_get_type(tree->rr) == LDNS_RR_TYPE_DNSKEY) {
+					fprintf(out, " keytag: %u",
+					        (unsigned int) ldns_calc_keytag(tree->rr));
+					fprintf(out, " alg: ");
+					ldns_rdf_print(out, ldns_rr_rdf(tree->rr, 2));
+					fprintf(out, " flags: ");
+					ldns_rdf_print(out, ldns_rr_rdf(tree->rr, 0));
+				} else if (ldns_rr_get_type(tree->rr) == LDNS_RR_TYPE_DS) {
+					fprintf(out, " keytag: ");
+					ldns_rdf_print(out, ldns_rr_rdf(tree->rr, 0));
+					fprintf(out, " digest type: ");
+					ldns_rdf_print(out, ldns_rr_rdf(tree->rr, 2));
+				}
+				if (ldns_rr_get_type(tree->rr) == LDNS_RR_TYPE_NSEC) {
+					fprintf(out, " ");
+					ldns_rdf_print(out, ldns_rr_rdf(tree->rr, 0));
+					fprintf(out, " ");
+					ldns_rdf_print(out, ldns_rr_rdf(tree->rr, 1));
+				}
+			}
+			
+			fprintf(out, ")\n");
+			for (i = 0; i < tree->parent_count; i++) {
+				if (tree->parent_count > 1 && i < tree->parent_count - 1) {
+					sibmap[tabs] = 1;
+				} else {
+					sibmap[tabs] = 0;
+				}
+				/* only print errors */
+				if (ldns_rr_get_type(tree->parents[i]->rr) == 
+				    LDNS_RR_TYPE_NSEC ||
+				    ldns_rr_get_type(tree->parents[i]->rr) ==
+				    LDNS_RR_TYPE_NSEC3) {
+					if (tree->parent_status[i] == LDNS_STATUS_OK) {
+						print_tabs(out, tabs + 1, sibmap, treedepth);
+						if (tabs == 0 &&
+						    ldns_rr_get_type(tree->rr) == LDNS_RR_TYPE_NS &&
+							ldns_rr_rd_count(tree->rr) > 0) {
+							fprintf(out, "Existence of DS is denied by:\n");
+						} else {
+							fprintf(out, "Existence is denied by:\n");
+						}
+					} else {
+						/* NS records aren't signed */
+						if (ldns_rr_get_type(tree->rr) == LDNS_RR_TYPE_NS) {
+							fprintf(out, "Existence of DS is denied by:\n");
+						} else {
+							print_tabs(out, tabs + 1, sibmap, treedepth);
+							fprintf(out,
+								   "Error in denial of existence: %s\n",
+								   ldns_get_errorstr_by_id(
+									   tree->parent_status[i]));
+						}
+					}
+				} else
+					if (tree->parent_status[i] != LDNS_STATUS_OK) {
+						print_tabs(out, tabs + 1, sibmap, treedepth);
+						fprintf(out,
+							   "%s:\n",
+							   ldns_get_errorstr_by_id(
+							       tree->parent_status[i]));
+						if (tree->parent_status[i]
+						    == LDNS_STATUS_SSL_ERR) {
+							printf("; SSL Error: ");
+							ERR_load_crypto_strings();
+							ERR_print_errors_fp(stdout);
+							printf("\n");
+						}
+						ldns_rr_print_fmt(out, fmt, 
+							tree->
+							parent_signature[i]);
+						printf("For RRset:\n");
+						ldns_rr_list_print_fmt(out, fmt,
+								tree->rrset);
+						printf("With key:\n");
+						ldns_rr_print_fmt(out, fmt,
+							tree->parents[i]->rr);
+					}
+				ldns_dnssec_trust_tree_print_sm_fmt(out, fmt,
+						tree->parents[i],
+						tabs+1,
+						extended,
+						sibmap,
+						treedepth);
+			}
+		} else {
+			print_tabs(out, tabs, sibmap, treedepth);
+			fprintf(out, "<no data>\n");
+		}
+	} else {
+		fprintf(out, "<null pointer>\n");
+	}
+	
+	if (mapset) {
+		free(sibmap);
+	}
+}
+
+void
+ldns_dnssec_trust_tree_print_sm(FILE *out, 
+		ldns_dnssec_trust_tree *tree,
+		size_t tabs,
+		bool extended,
+		uint8_t *sibmap,
+		size_t treedepth)
+{
+	ldns_dnssec_trust_tree_print_sm_fmt(out, ldns_output_format_default, 
+			tree, tabs, extended, sibmap, treedepth);
+}
+
+void
+ldns_dnssec_trust_tree_print_fmt(FILE *out, const ldns_output_format *fmt,
+		ldns_dnssec_trust_tree *tree,
+		size_t tabs,
+		bool extended)
+{
+	ldns_dnssec_trust_tree_print_sm_fmt(out, fmt, 
+			tree, tabs, extended, NULL, 0);
+}
+
+void
+ldns_dnssec_trust_tree_print(FILE *out,
+		ldns_dnssec_trust_tree *tree,
+		size_t tabs,
+		bool extended)
+{
+	ldns_dnssec_trust_tree_print_fmt(out, ldns_output_format_default, 
+			tree, tabs, extended);
+}
+
+
+ldns_status
+ldns_dnssec_trust_tree_add_parent(ldns_dnssec_trust_tree *tree,
+                                  const ldns_dnssec_trust_tree *parent,
+                                  const ldns_rr *signature,
+                                  const ldns_status parent_status)
+{
+	if (tree
+	    && parent
+	    && tree->parent_count < LDNS_DNSSEC_TRUST_TREE_MAX_PARENTS) {
+		/*
+		  printf("Add parent for: ");
+		  ldns_rr_print(stdout, tree->rr);
+		  printf("parent: ");
+		  ldns_rr_print(stdout, parent->rr);
+		*/
+		tree->parents[tree->parent_count] =
+			(ldns_dnssec_trust_tree *) parent;
+		tree->parent_status[tree->parent_count] = parent_status;
+		tree->parent_signature[tree->parent_count] = (ldns_rr *) signature;
+		tree->parent_count++;
+		return LDNS_STATUS_OK;
+	} else {
+		return LDNS_STATUS_ERR;
+	}
+}
+
+/* if rr is null, take the first from the rrset */
+ldns_dnssec_trust_tree *
+ldns_dnssec_derive_trust_tree_time(
+		ldns_dnssec_data_chain *data_chain, 
+		ldns_rr *rr, 
+		time_t check_time
+		)
+{
+	ldns_rr_list *cur_rrset;
+	ldns_rr_list *cur_sigs;
+	ldns_rr *cur_rr = NULL;
+	ldns_rr *cur_sig_rr;
+	size_t i, j;
+
+	ldns_dnssec_trust_tree *new_tree = ldns_dnssec_trust_tree_new();
+        if(!new_tree)
+                return NULL;
+	
+	if (data_chain && data_chain->rrset) {
+		cur_rrset = data_chain->rrset;
+	
+		cur_sigs = data_chain->signatures;
+
+		if (rr) {
+			cur_rr = rr;
+		}
+
+		if (!cur_rr && ldns_rr_list_rr_count(cur_rrset) > 0) {
+			cur_rr = ldns_rr_list_rr(cur_rrset, 0);
+		}
+
+		if (cur_rr) {
+			new_tree->rr = cur_rr;
+			new_tree->rrset = cur_rrset;
+			/* there are three possibilities:
+			   1 - 'normal' rrset, signed by a key
+			   2 - dnskey signed by other dnskey
+			   3 - dnskey proven by higher level DS
+			   (data denied by nsec is a special case that can
+			   occur in multiple places)
+				   
+			*/
+			if (cur_sigs) {
+				for (i = 0; i < ldns_rr_list_rr_count(cur_sigs); i++) {
+					/* find the appropriate key in the parent list */
+					cur_sig_rr = ldns_rr_list_rr(cur_sigs, i);
+
+					if (ldns_rr_get_type(cur_rr) == LDNS_RR_TYPE_NSEC) {
+						if (ldns_dname_compare(ldns_rr_owner(cur_sig_rr),
+										   ldns_rr_owner(cur_rr)))
+							{
+								/* find first that does match */
+
+								for (j = 0;
+								     j < ldns_rr_list_rr_count(cur_rrset) && 
+										ldns_dname_compare(ldns_rr_owner(cur_sig_rr),ldns_rr_owner(cur_rr)) != 0;
+								     j++) {
+									cur_rr = ldns_rr_list_rr(cur_rrset, j);
+									
+								}
+								if (ldns_dname_compare(ldns_rr_owner(cur_sig_rr), 
+												   ldns_rr_owner(cur_rr)))
+									{
+										break;
+									}
+							}
+							
+					}
+					/* option 1 */
+					if (data_chain->parent) {
+						ldns_dnssec_derive_trust_tree_normal_rrset_time(
+						    new_tree,
+						    data_chain,
+						    cur_sig_rr,
+						    check_time);
+					}
+
+					/* option 2 */
+					ldns_dnssec_derive_trust_tree_dnskey_rrset_time(
+					    new_tree,
+					    data_chain,
+					    cur_rr,
+					    cur_sig_rr,
+					    check_time);
+				}
+					
+				ldns_dnssec_derive_trust_tree_ds_rrset_time(
+						new_tree, data_chain, 
+						cur_rr, check_time);
+			} else {
+				/* no signatures? maybe it's nsec data */
+					
+				/* just add every rr from parent as new parent */
+				ldns_dnssec_derive_trust_tree_no_sig_time(
+					new_tree, data_chain, check_time);
+			}
+		}
+	}
+
+	return new_tree;
+}
+
+ldns_dnssec_trust_tree *
+ldns_dnssec_derive_trust_tree(ldns_dnssec_data_chain *data_chain, ldns_rr *rr)
+{
+	return ldns_dnssec_derive_trust_tree_time(data_chain, rr, ldns_time(NULL));
+}
+
+void
+ldns_dnssec_derive_trust_tree_normal_rrset_time(
+		ldns_dnssec_trust_tree *new_tree, 
+		ldns_dnssec_data_chain *data_chain, 
+		ldns_rr *cur_sig_rr,
+		time_t check_time)
+{
+	size_t i, j;
+	ldns_rr_list *cur_rrset = ldns_rr_list_clone(data_chain->rrset); 
+	ldns_dnssec_trust_tree *cur_parent_tree;
+	ldns_rr *cur_parent_rr;
+	uint16_t cur_keytag;
+	ldns_rr_list *tmp_rrset = NULL;
+	ldns_status cur_status;
+
+	cur_keytag = ldns_rdf2native_int16(ldns_rr_rrsig_keytag(cur_sig_rr));
+	
+	for (j = 0; j < ldns_rr_list_rr_count(data_chain->parent->rrset); j++) {
+		cur_parent_rr = ldns_rr_list_rr(data_chain->parent->rrset, j);
+		if (ldns_rr_get_type(cur_parent_rr) == LDNS_RR_TYPE_DNSKEY) {
+			if (ldns_calc_keytag(cur_parent_rr) == cur_keytag) {
+
+				/* TODO: check wildcard nsec too */
+				if (cur_rrset && ldns_rr_list_rr_count(cur_rrset) > 0) {
+					tmp_rrset = cur_rrset;
+					if (ldns_rr_get_type(ldns_rr_list_rr(cur_rrset, 0))
+					    == LDNS_RR_TYPE_NSEC ||
+					    ldns_rr_get_type(ldns_rr_list_rr(cur_rrset, 0))
+					    == LDNS_RR_TYPE_NSEC3) {
+						/* might contain different names! 
+						   sort and split */
+						ldns_rr_list_sort(cur_rrset);
+						if (tmp_rrset && tmp_rrset != cur_rrset) {
+							ldns_rr_list_deep_free(tmp_rrset);
+							tmp_rrset = NULL;
+						}
+						tmp_rrset = ldns_rr_list_pop_rrset(cur_rrset);
+						
+						/* with nsecs, this might be the wrong one */
+						while (tmp_rrset &&
+						       ldns_rr_list_rr_count(cur_rrset) > 0 &&
+						       ldns_dname_compare(
+								ldns_rr_owner(ldns_rr_list_rr(
+										        tmp_rrset, 0)),
+								ldns_rr_owner(cur_sig_rr)) != 0) {
+							ldns_rr_list_deep_free(tmp_rrset);
+							tmp_rrset =
+								ldns_rr_list_pop_rrset(cur_rrset);
+						}
+					}
+					cur_status = ldns_verify_rrsig_time(
+							tmp_rrset, 
+							cur_sig_rr, 
+							cur_parent_rr,
+							check_time);
+					/* avoid dupes */
+					for (i = 0; i < new_tree->parent_count; i++) {
+						if (cur_parent_rr == new_tree->parents[i]->rr) {
+							goto done;
+						}
+					}
+
+					cur_parent_tree =
+						ldns_dnssec_derive_trust_tree_time(
+								data_chain->parent,
+						                cur_parent_rr,
+								check_time);
+					(void)ldns_dnssec_trust_tree_add_parent(new_tree,
+					           cur_parent_tree,
+					           cur_sig_rr,
+					           cur_status);
+				}
+			}
+		}
+	}
+ done:
+	if (tmp_rrset && tmp_rrset != cur_rrset) {
+		ldns_rr_list_deep_free(tmp_rrset);
+	}
+	ldns_rr_list_deep_free(cur_rrset);
+}
+
+void
+ldns_dnssec_derive_trust_tree_normal_rrset(ldns_dnssec_trust_tree *new_tree,
+                                           ldns_dnssec_data_chain *data_chain,
+                                           ldns_rr *cur_sig_rr)
+{
+	ldns_dnssec_derive_trust_tree_normal_rrset_time(
+			new_tree, data_chain, cur_sig_rr, ldns_time(NULL));
+}
+
+void
+ldns_dnssec_derive_trust_tree_dnskey_rrset_time(
+		ldns_dnssec_trust_tree *new_tree, 
+		ldns_dnssec_data_chain *data_chain, 
+		ldns_rr *cur_rr, 
+		ldns_rr *cur_sig_rr,
+		time_t check_time)
+{
+	size_t j;
+	ldns_rr_list *cur_rrset = data_chain->rrset;
+	ldns_dnssec_trust_tree *cur_parent_tree;
+	ldns_rr *cur_parent_rr;
+	uint16_t cur_keytag;
+	ldns_status cur_status;
+
+	cur_keytag = ldns_rdf2native_int16(ldns_rr_rrsig_keytag(cur_sig_rr));
+
+	for (j = 0; j < ldns_rr_list_rr_count(cur_rrset); j++) {
+		cur_parent_rr = ldns_rr_list_rr(cur_rrset, j);
+		if (cur_parent_rr != cur_rr &&
+		    ldns_rr_get_type(cur_parent_rr) == LDNS_RR_TYPE_DNSKEY) {
+			if (ldns_calc_keytag(cur_parent_rr) == cur_keytag
+			    ) {
+				cur_parent_tree = ldns_dnssec_trust_tree_new();
+				cur_parent_tree->rr = cur_parent_rr;
+				cur_parent_tree->rrset = cur_rrset;
+				cur_status = ldns_verify_rrsig_time(
+						cur_rrset, cur_sig_rr, 
+						cur_parent_rr, check_time);
+				(void) ldns_dnssec_trust_tree_add_parent(new_tree,
+				            cur_parent_tree, cur_sig_rr, cur_status);
+			}
+		}
+	}
+}
+
+void
+ldns_dnssec_derive_trust_tree_dnskey_rrset(ldns_dnssec_trust_tree *new_tree,
+                                           ldns_dnssec_data_chain *data_chain,
+                                           ldns_rr *cur_rr,
+                                           ldns_rr *cur_sig_rr)
+{
+	ldns_dnssec_derive_trust_tree_dnskey_rrset_time(
+			new_tree, data_chain, cur_rr, cur_sig_rr, ldns_time(NULL));
+}
+
+void
+ldns_dnssec_derive_trust_tree_ds_rrset_time(
+		ldns_dnssec_trust_tree *new_tree,
+		ldns_dnssec_data_chain *data_chain, 
+		ldns_rr *cur_rr,
+		time_t check_time)
+{
+	size_t j, h;
+	ldns_rr_list *cur_rrset = data_chain->rrset;
+	ldns_dnssec_trust_tree *cur_parent_tree;
+	ldns_rr *cur_parent_rr;
+
+	/* try the parent to see whether there are DSs there */
+	if (ldns_rr_get_type(cur_rr) == LDNS_RR_TYPE_DNSKEY &&
+	    data_chain->parent &&
+	    data_chain->parent->rrset
+	    ) {
+		for (j = 0;
+			j < ldns_rr_list_rr_count(data_chain->parent->rrset);
+			j++) {
+			cur_parent_rr = ldns_rr_list_rr(data_chain->parent->rrset, j);
+			if (ldns_rr_get_type(cur_parent_rr) == LDNS_RR_TYPE_DS) {
+				for (h = 0; h < ldns_rr_list_rr_count(cur_rrset); h++) {
+					cur_rr = ldns_rr_list_rr(cur_rrset, h);
+					if (ldns_rr_compare_ds(cur_rr, cur_parent_rr)) {
+						cur_parent_tree =
+							ldns_dnssec_derive_trust_tree_time(
+							    data_chain->parent, 
+							    cur_parent_rr,
+							    check_time);
+						(void) ldns_dnssec_trust_tree_add_parent(
+						            new_tree,
+						            cur_parent_tree,
+						            NULL,
+						            LDNS_STATUS_OK);
+					} else {
+						/*ldns_rr_print(stdout, cur_parent_rr);*/
+					}
+				}
+			}
+		}
+	}
+}
+
+void
+ldns_dnssec_derive_trust_tree_ds_rrset(ldns_dnssec_trust_tree *new_tree,
+                                       ldns_dnssec_data_chain *data_chain,
+                                       ldns_rr *cur_rr)
+{
+	ldns_dnssec_derive_trust_tree_ds_rrset_time(
+			new_tree, data_chain, cur_rr, ldns_time(NULL));
+}
+
+void
+ldns_dnssec_derive_trust_tree_no_sig_time(
+		ldns_dnssec_trust_tree *new_tree, 
+		ldns_dnssec_data_chain *data_chain,
+		time_t check_time)
+{
+	size_t i;
+	ldns_rr_list *cur_rrset;
+	ldns_rr *cur_parent_rr;
+	ldns_dnssec_trust_tree *cur_parent_tree;
+	ldns_status result;
+	
+	if (data_chain->parent && data_chain->parent->rrset) {
+		cur_rrset = data_chain->parent->rrset;
+		/* nsec? */
+		if (cur_rrset && ldns_rr_list_rr_count(cur_rrset) > 0) {
+			if (ldns_rr_get_type(ldns_rr_list_rr(cur_rrset, 0)) ==
+			    LDNS_RR_TYPE_NSEC3) {
+				result = ldns_dnssec_verify_denial_nsec3(
+					        new_tree->rr,
+						   cur_rrset,
+						   data_chain->parent->signatures,
+						   data_chain->packet_rcode,
+						   data_chain->packet_qtype,
+						   data_chain->packet_nodata);
+			} else if (ldns_rr_get_type(ldns_rr_list_rr(cur_rrset, 0)) ==
+					 LDNS_RR_TYPE_NSEC) {
+				result = ldns_dnssec_verify_denial(
+					        new_tree->rr,
+						   cur_rrset,
+						   data_chain->parent->signatures);
+			} else {
+				/* unsigned zone, unsigned parent */
+				result = LDNS_STATUS_OK;
+			}
+		} else {
+			result = LDNS_STATUS_DNSSEC_NSEC_RR_NOT_COVERED;
+		}
+		for (i = 0; i < ldns_rr_list_rr_count(cur_rrset); i++) {
+			cur_parent_rr = ldns_rr_list_rr(cur_rrset, i);
+			cur_parent_tree = 
+				ldns_dnssec_derive_trust_tree_time(
+						data_chain->parent, 
+						cur_parent_rr,
+						check_time);
+			(void) ldns_dnssec_trust_tree_add_parent(new_tree,
+			            cur_parent_tree, NULL, result);
+		}
+	}
+}
+
+void
+ldns_dnssec_derive_trust_tree_no_sig(ldns_dnssec_trust_tree *new_tree,
+                                     ldns_dnssec_data_chain *data_chain)
+{
+	ldns_dnssec_derive_trust_tree_no_sig_time(
+			new_tree, data_chain, ldns_time(NULL));
+}
+
+/*
+ * returns OK if there is a path from tree to key with only OK
+ * the (first) error in between otherwise
+ * or NOT_FOUND if the key wasn't present at all
+ */
+ldns_status
+ldns_dnssec_trust_tree_contains_keys(ldns_dnssec_trust_tree *tree,
+							  ldns_rr_list *trusted_keys)
+{
+	size_t i;
+	ldns_status result = LDNS_STATUS_CRYPTO_NO_DNSKEY;
+	bool equal;
+	ldns_status parent_result;
+	
+	if (tree && trusted_keys && ldns_rr_list_rr_count(trusted_keys) > 0)
+		{ if (tree->rr) {
+				for (i = 0; i < ldns_rr_list_rr_count(trusted_keys); i++) {
+					equal = ldns_rr_compare_ds(
+							  tree->rr,
+							  ldns_rr_list_rr(trusted_keys, i));
+					if (equal) {
+						result = LDNS_STATUS_OK;
+						return result;
+					}
+				}
+			}
+			for (i = 0; i < tree->parent_count; i++) {
+				parent_result =
+					ldns_dnssec_trust_tree_contains_keys(tree->parents[i],
+												  trusted_keys);
+				if (parent_result != LDNS_STATUS_CRYPTO_NO_DNSKEY) {
+					if (tree->parent_status[i] != LDNS_STATUS_OK) {
+						result = tree->parent_status[i];
+					} else {
+						if (ldns_rr_get_type(tree->rr)
+						    == LDNS_RR_TYPE_NSEC &&
+						    parent_result == LDNS_STATUS_OK
+						    ) {
+							result =
+								LDNS_STATUS_DNSSEC_EXISTENCE_DENIED;
+						} else {
+							result = parent_result;
+						}
+					}
+				}
+			}
+		} else {
+		result = LDNS_STATUS_ERR;
+	}
+	
+	return result;
+}
+
+ldns_status
+ldns_verify_time(
+		ldns_rr_list *rrset,
+		ldns_rr_list *rrsig, 
+		const ldns_rr_list *keys, 
+		time_t check_time,
+		ldns_rr_list *good_keys
+		)
+{
+	uint16_t i;
+	ldns_status verify_result = LDNS_STATUS_ERR;
+
+	if (!rrset || !rrsig || !keys) {
+		return LDNS_STATUS_ERR;
+	}
+
+	if (ldns_rr_list_rr_count(rrset) < 1) {
+		return LDNS_STATUS_ERR;
+	}
+
+	if (ldns_rr_list_rr_count(rrsig) < 1) {
+		return LDNS_STATUS_CRYPTO_NO_RRSIG;
+	}
+	
+	if (ldns_rr_list_rr_count(keys) < 1) {
+		verify_result = LDNS_STATUS_CRYPTO_NO_TRUSTED_DNSKEY;
+	} else {
+		for (i = 0; i < ldns_rr_list_rr_count(rrsig); i++) {
+			ldns_status s = ldns_verify_rrsig_keylist_time(
+					rrset, ldns_rr_list_rr(rrsig, i), 
+					keys, check_time, good_keys);
+			/* try a little to get more descriptive error */
+			if(s == LDNS_STATUS_OK) {
+				verify_result = LDNS_STATUS_OK;
+			} else if(verify_result == LDNS_STATUS_ERR)
+				verify_result = s;
+			else if(s !=  LDNS_STATUS_ERR && verify_result ==
+				LDNS_STATUS_CRYPTO_NO_MATCHING_KEYTAG_DNSKEY)
+				verify_result = s;
+		}
+	}
+	return verify_result;
+}
+
+ldns_status
+ldns_verify(ldns_rr_list *rrset, ldns_rr_list *rrsig, const ldns_rr_list *keys, 
+		  ldns_rr_list *good_keys)
+{
+	return ldns_verify_time(rrset, rrsig, keys, ldns_time(NULL), good_keys);
+}
+
+ldns_status
+ldns_verify_notime(ldns_rr_list *rrset, ldns_rr_list *rrsig,
+	const ldns_rr_list *keys, ldns_rr_list *good_keys)
+{
+	uint16_t i;
+	ldns_status verify_result = LDNS_STATUS_ERR;
+
+	if (!rrset || !rrsig || !keys) {
+		return LDNS_STATUS_ERR;
+	}
+
+	if (ldns_rr_list_rr_count(rrset) < 1) {
+		return LDNS_STATUS_ERR;
+	}
+
+	if (ldns_rr_list_rr_count(rrsig) < 1) {
+		return LDNS_STATUS_CRYPTO_NO_RRSIG;
+	}
+
+	if (ldns_rr_list_rr_count(keys) < 1) {
+		verify_result = LDNS_STATUS_CRYPTO_NO_TRUSTED_DNSKEY;
+	} else {
+		for (i = 0; i < ldns_rr_list_rr_count(rrsig); i++) {
+			ldns_status s = ldns_verify_rrsig_keylist_notime(rrset,
+				ldns_rr_list_rr(rrsig, i), keys, good_keys);
+
+			/* try a little to get more descriptive error */
+			if (s == LDNS_STATUS_OK) {
+				verify_result = LDNS_STATUS_OK;
+			} else if (verify_result == LDNS_STATUS_ERR) {
+				verify_result = s;
+			} else if (s !=  LDNS_STATUS_ERR && verify_result ==
+				LDNS_STATUS_CRYPTO_NO_MATCHING_KEYTAG_DNSKEY) {
+				verify_result = s;
+			}
+		}
+	}
+	return verify_result;
+}
+
+ldns_rr_list *
+ldns_fetch_valid_domain_keys_time(const ldns_resolver *res,
+                             const ldns_rdf *domain,
+                             const ldns_rr_list *keys,
+			     time_t check_time,
+                             ldns_status *status)
+{
+	ldns_rr_list * trusted_keys = NULL;
+	ldns_rr_list * ds_keys = NULL;
+	ldns_rdf * prev_parent_domain;
+	ldns_rdf *      parent_domain;
+	ldns_rr_list * parent_keys = NULL;
+
+	if (res && domain && keys) {
+
+		if ((trusted_keys = ldns_validate_domain_dnskey_time(res,
+                                         domain, keys, check_time))) {
+			*status = LDNS_STATUS_OK;
+		} else {
+			/* No trusted keys in this domain, we'll have to find some in the parent domain */
+			*status = LDNS_STATUS_CRYPTO_NO_TRUSTED_DNSKEY;
+
+			parent_domain = ldns_dname_left_chop(domain);
+			while (ldns_rdf_size(parent_domain) > 0) {
+				/* Fail if we are at the root */
+	
+				if ((parent_keys = 
+					ldns_fetch_valid_domain_keys_time(res,
+					     parent_domain,
+					     keys,
+					     check_time,
+					     status))) {
+					/* Check DS records */
+					if ((ds_keys =
+						ldns_validate_domain_ds_time(res,
+						     domain,
+						     parent_keys,
+						     check_time))) {
+						trusted_keys =
+						ldns_fetch_valid_domain_keys_time(
+								res, 
+								domain, 
+								ds_keys, 
+								check_time,
+								status);
+						ldns_rr_list_deep_free(ds_keys);
+					} else {
+						/* No valid DS at the parent -- fail */
+						*status = LDNS_STATUS_CRYPTO_NO_TRUSTED_DS ;
+					}
+					ldns_rr_list_deep_free(parent_keys);
+					break;
+				} else {
+					parent_domain = ldns_dname_left_chop((
+						prev_parent_domain 
+							= parent_domain
+						));
+					ldns_rdf_deep_free(prev_parent_domain);
+				}
+			}
+			ldns_rdf_deep_free(parent_domain);
+		}
+	}
+	return trusted_keys;
+}
+
+ldns_rr_list *
+ldns_fetch_valid_domain_keys(const ldns_resolver *res,
+                             const ldns_rdf *domain,
+                             const ldns_rr_list *keys,
+                             ldns_status *status)
+{
+	return ldns_fetch_valid_domain_keys_time(
+			res, domain, keys, ldns_time(NULL), status);
+}
+
+ldns_rr_list *
+ldns_validate_domain_dnskey_time(
+		const ldns_resolver * res,
+		const ldns_rdf * domain,
+		const ldns_rr_list * keys,
+		time_t check_time
+		)
+{
+	ldns_pkt * keypkt;
+	ldns_rr * cur_key;
+	uint16_t key_i; uint16_t key_j; uint16_t key_k;
+	uint16_t sig_i; ldns_rr * cur_sig;
+
+	ldns_rr_list * domain_keys = NULL;
+	ldns_rr_list * domain_sigs = NULL;
+	ldns_rr_list * trusted_keys = NULL;
+
+	/* Fetch keys for the domain */
+	keypkt = ldns_resolver_query(res, domain,
+		LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN, LDNS_RD);
+	if (keypkt) {
+		domain_keys = ldns_pkt_rr_list_by_type(keypkt,
+									    LDNS_RR_TYPE_DNSKEY,
+									    LDNS_SECTION_ANSWER);
+		domain_sigs = ldns_pkt_rr_list_by_type(keypkt,
+									    LDNS_RR_TYPE_RRSIG,
+									    LDNS_SECTION_ANSWER);
+
+		/* Try to validate the record using our keys */
+		for (key_i=0; key_i< ldns_rr_list_rr_count(domain_keys); key_i++) {
+      
+			cur_key = ldns_rr_list_rr(domain_keys, key_i);
+			for (key_j=0; key_j<ldns_rr_list_rr_count(keys); key_j++) {
+				if (ldns_rr_compare_ds(ldns_rr_list_rr(keys, key_j),
+								   cur_key)) {
+          
+					/* Current key is trusted -- validate */
+					trusted_keys = ldns_rr_list_new();
+          
+					for (sig_i=0;
+						sig_i<ldns_rr_list_rr_count(domain_sigs);
+						sig_i++) {
+						cur_sig = ldns_rr_list_rr(domain_sigs, sig_i);
+						/* Avoid non-matching sigs */
+						if (ldns_rdf2native_int16(
+							   ldns_rr_rrsig_keytag(cur_sig))
+						    == ldns_calc_keytag(cur_key)) {
+							if (ldns_verify_rrsig_time(
+									domain_keys,
+									cur_sig,
+									cur_key,
+									check_time)
+							    == LDNS_STATUS_OK) {
+                
+								/* Push the whole rrset 
+								   -- we can't do much more */
+								for (key_k=0;
+									key_k<ldns_rr_list_rr_count(
+											domain_keys);
+									key_k++) {
+									ldns_rr_list_push_rr(
+									    trusted_keys,
+									    ldns_rr_clone(
+										   ldns_rr_list_rr(
+											  domain_keys,
+											  key_k)));
+								}
+                
+								ldns_rr_list_deep_free(domain_keys);
+								ldns_rr_list_deep_free(domain_sigs);
+								ldns_pkt_free(keypkt);
+								return trusted_keys;
+							}
+						}
+					}
+	  
+					/* Only push our trusted key */
+					ldns_rr_list_push_rr(trusted_keys,
+									 ldns_rr_clone(cur_key));
+				}
+			}
+		}
+
+		ldns_rr_list_deep_free(domain_keys);
+		ldns_rr_list_deep_free(domain_sigs);
+		ldns_pkt_free(keypkt);
+
+	} else {
+		/* LDNS_STATUS_CRYPTO_NO_DNSKEY */
+	}
+    
+	return trusted_keys;
+}
+
+ldns_rr_list *
+ldns_validate_domain_dnskey(const ldns_resolver * res,
+					   const ldns_rdf * domain,
+					   const ldns_rr_list * keys)
+{
+	return ldns_validate_domain_dnskey_time(
+			res, domain, keys, ldns_time(NULL));
+}
+
+ldns_rr_list *
+ldns_validate_domain_ds_time(
+		const ldns_resolver *res, 
+		const ldns_rdf * domain,
+		const ldns_rr_list * keys,
+		time_t check_time)
+{
+	ldns_pkt * dspkt;
+	uint16_t key_i;
+	ldns_rr_list * rrset = NULL;
+	ldns_rr_list * sigs = NULL;
+	ldns_rr_list * trusted_keys = NULL;
+
+	/* Fetch DS for the domain */
+	dspkt = ldns_resolver_query(res, domain,
+		LDNS_RR_TYPE_DS, LDNS_RR_CLASS_IN, LDNS_RD);
+	if (dspkt) {
+		rrset = ldns_pkt_rr_list_by_type(dspkt,
+								   LDNS_RR_TYPE_DS,
+								   LDNS_SECTION_ANSWER);
+		sigs = ldns_pkt_rr_list_by_type(dspkt,
+								  LDNS_RR_TYPE_RRSIG,
+								  LDNS_SECTION_ANSWER);
+
+		/* Validate sigs */
+		if (ldns_verify_time(rrset, sigs, keys, check_time, NULL)
+			       	== LDNS_STATUS_OK) {
+			trusted_keys = ldns_rr_list_new();
+			for (key_i=0; key_i<ldns_rr_list_rr_count(rrset); key_i++) {
+				ldns_rr_list_push_rr(trusted_keys,
+								 ldns_rr_clone(ldns_rr_list_rr(rrset,
+														 key_i)
+											)
+								 );
+			}
+		}
+
+		ldns_rr_list_deep_free(rrset);
+		ldns_rr_list_deep_free(sigs);
+		ldns_pkt_free(dspkt);
+
+	} else {
+		/* LDNS_STATUS_CRYPTO_NO_DS */
+	}
+
+	return trusted_keys;
+}
+
+ldns_rr_list *
+ldns_validate_domain_ds(const ldns_resolver *res,
+				    const ldns_rdf * domain,
+				    const ldns_rr_list * keys)
+{
+	return ldns_validate_domain_ds_time(res, domain, keys, ldns_time(NULL));
+}
+
+ldns_status
+ldns_verify_trusted_time(
+		ldns_resolver *res, 
+		ldns_rr_list *rrset, 
+		ldns_rr_list * rrsigs, 
+		time_t check_time,
+		ldns_rr_list * validating_keys
+		)
+{
+	uint16_t sig_i; uint16_t key_i;
+	ldns_rr * cur_sig; ldns_rr * cur_key;
+	ldns_rr_list * trusted_keys = NULL;
+	ldns_status result = LDNS_STATUS_ERR;
+
+	if (!res || !rrset || !rrsigs) {
+		return LDNS_STATUS_ERR;
+	}
+
+	if (ldns_rr_list_rr_count(rrset) < 1) {
+		return LDNS_STATUS_ERR;
+	}
+
+	if (ldns_rr_list_rr_count(rrsigs) < 1) {
+		return LDNS_STATUS_CRYPTO_NO_RRSIG;
+	}
+  
+	/* Look at each sig */
+	for (sig_i=0; sig_i < ldns_rr_list_rr_count(rrsigs); sig_i++) {
+
+		cur_sig = ldns_rr_list_rr(rrsigs, sig_i);
+		/* Get a valid signer key and validate the sig */
+		if ((trusted_keys = ldns_fetch_valid_domain_keys_time(
+					res, 
+					ldns_rr_rrsig_signame(cur_sig), 
+					ldns_resolver_dnssec_anchors(res), 
+					check_time,
+					&result))) {
+
+			for (key_i = 0;
+				key_i < ldns_rr_list_rr_count(trusted_keys);
+				key_i++) {
+				cur_key = ldns_rr_list_rr(trusted_keys, key_i);
+
+				if ((result = ldns_verify_rrsig_time(rrset,
+								cur_sig, 
+								cur_key,
+								check_time))
+				    == LDNS_STATUS_OK) {
+					if (validating_keys) {
+						ldns_rr_list_push_rr(validating_keys,
+										 ldns_rr_clone(cur_key));
+					}
+					ldns_rr_list_deep_free(trusted_keys);
+					return LDNS_STATUS_OK;
+				} 
+			}
+		}
+	}
+
+	ldns_rr_list_deep_free(trusted_keys);
+	return result;
+}
+
+ldns_status
+ldns_verify_trusted(
+		ldns_resolver *res,
+		ldns_rr_list *rrset, 
+		ldns_rr_list * rrsigs, 
+		ldns_rr_list * validating_keys)
+{
+	return ldns_verify_trusted_time(
+			res, rrset, rrsigs, ldns_time(NULL), validating_keys);
+}
+
+
+ldns_status
+ldns_dnssec_verify_denial(ldns_rr *rr,
+                          ldns_rr_list *nsecs,
+                          ldns_rr_list *rrsigs)
+{
+	ldns_rdf *rr_name;
+	ldns_rdf *wildcard_name;
+	ldns_rdf *chopped_dname;
+	ldns_rr *cur_nsec;
+	size_t i;
+	ldns_status result;
+	/* needed for wildcard check on exact match */
+	ldns_rr *rrsig;
+	bool name_covered = false;
+	bool type_covered = false;
+	bool wildcard_covered = false;
+	bool wildcard_type_covered = false;
+
+	wildcard_name = ldns_dname_new_frm_str("*");
+	rr_name = ldns_rr_owner(rr);
+	chopped_dname = ldns_dname_left_chop(rr_name);
+	result = ldns_dname_cat(wildcard_name, chopped_dname);
+	if (result != LDNS_STATUS_OK) {
+		return result;
+	}
+	
+	ldns_rdf_deep_free(chopped_dname);
+	
+	for  (i = 0; i < ldns_rr_list_rr_count(nsecs); i++) {
+		cur_nsec = ldns_rr_list_rr(nsecs, i);
+		if (ldns_dname_compare(rr_name, ldns_rr_owner(cur_nsec)) == 0) {
+			/* see section 5.4 of RFC4035, if the label count of the NSEC's
+			   RRSIG is equal, then it is proven that wildcard expansion 
+			   could not have been used to match the request */
+			rrsig = ldns_dnssec_get_rrsig_for_name_and_type(
+					  ldns_rr_owner(cur_nsec),
+					  ldns_rr_get_type(cur_nsec),
+					  rrsigs);
+			if (rrsig && ldns_rdf2native_int8(ldns_rr_rrsig_labels(rrsig))
+			    == ldns_dname_label_count(rr_name)) {
+				wildcard_covered = true;
+			}
+			
+			if (ldns_nsec_bitmap_covers_type(ldns_nsec_get_bitmap(cur_nsec),
+									   ldns_rr_get_type(rr))) {
+				type_covered = true;
+			}
+		}
+		if (ldns_nsec_covers_name(cur_nsec, rr_name)) {
+			name_covered = true;
+		}
+		
+		if (ldns_dname_compare(wildcard_name,
+						   ldns_rr_owner(cur_nsec)) == 0) {
+			if (ldns_nsec_bitmap_covers_type(ldns_nsec_get_bitmap(cur_nsec),
+									   ldns_rr_get_type(rr))) {
+				wildcard_type_covered = true;
+			}
+		}
+		
+		if (ldns_nsec_covers_name(cur_nsec, wildcard_name)) {
+			wildcard_covered = true;
+		}
+		
+	}
+	
+	ldns_rdf_deep_free(wildcard_name);
+	
+	if (type_covered || !name_covered) {
+		return LDNS_STATUS_DNSSEC_NSEC_RR_NOT_COVERED;
+	}
+	
+	if (wildcard_type_covered || !wildcard_covered) {
+		return LDNS_STATUS_DNSSEC_NSEC_WILDCARD_NOT_COVERED;
+	}
+
+	return LDNS_STATUS_OK;
+}
+
+#ifdef HAVE_SSL
+ldns_status
+ldns_dnssec_verify_denial_nsec3_match(ldns_rr *rr,
+						  ldns_rr_list *nsecs,
+						  ldns_rr_list *rrsigs,
+						  ldns_pkt_rcode packet_rcode,
+						  ldns_rr_type packet_qtype,
+						  bool packet_nodata,
+						  ldns_rr **match)
+{
+	ldns_rdf *closest_encloser;
+	ldns_rdf *wildcard;
+	ldns_rdf *hashed_wildcard_name;
+	bool wildcard_covered = false;
+	ldns_rdf *zone_name;
+	ldns_rdf *hashed_name;
+	size_t i;
+	ldns_status result = LDNS_STATUS_DNSSEC_NSEC_RR_NOT_COVERED;
+
+	rrsigs = rrsigs;
+
+	if (match) {
+		*match = NULL;
+	}
+
+	zone_name = ldns_dname_left_chop(ldns_rr_owner(ldns_rr_list_rr(nsecs,0)));
+
+	/* section 8.4 */
+	if (packet_rcode == LDNS_RCODE_NXDOMAIN) {
+		closest_encloser = ldns_dnssec_nsec3_closest_encloser(
+						   ldns_rr_owner(rr),
+						   ldns_rr_get_type(rr),
+						   nsecs);
+                if(!closest_encloser) {
+                        result = LDNS_STATUS_NSEC3_ERR;
+                        goto done;
+                }
+
+		wildcard = ldns_dname_new_frm_str("*");
+		(void) ldns_dname_cat(wildcard, closest_encloser);
+
+		for (i = 0; i < ldns_rr_list_rr_count(nsecs); i++) {
+			hashed_wildcard_name =
+				ldns_nsec3_hash_name_frm_nsec3(ldns_rr_list_rr(nsecs, 0),
+										 wildcard
+										 );
+			(void) ldns_dname_cat(hashed_wildcard_name, zone_name);
+
+			if (ldns_nsec_covers_name(ldns_rr_list_rr(nsecs, i),
+								 hashed_wildcard_name)) {
+				wildcard_covered = true;
+				if (match) {
+					*match = ldns_rr_list_rr(nsecs, i);
+				}
+			}
+			ldns_rdf_deep_free(hashed_wildcard_name);
+		}
+
+		ldns_rdf_deep_free(closest_encloser);
+		ldns_rdf_deep_free(wildcard);
+
+		if (!wildcard_covered) {
+			result = LDNS_STATUS_DNSSEC_NSEC_WILDCARD_NOT_COVERED;
+		} else if (closest_encloser && wildcard_covered) {
+			result = LDNS_STATUS_OK;
+		} else {
+			result = LDNS_STATUS_DNSSEC_NSEC_RR_NOT_COVERED;
+		}
+	} else if (packet_nodata && packet_qtype != LDNS_RR_TYPE_DS) {
+		/* section 8.5 */
+		hashed_name = ldns_nsec3_hash_name_frm_nsec3(
+		                   ldns_rr_list_rr(nsecs, 0),
+		                   ldns_rr_owner(rr));
+		(void) ldns_dname_cat(hashed_name, zone_name);
+		for (i = 0; i < ldns_rr_list_rr_count(nsecs); i++) {
+			if (ldns_dname_compare(hashed_name,
+			         ldns_rr_owner(ldns_rr_list_rr(nsecs, i)))
+			    == 0) {
+				if (!ldns_nsec_bitmap_covers_type(
+					    ldns_nsec3_bitmap(ldns_rr_list_rr(nsecs, i)),
+					    packet_qtype)
+				    &&
+				    !ldns_nsec_bitmap_covers_type(
+					    ldns_nsec3_bitmap(ldns_rr_list_rr(nsecs, i)),
+					    LDNS_RR_TYPE_CNAME)) {
+					result = LDNS_STATUS_OK;
+					if (match) {
+						*match = ldns_rr_list_rr(nsecs, i);
+					}
+					goto done;
+				}
+			}
+		}
+		result = LDNS_STATUS_DNSSEC_NSEC_RR_NOT_COVERED;
+		/* wildcard no data? section 8.7 */
+		closest_encloser = ldns_dnssec_nsec3_closest_encloser(
+				   ldns_rr_owner(rr),
+				   ldns_rr_get_type(rr),
+				   nsecs);
+		if(!closest_encloser) {
+			result = LDNS_STATUS_NSEC3_ERR;
+			goto done;
+		}
+		wildcard = ldns_dname_new_frm_str("*");
+		(void) ldns_dname_cat(wildcard, closest_encloser);
+		for (i = 0; i < ldns_rr_list_rr_count(nsecs); i++) {
+			hashed_wildcard_name =
+				ldns_nsec3_hash_name_frm_nsec3(ldns_rr_list_rr(nsecs, 0),
+					 wildcard);
+			(void) ldns_dname_cat(hashed_wildcard_name, zone_name);
+
+			if (ldns_dname_compare(hashed_wildcard_name,
+			         ldns_rr_owner(ldns_rr_list_rr(nsecs, i)))
+			    == 0) {
+				if (!ldns_nsec_bitmap_covers_type(
+					    ldns_nsec3_bitmap(ldns_rr_list_rr(nsecs, i)),
+					    packet_qtype)
+				    &&
+				    !ldns_nsec_bitmap_covers_type(
+					    ldns_nsec3_bitmap(ldns_rr_list_rr(nsecs, i)),
+					    LDNS_RR_TYPE_CNAME)) {
+					result = LDNS_STATUS_OK;
+					if (match) {
+						*match = ldns_rr_list_rr(nsecs, i);
+					}
+				}
+			}
+			ldns_rdf_deep_free(hashed_wildcard_name);
+			if (result == LDNS_STATUS_OK) {
+				break;
+			}
+		}
+		ldns_rdf_deep_free(closest_encloser);
+		ldns_rdf_deep_free(wildcard);
+	} else if (packet_nodata && packet_qtype == LDNS_RR_TYPE_DS) {
+		/* section 8.6 */
+		/* note: up to XXX this is the same as for 8.5 */
+		hashed_name = ldns_nsec3_hash_name_frm_nsec3(ldns_rr_list_rr(nsecs,
+														 0),
+											ldns_rr_owner(rr)
+											);
+		(void) ldns_dname_cat(hashed_name, zone_name);
+		for (i = 0; i < ldns_rr_list_rr_count(nsecs); i++) {
+			if (ldns_dname_compare(hashed_name,
+							   ldns_rr_owner(ldns_rr_list_rr(nsecs,
+													   i)))
+			    == 0) {
+				if (!ldns_nsec_bitmap_covers_type(
+					    ldns_nsec3_bitmap(ldns_rr_list_rr(nsecs, i)),
+					    LDNS_RR_TYPE_DS)
+				    && 
+				    !ldns_nsec_bitmap_covers_type(
+					    ldns_nsec3_bitmap(ldns_rr_list_rr(nsecs, i)),
+					    LDNS_RR_TYPE_CNAME)) {
+					result = LDNS_STATUS_OK;
+					if (match) {
+						*match = ldns_rr_list_rr(nsecs, i);
+					}
+					goto done;
+				}
+			}
+		}
+
+		/* XXX see note above */
+		result = LDNS_STATUS_DNSSEC_NSEC_RR_NOT_COVERED;
+	}
+
+ done:
+	ldns_rdf_deep_free(zone_name);
+	return result;
+}
+
+ldns_status
+ldns_dnssec_verify_denial_nsec3(ldns_rr *rr,
+						  ldns_rr_list *nsecs,
+						  ldns_rr_list *rrsigs,
+						  ldns_pkt_rcode packet_rcode,
+						  ldns_rr_type packet_qtype,
+						  bool packet_nodata)
+{
+	return ldns_dnssec_verify_denial_nsec3_match(
+				rr, nsecs, rrsigs, packet_rcode,
+				packet_qtype, packet_nodata, NULL
+	       );
+}
+
+
+#endif /* HAVE_SSL */
+
+#ifdef USE_GOST
+EVP_PKEY*
+ldns_gost2pkey_raw(unsigned char* key, size_t keylen)
+{
+	/* prefix header for X509 encoding */
+	uint8_t asn[37] = { 0x30, 0x63, 0x30, 0x1c, 0x06, 0x06, 0x2a, 0x85, 
+		0x03, 0x02, 0x02, 0x13, 0x30, 0x12, 0x06, 0x07, 0x2a, 0x85, 
+		0x03, 0x02, 0x02, 0x23, 0x01, 0x06, 0x07, 0x2a, 0x85, 0x03, 
+		0x02, 0x02, 0x1e, 0x01, 0x03, 0x43, 0x00, 0x04, 0x40};
+	unsigned char encoded[37+64];
+	const unsigned char* pp;
+	if(keylen != 64) {
+		/* key wrong size */
+		return NULL;
+	}
+
+	/* create evp_key */
+	memmove(encoded, asn, 37);
+	memmove(encoded+37, key, 64);
+	pp = (unsigned char*)&encoded[0];
+
+	return d2i_PUBKEY(NULL, &pp, (int)sizeof(encoded));
+}
+
+static ldns_status
+ldns_verify_rrsig_gost_raw(unsigned char* sig, size_t siglen, 
+	ldns_buffer* rrset, unsigned char* key, size_t keylen)
+{
+	EVP_PKEY *evp_key;
+	ldns_status result;
+
+	(void) ldns_key_EVP_load_gost_id();
+	evp_key = ldns_gost2pkey_raw(key, keylen);
+	if(!evp_key) {
+		/* could not convert key */
+		return LDNS_STATUS_CRYPTO_BOGUS;
+	}
+
+	/* verify signature */
+	result = ldns_verify_rrsig_evp_raw(sig, siglen, rrset, 
+		evp_key, EVP_get_digestbyname("md_gost94"));
+	EVP_PKEY_free(evp_key);
+
+	return result;
+}
+#endif
+
+#ifdef USE_ECDSA
+EVP_PKEY*
+ldns_ecdsa2pkey_raw(unsigned char* key, size_t keylen, uint8_t algo)
+{
+	unsigned char buf[256+2]; /* sufficient for 2*384/8+1 */
+        const unsigned char* pp = buf;
+        EVP_PKEY *evp_key;
+        EC_KEY *ec;
+	/* check length, which uncompressed must be 2 bignums */
+        if(algo == LDNS_ECDSAP256SHA256) {
+		if(keylen != 2*256/8) return NULL;
+                ec = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
+        } else if(algo == LDNS_ECDSAP384SHA384) {
+		if(keylen != 2*384/8) return NULL;
+                ec = EC_KEY_new_by_curve_name(NID_secp384r1);
+        } else    ec = NULL;
+        if(!ec) return NULL;
+	if(keylen+1 > sizeof(buf))
+		return NULL; /* sanity check */
+	/* prepend the 0x02 (from docs) (or actually 0x04 from implementation
+	 * of openssl) for uncompressed data */
+	buf[0] = POINT_CONVERSION_UNCOMPRESSED;
+	memmove(buf+1, key, keylen);
+        if(!o2i_ECPublicKey(&ec, &pp, (int)keylen+1)) {
+                EC_KEY_free(ec);
+                return NULL;
+        }
+        evp_key = EVP_PKEY_new();
+        if(!evp_key) {
+                EC_KEY_free(ec);
+                return NULL;
+        }
+        if (!EVP_PKEY_assign_EC_KEY(evp_key, ec)) {
+		EVP_PKEY_free(evp_key);
+		EC_KEY_free(ec);
+		return NULL;
+	}
+        return evp_key;
+}
+
+static ldns_status
+ldns_verify_rrsig_ecdsa_raw(unsigned char* sig, size_t siglen, 
+	ldns_buffer* rrset, unsigned char* key, size_t keylen, uint8_t algo)
+{
+        EVP_PKEY *evp_key;
+        ldns_status result;
+        const EVP_MD *d;
+
+        evp_key = ldns_ecdsa2pkey_raw(key, keylen, algo);
+        if(!evp_key) {
+		/* could not convert key */
+		return LDNS_STATUS_CRYPTO_BOGUS;
+        }
+        if(algo == LDNS_ECDSAP256SHA256)
+                d = EVP_sha256();
+        else    d = EVP_sha384(); /* LDNS_ECDSAP384SHA384 */
+	result = ldns_verify_rrsig_evp_raw(sig, siglen, rrset, evp_key, d);
+	EVP_PKEY_free(evp_key);
+	return result;
+}
+#endif
+
+ldns_status
+ldns_verify_rrsig_buffers(ldns_buffer *rawsig_buf, ldns_buffer *verify_buf, 
+					 ldns_buffer *key_buf, uint8_t algo)
+{
+	return ldns_verify_rrsig_buffers_raw(
+			 (unsigned char*)ldns_buffer_begin(rawsig_buf),
+			 ldns_buffer_position(rawsig_buf),
+			 verify_buf,
+			 (unsigned char*)ldns_buffer_begin(key_buf), 
+			 ldns_buffer_position(key_buf), algo);
+}
+
+ldns_status
+ldns_verify_rrsig_buffers_raw(unsigned char* sig, size_t siglen,
+						ldns_buffer *verify_buf, unsigned char* key, size_t keylen, 
+						uint8_t algo)
+{
+	/* check for right key */
+	switch(algo) {
+	case LDNS_DSA:
+	case LDNS_DSA_NSEC3:
+		return ldns_verify_rrsig_dsa_raw(sig,
+								   siglen,
+								   verify_buf,
+								   key,
+								   keylen);
+		break;
+	case LDNS_RSASHA1:
+	case LDNS_RSASHA1_NSEC3:
+		return ldns_verify_rrsig_rsasha1_raw(sig,
+									  siglen,
+									  verify_buf,
+									  key,
+									  keylen);
+		break;
+#ifdef USE_SHA2
+	case LDNS_RSASHA256:
+		return ldns_verify_rrsig_rsasha256_raw(sig,
+									    siglen,
+									    verify_buf,
+									    key,
+									    keylen);
+		break;
+	case LDNS_RSASHA512:
+		return ldns_verify_rrsig_rsasha512_raw(sig,
+									    siglen,
+									    verify_buf,
+									    key,
+									    keylen);
+		break;
+#endif
+#ifdef USE_GOST
+	case LDNS_ECC_GOST:
+		return ldns_verify_rrsig_gost_raw(sig, siglen, verify_buf,
+			key, keylen);
+		break;
+#endif
+#ifdef USE_ECDSA
+        case LDNS_ECDSAP256SHA256:
+        case LDNS_ECDSAP384SHA384:
+		return ldns_verify_rrsig_ecdsa_raw(sig, siglen, verify_buf,
+			key, keylen, algo);
+		break;
+#endif
+	case LDNS_RSAMD5:
+		return ldns_verify_rrsig_rsamd5_raw(sig,
+									 siglen,
+									 verify_buf,
+									 key,
+									 keylen);
+		break;
+	default:
+		/* do you know this alg?! */
+		return LDNS_STATUS_CRYPTO_UNKNOWN_ALGO;
+	}
+}
+
+
+/**
+ * Reset the ttl in the rrset with the orig_ttl from the sig 
+ * and update owner name if it was wildcard 
+ * Also canonicalizes the rrset.
+ * @param rrset: rrset to modify
+ * @param sig: signature to take TTL and wildcard values from
+ */
+static void
+ldns_rrset_use_signature_ttl(ldns_rr_list* rrset_clone, ldns_rr* rrsig)
+{
+	uint32_t orig_ttl;
+	uint16_t i;
+	uint8_t label_count;
+	ldns_rdf *wildcard_name;
+	ldns_rdf *wildcard_chopped;
+	ldns_rdf *wildcard_chopped_tmp;
+	
+	if ((rrsig == NULL) || ldns_rr_rd_count(rrsig) < 4) {
+		return;
+	}
+
+	orig_ttl = ldns_rdf2native_int32( ldns_rr_rdf(rrsig, 3));
+	label_count = ldns_rdf2native_int8(ldns_rr_rdf(rrsig, 2));
+
+	for(i = 0; i < ldns_rr_list_rr_count(rrset_clone); i++) {
+		if (label_count < 
+		    ldns_dname_label_count(
+			   ldns_rr_owner(ldns_rr_list_rr(rrset_clone, i)))) {
+			(void) ldns_str2rdf_dname(&wildcard_name, "*");
+			wildcard_chopped = ldns_rdf_clone(ldns_rr_owner(
+				ldns_rr_list_rr(rrset_clone, i)));
+			while (label_count < ldns_dname_label_count(wildcard_chopped)) {
+				wildcard_chopped_tmp = ldns_dname_left_chop(
+					wildcard_chopped);
+				ldns_rdf_deep_free(wildcard_chopped);
+				wildcard_chopped = wildcard_chopped_tmp;
+			}
+			(void) ldns_dname_cat(wildcard_name, wildcard_chopped);
+			ldns_rdf_deep_free(wildcard_chopped);
+			ldns_rdf_deep_free(ldns_rr_owner(ldns_rr_list_rr(
+				rrset_clone, i)));
+			ldns_rr_set_owner(ldns_rr_list_rr(rrset_clone, i), 
+				wildcard_name);
+		}
+		ldns_rr_set_ttl(ldns_rr_list_rr(rrset_clone, i), orig_ttl);
+		/* convert to lowercase */
+		ldns_rr2canonical(ldns_rr_list_rr(rrset_clone, i));
+	}
+}
+
+/**
+ * Make raw signature buffer out of rrsig
+ * @param rawsig_buf: raw signature buffer for result
+ * @param rrsig: signature to convert
+ * @return OK or more specific error.
+ */
+static ldns_status
+ldns_rrsig2rawsig_buffer(ldns_buffer* rawsig_buf, ldns_rr* rrsig)
+{
+	uint8_t sig_algo;
+       
+	if (rrsig == NULL) {
+		return LDNS_STATUS_CRYPTO_NO_RRSIG;
+	}
+	if (ldns_rr_rdf(rrsig, 1) == NULL) {
+		return LDNS_STATUS_MISSING_RDATA_FIELDS_RRSIG;
+	}
+	sig_algo = ldns_rdf2native_int8(ldns_rr_rdf(rrsig, 1));
+	/* check for known and implemented algo's now (otherwise 
+	 * the function could return a wrong error
+	 */
+	/* create a buffer with signature rdata */
+	/* for some algorithms we need other data than for others... */
+	/* (the DSA API wants DER encoding for instance) */
+
+	switch(sig_algo) {
+	case LDNS_RSAMD5:
+	case LDNS_RSASHA1:
+	case LDNS_RSASHA1_NSEC3:
+#ifdef USE_SHA2
+	case LDNS_RSASHA256:
+	case LDNS_RSASHA512:
+#endif
+#ifdef USE_GOST
+	case LDNS_ECC_GOST:
+#endif
+		if (ldns_rr_rdf(rrsig, 8) == NULL) {
+			return LDNS_STATUS_MISSING_RDATA_FIELDS_RRSIG;
+		}
+		if (ldns_rdf2buffer_wire(rawsig_buf, ldns_rr_rdf(rrsig, 8))
+			       	!= LDNS_STATUS_OK) {
+			return LDNS_STATUS_MEM_ERR;
+		}
+		break;
+	case LDNS_DSA:
+	case LDNS_DSA_NSEC3:
+		/* EVP takes rfc2459 format, which is a tad longer than dns format */
+		if (ldns_rr_rdf(rrsig, 8) == NULL) {
+			return LDNS_STATUS_MISSING_RDATA_FIELDS_RRSIG;
+		}
+		if (ldns_convert_dsa_rrsig_rdf2asn1(
+					rawsig_buf, ldns_rr_rdf(rrsig, 8)) 
+				!= LDNS_STATUS_OK) {
+			/*
+			  if (ldns_rdf2buffer_wire(rawsig_buf,
+			  ldns_rr_rdf(rrsig, 8)) != LDNS_STATUS_OK) {
+			*/
+			return LDNS_STATUS_MEM_ERR;
+		}
+		break;
+#ifdef USE_ECDSA
+        case LDNS_ECDSAP256SHA256:
+        case LDNS_ECDSAP384SHA384:
+                /* EVP produces an ASN prefix on the signature, which is
+                 * not used in the DNS */
+		if (ldns_rr_rdf(rrsig, 8) == NULL) {
+			return LDNS_STATUS_MISSING_RDATA_FIELDS_RRSIG;
+		}
+		if (ldns_convert_ecdsa_rrsig_rdf2asn1(
+					rawsig_buf, ldns_rr_rdf(rrsig, 8))
+				!= LDNS_STATUS_OK) {
+			return LDNS_STATUS_MEM_ERR;
+                }
+                break;
+#endif
+	case LDNS_DH:
+	case LDNS_ECC:
+	case LDNS_INDIRECT:
+		return LDNS_STATUS_CRYPTO_ALGO_NOT_IMPL;
+	default:
+		return LDNS_STATUS_CRYPTO_UNKNOWN_ALGO;
+	}
+	return LDNS_STATUS_OK;
+}
+
+/**
+ * Check RRSIG timestamps against the given 'now' time.
+ * @param rrsig: signature to check.
+ * @param now: the current time in seconds epoch.
+ * @return status code LDNS_STATUS_OK if all is fine.
+ */
+static ldns_status
+ldns_rrsig_check_timestamps(ldns_rr* rrsig, time_t now)
+{
+	int32_t inception, expiration;
+	
+	/* check the signature time stamps */
+	inception = (int32_t)ldns_rdf2native_time_t(
+		ldns_rr_rrsig_inception(rrsig));
+	expiration = (int32_t)ldns_rdf2native_time_t(
+		ldns_rr_rrsig_expiration(rrsig));
+
+	if (expiration - inception < 0) {
+		/* bad sig, expiration before inception?? Tsssg */
+		return LDNS_STATUS_CRYPTO_EXPIRATION_BEFORE_INCEPTION;
+	}
+	if (now - inception < 0) {
+		/* bad sig, inception date has not yet come to pass */
+		return LDNS_STATUS_CRYPTO_SIG_NOT_INCEPTED;
+	}
+	if (expiration - now < 0) {
+		/* bad sig, expiration date has passed */
+		return LDNS_STATUS_CRYPTO_SIG_EXPIRED;
+	}
+	return LDNS_STATUS_OK;
+}
+
+/**
+ * Prepare for verification.
+ * @param rawsig_buf: raw signature buffer made ready.
+ * @param verify_buf: data for verification buffer made ready.
+ * @param rrset_clone: made ready.
+ * @param rrsig: signature to prepare for.
+ * @return LDNS_STATUS_OK is all went well. Otherwise specific error.
+ */
+static ldns_status
+ldns_prepare_for_verify(ldns_buffer* rawsig_buf, ldns_buffer* verify_buf, 
+	ldns_rr_list* rrset_clone, ldns_rr* rrsig)
+{
+	ldns_status result;
+
+	/* canonicalize the sig */
+	ldns_dname2canonical(ldns_rr_owner(rrsig));
+	
+	/* check if the typecovered is equal to the type checked */
+	if (ldns_rdf2rr_type(ldns_rr_rrsig_typecovered(rrsig)) !=
+	    ldns_rr_get_type(ldns_rr_list_rr(rrset_clone, 0)))
+		return LDNS_STATUS_CRYPTO_TYPE_COVERED_ERR;
+	
+	/* create a buffer with b64 signature rdata */
+	result = ldns_rrsig2rawsig_buffer(rawsig_buf, rrsig);
+	if(result != LDNS_STATUS_OK)
+		return result;
+
+	/* use TTL from signature. Use wildcard names for wildcards */
+	/* also canonicalizes rrset_clone */
+	ldns_rrset_use_signature_ttl(rrset_clone, rrsig);
+
+	/* sort the rrset in canonical order  */
+	ldns_rr_list_sort(rrset_clone);
+
+	/* put the signature rr (without the b64) to the verify_buf */
+	if (ldns_rrsig2buffer_wire(verify_buf, rrsig) != LDNS_STATUS_OK)
+		return LDNS_STATUS_MEM_ERR;
+
+	/* add the rrset in verify_buf */
+	if(ldns_rr_list2buffer_wire(verify_buf, rrset_clone) 
+		!= LDNS_STATUS_OK)
+		return LDNS_STATUS_MEM_ERR;
+
+	return LDNS_STATUS_OK;
+}
+
+/**
+ * Check if a key matches a signature.
+ * Checks keytag, sigalgo and signature.
+ * @param rawsig_buf: raw signature buffer for verify
+ * @param verify_buf: raw data buffer for verify
+ * @param rrsig: the rrsig
+ * @param key: key to attempt.
+ * @return LDNS_STATUS_OK if OK, else some specific error.
+ */
+static ldns_status
+ldns_verify_test_sig_key(ldns_buffer* rawsig_buf, ldns_buffer* verify_buf, 
+	ldns_rr* rrsig, ldns_rr* key)
+{
+	uint8_t sig_algo;
+       
+	if (rrsig == NULL) {
+		return LDNS_STATUS_CRYPTO_NO_RRSIG;
+	}
+	if (ldns_rr_rdf(rrsig, 1) == NULL) {
+		return LDNS_STATUS_MISSING_RDATA_FIELDS_RRSIG;
+	}
+	sig_algo = ldns_rdf2native_int8(ldns_rr_rdf(rrsig, 1));
+
+	/* before anything, check if the keytags match */
+	if (ldns_calc_keytag(key)
+	    ==
+	    ldns_rdf2native_int16(ldns_rr_rrsig_keytag(rrsig))
+	    ) {
+		ldns_buffer* key_buf = ldns_buffer_new(LDNS_MAX_PACKETLEN);
+		ldns_status result = LDNS_STATUS_ERR;
+
+		/* put the key-data in a buffer, that's the third rdf, with
+		 * the base64 encoded key data */
+		if (ldns_rr_rdf(key, 3) == NULL) {
+			ldns_buffer_free(key_buf);
+			return LDNS_STATUS_MISSING_RDATA_FIELDS_KEY;
+		}
+		if (ldns_rdf2buffer_wire(key_buf, ldns_rr_rdf(key, 3))
+			       	!= LDNS_STATUS_OK) {
+			ldns_buffer_free(key_buf); 
+			/* returning is bad might screw up
+			   good keys later in the list
+			   what to do? */
+			return LDNS_STATUS_ERR;
+		}
+
+		if (ldns_rr_rdf(key, 2) == NULL) {
+			result = LDNS_STATUS_MISSING_RDATA_FIELDS_KEY;
+		}
+		else if (sig_algo == ldns_rdf2native_int8(
+					ldns_rr_rdf(key, 2))) {
+			result = ldns_verify_rrsig_buffers(rawsig_buf, 
+				verify_buf, key_buf, sig_algo);
+		} else {
+			/* No keys with the corresponding algorithm are found */
+			result = LDNS_STATUS_CRYPTO_NO_MATCHING_KEYTAG_DNSKEY;
+		}
+
+		ldns_buffer_free(key_buf); 
+		return result;
+	}
+	else {
+		/* No keys with the corresponding keytag are found */
+		return LDNS_STATUS_CRYPTO_NO_MATCHING_KEYTAG_DNSKEY;
+	}
+}
+
+/* 
+ * to verify:
+ * - create the wire fmt of the b64 key rdata
+ * - create the wire fmt of the sorted rrset
+ * - create the wire fmt of the b64 sig rdata
+ * - create the wire fmt of the sig without the b64 rdata
+ * - cat the sig data (without b64 rdata) to the rrset
+ * - verify the rrset+sig, with the b64 data and the b64 key data
+ */
+ldns_status
+ldns_verify_rrsig_keylist_time(
+		ldns_rr_list *rrset,
+		ldns_rr *rrsig,
+		const ldns_rr_list *keys, 
+		time_t check_time,
+		ldns_rr_list *good_keys)
+{
+	ldns_status result;
+	ldns_rr_list *valid = ldns_rr_list_new();
+	if (!valid)
+		return LDNS_STATUS_MEM_ERR;
+
+	result = ldns_verify_rrsig_keylist_notime(rrset, rrsig, keys, valid);
+	if(result != LDNS_STATUS_OK) {
+		ldns_rr_list_free(valid); 
+		return result;
+	}
+
+	/* check timestamps last; its OK except time */
+	result = ldns_rrsig_check_timestamps(rrsig, check_time);
+	if(result != LDNS_STATUS_OK) {
+		ldns_rr_list_free(valid); 
+		return result;
+	}
+
+	ldns_rr_list_cat(good_keys, valid);
+	ldns_rr_list_free(valid);
+	return LDNS_STATUS_OK;
+}
+
+/* 
+ * to verify:
+ * - create the wire fmt of the b64 key rdata
+ * - create the wire fmt of the sorted rrset
+ * - create the wire fmt of the b64 sig rdata
+ * - create the wire fmt of the sig without the b64 rdata
+ * - cat the sig data (without b64 rdata) to the rrset
+ * - verify the rrset+sig, with the b64 data and the b64 key data
+ */
+ldns_status
+ldns_verify_rrsig_keylist(ldns_rr_list *rrset,
+					 ldns_rr *rrsig,
+					 const ldns_rr_list *keys, 
+					 ldns_rr_list *good_keys)
+{
+	return ldns_verify_rrsig_keylist_time(
+			rrset, rrsig, keys, ldns_time(NULL), good_keys);
+}
+
+ldns_status
+ldns_verify_rrsig_keylist_notime(ldns_rr_list *rrset,
+					 ldns_rr *rrsig,
+					 const ldns_rr_list *keys, 
+					 ldns_rr_list *good_keys)
+{
+	ldns_buffer *rawsig_buf;
+	ldns_buffer *verify_buf;
+	uint16_t i;
+	ldns_status result, status;
+	ldns_rr_list *rrset_clone;
+	ldns_rr_list *validkeys;
+
+	if (!rrset) {
+		return LDNS_STATUS_ERR;
+	}
+
+	validkeys = ldns_rr_list_new();
+	if (!validkeys) {
+		return LDNS_STATUS_MEM_ERR;
+	}
+	
+	/* clone the rrset so that we can fiddle with it */
+	rrset_clone = ldns_rr_list_clone(rrset);
+
+	/* create the buffers which will certainly hold the raw data */
+	rawsig_buf = ldns_buffer_new(LDNS_MAX_PACKETLEN);
+	verify_buf  = ldns_buffer_new(LDNS_MAX_PACKETLEN);
+
+	result = ldns_prepare_for_verify(rawsig_buf, verify_buf, 
+		rrset_clone, rrsig);
+	if(result != LDNS_STATUS_OK) {
+		ldns_buffer_free(verify_buf);
+		ldns_buffer_free(rawsig_buf);
+		ldns_rr_list_deep_free(rrset_clone);
+		ldns_rr_list_free(validkeys);
+		return result;
+	}
+
+	result = LDNS_STATUS_CRYPTO_NO_MATCHING_KEYTAG_DNSKEY;
+	for(i = 0; i < ldns_rr_list_rr_count(keys); i++) {
+		status = ldns_verify_test_sig_key(rawsig_buf, verify_buf, 
+			rrsig, ldns_rr_list_rr(keys, i));
+		if (status == LDNS_STATUS_OK) {
+			/* one of the keys has matched, don't break
+			 * here, instead put the 'winning' key in
+			 * the validkey list and return the list 
+			 * later */
+			if (!ldns_rr_list_push_rr(validkeys, 
+				ldns_rr_list_rr(keys,i))) {
+				/* couldn't push the key?? */
+				ldns_buffer_free(rawsig_buf);
+				ldns_buffer_free(verify_buf);
+				ldns_rr_list_deep_free(rrset_clone);
+				ldns_rr_list_free(validkeys);
+				return LDNS_STATUS_MEM_ERR;
+			}
+
+			result = status;
+		}
+
+		if (result == LDNS_STATUS_CRYPTO_NO_MATCHING_KEYTAG_DNSKEY) {
+			result = status;
+		}
+	}
+
+	/* no longer needed */
+	ldns_rr_list_deep_free(rrset_clone);
+	ldns_buffer_free(rawsig_buf);
+	ldns_buffer_free(verify_buf);
+
+	if (ldns_rr_list_rr_count(validkeys) == 0) {
+		/* no keys were added, return last error */
+		ldns_rr_list_free(validkeys); 
+		return result;
+	}
+
+	/* do not check timestamps */
+
+	ldns_rr_list_cat(good_keys, validkeys);
+	ldns_rr_list_free(validkeys);
+	return LDNS_STATUS_OK;
+}
+
+ldns_status
+ldns_verify_rrsig_time(
+		ldns_rr_list *rrset, 
+		ldns_rr *rrsig, 
+		ldns_rr *key, 
+		time_t check_time)
+{
+	ldns_buffer *rawsig_buf;
+	ldns_buffer *verify_buf;
+	ldns_status result;
+	ldns_rr_list *rrset_clone;
+
+	if (!rrset) {
+		return LDNS_STATUS_NO_DATA;
+	}
+	/* clone the rrset so that we can fiddle with it */
+	rrset_clone = ldns_rr_list_clone(rrset);
+	/* create the buffers which will certainly hold the raw data */
+	rawsig_buf = ldns_buffer_new(LDNS_MAX_PACKETLEN);
+	verify_buf  = ldns_buffer_new(LDNS_MAX_PACKETLEN);
+
+	result = ldns_prepare_for_verify(rawsig_buf, verify_buf, 
+		rrset_clone, rrsig);
+	if(result != LDNS_STATUS_OK) {
+		ldns_rr_list_deep_free(rrset_clone);
+		ldns_buffer_free(rawsig_buf);
+		ldns_buffer_free(verify_buf);
+		return result;
+	}
+	result = ldns_verify_test_sig_key(rawsig_buf, verify_buf, 
+		rrsig, key);
+	/* no longer needed */
+	ldns_rr_list_deep_free(rrset_clone);
+	ldns_buffer_free(rawsig_buf);
+	ldns_buffer_free(verify_buf);
+
+	/* check timestamp last, apart from time its OK */
+	if(result == LDNS_STATUS_OK)
+		result = ldns_rrsig_check_timestamps(rrsig, check_time);
+
+	return result;
+}
+
+ldns_status
+ldns_verify_rrsig(ldns_rr_list *rrset, ldns_rr *rrsig, ldns_rr *key)
+{
+	return ldns_verify_rrsig_time(rrset, rrsig, key, ldns_time(NULL));
+}
+
+
+ldns_status
+ldns_verify_rrsig_evp(ldns_buffer *sig,
+				  ldns_buffer *rrset,
+				  EVP_PKEY *key,
+				  const EVP_MD *digest_type)
+{
+	return ldns_verify_rrsig_evp_raw(
+			 (unsigned char*)ldns_buffer_begin(sig),
+			 ldns_buffer_position(sig),
+			 rrset,
+			 key,
+			 digest_type);
+}
+
+ldns_status
+ldns_verify_rrsig_evp_raw(unsigned char *sig, size_t siglen, 
+					 ldns_buffer *rrset, EVP_PKEY *key, const EVP_MD *digest_type)
+{
+	EVP_MD_CTX ctx;
+	int res;
+
+	EVP_MD_CTX_init(&ctx);
+	
+	EVP_VerifyInit(&ctx, digest_type);
+	EVP_VerifyUpdate(&ctx,
+				  ldns_buffer_begin(rrset),
+				  ldns_buffer_position(rrset));
+	res = EVP_VerifyFinal(&ctx, sig, (unsigned int) siglen, key);
+	
+	EVP_MD_CTX_cleanup(&ctx);
+	
+	if (res == 1) {
+		return LDNS_STATUS_OK;
+	} else if (res == 0) {
+		return LDNS_STATUS_CRYPTO_BOGUS;
+	}
+	/* TODO how to communicate internal SSL error?
+	   let caller use ssl's get_error() */
+	return LDNS_STATUS_SSL_ERR;
+}
+
+ldns_status
+ldns_verify_rrsig_dsa(ldns_buffer *sig, ldns_buffer *rrset, ldns_buffer *key)
+{
+	return ldns_verify_rrsig_dsa_raw(
+			 (unsigned char*) ldns_buffer_begin(sig),
+			 ldns_buffer_position(sig),
+			 rrset,
+			 (unsigned char*) ldns_buffer_begin(key),
+			 ldns_buffer_position(key));
+}
+
+ldns_status
+ldns_verify_rrsig_rsasha1(ldns_buffer *sig, ldns_buffer *rrset, ldns_buffer *key)
+{
+	return ldns_verify_rrsig_rsasha1_raw(
+			 (unsigned char*)ldns_buffer_begin(sig),
+			 ldns_buffer_position(sig),
+			 rrset,
+			 (unsigned char*) ldns_buffer_begin(key),
+			 ldns_buffer_position(key));
+}
+
+ldns_status
+ldns_verify_rrsig_rsamd5(ldns_buffer *sig, ldns_buffer *rrset, ldns_buffer *key)
+{
+	return ldns_verify_rrsig_rsamd5_raw(
+			 (unsigned char*)ldns_buffer_begin(sig),
+			 ldns_buffer_position(sig),
+			 rrset,
+			 (unsigned char*) ldns_buffer_begin(key),
+			 ldns_buffer_position(key));
+}
+
+ldns_status
+ldns_verify_rrsig_dsa_raw(unsigned char* sig, size_t siglen,
+					 ldns_buffer* rrset, unsigned char* key, size_t keylen)
+{
+	EVP_PKEY *evp_key;
+	ldns_status result;
+
+	evp_key = EVP_PKEY_new();
+	if (EVP_PKEY_assign_DSA(evp_key, ldns_key_buf2dsa_raw(key, keylen))) {
+		result = ldns_verify_rrsig_evp_raw(sig,
+								siglen,
+								rrset,
+								evp_key,
+								EVP_dss1());
+	} else {
+		result = LDNS_STATUS_SSL_ERR;
+	}
+	EVP_PKEY_free(evp_key);
+	return result;
+
+}
+
+ldns_status
+ldns_verify_rrsig_rsasha1_raw(unsigned char* sig, size_t siglen,
+						ldns_buffer* rrset, unsigned char* key, size_t keylen)
+{
+	EVP_PKEY *evp_key;
+	ldns_status result;
+
+	evp_key = EVP_PKEY_new();
+	if (EVP_PKEY_assign_RSA(evp_key, ldns_key_buf2rsa_raw(key, keylen))) {
+		result = ldns_verify_rrsig_evp_raw(sig,
+								siglen,
+								rrset,
+								evp_key,
+								EVP_sha1());
+	} else {
+		result = LDNS_STATUS_SSL_ERR;
+	}
+	EVP_PKEY_free(evp_key);
+
+	return result;
+}
+
+ldns_status
+ldns_verify_rrsig_rsasha256_raw(unsigned char* sig,
+						  size_t siglen,
+						  ldns_buffer* rrset,
+						  unsigned char* key,
+						  size_t keylen)
+{
+#ifdef USE_SHA2
+	EVP_PKEY *evp_key;
+	ldns_status result;
+
+	evp_key = EVP_PKEY_new();
+	if (EVP_PKEY_assign_RSA(evp_key, ldns_key_buf2rsa_raw(key, keylen))) {
+		result = ldns_verify_rrsig_evp_raw(sig,
+								siglen,
+								rrset,
+								evp_key,
+								EVP_sha256());
+	} else {
+		result = LDNS_STATUS_SSL_ERR;
+	}
+	EVP_PKEY_free(evp_key);
+
+	return result;
+#else
+	/* touch these to prevent compiler warnings */
+	(void) sig;
+	(void) siglen;
+	(void) rrset;
+	(void) key;
+	(void) keylen;
+	return LDNS_STATUS_CRYPTO_UNKNOWN_ALGO;
+#endif
+}
+
+ldns_status
+ldns_verify_rrsig_rsasha512_raw(unsigned char* sig,
+						  size_t siglen,
+						  ldns_buffer* rrset,
+						  unsigned char* key,
+						  size_t keylen)
+{
+#ifdef USE_SHA2
+	EVP_PKEY *evp_key;
+	ldns_status result;
+
+	evp_key = EVP_PKEY_new();
+	if (EVP_PKEY_assign_RSA(evp_key, ldns_key_buf2rsa_raw(key, keylen))) {
+		result = ldns_verify_rrsig_evp_raw(sig,
+								siglen,
+								rrset,
+								evp_key,
+								EVP_sha512());
+	} else {
+		result = LDNS_STATUS_SSL_ERR;
+	}
+	EVP_PKEY_free(evp_key);
+
+	return result;
+#else
+	/* touch these to prevent compiler warnings */
+	(void) sig;
+	(void) siglen;
+	(void) rrset;
+	(void) key;
+	(void) keylen;
+	return LDNS_STATUS_CRYPTO_UNKNOWN_ALGO;
+#endif
+}
+
+
+ldns_status
+ldns_verify_rrsig_rsamd5_raw(unsigned char* sig,
+					    size_t siglen,
+					    ldns_buffer* rrset,
+					    unsigned char* key,
+					    size_t keylen)
+{
+	EVP_PKEY *evp_key;
+	ldns_status result;
+
+	evp_key = EVP_PKEY_new();
+	if (EVP_PKEY_assign_RSA(evp_key, ldns_key_buf2rsa_raw(key, keylen))) {
+		result = ldns_verify_rrsig_evp_raw(sig,
+								siglen,
+								rrset,
+								evp_key,
+								EVP_md5());
+	} else {
+		result = LDNS_STATUS_SSL_ERR;
+	}
+	EVP_PKEY_free(evp_key);
+
+	return result;
+}
+
+#endif
diff --git a/3rdParty/Ldns/src/src/dnssec_zone.c b/3rdParty/Ldns/src/src/dnssec_zone.c
new file mode 100644
index 0000000..89bdf8d
--- /dev/null
+++ b/3rdParty/Ldns/src/src/dnssec_zone.c
@@ -0,0 +1,921 @@
+/*
+ * special zone file structures and functions for better dnssec handling
+ */
+
+#include <ldns/config.h>
+
+#include <ldns/ldns.h>
+
+ldns_dnssec_rrs *
+ldns_dnssec_rrs_new()
+{
+	ldns_dnssec_rrs *new_rrs;
+	new_rrs = LDNS_MALLOC(ldns_dnssec_rrs);
+        if(!new_rrs) return NULL;
+	new_rrs->rr = NULL;
+	new_rrs->next = NULL;
+	return new_rrs;
+}
+
+INLINE void
+ldns_dnssec_rrs_free_internal(ldns_dnssec_rrs *rrs, int deep)
+{
+	ldns_dnssec_rrs *next;
+	while (rrs) {
+		next = rrs->next;
+		if (deep) {
+			ldns_rr_free(rrs->rr);
+		}
+		LDNS_FREE(rrs);
+		rrs = next;
+	}
+}
+
+void
+ldns_dnssec_rrs_free(ldns_dnssec_rrs *rrs)
+{
+	ldns_dnssec_rrs_free_internal(rrs, 0);
+}
+
+void
+ldns_dnssec_rrs_deep_free(ldns_dnssec_rrs *rrs)
+{
+	ldns_dnssec_rrs_free_internal(rrs, 1);
+}
+
+ldns_status
+ldns_dnssec_rrs_add_rr(ldns_dnssec_rrs *rrs, ldns_rr *rr)
+{
+	int cmp;
+	ldns_dnssec_rrs *new_rrs;
+	if (!rrs || !rr) {
+		return LDNS_STATUS_ERR;
+	}
+
+	/* this could be done more efficiently; name and type should already
+	   be equal */
+	cmp = ldns_rr_compare(rrs->rr,
+					  rr);
+	/* should we error on equal? */
+	if (cmp <= 0) {
+		if (rrs->next) {
+			return ldns_dnssec_rrs_add_rr(rrs->next, rr);
+		} else {
+			new_rrs = ldns_dnssec_rrs_new();
+			new_rrs->rr = rr;
+			rrs->next = new_rrs;
+		}
+	} else if (cmp > 0) {
+		/* put the current old rr in the new next, put the new
+		   rr in the current container */
+		new_rrs = ldns_dnssec_rrs_new();
+		new_rrs->rr = rrs->rr;
+		new_rrs->next = rrs->next;
+		rrs->rr = rr;
+		rrs->next = new_rrs;
+	}
+	return LDNS_STATUS_OK;
+}
+
+void
+ldns_dnssec_rrs_print_fmt(FILE *out, const ldns_output_format *fmt,
+	       ldns_dnssec_rrs *rrs)
+{
+	if (!rrs) {
+		if ((fmt->flags & LDNS_COMMENT_LAYOUT))
+			fprintf(out, "; <void>");
+	} else {
+		if (rrs->rr) {
+			ldns_rr_print_fmt(out, fmt, rrs->rr);
+		}
+		if (rrs->next) {
+			ldns_dnssec_rrs_print_fmt(out, fmt, rrs->next);
+		}
+	}
+}
+
+void
+ldns_dnssec_rrs_print(FILE *out, ldns_dnssec_rrs *rrs)
+{
+	ldns_dnssec_rrs_print_fmt(out, ldns_output_format_default, rrs);
+}
+
+
+ldns_dnssec_rrsets *
+ldns_dnssec_rrsets_new()
+{
+	ldns_dnssec_rrsets *new_rrsets;
+	new_rrsets = LDNS_MALLOC(ldns_dnssec_rrsets);
+        if(!new_rrsets) return NULL;
+	new_rrsets->rrs = NULL;
+	new_rrsets->type = 0;
+	new_rrsets->signatures = NULL;
+	new_rrsets->next = NULL;
+	return new_rrsets;
+}
+
+INLINE void
+ldns_dnssec_rrsets_free_internal(ldns_dnssec_rrsets *rrsets, int deep)
+{
+	if (rrsets) {
+		if (rrsets->rrs) {
+			ldns_dnssec_rrs_free_internal(rrsets->rrs, deep);
+		}
+		if (rrsets->next) {
+			ldns_dnssec_rrsets_free_internal(rrsets->next, deep);
+		}
+		if (rrsets->signatures) {
+			ldns_dnssec_rrs_free_internal(rrsets->signatures, deep);
+		}
+		LDNS_FREE(rrsets);
+	}
+}
+
+void
+ldns_dnssec_rrsets_free(ldns_dnssec_rrsets *rrsets)
+{
+	ldns_dnssec_rrsets_free_internal(rrsets, 0);
+}
+
+void
+ldns_dnssec_rrsets_deep_free(ldns_dnssec_rrsets *rrsets)
+{
+	ldns_dnssec_rrsets_free_internal(rrsets, 1);
+}
+
+ldns_rr_type
+ldns_dnssec_rrsets_type(ldns_dnssec_rrsets *rrsets)
+{
+	if (rrsets) {
+		return rrsets->type;
+	} else {
+		return 0;
+	}
+}
+
+ldns_status
+ldns_dnssec_rrsets_set_type(ldns_dnssec_rrsets *rrsets,
+					   ldns_rr_type type)
+{
+	if (rrsets) {
+		rrsets->type = type;
+		return LDNS_STATUS_OK;
+	}
+	return LDNS_STATUS_ERR;
+}
+
+ldns_dnssec_rrsets *
+ldns_dnssec_rrsets_new_frm_rr(ldns_rr *rr)
+{
+	ldns_dnssec_rrsets *new_rrsets;
+	ldns_rr_type rr_type;
+	bool rrsig;
+
+	new_rrsets = ldns_dnssec_rrsets_new();
+	rr_type = ldns_rr_get_type(rr);
+	if (rr_type == LDNS_RR_TYPE_RRSIG) {
+		rrsig = true;
+		rr_type = ldns_rdf2rr_type(ldns_rr_rrsig_typecovered(rr));
+	} else {
+		rrsig = false;
+	}
+	if (!rrsig) {
+		new_rrsets->rrs = ldns_dnssec_rrs_new();
+		new_rrsets->rrs->rr = rr;
+	} else {
+		new_rrsets->signatures = ldns_dnssec_rrs_new();
+		new_rrsets->signatures->rr = rr;
+	}
+	new_rrsets->type = rr_type;
+	return new_rrsets;
+}
+
+ldns_status
+ldns_dnssec_rrsets_add_rr(ldns_dnssec_rrsets *rrsets, ldns_rr *rr)
+{
+	ldns_dnssec_rrsets *new_rrsets;
+	ldns_rr_type rr_type;
+	bool rrsig = false;
+	ldns_status result = LDNS_STATUS_OK;
+
+	if (!rrsets || !rr) {
+		return LDNS_STATUS_ERR;
+	}
+
+	rr_type = ldns_rr_get_type(rr);
+
+	if (rr_type == LDNS_RR_TYPE_RRSIG) {
+		rrsig = true;
+		rr_type = ldns_rdf2rr_type(ldns_rr_rrsig_typecovered(rr));
+	}
+
+	if (!rrsets->rrs && rrsets->type == 0 && !rrsets->signatures) {
+		if (!rrsig) {
+			rrsets->rrs = ldns_dnssec_rrs_new();
+			rrsets->rrs->rr = rr;
+			rrsets->type = rr_type;
+		} else {
+			rrsets->signatures = ldns_dnssec_rrs_new();
+			rrsets->signatures->rr = rr;
+			rrsets->type = rr_type;
+		}
+		return LDNS_STATUS_OK;
+	}
+
+	if (rr_type > ldns_dnssec_rrsets_type(rrsets)) {
+		if (rrsets->next) {
+			result = ldns_dnssec_rrsets_add_rr(rrsets->next, rr);
+		} else {
+			new_rrsets = ldns_dnssec_rrsets_new_frm_rr(rr);
+			rrsets->next = new_rrsets;
+		}
+	} else if (rr_type < ldns_dnssec_rrsets_type(rrsets)) {
+		/* move the current one into the new next, 
+		   replace field of current with data from new rr */
+		new_rrsets = ldns_dnssec_rrsets_new();
+		new_rrsets->rrs = rrsets->rrs;
+		new_rrsets->type = rrsets->type;
+		new_rrsets->signatures = rrsets->signatures;
+		new_rrsets->next = rrsets->next;
+		if (!rrsig) {
+			rrsets->rrs = ldns_dnssec_rrs_new();
+			rrsets->rrs->rr = rr;
+			rrsets->signatures = NULL;
+		} else {
+			rrsets->rrs = NULL;
+			rrsets->signatures = ldns_dnssec_rrs_new();
+			rrsets->signatures->rr = rr;
+		}
+		rrsets->type = rr_type;
+		rrsets->next = new_rrsets;
+	} else {
+		/* equal, add to current rrsets */
+		if (rrsig) {
+			if (rrsets->signatures) {
+				result = ldns_dnssec_rrs_add_rr(rrsets->signatures, rr);
+			} else {
+				rrsets->signatures = ldns_dnssec_rrs_new();
+				rrsets->signatures->rr = rr;
+			}
+		} else {
+			if (rrsets->rrs) {
+				result = ldns_dnssec_rrs_add_rr(rrsets->rrs, rr);
+			} else {
+				rrsets->rrs = ldns_dnssec_rrs_new();
+				rrsets->rrs->rr = rr;
+			}
+		}
+	}
+
+	return result;
+}
+
+void
+ldns_dnssec_rrsets_print_soa_fmt(FILE *out, const ldns_output_format *fmt,
+		ldns_dnssec_rrsets *rrsets,
+		bool follow,
+		bool show_soa)
+{
+	if (!rrsets) {
+		if ((fmt->flags & LDNS_COMMENT_LAYOUT))
+			fprintf(out, "; <void>\n");
+	} else {
+		if (rrsets->rrs &&
+		    (show_soa ||
+			ldns_rr_get_type(rrsets->rrs->rr) != LDNS_RR_TYPE_SOA
+		    )
+		   ) {
+			ldns_dnssec_rrs_print_fmt(out, fmt, rrsets->rrs);
+			if (rrsets->signatures) {
+				ldns_dnssec_rrs_print_fmt(out, fmt, 
+						rrsets->signatures);
+			}
+		}
+		if (follow && rrsets->next) {
+			ldns_dnssec_rrsets_print_soa_fmt(out, fmt, 
+					rrsets->next, follow, show_soa);
+		}
+	}
+}
+
+void
+ldns_dnssec_rrsets_print_soa(FILE *out,
+		ldns_dnssec_rrsets *rrsets,
+		bool follow,
+		bool show_soa)
+{
+	ldns_dnssec_rrsets_print_soa_fmt(out, ldns_output_format_default,
+		       	rrsets, follow, show_soa);
+}
+
+
+void
+ldns_dnssec_rrsets_print_fmt(FILE *out, const ldns_output_format *fmt,
+		ldns_dnssec_rrsets *rrsets, 
+		bool follow)
+{
+	ldns_dnssec_rrsets_print_soa_fmt(out, fmt, rrsets, follow, true);
+}
+
+void
+ldns_dnssec_rrsets_print(FILE *out, ldns_dnssec_rrsets *rrsets, bool follow)
+{
+	ldns_dnssec_rrsets_print_fmt(out, ldns_output_format_default, 
+			rrsets, follow);
+}
+
+ldns_dnssec_name *
+ldns_dnssec_name_new()
+{
+	ldns_dnssec_name *new_name;
+
+	new_name = LDNS_CALLOC(ldns_dnssec_name, 1);
+	if (!new_name) {
+		return NULL;
+	}
+	/*
+	 * not needed anymore because CALLOC initalizes everything to zero.
+
+	new_name->name = NULL;
+	new_name->rrsets = NULL;
+	new_name->name_alloced = false;
+	new_name->nsec = NULL;
+	new_name->nsec_signatures = NULL;
+
+	new_name->is_glue = false;
+	new_name->hashed_name = NULL;
+
+	 */
+	return new_name;
+}
+
+ldns_dnssec_name *
+ldns_dnssec_name_new_frm_rr(ldns_rr *rr)
+{
+	ldns_dnssec_name *new_name = ldns_dnssec_name_new();
+
+	new_name->name = ldns_rr_owner(rr);
+	if(ldns_dnssec_name_add_rr(new_name, rr) != LDNS_STATUS_OK) {
+		ldns_dnssec_name_free(new_name);
+		return NULL;
+	}
+
+	return new_name;
+}
+
+INLINE void
+ldns_dnssec_name_free_internal(ldns_dnssec_name *name,
+                               int deep)
+{
+	if (name) {
+		if (name->name_alloced) {
+			ldns_rdf_deep_free(name->name);
+		}
+		if (name->rrsets) {
+			ldns_dnssec_rrsets_free_internal(name->rrsets, deep);
+		}
+		if (name->nsec && deep) {
+			ldns_rr_free(name->nsec);
+		}
+		if (name->nsec_signatures) {
+			ldns_dnssec_rrs_free_internal(name->nsec_signatures, deep);
+		}
+		if (name->hashed_name) {
+			if (deep) {
+				ldns_rdf_deep_free(name->hashed_name);
+			}
+		}
+		LDNS_FREE(name);
+	}
+}
+
+void
+ldns_dnssec_name_free(ldns_dnssec_name *name)
+{
+  ldns_dnssec_name_free_internal(name, 0);
+}
+
+void
+ldns_dnssec_name_deep_free(ldns_dnssec_name *name)
+{
+  ldns_dnssec_name_free_internal(name, 1);
+}
+
+ldns_rdf *
+ldns_dnssec_name_name(ldns_dnssec_name *name)
+{
+	if (name) {
+		return name->name;
+	}
+	return NULL;
+}
+
+bool
+ldns_dnssec_name_is_glue(ldns_dnssec_name *name)
+{
+	if (name) {
+		return name->is_glue;
+	}
+	return false;
+}
+
+void
+ldns_dnssec_name_set_name(ldns_dnssec_name *rrset,
+					 ldns_rdf *dname)
+{
+	if (rrset && dname) {
+		rrset->name = dname;
+	}
+}
+
+ldns_rr *
+ldns_dnssec_name_nsec(ldns_dnssec_name *rrset)
+{
+	if (rrset) {
+		return rrset->nsec;
+	}
+	return NULL;
+}
+
+void
+ldns_dnssec_name_set_nsec(ldns_dnssec_name *rrset, ldns_rr *nsec)
+{
+	if (rrset && nsec) {
+		rrset->nsec = nsec;
+	}
+}
+
+int
+ldns_dnssec_name_cmp(const void *a, const void *b)
+{
+	ldns_dnssec_name *na = (ldns_dnssec_name *) a;
+	ldns_dnssec_name *nb = (ldns_dnssec_name *) b;
+
+	if (na && nb) {
+		return ldns_dname_compare(ldns_dnssec_name_name(na),
+							 ldns_dnssec_name_name(nb));
+	} else if (na) {
+		return 1;
+	} else if (nb) {
+		return -1;
+	} else {
+		return 0;
+	}
+}
+
+ldns_status
+ldns_dnssec_name_add_rr(ldns_dnssec_name *name,
+				    ldns_rr *rr)
+{
+	ldns_status result = LDNS_STATUS_OK;
+	ldns_rdf *name_name;
+	bool hashed_name = false;
+	ldns_rr_type rr_type;
+	ldns_rr_type typecovered = 0;
+
+	/* special handling for NSEC3 and NSECX covering RRSIGS */
+
+	if (!name || !rr) {
+		return LDNS_STATUS_ERR;
+	}
+
+	rr_type = ldns_rr_get_type(rr);
+
+	if (rr_type == LDNS_RR_TYPE_RRSIG) {
+		typecovered = ldns_rdf2rr_type(ldns_rr_rrsig_typecovered(rr));
+	}
+
+#ifdef HAVE_SSL
+	if (rr_type == LDNS_RR_TYPE_NSEC3 ||
+	    typecovered == LDNS_RR_TYPE_NSEC3) {
+		name_name = ldns_nsec3_hash_name_frm_nsec3(rr,
+										   ldns_dnssec_name_name(name));
+		hashed_name = true;
+	} else {
+		name_name = ldns_dnssec_name_name(name);
+	}
+#else
+	name_name = ldns_dnssec_name_name(name);
+#endif /* HAVE_SSL */
+
+	if (rr_type == LDNS_RR_TYPE_NSEC ||
+	    rr_type == LDNS_RR_TYPE_NSEC3) {
+		/* XX check if is already set (and error?) */
+		name->nsec = rr;
+	} else if (typecovered == LDNS_RR_TYPE_NSEC ||
+			 typecovered == LDNS_RR_TYPE_NSEC3) {
+		if (name->nsec_signatures) {
+			result = ldns_dnssec_rrs_add_rr(name->nsec_signatures, rr);
+		} else {
+			name->nsec_signatures = ldns_dnssec_rrs_new();
+			name->nsec_signatures->rr = rr;
+		}
+	} else {
+		/* it's a 'normal' RR, add it to the right rrset */
+		if (name->rrsets) {
+			result = ldns_dnssec_rrsets_add_rr(name->rrsets, rr);
+		} else {
+			name->rrsets = ldns_dnssec_rrsets_new();
+			result = ldns_dnssec_rrsets_add_rr(name->rrsets, rr);
+		}
+	}
+
+	if (hashed_name) {
+		ldns_rdf_deep_free(name_name);
+	}
+
+	return result;
+}
+
+ldns_dnssec_rrsets *
+ldns_dnssec_name_find_rrset(ldns_dnssec_name *name,
+					   ldns_rr_type type) {
+	ldns_dnssec_rrsets *result;
+
+	result = name->rrsets;
+	while (result) {
+		if (result->type == type) {
+			return result;
+		} else {
+			result = result->next;
+		}
+	}
+	return NULL;
+}
+
+ldns_dnssec_rrsets *
+ldns_dnssec_zone_find_rrset(ldns_dnssec_zone *zone,
+					   ldns_rdf *dname,
+					   ldns_rr_type type)
+{
+	ldns_rbnode_t *node;
+
+	if (!zone || !dname) {
+		return NULL;
+	}
+
+	node = ldns_rbtree_search(zone->names, dname);
+	if (node) {
+		return ldns_dnssec_name_find_rrset((ldns_dnssec_name *)node->data,
+									type);
+	} else {
+		return NULL;
+	}
+}
+
+void
+ldns_dnssec_name_print_soa_fmt(FILE *out, const ldns_output_format *fmt,
+		ldns_dnssec_name *name, 
+		bool show_soa)
+{
+	if (name) {
+		if(name->rrsets) {
+			ldns_dnssec_rrsets_print_soa_fmt(out, fmt, 
+					name->rrsets, true, show_soa);
+		} else if ((fmt->flags & LDNS_COMMENT_LAYOUT)) {
+			fprintf(out, ";; Empty nonterminal: ");
+			ldns_rdf_print(out, name->name);
+			fprintf(out, "\n");
+		}
+		if(name->nsec) {
+			ldns_rr_print_fmt(out, fmt, name->nsec);
+		}
+		if (name->nsec_signatures) {
+			ldns_dnssec_rrs_print_fmt(out, fmt, 
+					name->nsec_signatures);
+		}
+	} else if ((fmt->flags & LDNS_COMMENT_LAYOUT)) {
+		fprintf(out, "; <void>\n");
+	}
+}
+
+void
+ldns_dnssec_name_print_soa(FILE *out, ldns_dnssec_name *name, bool show_soa)
+{
+	ldns_dnssec_name_print_soa_fmt(out, ldns_output_format_default,
+		       name, show_soa);
+}
+
+void
+ldns_dnssec_name_print_fmt(FILE *out, const ldns_output_format *fmt,
+		ldns_dnssec_name *name)
+{
+	ldns_dnssec_name_print_soa_fmt(out, fmt, name, true);
+}
+
+void
+ldns_dnssec_name_print(FILE *out, ldns_dnssec_name *name)
+{
+	ldns_dnssec_name_print_fmt(out, ldns_output_format_default, name);
+}
+
+
+ldns_dnssec_zone *
+ldns_dnssec_zone_new()
+{
+	ldns_dnssec_zone *zone = LDNS_MALLOC(ldns_dnssec_zone);
+        if(!zone) return NULL;
+	zone->soa = NULL;
+	zone->names = NULL;
+
+	return zone;
+}
+
+void
+ldns_dnssec_name_node_free(ldns_rbnode_t *node, void *arg) {
+	(void) arg;
+	ldns_dnssec_name_free((ldns_dnssec_name *)node->data);
+	free(node);
+}
+
+void
+ldns_dnssec_name_node_deep_free(ldns_rbnode_t *node, void *arg) {
+	(void) arg;
+	ldns_dnssec_name_deep_free((ldns_dnssec_name *)node->data);
+	free(node);
+}
+
+void
+ldns_dnssec_zone_free(ldns_dnssec_zone *zone)
+{
+	if (zone) {
+		if (zone->names) {
+			/* destroy all name structures within the tree */
+			ldns_traverse_postorder(zone->names,
+						    ldns_dnssec_name_node_free,
+						    NULL);
+			free(zone->names);
+		}
+		LDNS_FREE(zone);
+	}
+}
+
+void
+ldns_dnssec_zone_deep_free(ldns_dnssec_zone *zone)
+{
+	if (zone) {
+		if (zone->names) {
+			/* destroy all name structures within the tree */
+			ldns_traverse_postorder(zone->names,
+						    ldns_dnssec_name_node_deep_free,
+						    NULL);
+			free(zone->names);
+		}
+		LDNS_FREE(zone);
+	}
+}
+
+/* use for dname comparison in tree */
+int
+ldns_dname_compare_v(const void *a, const void *b) {
+	return ldns_dname_compare((ldns_rdf *)a, (ldns_rdf *)b);
+}
+
+#ifdef HAVE_SSL
+ldns_rbnode_t *
+ldns_dnssec_zone_find_nsec3_original(ldns_dnssec_zone *zone,
+                                     ldns_rr *rr) {
+	ldns_rbnode_t *current_node = ldns_rbtree_first(zone->names);
+	ldns_dnssec_name *current_name;
+	ldns_rdf *hashed_name;
+
+	hashed_name = ldns_dname_label(ldns_rr_owner(rr), 0);
+
+	while (current_node != LDNS_RBTREE_NULL) {
+		current_name = (ldns_dnssec_name *) current_node->data;
+		if (!current_name->hashed_name) {
+			current_name->hashed_name =
+				ldns_nsec3_hash_name_frm_nsec3(rr, current_name->name);
+		}
+		if (ldns_dname_compare(hashed_name,
+						   current_name->hashed_name)
+		    == 0) {
+			ldns_rdf_deep_free(hashed_name);
+			return current_node;
+		}
+		current_node = ldns_rbtree_next(current_node);
+	}
+	ldns_rdf_deep_free(hashed_name);
+	return NULL;
+}
+
+ldns_status
+ldns_dnssec_zone_add_rr(ldns_dnssec_zone *zone, ldns_rr *rr)
+{
+	ldns_status result = LDNS_STATUS_OK;
+	ldns_dnssec_name *cur_name;
+	ldns_rbnode_t *cur_node;
+	ldns_rr_type type_covered = 0;
+
+	if (!zone || !rr) {
+		return LDNS_STATUS_ERR;
+	}
+
+	if (!zone->names) {
+		zone->names = ldns_rbtree_create(ldns_dname_compare_v);
+                if(!zone->names) return LDNS_STATUS_MEM_ERR;
+	}
+
+	/* we need the original of the hashed name if this is
+	   an NSEC3, or an RRSIG that covers an NSEC3 */
+	if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_RRSIG) {
+		type_covered = ldns_rdf2rr_type(ldns_rr_rrsig_typecovered(rr));
+	}
+	if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_NSEC3 ||
+	    type_covered == LDNS_RR_TYPE_NSEC3) {
+		cur_node = ldns_dnssec_zone_find_nsec3_original(zone,
+					 						   rr);
+		if (!cur_node) {
+			return LDNS_STATUS_DNSSEC_NSEC3_ORIGINAL_NOT_FOUND;
+		}
+	} else {
+		cur_node = ldns_rbtree_search(zone->names, ldns_rr_owner(rr));
+	}
+
+	if (!cur_node) {
+		/* add */
+		cur_name = ldns_dnssec_name_new_frm_rr(rr);
+                if(!cur_name) return LDNS_STATUS_MEM_ERR;
+		cur_node = LDNS_MALLOC(ldns_rbnode_t);
+                if(!cur_node) {
+                        ldns_dnssec_name_free(cur_name);
+                        return LDNS_STATUS_MEM_ERR;
+                }
+		cur_node->key = ldns_rr_owner(rr);
+		cur_node->data = cur_name;
+		(void)ldns_rbtree_insert(zone->names, cur_node);
+	} else {
+		cur_name = (ldns_dnssec_name *) cur_node->data;
+		result = ldns_dnssec_name_add_rr(cur_name, rr);
+	}
+
+	if (result != LDNS_STATUS_OK) {
+		fprintf(stderr, "error adding rr: ");
+		ldns_rr_print(stderr, rr);
+	}
+
+	/*TODO ldns_dnssec_name_print_names(stdout, zone->names, 0);*/
+	if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_SOA) {
+		zone->soa = cur_name;
+	}
+
+	return result;
+}
+#endif /* HAVE_SSL */
+
+void
+ldns_dnssec_zone_names_print_fmt(FILE *out, const ldns_output_format *fmt,
+		ldns_rbtree_t *tree, 
+		bool print_soa)
+{
+	ldns_rbnode_t *node;
+	ldns_dnssec_name *name;
+
+	node = ldns_rbtree_first(tree);
+	while (node != LDNS_RBTREE_NULL) {
+		name = (ldns_dnssec_name *) node->data;
+		ldns_dnssec_name_print_soa_fmt(out, fmt, name, print_soa);
+		if ((fmt->flags & LDNS_COMMENT_LAYOUT))
+			fprintf(out, ";\n");
+		node = ldns_rbtree_next(node);
+	}
+}
+
+void
+ldns_dnssec_zone_names_print(FILE *out, ldns_rbtree_t *tree, bool print_soa)
+{
+	ldns_dnssec_zone_names_print_fmt(out, ldns_output_format_default,
+		       tree, print_soa);
+}
+
+void
+ldns_dnssec_zone_print_fmt(FILE *out, const ldns_output_format *fmt,
+	       ldns_dnssec_zone *zone)
+{
+	if (zone) {
+		if (zone->soa) {
+			if ((fmt->flags & LDNS_COMMENT_LAYOUT)) {
+				fprintf(out, ";; Zone: ");
+				ldns_rdf_print(out, ldns_dnssec_name_name(
+							zone->soa));
+				fprintf(out, "\n;\n");
+			}
+			ldns_dnssec_rrsets_print_fmt(out, fmt,
+					ldns_dnssec_name_find_rrset(
+						zone->soa, 
+						LDNS_RR_TYPE_SOA), 
+					false);
+			if ((fmt->flags & LDNS_COMMENT_LAYOUT))
+				fprintf(out, ";\n");
+		}
+
+		if (zone->names) {
+			ldns_dnssec_zone_names_print_fmt(out, fmt, 
+					zone->names, false);
+		}
+	}
+}
+
+void
+ldns_dnssec_zone_print(FILE *out, ldns_dnssec_zone *zone)
+{
+	ldns_dnssec_zone_print_fmt(out, ldns_output_format_default, zone);
+}
+
+ldns_status
+ldns_dnssec_zone_add_empty_nonterminals(ldns_dnssec_zone *zone)
+{
+	ldns_dnssec_name *new_name;
+	ldns_rdf *cur_name;
+	ldns_rdf *next_name;
+	ldns_rbnode_t *cur_node, *next_node, *new_node;
+
+	/* for the detection */
+	uint16_t i, cur_label_count, next_label_count;
+	uint16_t soa_label_count = 0;
+	ldns_rdf *l1, *l2;
+	int lpos;
+
+	if (!zone) {
+		return LDNS_STATUS_ERR;
+	}
+	if (zone->soa && zone->soa->name) {
+		soa_label_count = ldns_dname_label_count(zone->soa->name);
+	}
+	
+	cur_node = ldns_rbtree_first(zone->names);
+	while (cur_node != LDNS_RBTREE_NULL) {
+		next_node = ldns_rbtree_next(cur_node);
+		
+		/* skip glue */
+		while (next_node != LDNS_RBTREE_NULL && 
+		       next_node->data &&
+		       ((ldns_dnssec_name *)next_node->data)->is_glue
+		) {
+			next_node = ldns_rbtree_next(next_node);
+		}
+
+		if (next_node == LDNS_RBTREE_NULL) {
+			next_node = ldns_rbtree_first(zone->names);
+		}
+
+		cur_name = ((ldns_dnssec_name *)cur_node->data)->name;
+		next_name = ((ldns_dnssec_name *)next_node->data)->name;
+		cur_label_count = ldns_dname_label_count(cur_name);
+		next_label_count = ldns_dname_label_count(next_name);
+
+		/* Since the names are in canonical order, we can
+		 * recognize empty non-terminals by their labels;
+		 * every label after the first one on the next owner
+		 * name is a non-terminal if it either does not exist
+		 * in the current name or is different from the same
+		 * label in the current name (counting from the end)
+		 */
+		for (i = 1; i < next_label_count - soa_label_count; i++) {
+			lpos = (int)cur_label_count - (int)next_label_count + (int)i;
+			if (lpos >= 0) {
+				l1 = ldns_dname_clone_from(cur_name, (uint8_t)lpos);
+			} else {
+				l1 = NULL;
+			}
+			l2 = ldns_dname_clone_from(next_name, i);
+
+			if (!l1 || ldns_dname_compare(l1, l2) != 0) {
+				/* We have an empty nonterminal, add it to the
+				 * tree
+				 */
+				new_name = ldns_dnssec_name_new();
+				if (!new_name) {
+					return LDNS_STATUS_MEM_ERR;
+				}
+				new_name->name = ldns_dname_clone_from(next_name,
+				                                       i);
+				if (!new_name->name) {
+					ldns_dnssec_name_free(new_name);
+					return LDNS_STATUS_MEM_ERR;
+				}
+				new_name->name_alloced = true;
+				new_node = LDNS_MALLOC(ldns_rbnode_t);
+				if (!new_node) {
+					ldns_dnssec_name_free(new_name);
+					return LDNS_STATUS_MEM_ERR;
+				}
+				new_node->key = new_name->name;
+				new_node->data = new_name;
+				(void)ldns_rbtree_insert(zone->names, new_node);
+			}
+			ldns_rdf_deep_free(l1);
+			ldns_rdf_deep_free(l2);
+		}
+		
+		/* we might have inserted a new node after
+		 * the current one so we can't just use next()
+		 */
+		if (next_node != ldns_rbtree_first(zone->names)) {
+			cur_node = next_node;
+		} else {
+			cur_node = LDNS_RBTREE_NULL;
+		}
+	}
+	return LDNS_STATUS_OK;
+}
diff --git a/3rdParty/Ldns/src/src/error.c b/3rdParty/Ldns/src/src/error.c
new file mode 100644
index 0000000..a619502
--- /dev/null
+++ b/3rdParty/Ldns/src/src/error.c
@@ -0,0 +1,107 @@
+/*
+ * a error2str function to make sense of all the
+ * error codes we have laying ardoun
+ *
+ * a Net::DNS like library for C
+ * LibDNS Team @ NLnet Labs
+ * (c) NLnet Labs, 2005-2006
+ * See the file LICENSE for the license
+ */
+
+#include <ldns/config.h>
+
+#include <ldns/ldns.h>
+
+ldns_lookup_table ldns_error_str[] = {
+	{ LDNS_STATUS_OK, "All OK" },
+	{ LDNS_STATUS_EMPTY_LABEL, "Empty label" },
+        { LDNS_STATUS_LABEL_OVERFLOW, "Label length overflow" },
+        { LDNS_STATUS_DOMAINNAME_OVERFLOW, "Domainname length overflow" },
+        { LDNS_STATUS_DOMAINNAME_UNDERFLOW, "Domainname length underflow (zero length)" },
+        { LDNS_STATUS_DDD_OVERFLOW, "\\DDD sequence overflow (>255)" },
+        { LDNS_STATUS_PACKET_OVERFLOW, "Packet size overflow" },
+        { LDNS_STATUS_INVALID_POINTER, "Invalid compression pointer" },
+        { LDNS_STATUS_MEM_ERR, "General memory error" },
+        { LDNS_STATUS_INTERNAL_ERR, "Internal error, this should not happen" },
+        { LDNS_STATUS_SSL_ERR, "Error in SSL library" },
+        { LDNS_STATUS_ERR, "General LDNS error" },
+        { LDNS_STATUS_INVALID_INT, "Conversion error, integer expected" },
+        { LDNS_STATUS_INVALID_IP4, "Conversion error, ip4 addr expected" },
+        { LDNS_STATUS_INVALID_IP6, "Conversion error, ip6 addr expected" },
+        { LDNS_STATUS_INVALID_STR, "Conversion error, string expected" },
+        { LDNS_STATUS_INVALID_B64, "Conversion error, b64 encoding expected" },
+        { LDNS_STATUS_INVALID_HEX, "Conversion error, hex encoding expected" },
+        { LDNS_STATUS_INVALID_TIME, "Conversion error, time encoding expected" },
+        { LDNS_STATUS_NETWORK_ERR, "Could not send or receive, because of network error" },
+        { LDNS_STATUS_ADDRESS_ERR, "Could not start AXFR, because of address error" },
+        { LDNS_STATUS_FILE_ERR, "Could not open the files" },
+        { LDNS_STATUS_UNKNOWN_INET, "Uknown address family" },
+        { LDNS_STATUS_NOT_IMPL, "This function is not implemented (yet), please notify the developers - or not..." },
+	{ LDNS_STATUS_NULL, "Supplied value pointer null" },
+        { LDNS_STATUS_CRYPTO_UNKNOWN_ALGO, "Unknown cryptographic algorithm" },
+        { LDNS_STATUS_CRYPTO_ALGO_NOT_IMPL, "Cryptographic algorithm not implemented" },
+        { LDNS_STATUS_CRYPTO_NO_RRSIG, "No DNSSEC signature(s)" },
+        { LDNS_STATUS_CRYPTO_NO_DNSKEY, "No DNSSEC public key(s)" },
+        { LDNS_STATUS_CRYPTO_TYPE_COVERED_ERR, "The signature does not cover this RRset" },
+        { LDNS_STATUS_CRYPTO_NO_TRUSTED_DNSKEY, "No signatures found for trusted DNSSEC public key(s)" },
+        { LDNS_STATUS_CRYPTO_NO_DS, "No DS record(s)" },
+        { LDNS_STATUS_CRYPTO_NO_TRUSTED_DS, "Could not validate DS record(s)" },
+        { LDNS_STATUS_CRYPTO_NO_MATCHING_KEYTAG_DNSKEY, "No keys with the keytag and algorithm from the RRSIG found" },
+        { LDNS_STATUS_CRYPTO_VALIDATED, "Valid DNSSEC signature" },
+        { LDNS_STATUS_CRYPTO_BOGUS, "Bogus DNSSEC signature" },
+        { LDNS_STATUS_CRYPTO_SIG_EXPIRED, "DNSSEC signature has expired" },
+        { LDNS_STATUS_CRYPTO_SIG_NOT_INCEPTED, "DNSSEC signature not incepted yet" },
+	{ LDNS_STATUS_CRYPTO_TSIG_BOGUS, "Bogus TSIG signature" },
+	{ LDNS_STATUS_CRYPTO_TSIG_ERR, "Could not create TSIG signature" },
+        { LDNS_STATUS_CRYPTO_EXPIRATION_BEFORE_INCEPTION, "DNSSEC signature has expiration date earlier than inception date" },
+	{ LDNS_STATUS_ENGINE_KEY_NOT_LOADED, "Unable to load private key from engine" },
+        { LDNS_STATUS_NSEC3_ERR, "Error in NSEC3 denial of existence proof" },
+	{ LDNS_STATUS_RES_NO_NS, "No (valid) nameservers defined in the resolver" },
+	{ LDNS_STATUS_RES_QUERY, "No correct query given to resolver" },
+	{ LDNS_STATUS_WIRE_INCOMPLETE_HEADER, "header section incomplete" },
+	{ LDNS_STATUS_WIRE_INCOMPLETE_QUESTION, "question section incomplete" },
+	{ LDNS_STATUS_WIRE_INCOMPLETE_ANSWER, "answer section incomplete" },
+	{ LDNS_STATUS_WIRE_INCOMPLETE_AUTHORITY, "authority section incomplete" },
+	{ LDNS_STATUS_WIRE_INCOMPLETE_ADDITIONAL, "additional section incomplete" },
+	{ LDNS_STATUS_NO_DATA, "No data" },
+	{ LDNS_STATUS_CERT_BAD_ALGORITHM, "Bad algorithm type for CERT record" },
+	{ LDNS_STATUS_SYNTAX_TYPE_ERR, "Syntax error, could not parse the RR's type" },
+	{ LDNS_STATUS_SYNTAX_CLASS_ERR, "Syntax error, could not parse the RR's class" },
+	{ LDNS_STATUS_SYNTAX_TTL_ERR, "Syntax error, could not parse the RR's TTL" },
+	{ LDNS_STATUS_SYNTAX_INCLUDE_ERR_NOTIMPL, "Syntax error, $INCLUDE not implemented" },
+	{ LDNS_STATUS_SYNTAX_RDATA_ERR, "Syntax error, could not parse the RR's rdata" },
+	{ LDNS_STATUS_SYNTAX_DNAME_ERR, "Syntax error, could not parse the RR's dname(s)" },
+	{ LDNS_STATUS_SYNTAX_VERSION_ERR, "Syntax error, version mismatch" },
+	{ LDNS_STATUS_SYNTAX_ALG_ERR, "Syntax error, algorithm unknown or non parseable" },
+	{ LDNS_STATUS_SYNTAX_KEYWORD_ERR, "Syntax error, unknown keyword in input" },
+	{ LDNS_STATUS_SYNTAX_ERR, "Syntax error, could not parse the RR" },
+	{ LDNS_STATUS_SYNTAX_EMPTY, "Empty line was returned" },
+	{ LDNS_STATUS_SYNTAX_TTL, "$TTL directive was seen in the zone" },
+	{ LDNS_STATUS_SYNTAX_ORIGIN, "$ORIGIN directive was seen in the zone" },
+	{ LDNS_STATUS_SYNTAX_INCLUDE, "$INCLUDE directive was seen in the zone" },
+	{ LDNS_STATUS_SYNTAX_ITERATIONS_OVERFLOW, "Iterations count for NSEC3 record higher than maximum" },
+	{ LDNS_STATUS_SYNTAX_MISSING_VALUE_ERR, "Syntax error, value expected" },
+	{ LDNS_STATUS_SYNTAX_INTEGER_OVERFLOW, "Syntax error, integer value too large" },
+	{ LDNS_STATUS_SYNTAX_BAD_ESCAPE, "Syntax error, bad escape sequence" },
+	{ LDNS_STATUS_SOCKET_ERROR, "Error creating socket" },
+	{ LDNS_STATUS_DNSSEC_EXISTENCE_DENIED, "Existence denied by NSEC" },
+	{ LDNS_STATUS_DNSSEC_NSEC_RR_NOT_COVERED, "RR not covered by the given NSEC RRs" },
+	{ LDNS_STATUS_DNSSEC_NSEC_WILDCARD_NOT_COVERED, "wildcard not covered by the given NSEC RRs" },
+	{ LDNS_STATUS_DNSSEC_NSEC3_ORIGINAL_NOT_FOUND, "original of NSEC3 hashed name could not be found" },
+	{ LDNS_STATUS_MISSING_RDATA_FIELDS_RRSIG, "The RRSIG has to few rdata fields" },
+	{ LDNS_STATUS_MISSING_RDATA_FIELDS_KEY, "The DNSKEY has to few rdata fields" },
+	{ 0, NULL }
+};
+
+const char *
+ldns_get_errorstr_by_id(ldns_status err)
+{
+        ldns_lookup_table *lt;
+
+        lt = ldns_lookup_by_id(ldns_error_str, err);
+
+        if (lt) {
+                return lt->name;
+        }
+        return NULL;
+}
diff --git a/3rdParty/Ldns/src/src/higher.c b/3rdParty/Ldns/src/src/higher.c
new file mode 100644
index 0000000..c9eb173
--- /dev/null
+++ b/3rdParty/Ldns/src/src/higher.c
@@ -0,0 +1,359 @@
+/*
+ * higher.c
+ *
+ * Specify some higher level functions that would
+ * be usefull to would be developers
+ *
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2004-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+#include <ldns/config.h>
+
+#include <ldns/ldns.h>
+
+#ifdef HAVE_SSL
+#include <openssl/ssl.h>
+#include <openssl/sha.h>
+#endif /* HAVE_SSL */
+
+ldns_rr_list *
+ldns_get_rr_list_addr_by_name(ldns_resolver *res, ldns_rdf *name, ldns_rr_class c, 
+		uint16_t flags)
+{
+	ldns_pkt *pkt;
+	ldns_rr_list *aaaa;
+	ldns_rr_list *a;
+	ldns_rr_list *result = NULL;
+	ldns_rr_list *hostsfilenames;
+	size_t i;
+	uint8_t ip6;
+
+	a = NULL; 
+	aaaa = NULL; 
+	result = NULL;
+
+	if (!res) {
+		return NULL;
+	}
+	if (ldns_rdf_get_type(name) != LDNS_RDF_TYPE_DNAME) {
+		return NULL;
+	}
+
+	ip6 = ldns_resolver_ip6(res); /* we use INET_ANY here, save
+					 what was there */
+
+	ldns_resolver_set_ip6(res, LDNS_RESOLV_INETANY);
+	
+	hostsfilenames = ldns_get_rr_list_hosts_frm_file(NULL);
+	for (i = 0; i < ldns_rr_list_rr_count(hostsfilenames); i++) {
+		if (ldns_rdf_compare(name, 
+					ldns_rr_owner(ldns_rr_list_rr(hostsfilenames, 
+							i))) == 0) {
+			if (!result) {
+				result = ldns_rr_list_new();
+			}
+			ldns_rr_list_push_rr(result, 
+					ldns_rr_clone(ldns_rr_list_rr(hostsfilenames, i)));
+		}
+	}
+	ldns_rr_list_deep_free(hostsfilenames);
+
+	if (result) {
+		return result;
+	}
+
+	/* add the RD flags, because we want an answer */
+	pkt = ldns_resolver_query(res, name, LDNS_RR_TYPE_AAAA, c, flags | LDNS_RD);
+	if (pkt) {
+		/* extract the data we need */
+		aaaa = ldns_pkt_rr_list_by_type(pkt, LDNS_RR_TYPE_AAAA, 
+			LDNS_SECTION_ANSWER);
+		ldns_pkt_free(pkt);
+	} 
+
+	pkt = ldns_resolver_query(res, name, LDNS_RR_TYPE_A, c, flags | LDNS_RD);
+	if (pkt) {
+		/* extract the data we need */
+		a = ldns_pkt_rr_list_by_type(pkt, LDNS_RR_TYPE_A, LDNS_SECTION_ANSWER);
+		ldns_pkt_free(pkt);
+	} 
+	ldns_resolver_set_ip6(res, ip6);
+
+	if (aaaa && a) {
+		result = ldns_rr_list_cat_clone(aaaa, a);
+		ldns_rr_list_deep_free(aaaa);
+		ldns_rr_list_deep_free(a);
+		return result;
+	}
+	
+	if (aaaa) {
+		result = ldns_rr_list_clone(aaaa);
+	}
+	
+	if (a) {
+		result = ldns_rr_list_clone(a);
+	}
+
+	ldns_rr_list_deep_free(aaaa);
+	ldns_rr_list_deep_free(a);
+	return result;
+}
+
+ldns_rr_list *
+ldns_get_rr_list_name_by_addr(ldns_resolver *res, ldns_rdf *addr, ldns_rr_class c, 
+		uint16_t flags)
+{
+	ldns_pkt *pkt;
+	ldns_rr_list *names;
+	ldns_rdf *name;
+
+	names = NULL;
+
+	if (!res || !addr) {
+		return NULL;
+	}
+
+	if (ldns_rdf_get_type(addr) != LDNS_RDF_TYPE_A &&
+			ldns_rdf_get_type(addr) != LDNS_RDF_TYPE_AAAA) {
+		return NULL;
+	}
+
+	name = ldns_rdf_address_reverse(addr);
+	
+	/* add the RD flags, because we want an answer */
+	pkt = ldns_resolver_query(res, name, LDNS_RR_TYPE_PTR, c, flags | LDNS_RD);
+	if (pkt) {
+		/* extract the data we need */
+		names = ldns_pkt_rr_list_by_type(pkt, 
+				LDNS_RR_TYPE_PTR, LDNS_SECTION_ANSWER);
+	}
+	return names;
+}
+
+/* read a line, put it in a buffer, parse the buffer */
+ldns_rr_list *
+ldns_get_rr_list_hosts_frm_fp(FILE *fp)
+{
+	return ldns_get_rr_list_hosts_frm_fp_l(fp, NULL);
+}
+
+ldns_rr_list *
+ldns_get_rr_list_hosts_frm_fp_l(FILE *fp, int *line_nr)
+{
+	ssize_t i, j;
+	size_t cnt;
+	char *line;
+	char *word;
+	char *addr;
+	char *rr_str;
+	ldns_buffer *linebuf;
+	ldns_rr *rr;
+	ldns_rr_list *list;
+	ldns_rdf *tmp;
+	bool ip6;
+	ldns_status parse_result;
+
+	line = LDNS_XMALLOC(char, LDNS_MAX_LINELEN + 1);
+	word = LDNS_XMALLOC(char, LDNS_MAX_LINELEN + 1);
+	addr = LDNS_XMALLOC(char, LDNS_MAX_LINELEN + 1);
+	rr_str = LDNS_XMALLOC(char, LDNS_MAX_LINELEN + 1);
+	ip6 = false;
+	list = ldns_rr_list_new();
+	rr = NULL;
+	if(!line || !word || !addr || !rr_str || !list) {
+		LDNS_FREE(line);
+		LDNS_FREE(word);
+		LDNS_FREE(addr);
+		LDNS_FREE(rr_str);
+		ldns_rr_list_free(list);
+		return NULL;
+	}
+
+	for(i = ldns_fget_token_l(fp, line, "\n", LDNS_MAX_LINELEN, line_nr);
+			i > 0; i = ldns_fget_token_l(fp, line, "\n", LDNS_MAX_LINELEN, line_nr)) {
+		/* # is comment */
+		if (line[0] == '#') {
+			continue;
+		}
+		/* put it in a buffer for further processing */
+		linebuf = LDNS_MALLOC(ldns_buffer);
+		if(!linebuf) {
+			LDNS_FREE(line);
+			LDNS_FREE(word);
+			LDNS_FREE(addr);
+			LDNS_FREE(rr_str);
+			ldns_rr_list_deep_free(list);
+			return NULL;
+		}
+
+		ldns_buffer_new_frm_data(linebuf, line, (size_t) i);
+		for(cnt = 0, j = ldns_bget_token(linebuf, word, LDNS_PARSE_NO_NL, LDNS_MAX_LINELEN);
+				j > 0;
+				j = ldns_bget_token(linebuf, word, LDNS_PARSE_NO_NL, LDNS_MAX_LINELEN), cnt++) {
+			if (cnt == 0) {
+				/* the address */
+				if ((tmp = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_AAAA, 
+								word))) {
+					/* ip6 */
+					ldns_rdf_deep_free(tmp);
+					ip6 = true;
+				} else {
+					if ((tmp = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_A, 
+									word))) {
+						/* ip4 */
+						ldns_rdf_deep_free(tmp);
+						ip6 = false;
+					} else {
+						/* kaput */
+						break;
+					}
+				}
+				(void)strlcpy(addr, word, LDNS_MAX_LINELEN+1);
+			} else {
+				/* la al la la */
+				if (ip6) {
+					snprintf(rr_str, LDNS_MAX_LINELEN, 
+						"%s IN AAAA %s", word, addr);
+				} else {
+					snprintf(rr_str, LDNS_MAX_LINELEN, 
+						"%s IN A %s", word, addr);
+				}
+				parse_result = ldns_rr_new_frm_str(&rr, rr_str, 0, NULL, NULL);
+				if (parse_result == LDNS_STATUS_OK && ldns_rr_owner(rr) && ldns_rr_rd_count(rr) > 0) {
+					ldns_rr_list_push_rr(list, ldns_rr_clone(rr));
+				}
+				ldns_rr_free(rr);
+			}
+		}
+		ldns_buffer_free(linebuf);
+	}
+	LDNS_FREE(line);
+	LDNS_FREE(word);
+	LDNS_FREE(addr);
+	LDNS_FREE(rr_str);
+	return list;
+}
+
+ldns_rr_list *
+ldns_get_rr_list_hosts_frm_file(char *filename)
+{
+	ldns_rr_list *names;
+	FILE *fp;
+
+	if (!filename) {
+                fp = fopen(LDNS_RESOLV_HOSTS, "r");
+        
+        } else {
+                fp = fopen(filename, "r");
+        }
+        if (!fp) {
+                return NULL;
+        }
+
+	names = ldns_get_rr_list_hosts_frm_fp(fp);
+	fclose(fp);
+	return names;
+}
+
+uint16_t
+ldns_getaddrinfo(ldns_resolver *res, ldns_rdf *node, ldns_rr_class c, 
+		ldns_rr_list **ret)
+{
+	ldns_rdf_type t;
+	uint16_t names_found;
+	ldns_resolver *r;
+	ldns_status s;
+
+	t = ldns_rdf_get_type(node);
+	names_found = 0;
+	r = res;
+
+	if (res == NULL) {
+		/* prepare a new resolver, using /etc/resolv.conf as a guide  */
+		s = ldns_resolver_new_frm_file(&r, NULL);
+		if (s != LDNS_STATUS_OK) {
+			return 0;
+		} 
+	}
+
+	if (t == LDNS_RDF_TYPE_DNAME) {
+		/* we're asked to query for a name */
+		*ret = ldns_get_rr_list_addr_by_name(r, node, c, 0);
+		names_found = ldns_rr_list_rr_count(*ret);
+	}
+
+	if (t == LDNS_RDF_TYPE_A || t == LDNS_RDF_TYPE_AAAA) {
+		/* an address */
+		*ret = ldns_get_rr_list_name_by_addr(r, node, c, 0);
+		names_found = ldns_rr_list_rr_count(*ret);
+	}
+
+	if (res == NULL) {
+		ldns_resolver_deep_free(r);
+	}
+	
+	return names_found;
+}
+
+bool
+ldns_nsec_type_check(ldns_rr *nsec, ldns_rr_type t)
+{
+	/* does the nsec cover the t given? */
+	/* copied from host2str.c line 465: ldns_rdf2buffer_str_nsec */
+        uint8_t window_block_nr;
+        uint8_t bitmap_length;
+        uint16_t type;
+        uint16_t pos = 0;
+        uint16_t bit_pos;
+	ldns_rdf *nsec_type_list = ldns_rr_rdf(nsec, 1); 
+	uint8_t *data;
+	
+	if (nsec_type_list == NULL) {
+		return false;
+	}
+	data  = ldns_rdf_data(nsec_type_list);
+
+	while(pos < ldns_rdf_size(nsec_type_list)) {
+		window_block_nr = data[pos];
+		bitmap_length = data[pos + 1];
+		pos += 2;
+
+		for (bit_pos = 0; bit_pos < (bitmap_length) * 8; bit_pos++) {
+			if (ldns_get_bit(&data[pos], bit_pos)) {
+				type = 256 * (uint16_t) window_block_nr + bit_pos;
+
+				if ((ldns_rr_type)type == t) {
+					/* we have a winner */
+					return true;
+				}
+			}
+		}
+		pos += (uint16_t) bitmap_length;
+	}
+	return false;
+}
+
+void
+ldns_print_rr_rdf(FILE *fp, ldns_rr *r, int rdfnum, ...)
+{
+	int16_t rdf;
+	ldns_rdf *rd;
+	va_list va_rdf;
+	va_start(va_rdf, rdfnum);
+
+	for (rdf = (int16_t)rdfnum; rdf != -1; rdf = (int16_t)va_arg(va_rdf, int)) 
+	{
+		rd = ldns_rr_rdf(r, rdf);
+		if (!rd) {
+			continue;
+		} else {
+			ldns_rdf_print(fp, rd);
+			fprintf(fp, " "); /* not sure if we want to do this */
+		}
+	}
+	va_end(va_rdf);
+}
diff --git a/3rdParty/Ldns/src/src/host2str.c b/3rdParty/Ldns/src/src/host2str.c
new file mode 100644
index 0000000..eff1216
--- /dev/null
+++ b/3rdParty/Ldns/src/src/host2str.c
@@ -0,0 +1,2311 @@
+/*
+ * host2str.c
+ *
+ * conversion routines from the host format
+ * to the presentation format (strings)
+ *
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2004-2006
+ *
+ * See the file LICENSE for the license
+ */
+#include <ldns/config.h>
+
+#include <ldns/ldns.h>
+
+#include <limits.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#include <time.h>
+#include <sys/time.h>
+
+#ifndef INET_ADDRSTRLEN
+#define INET_ADDRSTRLEN 16
+#endif
+#ifndef INET6_ADDRSTRLEN
+#define INET6_ADDRSTRLEN 46
+#endif
+
+/* lookup tables for standard DNS stuff  */
+
+/* Taken from RFC 2535, section 7.  */
+ldns_lookup_table ldns_algorithms[] = {
+        { LDNS_RSAMD5, "RSAMD5" },
+        { LDNS_DH, "DH" },
+        { LDNS_DSA, "DSA" },
+        { LDNS_ECC, "ECC" },
+        { LDNS_RSASHA1, "RSASHA1" },
+        { LDNS_DSA_NSEC3, "DSA-NSEC3-SHA1" },
+        { LDNS_RSASHA1_NSEC3, "RSASHA1-NSEC3-SHA1" },
+#ifdef USE_SHA2
+	{ LDNS_RSASHA256, "RSASHA256"},
+	{ LDNS_RSASHA512, "RSASHA512"},
+#endif
+#ifdef USE_GOST
+	{ LDNS_ECC_GOST, "ECC-GOST"},
+#endif
+#ifdef USE_ECDSA
+        { LDNS_ECDSAP256SHA256, "ECDSAP256SHA256"},
+        { LDNS_ECDSAP384SHA384, "ECDSAP384SHA384"},
+#endif
+        { LDNS_INDIRECT, "INDIRECT" },
+        { LDNS_PRIVATEDNS, "PRIVATEDNS" },
+        { LDNS_PRIVATEOID, "PRIVATEOID" },
+        { 0, NULL }
+};
+
+/* Taken from RFC 4398  */
+ldns_lookup_table ldns_cert_algorithms[] = {
+        { LDNS_CERT_PKIX, "PKIX" },
+        { LDNS_CERT_SPKI, "SPKI" },
+        { LDNS_CERT_PGP, "PGP" },
+        { LDNS_CERT_IPKIX, "IPKIX" },
+        { LDNS_CERT_ISPKI, "ISPKI" },
+        { LDNS_CERT_IPGP, "IPGP" },
+        { LDNS_CERT_ACPKIX, "ACPKIX" },
+        { LDNS_CERT_IACPKIX, "IACPKIX" },
+        { LDNS_CERT_URI, "URI" },
+        { LDNS_CERT_OID, "OID" },
+        { 0, NULL }
+};
+
+/* classes  */
+ldns_lookup_table ldns_rr_classes[] = {
+        { LDNS_RR_CLASS_IN, "IN" },
+        { LDNS_RR_CLASS_CH, "CH" },
+        { LDNS_RR_CLASS_HS, "HS" },
+        { LDNS_RR_CLASS_NONE, "NONE" },
+        { LDNS_RR_CLASS_ANY, "ANY" },
+        { 0, NULL }
+};
+
+/* if these are used elsewhere */
+ldns_lookup_table ldns_rcodes[] = {
+        { LDNS_RCODE_NOERROR, "NOERROR" },
+        { LDNS_RCODE_FORMERR, "FORMERR" },
+        { LDNS_RCODE_SERVFAIL, "SERVFAIL" },
+        { LDNS_RCODE_NXDOMAIN, "NXDOMAIN" },
+        { LDNS_RCODE_NOTIMPL, "NOTIMPL" },
+        { LDNS_RCODE_REFUSED, "REFUSED" },
+        { LDNS_RCODE_YXDOMAIN, "YXDOMAIN" },
+        { LDNS_RCODE_YXRRSET, "YXRRSET" },
+        { LDNS_RCODE_NXRRSET, "NXRRSET" },
+        { LDNS_RCODE_NOTAUTH, "NOTAUTH" },
+        { LDNS_RCODE_NOTZONE, "NOTZONE" },
+        { 0, NULL }
+};
+
+ldns_lookup_table ldns_opcodes[] = {
+        { LDNS_PACKET_QUERY, "QUERY" },
+        { LDNS_PACKET_IQUERY, "IQUERY" },
+        { LDNS_PACKET_STATUS, "STATUS" },
+	{ LDNS_PACKET_NOTIFY, "NOTIFY" },
+	{ LDNS_PACKET_UPDATE, "UPDATE" },
+        { 0, NULL }
+};
+
+const ldns_output_format   ldns_output_format_nocomments_record = { 0, NULL };
+const ldns_output_format  *ldns_output_format_nocomments 
+			= &ldns_output_format_nocomments_record;
+const ldns_output_format   ldns_output_format_onlykeyids_record = {
+	LDNS_COMMENT_KEY, NULL
+};
+const ldns_output_format  *ldns_output_format_onlykeyids
+			= &ldns_output_format_onlykeyids_record;
+const ldns_output_format  *ldns_output_format_default
+			= &ldns_output_format_onlykeyids_record;
+const ldns_output_format   ldns_output_format_bubblebabble_record = { 
+	LDNS_COMMENT_KEY | LDNS_COMMENT_BUBBLEBABBLE | LDNS_COMMENT_FLAGS, NULL
+};
+const ldns_output_format  *ldns_output_format_bubblebabble 
+			= &ldns_output_format_bubblebabble_record;
+
+ldns_status
+ldns_pkt_opcode2buffer_str(ldns_buffer *output, ldns_pkt_opcode opcode)
+{
+	ldns_lookup_table *lt = ldns_lookup_by_id(ldns_opcodes, opcode);
+	if (lt && lt->name) {
+		ldns_buffer_printf(output, "%s", lt->name);
+	} else {
+		ldns_buffer_printf(output, "OPCODE%u", opcode);
+	}
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_pkt_rcode2buffer_str(ldns_buffer *output, ldns_pkt_rcode rcode)
+{
+	ldns_lookup_table *lt = ldns_lookup_by_id(ldns_rcodes, rcode);
+	if (lt && lt->name) {
+		ldns_buffer_printf(output, "%s", lt->name);
+	} else {
+		ldns_buffer_printf(output, "RCODE%u", rcode);
+	}
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_algorithm2buffer_str(ldns_buffer *output,
+                          ldns_algorithm algorithm)
+{
+	ldns_lookup_table *lt = ldns_lookup_by_id(ldns_algorithms,
+	                                          algorithm);
+	if (lt && lt->name) {
+		ldns_buffer_printf(output, "%s", lt->name);
+	} else {
+		ldns_buffer_printf(output, "ALG%u", algorithm);
+	}
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_cert_algorithm2buffer_str(ldns_buffer *output,
+                               ldns_cert_algorithm cert_algorithm)
+{
+	ldns_lookup_table *lt = ldns_lookup_by_id(ldns_cert_algorithms,
+	                                          cert_algorithm);
+	if (lt && lt->name) {
+		ldns_buffer_printf(output, "%s", lt->name);
+	} else {
+		ldns_buffer_printf(output, "CERT_ALG%u",
+		                   cert_algorithm);
+	}
+	return ldns_buffer_status(output);
+}
+
+char *
+ldns_pkt_opcode2str(ldns_pkt_opcode opcode)
+{
+	char *str;
+	ldns_buffer *buf;
+
+	buf = ldns_buffer_new(12);
+	if (!buf) {
+		return NULL;
+	}
+
+	str = NULL;
+	if (ldns_pkt_opcode2buffer_str(buf, opcode) == LDNS_STATUS_OK) {
+		str = ldns_buffer2str(buf);
+	}
+
+	ldns_buffer_free(buf);
+	return str;
+}
+
+char *
+ldns_pkt_rcode2str(ldns_pkt_rcode rcode)
+{
+	char *str;
+	ldns_buffer *buf;
+
+	buf = ldns_buffer_new(10);
+	if (!buf) {
+		return NULL;
+	}
+
+	str = NULL;
+	if (ldns_pkt_rcode2buffer_str(buf, rcode) == LDNS_STATUS_OK) {
+		str = ldns_buffer2str(buf);
+	}
+
+	ldns_buffer_free(buf);
+	return str;
+}
+
+char *
+ldns_pkt_algorithm2str(ldns_algorithm algorithm)
+{
+	char *str;
+	ldns_buffer *buf;
+
+	buf = ldns_buffer_new(10);
+	if (!buf) {
+		return NULL;
+	}
+
+	str = NULL;
+	if (ldns_algorithm2buffer_str(buf, algorithm)
+	    == LDNS_STATUS_OK) {
+		str = ldns_buffer2str(buf);
+	}
+
+	ldns_buffer_free(buf);
+	return str;
+}
+
+char *
+ldns_pkt_cert_algorithm2str(ldns_cert_algorithm cert_algorithm)
+{
+	char *str;
+	ldns_buffer *buf;
+
+	buf = ldns_buffer_new(10);
+	if (!buf) {
+		return NULL;
+	}
+
+	str = NULL;
+	if (ldns_cert_algorithm2buffer_str(buf, cert_algorithm)
+	    == LDNS_STATUS_OK) {
+		str = ldns_buffer2str(buf);
+	}
+
+	ldns_buffer_free(buf);
+	return str;
+}
+
+
+/* do NOT pass compressed data here :p */
+ldns_status
+ldns_rdf2buffer_str_dname(ldns_buffer *output, const ldns_rdf *dname)
+{
+	/* can we do with 1 pos var? or without at all? */
+	uint8_t src_pos = 0;
+	uint8_t len;
+	uint8_t *data;
+	uint8_t i;
+	unsigned char c;
+
+	data = (uint8_t*)ldns_rdf_data(dname);
+	len = data[src_pos];
+
+	if (ldns_rdf_size(dname) > LDNS_MAX_DOMAINLEN) {
+		/* too large, return */
+		return LDNS_STATUS_DOMAINNAME_OVERFLOW;
+	}
+
+	/* special case: root label */
+	if (1 == ldns_rdf_size(dname)) {
+		ldns_buffer_printf(output, ".");
+	} else {
+		while ((len > 0) && src_pos < ldns_rdf_size(dname)) {
+			src_pos++;
+			for(i = 0; i < len; i++) {
+				/* paranoia check for various 'strange'
+				   characters in dnames
+				*/
+				c = (unsigned char) data[src_pos];
+				if(c == '.' || c == ';' ||
+				   c == '(' || c == ')' ||
+				   c == '\\') {
+					ldns_buffer_printf(output, "\\%c",
+							data[src_pos]);
+				} else if (!(isascii(c) && isgraph(c))) {
+					ldns_buffer_printf(output, "\\%03u",
+						        data[src_pos]);
+				} else {
+					ldns_buffer_printf(output, "%c", data[src_pos]);
+				}
+				src_pos++;
+			}
+
+			if (src_pos < ldns_rdf_size(dname)) {
+				ldns_buffer_printf(output, ".");
+			}
+			len = data[src_pos];
+		}
+	}
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_rdf2buffer_str_int8(ldns_buffer *output, const ldns_rdf *rdf)
+{
+	uint8_t data = ldns_rdf_data(rdf)[0];
+	ldns_buffer_printf(output, "%lu", (unsigned long) data);
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_rdf2buffer_str_int16(ldns_buffer *output, const ldns_rdf *rdf)
+{
+	uint16_t data = ldns_read_uint16(ldns_rdf_data(rdf));
+	ldns_buffer_printf(output, "%lu", (unsigned long) data);
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_rdf2buffer_str_int32(ldns_buffer *output, const ldns_rdf *rdf)
+{
+	uint32_t data = ldns_read_uint32(ldns_rdf_data(rdf));
+	ldns_buffer_printf(output, "%lu", (unsigned long) data);
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_rdf2buffer_str_time(ldns_buffer *output, const ldns_rdf *rdf)
+{
+	/* create a YYYYMMDDHHMMSS string if possible */
+	struct tm tm;
+	char date_buf[16];
+
+	memset(&tm, 0, sizeof(tm));
+	if (ldns_serial_arithmitics_gmtime_r(ldns_rdf2native_int32(rdf), time(NULL), &tm)
+	    && strftime(date_buf, 15, "%Y%m%d%H%M%S", &tm)) {
+		ldns_buffer_printf(output, "%s", date_buf);
+	}
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_rdf2buffer_str_a(ldns_buffer *output, const ldns_rdf *rdf)
+{
+	char str[INET_ADDRSTRLEN];
+
+	if (inet_ntop(AF_INET, ldns_rdf_data(rdf), str, INET_ADDRSTRLEN)) {
+		ldns_buffer_printf(output, "%s", str);
+	}
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_rdf2buffer_str_aaaa(ldns_buffer *output, const ldns_rdf *rdf)
+{
+	char str[INET6_ADDRSTRLEN];
+
+	if (inet_ntop(AF_INET6, ldns_rdf_data(rdf), str, INET6_ADDRSTRLEN)) {
+		ldns_buffer_printf(output, "%s", str);
+	}
+
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_rdf2buffer_str_str(ldns_buffer *output, const ldns_rdf *rdf)
+{
+	const uint8_t *data = ldns_rdf_data(rdf);
+	uint8_t length = data[0];
+	size_t i;
+
+	ldns_buffer_printf(output, "\"");
+	for (i = 1; i <= length; ++i) {
+		char ch = (char) data[i];
+		if (isprint((int)ch) || ch=='\t') {
+			if (ch=='\"'||ch=='\\')
+				ldns_buffer_printf(output, "\\%c", ch);
+			else
+				ldns_buffer_printf(output, "%c", ch);
+		} else {
+			ldns_buffer_printf(output, "\\%03u",
+                                (unsigned)(uint8_t) ch);
+		}
+	}
+	ldns_buffer_printf(output, "\"");
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_rdf2buffer_str_b64(ldns_buffer *output, const ldns_rdf *rdf)
+{
+	size_t size = ldns_b64_ntop_calculate_size(ldns_rdf_size(rdf));
+	char *b64 = LDNS_XMALLOC(char, size);
+	if(!b64) return LDNS_STATUS_MEM_ERR;
+	if (ldns_b64_ntop(ldns_rdf_data(rdf), ldns_rdf_size(rdf), b64, size)) {
+		ldns_buffer_printf(output, "%s", b64);
+	}
+	LDNS_FREE(b64);
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_rdf2buffer_str_b32_ext(ldns_buffer *output, const ldns_rdf *rdf)
+{
+	size_t size;
+	char *b32;
+	if(ldns_rdf_size(rdf) == 0)
+		return LDNS_STATUS_OK;
+        /* remove -1 for the b32-hash-len octet */
+	size = ldns_b32_ntop_calculate_size(ldns_rdf_size(rdf) - 1);
+        /* add one for the end nul for the string */
+	b32 = LDNS_XMALLOC(char, size + 1);
+	if(!b32) return LDNS_STATUS_MEM_ERR;
+	size = (size_t) ldns_b32_ntop_extended_hex(ldns_rdf_data(rdf) + 1,
+		ldns_rdf_size(rdf) - 1, b32, size+1);
+	if (size > 0) {
+		ldns_buffer_printf(output, "%s", b32);
+	}
+	LDNS_FREE(b32);
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_rdf2buffer_str_hex(ldns_buffer *output, const ldns_rdf *rdf)
+{
+	size_t i;
+	for (i = 0; i < ldns_rdf_size(rdf); i++) {
+		ldns_buffer_printf(output, "%02x", ldns_rdf_data(rdf)[i]);
+	}
+
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_rdf2buffer_str_type(ldns_buffer *output, const ldns_rdf *rdf)
+{
+        uint16_t data = ldns_read_uint16(ldns_rdf_data(rdf));
+	const ldns_rr_descriptor *descriptor;
+
+	descriptor = ldns_rr_descript(data);
+	if (descriptor && descriptor->_name) {
+		ldns_buffer_printf(output, "%s", descriptor->_name);
+	} else {
+		ldns_buffer_printf(output, "TYPE%u", data);
+	}
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_rdf2buffer_str_class(ldns_buffer *output, const ldns_rdf *rdf)
+{
+	uint16_t data = ldns_read_uint16(ldns_rdf_data(rdf));
+	ldns_lookup_table *lt;
+
+ 	lt = ldns_lookup_by_id(ldns_rr_classes, (int) data);
+	if (lt) {
+		ldns_buffer_printf(output, "\t%s", lt->name);
+	} else {
+		ldns_buffer_printf(output, "\tCLASS%d", data);
+	}
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_rdf2buffer_str_cert_alg(ldns_buffer *output, const ldns_rdf *rdf)
+{
+        uint16_t data = ldns_read_uint16(ldns_rdf_data(rdf));
+	ldns_lookup_table *lt;
+ 	lt = ldns_lookup_by_id(ldns_cert_algorithms, (int) data);
+	if (lt) {
+		ldns_buffer_printf(output, "%s", lt->name);
+	} else {
+		ldns_buffer_printf(output, "%d", data);
+	}
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_rdf2buffer_str_alg(ldns_buffer *output, const ldns_rdf *rdf)
+{
+	/* don't use algorithm mnemonics in the presentation format
+	   this kind of got sneaked into the rfc's */
+        uint8_t data = ldns_rdf_data(rdf)[0];
+		ldns_buffer_printf(output, "%d", data);
+	return ldns_buffer_status(output);
+}
+
+static void
+loc_cm_print(ldns_buffer *output, uint8_t mantissa, uint8_t exponent)
+{
+	uint8_t i;
+	/* is it 0.<two digits> ? */
+	if(exponent < 2) {
+		if(exponent == 1)
+			mantissa *= 10;
+		ldns_buffer_printf(output, "0.%02ld", (long)mantissa);
+		return;
+	}
+	/* always <digit><string of zeros> */
+	ldns_buffer_printf(output, "%d", (int)mantissa);
+	for(i=0; i<exponent-2; i++)
+		ldns_buffer_printf(output, "0");
+}
+
+ldns_status
+ldns_rr_type2buffer_str(ldns_buffer *output, const ldns_rr_type type)
+{
+	const ldns_rr_descriptor *descriptor;
+
+	descriptor = ldns_rr_descript(type);
+
+	if (descriptor && descriptor->_name) {
+		ldns_buffer_printf(output, "%s", descriptor->_name);
+	} else {
+		/* exceptions for pseudotypes */
+		switch (type) {
+			case LDNS_RR_TYPE_IXFR:
+				ldns_buffer_printf(output, "IXFR");
+				break;
+			case LDNS_RR_TYPE_AXFR:
+				ldns_buffer_printf(output, "AXFR");
+				break;
+			case LDNS_RR_TYPE_MAILA:
+				ldns_buffer_printf(output, "MAILA");
+				break;
+			case LDNS_RR_TYPE_MAILB:
+				ldns_buffer_printf(output, "MAILB");
+				break;
+			case LDNS_RR_TYPE_ANY:
+				ldns_buffer_printf(output, "ANY");
+				break;
+			default:
+				ldns_buffer_printf(output, "TYPE%u", type);
+		}
+	}
+	return ldns_buffer_status(output);
+}
+
+char *
+ldns_rr_type2str(const ldns_rr_type type)
+{
+	char *str;
+	ldns_buffer *buf;
+
+	buf = ldns_buffer_new(10);
+	if (!buf) {
+		return NULL;
+	}
+
+	str = NULL;
+	if (ldns_rr_type2buffer_str(buf, type) == LDNS_STATUS_OK) {
+		str = ldns_buffer2str(buf);
+	}
+
+	ldns_buffer_free(buf);
+	return str;
+}
+
+
+ldns_status
+ldns_rr_class2buffer_str(ldns_buffer *output,
+                         const ldns_rr_class klass)
+{
+	ldns_lookup_table *lt;
+
+	lt = ldns_lookup_by_id(ldns_rr_classes, klass);
+	if (lt) {
+		ldns_buffer_printf(output, "%s", lt->name);
+	} else {
+		ldns_buffer_printf(output, "CLASS%d", klass);
+	}
+	return ldns_buffer_status(output);
+}
+
+char *
+ldns_rr_class2str(const ldns_rr_class klass)
+{
+	ldns_buffer *buf;
+	char *str;
+
+	buf = ldns_buffer_new(10);
+	if (!buf) {
+		return NULL;
+	}
+
+	str = NULL;
+	if (ldns_rr_class2buffer_str(buf, klass) == LDNS_STATUS_OK) {
+		str = ldns_buffer2str(buf);
+	}
+	ldns_buffer_free(buf);
+	return str;
+}
+
+ldns_status
+ldns_rdf2buffer_str_loc(ldns_buffer *output, const ldns_rdf *rdf)
+{
+	/* we could do checking (ie degrees < 90 etc)? */
+	uint8_t version = ldns_rdf_data(rdf)[0];
+	uint8_t size;
+	uint8_t horizontal_precision;
+	uint8_t vertical_precision;
+	uint32_t longitude;
+	uint32_t latitude;
+	uint32_t altitude;
+	char northerness;
+	char easterness;
+	uint32_t h;
+	uint32_t m;
+	double s;
+
+	uint32_t equator = (uint32_t) ldns_power(2, 31);
+
+	if (version == 0) {
+		size = ldns_rdf_data(rdf)[1];
+		horizontal_precision = ldns_rdf_data(rdf)[2];
+		vertical_precision = ldns_rdf_data(rdf)[3];
+
+		latitude = ldns_read_uint32(&ldns_rdf_data(rdf)[4]);
+		longitude = ldns_read_uint32(&ldns_rdf_data(rdf)[8]);
+		altitude = ldns_read_uint32(&ldns_rdf_data(rdf)[12]);
+
+		if (latitude > equator) {
+			northerness = 'N';
+			latitude = latitude - equator;
+		} else {
+			northerness = 'S';
+			latitude = equator - latitude;
+		}
+		h = latitude / (1000 * 60 * 60);
+		latitude = latitude % (1000 * 60 * 60);
+		m = latitude / (1000 * 60);
+		latitude = latitude % (1000 * 60);
+		s = (double) latitude / 1000.0;
+		ldns_buffer_printf(output, "%02u %02u %0.3f %c ",
+			h, m, s, northerness);
+
+		if (longitude > equator) {
+			easterness = 'E';
+			longitude = longitude - equator;
+		} else {
+			easterness = 'W';
+			longitude = equator - longitude;
+		}
+		h = longitude / (1000 * 60 * 60);
+		longitude = longitude % (1000 * 60 * 60);
+		m = longitude / (1000 * 60);
+		longitude = longitude % (1000 * 60);
+		s = (double) longitude / (1000.0);
+		ldns_buffer_printf(output, "%02u %02u %0.3f %c ",
+			h, m, s, easterness);
+
+
+        s = ((double) altitude) / 100;
+        s -= 100000;
+
+		if(altitude%100 != 0)
+			ldns_buffer_printf(output, "%.2f", s);
+        else
+			ldns_buffer_printf(output, "%.0f", s);
+
+		ldns_buffer_printf(output, "m ");
+
+		loc_cm_print(output, (size & 0xf0) >> 4, size & 0x0f);
+		ldns_buffer_printf(output, "m ");
+
+		loc_cm_print(output, (horizontal_precision & 0xf0) >> 4,
+			horizontal_precision & 0x0f);
+		ldns_buffer_printf(output, "m ");
+
+		loc_cm_print(output, (vertical_precision & 0xf0) >> 4,
+			vertical_precision & 0x0f);
+		ldns_buffer_printf(output, "m");
+
+		return ldns_buffer_status(output);
+	} else {
+		return ldns_rdf2buffer_str_hex(output, rdf);
+	}
+}
+
+ldns_status
+ldns_rdf2buffer_str_unknown(ldns_buffer *output, const ldns_rdf *rdf)
+{
+	ldns_buffer_printf(output, "\\# %u ", ldns_rdf_size(rdf));
+	return ldns_rdf2buffer_str_hex(output, rdf);
+}
+
+ldns_status
+ldns_rdf2buffer_str_nsap(ldns_buffer *output, const ldns_rdf *rdf)
+{
+	ldns_buffer_printf(output, "0x");
+	return ldns_rdf2buffer_str_hex(output, rdf);
+}
+
+ldns_status
+ldns_rdf2buffer_str_atma(ldns_buffer *output, const ldns_rdf *rdf)
+{
+	return ldns_rdf2buffer_str_hex(output, rdf);
+}
+
+ldns_status
+ldns_rdf2buffer_str_wks(ldns_buffer *output, const ldns_rdf *rdf)
+{
+	/* protocol, followed by bitmap of services */
+	struct protoent *protocol;
+	char *proto_name = NULL;
+	uint8_t protocol_nr;
+	struct servent *service;
+	uint16_t current_service;
+
+	protocol_nr = ldns_rdf_data(rdf)[0];
+	protocol = getprotobynumber((int) protocol_nr);
+	if (protocol && (protocol->p_name != NULL)) {
+		proto_name = protocol->p_name;
+		ldns_buffer_printf(output, "%s ", protocol->p_name);
+	} else {
+		ldns_buffer_printf(output, "%u ", protocol_nr);
+	}
+
+#ifdef HAVE_ENDPROTOENT
+	endprotoent();
+#endif
+
+	for (current_service = 0;
+	     current_service < ldns_rdf_size(rdf) * 7; current_service++) {
+		if (ldns_get_bit(&(ldns_rdf_data(rdf)[1]), current_service)) {
+			service = getservbyport((int) htons(current_service),
+			                        proto_name);
+			if (service && service->s_name) {
+				ldns_buffer_printf(output, "%s ", service->s_name);
+			} else {
+				ldns_buffer_printf(output, "%u ", current_service);
+			}
+#ifdef HAVE_ENDSERVENT
+			endservent();
+#endif
+		}
+	}
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_rdf2buffer_str_nsec(ldns_buffer *output, const ldns_rdf *rdf)
+{
+	/* Note: this code is duplicated in higher.c in
+	 * ldns_nsec_type_check() function
+	 */
+	uint8_t window_block_nr;
+	uint8_t bitmap_length;
+	uint16_t type;
+	uint16_t pos = 0;
+	uint16_t bit_pos;
+	uint8_t *data = ldns_rdf_data(rdf);
+	const ldns_rr_descriptor *descriptor;
+
+	while(pos < ldns_rdf_size(rdf)) {
+		window_block_nr = data[pos];
+		bitmap_length = data[pos + 1];
+		pos += 2;
+
+		for (bit_pos = 0; bit_pos < (bitmap_length) * 8; bit_pos++) {
+			if (ldns_get_bit(&data[pos], bit_pos)) {
+				type = 256 * (uint16_t) window_block_nr + bit_pos;
+				descriptor = ldns_rr_descript(type);
+
+				if (descriptor && descriptor->_name) {
+					ldns_buffer_printf(output, "%s ",
+							descriptor->_name);
+				} else {
+					ldns_buffer_printf(output, "TYPE%u ", type);
+				}
+			}
+		}
+
+		pos += (uint16_t) bitmap_length;
+	}
+
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_rdf2buffer_str_nsec3_salt(ldns_buffer *output, const ldns_rdf *rdf)
+{
+	uint8_t salt_length;
+	uint8_t salt_pos;
+
+	uint8_t *data = ldns_rdf_data(rdf);
+
+        if(ldns_rdf_size(rdf) == 0) {
+                output->_status = LDNS_STATUS_ERR;
+	        return ldns_buffer_status(output);
+        }
+	salt_length = data[0];
+	/* from now there are variable length entries so remember pos */
+	if (salt_length == 0 || ((size_t)salt_length)+1 > ldns_rdf_size(rdf)) {
+		ldns_buffer_printf(output, "- ");
+	} else {
+		for (salt_pos = 0; salt_pos < salt_length; salt_pos++) {
+			ldns_buffer_printf(output, "%02x", data[1 + salt_pos]);
+		}
+		ldns_buffer_printf(output, " ");
+	}
+
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_rdf2buffer_str_period(ldns_buffer *output, const ldns_rdf *rdf)
+{
+	/* period is the number of seconds */
+	uint32_t p = ldns_read_uint32(ldns_rdf_data(rdf));
+	ldns_buffer_printf(output, "%u", p);
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_rdf2buffer_str_tsigtime(ldns_buffer *output,const  ldns_rdf *rdf)
+{
+	/* tsigtime is 48 bits network order unsigned integer */
+	uint64_t tsigtime = 0;
+	uint8_t *data = ldns_rdf_data(rdf);
+
+	if (ldns_rdf_size(rdf) != 6) {
+		return LDNS_STATUS_ERR;
+	}
+
+	tsigtime = ldns_read_uint16(data);
+	tsigtime *= 65536;
+	tsigtime += ldns_read_uint16(data+2);
+	tsigtime *= 65536;
+
+	ldns_buffer_printf(output, "%llu ", tsigtime);
+
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_rdf2buffer_str_apl(ldns_buffer *output, const ldns_rdf *rdf)
+{
+	uint8_t *data = ldns_rdf_data(rdf);
+	uint16_t address_family;
+	uint8_t prefix;
+	bool negation;
+	uint8_t adf_length;
+	size_t i;
+	size_t pos = 0;
+
+	while (pos < (unsigned int) ldns_rdf_size(rdf)) {
+                if(pos + 3 >= (unsigned)ldns_rdf_size(rdf))
+                        return LDNS_STATUS_SYNTAX_RDATA_ERR;
+		address_family = ldns_read_uint16(&data[pos]);
+		prefix = data[pos + 2];
+		negation = data[pos + 3] & LDNS_APL_NEGATION;
+		adf_length = data[pos + 3] & LDNS_APL_MASK;
+		if (address_family == LDNS_APL_IP4) {
+			/* check if prefix < 32? */
+			if (negation) {
+				ldns_buffer_printf(output, "!");
+			}
+			ldns_buffer_printf(output, "%u:", address_family);
+			/* address is variable length 0 - 4 */
+			for (i = 0; i < 4; i++) {
+				if (i > 0) {
+					ldns_buffer_printf(output, ".");
+				}
+				if (i < (unsigned short) adf_length) {
+                                        if(pos+i+4 >= ldns_rdf_size(rdf))
+                                                return LDNS_STATUS_SYNTAX_RDATA_ERR;
+					ldns_buffer_printf(output, "%d",
+					                   data[pos + i + 4]);
+				} else {
+					ldns_buffer_printf(output, "0");
+				}
+			}
+			ldns_buffer_printf(output, "/%u ", prefix);
+		} else if (address_family == LDNS_APL_IP6) {
+			/* check if prefix < 128? */
+			if (negation) {
+				ldns_buffer_printf(output, "!");
+			}
+			ldns_buffer_printf(output, "%u:", address_family);
+			/* address is variable length 0 - 16 */
+			for (i = 0; i < 16; i++) {
+				if (i % 2 == 0 && i > 0) {
+					ldns_buffer_printf(output, ":");
+				}
+				if (i < (unsigned short) adf_length) {
+                                        if(pos+i+4 >= ldns_rdf_size(rdf))
+                                                return LDNS_STATUS_SYNTAX_RDATA_ERR;
+					ldns_buffer_printf(output, "%02x",
+					                   data[pos + i + 4]);
+				} else {
+					ldns_buffer_printf(output, "00");
+				}
+			}
+			ldns_buffer_printf(output, "/%u ", prefix);
+
+		} else {
+			/* unknown address family */
+			ldns_buffer_printf(output, "Unknown address family: %u data: ",
+					address_family);
+			for (i = 1; i < (unsigned short) (4 + adf_length); i++) {
+                                if(pos+i >= ldns_rdf_size(rdf))
+                                        return LDNS_STATUS_SYNTAX_RDATA_ERR;
+				ldns_buffer_printf(output, "%02x", data[i]);
+			}
+		}
+		pos += 4 + adf_length;
+	}
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_rdf2buffer_str_int16_data(ldns_buffer *output, const ldns_rdf *rdf)
+{
+	/* Subtract the size (2) of the number that specifies the length */
+	size_t size = ldns_b64_ntop_calculate_size(ldns_rdf_size(rdf) - 2);
+	char *b64 = LDNS_XMALLOC(char, size);
+        if(!b64)
+                return LDNS_STATUS_MEM_ERR;
+
+	ldns_buffer_printf(output, "%u ", ldns_rdf_size(rdf) - 2);
+
+	if (ldns_rdf_size(rdf) > 2 &&
+	    ldns_b64_ntop(ldns_rdf_data(rdf) + 2,
+				   ldns_rdf_size(rdf) - 2,
+				   b64, size)) {
+		ldns_buffer_printf(output, "%s", b64);
+	}
+	LDNS_FREE(b64);
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_rdf2buffer_str_ipseckey(ldns_buffer *output, const ldns_rdf *rdf)
+{
+	/* wire format from
+	   http://www.ietf.org/internet-drafts/draft-ietf-ipseckey-rr-12.txt
+	*/
+	uint8_t *data = ldns_rdf_data(rdf);
+	uint8_t precedence;
+	uint8_t gateway_type;
+	uint8_t algorithm;
+
+	ldns_rdf *gateway = NULL;
+	uint8_t *gateway_data;
+
+	size_t public_key_size;
+	uint8_t *public_key_data;
+	ldns_rdf *public_key;
+
+	size_t offset = 0;
+	ldns_status status;
+
+	precedence = data[0];
+	gateway_type = data[1];
+	algorithm = data[2];
+	offset = 3;
+
+	switch (gateway_type) {
+		case 0:
+			/* no gateway */
+			break;
+		case 1:
+			gateway_data = LDNS_XMALLOC(uint8_t, LDNS_IP4ADDRLEN);
+                        if(!gateway_data)
+                                return LDNS_STATUS_MEM_ERR;
+			memcpy(gateway_data, &data[offset], LDNS_IP4ADDRLEN);
+			gateway = ldns_rdf_new(LDNS_RDF_TYPE_A, LDNS_IP4ADDRLEN , gateway_data);
+			offset += LDNS_IP4ADDRLEN;
+                        if(!gateway) {
+                                LDNS_FREE(gateway_data);
+                                return LDNS_STATUS_MEM_ERR;
+                        }
+			break;
+		case 2:
+			gateway_data = LDNS_XMALLOC(uint8_t, LDNS_IP6ADDRLEN);
+                        if(!gateway_data)
+                                return LDNS_STATUS_MEM_ERR;
+			memcpy(gateway_data, &data[offset], LDNS_IP6ADDRLEN);
+			offset += LDNS_IP6ADDRLEN;
+			gateway =
+				ldns_rdf_new(LDNS_RDF_TYPE_AAAA, LDNS_IP6ADDRLEN, gateway_data);
+                        if(!gateway) {
+                                LDNS_FREE(gateway_data);
+                                return LDNS_STATUS_MEM_ERR;
+                        }
+			break;
+		case 3:
+			status = ldns_wire2dname(&gateway, data, ldns_rdf_size(rdf), &offset);
+                        if(status != LDNS_STATUS_OK)
+                                return status;
+			break;
+		default:
+			/* error? */
+			break;
+	}
+
+	public_key_size = ldns_rdf_size(rdf) - offset;
+	public_key_data = LDNS_XMALLOC(uint8_t, public_key_size);
+        if(!public_key_data) {
+                ldns_rdf_free(gateway);
+                return LDNS_STATUS_MEM_ERR;
+        }
+	memcpy(public_key_data, &data[offset], public_key_size);
+	public_key = ldns_rdf_new(LDNS_RDF_TYPE_B64, public_key_size, public_key_data);
+        if(!public_key) {
+                LDNS_FREE(public_key_data);
+                ldns_rdf_free(gateway);
+                return LDNS_STATUS_MEM_ERR;
+        }
+
+	ldns_buffer_printf(output, "%u %u %u ", precedence, gateway_type, algorithm);
+    if (gateway)
+	  	(void) ldns_rdf2buffer_str(output, gateway);
+	else
+		ldns_buffer_printf(output, ".");
+	ldns_buffer_printf(output, " ");
+	(void) ldns_rdf2buffer_str(output, public_key);
+
+	ldns_rdf_free(gateway);
+	ldns_rdf_free(public_key);
+
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_rdf2buffer_str_tsig(ldns_buffer *output, const ldns_rdf *rdf)
+{
+	/* TSIG RRs have no presentation format, make them #size <data> */
+	return ldns_rdf2buffer_str_unknown(output, rdf);
+}
+
+
+ldns_status
+ldns_rdf2buffer_str(ldns_buffer *buffer, const ldns_rdf *rdf)
+{
+	ldns_status res = LDNS_STATUS_OK;
+
+	/*ldns_buffer_printf(buffer, "%u:", ldns_rdf_get_type(rdf));*/
+	if (rdf) {
+		switch(ldns_rdf_get_type(rdf)) {
+		case LDNS_RDF_TYPE_NONE:
+			break;
+		case LDNS_RDF_TYPE_DNAME:
+			res = ldns_rdf2buffer_str_dname(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_INT8:
+			res = ldns_rdf2buffer_str_int8(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_INT16:
+			res = ldns_rdf2buffer_str_int16(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_INT32:
+			res = ldns_rdf2buffer_str_int32(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_PERIOD:
+			res = ldns_rdf2buffer_str_period(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_TSIGTIME:
+			res = ldns_rdf2buffer_str_tsigtime(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_A:
+			res = ldns_rdf2buffer_str_a(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_AAAA:
+			res = ldns_rdf2buffer_str_aaaa(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_STR:
+			res = ldns_rdf2buffer_str_str(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_APL:
+			res = ldns_rdf2buffer_str_apl(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_B32_EXT:
+			res = ldns_rdf2buffer_str_b32_ext(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_B64:
+			res = ldns_rdf2buffer_str_b64(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_HEX:
+			res = ldns_rdf2buffer_str_hex(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_NSEC:
+			res = ldns_rdf2buffer_str_nsec(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_NSEC3_SALT:
+			res = ldns_rdf2buffer_str_nsec3_salt(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_TYPE:
+			res = ldns_rdf2buffer_str_type(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_CLASS:
+			res = ldns_rdf2buffer_str_class(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_CERT_ALG:
+			res = ldns_rdf2buffer_str_cert_alg(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_ALG:
+			res = ldns_rdf2buffer_str_alg(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_UNKNOWN:
+			res = ldns_rdf2buffer_str_unknown(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_TIME:
+			res = ldns_rdf2buffer_str_time(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_LOC:
+			res = ldns_rdf2buffer_str_loc(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_WKS:
+		case LDNS_RDF_TYPE_SERVICE:
+			res = ldns_rdf2buffer_str_wks(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_NSAP:
+			res = ldns_rdf2buffer_str_nsap(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_ATMA:
+			res = ldns_rdf2buffer_str_atma(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_IPSECKEY:
+			res = ldns_rdf2buffer_str_ipseckey(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_TSIG:
+			res = ldns_rdf2buffer_str_tsig(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_INT16_DATA:
+			res = ldns_rdf2buffer_str_int16_data(buffer, rdf);
+			break;
+		case LDNS_RDF_TYPE_NSEC3_NEXT_OWNER:
+			res = ldns_rdf2buffer_str_b32_ext(buffer, rdf);
+			break;
+		}
+	} else {
+		ldns_buffer_printf(buffer, "(null) ");
+	        res = ldns_buffer_status(buffer);
+	}
+	return res;
+}
+
+ldns_rdf *
+ldns_b32_ext2dname(const ldns_rdf *rdf)
+{
+	size_t size;
+	char *b32;
+	ldns_rdf *out;
+	if(ldns_rdf_size(rdf) == 0)
+		return NULL;
+        /* remove -1 for the b32-hash-len octet */
+	size = ldns_b32_ntop_calculate_size(ldns_rdf_size(rdf) - 1);
+        /* add one for the end nul for the string */
+	b32 = LDNS_XMALLOC(char, size + 2);
+	if (b32) {
+		if (ldns_b32_ntop_extended_hex(ldns_rdf_data(rdf) + 1, 
+				ldns_rdf_size(rdf) - 1, b32, size+1) > 0) {
+			b32[size] = '.';
+			b32[size+1] = '\0';
+			if (ldns_str2rdf_dname(&out, b32) == LDNS_STATUS_OK) {
+				LDNS_FREE(b32);
+				return out;
+			}
+		}
+		LDNS_FREE(b32);
+	}
+	return NULL;
+}
+
+ldns_status
+ldns_rr2buffer_str_fmt(ldns_buffer *output, 
+		const ldns_output_format *fmt, const ldns_rr *rr)
+{
+	uint16_t i, flags;
+	ldns_status status = LDNS_STATUS_OK;
+
+	if (fmt == NULL) {
+		fmt = ldns_output_format_default;
+	}
+	if (!rr) {
+		if (LDNS_COMMENT_NULLS & fmt->flags) {
+			ldns_buffer_printf(output, "; (null)\n");
+		}
+		return ldns_buffer_status(output);
+	}
+	if (ldns_rr_owner(rr)) {
+		status = ldns_rdf2buffer_str_dname(output, ldns_rr_owner(rr));
+	}
+	if (status != LDNS_STATUS_OK) {
+		return status;
+	}
+
+	/* TTL should NOT be printed if it is a question */
+	if (!ldns_rr_is_question(rr)) {
+		ldns_buffer_printf(output, "\t%d", ldns_rr_ttl(rr));
+	}
+
+	ldns_buffer_printf(output, "\t");
+	status = ldns_rr_class2buffer_str(output, ldns_rr_get_class(rr));
+	if (status != LDNS_STATUS_OK) {
+		return status;
+	}
+	ldns_buffer_printf(output, "\t");
+
+	status = ldns_rr_type2buffer_str(output, ldns_rr_get_type(rr));
+	if (status != LDNS_STATUS_OK) {
+		return status;
+	}
+
+	if (ldns_rr_rd_count(rr) > 0) {
+		ldns_buffer_printf(output, "\t");
+	} else if (!ldns_rr_is_question(rr)) {
+		ldns_buffer_printf(output, "\t\\# 0");
+	}
+
+	for (i = 0; i < ldns_rr_rd_count(rr); i++) {
+		/* ldns_rdf2buffer_str handles NULL input fine! */
+		status = ldns_rdf2buffer_str(output, ldns_rr_rdf(rr, i));
+		if(status != LDNS_STATUS_OK)
+			return status;
+		if (i < ldns_rr_rd_count(rr) - 1) {
+			ldns_buffer_printf(output, " ");
+		}
+	}
+	/* per RR special comments - handy for DNSSEC types */
+	/* check to prevent question sec. rr from
+	 * getting here */
+	if (ldns_rr_rd_count(rr) > 0) {
+		switch (ldns_rr_get_type(rr)) {
+			case LDNS_RR_TYPE_DNSKEY:
+				/* if ldns_rr_rd_count(rr) > 0
+				   then ldns_rr_rdf(rr, 0) exists! */
+				if (! (fmt->flags & LDNS_COMMENT_KEY)) {
+					break;
+				}
+				flags = ldns_rdf2native_int16(
+						ldns_rr_rdf(rr, 0));
+				ldns_buffer_printf(output, " ;{");
+				if (fmt->flags & LDNS_COMMENT_KEY_ID) {
+					ldns_buffer_printf(output, "id = %u",
+							(unsigned int)
+							ldns_calc_keytag(rr));
+				}
+				if ((fmt->flags & LDNS_COMMENT_KEY_TYPE)
+						&& (flags & LDNS_KEY_ZONE_KEY)){
+					if (flags & LDNS_KEY_SEP_KEY) {
+						ldns_buffer_printf(
+							output, " (ksk)");
+					}
+					else {
+						ldns_buffer_printf(
+							output, " (zsk)");
+					}
+					if (fmt->flags & LDNS_COMMENT_KEY_SIZE){
+						ldns_buffer_printf(
+							output, ", ");
+					}
+				} else if (fmt->flags
+						& (LDNS_COMMENT_KEY_ID
+						  |LDNS_COMMENT_KEY_SIZE)) {
+					ldns_buffer_printf( output, ", ");
+				}
+				if (fmt->flags & LDNS_COMMENT_KEY_SIZE) {
+					ldns_buffer_printf(output, "size = %db",
+						ldns_rr_dnskey_key_size(rr));
+				}
+				ldns_buffer_printf(output, "}");
+				break;
+			case LDNS_RR_TYPE_RRSIG:
+				if ((fmt->flags & LDNS_COMMENT_KEY)
+						&& (fmt->flags 
+							& LDNS_COMMENT_RRSIGS)
+						&& ldns_rr_rdf(rr, 6) != NULL) {
+					ldns_buffer_printf(output
+						, " ;{id = %d}"
+						, ldns_rdf2native_int16(
+							ldns_rr_rdf(rr, 6)));
+				}
+				break;
+			case LDNS_RR_TYPE_DS:
+				if ((fmt->flags & LDNS_COMMENT_BUBBLEBABBLE)
+						&& ldns_rr_rdf(rr, 3) != NULL) {
+					uint8_t *data = ldns_rdf_data(
+							ldns_rr_rdf(rr, 3));
+					size_t len = ldns_rdf_size(
+							ldns_rr_rdf(rr, 3));
+					char *babble = ldns_bubblebabble(
+							data, len);
+					if(babble) {
+						ldns_buffer_printf(output
+							, " ;{%s}", babble);
+					}
+					LDNS_FREE(babble);
+				}
+				break;
+			case LDNS_RR_TYPE_NSEC3:
+				if (! (fmt->flags & LDNS_COMMENT_FLAGS) &&
+				    ! (fmt->flags & LDNS_COMMENT_NSEC3_CHAIN)) {
+					break;
+				}
+				ldns_buffer_printf(output, " ;{");
+				if ((fmt->flags & LDNS_COMMENT_FLAGS)) {
+					if (ldns_nsec3_optout(rr)) {
+						ldns_buffer_printf(output,
+							" flags: optout");
+					} else {
+						ldns_buffer_printf(output,
+							" flags: -");
+					}
+					if (fmt->flags & LDNS_COMMENT_NSEC3_CHAIN
+							&& fmt->data != NULL) {
+						ldns_buffer_printf(output, ", ");
+					}
+				}
+				if (fmt->flags & LDNS_COMMENT_NSEC3_CHAIN
+						&& fmt->data != NULL) {
+					ldns_rbnode_t *node;
+					ldns_rdf *key = ldns_dname_label(
+							ldns_rr_owner(rr), 0);
+					if (key) {
+				       		node = ldns_rbtree_search(
+							(ldns_rbtree_t *)
+								fmt->data,
+							(void *) key);
+						if (node->data) {
+							ldns_buffer_printf(
+								output,
+							       	"from: ");
+							(void)
+							ldns_rdf2buffer_str(
+								output, 
+								(ldns_rdf *)
+								node->data);
+						}
+						ldns_rdf_free(key);
+					}
+					key = ldns_b32_ext2dname(
+						ldns_nsec3_next_owner(rr));
+					if (key) {
+						node = ldns_rbtree_search(
+							(ldns_rbtree_t *)
+								fmt->data,
+							(void *) key);
+						if (node->data) {
+							ldns_buffer_printf(
+								output,
+								" to: ");
+							(void)
+							ldns_rdf2buffer_str(
+								output, 
+								(ldns_rdf *)
+								node->data);
+						}
+						ldns_rdf_free(key);
+					}
+				}
+				ldns_buffer_printf(output, "}");
+				break;
+			default:
+				break;
+
+		}
+	}
+	/* last */
+	ldns_buffer_printf(output, "\n");
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_rr2buffer_str(ldns_buffer *output, const ldns_rr *rr)
+{
+	return ldns_rr2buffer_str_fmt(output, ldns_output_format_default, rr);
+}
+
+ldns_status
+ldns_rr_list2buffer_str_fmt(ldns_buffer *output, 
+		const ldns_output_format *fmt, const ldns_rr_list *list)
+{
+	uint16_t i;
+
+	for(i = 0; i < ldns_rr_list_rr_count(list); i++) {
+		(void) ldns_rr2buffer_str_fmt(output, fmt, 
+				ldns_rr_list_rr(list, i));
+	}
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_rr_list2buffer_str(ldns_buffer *output, const ldns_rr_list *list)
+{
+	return ldns_rr_list2buffer_str_fmt(
+			output, ldns_output_format_default, list);
+}
+
+ldns_status
+ldns_pktheader2buffer_str(ldns_buffer *output, const ldns_pkt *pkt)
+{
+	ldns_lookup_table *opcode = ldns_lookup_by_id(ldns_opcodes,
+			                    (int) ldns_pkt_get_opcode(pkt));
+	ldns_lookup_table *rcode = ldns_lookup_by_id(ldns_rcodes,
+			                    (int) ldns_pkt_get_rcode(pkt));
+
+	ldns_buffer_printf(output, ";; ->>HEADER<<- ");
+	if (opcode) {
+		ldns_buffer_printf(output, "opcode: %s, ", opcode->name);
+	} else {
+		ldns_buffer_printf(output, "opcode: ?? (%u), ",
+				ldns_pkt_get_opcode(pkt));
+	}
+	if (rcode) {
+		ldns_buffer_printf(output, "rcode: %s, ", rcode->name);
+	} else {
+		ldns_buffer_printf(output, "rcode: ?? (%u), ", ldns_pkt_get_rcode(pkt));
+	}
+	ldns_buffer_printf(output, "id: %d\n", ldns_pkt_id(pkt));
+	ldns_buffer_printf(output, ";; flags: ");
+
+	if (ldns_pkt_qr(pkt)) {
+		ldns_buffer_printf(output, "qr ");
+	}
+	if (ldns_pkt_aa(pkt)) {
+		ldns_buffer_printf(output, "aa ");
+	}
+	if (ldns_pkt_tc(pkt)) {
+		ldns_buffer_printf(output, "tc ");
+	}
+	if (ldns_pkt_rd(pkt)) {
+		ldns_buffer_printf(output, "rd ");
+	}
+	if (ldns_pkt_cd(pkt)) {
+		ldns_buffer_printf(output, "cd ");
+	}
+	if (ldns_pkt_ra(pkt)) {
+		ldns_buffer_printf(output, "ra ");
+	}
+	if (ldns_pkt_ad(pkt)) {
+		ldns_buffer_printf(output, "ad ");
+	}
+	ldns_buffer_printf(output, "; ");
+	ldns_buffer_printf(output, "QUERY: %u, ", ldns_pkt_qdcount(pkt));
+	ldns_buffer_printf(output, "ANSWER: %u, ", ldns_pkt_ancount(pkt));
+	ldns_buffer_printf(output, "AUTHORITY: %u, ", ldns_pkt_nscount(pkt));
+	ldns_buffer_printf(output, "ADDITIONAL: %u ", ldns_pkt_arcount(pkt));
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_pkt2buffer_str_fmt(ldns_buffer *output, 
+		const ldns_output_format *fmt, const ldns_pkt *pkt)
+{
+	uint16_t i;
+	ldns_status status = LDNS_STATUS_OK;
+	char *tmp;
+	struct timeval time;
+	time_t time_tt;
+
+	if (!pkt) {
+		ldns_buffer_printf(output, "null");
+		return LDNS_STATUS_OK;
+	}
+
+	if (ldns_buffer_status_ok(output)) {
+		status = ldns_pktheader2buffer_str(output, pkt);
+		if (status != LDNS_STATUS_OK) {
+			return status;
+		}
+
+		ldns_buffer_printf(output, "\n");
+
+		ldns_buffer_printf(output, ";; QUESTION SECTION:\n;; ");
+
+
+		for (i = 0; i < ldns_pkt_qdcount(pkt); i++) {
+			status = ldns_rr2buffer_str_fmt(output, fmt,
+				       ldns_rr_list_rr(
+					       ldns_pkt_question(pkt), i));
+			if (status != LDNS_STATUS_OK) {
+				return status;
+			}
+		}
+		ldns_buffer_printf(output, "\n");
+
+		ldns_buffer_printf(output, ";; ANSWER SECTION:\n");
+		for (i = 0; i < ldns_pkt_ancount(pkt); i++) {
+			status = ldns_rr2buffer_str_fmt(output, fmt,
+				       ldns_rr_list_rr(
+					       ldns_pkt_answer(pkt), i));
+			if (status != LDNS_STATUS_OK) {
+				return status;
+			}
+
+		}
+		ldns_buffer_printf(output, "\n");
+
+		ldns_buffer_printf(output, ";; AUTHORITY SECTION:\n");
+
+		for (i = 0; i < ldns_pkt_nscount(pkt); i++) {
+			status = ldns_rr2buffer_str_fmt(output, fmt,
+				       ldns_rr_list_rr(
+					       ldns_pkt_authority(pkt), i));
+			if (status != LDNS_STATUS_OK) {
+				return status;
+			}
+		}
+		ldns_buffer_printf(output, "\n");
+
+		ldns_buffer_printf(output, ";; ADDITIONAL SECTION:\n");
+		for (i = 0; i < ldns_pkt_arcount(pkt); i++) {
+			status = ldns_rr2buffer_str_fmt(output, fmt,
+				       ldns_rr_list_rr(
+					       ldns_pkt_additional(pkt), i));
+			if (status != LDNS_STATUS_OK) {
+				return status;
+			}
+
+		}
+		ldns_buffer_printf(output, "\n");
+		/* add some futher fields */
+		ldns_buffer_printf(output, ";; Query time: %d msec\n",
+				ldns_pkt_querytime(pkt));
+		if (ldns_pkt_edns(pkt)) {
+			ldns_buffer_printf(output,
+				   ";; EDNS: version %u; flags:",
+				   ldns_pkt_edns_version(pkt));
+			if (ldns_pkt_edns_do(pkt)) {
+				ldns_buffer_printf(output, " do");
+			}
+			/* the extended rcode is the value set, shifted four bits,
+			 * and or'd with the original rcode */
+			if (ldns_pkt_edns_extended_rcode(pkt)) {
+				ldns_buffer_printf(output, " ; ext-rcode: %d",
+					(ldns_pkt_edns_extended_rcode(pkt) << 4 | ldns_pkt_get_rcode(pkt)));
+			}
+			ldns_buffer_printf(output, " ; udp: %u\n",
+					   ldns_pkt_edns_udp_size(pkt));
+
+			if (ldns_pkt_edns_data(pkt)) {
+				ldns_buffer_printf(output, ";; Data: ");
+				(void)ldns_rdf2buffer_str(output,
+							  ldns_pkt_edns_data(pkt));
+				ldns_buffer_printf(output, "\n");
+			}
+		}
+		if (ldns_pkt_tsig(pkt)) {
+			ldns_buffer_printf(output, ";; TSIG:\n;; ");
+			(void) ldns_rr2buffer_str_fmt(
+					output, fmt, ldns_pkt_tsig(pkt));
+			ldns_buffer_printf(output, "\n");
+		}
+		if (ldns_pkt_answerfrom(pkt)) {
+			tmp = ldns_rdf2str(ldns_pkt_answerfrom(pkt));
+			ldns_buffer_printf(output, ";; SERVER: %s\n", tmp);
+			LDNS_FREE(tmp);
+		}
+		time = ldns_pkt_timestamp(pkt);
+		time_tt = (time_t)time.tv_sec;
+		ldns_buffer_printf(output, ";; WHEN: %s",
+				(char*)ctime(&time_tt));
+
+		ldns_buffer_printf(output, ";; MSG SIZE  rcvd: %d\n",
+				(int)ldns_pkt_size(pkt));
+	} else {
+		return ldns_buffer_status(output);
+	}
+	return status;
+}
+
+ldns_status
+ldns_pkt2buffer_str(ldns_buffer *output, const ldns_pkt *pkt)
+{
+	return ldns_pkt2buffer_str_fmt(output, ldns_output_format_default, pkt);
+}
+
+
+#ifdef HAVE_SSL
+static ldns_status
+ldns_hmac_key2buffer_str(ldns_buffer *output, const ldns_key *k)
+{
+	ldns_status status;
+	size_t i;
+	ldns_rdf *b64_bignum;
+
+	ldns_buffer_printf(output, "Key: ");
+
+ 	i = ldns_key_hmac_size(k);
+	b64_bignum =  ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64, i, ldns_key_hmac_key(k));
+	status = ldns_rdf2buffer_str(output, b64_bignum);
+	ldns_rdf_deep_free(b64_bignum);
+	ldns_buffer_printf(output, "\n");
+	return status;
+}
+#endif
+
+#if defined(HAVE_SSL) && defined(USE_GOST)
+static ldns_status
+ldns_gost_key2buffer_str(ldns_buffer *output, EVP_PKEY *p)
+{
+	unsigned char* pp = NULL;
+	int ret;
+	ldns_rdf *b64_bignum;
+	ldns_status status;
+
+	ldns_buffer_printf(output, "GostAsn1: ");
+
+	ret = i2d_PrivateKey(p, &pp);
+	b64_bignum = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64, (size_t)ret, pp);
+	status = ldns_rdf2buffer_str(output, b64_bignum);
+
+	ldns_rdf_deep_free(b64_bignum);
+	OPENSSL_free(pp);
+	ldns_buffer_printf(output, "\n");
+	return status;
+}
+#endif
+
+ldns_status
+ldns_key2buffer_str(ldns_buffer *output, const ldns_key *k)
+{
+	ldns_status status = LDNS_STATUS_OK;
+	unsigned char  *bignum;
+#ifndef S_SPLINT_S
+	uint16_t i;
+#endif
+
+#ifdef HAVE_SSL
+	/* not used when ssl is not defined */
+	ldns_rdf *b64_bignum = NULL;
+
+	RSA *rsa;
+	DSA *dsa;
+#endif /* HAVE_SSL */
+
+	if (!k) {
+		return LDNS_STATUS_ERR;
+	}
+
+	bignum = LDNS_XMALLOC(unsigned char, LDNS_MAX_KEYLEN);
+	if (!bignum) {
+		return LDNS_STATUS_ERR;
+	}
+
+	if (ldns_buffer_status_ok(output)) {
+#ifdef HAVE_SSL
+		switch(ldns_key_algorithm(k)) {
+			case LDNS_SIGN_RSASHA1:
+			case LDNS_SIGN_RSASHA1_NSEC3:
+			case LDNS_SIGN_RSASHA256:
+			case LDNS_SIGN_RSASHA512:
+			case LDNS_SIGN_RSAMD5:
+				/* copied by looking at dnssec-keygen output */
+				/* header */
+				rsa = ldns_key_rsa_key(k);
+
+				ldns_buffer_printf(output,"Private-key-format: v1.2\n");
+				switch(ldns_key_algorithm(k)) {
+				case LDNS_SIGN_RSAMD5:
+					ldns_buffer_printf(output,
+								    "Algorithm: %u (RSA)\n",
+								    LDNS_RSAMD5);
+					break;
+				case LDNS_SIGN_RSASHA1:
+					ldns_buffer_printf(output,
+								    "Algorithm: %u (RSASHA1)\n",
+								    LDNS_RSASHA1);
+					break;
+				case LDNS_SIGN_RSASHA1_NSEC3:
+					ldns_buffer_printf(output,
+								    "Algorithm: %u (RSASHA1_NSEC3)\n",
+								    LDNS_RSASHA1_NSEC3);
+					break;
+#ifdef USE_SHA2
+				case LDNS_SIGN_RSASHA256:
+					ldns_buffer_printf(output,
+								    "Algorithm: %u (RSASHA256)\n",
+								    LDNS_RSASHA256);
+					break;
+				case LDNS_SIGN_RSASHA512:
+					ldns_buffer_printf(output,
+								    "Algorithm: %u (RSASHA512)\n",
+								    LDNS_RSASHA512);
+					break;
+#endif
+				default:
+					fprintf(stderr, "Warning: unknown signature ");
+					fprintf(stderr,
+						   "algorithm type %u\n",
+						   ldns_key_algorithm(k));
+					ldns_buffer_printf(output,
+								    "Algorithm: %u (Unknown)\n",
+								    ldns_key_algorithm(k));
+					break;
+				}
+
+				/* print to buf, convert to bin, convert to b64,
+				 * print to buf */
+				ldns_buffer_printf(output, "Modulus: ");
+#ifndef S_SPLINT_S
+				i = (uint16_t)BN_bn2bin(rsa->n, bignum);
+				if (i > LDNS_MAX_KEYLEN) {
+					goto error;
+				}
+				b64_bignum =  ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64, i, bignum);
+				if (ldns_rdf2buffer_str(output, b64_bignum) != LDNS_STATUS_OK) {
+					goto error;
+				}
+				ldns_rdf_deep_free(b64_bignum);
+				ldns_buffer_printf(output, "\n");
+				ldns_buffer_printf(output, "PublicExponent: ");
+				i = (uint16_t)BN_bn2bin(rsa->e, bignum);
+				if (i > LDNS_MAX_KEYLEN) {
+					goto error;
+				}
+				b64_bignum =  ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64, i, bignum);
+				if (ldns_rdf2buffer_str(output, b64_bignum) != LDNS_STATUS_OK) {
+					goto error;
+				}
+				ldns_rdf_deep_free(b64_bignum);
+				ldns_buffer_printf(output, "\n");
+
+				ldns_buffer_printf(output, "PrivateExponent: ");
+				if (rsa->d) {
+					i = (uint16_t)BN_bn2bin(rsa->d, bignum);
+					if (i > LDNS_MAX_KEYLEN) {
+						goto error;
+					}
+					b64_bignum =  ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64, i, bignum);
+					if (ldns_rdf2buffer_str(output, b64_bignum) != LDNS_STATUS_OK) {
+						goto error;
+					}
+					ldns_rdf_deep_free(b64_bignum);
+					ldns_buffer_printf(output, "\n");
+				} else {
+					ldns_buffer_printf(output, "(Not available)\n");
+				}
+
+				ldns_buffer_printf(output, "Prime1: ");
+				if (rsa->p) {
+					i = (uint16_t)BN_bn2bin(rsa->p, bignum);
+					if (i > LDNS_MAX_KEYLEN) {
+						goto error;
+					}
+					b64_bignum =  ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64, i, bignum);
+					if (ldns_rdf2buffer_str(output, b64_bignum) != LDNS_STATUS_OK) {
+						goto error;
+					}
+					ldns_rdf_deep_free(b64_bignum);
+					ldns_buffer_printf(output, "\n");
+				} else {
+					ldns_buffer_printf(output, "(Not available)\n");
+				}
+
+				ldns_buffer_printf(output, "Prime2: ");
+				if (rsa->q) {
+					i = (uint16_t)BN_bn2bin(rsa->q, bignum);
+					if (i > LDNS_MAX_KEYLEN) {
+						goto error;
+					}
+					b64_bignum =  ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64, i, bignum);
+					if (ldns_rdf2buffer_str(output, b64_bignum) != LDNS_STATUS_OK) {
+						goto error;
+					}
+					ldns_rdf_deep_free(b64_bignum);
+					ldns_buffer_printf(output, "\n");
+				} else {
+					ldns_buffer_printf(output, "(Not available)\n");
+				}
+
+				ldns_buffer_printf(output, "Exponent1: ");
+				if (rsa->dmp1) {
+					i = (uint16_t)BN_bn2bin(rsa->dmp1, bignum);
+					if (i > LDNS_MAX_KEYLEN) {
+						goto error;
+					}
+					b64_bignum =  ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64, i, bignum);
+					if (ldns_rdf2buffer_str(output, b64_bignum) != LDNS_STATUS_OK) {
+						goto error;
+					}
+					ldns_rdf_deep_free(b64_bignum);
+					ldns_buffer_printf(output, "\n");
+				} else {
+					ldns_buffer_printf(output, "(Not available)\n");
+				}
+
+				ldns_buffer_printf(output, "Exponent2: ");
+				if (rsa->dmq1) {
+					i = (uint16_t)BN_bn2bin(rsa->dmq1, bignum);
+					if (i > LDNS_MAX_KEYLEN) {
+						goto error;
+					}
+					b64_bignum =  ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64, i, bignum);
+					if (ldns_rdf2buffer_str(output, b64_bignum) != LDNS_STATUS_OK) {
+						goto error;
+					}
+					ldns_rdf_deep_free(b64_bignum);
+					ldns_buffer_printf(output, "\n");
+				} else {
+					ldns_buffer_printf(output, "(Not available)\n");
+				}
+
+				ldns_buffer_printf(output, "Coefficient: ");
+				if (rsa->iqmp) {
+					i = (uint16_t)BN_bn2bin(rsa->iqmp, bignum);
+					if (i > LDNS_MAX_KEYLEN) {
+						goto error;
+					}
+					b64_bignum =  ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64, i, bignum);
+					if (ldns_rdf2buffer_str(output, b64_bignum) != LDNS_STATUS_OK) {
+						goto error;
+					}
+					ldns_rdf_deep_free(b64_bignum);
+					ldns_buffer_printf(output, "\n");
+				} else {
+					ldns_buffer_printf(output, "(Not available)\n");
+				}
+#endif /* splint */
+
+				RSA_free(rsa);
+				break;
+			case LDNS_SIGN_DSA:
+			case LDNS_SIGN_DSA_NSEC3:
+				dsa = ldns_key_dsa_key(k);
+
+				ldns_buffer_printf(output,"Private-key-format: v1.2\n");
+				if (ldns_key_algorithm(k) == LDNS_SIGN_DSA) {
+					ldns_buffer_printf(output,"Algorithm: 3 (DSA)\n");
+				} else if (ldns_key_algorithm(k) == LDNS_SIGN_DSA_NSEC3) {
+					ldns_buffer_printf(output,"Algorithm: 6 (DSA_NSEC3)\n");
+				}
+
+				/* print to buf, convert to bin, convert to b64,
+				 * print to buf */
+				ldns_buffer_printf(output, "Prime(p): ");
+#ifndef S_SPLINT_S
+				if (dsa->p) {
+					i = (uint16_t)BN_bn2bin(dsa->p, bignum);
+					if (i > LDNS_MAX_KEYLEN) {
+						goto error;
+					}
+					b64_bignum =  ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64, i, bignum);
+					if (ldns_rdf2buffer_str(output, b64_bignum) != LDNS_STATUS_OK) {
+						goto error;
+					}
+					ldns_rdf_deep_free(b64_bignum);
+					ldns_buffer_printf(output, "\n");
+				} else {
+					printf("(Not available)\n");
+				}
+
+				ldns_buffer_printf(output, "Subprime(q): ");
+				if (dsa->q) {
+					i = (uint16_t)BN_bn2bin(dsa->q, bignum);
+					if (i > LDNS_MAX_KEYLEN) {
+						goto error;
+					}
+					b64_bignum =  ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64, i, bignum);
+					if (ldns_rdf2buffer_str(output, b64_bignum) != LDNS_STATUS_OK) {
+						goto error;
+					}
+					ldns_rdf_deep_free(b64_bignum);
+					ldns_buffer_printf(output, "\n");
+				} else {
+					printf("(Not available)\n");
+				}
+
+				ldns_buffer_printf(output, "Base(g): ");
+				if (dsa->g) {
+					i = (uint16_t)BN_bn2bin(dsa->g, bignum);
+					if (i > LDNS_MAX_KEYLEN) {
+						goto error;
+					}
+					b64_bignum =  ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64, i, bignum);
+					if (ldns_rdf2buffer_str(output, b64_bignum) != LDNS_STATUS_OK) {
+						goto error;
+					}
+					ldns_rdf_deep_free(b64_bignum);
+					ldns_buffer_printf(output, "\n");
+				} else {
+					printf("(Not available)\n");
+				}
+
+				ldns_buffer_printf(output, "Private_value(x): ");
+				if (dsa->priv_key) {
+					i = (uint16_t)BN_bn2bin(dsa->priv_key, bignum);
+					if (i > LDNS_MAX_KEYLEN) {
+						goto error;
+					}
+					b64_bignum =  ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64, i, bignum);
+					if (ldns_rdf2buffer_str(output, b64_bignum) != LDNS_STATUS_OK) {
+						goto error;
+					}
+					ldns_rdf_deep_free(b64_bignum);
+					ldns_buffer_printf(output, "\n");
+				} else {
+					printf("(Not available)\n");
+				}
+
+				ldns_buffer_printf(output, "Public_value(y): ");
+				if (dsa->pub_key) {
+					i = (uint16_t)BN_bn2bin(dsa->pub_key, bignum);
+					if (i > LDNS_MAX_KEYLEN) {
+						goto error;
+					}
+					b64_bignum =  ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64, i, bignum);
+					if (ldns_rdf2buffer_str(output, b64_bignum) != LDNS_STATUS_OK) {
+						goto error;
+					}
+					ldns_rdf_deep_free(b64_bignum);
+					ldns_buffer_printf(output, "\n");
+				} else {
+					printf("(Not available)\n");
+				}
+#endif /* splint */
+				break;
+			case LDNS_SIGN_ECC_GOST:
+				/* no format defined, use blob */
+#if defined(HAVE_SSL) && defined(USE_GOST)
+				ldns_buffer_printf(output, "Private-key-format: v1.2\n");
+				ldns_buffer_printf(output, "Algorithm: %d (ECC-GOST)\n", LDNS_SIGN_ECC_GOST);
+				status = ldns_gost_key2buffer_str(output, 
+#ifndef S_SPLINT_S
+					k->_key.key
+#else
+					NULL
+#endif
+				);
+
+#endif
+				break;
+#ifdef USE_ECDSA
+			case LDNS_SIGN_ECDSAP256SHA256:
+			case LDNS_SIGN_ECDSAP384SHA384:
+                                ldns_buffer_printf(output, "Private-key-format: v1.2\n");
+				ldns_buffer_printf(output, "Algorithm: %d (", ldns_key_algorithm(k));
+                                status=ldns_algorithm2buffer_str(output, (ldns_algorithm)ldns_key_algorithm(k));
+#ifndef S_SPLINT_S
+				ldns_buffer_printf(output, ")\n");
+                                if(k->_key.key) {
+                                        EC_KEY* ec = EVP_PKEY_get1_EC_KEY(k->_key.key);
+                                        const BIGNUM* b = EC_KEY_get0_private_key(ec);
+                                        ldns_buffer_printf(output, "PrivateKey: ");
+                                        i = (uint16_t)BN_bn2bin(b, bignum);
+                                        if (i > LDNS_MAX_KEYLEN) {
+                                                goto error;
+                                        }
+                                        b64_bignum =  ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64, i, bignum);
+                                        if (ldns_rdf2buffer_str(output, b64_bignum) != LDNS_STATUS_OK) {
+                                                goto error;
+                                        }
+                                        ldns_rdf_deep_free(b64_bignum);
+				        ldns_buffer_printf(output, "\n");
+                                        /* down reference count in EC_KEY
+                                         * its still assigned to the PKEY */
+                                        EC_KEY_free(ec);
+                                }
+#endif /* splint */
+                                break;
+#endif
+			case LDNS_SIGN_HMACMD5:
+				/* there's not much of a format defined for TSIG */
+				/* It's just a binary blob, Same for all algorithms */
+                ldns_buffer_printf(output, "Private-key-format: v1.2\n");
+                ldns_buffer_printf(output, "Algorithm: 157 (HMAC_MD5)\n");
+				status = ldns_hmac_key2buffer_str(output, k);
+				break;
+			case LDNS_SIGN_HMACSHA1:
+		        ldns_buffer_printf(output, "Private-key-format: v1.2\n");
+		        ldns_buffer_printf(output, "Algorithm: 158 (HMAC_SHA1)\n");
+				status = ldns_hmac_key2buffer_str(output, k);
+				break;
+			case LDNS_SIGN_HMACSHA256:
+		        ldns_buffer_printf(output, "Private-key-format: v1.2\n");
+		        ldns_buffer_printf(output, "Algorithm: 159 (HMAC_SHA256)\n");
+				status = ldns_hmac_key2buffer_str(output, k);
+				break;
+		}
+#endif /* HAVE_SSL */
+	} else {
+#ifdef HAVE_SSL
+		LDNS_FREE(b64_bignum);
+#endif
+		LDNS_FREE(bignum);
+		return ldns_buffer_status(output);
+	}
+	LDNS_FREE(bignum);
+	return status;
+
+#ifdef HAVE_SSL
+	/* compiles warn the label isn't used */
+error:
+	LDNS_FREE(bignum);
+	return LDNS_STATUS_ERR;
+#endif /* HAVE_SSL */
+
+}
+
+/*
+ * Zero terminate the buffer and fix it to the size of the string.
+ */
+char *
+ldns_buffer2str(ldns_buffer *buffer)
+{
+	char *tmp_str;
+	char *str;
+
+	/* check if buffer ends with \0, if not, and
+	   if there is space, add it */
+	if (*(ldns_buffer_at(buffer, ldns_buffer_position(buffer))) != 0) {
+		if (!ldns_buffer_reserve(buffer, 1)) {
+			return NULL;
+		}
+		ldns_buffer_write_u8(buffer, (uint8_t) '\0');
+		if (!ldns_buffer_set_capacity(buffer, ldns_buffer_position(buffer))) {
+			return NULL;
+		}
+	}
+
+	tmp_str = ldns_buffer_export(buffer);
+	str = LDNS_XMALLOC(char, strlen(tmp_str) + 1);
+        if(!str) {
+                return NULL;
+        }
+	memcpy(str, tmp_str, strlen(tmp_str) + 1);
+
+	return str;
+}
+
+char *
+ldns_rdf2str(const ldns_rdf *rdf)
+{
+	char *result = NULL;
+	ldns_buffer *tmp_buffer = ldns_buffer_new(LDNS_MAX_PACKETLEN);
+
+	if (!tmp_buffer) {
+		return NULL;
+	}
+	if (ldns_rdf2buffer_str(tmp_buffer, rdf) == LDNS_STATUS_OK) {
+		/* export and return string, destroy rest */
+		result = ldns_buffer2str(tmp_buffer);
+	}
+	ldns_buffer_free(tmp_buffer);
+	return result;
+}
+
+char *
+ldns_rr2str_fmt(const ldns_output_format *fmt, const ldns_rr *rr)
+{
+	char *result = NULL;
+	ldns_buffer *tmp_buffer = ldns_buffer_new(LDNS_MAX_PACKETLEN);
+
+	if (!tmp_buffer) {
+		return NULL;
+	}
+	if (ldns_rr2buffer_str_fmt(tmp_buffer, fmt, rr)
+		       	== LDNS_STATUS_OK) {
+		/* export and return string, destroy rest */
+		result = ldns_buffer2str(tmp_buffer);
+	}
+	ldns_buffer_free(tmp_buffer);
+	return result;
+}
+
+char *
+ldns_rr2str(const ldns_rr *rr)
+{
+	return ldns_rr2str_fmt(ldns_output_format_default, rr);
+}
+
+char *
+ldns_pkt2str_fmt(const ldns_output_format *fmt, const ldns_pkt *pkt)
+{
+	char *result = NULL;
+	ldns_buffer *tmp_buffer = ldns_buffer_new(LDNS_MAX_PACKETLEN);
+
+	if (!tmp_buffer) {
+		return NULL;
+	}
+	if (ldns_pkt2buffer_str_fmt(tmp_buffer, fmt, pkt)
+		       	== LDNS_STATUS_OK) {
+		/* export and return string, destroy rest */
+		result = ldns_buffer2str(tmp_buffer);
+	}
+
+	ldns_buffer_free(tmp_buffer);
+	return result;
+}
+
+char *
+ldns_pkt2str(const ldns_pkt *pkt)
+{
+	return ldns_pkt2str_fmt(ldns_output_format_default, pkt);
+}
+
+char *
+ldns_key2str(const ldns_key *k)
+{
+	char *result = NULL;
+	ldns_buffer *tmp_buffer = ldns_buffer_new(LDNS_MAX_PACKETLEN);
+
+	if (!tmp_buffer) {
+		return NULL;
+	}
+	if (ldns_key2buffer_str(tmp_buffer, k) == LDNS_STATUS_OK) {
+		/* export and return string, destroy rest */
+		result = ldns_buffer2str(tmp_buffer);
+	}
+	ldns_buffer_free(tmp_buffer);
+	return result;
+}
+
+char *
+ldns_rr_list2str_fmt(const ldns_output_format *fmt, const ldns_rr_list *list)
+{
+	char *result = NULL;
+	ldns_buffer *tmp_buffer = ldns_buffer_new(LDNS_MAX_PACKETLEN);
+
+	if (!tmp_buffer) {
+		return NULL;
+	}
+	if (list) {
+		if (ldns_rr_list2buffer_str_fmt(
+				   tmp_buffer, fmt, list)
+			       	== LDNS_STATUS_OK) {
+		}
+	} else {
+		if (fmt == NULL) {
+			fmt = ldns_output_format_default;
+		}
+		if (fmt->flags & LDNS_COMMENT_NULLS) {
+			ldns_buffer_printf(tmp_buffer, "; (null)\n");
+		}
+	}
+
+	/* export and return string, destroy rest */
+	result = ldns_buffer2str(tmp_buffer);
+	ldns_buffer_free(tmp_buffer);
+	return result;
+}
+
+char *
+ldns_rr_list2str(const ldns_rr_list *list)
+{
+	return ldns_rr_list2str_fmt(ldns_output_format_default, list);
+}
+
+void
+ldns_rdf_print(FILE *output, const ldns_rdf *rdf)
+{
+	char *str = ldns_rdf2str(rdf);
+	if (str) {
+		fprintf(output, "%s", str);
+	} else {
+		fprintf(output, "Unable to convert rdf to string\n");
+	}
+	LDNS_FREE(str);
+}
+
+void
+ldns_rr_print_fmt(FILE *output, 
+		const ldns_output_format *fmt, const ldns_rr *rr)
+{
+	char *str = ldns_rr2str_fmt(fmt, rr);
+	if (str) {
+		fprintf(output, "%s", str);
+	} else {
+		fprintf(output, "Unable to convert rr to string\n");
+	}
+	LDNS_FREE(str);
+}
+
+void
+ldns_rr_print(FILE *output, const ldns_rr *rr)
+{
+	ldns_rr_print_fmt(output, ldns_output_format_default, rr);
+}
+
+void
+ldns_pkt_print_fmt(FILE *output, 
+		const ldns_output_format *fmt, const ldns_pkt *pkt)
+{
+	char *str = ldns_pkt2str_fmt(fmt, pkt);
+	if (str) {
+		fprintf(output, "%s", str);
+	} else {
+		fprintf(output, "Unable to convert packet to string\n");
+	}
+	LDNS_FREE(str);
+}
+
+void
+ldns_pkt_print(FILE *output, const ldns_pkt *pkt)
+{
+	ldns_pkt_print_fmt(output, ldns_output_format_default, pkt);
+}
+
+void
+ldns_rr_list_print_fmt(FILE *output, 
+		const ldns_output_format *fmt, const ldns_rr_list *lst)
+{
+	size_t i;
+	for (i = 0; i < ldns_rr_list_rr_count(lst); i++) {
+		ldns_rr_print_fmt(output, fmt, ldns_rr_list_rr(lst, i));
+	}
+}
+
+void
+ldns_rr_list_print(FILE *output, const ldns_rr_list *lst)
+{
+	ldns_rr_list_print_fmt(output, ldns_output_format_default, lst);
+}
+
+void
+ldns_resolver_print_fmt(FILE *output, 
+		const ldns_output_format *fmt, const ldns_resolver *r)
+{
+	uint16_t i;
+	ldns_rdf **n;
+	ldns_rdf **s;
+	size_t *rtt;
+	if (!r) {
+		return;
+	}
+	n = ldns_resolver_nameservers(r);
+	s = ldns_resolver_searchlist(r);
+	rtt = ldns_resolver_rtt(r);
+
+	fprintf(output, "port: %d\n", (int)ldns_resolver_port(r));
+	fprintf(output, "edns0 size: %d\n", (int)ldns_resolver_edns_udp_size(r));
+	fprintf(output, "use ip6: %d\n", (int)ldns_resolver_ip6(r));
+
+	fprintf(output, "recursive: %d\n", ldns_resolver_recursive(r));
+	fprintf(output, "usevc: %d\n", ldns_resolver_usevc(r));
+	fprintf(output, "igntc: %d\n", ldns_resolver_igntc(r));
+	fprintf(output, "fail: %d\n", ldns_resolver_fail(r));
+	fprintf(output, "retry: %d\n", (int)ldns_resolver_retry(r));
+	fprintf(output, "retrans: %d\n", (int)ldns_resolver_retrans(r));
+	fprintf(output, "fallback: %d\n", ldns_resolver_fallback(r));
+	fprintf(output, "random: %d\n", ldns_resolver_random(r));
+	fprintf(output, "timeout: %d\n", (int)ldns_resolver_timeout(r).tv_sec);
+	fprintf(output, "dnssec: %d\n", ldns_resolver_dnssec(r));
+	fprintf(output, "dnssec cd: %d\n", ldns_resolver_dnssec_cd(r));
+	fprintf(output, "trust anchors (%d listed):\n",
+		(int)ldns_rr_list_rr_count(ldns_resolver_dnssec_anchors(r)));
+	ldns_rr_list_print_fmt(output, fmt, ldns_resolver_dnssec_anchors(r));
+	fprintf(output, "tsig: %s %s\n",
+                ldns_resolver_tsig_keyname(r)?ldns_resolver_tsig_keyname(r):"-",
+                ldns_resolver_tsig_algorithm(r)?ldns_resolver_tsig_algorithm(r):"-");
+	fprintf(output, "debug: %d\n", ldns_resolver_debug(r));
+
+	fprintf(output, "default domain: ");
+	ldns_rdf_print(output, ldns_resolver_domain(r));
+	fprintf(output, "\n");
+	fprintf(output, "apply default domain: %d\n", ldns_resolver_defnames(r));
+
+	fprintf(output, "searchlist (%d listed):\n",  (int)ldns_resolver_searchlist_count(r));
+	for (i = 0; i < ldns_resolver_searchlist_count(r); i++) {
+		fprintf(output, "\t");
+		ldns_rdf_print(output, s[i]);
+		fprintf(output, "\n");
+	}
+	fprintf(output, "apply search list: %d\n", ldns_resolver_dnsrch(r));
+
+	fprintf(output, "nameservers (%d listed):\n", (int)ldns_resolver_nameserver_count(r));
+	for (i = 0; i < ldns_resolver_nameserver_count(r); i++) {
+		fprintf(output, "\t");
+		ldns_rdf_print(output, n[i]);
+
+		switch ((int)rtt[i]) {
+			case LDNS_RESOLV_RTT_MIN:
+			fprintf(output, " - reachable\n");
+			break;
+			case LDNS_RESOLV_RTT_INF:
+			fprintf(output, " - unreachable\n");
+			break;
+		}
+	}
+}
+
+void
+ldns_resolver_print(FILE *output, const ldns_resolver *r)
+{
+	ldns_resolver_print_fmt(output, ldns_output_format_default, r);
+}
+
+void
+ldns_zone_print_fmt(FILE *output, 
+		const ldns_output_format *fmt, const ldns_zone *z)
+{
+	if(ldns_zone_soa(z))
+		ldns_rr_print_fmt(output, fmt, ldns_zone_soa(z));
+	ldns_rr_list_print_fmt(output, fmt, ldns_zone_rrs(z));
+}
+void
+ldns_zone_print(FILE *output, const ldns_zone *z)
+{
+	ldns_zone_print_fmt(output, ldns_output_format_default, z);
+}
diff --git a/3rdParty/Ldns/src/src/host2wire.c b/3rdParty/Ldns/src/src/host2wire.c
new file mode 100644
index 0000000..ca28dba
--- /dev/null
+++ b/3rdParty/Ldns/src/src/host2wire.c
@@ -0,0 +1,433 @@
+/*
+ * host2wire.c
+ *
+ * conversion routines from the host to the wire format.
+ * This will usually just a re-ordering of the
+ * data (as we store it in network format)
+ *
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2004-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+#include <ldns/config.h>
+
+#include <ldns/ldns.h>
+
+/* TODO Jelte
+  add a pointer to a 'possiblecompression' structure
+  to all the needed functions?
+  something like an array of name, pointer values?
+  every dname part could be added to it
+*/
+
+ldns_status
+ldns_dname2buffer_wire(ldns_buffer *buffer, const ldns_rdf *name)
+{
+	if (ldns_buffer_reserve(buffer, ldns_rdf_size(name))) {
+		ldns_buffer_write(buffer, ldns_rdf_data(name), ldns_rdf_size(name));
+	}
+	return ldns_buffer_status(buffer);
+}
+
+ldns_status
+ldns_rdf2buffer_wire(ldns_buffer *buffer, const ldns_rdf *rdf)
+{
+	if (ldns_buffer_reserve(buffer, ldns_rdf_size(rdf))) {
+		ldns_buffer_write(buffer, ldns_rdf_data(rdf), ldns_rdf_size(rdf));
+	}
+	return ldns_buffer_status(buffer);
+}
+
+ldns_status
+ldns_rdf2buffer_wire_canonical(ldns_buffer *buffer, const ldns_rdf *rdf)
+{
+	size_t i;
+	uint8_t *rdf_data;
+
+	if (ldns_rdf_get_type(rdf) == LDNS_RDF_TYPE_DNAME) {
+		if (ldns_buffer_reserve(buffer, ldns_rdf_size(rdf))) {
+			rdf_data = ldns_rdf_data(rdf);
+			for (i = 0; i < ldns_rdf_size(rdf); i++) {
+				ldns_buffer_write_u8(buffer,
+				    (uint8_t) LDNS_DNAME_NORMALIZE((int)rdf_data[i]));
+			}
+		}
+	} else {
+		/* direct copy for all other types */
+		if (ldns_buffer_reserve(buffer, ldns_rdf_size(rdf))) {
+			ldns_buffer_write(buffer,
+						   ldns_rdf_data(rdf),
+						   ldns_rdf_size(rdf));
+		}
+	}
+	return ldns_buffer_status(buffer);
+}
+
+/* convert a rr list to wireformat */
+ldns_status
+ldns_rr_list2buffer_wire(ldns_buffer *buffer,const ldns_rr_list *rr_list)
+{
+	uint16_t rr_count;
+	uint16_t i;
+
+	rr_count = ldns_rr_list_rr_count(rr_list);
+	for(i = 0; i < rr_count; i++) {
+		(void)ldns_rr2buffer_wire(buffer, ldns_rr_list_rr(rr_list, i), 
+					  LDNS_SECTION_ANY);
+	}
+	return ldns_buffer_status(buffer);
+}
+
+ldns_status
+ldns_rr2buffer_wire_canonical(ldns_buffer *buffer,
+						const ldns_rr *rr,
+						int section)
+{
+	uint16_t i;
+	uint16_t rdl_pos = 0;
+	bool pre_rfc3597 = false;
+	switch (ldns_rr_get_type(rr)) {
+	case LDNS_RR_TYPE_NS:
+	case LDNS_RR_TYPE_MD:
+	case LDNS_RR_TYPE_MF:
+	case LDNS_RR_TYPE_CNAME:
+	case LDNS_RR_TYPE_SOA:
+	case LDNS_RR_TYPE_MB:
+	case LDNS_RR_TYPE_MG:
+	case LDNS_RR_TYPE_MR:
+	case LDNS_RR_TYPE_PTR:
+	case LDNS_RR_TYPE_HINFO:
+	case LDNS_RR_TYPE_MINFO:
+	case LDNS_RR_TYPE_MX:
+	case LDNS_RR_TYPE_RP:
+	case LDNS_RR_TYPE_AFSDB:
+	case LDNS_RR_TYPE_RT:
+	case LDNS_RR_TYPE_SIG:
+	case LDNS_RR_TYPE_PX:
+	case LDNS_RR_TYPE_NXT:
+	case LDNS_RR_TYPE_NAPTR:
+	case LDNS_RR_TYPE_KX:
+	case LDNS_RR_TYPE_SRV:
+	case LDNS_RR_TYPE_DNAME:
+	case LDNS_RR_TYPE_A6:
+		pre_rfc3597 = true;
+		break;
+	default:
+		break;
+	}
+	
+	if (ldns_rr_owner(rr)) {
+		(void) ldns_rdf2buffer_wire_canonical(buffer, ldns_rr_owner(rr));
+	}
+	
+	if (ldns_buffer_reserve(buffer, 4)) {
+		(void) ldns_buffer_write_u16(buffer, ldns_rr_get_type(rr));
+		(void) ldns_buffer_write_u16(buffer, ldns_rr_get_class(rr));
+	}
+
+	if (section != LDNS_SECTION_QUESTION) {
+		if (ldns_buffer_reserve(buffer, 6)) {
+			ldns_buffer_write_u32(buffer, ldns_rr_ttl(rr));
+			/* remember pos for later */
+			rdl_pos = ldns_buffer_position(buffer);
+			ldns_buffer_write_u16(buffer, 0);
+		}	
+
+		for (i = 0; i < ldns_rr_rd_count(rr); i++) {
+			if (pre_rfc3597) {
+				(void) ldns_rdf2buffer_wire_canonical(
+						buffer, ldns_rr_rdf(rr, i));
+			} else {
+				(void) ldns_rdf2buffer_wire(
+						buffer, ldns_rr_rdf(rr, i));
+			}
+		}
+		
+		if (rdl_pos != 0) {
+			ldns_buffer_write_u16_at(buffer, rdl_pos,
+			                         ldns_buffer_position(buffer)
+		        	                   - rdl_pos - 2);
+		}
+	}
+	return ldns_buffer_status(buffer);
+}
+
+ldns_status
+ldns_rr2buffer_wire(ldns_buffer *buffer, const ldns_rr *rr, int section)
+{
+	uint16_t i;
+	uint16_t rdl_pos = 0;
+	
+	if (ldns_rr_owner(rr)) {
+		(void) ldns_dname2buffer_wire(buffer, ldns_rr_owner(rr));
+	}
+	
+	if (ldns_buffer_reserve(buffer, 4)) {
+		(void) ldns_buffer_write_u16(buffer, ldns_rr_get_type(rr));
+		(void) ldns_buffer_write_u16(buffer, ldns_rr_get_class(rr));
+	}
+
+	if (section != LDNS_SECTION_QUESTION) {
+		if (ldns_buffer_reserve(buffer, 6)) {
+			ldns_buffer_write_u32(buffer, ldns_rr_ttl(rr));
+			/* remember pos for later */
+			rdl_pos = ldns_buffer_position(buffer);
+			ldns_buffer_write_u16(buffer, 0);
+		}	
+
+		for (i = 0; i < ldns_rr_rd_count(rr); i++) {
+			(void) ldns_rdf2buffer_wire(
+					buffer, ldns_rr_rdf(rr, i));
+		}
+		
+		if (rdl_pos != 0) {
+			ldns_buffer_write_u16_at(buffer, rdl_pos,
+			                         ldns_buffer_position(buffer)
+		        	                   - rdl_pos - 2);
+		}
+	}
+	return ldns_buffer_status(buffer);
+}
+
+ldns_status
+ldns_rrsig2buffer_wire(ldns_buffer *buffer, const ldns_rr *rr)
+{
+	uint16_t i;
+
+	/* it must be a sig RR */
+	if (ldns_rr_get_type(rr) != LDNS_RR_TYPE_RRSIG) {
+		return LDNS_STATUS_ERR;
+	}
+	
+	/* Convert all the rdfs, except the actual signature data
+	 * rdf number 8  - the last, hence: -1 */
+	for (i = 0; i < ldns_rr_rd_count(rr) - 1; i++) {
+		(void) ldns_rdf2buffer_wire(buffer, ldns_rr_rdf(rr, i));
+	}
+
+	return ldns_buffer_status(buffer);
+}
+
+ldns_status
+ldns_rr_rdata2buffer_wire(ldns_buffer *buffer, const ldns_rr *rr)
+{
+	uint16_t i;
+	/* convert all the rdf's */
+	for (i = 0; i < ldns_rr_rd_count(rr); i++) {
+		(void) ldns_rdf2buffer_wire(buffer, ldns_rr_rdf(rr, i));
+	}
+
+	return ldns_buffer_status(buffer);
+}
+
+/*
+ * Copies the packet header data to the buffer in wire format
+ */
+static ldns_status
+ldns_hdr2buffer_wire(ldns_buffer *buffer, const ldns_pkt *packet)
+{
+	uint8_t flags;
+	uint16_t arcount;
+	
+	if (ldns_buffer_reserve(buffer, 12)) {
+		ldns_buffer_write_u16(buffer, ldns_pkt_id(packet));
+		
+		flags = ldns_pkt_qr(packet) << 7
+		        | ldns_pkt_get_opcode(packet) << 3
+		        | ldns_pkt_aa(packet) << 2
+		        | ldns_pkt_tc(packet) << 1 | ldns_pkt_rd(packet);
+		ldns_buffer_write_u8(buffer, flags);
+		
+		flags = ldns_pkt_ra(packet) << 7
+		        /*| ldns_pkt_z(packet) << 6*/
+		        | ldns_pkt_ad(packet) << 5
+		        | ldns_pkt_cd(packet) << 4 | ldns_pkt_get_rcode(packet);
+		ldns_buffer_write_u8(buffer, flags);
+		
+		ldns_buffer_write_u16(buffer, ldns_pkt_qdcount(packet));
+		ldns_buffer_write_u16(buffer, ldns_pkt_ancount(packet));
+		ldns_buffer_write_u16(buffer, ldns_pkt_nscount(packet));
+		/* add EDNS0 and TSIG to additional if they are there */
+		arcount = ldns_pkt_arcount(packet);
+		if (ldns_pkt_tsig(packet)) {
+			arcount++;
+		}
+		if (ldns_pkt_edns(packet)) {
+			arcount++;
+		}
+		ldns_buffer_write_u16(buffer, arcount);
+	}
+	
+	return ldns_buffer_status(buffer);
+}
+
+ldns_status
+ldns_pkt2buffer_wire(ldns_buffer *buffer, const ldns_pkt *packet)
+{
+	ldns_rr_list *rr_list;
+	uint16_t i;
+	
+	/* edns tmp vars */
+	ldns_rr *edns_rr;
+	uint8_t edata[4];
+	
+	(void) ldns_hdr2buffer_wire(buffer, packet);
+
+	rr_list = ldns_pkt_question(packet);
+	if (rr_list) {
+		for (i = 0; i < ldns_rr_list_rr_count(rr_list); i++) {
+			(void) ldns_rr2buffer_wire(buffer, 
+			             ldns_rr_list_rr(rr_list, i), LDNS_SECTION_QUESTION);
+		}
+	}
+	rr_list = ldns_pkt_answer(packet);
+	if (rr_list) {
+		for (i = 0; i < ldns_rr_list_rr_count(rr_list); i++) {
+			(void) ldns_rr2buffer_wire(buffer, 
+			             ldns_rr_list_rr(rr_list, i), LDNS_SECTION_ANSWER);
+		}
+	}
+	rr_list = ldns_pkt_authority(packet);
+	if (rr_list) {
+		for (i = 0; i < ldns_rr_list_rr_count(rr_list); i++) {
+			(void) ldns_rr2buffer_wire(buffer, 
+			             ldns_rr_list_rr(rr_list, i), LDNS_SECTION_AUTHORITY);
+		}
+	}
+	rr_list = ldns_pkt_additional(packet);
+	if (rr_list) {
+		for (i = 0; i < ldns_rr_list_rr_count(rr_list); i++) {
+			(void) ldns_rr2buffer_wire(buffer, 
+			             ldns_rr_list_rr(rr_list, i), LDNS_SECTION_ADDITIONAL);
+		}
+	}
+	
+	/* add EDNS to additional if it is needed */
+	if (ldns_pkt_edns(packet)) {
+		edns_rr = ldns_rr_new();
+		if(!edns_rr) return LDNS_STATUS_MEM_ERR;
+		ldns_rr_set_owner(edns_rr,
+				ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, "."));
+		ldns_rr_set_type(edns_rr, LDNS_RR_TYPE_OPT);
+		ldns_rr_set_class(edns_rr, ldns_pkt_edns_udp_size(packet));
+		edata[0] = ldns_pkt_edns_extended_rcode(packet);
+		edata[1] = ldns_pkt_edns_version(packet);
+		ldns_write_uint16(&edata[2], ldns_pkt_edns_z(packet));
+		ldns_rr_set_ttl(edns_rr, ldns_read_uint32(edata));
+		/* don't forget to add the edns rdata (if any) */
+		if (packet->_edns_data)
+			ldns_rr_push_rdf (edns_rr, packet->_edns_data);
+		(void)ldns_rr2buffer_wire(buffer, edns_rr, LDNS_SECTION_ADDITIONAL);
+		/* take the edns rdata back out of the rr before we free rr */
+		if (packet->_edns_data)
+			(void)ldns_rr_pop_rdf (edns_rr);
+		ldns_rr_free(edns_rr);
+	}
+	
+	/* add TSIG to additional if it is there */
+	if (ldns_pkt_tsig(packet)) {
+		(void) ldns_rr2buffer_wire(buffer,
+		                           ldns_pkt_tsig(packet), LDNS_SECTION_ADDITIONAL);
+	}
+	
+	return LDNS_STATUS_OK;
+}
+
+ldns_status
+ldns_rdf2wire(uint8_t **dest, const ldns_rdf *rdf, size_t *result_size)
+{
+	ldns_buffer *buffer = ldns_buffer_new(LDNS_MAX_PACKETLEN);
+	uint8_t *result = NULL;
+	ldns_status status;
+	*result_size = 0;
+	*dest = NULL;
+	if(!buffer) return LDNS_STATUS_MEM_ERR;
+	
+	status = ldns_rdf2buffer_wire(buffer, rdf);
+	if (status == LDNS_STATUS_OK) {
+		*result_size =  ldns_buffer_position(buffer);
+		result = (uint8_t *) ldns_buffer_export(buffer);
+	} else {
+		ldns_buffer_free(buffer);
+		return status;
+	}
+	
+	if (result) {
+		*dest = LDNS_XMALLOC(uint8_t, ldns_buffer_position(buffer));
+		if(!*dest) {
+			ldns_buffer_free(buffer);
+			return LDNS_STATUS_MEM_ERR;
+		}
+		memcpy(*dest, result, ldns_buffer_position(buffer));
+	}
+	
+	ldns_buffer_free(buffer);
+	return status;
+}
+
+ldns_status
+ldns_rr2wire(uint8_t **dest, const ldns_rr *rr, int section, size_t *result_size)
+{
+	ldns_buffer *buffer = ldns_buffer_new(LDNS_MAX_PACKETLEN);
+	uint8_t *result = NULL;
+	ldns_status status;
+	*result_size = 0;
+	*dest = NULL;
+	if(!buffer) return LDNS_STATUS_MEM_ERR;
+	
+	status = ldns_rr2buffer_wire(buffer, rr, section);
+	if (status == LDNS_STATUS_OK) {
+		*result_size =  ldns_buffer_position(buffer);
+		result = (uint8_t *) ldns_buffer_export(buffer);
+	} else {
+		ldns_buffer_free(buffer);
+		return status;
+	}
+	
+	if (result) {
+		*dest = LDNS_XMALLOC(uint8_t, ldns_buffer_position(buffer));
+		if(!*dest) {
+			ldns_buffer_free(buffer);
+			return LDNS_STATUS_MEM_ERR;
+		}
+		memcpy(*dest, result, ldns_buffer_position(buffer));
+	}
+	
+	ldns_buffer_free(buffer);
+	return status;
+}
+
+ldns_status
+ldns_pkt2wire(uint8_t **dest, const ldns_pkt *packet, size_t *result_size)
+{
+	ldns_buffer *buffer = ldns_buffer_new(LDNS_MAX_PACKETLEN);
+	uint8_t *result = NULL;
+	ldns_status status;
+	*result_size = 0;
+	*dest = NULL;
+	if(!buffer) return LDNS_STATUS_MEM_ERR;
+	
+	status = ldns_pkt2buffer_wire(buffer, packet);
+	if (status == LDNS_STATUS_OK) {
+		*result_size =  ldns_buffer_position(buffer);
+		result = (uint8_t *) ldns_buffer_export(buffer);
+	} else {
+		ldns_buffer_free(buffer);
+		return status;
+	}
+	
+	if (result) {
+		*dest = LDNS_XMALLOC(uint8_t, ldns_buffer_position(buffer));
+		if(!*dest) {
+			ldns_buffer_free(buffer);
+			return LDNS_STATUS_MEM_ERR;
+		}
+		memcpy(*dest, result, ldns_buffer_position(buffer));
+	}
+	
+	ldns_buffer_free(buffer);
+	return status;
+}
diff --git a/3rdParty/Ldns/src/src/keys.c b/3rdParty/Ldns/src/src/keys.c
new file mode 100644
index 0000000..3772122
--- /dev/null
+++ b/3rdParty/Ldns/src/src/keys.c
@@ -0,0 +1,1672 @@
+/*
+ * keys.c handle private keys for use in DNSSEC
+ *
+ * This module should hide some of the openSSL complexities
+ * and give a general interface for private keys and hmac
+ * handling
+ *
+ * (c) NLnet Labs, 2004-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+#include <ldns/config.h>
+
+#include <ldns/ldns.h>
+
+#ifdef HAVE_SSL
+#include <openssl/ssl.h>
+#include <openssl/engine.h>
+#include <openssl/rand.h>
+#endif /* HAVE_SSL */
+
+ldns_lookup_table ldns_signing_algorithms[] = {
+        { LDNS_SIGN_RSAMD5, "RSAMD5" },
+        { LDNS_SIGN_RSASHA1, "RSASHA1" },
+        { LDNS_SIGN_RSASHA1_NSEC3, "RSASHA1-NSEC3-SHA1" },
+#ifdef USE_SHA2
+        { LDNS_SIGN_RSASHA256, "RSASHA256" },
+        { LDNS_SIGN_RSASHA512, "RSASHA512" },
+#endif
+#ifdef USE_GOST
+        { LDNS_SIGN_ECC_GOST, "ECC-GOST" },
+#endif
+#ifdef USE_ECDSA
+        { LDNS_SIGN_ECDSAP256SHA256, "ECDSAP256SHA256" },
+        { LDNS_SIGN_ECDSAP384SHA384, "ECDSAP384SHA384" },
+#endif
+        { LDNS_SIGN_DSA, "DSA" },
+        { LDNS_SIGN_DSA_NSEC3, "DSA-NSEC3-SHA1" },
+        { LDNS_SIGN_HMACMD5, "hmac-md5.sig-alg.reg.int" },
+        { LDNS_SIGN_HMACSHA1, "hmac-sha1" },
+        { LDNS_SIGN_HMACSHA256, "hmac-sha256" },
+        { 0, NULL }
+};
+
+ldns_key_list *
+ldns_key_list_new()
+{
+	ldns_key_list *key_list = LDNS_MALLOC(ldns_key_list);
+	if (!key_list) {
+		return NULL;
+	} else {
+		key_list->_key_count = 0;
+		key_list->_keys = NULL;
+		return key_list;
+	}
+}
+
+ldns_key *
+ldns_key_new()
+{
+	ldns_key *newkey;
+
+	newkey = LDNS_MALLOC(ldns_key);
+	if (!newkey) {
+		return NULL;
+	} else {
+		/* some defaults - not sure wether to do this */
+		ldns_key_set_use(newkey, true);
+		ldns_key_set_flags(newkey, LDNS_KEY_ZONE_KEY);
+		ldns_key_set_origttl(newkey, 0);
+		ldns_key_set_keytag(newkey, 0);
+		ldns_key_set_inception(newkey, 0);
+		ldns_key_set_expiration(newkey, 0);
+		ldns_key_set_pubkey_owner(newkey, NULL);
+#ifdef HAVE_SSL
+		ldns_key_set_evp_key(newkey, NULL);
+#endif /* HAVE_SSL */
+		ldns_key_set_hmac_key(newkey, NULL);
+		ldns_key_set_external_key(newkey, NULL);
+		return newkey;
+	}
+}
+
+ldns_status
+ldns_key_new_frm_fp(ldns_key **k, FILE *fp)
+{
+	return ldns_key_new_frm_fp_l(k, fp, NULL);
+}
+
+#ifdef HAVE_SSL
+ldns_status
+ldns_key_new_frm_engine(ldns_key **key, ENGINE *e, char *key_id, ldns_algorithm alg)
+{
+	ldns_key *k;
+
+	k = ldns_key_new();
+        if(!k) return LDNS_STATUS_MEM_ERR;
+#ifndef S_SPLINT_S
+	k->_key.key = ENGINE_load_private_key(e, key_id, UI_OpenSSL(), NULL);
+        if(!k->_key.key) {
+                ldns_key_free(k);
+                return LDNS_STATUS_ERR;
+        }
+	ldns_key_set_algorithm(k, (ldns_signing_algorithm) alg);
+	if (!k->_key.key) {
+                ldns_key_free(k);
+		return LDNS_STATUS_ENGINE_KEY_NOT_LOADED;
+	} 
+#endif /* splint */
+	*key = k;
+	return LDNS_STATUS_OK;
+}
+#endif
+
+#ifdef USE_GOST
+/** store GOST engine reference loaded into OpenSSL library */
+ENGINE* ldns_gost_engine = NULL;
+
+int
+ldns_key_EVP_load_gost_id(void)
+{
+	static int gost_id = 0;
+	const EVP_PKEY_ASN1_METHOD* meth;
+	ENGINE* e;
+
+	if(gost_id) return gost_id;
+
+	/* see if configuration loaded gost implementation from other engine*/
+	meth = EVP_PKEY_asn1_find_str(NULL, "gost2001", -1);
+	if(meth) {
+		EVP_PKEY_asn1_get0_info(&gost_id, NULL, NULL, NULL, NULL, meth);
+		return gost_id;
+	}
+
+	/* see if engine can be loaded already */
+	e = ENGINE_by_id("gost");
+	if(!e) {
+		/* load it ourself, in case statically linked */
+		ENGINE_load_builtin_engines();
+		ENGINE_load_dynamic();
+		e = ENGINE_by_id("gost");
+	}
+	if(!e) {
+		/* no gost engine in openssl */
+		return 0;
+	}
+	if(!ENGINE_set_default(e, ENGINE_METHOD_ALL)) {
+		ENGINE_finish(e);
+		ENGINE_free(e);
+		return 0;
+	}
+
+	meth = EVP_PKEY_asn1_find_str(&e, "gost2001", -1);
+	if(!meth) {
+		/* algo not found */
+		ENGINE_finish(e);
+		ENGINE_free(e);
+		return 0;
+	}
+        /* Note: do not ENGINE_finish and ENGINE_free the acquired engine
+         * on some platforms this frees up the meth and unloads gost stuff */
+        ldns_gost_engine = e;
+	
+	EVP_PKEY_asn1_get0_info(&gost_id, NULL, NULL, NULL, NULL, meth);
+	return gost_id;
+} 
+
+void ldns_key_EVP_unload_gost(void)
+{
+        if(ldns_gost_engine) {
+                ENGINE_finish(ldns_gost_engine);
+                ENGINE_free(ldns_gost_engine);
+                ldns_gost_engine = NULL;
+        }
+}
+
+/** read GOST private key */
+static EVP_PKEY*
+ldns_key_new_frm_fp_gost_l(FILE* fp, int* line_nr)
+{
+	char token[16384];
+	const unsigned char* pp;
+	int gost_id;
+	EVP_PKEY* pkey;
+	ldns_rdf* b64rdf = NULL;
+
+	gost_id = ldns_key_EVP_load_gost_id();
+	if(!gost_id)
+		return NULL;
+
+	if (ldns_fget_keyword_data_l(fp, "GostAsn1", ": ", token, "\n", 
+		sizeof(token), line_nr) == -1)
+		return NULL;
+	while(strlen(token) < 96) {
+		/* read more b64 from the file, b64 split on multiple lines */
+		if(ldns_fget_token_l(fp, token+strlen(token), "\n",
+			sizeof(token)-strlen(token), line_nr) == -1)
+			return NULL;
+	}
+	if(ldns_str2rdf_b64(&b64rdf, token) != LDNS_STATUS_OK)
+		return NULL;
+	pp = (unsigned char*)ldns_rdf_data(b64rdf);
+	pkey = d2i_PrivateKey(gost_id, NULL, &pp, (int)ldns_rdf_size(b64rdf));
+	ldns_rdf_deep_free(b64rdf);
+	return pkey;
+}
+#endif
+
+#ifdef USE_ECDSA
+/** calculate public key from private key */
+static int
+ldns_EC_KEY_calc_public(EC_KEY* ec)
+{
+        EC_POINT* pub_key;
+        const EC_GROUP* group;
+        group = EC_KEY_get0_group(ec);
+        pub_key = EC_POINT_new(group);
+        if(!pub_key) return 0;
+        if(!EC_POINT_copy(pub_key, EC_GROUP_get0_generator(group))) {
+                EC_POINT_free(pub_key);
+                return 0;
+        }
+        if(!EC_POINT_mul(group, pub_key, EC_KEY_get0_private_key(ec),
+                NULL, NULL, NULL)) {
+                EC_POINT_free(pub_key);
+                return 0;
+        }
+        if(EC_KEY_set_public_key(ec, pub_key) == 0) {
+                EC_POINT_free(pub_key);
+                return 0;
+        }
+        EC_POINT_free(pub_key);
+        return 1;
+}
+
+/** read ECDSA private key */
+static EVP_PKEY*
+ldns_key_new_frm_fp_ecdsa_l(FILE* fp, ldns_algorithm alg, int* line_nr)
+{
+	char token[16384];
+        ldns_rdf* b64rdf = NULL;
+        unsigned char* pp;
+        BIGNUM* bn;
+        EVP_PKEY* evp_key;
+        EC_KEY* ec;
+	if (ldns_fget_keyword_data_l(fp, "PrivateKey", ": ", token, "\n", 
+		sizeof(token), line_nr) == -1)
+		return NULL;
+	if(ldns_str2rdf_b64(&b64rdf, token) != LDNS_STATUS_OK)
+		return NULL;
+        pp = (unsigned char*)ldns_rdf_data(b64rdf);
+
+        if(alg == LDNS_ECDSAP256SHA256)
+                ec = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
+        else if(alg == LDNS_ECDSAP384SHA384)
+                ec = EC_KEY_new_by_curve_name(NID_secp384r1);
+        else    ec = NULL;
+        if(!ec) {
+	        ldns_rdf_deep_free(b64rdf);
+                return NULL;
+        }
+	bn = BN_bin2bn(pp, (int)ldns_rdf_size(b64rdf), NULL);
+	ldns_rdf_deep_free(b64rdf);
+        if(!bn) {
+                EC_KEY_free(ec);
+                return NULL;
+        }
+        EC_KEY_set_private_key(ec, bn);
+        BN_free(bn);
+        if(!ldns_EC_KEY_calc_public(ec)) {
+                EC_KEY_free(ec);
+                return NULL;
+        }
+
+        evp_key = EVP_PKEY_new();
+        if(!evp_key) {
+                EC_KEY_free(ec);
+                return NULL;
+        }
+        if (!EVP_PKEY_assign_EC_KEY(evp_key, ec)) {
+		EVP_PKEY_free(evp_key);
+                EC_KEY_free(ec);
+                return NULL;
+	}
+        return evp_key;
+}
+#endif
+	
+ldns_status
+ldns_key_new_frm_fp_l(ldns_key **key, FILE *fp, int *line_nr)
+{
+	ldns_key *k;
+	char *d;
+	ldns_signing_algorithm alg;
+	ldns_rr *key_rr;
+#ifdef HAVE_SSL
+	RSA *rsa;
+	DSA *dsa;
+	unsigned char *hmac;
+	size_t hmac_size;
+#endif /* HAVE_SSL */
+
+	k = ldns_key_new();
+
+	d = LDNS_XMALLOC(char, LDNS_MAX_LINELEN);
+	if (!k || !d) {
+                ldns_key_free(k);
+                LDNS_FREE(d);
+		return LDNS_STATUS_MEM_ERR;
+	}
+
+	alg = 0;
+
+	/* the file is highly structured. Do this in sequence */
+	/* RSA:
+	 * Private-key-format: v1.x.
+ 	 * Algorithm: 1 (RSA)
+
+	 */
+	/* get the key format version number */
+	if (ldns_fget_keyword_data_l(fp, "Private-key-format", ": ", d, "\n",
+				LDNS_MAX_LINELEN, line_nr) == -1) {
+		/* no version information */
+                ldns_key_free(k);
+                LDNS_FREE(d);
+		return LDNS_STATUS_SYNTAX_ERR;
+	}
+	if (strncmp(d, "v1.", 3) != 0) {
+                ldns_key_free(k);
+                LDNS_FREE(d);
+		return LDNS_STATUS_SYNTAX_VERSION_ERR;
+	}
+
+	/* get the algorithm type, our file function strip ( ) so there are
+	 * not in the return string! */
+	if (ldns_fget_keyword_data_l(fp, "Algorithm", ": ", d, "\n",
+				LDNS_MAX_LINELEN, line_nr) == -1) {
+		/* no alg information */
+                ldns_key_free(k);
+                LDNS_FREE(d);
+		return LDNS_STATUS_SYNTAX_ALG_ERR;
+	}
+
+	if (strncmp(d, "1 RSA", 2) == 0) {
+		alg = LDNS_SIGN_RSAMD5;
+	}
+	if (strncmp(d, "2 DH", 2) == 0) {
+		alg = (ldns_signing_algorithm)LDNS_DH;
+	}
+	if (strncmp(d, "3 DSA", 2) == 0) {
+		alg = LDNS_SIGN_DSA;
+	}
+	if (strncmp(d, "4 ECC", 2) == 0) {
+		alg = (ldns_signing_algorithm)LDNS_ECC;
+	}
+	if (strncmp(d, "5 RSASHA1", 2) == 0) {
+		alg = LDNS_SIGN_RSASHA1;
+	}
+	if (strncmp(d, "6 DSA", 2) == 0) {
+		alg = LDNS_SIGN_DSA_NSEC3;
+	}
+	if (strncmp(d, "7 RSASHA1", 2) == 0) {
+		alg = LDNS_SIGN_RSASHA1_NSEC3;
+	}
+
+	if (strncmp(d, "8 RSASHA256", 2) == 0) {
+#ifdef USE_SHA2
+		alg = LDNS_SIGN_RSASHA256;
+#else
+		fprintf(stderr, "Warning: SHA256 not compiled into this ");
+		fprintf(stderr, "version of ldns\n");
+#endif
+	}
+	if (strncmp(d, "10 RSASHA512", 3) == 0) {
+#ifdef USE_SHA2
+		alg = LDNS_SIGN_RSASHA512;
+#else
+		fprintf(stderr, "Warning: SHA512 not compiled into this ");
+		fprintf(stderr, "version of ldns\n");
+#endif
+	}
+	if (strncmp(d, "12 ECC-GOST", 3) == 0) {
+#ifdef USE_GOST
+		alg = LDNS_SIGN_ECC_GOST;
+#else
+		fprintf(stderr, "Warning: ECC-GOST not compiled into this ");
+		fprintf(stderr, "version of ldns, use --enable-gost\n");
+#endif
+	}
+#ifdef USE_ECDSA
+	if (strncmp(d, "13 ECDSAP256SHA256", 3) == 0) {
+                alg = LDNS_SIGN_ECDSAP256SHA256;
+        }
+	if (strncmp(d, "14 ECDSAP384SHA384", 3) == 0) {
+                alg = LDNS_SIGN_ECDSAP384SHA384;
+        }
+#endif
+	if (strncmp(d, "157 HMAC-MD5", 4) == 0) {
+		alg = LDNS_SIGN_HMACMD5;
+	}
+	if (strncmp(d, "158 HMAC-SHA1", 4) == 0) {
+		alg = LDNS_SIGN_HMACSHA1;
+	}
+	if (strncmp(d, "159 HMAC-SHA256", 4) == 0) {
+		alg = LDNS_SIGN_HMACSHA256;
+	}
+
+	LDNS_FREE(d);
+
+	switch(alg) {
+		case LDNS_SIGN_RSAMD5:
+		case LDNS_SIGN_RSASHA1:
+		case LDNS_SIGN_RSASHA1_NSEC3:
+#ifdef USE_SHA2
+		case LDNS_SIGN_RSASHA256:
+		case LDNS_SIGN_RSASHA512:
+#endif
+			ldns_key_set_algorithm(k, alg);
+#ifdef HAVE_SSL
+			rsa = ldns_key_new_frm_fp_rsa_l(fp, line_nr);
+			if (!rsa) {
+				ldns_key_free(k);
+				return LDNS_STATUS_ERR;
+			}
+			ldns_key_set_rsa_key(k, rsa);
+			RSA_free(rsa);
+#endif /* HAVE_SSL */
+			break;
+		case LDNS_SIGN_DSA:
+		case LDNS_SIGN_DSA_NSEC3:
+			ldns_key_set_algorithm(k, alg);
+#ifdef HAVE_SSL
+			dsa = ldns_key_new_frm_fp_dsa_l(fp, line_nr);
+			if (!dsa) {
+				ldns_key_free(k);
+				return LDNS_STATUS_ERR;
+			}
+			ldns_key_set_dsa_key(k, dsa);
+			DSA_free(dsa);
+#endif /* HAVE_SSL */
+			break;
+		case LDNS_SIGN_HMACMD5:
+		case LDNS_SIGN_HMACSHA1:
+		case LDNS_SIGN_HMACSHA256:
+			ldns_key_set_algorithm(k, alg);
+#ifdef HAVE_SSL
+			hmac = ldns_key_new_frm_fp_hmac_l(fp, line_nr, &hmac_size);
+			if (!hmac) {
+				ldns_key_free(k);
+				return LDNS_STATUS_ERR;
+			}
+			ldns_key_set_hmac_size(k, hmac_size);
+			ldns_key_set_hmac_key(k, hmac);
+#endif /* HAVE_SSL */
+			break;
+		case LDNS_SIGN_ECC_GOST:
+			ldns_key_set_algorithm(k, alg);
+#if defined(HAVE_SSL) && defined(USE_GOST)
+                        if(!ldns_key_EVP_load_gost_id()) {
+				ldns_key_free(k);
+                                return LDNS_STATUS_CRYPTO_ALGO_NOT_IMPL;
+                        }
+			ldns_key_set_evp_key(k, 
+				ldns_key_new_frm_fp_gost_l(fp, line_nr));
+#ifndef S_SPLINT_S
+			if(!k->_key.key) {
+				ldns_key_free(k);
+				return LDNS_STATUS_ERR;
+			}
+#endif /* splint */
+#endif
+			break;
+#ifdef USE_ECDSA
+               case LDNS_SIGN_ECDSAP256SHA256:
+               case LDNS_SIGN_ECDSAP384SHA384:
+                        ldns_key_set_algorithm(k, alg);
+                        ldns_key_set_evp_key(k,
+                                ldns_key_new_frm_fp_ecdsa_l(fp, (ldns_algorithm)alg, line_nr));
+#ifndef S_SPLINT_S
+			if(!k->_key.key) {
+				ldns_key_free(k);
+				return LDNS_STATUS_ERR;
+			}
+#endif /* splint */
+			break;
+#endif
+		default:
+			ldns_key_free(k);
+			return LDNS_STATUS_SYNTAX_ALG_ERR;
+	}
+	key_rr = ldns_key2rr(k);
+	ldns_key_set_keytag(k, ldns_calc_keytag(key_rr));
+	ldns_rr_free(key_rr);
+
+	if (key) {
+		*key = k;
+		return LDNS_STATUS_OK;
+	}
+	return LDNS_STATUS_ERR;
+}
+
+#ifdef HAVE_SSL
+RSA *
+ldns_key_new_frm_fp_rsa(FILE *f)
+{
+	return ldns_key_new_frm_fp_rsa_l(f, NULL);
+}
+
+RSA *
+ldns_key_new_frm_fp_rsa_l(FILE *f, int *line_nr)
+{
+	/* we parse
+ 	 * Modulus:
+ 	 * PublicExponent:
+ 	 * PrivateExponent:
+ 	 * Prime1:
+ 	 * Prime2:
+ 	 * Exponent1:
+ 	 * Exponent2:
+ 	 * Coefficient:
+	 *
+	 * man 3 RSA:
+	 *
+	 * struct
+         *     {
+         *     BIGNUM *n;              // public modulus
+         *     BIGNUM *e;              // public exponent
+         *     BIGNUM *d;              // private exponent
+         *     BIGNUM *p;              // secret prime factor
+         *     BIGNUM *q;              // secret prime factor
+         *     BIGNUM *dmp1;           // d mod (p-1)
+         *     BIGNUM *dmq1;           // d mod (q-1)
+         *     BIGNUM *iqmp;           // q^-1 mod p
+         *     // ...
+	 *
+	 */
+	char *d;
+	RSA *rsa;
+	uint8_t *buf;
+	int i;
+
+	d = LDNS_XMALLOC(char, LDNS_MAX_LINELEN);
+	buf = LDNS_XMALLOC(uint8_t, LDNS_MAX_LINELEN);
+	rsa = RSA_new();
+	if (!d || !rsa || !buf) {
+                goto error;
+	}
+
+	/* I could use functions again, but that seems an overkill,
+	 * allthough this also looks tedious
+	 */
+
+	/* Modules, rsa->n */
+	if (ldns_fget_keyword_data_l(f, "Modulus", ": ", d, "\n", LDNS_MAX_LINELEN, line_nr) == -1) {
+		goto error;
+	}
+	i = ldns_b64_pton((const char*)d, buf, ldns_b64_ntop_calculate_size(strlen(d)));
+#ifndef S_SPLINT_S
+	rsa->n = BN_bin2bn((const char unsigned*)buf, i, NULL);
+	if (!rsa->n) {
+		goto error;
+	}
+
+	/* PublicExponent, rsa->e */
+	if (ldns_fget_keyword_data_l(f, "PublicExponent", ": ", d, "\n", LDNS_MAX_LINELEN, line_nr) == -1) {
+		goto error;
+	}
+	i = ldns_b64_pton((const char*)d, buf, ldns_b64_ntop_calculate_size(strlen(d)));
+	rsa->e = BN_bin2bn((const char unsigned*)buf, i, NULL);
+	if (!rsa->e) {
+		goto error;
+	}
+
+	/* PrivateExponent, rsa->d */
+	if (ldns_fget_keyword_data_l(f, "PrivateExponent", ": ", d, "\n", LDNS_MAX_LINELEN, line_nr) == -1) {
+		goto error;
+	}
+	i = ldns_b64_pton((const char*)d, buf, ldns_b64_ntop_calculate_size(strlen(d)));
+	rsa->d = BN_bin2bn((const char unsigned*)buf, i, NULL);
+	if (!rsa->d) {
+		goto error;
+	}
+
+	/* Prime1, rsa->p */
+	if (ldns_fget_keyword_data_l(f, "Prime1", ": ", d, "\n", LDNS_MAX_LINELEN, line_nr) == -1) {
+		goto error;
+	}
+	i = ldns_b64_pton((const char*)d, buf, ldns_b64_ntop_calculate_size(strlen(d)));
+	rsa->p = BN_bin2bn((const char unsigned*)buf, i, NULL);
+	if (!rsa->p) {
+		goto error;
+	}
+
+	/* Prime2, rsa->q */
+	if (ldns_fget_keyword_data_l(f, "Prime2", ": ", d, "\n", LDNS_MAX_LINELEN, line_nr) == -1) {
+		goto error;
+	}
+	i = ldns_b64_pton((const char*)d, buf, ldns_b64_ntop_calculate_size(strlen(d)));
+	rsa->q = BN_bin2bn((const char unsigned*)buf, i, NULL);
+	if (!rsa->q) {
+		goto error;
+	}
+
+	/* Exponent1, rsa->dmp1 */
+	if (ldns_fget_keyword_data_l(f, "Exponent1", ": ", d, "\n", LDNS_MAX_LINELEN, line_nr) == -1) {
+		goto error;
+	}
+	i = ldns_b64_pton((const char*)d, buf, ldns_b64_ntop_calculate_size(strlen(d)));
+	rsa->dmp1 = BN_bin2bn((const char unsigned*)buf, i, NULL);
+	if (!rsa->dmp1) {
+		goto error;
+	}
+
+	/* Exponent2, rsa->dmq1 */
+	if (ldns_fget_keyword_data_l(f, "Exponent2", ": ", d, "\n", LDNS_MAX_LINELEN, line_nr) == -1) {
+		goto error;
+	}
+	i = ldns_b64_pton((const char*)d, buf, ldns_b64_ntop_calculate_size(strlen(d)));
+	rsa->dmq1 = BN_bin2bn((const char unsigned*)buf, i, NULL);
+	if (!rsa->dmq1) {
+		goto error;
+	}
+
+	/* Coefficient, rsa->iqmp */
+	if (ldns_fget_keyword_data_l(f, "Coefficient", ": ", d, "\n", LDNS_MAX_LINELEN, line_nr) == -1) {
+		goto error;
+	}
+	i = ldns_b64_pton((const char*)d, buf, ldns_b64_ntop_calculate_size(strlen(d)));
+	rsa->iqmp = BN_bin2bn((const char unsigned*)buf, i, NULL);
+	if (!rsa->iqmp) {
+		goto error;
+	}
+#endif /* splint */
+
+	LDNS_FREE(buf);
+	LDNS_FREE(d);
+	return rsa;
+
+error:
+	RSA_free(rsa);
+	LDNS_FREE(d);
+	LDNS_FREE(buf);
+	return NULL;
+}
+
+DSA *
+ldns_key_new_frm_fp_dsa(FILE *f)
+{
+	return ldns_key_new_frm_fp_dsa_l(f, NULL);
+}
+
+DSA *
+ldns_key_new_frm_fp_dsa_l(FILE *f, int *line_nr)
+{
+	int i;
+	char *d;
+	DSA *dsa;
+	uint8_t *buf;
+
+	line_nr = line_nr;
+
+	d = LDNS_XMALLOC(char, LDNS_MAX_LINELEN);
+	buf = LDNS_XMALLOC(uint8_t, LDNS_MAX_LINELEN);
+	dsa = DSA_new();
+	if (!d || !dsa || !buf) {
+                goto error;
+	}
+
+	/* the line parser removes the () from the input... */
+
+	/* Prime, dsa->p */
+	if (ldns_fget_keyword_data_l(f, "Primep", ": ", d, "\n", LDNS_MAX_LINELEN, line_nr) == -1) {
+		goto error;
+	}
+	i = ldns_b64_pton((const char*)d, buf, ldns_b64_ntop_calculate_size(strlen(d)));
+#ifndef S_SPLINT_S
+	dsa->p = BN_bin2bn((const char unsigned*)buf, i, NULL);
+	if (!dsa->p) {
+		goto error;
+	}
+
+	/* Subprime, dsa->q */
+	if (ldns_fget_keyword_data_l(f, "Subprimeq", ": ", d, "\n", LDNS_MAX_LINELEN, line_nr) == -1) {
+		goto error;
+	}
+	i = ldns_b64_pton((const char*)d, buf, ldns_b64_ntop_calculate_size(strlen(d)));
+	dsa->q = BN_bin2bn((const char unsigned*)buf, i, NULL);
+	if (!dsa->q) {
+		goto error;
+	}
+
+	/* Base, dsa->g */
+	if (ldns_fget_keyword_data_l(f, "Baseg", ": ", d, "\n", LDNS_MAX_LINELEN, line_nr) == -1) {
+		goto error;
+	}
+	i = ldns_b64_pton((const char*)d, buf, ldns_b64_ntop_calculate_size(strlen(d)));
+	dsa->g = BN_bin2bn((const char unsigned*)buf, i, NULL);
+	if (!dsa->g) {
+		goto error;
+	}
+
+	/* Private key, dsa->priv_key */
+	if (ldns_fget_keyword_data_l(f, "Private_valuex", ": ", d, "\n", LDNS_MAX_LINELEN, line_nr) == -1) {
+		goto error;
+	}
+	i = ldns_b64_pton((const char*)d, buf, ldns_b64_ntop_calculate_size(strlen(d)));
+	dsa->priv_key = BN_bin2bn((const char unsigned*)buf, i, NULL);
+	if (!dsa->priv_key) {
+		goto error;
+	}
+
+	/* Public key, dsa->priv_key */
+	if (ldns_fget_keyword_data_l(f, "Public_valuey", ": ", d, "\n", LDNS_MAX_LINELEN, line_nr) == -1) {
+		goto error;
+	}
+	i = ldns_b64_pton((const char*)d, buf, ldns_b64_ntop_calculate_size(strlen(d)));
+	dsa->pub_key = BN_bin2bn((const char unsigned*)buf, i, NULL);
+	if (!dsa->pub_key) {
+		goto error;
+	}
+#endif /* splint */
+
+	LDNS_FREE(buf);
+	LDNS_FREE(d);
+
+	return dsa;
+
+error:
+	LDNS_FREE(d);
+	LDNS_FREE(buf);
+        DSA_free(dsa);
+	return NULL;
+}
+
+unsigned char *
+ldns_key_new_frm_fp_hmac(FILE *f, size_t *hmac_size)
+{
+	return ldns_key_new_frm_fp_hmac_l(f, NULL, hmac_size);
+}
+
+unsigned char *
+ldns_key_new_frm_fp_hmac_l(FILE *f, int *line_nr, size_t *hmac_size)
+{
+	size_t i;
+	char *d;
+	unsigned char *buf;
+
+	line_nr = line_nr;
+
+	d = LDNS_XMALLOC(char, LDNS_MAX_LINELEN);
+	buf = LDNS_XMALLOC(unsigned char, LDNS_MAX_LINELEN);
+        if(!d || !buf) {
+                goto error;
+        }
+
+	if (ldns_fget_keyword_data_l(f, "Key", ": ", d, "\n", LDNS_MAX_LINELEN, line_nr) == -1) {
+		goto error;
+	}
+	i = (size_t) ldns_b64_pton((const char*)d,
+	                           buf,
+	                           ldns_b64_ntop_calculate_size(strlen(d)));
+
+	*hmac_size = i;
+	return buf;
+
+	error:
+	LDNS_FREE(d);
+	LDNS_FREE(buf);
+	*hmac_size = 0;
+	return NULL;
+}
+#endif /* HAVE_SSL */
+
+#ifdef USE_GOST
+static EVP_PKEY*
+ldns_gen_gost_key(void)
+{
+	EVP_PKEY_CTX* ctx;
+	EVP_PKEY* p = NULL;
+	int gost_id = ldns_key_EVP_load_gost_id();
+	if(!gost_id)
+		return NULL;
+	ctx = EVP_PKEY_CTX_new_id(gost_id, NULL);
+	if(!ctx) {
+		/* the id should be available now */
+		return NULL;
+	}
+	if(EVP_PKEY_CTX_ctrl_str(ctx, "paramset", "A") <= 0) {
+		/* cannot set paramset */
+		EVP_PKEY_CTX_free(ctx);
+		return NULL;
+	}
+
+	if(EVP_PKEY_keygen_init(ctx) <= 0) {
+		EVP_PKEY_CTX_free(ctx);
+		return NULL;
+	}
+	if(EVP_PKEY_keygen(ctx, &p) <= 0) {
+		EVP_PKEY_free(p);
+		EVP_PKEY_CTX_free(ctx);
+		return NULL;
+	}
+	EVP_PKEY_CTX_free(ctx);
+	return p;
+}
+#endif
+
+ldns_key *
+ldns_key_new_frm_algorithm(ldns_signing_algorithm alg, uint16_t size)
+{
+	ldns_key *k;
+#ifdef HAVE_SSL
+	DSA *d;
+	RSA *r;
+#  ifdef USE_ECDSA
+        EC_KEY *ec = NULL;
+#  endif
+#else
+	int i;
+	uint16_t offset = 0;
+#endif
+	unsigned char *hmac;
+
+	k = ldns_key_new();
+	if (!k) {
+		return NULL;
+	}
+	switch(alg) {
+		case LDNS_SIGN_RSAMD5:
+		case LDNS_SIGN_RSASHA1:
+		case LDNS_SIGN_RSASHA1_NSEC3:
+		case LDNS_SIGN_RSASHA256:
+		case LDNS_SIGN_RSASHA512:
+#ifdef HAVE_SSL
+			r = RSA_generate_key((int)size, RSA_F4, NULL, NULL);
+                        if(!r) {
+				ldns_key_free(k);
+				return NULL;
+			}
+			if (RSA_check_key(r) != 1) {
+				ldns_key_free(k);
+				return NULL;
+			}
+			ldns_key_set_rsa_key(k, r);
+#endif /* HAVE_SSL */
+			break;
+		case LDNS_SIGN_DSA:
+		case LDNS_SIGN_DSA_NSEC3:
+#ifdef HAVE_SSL
+			d = DSA_generate_parameters((int)size, NULL, 0, NULL, NULL, NULL, NULL);
+			if (!d) {
+				ldns_key_free(k);
+				return NULL;
+			}
+			if (DSA_generate_key(d) != 1) {
+				ldns_key_free(k);
+				return NULL;
+			}
+			ldns_key_set_dsa_key(k, d);
+#endif /* HAVE_SSL */
+			break;
+		case LDNS_SIGN_HMACMD5:
+		case LDNS_SIGN_HMACSHA1:
+		case LDNS_SIGN_HMACSHA256:
+#ifdef HAVE_SSL
+#ifndef S_SPLINT_S
+			k->_key.key = NULL;
+#endif /* splint */
+#endif /* HAVE_SSL */
+			size = size / 8;
+			ldns_key_set_hmac_size(k, size);
+
+			hmac = LDNS_XMALLOC(unsigned char, size);
+                        if(!hmac) {
+				ldns_key_free(k);
+				return NULL;
+                        }
+#ifdef HAVE_SSL
+			if (RAND_bytes(hmac, (int) size) != 1) {
+				LDNS_FREE(hmac);
+				ldns_key_free(k);
+				return NULL;
+			}
+#else
+			while (offset + sizeof(i) < size) {
+			  i = random();
+			  memcpy(&hmac[offset], &i, sizeof(i));
+			  offset += sizeof(i);
+			}
+			if (offset < size) {
+			  i = random();
+			  memcpy(&hmac[offset], &i, size - offset);
+			}
+#endif /* HAVE_SSL */
+			ldns_key_set_hmac_key(k, hmac);
+
+			ldns_key_set_flags(k, 0);
+			break;
+		case LDNS_SIGN_ECC_GOST:
+#if defined(HAVE_SSL) && defined(USE_GOST)
+			ldns_key_set_evp_key(k, ldns_gen_gost_key());
+#ifndef S_SPLINT_S
+                        if(!k->_key.key) {
+                                ldns_key_free(k);
+                                return NULL;
+                        }
+#endif /* splint */
+#endif /* HAVE_SSL and USE_GOST */
+                        break;
+#ifdef USE_ECDSA
+                case LDNS_SIGN_ECDSAP256SHA256:
+                case LDNS_SIGN_ECDSAP384SHA384:
+                        if(alg == LDNS_SIGN_ECDSAP256SHA256)
+                                ec = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
+                        else if(alg == LDNS_SIGN_ECDSAP384SHA384)
+                                ec = EC_KEY_new_by_curve_name(NID_secp384r1);
+                        if(!ec) {
+                                ldns_key_free(k);
+                                return NULL;
+                        }
+                        if(!EC_KEY_generate_key(ec)) {
+                                ldns_key_free(k);
+                                EC_KEY_free(ec);
+                                return NULL;
+                        }
+#ifndef S_SPLINT_S
+                        k->_key.key = EVP_PKEY_new();
+                        if(!k->_key.key) {
+                                ldns_key_free(k);
+                                EC_KEY_free(ec);
+                                return NULL;
+                        }
+                        if (!EVP_PKEY_assign_EC_KEY(k->_key.key, ec)) {
+                                ldns_key_free(k);
+                                EC_KEY_free(ec);
+                                return NULL;
+			}
+#endif /* splint */
+			break;
+#endif
+	}
+	ldns_key_set_algorithm(k, alg);
+	return k;
+}
+
+void
+ldns_key_print(FILE *output, const ldns_key *k)
+{
+	char *str = ldns_key2str(k);
+	if (str) {
+                fprintf(output, "%s", str);
+        } else {
+                fprintf(output, "Unable to convert private key to string\n");
+        }
+        LDNS_FREE(str);
+}
+
+
+void
+ldns_key_set_algorithm(ldns_key *k, ldns_signing_algorithm l)
+{
+	k->_alg = l;
+}
+
+void
+ldns_key_set_flags(ldns_key *k, uint16_t f)
+{
+	k->_extra.dnssec.flags = f;
+}
+
+#ifdef HAVE_SSL
+#ifndef S_SPLINT_S
+void
+ldns_key_set_evp_key(ldns_key *k, EVP_PKEY *e)
+{
+	k->_key.key = e;
+}
+
+void
+ldns_key_set_rsa_key(ldns_key *k, RSA *r)
+{
+	EVP_PKEY *key = EVP_PKEY_new();
+	EVP_PKEY_set1_RSA(key, r);
+	k->_key.key = key;
+}
+
+void
+ldns_key_set_dsa_key(ldns_key *k, DSA *d)
+{
+	EVP_PKEY *key = EVP_PKEY_new();
+	EVP_PKEY_set1_DSA(key, d);
+	k->_key.key  = key;
+}
+#endif /* splint */
+#endif /* HAVE_SSL */
+
+void
+ldns_key_set_hmac_key(ldns_key *k, unsigned char *hmac)
+{
+	k->_key.hmac.key = hmac;
+}
+
+void
+ldns_key_set_hmac_size(ldns_key *k, size_t hmac_size)
+{
+	k->_key.hmac.size = hmac_size;
+}
+
+void
+ldns_key_set_external_key(ldns_key *k, void *external_key)
+{
+	k->_key.external_key = external_key;
+}
+
+void
+ldns_key_set_origttl(ldns_key *k, uint32_t t)
+{
+	k->_extra.dnssec.orig_ttl = t;
+}
+
+void
+ldns_key_set_inception(ldns_key *k, uint32_t i)
+{
+	k->_extra.dnssec.inception = i;
+}
+
+void
+ldns_key_set_expiration(ldns_key *k, uint32_t e)
+{
+	k->_extra.dnssec.expiration = e;
+}
+
+void
+ldns_key_set_pubkey_owner(ldns_key *k, ldns_rdf *r)
+{
+	k->_pubkey_owner = r;
+}
+
+void
+ldns_key_set_keytag(ldns_key *k, uint16_t tag)
+{
+	k->_extra.dnssec.keytag = tag;
+}
+
+/* read */
+size_t
+ldns_key_list_key_count(const ldns_key_list *key_list)
+{
+	        return key_list->_key_count;
+}       
+
+ldns_key *
+ldns_key_list_key(const ldns_key_list *key, size_t nr)
+{       
+	if (nr < ldns_key_list_key_count(key)) {
+		return key->_keys[nr];
+	} else {
+		return NULL;
+	}
+}
+
+ldns_signing_algorithm
+ldns_key_algorithm(const ldns_key *k) 
+{
+	return k->_alg;
+}
+
+void
+ldns_key_set_use(ldns_key *k, bool v)
+{
+	if (k) {
+		k->_use = v;
+	}
+}
+
+bool
+ldns_key_use(const ldns_key *k)
+{
+	if (k) {
+		return k->_use;
+	}
+	return false;
+}
+
+#ifdef HAVE_SSL
+#ifndef S_SPLINT_S
+EVP_PKEY *
+ldns_key_evp_key(const ldns_key *k)
+{
+	return k->_key.key;
+}
+
+RSA *
+ldns_key_rsa_key(const ldns_key *k)
+{
+	if (k->_key.key) {
+		return EVP_PKEY_get1_RSA(k->_key.key);
+	} else {
+		return NULL;
+	}
+}
+
+DSA *
+ldns_key_dsa_key(const ldns_key *k)
+{
+	if (k->_key.key) {
+		return EVP_PKEY_get1_DSA(k->_key.key);
+	} else {
+		return NULL;
+	}
+}
+#endif /* splint */
+#endif /* HAVE_SSL */
+
+unsigned char *
+ldns_key_hmac_key(const ldns_key *k)
+{
+	if (k->_key.hmac.key) {
+		return k->_key.hmac.key;
+	} else {
+		return NULL;
+	}
+}
+
+size_t
+ldns_key_hmac_size(const ldns_key *k)
+{
+	if (k->_key.hmac.size) {
+		return k->_key.hmac.size;
+	} else {
+		return 0;
+	}
+}
+
+void *
+ldns_key_external_key(const ldns_key *k)
+{
+	return k->_key.external_key;
+}
+
+uint32_t
+ldns_key_origttl(const ldns_key *k)
+{
+	return k->_extra.dnssec.orig_ttl;
+}
+
+uint16_t
+ldns_key_flags(const ldns_key *k)
+{
+	return k->_extra.dnssec.flags;
+}
+
+uint32_t
+ldns_key_inception(const ldns_key *k)
+{
+	return k->_extra.dnssec.inception;
+}
+
+uint32_t
+ldns_key_expiration(const ldns_key *k)
+{
+	return k->_extra.dnssec.expiration;
+}
+
+uint16_t
+ldns_key_keytag(const ldns_key *k)
+{
+	return k->_extra.dnssec.keytag;
+}
+
+ldns_rdf *
+ldns_key_pubkey_owner(const ldns_key *k)
+{
+	return k->_pubkey_owner;
+}
+
+/* write */
+void
+ldns_key_list_set_use(ldns_key_list *keys, bool v)
+{
+	size_t i;
+
+	for (i = 0; i < ldns_key_list_key_count(keys); i++) {
+		ldns_key_set_use(ldns_key_list_key(keys, i), v);
+	}
+}
+
+void            
+ldns_key_list_set_key_count(ldns_key_list *key, size_t count)
+{
+	        key->_key_count = count;
+}       
+
+bool             
+ldns_key_list_push_key(ldns_key_list *key_list, ldns_key *key)
+{       
+        size_t key_count;
+        ldns_key **keys;
+
+        key_count = ldns_key_list_key_count(key_list);
+
+        /* grow the array */
+        keys = LDNS_XREALLOC(
+                key_list->_keys, ldns_key *, key_count + 1);
+        if (!keys) {
+                return false;
+        }
+
+        /* add the new member */
+        key_list->_keys = keys;
+        key_list->_keys[key_count] = key;
+
+        ldns_key_list_set_key_count(key_list, key_count + 1);
+        return true;
+}
+
+ldns_key *
+ldns_key_list_pop_key(ldns_key_list *key_list)
+{                               
+        size_t key_count;
+        ldns_key** a;
+        ldns_key *pop;
+
+	if (!key_list) {
+		return NULL;
+	}
+        
+        key_count = ldns_key_list_key_count(key_list);
+        if (key_count == 0) {
+                return NULL;
+        }       
+        
+        pop = ldns_key_list_key(key_list, key_count);
+        
+        /* shrink the array */
+        a = LDNS_XREALLOC(key_list->_keys, ldns_key *, key_count - 1);
+        if(a) {
+                key_list->_keys = a;
+        }
+
+        ldns_key_list_set_key_count(key_list, key_count - 1);
+
+        return pop;
+}       
+
+#ifdef HAVE_SSL
+#ifndef S_SPLINT_S
+/* data pointer must be large enough (LDNS_MAX_KEYLEN) */
+static bool
+ldns_key_rsa2bin(unsigned char *data, RSA *k, uint16_t *size)
+{
+	int i,j;
+	
+	if (!k) {
+		return false;
+	}
+	
+	if (BN_num_bytes(k->e) <= 256) {
+		/* normally only this path is executed (small factors are
+		 * more common 
+		 */
+		data[0] = (unsigned char) BN_num_bytes(k->e);
+		i = BN_bn2bin(k->e, data + 1);  
+		j = BN_bn2bin(k->n, data + i + 1);
+		*size = (uint16_t) i + j;
+	} else if (BN_num_bytes(k->e) <= 65536) {
+		data[0] = 0;
+		/* BN_bn2bin does bigendian, _uint16 also */
+		ldns_write_uint16(data + 1, (uint16_t) BN_num_bytes(k->e)); 
+
+		BN_bn2bin(k->e, data + 3); 
+		BN_bn2bin(k->n, data + 4 + BN_num_bytes(k->e));
+		*size = (uint16_t) BN_num_bytes(k->n) + 6;
+	} else {
+		return false;
+	}
+	return true;
+}
+
+/* data pointer must be large enough (LDNS_MAX_KEYLEN) */
+static bool
+ldns_key_dsa2bin(unsigned char *data, DSA *k, uint16_t *size)
+{
+	uint8_t T;
+
+	if (!k) {
+		return false;
+	}
+	
+	/* See RFC2536 */
+	*size = (uint16_t)BN_num_bytes(k->g);
+	T = (*size - 64) / 8;
+	memcpy(data, &T, 1);
+
+	if (T > 8) {
+		fprintf(stderr, "DSA key with T > 8 (ie. > 1024 bits)");
+		fprintf(stderr, " not implemented\n");
+		return false;
+	}
+
+	/* size = 64 + (T * 8); */
+	data[0] = (unsigned char)T;
+	BN_bn2bin(k->q, data + 1 ); 		/* 20 octects */
+	BN_bn2bin(k->p, data + 21 ); 		/* offset octects */
+	BN_bn2bin(k->g, data + 21 + *size); 	/* offset octets */
+	BN_bn2bin(k->pub_key, data + 21 + *size + *size); /* offset octets */
+	*size = 21 + (*size * 3);
+	return true;
+}
+
+#ifdef USE_GOST
+static bool
+ldns_key_gost2bin(unsigned char* data, EVP_PKEY* k, uint16_t* size)
+{
+	int i;
+	unsigned char* pp = NULL;
+	if(i2d_PUBKEY(k, &pp) != 37 + 64) {
+		/* expect 37 byte(ASN header) and 64 byte(X and Y) */
+		CRYPTO_free(pp);
+		return false;
+	}
+	/* omit ASN header */
+	for(i=0; i<64; i++)
+		data[i] = pp[i+37];
+	CRYPTO_free(pp);
+	*size = 64;
+	return true;
+}
+#endif /* USE_GOST */
+#endif /* splint */
+#endif /* HAVE_SSL */
+
+ldns_rr *
+ldns_key2rr(const ldns_key *k)
+{
+	/* this function will convert a the keydata contained in
+	 * rsa/dsa pointers to a DNSKEY rr. It will fill in as
+	 * much as it can, but it does not know about key-flags
+	 * for instance
+	 */
+	ldns_rr *pubkey;
+	ldns_rdf *keybin;
+	unsigned char *bin = NULL;
+	uint16_t size = 0;
+#ifdef HAVE_SSL
+	RSA *rsa = NULL;
+	DSA *dsa = NULL;
+#endif /* HAVE_SSL */
+#ifdef USE_ECDSA
+        EC_KEY* ec;
+#endif
+	int internal_data = 0;
+
+	pubkey = ldns_rr_new();
+	if (!k) {
+		return NULL;
+	}
+
+	switch (ldns_key_algorithm(k)) {
+	case LDNS_SIGN_HMACMD5:
+	case LDNS_SIGN_HMACSHA1:
+	case LDNS_SIGN_HMACSHA256:
+		ldns_rr_set_type(pubkey, LDNS_RR_TYPE_KEY);
+        	break;
+	default:
+		ldns_rr_set_type(pubkey, LDNS_RR_TYPE_DNSKEY);
+		break;
+        }
+	/* zero-th rdf - flags */
+	ldns_rr_push_rdf(pubkey,
+			ldns_native2rdf_int16(LDNS_RDF_TYPE_INT16,
+				ldns_key_flags(k)));
+	/* first - proto */
+	ldns_rr_push_rdf(pubkey,
+			ldns_native2rdf_int8(LDNS_RDF_TYPE_INT8, LDNS_DNSSEC_KEYPROTO));
+
+	if (ldns_key_pubkey_owner(k)) {
+		ldns_rr_set_owner(pubkey, ldns_rdf_clone(ldns_key_pubkey_owner(k)));
+	}
+
+	/* third - da algorithm */
+	switch(ldns_key_algorithm(k)) {
+		case LDNS_SIGN_RSAMD5:
+		case LDNS_SIGN_RSASHA1:
+		case LDNS_SIGN_RSASHA1_NSEC3:
+		case LDNS_SIGN_RSASHA256:
+		case LDNS_SIGN_RSASHA512:
+			ldns_rr_push_rdf(pubkey,
+						  ldns_native2rdf_int8(LDNS_RDF_TYPE_ALG, ldns_key_algorithm(k)));
+#ifdef HAVE_SSL
+			rsa =  ldns_key_rsa_key(k);
+			if (rsa) {
+				bin = LDNS_XMALLOC(unsigned char, LDNS_MAX_KEYLEN);
+				if (!bin) {
+                                        ldns_rr_free(pubkey);
+					return NULL;
+				}
+				if (!ldns_key_rsa2bin(bin, rsa, &size)) {
+		                        LDNS_FREE(bin);
+                                        ldns_rr_free(pubkey);
+					return NULL;
+				}
+				RSA_free(rsa);
+				internal_data = 1;
+			}
+#endif
+			size++;
+			break;
+		case LDNS_SIGN_DSA:
+			ldns_rr_push_rdf(pubkey,
+					ldns_native2rdf_int8(LDNS_RDF_TYPE_ALG, LDNS_DSA));
+#ifdef HAVE_SSL
+			dsa = ldns_key_dsa_key(k);
+			if (dsa) {
+				bin = LDNS_XMALLOC(unsigned char, LDNS_MAX_KEYLEN);
+				if (!bin) {
+                                        ldns_rr_free(pubkey);
+					return NULL;
+				}
+				if (!ldns_key_dsa2bin(bin, dsa, &size)) {
+		                        LDNS_FREE(bin);
+                                        ldns_rr_free(pubkey);
+					return NULL;
+				}
+				DSA_free(dsa);
+				internal_data = 1;
+			}
+#endif /* HAVE_SSL */
+			break;
+		case LDNS_SIGN_DSA_NSEC3:
+			ldns_rr_push_rdf(pubkey,
+					ldns_native2rdf_int8(LDNS_RDF_TYPE_ALG, LDNS_DSA_NSEC3));
+#ifdef HAVE_SSL
+			dsa = ldns_key_dsa_key(k);
+			if (dsa) {
+				bin = LDNS_XMALLOC(unsigned char, LDNS_MAX_KEYLEN);
+				if (!bin) {
+                                        ldns_rr_free(pubkey);
+					return NULL;
+				}
+				if (!ldns_key_dsa2bin(bin, dsa, &size)) {
+		                        LDNS_FREE(bin);
+                                        ldns_rr_free(pubkey);
+					return NULL;
+				}
+				DSA_free(dsa);
+				internal_data = 1;
+			}
+#endif /* HAVE_SSL */
+			break;
+		case LDNS_SIGN_ECC_GOST:
+			ldns_rr_push_rdf(pubkey, ldns_native2rdf_int8(
+				LDNS_RDF_TYPE_ALG, ldns_key_algorithm(k)));
+#if defined(HAVE_SSL) && defined(USE_GOST)
+			bin = LDNS_XMALLOC(unsigned char, LDNS_MAX_KEYLEN);
+			if (!bin) {
+                                ldns_rr_free(pubkey);
+				return NULL;
+                        }
+#ifndef S_SPLINT_S
+			if (!ldns_key_gost2bin(bin, k->_key.key, &size)) {
+		                LDNS_FREE(bin);
+                                ldns_rr_free(pubkey);
+				return NULL;
+			}
+#endif /* splint */
+			internal_data = 1;
+#endif /* HAVE_SSL and USE_GOST */
+			break;
+#ifdef USE_ECDSA
+                case LDNS_SIGN_ECDSAP256SHA256:
+                case LDNS_SIGN_ECDSAP384SHA384:
+			ldns_rr_push_rdf(pubkey, ldns_native2rdf_int8(
+				LDNS_RDF_TYPE_ALG, ldns_key_algorithm(k)));
+                        bin = NULL;
+#ifndef S_SPLINT_S
+                        ec = EVP_PKEY_get1_EC_KEY(k->_key.key);
+#endif
+                        EC_KEY_set_conv_form(ec, POINT_CONVERSION_UNCOMPRESSED);
+                        size = (uint16_t)i2o_ECPublicKey(ec, NULL);
+                        if(!i2o_ECPublicKey(ec, &bin)) {
+                                EC_KEY_free(ec);
+                                ldns_rr_free(pubkey);
+                                return NULL;
+                        }
+			if(size > 1) {
+				/* move back one byte to shave off the 0x02
+				 * 'uncompressed' indicator that openssl made
+				 * Actually its 0x04 (from implementation).
+				 */
+				assert(bin[0] == POINT_CONVERSION_UNCOMPRESSED);
+				size -= 1;
+				memmove(bin, bin+1, size);
+			}
+                        /* down the reference count for ec, its still assigned
+                         * to the pkey */
+                        EC_KEY_free(ec);
+			internal_data = 1;
+                        break;
+#endif
+		case LDNS_SIGN_HMACMD5:
+		case LDNS_SIGN_HMACSHA1:
+		case LDNS_SIGN_HMACSHA256:
+			bin = LDNS_XMALLOC(unsigned char, ldns_key_hmac_size(k));
+			if (!bin) {
+                                ldns_rr_free(pubkey);
+				return NULL;
+			}
+			ldns_rr_push_rdf(pubkey,
+			                 ldns_native2rdf_int8(LDNS_RDF_TYPE_ALG,
+			                 ldns_key_algorithm(k)));
+			size = ldns_key_hmac_size(k);
+			memcpy(bin, ldns_key_hmac_key(k), size);
+			internal_data = 1;
+			break;
+	}
+	/* fourth the key bin material */
+	if (internal_data) {
+		keybin = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64, size, bin);
+		LDNS_FREE(bin);
+		ldns_rr_push_rdf(pubkey, keybin);
+	}
+	return pubkey;
+}
+
+void
+ldns_key_free(ldns_key *key)
+{
+	LDNS_FREE(key);
+}
+
+void
+ldns_key_deep_free(ldns_key *key)
+{
+	if (ldns_key_pubkey_owner(key)) {
+		ldns_rdf_deep_free(ldns_key_pubkey_owner(key));
+	}
+#ifdef HAVE_SSL
+	if (ldns_key_evp_key(key)) {
+		EVP_PKEY_free(ldns_key_evp_key(key));
+	}
+#endif /* HAVE_SSL */
+	if (ldns_key_hmac_key(key)) {
+		free(ldns_key_hmac_key(key));
+	}
+	LDNS_FREE(key);
+}
+
+void
+ldns_key_list_free(ldns_key_list *key_list)
+{
+	size_t i;
+	for (i = 0; i < ldns_key_list_key_count(key_list); i++) {
+		ldns_key_deep_free(ldns_key_list_key(key_list, i));
+	}
+	LDNS_FREE(key_list->_keys);
+	LDNS_FREE(key_list);
+}
+
+ldns_rr *
+ldns_read_anchor_file(const char *filename)
+{
+	FILE *fp;
+	/*char line[LDNS_MAX_PACKETLEN];*/
+	char *line = LDNS_XMALLOC(char, LDNS_MAX_PACKETLEN);
+	int c;
+	size_t i = 0;
+	ldns_rr *r;
+	ldns_status status;
+        if(!line) {
+                return NULL;
+        }
+
+	fp = fopen(filename, "r");
+	if (!fp) {
+		fprintf(stderr, "Unable to open %s: %s\n", filename, strerror(errno));
+		LDNS_FREE(line);
+		return NULL;
+	}
+	
+	while ((c = fgetc(fp)) && i+1 < LDNS_MAX_PACKETLEN && c != EOF) {
+		line[i] = c;
+		i++;
+	}
+	line[i] = '\0';
+	
+	fclose(fp);
+	
+	if (i <= 0) {
+		fprintf(stderr, "nothing read from %s", filename);
+		LDNS_FREE(line);
+		return NULL;
+	} else {
+		status = ldns_rr_new_frm_str(&r, line, 0, NULL, NULL);
+		if (status == LDNS_STATUS_OK && (ldns_rr_get_type(r) == LDNS_RR_TYPE_DNSKEY || ldns_rr_get_type(r) == LDNS_RR_TYPE_DS)) {
+			LDNS_FREE(line);
+			return r;
+		} else {
+			fprintf(stderr, "Error creating DNSKEY or DS rr from %s: %s\n", filename, ldns_get_errorstr_by_id(status));
+			LDNS_FREE(line);
+			return NULL;
+		}
+	}
+}
+
+char *
+ldns_key_get_file_base_name(ldns_key *key)
+{
+	ldns_buffer *buffer;
+	char *file_base_name;
+	
+	buffer = ldns_buffer_new(255);
+	ldns_buffer_printf(buffer, "K");
+	(void)ldns_rdf2buffer_str_dname(buffer, ldns_key_pubkey_owner(key));
+	ldns_buffer_printf(buffer,
+	                   "+%03u+%05u",
+			   ldns_key_algorithm(key),
+			   ldns_key_keytag(key));
+	file_base_name = strdup(ldns_buffer_export(buffer));
+	ldns_buffer_free(buffer);
+	return file_base_name;
+}
+
+int ldns_key_algo_supported(int algo)
+{
+	ldns_lookup_table *lt = ldns_signing_algorithms;
+	while(lt->name) {
+		if(lt->id == algo)
+			return 1;
+		lt++;
+	}
+	return 0;
+}
+
+ldns_signing_algorithm ldns_get_signing_algorithm_by_name(const char* name)
+{
+        /* list of (signing algorithm id, alias_name) */
+        ldns_lookup_table aliases[] = {
+                /* from bind dnssec-keygen */
+                {LDNS_SIGN_HMACMD5, "HMAC-MD5"},
+                {LDNS_SIGN_DSA_NSEC3, "NSEC3DSA"},
+                {LDNS_SIGN_RSASHA1_NSEC3, "NSEC3RSASHA1"},
+                /* old ldns usage, now RFC names */
+                {LDNS_SIGN_DSA_NSEC3, "DSA_NSEC3" },
+                {LDNS_SIGN_RSASHA1_NSEC3, "RSASHA1_NSEC3" },
+#ifdef USE_GOST
+                {LDNS_SIGN_ECC_GOST, "GOST"},
+#endif
+                /* compat with possible output */
+                {LDNS_DH, "DH"},
+                {LDNS_ECC, "ECC"},
+                {LDNS_INDIRECT, "INDIRECT"},
+                {LDNS_PRIVATEDNS, "PRIVATEDNS"},
+                {LDNS_PRIVATEOID, "PRIVATEOID"},
+                {0, NULL}};
+        ldns_lookup_table* lt = ldns_signing_algorithms;
+        while(lt->name) {
+                if(strcasecmp(lt->name, name) == 0)
+                        return lt->id;
+                lt++;
+        }
+        lt = aliases;
+        while(lt->name) {
+                if(strcasecmp(lt->name, name) == 0)
+                        return lt->id;
+                lt++;
+        }
+        if(atoi(name) != 0)
+                return atoi(name);
+        return 0;
+}
diff --git a/3rdParty/Ldns/src/src/linktest.c b/3rdParty/Ldns/src/src/linktest.c
new file mode 100644
index 0000000..c21753a
--- /dev/null
+++ b/3rdParty/Ldns/src/src/linktest.c
@@ -0,0 +1,13 @@
+
+#include "ldns/config.h"
+#include <ldns/ldns.h>
+
+int 
+main(void) 
+{
+  ldns_rr *rr = ldns_rr_new();
+
+  ldns_rr_free(rr);
+  return 0;
+}
+
diff --git a/3rdParty/Ldns/src/src/net.c b/3rdParty/Ldns/src/src/net.c
new file mode 100644
index 0000000..870511a
--- /dev/null
+++ b/3rdParty/Ldns/src/src/net.c
@@ -0,0 +1,907 @@
+/*
+ * net.c
+ *
+ * Network implementation
+ * All network related functions are grouped here
+ *
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2004-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+#include <ldns/config.h>
+
+#include <ldns/ldns.h>
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#include <sys/time.h>
+#include <errno.h>
+#include <fcntl.h>
+
+ldns_status
+ldns_send(ldns_pkt **result_packet, ldns_resolver *r, const ldns_pkt *query_pkt)
+{
+	ldns_buffer *qb;
+	ldns_status result;
+	ldns_rdf *tsig_mac = NULL;
+
+	qb = ldns_buffer_new(LDNS_MIN_BUFLEN);
+
+	if (query_pkt && ldns_pkt_tsig(query_pkt)) {
+		tsig_mac = ldns_rr_rdf(ldns_pkt_tsig(query_pkt), 3);
+	}
+
+	if (!query_pkt ||
+	    ldns_pkt2buffer_wire(qb, query_pkt) != LDNS_STATUS_OK) {
+		result = LDNS_STATUS_ERR;
+	} else {
+        	result = ldns_send_buffer(result_packet, r, qb, tsig_mac);
+	}
+
+	ldns_buffer_free(qb);
+
+	return result;
+}
+
+ldns_status
+ldns_send_buffer(ldns_pkt **result, ldns_resolver *r, ldns_buffer *qb, ldns_rdf *tsig_mac)
+{
+	uint8_t i;
+	
+	struct sockaddr_storage *ns;
+	size_t ns_len;
+	struct timeval tv_s;
+	struct timeval tv_e;
+
+	ldns_rdf **ns_array;
+	size_t *rtt;
+	ldns_pkt *reply;
+	bool all_servers_rtt_inf;
+	uint8_t retries;
+
+	uint8_t *reply_bytes = NULL;
+	size_t reply_size = 0;
+	ldns_status status, send_status;
+
+	assert(r != NULL);
+
+	status = LDNS_STATUS_OK;
+	rtt = ldns_resolver_rtt(r);
+	ns_array = ldns_resolver_nameservers(r);
+	reply = NULL; 
+	ns_len = 0;
+
+	all_servers_rtt_inf = true;
+
+	if (ldns_resolver_random(r)) {
+		ldns_resolver_nameservers_randomize(r);
+	}
+
+	/* loop through all defined nameservers */
+	for (i = 0; i < ldns_resolver_nameserver_count(r); i++) {
+		if (rtt[i] == LDNS_RESOLV_RTT_INF) {
+			/* not reachable nameserver! */
+			continue;
+		}
+
+		/* maybe verbosity setting?
+		printf("Sending to ");
+		ldns_rdf_print(stdout, ns_array[i]);
+		printf("\n");
+		*/
+		ns = ldns_rdf2native_sockaddr_storage(ns_array[i],
+				ldns_resolver_port(r), &ns_len);
+
+
+#ifndef S_SPLINT_S
+		if ((ns->ss_family == AF_INET) &&
+				(ldns_resolver_ip6(r) == LDNS_RESOLV_INET6)) {
+			/* not reachable */
+			continue;
+		}
+
+		if ((ns->ss_family == AF_INET6) &&
+				 (ldns_resolver_ip6(r) == LDNS_RESOLV_INET)) {
+			/* not reachable */
+			continue;
+		}
+#endif
+
+		all_servers_rtt_inf = false;
+
+		gettimeofday(&tv_s, NULL);
+
+		send_status = LDNS_STATUS_ERR;
+
+		/* reply_bytes implicitly handles our error */
+		if (1 == ldns_resolver_usevc(r)) {
+			for (retries = ldns_resolver_retry(r); retries > 0; retries--) {
+				send_status = 
+					ldns_tcp_send(&reply_bytes, qb, ns, 
+					(socklen_t)ns_len, ldns_resolver_timeout(r), 
+					&reply_size);
+				if (send_status == LDNS_STATUS_OK) {
+					break;
+				}
+			}
+		} else {
+			for (retries = ldns_resolver_retry(r); retries > 0; retries--) {
+				/* ldns_rdf_print(stdout, ns_array[i]); */
+				send_status = 
+					ldns_udp_send(&reply_bytes, qb, ns, 
+							(socklen_t)ns_len, ldns_resolver_timeout(r), 
+							&reply_size);
+				
+				if (send_status == LDNS_STATUS_OK) {
+					break;
+				}
+			}
+		}
+
+		if (send_status != LDNS_STATUS_OK) {
+			ldns_resolver_set_nameserver_rtt(r, i, LDNS_RESOLV_RTT_INF);
+			status = send_status;
+		}
+		
+		/* obey the fail directive */
+		if (!reply_bytes) {
+			/* the current nameserver seems to have a problem, blacklist it */
+			if (ldns_resolver_fail(r)) {
+				LDNS_FREE(ns);
+				return LDNS_STATUS_ERR;
+			} else {
+				LDNS_FREE(ns);
+				continue;
+			}
+		} 
+		
+		status = ldns_wire2pkt(&reply, reply_bytes, reply_size);
+		if (status != LDNS_STATUS_OK) {
+			LDNS_FREE(reply_bytes);
+			LDNS_FREE(ns);
+			return status;
+		}
+		
+		LDNS_FREE(ns);
+		gettimeofday(&tv_e, NULL);
+
+		if (reply) {
+			ldns_pkt_set_querytime(reply, (uint32_t)
+				((tv_e.tv_sec - tv_s.tv_sec) * 1000) +
+				(tv_e.tv_usec - tv_s.tv_usec) / 1000);
+			ldns_pkt_set_answerfrom(reply, ns_array[i]);
+			ldns_pkt_set_timestamp(reply, tv_s);
+			ldns_pkt_set_size(reply, reply_size);
+			break;
+		} else {
+			if (ldns_resolver_fail(r)) {
+				/* if fail is set bail out, after the first
+				 * one */
+				break;
+			}
+		}
+
+		/* wait retrans seconds... */
+		sleep((unsigned int) ldns_resolver_retrans(r));
+	}
+
+	if (all_servers_rtt_inf) {
+		LDNS_FREE(reply_bytes);
+		return LDNS_STATUS_RES_NO_NS;
+	}
+#ifdef HAVE_SSL
+	if (tsig_mac && reply_bytes) {
+		if (!ldns_pkt_tsig_verify(reply,
+		                          reply_bytes,
+					  reply_size,
+		                          ldns_resolver_tsig_keyname(r),
+		                          ldns_resolver_tsig_keydata(r), tsig_mac)) {
+			status = LDNS_STATUS_CRYPTO_TSIG_BOGUS;
+		}
+	}
+#else
+	(void)tsig_mac;
+#endif /* HAVE_SSL */
+
+	LDNS_FREE(reply_bytes);
+	if (result) {
+		*result = reply;
+	}
+
+	return status;
+}
+
+/** best effort to set nonblocking */
+static void
+ldns_sock_nonblock(int sockfd)
+{
+#ifdef HAVE_FCNTL
+	int flag;
+	if((flag = fcntl(sockfd, F_GETFL)) != -1) {
+		flag |= O_NONBLOCK;
+		if(fcntl(sockfd, F_SETFL, flag) == -1) {
+			/* ignore error, continue blockingly */
+		}
+	}
+#elif defined(HAVE_IOCTLSOCKET)
+	unsigned long on = 1;
+	if(ioctlsocket(sockfd, FIONBIO, &on) != 0) {
+		/* ignore error, continue blockingly */
+	}
+#endif
+}
+
+/** best effort to set blocking */
+static void
+ldns_sock_block(int sockfd)
+{
+#ifdef HAVE_FCNTL
+	int flag;
+	if((flag = fcntl(sockfd, F_GETFL)) != -1) {
+		flag &= ~O_NONBLOCK;
+		if(fcntl(sockfd, F_SETFL, flag) == -1) {
+			/* ignore error, continue */
+		}
+	}
+#elif defined(HAVE_IOCTLSOCKET)
+	unsigned long off = 0;
+	if(ioctlsocket(sockfd, FIONBIO, &off) != 0) {
+		/* ignore error, continue */
+	}
+#endif
+}
+
+/** wait for a socket to become ready */
+static int
+ldns_sock_wait(int sockfd, struct timeval timeout, int write)
+{
+	int ret;
+#ifndef S_SPLINT_S
+	fd_set fds;
+	FD_ZERO(&fds);
+	FD_SET(FD_SET_T sockfd, &fds);
+	if(write)
+		ret = select(sockfd+1, NULL, &fds, NULL, &timeout);
+	else
+		ret = select(sockfd+1, &fds, NULL, NULL, &timeout);
+#endif
+	if(ret == 0)
+		/* timeout expired */
+		return 0;
+	else if(ret == -1)
+		/* error */
+		return 0;
+	return 1;
+}
+
+ldns_status
+ldns_udp_send(uint8_t **result, ldns_buffer *qbin, const struct sockaddr_storage *to,
+		socklen_t tolen, struct timeval timeout, size_t *answer_size)
+{
+	int sockfd;
+	uint8_t *answer;
+
+	sockfd = ldns_udp_bgsend(qbin, to, tolen, timeout);
+
+	if (sockfd == 0) {
+		return LDNS_STATUS_SOCKET_ERROR;
+	}
+
+	/* wait for an response*/
+	if(!ldns_sock_wait(sockfd, timeout, 0)) {
+#ifndef USE_WINSOCK
+		close(sockfd);
+#else
+                closesocket(sockfd);
+#endif
+		return LDNS_STATUS_NETWORK_ERR;
+	}
+
+        /* set to nonblocking, so if the checksum is bad, it becomes
+         * an EGAIN error and the ldns_udp_send function does not block,
+         * but returns a 'NETWORK_ERROR' much like a timeout. */
+        ldns_sock_nonblock(sockfd);
+
+	answer = ldns_udp_read_wire(sockfd, answer_size, NULL, NULL);
+#ifndef USE_WINSOCK
+	close(sockfd);
+#else
+        closesocket(sockfd);
+#endif
+
+	if (*answer_size == 0) {
+		/* oops */
+		return LDNS_STATUS_NETWORK_ERR;
+	}
+
+	*result = answer;
+	return LDNS_STATUS_OK;
+}
+
+int
+ldns_udp_bgsend(ldns_buffer *qbin, const struct sockaddr_storage *to, socklen_t tolen, 
+		struct timeval timeout)
+{
+	int sockfd;
+
+	sockfd = ldns_udp_connect(to, timeout);
+
+	if (sockfd == 0) {
+		return 0;
+	}
+
+	if (ldns_udp_send_query(qbin, sockfd, to, tolen) == 0) {
+#ifndef USE_WINSOCK
+		close(sockfd);
+#else
+		closesocket(sockfd);
+#endif
+		return 0;
+	}
+	return sockfd;
+}
+
+int
+ldns_udp_connect(const struct sockaddr_storage *to, struct timeval ATTR_UNUSED(timeout))
+{
+	int sockfd;
+
+#ifndef S_SPLINT_S
+	if ((sockfd = socket((int)((struct sockaddr*)to)->sa_family, SOCK_DGRAM, 
+					IPPROTO_UDP)) 
+			== -1) {
+                return 0;
+        }
+#endif
+	return sockfd;
+}
+
+int
+ldns_tcp_connect(const struct sockaddr_storage *to, socklen_t tolen, 
+		struct timeval timeout)
+{
+	int sockfd;
+
+#ifndef S_SPLINT_S
+	if ((sockfd = socket((int)((struct sockaddr*)to)->sa_family, SOCK_STREAM, 
+					IPPROTO_TCP)) == -1) {
+		return 0;
+	}
+#endif
+
+	/* perform nonblocking connect, to be able to wait with select() */
+	ldns_sock_nonblock(sockfd);
+	if (connect(sockfd, (struct sockaddr*)to, tolen) == -1) {
+#ifndef USE_WINSOCK
+#ifdef EINPROGRESS
+		if(errno != EINPROGRESS) {
+#else
+		if(1) {
+#endif
+			close(sockfd);
+			return 0;
+		}
+#else /* USE_WINSOCK */
+		if(WSAGetLastError() != WSAEINPROGRESS &&
+			WSAGetLastError() != WSAEWOULDBLOCK) {
+			closesocket(sockfd);
+			return 0;
+		}
+#endif
+		/* error was only telling us that it would block */
+	}
+
+	/* wait(write) until connected or error */
+	while(1) {
+		int error = 0;
+		socklen_t len = (socklen_t)sizeof(error);
+
+		if(!ldns_sock_wait(sockfd, timeout, 1)) {
+#ifndef USE_WINSOCK
+			close(sockfd);
+#else
+			closesocket(sockfd);
+#endif
+			return 0;
+		}
+
+		/* check if there is a pending error for nonblocking connect */
+		if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*)&error,
+			&len) < 0) {
+#ifndef USE_WINSOCK
+			error = errno; /* on solaris errno is error */
+#else
+			error = WSAGetLastError();
+#endif
+		}
+#ifndef USE_WINSOCK
+#if defined(EINPROGRESS) && defined(EWOULDBLOCK)
+		if(error == EINPROGRESS || error == EWOULDBLOCK)
+			continue; /* try again */
+#endif
+		else if(error != 0) {
+			close(sockfd);
+			/* error in errno for our user */
+			errno = error;
+			return 0;
+		}
+#else /* USE_WINSOCK */
+		if(error == WSAEINPROGRESS)
+			continue;
+		else if(error == WSAEWOULDBLOCK)
+			continue;
+		else if(error != 0) {
+			closesocket(sockfd);
+			errno = error;
+			return 0;
+		}
+#endif /* USE_WINSOCK */
+		/* connected */
+		break;
+	}
+
+	/* set the socket blocking again */
+	ldns_sock_block(sockfd);
+
+	return sockfd;
+}
+
+ssize_t
+ldns_tcp_send_query(ldns_buffer *qbin, int sockfd, 
+                    const struct sockaddr_storage *to, socklen_t tolen)
+{
+	uint8_t *sendbuf;
+	ssize_t bytes;
+
+	/* add length of packet */
+	sendbuf = LDNS_XMALLOC(uint8_t, ldns_buffer_position(qbin) + 2);
+	if(!sendbuf) return 0;
+	ldns_write_uint16(sendbuf, ldns_buffer_position(qbin));
+	memcpy(sendbuf + 2, ldns_buffer_export(qbin), ldns_buffer_position(qbin));
+
+	bytes = sendto(sockfd, (void*)sendbuf,
+			ldns_buffer_position(qbin) + 2, 0, (struct sockaddr *)to, tolen);
+
+        LDNS_FREE(sendbuf);
+
+	if (bytes == -1 || (size_t) bytes != ldns_buffer_position(qbin) + 2 ) {
+		return 0;
+	}
+	return bytes;
+}
+
+/* don't wait for an answer */
+ssize_t
+ldns_udp_send_query(ldns_buffer *qbin, int sockfd, const struct sockaddr_storage *to, 
+		socklen_t tolen)
+{
+	ssize_t bytes;
+
+	bytes = sendto(sockfd, (void*)ldns_buffer_begin(qbin),
+			ldns_buffer_position(qbin), 0, (struct sockaddr *)to, tolen);
+
+	if (bytes == -1 || (size_t)bytes != ldns_buffer_position(qbin)) {
+		return 0;
+	}
+	if ((size_t) bytes != ldns_buffer_position(qbin)) {
+		return 0;
+	}
+	return bytes;
+}
+
+uint8_t *
+ldns_udp_read_wire(int sockfd, size_t *size, struct sockaddr_storage *from,
+		socklen_t *fromlen)
+{
+	uint8_t *wire, *wireout;
+	ssize_t wire_size;
+
+	wire = LDNS_XMALLOC(uint8_t, LDNS_MAX_PACKETLEN);
+	if (!wire) {
+		*size = 0;
+		return NULL;
+	}
+
+	wire_size = recvfrom(sockfd, (void*)wire, LDNS_MAX_PACKETLEN, 0, 
+			(struct sockaddr *)from, fromlen);
+
+	/* recvfrom can also return 0 */
+	if (wire_size == -1 || wire_size == 0) {
+		*size = 0;
+		LDNS_FREE(wire);
+		return NULL;
+	}
+
+	*size = (size_t)wire_size;
+	wireout = LDNS_XREALLOC(wire, uint8_t, (size_t)wire_size);
+	if(!wireout) LDNS_FREE(wire);
+
+	return wireout;
+}
+
+uint8_t *
+ldns_tcp_read_wire_timeout(int sockfd, size_t *size, struct timeval timeout)
+{
+	uint8_t *wire;
+	uint16_t wire_size;
+	ssize_t bytes = 0, rc = 0;
+
+	wire = LDNS_XMALLOC(uint8_t, 2);
+	if (!wire) {
+		*size = 0;
+		return NULL;
+	}
+	
+	while (bytes < 2) {
+		if(!ldns_sock_wait(sockfd, timeout, 0)) {
+			*size = 0;
+			LDNS_FREE(wire);
+			return NULL;
+		}
+		rc = recv(sockfd, (void*) (wire + bytes), 
+				(size_t) (2 - bytes), 0);
+		if (rc == -1 || rc == 0) {
+			*size = 0;
+			LDNS_FREE(wire);
+			return NULL;
+		}
+                bytes += rc;
+	}
+
+	wire_size = ldns_read_uint16(wire);
+	
+	LDNS_FREE(wire);
+	wire = LDNS_XMALLOC(uint8_t, wire_size);
+	if (!wire) {
+		*size = 0;
+		return NULL;
+	}
+	bytes = 0;
+
+	while (bytes < (ssize_t) wire_size) {
+		if(!ldns_sock_wait(sockfd, timeout, 0)) {
+			*size = 0;
+			LDNS_FREE(wire);
+			return NULL;
+		}
+		rc = recv(sockfd, (void*) (wire + bytes), 
+				(size_t) (wire_size - bytes), 0);
+		if (rc == -1 || rc == 0) {
+			LDNS_FREE(wire);
+			*size = 0;
+			return NULL;
+		}
+                bytes += rc;
+	}
+	
+	*size = (size_t) bytes;
+	return wire;
+}
+
+uint8_t *
+ldns_tcp_read_wire(int sockfd, size_t *size)
+{
+	uint8_t *wire;
+	uint16_t wire_size;
+	ssize_t bytes = 0, rc = 0;
+
+	wire = LDNS_XMALLOC(uint8_t, 2);
+	if (!wire) {
+		*size = 0;
+		return NULL;
+	}
+	
+	while (bytes < 2) {
+		rc = recv(sockfd, (void*) (wire + bytes), 
+				(size_t) (2 - bytes), 0);
+		if (rc == -1 || rc == 0) {
+			*size = 0;
+			LDNS_FREE(wire);
+			return NULL;
+		}
+                bytes += rc;
+	}
+
+	wire_size = ldns_read_uint16(wire);
+	
+	LDNS_FREE(wire);
+	wire = LDNS_XMALLOC(uint8_t, wire_size);
+	if (!wire) {
+		*size = 0;
+		return NULL;
+	}
+	bytes = 0;
+
+	while (bytes < (ssize_t) wire_size) {
+		rc = recv(sockfd, (void*) (wire + bytes), 
+				(size_t) (wire_size - bytes), 0);
+		if (rc == -1 || rc == 0) {
+			LDNS_FREE(wire);
+			*size = 0;
+			return NULL;
+		}
+                bytes += rc;
+	}
+	
+	*size = (size_t) bytes;
+	return wire;
+}
+
+/* keep in mind that in DNS tcp messages the first 2 bytes signal the
+ * amount data to expect
+ */
+ldns_status
+ldns_tcp_send(uint8_t **result,  ldns_buffer *qbin, const struct sockaddr_storage *to, 
+		socklen_t tolen, struct timeval timeout, size_t *answer_size)
+{
+	int sockfd;
+	uint8_t *answer;
+	
+	sockfd = ldns_tcp_bgsend(qbin, to, tolen, timeout);
+	
+	if (sockfd == 0) {
+		return LDNS_STATUS_ERR;
+	}
+
+	answer = ldns_tcp_read_wire_timeout(sockfd, answer_size, timeout);
+#ifndef USE_WINSOCK
+	close(sockfd);
+#else
+	closesocket(sockfd);
+#endif
+
+	if (*answer_size == 0) {
+		/* oops */
+		return LDNS_STATUS_NETWORK_ERR;
+	}
+
+	/* resize accordingly */
+	*result = (uint8_t*)LDNS_XREALLOC(answer, uint8_t *, (size_t)*answer_size);
+        if(!*result) {
+                LDNS_FREE(answer);
+                return LDNS_STATUS_MEM_ERR;
+        }
+	return LDNS_STATUS_OK;
+}
+
+int
+ldns_tcp_bgsend(ldns_buffer *qbin, const struct sockaddr_storage *to, socklen_t tolen, 
+		struct timeval timeout)
+{
+	int sockfd;
+	
+	sockfd = ldns_tcp_connect(to, tolen, timeout);
+	
+	if (sockfd == 0) {
+		return 0;
+	}
+	
+	if (ldns_tcp_send_query(qbin, sockfd, to, tolen) == 0) {
+#ifndef USE_WINSOCK
+		close(sockfd);
+#else
+		closesocket(sockfd);
+#endif
+		return 0;
+	}
+	
+	return sockfd;
+}
+
+/* code from rdata.c */
+struct sockaddr_storage *
+ldns_rdf2native_sockaddr_storage(const ldns_rdf *rd, uint16_t port, size_t *size)
+{
+        struct sockaddr_storage *data;
+        struct sockaddr_in  *data_in;
+        struct sockaddr_in6 *data_in6;
+
+        data = LDNS_MALLOC(struct sockaddr_storage);
+        if (!data) {
+                return NULL;
+        }
+		/* zero the structure for portability */
+		memset(data, 0, sizeof(struct sockaddr_storage));
+        if (port == 0) {
+                port =  LDNS_PORT;
+        }
+
+        switch(ldns_rdf_get_type(rd)) {
+                case LDNS_RDF_TYPE_A:
+#ifndef S_SPLINT_S
+                        data->ss_family = AF_INET;
+#endif
+                        data_in = (struct sockaddr_in*) data;
+                        data_in->sin_port = (in_port_t)htons(port);
+                        memcpy(&(data_in->sin_addr), ldns_rdf_data(rd), ldns_rdf_size(rd));
+                        *size = sizeof(struct sockaddr_in);
+                        return data;
+                case LDNS_RDF_TYPE_AAAA:
+#ifndef S_SPLINT_S
+                        data->ss_family = AF_INET6;
+#endif
+                        data_in6 = (struct sockaddr_in6*) data;
+                        data_in6->sin6_port = (in_port_t)htons(port);
+                        memcpy(&data_in6->sin6_addr, ldns_rdf_data(rd), ldns_rdf_size(rd));
+                        *size = sizeof(struct sockaddr_in6);
+                        return data;
+                default:
+                        LDNS_FREE(data);
+                        return NULL;
+        }
+}
+
+#ifndef S_SPLINT_S
+ldns_rdf *
+ldns_sockaddr_storage2rdf(struct sockaddr_storage *sock, uint16_t *port)
+{
+        ldns_rdf *addr;
+        struct sockaddr_in *data_in;
+        struct sockaddr_in6 *data_in6;
+
+        switch(sock->ss_family) {
+                case AF_INET:
+                        data_in = (struct sockaddr_in*)sock;
+                        if (port) {
+                                *port = ntohs((uint16_t)data_in->sin_port);
+                        }
+                        addr = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_A,
+                                        LDNS_IP4ADDRLEN, &data_in->sin_addr);
+                        break;
+                case AF_INET6:
+                        data_in6 = (struct sockaddr_in6*)sock;
+                        if (port) {
+                                *port = ntohs((uint16_t)data_in6->sin6_port);
+                        }
+                        addr = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_AAAA,
+                                        LDNS_IP6ADDRLEN, &data_in6->sin6_addr);
+                        break;
+                default:
+                        if (port) {
+                                *port = 0;
+                        }
+                        return NULL;
+        }
+        return addr;
+}
+#endif
+
+/* code from resolver.c */
+ldns_status
+ldns_axfr_start(ldns_resolver *resolver, ldns_rdf *domain, ldns_rr_class class) 
+{
+        ldns_pkt *query;
+        ldns_buffer *query_wire;
+
+        struct sockaddr_storage *ns = NULL;
+        size_t ns_len = 0;
+        size_t ns_i;
+        ldns_status status;
+
+        if (!resolver || ldns_resolver_nameserver_count(resolver) < 1) {
+                return LDNS_STATUS_ERR;
+        }
+
+        query = ldns_pkt_query_new(ldns_rdf_clone(domain), LDNS_RR_TYPE_AXFR, class, 0);
+
+        if (!query) {
+                return LDNS_STATUS_ADDRESS_ERR;
+        }
+        /* For AXFR, we have to make the connection ourselves */
+        /* try all nameservers (which usually would mean v4 fallback if
+         * @hostname is used */
+        for (ns_i = 0;
+             ns_i < ldns_resolver_nameserver_count(resolver) &&
+             resolver->_socket == 0;
+             ns_i++) {
+	        ns = ldns_rdf2native_sockaddr_storage(
+	        	resolver->_nameservers[ns_i],
+			ldns_resolver_port(resolver), &ns_len);
+
+		resolver->_socket = ldns_tcp_connect(ns, (socklen_t)ns_len,
+				ldns_resolver_timeout(resolver));
+	}
+
+	if (resolver->_socket == 0) {
+		ldns_pkt_free(query);
+		LDNS_FREE(ns);
+		return LDNS_STATUS_NETWORK_ERR;
+	}
+
+#ifdef HAVE_SSL
+	if (ldns_resolver_tsig_keyname(resolver) && ldns_resolver_tsig_keydata(resolver)) {
+		status = ldns_pkt_tsig_sign(query,
+		                            ldns_resolver_tsig_keyname(resolver),
+		                            ldns_resolver_tsig_keydata(resolver),
+		                            300, ldns_resolver_tsig_algorithm(resolver), NULL);
+		if (status != LDNS_STATUS_OK) {
+			/* RoRi: to prevent problems on subsequent calls to ldns_axfr_start
+			   we have to close the socket here! */
+#ifndef USE_WINSOCK
+			close(resolver->_socket);
+#else
+			closesocket(resolver->_socket);
+#endif
+			resolver->_socket = 0;
+
+			return LDNS_STATUS_CRYPTO_TSIG_ERR;
+		}
+	}
+#endif /* HAVE_SSL */
+
+        /* Convert the query to a buffer
+         * Is this necessary?
+         */
+        query_wire = ldns_buffer_new(LDNS_MAX_PACKETLEN);
+        if(!query_wire) {
+                ldns_pkt_free(query);
+                LDNS_FREE(ns);
+#ifndef USE_WINSOCK
+		close(resolver->_socket);
+#else
+		closesocket(resolver->_socket);
+#endif
+		resolver->_socket = 0;
+
+                return LDNS_STATUS_MEM_ERR;
+        }
+        status = ldns_pkt2buffer_wire(query_wire, query);
+        if (status != LDNS_STATUS_OK) {
+                ldns_pkt_free(query);
+		ldns_buffer_free(query_wire);
+                LDNS_FREE(ns);
+
+		/* RoRi: to prevent problems on subsequent calls to ldns_axfr_start
+		    we have to close the socket here! */
+#ifndef USE_WINSOCK
+		close(resolver->_socket);
+#else
+		closesocket(resolver->_socket);
+#endif
+		resolver->_socket = 0;
+
+                return status;
+        }
+        /* Send the query */
+        if (ldns_tcp_send_query(query_wire, resolver->_socket, ns,
+				(socklen_t)ns_len) == 0) {
+                ldns_pkt_free(query);
+                ldns_buffer_free(query_wire);
+                LDNS_FREE(ns);
+
+		/* RoRi: to prevent problems on subsequent calls to ldns_axfr_start
+		         we have to close the socket here! */
+
+#ifndef USE_WINSOCK
+		close(resolver->_socket);
+#else
+		closesocket(resolver->_socket);
+#endif
+		resolver->_socket = 0;
+
+                return LDNS_STATUS_NETWORK_ERR;
+        }
+
+        ldns_pkt_free(query);
+        ldns_buffer_free(query_wire);
+        LDNS_FREE(ns);
+
+        /*
+         * The AXFR is done once the second SOA record is sent
+         */
+        resolver->_axfr_soa_count = 0;
+        return LDNS_STATUS_OK;
+}
diff --git a/3rdParty/Ldns/src/src/packet.c b/3rdParty/Ldns/src/src/packet.c
new file mode 100644
index 0000000..0ac5ca8
--- /dev/null
+++ b/3rdParty/Ldns/src/src/packet.c
@@ -0,0 +1,1007 @@
+/*
+ * packet.c
+ *
+ * dns packet implementation
+ *
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2004-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+#include <ldns/config.h>
+
+#include <ldns/ldns.h>
+
+#include <strings.h>
+#include <limits.h>
+
+#ifdef HAVE_SSL
+#include <openssl/rand.h>
+#endif
+
+/* Access functions 
+ * do this as functions to get type checking
+ */
+
+#define LDNS_EDNS_MASK_DO_BIT 0x8000
+
+/* TODO defines for 3600 */
+/* convert to and from numerical flag values */
+ldns_lookup_table ldns_edns_flags[] = {
+	{ 3600, "do"},
+	{ 0, NULL}
+};
+
+/* read */
+uint16_t
+ldns_pkt_id(const ldns_pkt *packet)
+{
+	return packet->_header->_id;
+}
+
+bool
+ldns_pkt_qr(const ldns_pkt *packet)
+{
+	return packet->_header->_qr;
+}
+
+bool
+ldns_pkt_aa(const ldns_pkt *packet)
+{
+	return packet->_header->_aa;
+}
+
+bool
+ldns_pkt_tc(const ldns_pkt *packet)
+{
+	return packet->_header->_tc;
+}
+
+bool
+ldns_pkt_rd(const ldns_pkt *packet)
+{
+	return packet->_header->_rd;
+}
+
+bool
+ldns_pkt_cd(const ldns_pkt *packet)
+{
+	return packet->_header->_cd;
+}
+
+bool
+ldns_pkt_ra(const ldns_pkt *packet)
+{
+	return packet->_header->_ra;
+}
+
+bool
+ldns_pkt_ad(const ldns_pkt *packet)
+{
+	return packet->_header->_ad;
+}
+
+ldns_pkt_opcode
+ldns_pkt_get_opcode(const ldns_pkt *packet)
+{
+	return packet->_header->_opcode;
+}
+
+ldns_pkt_rcode
+ldns_pkt_get_rcode(const ldns_pkt *packet)
+{
+	return packet->_header->_rcode;
+}
+
+uint16_t
+ldns_pkt_qdcount(const ldns_pkt *packet)
+{
+	return packet->_header->_qdcount;
+}
+
+uint16_t
+ldns_pkt_ancount(const ldns_pkt *packet)
+{
+	return packet->_header->_ancount;
+}
+
+uint16_t
+ldns_pkt_nscount(const ldns_pkt *packet)
+{
+	return packet->_header->_nscount;
+}
+
+uint16_t
+ldns_pkt_arcount(const ldns_pkt *packet)
+{
+	return packet->_header->_arcount;
+}
+
+ldns_rr_list *
+ldns_pkt_question(const ldns_pkt *packet)
+{
+	return packet->_question;
+}
+
+ldns_rr_list *
+ldns_pkt_answer(const ldns_pkt *packet)
+{
+	return packet->_answer;
+}
+
+ldns_rr_list *
+ldns_pkt_authority(const ldns_pkt *packet)
+{
+	return packet->_authority;
+}
+
+ldns_rr_list *
+ldns_pkt_additional(const ldns_pkt *packet)
+{
+	return packet->_additional;
+}
+
+/* return ALL section concatenated */
+ldns_rr_list *
+ldns_pkt_all(const ldns_pkt *packet)
+{
+	ldns_rr_list *all, *prev_all;
+
+	all = ldns_rr_list_cat_clone(
+			ldns_pkt_question(packet),
+			ldns_pkt_answer(packet));
+	prev_all = all;
+	all = ldns_rr_list_cat_clone(all,
+			ldns_pkt_authority(packet));
+	ldns_rr_list_deep_free(prev_all);
+	prev_all = all;
+	all = ldns_rr_list_cat_clone(all,
+			ldns_pkt_additional(packet));
+	ldns_rr_list_deep_free(prev_all);
+	return all;
+}
+
+ldns_rr_list *
+ldns_pkt_all_noquestion(const ldns_pkt *packet)
+{
+	ldns_rr_list *all, *all2;
+
+	all = ldns_rr_list_cat_clone(
+			ldns_pkt_answer(packet),
+			ldns_pkt_authority(packet));
+	all2 = ldns_rr_list_cat_clone(all,
+			ldns_pkt_additional(packet));
+	
+	ldns_rr_list_deep_free(all);
+	return all2;
+}
+
+size_t
+ldns_pkt_size(const ldns_pkt *packet)
+{
+	return packet->_size;
+}
+
+uint32_t 
+ldns_pkt_querytime(const ldns_pkt *packet)
+{
+	return packet->_querytime;
+}
+
+ldns_rdf *
+ldns_pkt_answerfrom(const ldns_pkt *packet)
+{
+	return packet->_answerfrom;
+}
+
+struct timeval
+ldns_pkt_timestamp(const ldns_pkt *packet)
+{
+	return packet->timestamp;
+}
+
+uint16_t
+ldns_pkt_edns_udp_size(const ldns_pkt *packet)
+{
+	return packet->_edns_udp_size;
+}
+
+uint8_t
+ldns_pkt_edns_extended_rcode(const ldns_pkt *packet)
+{
+	return packet->_edns_extended_rcode;
+}
+
+uint8_t
+ldns_pkt_edns_version(const ldns_pkt *packet)
+{
+	return packet->_edns_version;
+}
+
+uint16_t
+ldns_pkt_edns_z(const ldns_pkt *packet)
+{
+	return packet->_edns_z;
+}
+
+bool
+ldns_pkt_edns_do(const ldns_pkt *packet)
+{
+	return (packet->_edns_z & LDNS_EDNS_MASK_DO_BIT);
+}
+
+void
+ldns_pkt_set_edns_do(ldns_pkt *packet, bool value)
+{
+	if (value) {
+		packet->_edns_z = packet->_edns_z | LDNS_EDNS_MASK_DO_BIT;
+	} else {
+		packet->_edns_z = packet->_edns_z & ~LDNS_EDNS_MASK_DO_BIT;
+	}
+}
+
+ldns_rdf *
+ldns_pkt_edns_data(const ldns_pkt *packet)
+{
+	return packet->_edns_data;
+}
+
+/* return only those rr that share the ownername */
+ldns_rr_list *
+ldns_pkt_rr_list_by_name(ldns_pkt *packet,
+                         ldns_rdf *ownername,
+                         ldns_pkt_section sec)
+{
+	ldns_rr_list *rrs;
+	ldns_rr_list *new;
+	ldns_rr_list *ret;
+	uint16_t i;
+
+	if (!packet) {
+		return NULL;
+	}
+
+	rrs = ldns_pkt_get_section_clone(packet, sec);
+	new = ldns_rr_list_new();
+	ret = NULL;
+
+	for(i = 0; i < ldns_rr_list_rr_count(rrs); i++) {
+		if (ldns_rdf_compare(ldns_rr_owner(
+						ldns_rr_list_rr(rrs, i)), 
+					ownername) == 0) {
+			/* owner names match */
+			ldns_rr_list_push_rr(new, ldns_rr_list_rr(rrs, i));
+			ret = new;
+		}
+	}
+	return ret;
+}
+
+/* return only those rr that share a type */
+ldns_rr_list *
+ldns_pkt_rr_list_by_type(const ldns_pkt *packet,
+                         ldns_rr_type type,
+                         ldns_pkt_section sec)
+{
+	ldns_rr_list *rrs;
+	ldns_rr_list *new;
+	uint16_t i;
+
+	if(!packet) {
+		return NULL;
+	}
+	
+	rrs = ldns_pkt_get_section_clone(packet, sec);
+	new = ldns_rr_list_new();
+	
+	for(i = 0; i < ldns_rr_list_rr_count(rrs); i++) {
+		if (type == ldns_rr_get_type(ldns_rr_list_rr(rrs, i))) {
+			/* types match */
+			ldns_rr_list_push_rr(new, 
+			                     ldns_rr_clone(
+			                     	ldns_rr_list_rr(rrs, i))
+					     );
+		}
+	}
+	ldns_rr_list_deep_free(rrs);
+
+	if (ldns_rr_list_rr_count(new) == 0) {
+		ldns_rr_list_free(new);
+		return NULL;
+	} else {
+		return new;
+	}
+}
+
+/* return only those rrs that share name and type */
+ldns_rr_list *
+ldns_pkt_rr_list_by_name_and_type(const ldns_pkt *packet,
+                                  const ldns_rdf *ownername,
+                                  ldns_rr_type type,
+                                  ldns_pkt_section sec)
+{
+	ldns_rr_list *rrs;
+	ldns_rr_list *new;
+	ldns_rr_list *ret;
+	uint16_t i;
+
+	if(!packet) {
+		return NULL;
+	}
+	
+	rrs = ldns_pkt_get_section_clone(packet, sec);
+	new = ldns_rr_list_new();
+	ret = NULL;
+
+	for(i = 0; i < ldns_rr_list_rr_count(rrs); i++) {
+		if (type == ldns_rr_get_type(ldns_rr_list_rr(rrs, i)) &&
+		    ldns_rdf_compare(ldns_rr_owner(ldns_rr_list_rr(rrs, i)),
+		                     ownername
+		                    ) == 0
+		   ) {
+			/* types match */
+			ldns_rr_list_push_rr(new, ldns_rr_clone(ldns_rr_list_rr(rrs, i)));
+			ret = new;
+		}
+	}
+	ldns_rr_list_deep_free(rrs);
+	if (!ret) {
+		ldns_rr_list_free(new);
+	}
+	return ret;
+}
+
+bool
+ldns_pkt_rr(ldns_pkt *pkt, ldns_pkt_section sec, ldns_rr *rr)
+{
+	bool result = false;
+
+	switch (sec) {
+	case LDNS_SECTION_QUESTION:
+		return ldns_rr_list_contains_rr(ldns_pkt_question(pkt), rr);
+	case LDNS_SECTION_ANSWER:
+		return ldns_rr_list_contains_rr(ldns_pkt_answer(pkt), rr);
+	case LDNS_SECTION_AUTHORITY:
+		return ldns_rr_list_contains_rr(ldns_pkt_authority(pkt), rr);
+	case LDNS_SECTION_ADDITIONAL:
+		return ldns_rr_list_contains_rr(ldns_pkt_additional(pkt), rr);
+	case LDNS_SECTION_ANY:
+		result = ldns_rr_list_contains_rr(ldns_pkt_question(pkt), rr);
+	case LDNS_SECTION_ANY_NOQUESTION:
+		result = result
+		    || ldns_rr_list_contains_rr(ldns_pkt_answer(pkt), rr)
+		    || ldns_rr_list_contains_rr(ldns_pkt_authority(pkt), rr)
+		    || ldns_rr_list_contains_rr(ldns_pkt_additional(pkt), rr);
+	}
+
+	return result;
+}
+
+uint16_t
+ldns_pkt_section_count(const ldns_pkt *packet, ldns_pkt_section s)
+{
+	switch(s) {
+	case LDNS_SECTION_QUESTION:
+		return ldns_pkt_qdcount(packet);
+	case LDNS_SECTION_ANSWER:
+		return ldns_pkt_ancount(packet);
+	case LDNS_SECTION_AUTHORITY:
+		return ldns_pkt_nscount(packet);
+	case LDNS_SECTION_ADDITIONAL:
+		return ldns_pkt_arcount(packet);
+	case LDNS_SECTION_ANY:
+		return ldns_pkt_qdcount(packet) +
+			ldns_pkt_ancount(packet) +
+			ldns_pkt_nscount(packet) +
+			ldns_pkt_arcount(packet);
+	case LDNS_SECTION_ANY_NOQUESTION:
+		return ldns_pkt_ancount(packet) +
+			ldns_pkt_nscount(packet) +
+			ldns_pkt_arcount(packet);
+	default:
+		return 0;
+	}
+}
+
+bool
+ldns_pkt_empty(ldns_pkt *p)
+{
+	if (!p) {
+		return true; /* NULL is empty? */
+	}
+	if (ldns_pkt_section_count(p, LDNS_SECTION_ANY) > 0) {
+		return false;
+	} else {
+		return true;
+    }
+}
+
+
+ldns_rr_list *
+ldns_pkt_get_section_clone(const ldns_pkt *packet, ldns_pkt_section s)
+{
+	switch(s) {
+	case LDNS_SECTION_QUESTION:
+		return ldns_rr_list_clone(ldns_pkt_question(packet));
+	case LDNS_SECTION_ANSWER:
+		return ldns_rr_list_clone(ldns_pkt_answer(packet));
+	case LDNS_SECTION_AUTHORITY:
+		return ldns_rr_list_clone(ldns_pkt_authority(packet));
+	case LDNS_SECTION_ADDITIONAL:
+		return ldns_rr_list_clone(ldns_pkt_additional(packet));
+	case LDNS_SECTION_ANY:
+		/* these are already clones */
+		return ldns_pkt_all(packet);
+	case LDNS_SECTION_ANY_NOQUESTION:
+		return ldns_pkt_all_noquestion(packet);
+	default:
+		return NULL;
+	}
+}
+
+ldns_rr *ldns_pkt_tsig(const ldns_pkt *pkt) {
+	return pkt->_tsig_rr;
+}
+
+/* write */
+void
+ldns_pkt_set_id(ldns_pkt *packet, uint16_t id)
+{
+	packet->_header->_id = id;
+}
+
+void
+ldns_pkt_set_random_id(ldns_pkt *packet)
+{
+	uint16_t rid = ldns_get_random();
+	ldns_pkt_set_id(packet, rid);
+}
+
+
+void
+ldns_pkt_set_qr(ldns_pkt *packet, bool qr)
+{
+	packet->_header->_qr = qr;
+}
+
+void
+ldns_pkt_set_aa(ldns_pkt *packet, bool aa)
+{
+	packet->_header->_aa = aa;
+}
+
+void
+ldns_pkt_set_tc(ldns_pkt *packet, bool tc)
+{
+	packet->_header->_tc = tc;
+}
+
+void
+ldns_pkt_set_rd(ldns_pkt *packet, bool rd)
+{
+	packet->_header->_rd = rd;
+}
+
+void
+ldns_pkt_set_additional(ldns_pkt *p, ldns_rr_list *rr)
+{
+	p->_additional = rr;
+}
+
+void
+ldns_pkt_set_question(ldns_pkt *p, ldns_rr_list *rr)
+{
+	p->_question = rr;
+}
+
+void
+ldns_pkt_set_answer(ldns_pkt *p, ldns_rr_list *rr)
+{
+	p->_answer = rr;
+}
+
+void
+ldns_pkt_set_authority(ldns_pkt *p, ldns_rr_list *rr)
+{
+	p->_authority = rr;
+}
+
+void
+ldns_pkt_set_cd(ldns_pkt *packet, bool cd)
+{
+	packet->_header->_cd = cd;
+}
+
+void
+ldns_pkt_set_ra(ldns_pkt *packet, bool ra)
+{
+	packet->_header->_ra = ra;
+}
+
+void
+ldns_pkt_set_ad(ldns_pkt *packet, bool ad)
+{
+	packet->_header->_ad = ad;
+}
+
+void
+ldns_pkt_set_opcode(ldns_pkt *packet, ldns_pkt_opcode opcode)
+{
+	packet->_header->_opcode = opcode;
+}
+
+void
+ldns_pkt_set_rcode(ldns_pkt *packet, uint8_t rcode)
+{
+	packet->_header->_rcode = rcode;
+}
+
+void
+ldns_pkt_set_qdcount(ldns_pkt *packet, uint16_t qdcount)
+{
+	packet->_header->_qdcount = qdcount;
+}
+
+void
+ldns_pkt_set_ancount(ldns_pkt *packet, uint16_t ancount)
+{
+	packet->_header->_ancount = ancount;
+}
+
+void
+ldns_pkt_set_nscount(ldns_pkt *packet, uint16_t nscount)
+{
+	packet->_header->_nscount = nscount;
+}
+
+void
+ldns_pkt_set_arcount(ldns_pkt *packet, uint16_t arcount)
+{
+	packet->_header->_arcount = arcount;
+}
+
+void
+ldns_pkt_set_querytime(ldns_pkt *packet, uint32_t time) 
+{
+	packet->_querytime = time;
+}
+
+void
+ldns_pkt_set_answerfrom(ldns_pkt *packet, ldns_rdf *answerfrom)
+{
+	packet->_answerfrom = answerfrom;
+}
+
+void
+ldns_pkt_set_timestamp(ldns_pkt *packet, struct timeval timeval)
+{
+	packet->timestamp.tv_sec = timeval.tv_sec;
+	packet->timestamp.tv_usec = timeval.tv_usec;
+}
+
+void
+ldns_pkt_set_size(ldns_pkt *packet, size_t s)
+{
+	packet->_size = s;
+}
+
+void
+ldns_pkt_set_edns_udp_size(ldns_pkt *packet, uint16_t s)
+{
+	packet->_edns_udp_size = s;
+}
+
+void
+ldns_pkt_set_edns_extended_rcode(ldns_pkt *packet, uint8_t c)
+{
+	packet->_edns_extended_rcode = c;
+}
+
+void
+ldns_pkt_set_edns_version(ldns_pkt *packet, uint8_t v)
+{
+	packet->_edns_version = v;
+}
+
+void
+ldns_pkt_set_edns_z(ldns_pkt *packet, uint16_t z)
+{
+	packet->_edns_z = z;
+}
+
+void
+ldns_pkt_set_edns_data(ldns_pkt *packet, ldns_rdf *data)
+{
+	packet->_edns_data = data;
+}
+
+void
+ldns_pkt_set_section_count(ldns_pkt *packet, ldns_pkt_section s, uint16_t count)
+{
+	switch(s) {
+		case LDNS_SECTION_QUESTION:
+			ldns_pkt_set_qdcount(packet, count);
+			break;
+		case LDNS_SECTION_ANSWER:
+			ldns_pkt_set_ancount(packet, count);
+			break;
+		case LDNS_SECTION_AUTHORITY:
+			ldns_pkt_set_nscount(packet, count);
+			break;
+		case LDNS_SECTION_ADDITIONAL:
+			ldns_pkt_set_arcount(packet, count);
+			break;
+		case LDNS_SECTION_ANY:
+		case LDNS_SECTION_ANY_NOQUESTION:
+			break;
+	}
+}
+
+void ldns_pkt_set_tsig(ldns_pkt *pkt, ldns_rr *rr)
+{
+	pkt->_tsig_rr = rr;
+}
+
+bool
+ldns_pkt_push_rr(ldns_pkt *packet, ldns_pkt_section section, ldns_rr *rr)
+{
+	switch(section) {
+		case LDNS_SECTION_QUESTION:
+			ldns_rr_list_push_rr(ldns_pkt_question(packet), rr);
+			ldns_pkt_set_qdcount(packet, ldns_pkt_qdcount(packet) + 1);
+			break;
+		case LDNS_SECTION_ANSWER:
+			ldns_rr_list_push_rr(ldns_pkt_answer(packet), rr);
+			ldns_pkt_set_ancount(packet, ldns_pkt_ancount(packet) + 1);
+			break;
+		case LDNS_SECTION_AUTHORITY:
+			ldns_rr_list_push_rr(ldns_pkt_authority(packet), rr);
+			ldns_pkt_set_nscount(packet, ldns_pkt_nscount(packet) + 1);
+			break;
+		case LDNS_SECTION_ADDITIONAL:
+			ldns_rr_list_push_rr(ldns_pkt_additional(packet), rr);
+			ldns_pkt_set_arcount(packet, ldns_pkt_arcount(packet) + 1);
+			break;
+		case LDNS_SECTION_ANY:
+		case LDNS_SECTION_ANY_NOQUESTION:
+			/* shouldn't this error? */
+			break;
+	}
+	return true;
+}
+
+bool
+ldns_pkt_safe_push_rr(ldns_pkt *pkt, ldns_pkt_section sec, ldns_rr *rr)
+{
+
+	/* check to see if its there */
+	if (ldns_pkt_rr(pkt, sec, rr)) {
+		/* already there */
+		return false;
+	}
+	return ldns_pkt_push_rr(pkt, sec, rr);
+}
+
+bool
+ldns_pkt_push_rr_list(ldns_pkt *p, ldns_pkt_section s, ldns_rr_list *list)
+{
+	size_t i;
+	for(i = 0; i < ldns_rr_list_rr_count(list); i++) {
+		if (!ldns_pkt_push_rr(p, s, ldns_rr_list_rr(list, i))) {
+			return false;
+		}
+	}
+	return true;
+}
+
+bool
+ldns_pkt_safe_push_rr_list(ldns_pkt *p, ldns_pkt_section s, ldns_rr_list *list)
+{
+	size_t i;
+	for(i = 0; i < ldns_rr_list_rr_count(list); i++) {
+		if (!ldns_pkt_safe_push_rr(p, s, ldns_rr_list_rr(list, i))) {
+			return false;
+		}
+	}
+	return true;
+}
+
+bool
+ldns_pkt_edns(const ldns_pkt *pkt) {
+	return (ldns_pkt_edns_udp_size(pkt) > 0 ||
+		ldns_pkt_edns_extended_rcode(pkt) > 0 ||
+		ldns_pkt_edns_data(pkt) ||
+		ldns_pkt_edns_do(pkt)
+	       );
+}
+
+
+/* Create/destroy/convert functions
+ */
+ldns_pkt *
+ldns_pkt_new()
+{
+	ldns_pkt *packet;
+	packet = LDNS_MALLOC(ldns_pkt);
+	if (!packet) {
+		return NULL;
+	}
+
+	packet->_header = LDNS_MALLOC(ldns_hdr);
+	if (!packet->_header) {
+		LDNS_FREE(packet);
+		return NULL;
+	}
+
+	packet->_question = ldns_rr_list_new();
+	packet->_answer = ldns_rr_list_new();
+	packet->_authority = ldns_rr_list_new();
+	packet->_additional = ldns_rr_list_new();
+
+	/* default everything to false */
+	ldns_pkt_set_qr(packet, false);
+	ldns_pkt_set_aa(packet, false);
+	ldns_pkt_set_tc(packet, false);
+	ldns_pkt_set_rd(packet, false);
+	ldns_pkt_set_ra(packet, false);
+	ldns_pkt_set_ad(packet, false);
+	ldns_pkt_set_cd(packet, false);
+
+	ldns_pkt_set_opcode(packet, LDNS_PACKET_QUERY);
+	ldns_pkt_set_rcode(packet, 0);
+	ldns_pkt_set_id(packet, 0); 
+	ldns_pkt_set_size(packet, 0);
+	ldns_pkt_set_querytime(packet, 0);
+	memset(&packet->timestamp, 0, sizeof(packet->timestamp));
+	ldns_pkt_set_answerfrom(packet, NULL);
+	ldns_pkt_set_section_count(packet, LDNS_SECTION_QUESTION, 0);
+	ldns_pkt_set_section_count(packet, LDNS_SECTION_ANSWER, 0);
+	ldns_pkt_set_section_count(packet, LDNS_SECTION_AUTHORITY, 0);
+	ldns_pkt_set_section_count(packet, LDNS_SECTION_ADDITIONAL, 0);
+	
+	ldns_pkt_set_edns_udp_size(packet, 0);
+	ldns_pkt_set_edns_extended_rcode(packet, 0);
+	ldns_pkt_set_edns_version(packet, 0);
+	ldns_pkt_set_edns_z(packet, 0);
+	ldns_pkt_set_edns_data(packet, NULL);
+	
+	ldns_pkt_set_tsig(packet, NULL);
+	
+	return packet;
+}
+
+void
+ldns_pkt_free(ldns_pkt *packet)
+{
+	if (packet) {
+		LDNS_FREE(packet->_header);
+		ldns_rr_list_deep_free(packet->_question);
+		ldns_rr_list_deep_free(packet->_answer);
+		ldns_rr_list_deep_free(packet->_authority);
+		ldns_rr_list_deep_free(packet->_additional);
+		ldns_rr_free(packet->_tsig_rr);
+		ldns_rdf_deep_free(packet->_edns_data);
+		LDNS_FREE(packet);
+	}
+}
+
+bool
+ldns_pkt_set_flags(ldns_pkt *packet, uint16_t flags)
+{
+	if (!packet) {
+		return false;
+	}
+	if ((flags & LDNS_QR) == LDNS_QR) {
+		ldns_pkt_set_qr(packet, true);
+	}
+	if ((flags & LDNS_AA) == LDNS_AA) {
+		ldns_pkt_set_aa(packet, true);
+	}
+	if ((flags & LDNS_RD) == LDNS_RD) {
+		ldns_pkt_set_rd(packet, true);
+	}
+	if ((flags & LDNS_TC) == LDNS_TC) {
+		ldns_pkt_set_tc(packet, true);
+	}
+	if ((flags & LDNS_CD) == LDNS_CD) {
+		ldns_pkt_set_cd(packet, true);
+	}
+	if ((flags & LDNS_RA) == LDNS_RA) {
+		ldns_pkt_set_ra(packet, true);
+	}
+	if ((flags & LDNS_AD) == LDNS_AD) {
+		ldns_pkt_set_ad(packet, true);
+	}
+	return true;
+}
+
+ldns_status
+ldns_pkt_query_new_frm_str(ldns_pkt **p, const char *name, ldns_rr_type rr_type, 
+		ldns_rr_class rr_class, uint16_t flags)
+{
+	ldns_pkt *packet;
+	ldns_rr *question_rr;
+	ldns_rdf *name_rdf;
+
+	packet = ldns_pkt_new();
+	if (!packet) {
+		return LDNS_STATUS_MEM_ERR;
+	}
+	
+	if (!ldns_pkt_set_flags(packet, flags)) {
+		return LDNS_STATUS_ERR;
+	}
+	
+	question_rr = ldns_rr_new();
+	if (!question_rr) {
+		return LDNS_STATUS_MEM_ERR;
+	}
+
+	if (rr_type == 0) {
+		rr_type = LDNS_RR_TYPE_A;
+	}
+	if (rr_class == 0) {
+		rr_class = LDNS_RR_CLASS_IN;
+	}
+
+	if (ldns_str2rdf_dname(&name_rdf, name) == LDNS_STATUS_OK) {
+		ldns_rr_set_owner(question_rr, name_rdf);
+		ldns_rr_set_type(question_rr, rr_type);
+		ldns_rr_set_class(question_rr, rr_class);
+                ldns_rr_set_question(question_rr, true);
+		
+		ldns_pkt_push_rr(packet, LDNS_SECTION_QUESTION, question_rr);
+	} else {
+		ldns_rr_free(question_rr);
+		ldns_pkt_free(packet);
+		return LDNS_STATUS_ERR;
+	}
+	
+	packet->_tsig_rr = NULL;
+	
+	ldns_pkt_set_answerfrom(packet, NULL);
+	if (p) {
+		*p = packet;
+		return LDNS_STATUS_OK;
+	} else {
+		return LDNS_STATUS_NULL;
+	}
+}
+
+ldns_pkt *
+ldns_pkt_query_new(ldns_rdf *rr_name, ldns_rr_type rr_type, ldns_rr_class rr_class,
+		uint16_t flags)
+{
+	ldns_pkt *packet;
+	ldns_rr *question_rr;
+
+	packet = ldns_pkt_new();
+	if (!packet) {
+		return NULL;
+	}
+
+	if (!ldns_pkt_set_flags(packet, flags)) {
+		return NULL;
+	}
+	
+	question_rr = ldns_rr_new();
+	if (!question_rr) {
+		return NULL;
+	}
+
+	if (rr_type == 0) {
+		rr_type = LDNS_RR_TYPE_A;
+	}
+	if (rr_class == 0) {
+		rr_class = LDNS_RR_CLASS_IN;
+	}
+
+	ldns_rr_set_owner(question_rr, rr_name);
+	ldns_rr_set_type(question_rr, rr_type);
+	ldns_rr_set_class(question_rr, rr_class);
+        ldns_rr_set_question(question_rr, true);
+	
+	packet->_tsig_rr = NULL;
+	
+	ldns_pkt_push_rr(packet, LDNS_SECTION_QUESTION, question_rr);
+
+	return packet;
+}
+
+ldns_pkt_type
+ldns_pkt_reply_type(ldns_pkt *p)
+{
+	ldns_rr_list *tmp;
+
+	if (!p) {
+		return LDNS_PACKET_UNKNOWN;
+	}
+
+	if (ldns_pkt_get_rcode(p) == LDNS_RCODE_NXDOMAIN) {
+		return LDNS_PACKET_NXDOMAIN;
+	}
+
+	if (ldns_pkt_ancount(p) == 0 && ldns_pkt_arcount(p) == 0
+			&& ldns_pkt_nscount(p) == 1) {
+
+		/* check for SOA */
+		tmp = ldns_pkt_rr_list_by_type(p, LDNS_RR_TYPE_SOA, 
+					LDNS_SECTION_AUTHORITY);
+		if (tmp) {
+			ldns_rr_list_deep_free(tmp);
+			return LDNS_PACKET_NODATA;
+		} else {
+			/* I have no idea ... */
+		}
+	}
+
+	if (ldns_pkt_ancount(p) == 0 && ldns_pkt_nscount(p) > 0) {
+		tmp = ldns_pkt_rr_list_by_type(p, LDNS_RR_TYPE_NS,
+		                               LDNS_SECTION_AUTHORITY);
+		if (tmp) {
+			/* there are nameservers here */
+			ldns_rr_list_deep_free(tmp);
+			return LDNS_PACKET_REFERRAL;
+		} else {
+			/* I have no idea */
+		}
+		ldns_rr_list_deep_free(tmp);
+	}
+	
+	/* if we cannot determine the packet type, we say it's an 
+	 * answer...
+	 */
+	return LDNS_PACKET_ANSWER;
+}
+
+ldns_pkt *
+ldns_pkt_clone(ldns_pkt *pkt)
+{
+	ldns_pkt *new_pkt;
+	
+	if (!pkt) {
+		return NULL;
+	}
+	new_pkt = ldns_pkt_new();
+
+	ldns_pkt_set_id(new_pkt, ldns_pkt_id(pkt));
+	ldns_pkt_set_qr(new_pkt, ldns_pkt_qr(pkt));
+	ldns_pkt_set_aa(new_pkt, ldns_pkt_aa(pkt));
+	ldns_pkt_set_tc(new_pkt, ldns_pkt_tc(pkt));
+	ldns_pkt_set_rd(new_pkt, ldns_pkt_rd(pkt));
+	ldns_pkt_set_cd(new_pkt, ldns_pkt_cd(pkt));
+	ldns_pkt_set_ra(new_pkt, ldns_pkt_ra(pkt));
+	ldns_pkt_set_ad(new_pkt, ldns_pkt_ad(pkt));
+	ldns_pkt_set_opcode(new_pkt, ldns_pkt_get_opcode(pkt));
+	ldns_pkt_set_rcode(new_pkt, ldns_pkt_get_rcode(pkt));
+	ldns_pkt_set_qdcount(new_pkt, ldns_pkt_qdcount(pkt));
+	ldns_pkt_set_ancount(new_pkt, ldns_pkt_ancount(pkt));
+	ldns_pkt_set_nscount(new_pkt, ldns_pkt_nscount(pkt));
+	ldns_pkt_set_arcount(new_pkt, ldns_pkt_arcount(pkt));
+	ldns_pkt_set_answerfrom(new_pkt, ldns_pkt_answerfrom(pkt));
+	ldns_pkt_set_querytime(new_pkt, ldns_pkt_querytime(pkt));
+	ldns_pkt_set_size(new_pkt, ldns_pkt_size(pkt));
+	ldns_pkt_set_tsig(new_pkt, ldns_rr_clone(ldns_pkt_tsig(pkt)));
+	
+	ldns_pkt_set_edns_udp_size(new_pkt, ldns_pkt_edns_udp_size(pkt));
+	ldns_pkt_set_edns_extended_rcode(new_pkt, 
+		ldns_pkt_edns_extended_rcode(pkt));
+	ldns_pkt_set_edns_version(new_pkt, ldns_pkt_edns_version(pkt));
+	ldns_pkt_set_edns_z(new_pkt, ldns_pkt_edns_z(pkt));
+	if(ldns_pkt_edns_data(pkt))
+		ldns_pkt_set_edns_data(new_pkt, 
+			ldns_rdf_clone(ldns_pkt_edns_data(pkt)));
+	ldns_pkt_set_edns_do(new_pkt, ldns_pkt_edns_do(pkt));
+
+	ldns_rr_list_deep_free(new_pkt->_question);
+	ldns_rr_list_deep_free(new_pkt->_answer);
+	ldns_rr_list_deep_free(new_pkt->_authority);
+	ldns_rr_list_deep_free(new_pkt->_additional);
+	new_pkt->_question = ldns_rr_list_clone(ldns_pkt_question(pkt));
+	new_pkt->_answer = ldns_rr_list_clone(ldns_pkt_answer(pkt));
+	new_pkt->_authority = ldns_rr_list_clone(ldns_pkt_authority(pkt));
+	new_pkt->_additional = ldns_rr_list_clone(ldns_pkt_additional(pkt));
+	return new_pkt;
+}
diff --git a/3rdParty/Ldns/src/src/parse.c b/3rdParty/Ldns/src/src/parse.c
new file mode 100644
index 0000000..15cc300
--- /dev/null
+++ b/3rdParty/Ldns/src/src/parse.c
@@ -0,0 +1,450 @@
+/*
+ * a generic (simple) parser. Use to parse rr's, private key
+ * information and /etc/resolv.conf files
+ *
+ * a Net::DNS like library for C
+ * LibDNS Team @ NLnet Labs
+ * (c) NLnet Labs, 2005-2006
+ * See the file LICENSE for the license
+ */
+#include <ldns/config.h>
+#include <ldns/ldns.h>
+
+#include <limits.h>
+#include <strings.h>
+
+ldns_lookup_table ldns_directive_types[] = {
+        { LDNS_DIR_TTL, "$TTL" },
+        { LDNS_DIR_ORIGIN, "$ORIGIN" },
+        { LDNS_DIR_INCLUDE, "$INCLUDE" },
+        { 0, NULL }
+};
+
+/* add max_limit here? */
+ssize_t
+ldns_fget_token(FILE *f, char *token, const char *delim, size_t limit)
+{
+	return ldns_fget_token_l(f, token, delim, limit, NULL);
+}
+
+ssize_t
+ldns_fget_token_l(FILE *f, char *token, const char *delim, size_t limit, int *line_nr)
+{
+	int c, prev_c;
+	int p; /* 0 -> no parenthese seen, >0 nr of ( seen */
+	int com, quoted;
+	char *t;
+	size_t i;
+	const char *d;
+	const char *del;
+
+	/* standard delimeters */
+	if (!delim) {
+		/* from isspace(3) */
+		del = LDNS_PARSE_NORMAL;
+	} else {
+		del = delim;
+	}
+
+	p = 0;
+	i = 0;
+	com = 0;
+	quoted = 0;
+	prev_c = 0;
+	t = token;
+	if (del[0] == '"') {
+		quoted = 1;
+	}
+	while ((c = getc(f)) != EOF) {
+		if (c == '\r') /* carriage return */
+			c = ' ';
+		if (c == '(' && prev_c != '\\' && !quoted) {
+			/* this only counts for non-comments */
+			if (com == 0) {
+				p++;
+			}
+			prev_c = c;
+			continue;
+		}
+
+		if (c == ')' && prev_c != '\\' && !quoted) {
+			/* this only counts for non-comments */
+			if (com == 0) {
+				p--;
+			}
+			prev_c = c;
+			continue;
+		}
+
+		if (p < 0) {
+			/* more ) then ( - close off the string */
+			*t = '\0';
+			return 0;
+		}
+
+		/* do something with comments ; */
+		if (c == ';' && quoted == 0) {
+			if (prev_c != '\\') {
+				com = 1;
+			}
+		}
+		if (c == '\"' && com == 0 && prev_c != '\\') {
+			quoted = 1 - quoted;
+		}
+
+		if (c == '\n' && com != 0) {
+			/* comments */
+			com = 0;
+			*t = ' ';
+			if (line_nr) {
+				*line_nr = *line_nr + 1;
+			}
+			if (p == 0 && i > 0) {
+				goto tokenread;
+			} else {
+				prev_c = c;
+				continue;
+			}
+		}
+
+		if (com == 1) {
+			*t = ' ';
+			prev_c = c;
+			continue;
+		}
+
+		if (c == '\n' && p != 0 && t > token) {
+			/* in parentheses */
+			if (line_nr) {
+				*line_nr = *line_nr + 1;
+			}
+			*t++ = ' ';
+			prev_c = c;
+			continue;
+		}
+
+		/* check if we hit the delim */
+		for (d = del; *d; d++) {
+			if (c == *d && i > 0 && prev_c != '\\' && p == 0) {
+				if (c == '\n' && line_nr) {
+					*line_nr = *line_nr + 1;
+				}
+				goto tokenread;
+			}
+		}
+		if (c != '\0' && c != '\n') {
+			i++;
+		}
+		if (limit > 0 && i >= limit) {
+			*t = '\0';
+			return -1;
+		}
+		if (c != '\0' && c != '\n') {
+			*t++ = c;
+		}
+		if (c == '\\' && prev_c == '\\')
+			prev_c = 0;
+		else	prev_c = c;
+	}
+	*t = '\0';
+	if (c == EOF) {
+		return (ssize_t)i;
+	}
+
+	if (i == 0) {
+		/* nothing read */
+		return -1;
+	}
+	if (p != 0) {
+		return -1;
+	}
+	return (ssize_t)i;
+
+tokenread:
+	ldns_fskipcs_l(f, delim, line_nr);
+	*t = '\0';
+	if (p != 0) {
+		return -1;
+	}
+
+	return (ssize_t)i;
+}
+
+ssize_t
+ldns_fget_keyword_data(FILE *f, const char *keyword, const char *k_del, char *data,
+               const char *d_del, size_t data_limit)
+{
+       return ldns_fget_keyword_data_l(f, keyword, k_del, data, d_del,
+		       data_limit, NULL);
+}
+
+ssize_t
+ldns_fget_keyword_data_l(FILE *f, const char *keyword, const char *k_del, char *data,
+               const char *d_del, size_t data_limit, int *line_nr)
+{
+       /* we assume: keyword|sep|data */
+       char *fkeyword;
+       ssize_t i;
+
+       if(strlen(keyword) >= LDNS_MAX_KEYWORDLEN)
+               return -1;
+       fkeyword = LDNS_XMALLOC(char, LDNS_MAX_KEYWORDLEN);
+       if(!fkeyword)
+               return -1;
+
+       i = ldns_fget_token(f, fkeyword, k_del, LDNS_MAX_KEYWORDLEN);
+       if(i==0 || i==-1) {
+               LDNS_FREE(fkeyword);
+               return -1;
+       }
+
+       /* case??? i instead of strlen? */
+       if (strncmp(fkeyword, keyword, LDNS_MAX_KEYWORDLEN - 1) == 0) {
+               /* whee! */
+               /* printf("%s\n%s\n", "Matching keyword", fkeyword); */
+               i = ldns_fget_token_l(f, data, d_del, data_limit, line_nr);
+               LDNS_FREE(fkeyword);
+               return i;
+       } else {
+               /*printf("no match for %s (read: %s)\n", keyword, fkeyword);*/
+               LDNS_FREE(fkeyword);
+               return -1;
+       }
+}
+
+
+ssize_t
+ldns_bget_token(ldns_buffer *b, char *token, const char *delim, size_t limit)
+{
+	int c, lc;
+	int p; /* 0 -> no parenthese seen, >0 nr of ( seen */
+	int com, quoted;
+	char *t;
+	size_t i;
+	const char *d;
+	const char *del;
+
+	/* standard delimiters */
+	if (!delim) {
+		/* from isspace(3) */
+		del = LDNS_PARSE_NORMAL;
+	} else {
+		del = delim;
+	}
+
+	p = 0;
+	i = 0;
+	com = 0;
+	quoted = 0;
+	t = token;
+	lc = 0;
+	if (del[0] == '"') {
+		quoted = 1;
+	}
+
+	while ((c = ldns_bgetc(b)) != EOF) {
+		if (c == '\r') /* carriage return */
+			c = ' ';
+		if (c == '(' && lc != '\\' && !quoted) {
+			/* this only counts for non-comments */
+			if (com == 0) {
+				p++;
+			}
+			lc = c;
+			continue;
+		}
+
+		if (c == ')' && lc != '\\' && !quoted) {
+			/* this only counts for non-comments */
+			if (com == 0) {
+				p--;
+			}
+			lc = c;
+			continue;
+		}
+
+		if (p < 0) {
+			/* more ) then ( */
+			*t = '\0';
+			return 0;
+		}
+
+		/* do something with comments ; */
+		if (c == ';' && quoted == 0) {
+			if (lc != '\\') {
+				com = 1;
+			}
+		}
+		if (c == '"' && com == 0 && lc != '\\') {
+			quoted = 1 - quoted;
+		}
+
+		if (c == '\n' && com != 0) {
+			/* comments */
+			com = 0;
+			*t = ' ';
+			lc = c;
+			continue;
+		}
+
+		if (com == 1) {
+			*t = ' ';
+			lc = c;
+			continue;
+		}
+
+		if (c == '\n' && p != 0) {
+			/* in parentheses */
+			*t++ = ' ';
+			lc = c;
+			continue;
+		}
+
+		/* check if we hit the delim */
+		for (d = del; *d; d++) {
+                        if (c == *d && lc != '\\' && p == 0) {
+				goto tokenread;
+                        }
+		}
+
+		i++;
+		if (limit > 0 && i >= limit) {
+			*t = '\0';
+			return -1;
+		}
+		*t++ = c;
+
+		if (c == '\\' && lc == '\\') {
+			lc = 0;
+		} else {
+			lc = c;
+		}
+	}
+	*t = '\0';
+	if (i == 0) {
+		/* nothing read */
+		return -1;
+	}
+	if (p != 0) {
+		return -1;
+	}
+	return (ssize_t)i;
+
+tokenread:
+	ldns_bskipcs(b, delim);
+	*t = '\0';
+
+	if (p != 0) {
+		return -1;
+	}
+	return (ssize_t)i;
+}
+
+void
+ldns_bskipc(ldns_buffer *buffer, char c)
+{
+        while (c == (char) ldns_buffer_read_u8_at(buffer, ldns_buffer_position(buffer))) {
+                if (ldns_buffer_available_at(buffer,
+					buffer->_position + sizeof(char), sizeof(char))) {
+                        buffer->_position += sizeof(char);
+                } else {
+                        return;
+                }
+        }
+}
+
+void
+ldns_bskipcs(ldns_buffer *buffer, const char *s)
+{
+        bool found;
+        char c;
+        const char *d;
+
+        while(ldns_buffer_available_at(buffer, buffer->_position, sizeof(char))) {
+                c = (char) ldns_buffer_read_u8_at(buffer, buffer->_position);
+                found = false;
+                for (d = s; *d; d++) {
+                        if (*d == c) {
+                                found = true;
+                        }
+                }
+                if (found && buffer->_limit > buffer->_position) {
+                        buffer->_position += sizeof(char);
+                } else {
+                        return;
+                }
+        }
+}
+
+void
+ldns_fskipc(FILE *fp, char c)
+{
+	fp = fp;
+	c = c;
+}
+
+
+void
+ldns_fskipcs(FILE *fp, const char *s)
+{
+	ldns_fskipcs_l(fp, s, NULL);
+}
+
+void
+ldns_fskipcs_l(FILE *fp, const char *s, int *line_nr)
+{
+        bool found;
+        int c;
+        const char *d;
+
+	while ((c = fgetc(fp)) != EOF) {
+		if (line_nr && c == '\n') {
+			*line_nr = *line_nr + 1;
+		}
+                found = false;
+                for (d = s; *d; d++) {
+                        if (*d == c) {
+                                found = true;
+                        }
+                }
+		if (!found) {
+			/* with getc, we've read too far */
+			ungetc(c, fp);
+			return;
+		}
+	}
+}
+
+ssize_t
+ldns_bget_keyword_data(ldns_buffer *b, const char *keyword, const char *k_del, char
+*data, const char *d_del, size_t data_limit)
+{
+       /* we assume: keyword|sep|data */
+       char *fkeyword;
+       ssize_t i;
+
+       if(strlen(keyword) >= LDNS_MAX_KEYWORDLEN)
+               return -1;
+       fkeyword = LDNS_XMALLOC(char, LDNS_MAX_KEYWORDLEN);
+       if(!fkeyword)
+               return -1; /* out of memory */
+
+       i = ldns_bget_token(b, fkeyword, k_del, data_limit);
+       if(i==0 || i==-1) {
+               LDNS_FREE(fkeyword);
+               return -1; /* nothing read */
+       }
+
+       /* case??? */
+       if (strncmp(fkeyword, keyword, strlen(keyword)) == 0) {
+               LDNS_FREE(fkeyword);
+               /* whee, the match! */
+               /* retrieve it's data */
+               i = ldns_bget_token(b, data, d_del, 0);
+               return i;
+       } else {
+               LDNS_FREE(fkeyword);
+               return -1;
+       }
+}
+
diff --git a/3rdParty/Ldns/src/src/rbtree.c b/3rdParty/Ldns/src/src/rbtree.c
new file mode 100644
index 0000000..217e61d
--- /dev/null
+++ b/3rdParty/Ldns/src/src/rbtree.c
@@ -0,0 +1,669 @@
+/*
+ * rbtree.c -- generic red black tree
+ *
+ * Taken from Unbound, modified for ldns
+ *
+ * Copyright (c) 2001-2008, NLnet Labs. All rights reserved.
+ * 
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ *
+ */
+
+/**
+ * \file
+ * Implementation of a redblack tree.
+ */
+
+#include <ldns/config.h>
+#include <ldns/rbtree.h>
+#include <stdlib.h>
+
+/** Node colour black */
+#define	BLACK	0
+/** Node colour red */
+#define	RED	1
+
+/** the NULL node, global alloc */
+ldns_rbnode_t	ldns_rbtree_null_node = {
+	LDNS_RBTREE_NULL,	/* Parent.  */
+	LDNS_RBTREE_NULL,	/* Left.  */
+	LDNS_RBTREE_NULL,	/* Right.  */
+	NULL,			/* Key.  */
+	NULL,               /* Data. */
+	BLACK		     /* Color.  */
+};
+
+/** rotate subtree left (to preserve redblack property) */
+static void ldns_rbtree_rotate_left(ldns_rbtree_t *rbtree, ldns_rbnode_t *node);
+/** rotate subtree right (to preserve redblack property) */
+static void ldns_rbtree_rotate_right(ldns_rbtree_t *rbtree, ldns_rbnode_t *node);
+/** Fixup node colours when insert happened */
+static void ldns_rbtree_insert_fixup(ldns_rbtree_t *rbtree, ldns_rbnode_t *node);
+/** Fixup node colours when delete happened */
+static void ldns_rbtree_delete_fixup(ldns_rbtree_t* rbtree, ldns_rbnode_t* child, ldns_rbnode_t* child_parent);
+
+/*
+ * Creates a new red black tree, intializes and returns a pointer to it.
+ *
+ * Return NULL on failure.
+ *
+ */
+ldns_rbtree_t *
+ldns_rbtree_create (int (*cmpf)(const void *, const void *))
+{
+	ldns_rbtree_t *rbtree;
+
+	/* Allocate memory for it */
+	rbtree = (ldns_rbtree_t *) malloc(sizeof(ldns_rbtree_t));
+	if (!rbtree) {
+		return NULL;
+	}
+
+	/* Initialize it */
+	ldns_rbtree_init(rbtree, cmpf);
+
+	return rbtree;
+}
+
+void 
+ldns_rbtree_init(ldns_rbtree_t *rbtree, int (*cmpf)(const void *, const void *))
+{
+	/* Initialize it */
+	rbtree->root = LDNS_RBTREE_NULL;
+	rbtree->count = 0;
+	rbtree->cmp = cmpf;
+}
+
+void 
+ldns_rbtree_free(ldns_rbtree_t *rbtree)
+{
+	free(rbtree);
+}
+
+/*
+ * Rotates the node to the left.
+ *
+ */
+static void
+ldns_rbtree_rotate_left(ldns_rbtree_t *rbtree, ldns_rbnode_t *node)
+{
+	ldns_rbnode_t *right = node->right;
+	node->right = right->left;
+	if (right->left != LDNS_RBTREE_NULL)
+		right->left->parent = node;
+
+	right->parent = node->parent;
+
+	if (node->parent != LDNS_RBTREE_NULL) {
+		if (node == node->parent->left) {
+			node->parent->left = right;
+		} else  {
+			node->parent->right = right;
+		}
+	} else {
+		rbtree->root = right;
+	}
+	right->left = node;
+	node->parent = right;
+}
+
+/*
+ * Rotates the node to the right.
+ *
+ */
+static void
+ldns_rbtree_rotate_right(ldns_rbtree_t *rbtree, ldns_rbnode_t *node)
+{
+	ldns_rbnode_t *left = node->left;
+	node->left = left->right;
+	if (left->right != LDNS_RBTREE_NULL)
+		left->right->parent = node;
+
+	left->parent = node->parent;
+
+	if (node->parent != LDNS_RBTREE_NULL) {
+		if (node == node->parent->right) {
+			node->parent->right = left;
+		} else  {
+			node->parent->left = left;
+		}
+	} else {
+		rbtree->root = left;
+	}
+	left->right = node;
+	node->parent = left;
+}
+
+static void
+ldns_rbtree_insert_fixup(ldns_rbtree_t *rbtree, ldns_rbnode_t *node)
+{
+	ldns_rbnode_t	*uncle;
+
+	/* While not at the root and need fixing... */
+	while (node != rbtree->root && node->parent->color == RED) {
+		/* If our parent is left child of our grandparent... */
+		if (node->parent == node->parent->parent->left) {
+			uncle = node->parent->parent->right;
+
+			/* If our uncle is red... */
+			if (uncle->color == RED) {
+				/* Paint the parent and the uncle black... */
+				node->parent->color = BLACK;
+				uncle->color = BLACK;
+
+				/* And the grandparent red... */
+				node->parent->parent->color = RED;
+
+				/* And continue fixing the grandparent */
+				node = node->parent->parent;
+			} else {				/* Our uncle is black... */
+				/* Are we the right child? */
+				if (node == node->parent->right) {
+					node = node->parent;
+					ldns_rbtree_rotate_left(rbtree, node);
+				}
+				/* Now we're the left child, repaint and rotate... */
+				node->parent->color = BLACK;
+				node->parent->parent->color = RED;
+				ldns_rbtree_rotate_right(rbtree, node->parent->parent);
+			}
+		} else {
+			uncle = node->parent->parent->left;
+
+			/* If our uncle is red... */
+			if (uncle->color == RED) {
+				/* Paint the parent and the uncle black... */
+				node->parent->color = BLACK;
+				uncle->color = BLACK;
+
+				/* And the grandparent red... */
+				node->parent->parent->color = RED;
+
+				/* And continue fixing the grandparent */
+				node = node->parent->parent;
+			} else {				/* Our uncle is black... */
+				/* Are we the right child? */
+				if (node == node->parent->left) {
+					node = node->parent;
+					ldns_rbtree_rotate_right(rbtree, node);
+				}
+				/* Now we're the right child, repaint and rotate... */
+				node->parent->color = BLACK;
+				node->parent->parent->color = RED;
+				ldns_rbtree_rotate_left(rbtree, node->parent->parent);
+			}
+		}
+	}
+	rbtree->root->color = BLACK;
+}
+
+void
+ldns_rbtree_insert_vref(ldns_rbnode_t *data, void *rbtree)
+{
+	(void) ldns_rbtree_insert((ldns_rbtree_t *) rbtree,
+						 data);
+}
+
+/*
+ * Inserts a node into a red black tree.
+ *
+ * Returns NULL on failure or the pointer to the newly added node
+ * otherwise.
+ */
+ldns_rbnode_t *
+ldns_rbtree_insert (ldns_rbtree_t *rbtree, ldns_rbnode_t *data)
+{
+	/* XXX Not necessary, but keeps compiler quiet... */
+	int r = 0;
+
+	/* We start at the root of the tree */
+	ldns_rbnode_t	*node = rbtree->root;
+	ldns_rbnode_t	*parent = LDNS_RBTREE_NULL;
+
+	/* Lets find the new parent... */
+	while (node != LDNS_RBTREE_NULL) {
+		/* Compare two keys, do we have a duplicate? */
+		if ((r = rbtree->cmp(data->key, node->key)) == 0) {
+			return NULL;
+		}
+		parent = node;
+
+		if (r < 0) {
+			node = node->left;
+		} else {
+			node = node->right;
+		}
+	}
+
+	/* Initialize the new node */
+	data->parent = parent;
+	data->left = data->right = LDNS_RBTREE_NULL;
+	data->color = RED;
+	rbtree->count++;
+
+	/* Insert it into the tree... */
+	if (parent != LDNS_RBTREE_NULL) {
+		if (r < 0) {
+			parent->left = data;
+		} else {
+			parent->right = data;
+		}
+	} else {
+		rbtree->root = data;
+	}
+
+	/* Fix up the red-black properties... */
+	ldns_rbtree_insert_fixup(rbtree, data);
+
+	return data;
+}
+
+/*
+ * Searches the red black tree, returns the data if key is found or NULL otherwise.
+ *
+ */
+ldns_rbnode_t *
+ldns_rbtree_search (ldns_rbtree_t *rbtree, const void *key)
+{
+	ldns_rbnode_t *node;
+
+	if (ldns_rbtree_find_less_equal(rbtree, key, &node)) {
+		return node;
+	} else {
+		return NULL;
+	}
+}
+
+/** helpers for delete: swap node colours */
+static void swap_int8(uint8_t* x, uint8_t* y) 
+{ 
+	uint8_t t = *x; *x = *y; *y = t; 
+}
+
+/** helpers for delete: swap node pointers */
+static void swap_np(ldns_rbnode_t** x, ldns_rbnode_t** y) 
+{
+	ldns_rbnode_t* t = *x; *x = *y; *y = t; 
+}
+
+/** Update parent pointers of child trees of 'parent' */
+static void change_parent_ptr(ldns_rbtree_t* rbtree, ldns_rbnode_t* parent, ldns_rbnode_t* old, ldns_rbnode_t* new)
+{
+	if(parent == LDNS_RBTREE_NULL)
+	{
+		if(rbtree->root == old) rbtree->root = new;
+		return;
+	}
+	if(parent->left == old) parent->left = new;
+	if(parent->right == old) parent->right = new;
+}
+/** Update parent pointer of a node 'child' */
+static void change_child_ptr(ldns_rbnode_t* child, ldns_rbnode_t* old, ldns_rbnode_t* new)
+{
+	if(child == LDNS_RBTREE_NULL) return;
+	if(child->parent == old) child->parent = new;
+}
+
+ldns_rbnode_t* 
+ldns_rbtree_delete(ldns_rbtree_t *rbtree, const void *key)
+{
+	ldns_rbnode_t *to_delete;
+	ldns_rbnode_t *child;
+	if((to_delete = ldns_rbtree_search(rbtree, key)) == 0) return 0;
+	rbtree->count--;
+
+	/* make sure we have at most one non-leaf child */
+	if(to_delete->left != LDNS_RBTREE_NULL &&
+	   to_delete->right != LDNS_RBTREE_NULL)
+	{
+		/* swap with smallest from right subtree (or largest from left) */
+		ldns_rbnode_t *smright = to_delete->right;
+		while(smright->left != LDNS_RBTREE_NULL)
+			smright = smright->left;
+		/* swap the smright and to_delete elements in the tree,
+		 * but the ldns_rbnode_t is first part of user data struct
+		 * so cannot just swap the keys and data pointers. Instead
+		 * readjust the pointers left,right,parent */
+
+		/* swap colors - colors are tied to the position in the tree */
+		swap_int8(&to_delete->color, &smright->color);
+
+		/* swap child pointers in parents of smright/to_delete */
+		change_parent_ptr(rbtree, to_delete->parent, to_delete, smright);
+		if(to_delete->right != smright)
+			change_parent_ptr(rbtree, smright->parent, smright, to_delete);
+
+		/* swap parent pointers in children of smright/to_delete */
+		change_child_ptr(smright->left, smright, to_delete);
+		change_child_ptr(smright->left, smright, to_delete);
+		change_child_ptr(smright->right, smright, to_delete);
+		change_child_ptr(smright->right, smright, to_delete);
+		change_child_ptr(to_delete->left, to_delete, smright);
+		if(to_delete->right != smright)
+			change_child_ptr(to_delete->right, to_delete, smright);
+		if(to_delete->right == smright)
+		{
+			/* set up so after swap they work */
+			to_delete->right = to_delete;
+			smright->parent = smright;
+		}
+
+		/* swap pointers in to_delete/smright nodes */
+		swap_np(&to_delete->parent, &smright->parent);
+		swap_np(&to_delete->left, &smright->left);
+		swap_np(&to_delete->right, &smright->right);
+
+		/* now delete to_delete (which is at the location where the smright previously was) */
+	}
+
+	if(to_delete->left != LDNS_RBTREE_NULL) child = to_delete->left;
+	else child = to_delete->right;
+
+	/* unlink to_delete from the tree, replace to_delete with child */
+	change_parent_ptr(rbtree, to_delete->parent, to_delete, child);
+	change_child_ptr(child, to_delete, to_delete->parent);
+
+	if(to_delete->color == RED)
+	{
+		/* if node is red then the child (black) can be swapped in */
+	}
+	else if(child->color == RED)
+	{
+		/* change child to BLACK, removing a RED node is no problem */
+		if(child!=LDNS_RBTREE_NULL) child->color = BLACK;
+	}
+	else ldns_rbtree_delete_fixup(rbtree, child, to_delete->parent);
+
+	/* unlink completely */
+	to_delete->parent = LDNS_RBTREE_NULL;
+	to_delete->left = LDNS_RBTREE_NULL;
+	to_delete->right = LDNS_RBTREE_NULL;
+	to_delete->color = BLACK;
+	return to_delete;
+}
+
+static void ldns_rbtree_delete_fixup(ldns_rbtree_t* rbtree, ldns_rbnode_t* child, ldns_rbnode_t* child_parent)
+{
+	ldns_rbnode_t* sibling;
+	int go_up = 1;
+
+	/* determine sibling to the node that is one-black short */
+	if(child_parent->right == child) sibling = child_parent->left;
+	else sibling = child_parent->right;
+
+	while(go_up)
+	{
+		if(child_parent == LDNS_RBTREE_NULL)
+		{
+			/* removed parent==black from root, every path, so ok */
+			return;
+		}
+
+		if(sibling->color == RED)
+		{	/* rotate to get a black sibling */
+			child_parent->color = RED;
+			sibling->color = BLACK;
+			if(child_parent->right == child)
+				ldns_rbtree_rotate_right(rbtree, child_parent);
+			else	ldns_rbtree_rotate_left(rbtree, child_parent);
+			/* new sibling after rotation */
+			if(child_parent->right == child) sibling = child_parent->left;
+			else sibling = child_parent->right;
+		}
+
+		if(child_parent->color == BLACK 
+			&& sibling->color == BLACK
+			&& sibling->left->color == BLACK
+			&& sibling->right->color == BLACK)
+		{	/* fixup local with recolor of sibling */
+			if(sibling != LDNS_RBTREE_NULL)
+				sibling->color = RED;
+
+			child = child_parent;
+			child_parent = child_parent->parent;
+			/* prepare to go up, new sibling */
+			if(child_parent->right == child) sibling = child_parent->left;
+			else sibling = child_parent->right;
+		}
+		else go_up = 0;
+	}
+
+	if(child_parent->color == RED
+		&& sibling->color == BLACK
+		&& sibling->left->color == BLACK
+		&& sibling->right->color == BLACK) 
+	{
+		/* move red to sibling to rebalance */
+		if(sibling != LDNS_RBTREE_NULL)
+			sibling->color = RED;
+		child_parent->color = BLACK;
+		return;
+	}
+
+	/* get a new sibling, by rotating at sibling. See which child
+	   of sibling is red */
+	if(child_parent->right == child
+		&& sibling->color == BLACK
+		&& sibling->right->color == RED
+		&& sibling->left->color == BLACK)
+	{
+		sibling->color = RED;
+		sibling->right->color = BLACK;
+		ldns_rbtree_rotate_left(rbtree, sibling);
+		/* new sibling after rotation */
+		if(child_parent->right == child) sibling = child_parent->left;
+		else sibling = child_parent->right;
+	}
+	else if(child_parent->left == child
+		&& sibling->color == BLACK
+		&& sibling->left->color == RED
+		&& sibling->right->color == BLACK)
+	{
+		sibling->color = RED;
+		sibling->left->color = BLACK;
+		ldns_rbtree_rotate_right(rbtree, sibling);
+		/* new sibling after rotation */
+		if(child_parent->right == child) sibling = child_parent->left;
+		else sibling = child_parent->right;
+	}
+
+	/* now we have a black sibling with a red child. rotate and exchange colors. */
+	sibling->color = child_parent->color;
+	child_parent->color = BLACK;
+	if(child_parent->right == child)
+	{
+		sibling->left->color = BLACK;
+		ldns_rbtree_rotate_right(rbtree, child_parent);
+	}
+	else
+	{
+		sibling->right->color = BLACK;
+		ldns_rbtree_rotate_left(rbtree, child_parent);
+	}
+}
+
+int
+ldns_rbtree_find_less_equal(ldns_rbtree_t *rbtree, const void *key, ldns_rbnode_t **result)
+{
+	int r;
+	ldns_rbnode_t *node;
+
+	/* We start at root... */
+	node = rbtree->root;
+
+	*result = NULL;
+
+	/* While there are children... */
+	while (node != LDNS_RBTREE_NULL) {
+		r = rbtree->cmp(key, node->key);
+		if (r == 0) {
+			/* Exact match */
+			*result = node;
+			return 1;
+		} 
+		if (r < 0) {
+			node = node->left;
+		} else {
+			/* Temporary match */
+			*result = node;
+			node = node->right;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Finds the first element in the red black tree
+ *
+ */
+ldns_rbnode_t *
+ldns_rbtree_first (ldns_rbtree_t *rbtree)
+{
+	ldns_rbnode_t *node = rbtree->root;
+
+	if (rbtree->root != LDNS_RBTREE_NULL) {
+		for (node = rbtree->root; node->left != LDNS_RBTREE_NULL; node = node->left);
+	}
+	return node;
+}
+
+ldns_rbnode_t *
+ldns_rbtree_last (ldns_rbtree_t *rbtree)
+{
+	ldns_rbnode_t *node = rbtree->root;
+
+	if (rbtree->root != LDNS_RBTREE_NULL) {
+		for (node = rbtree->root; node->right != LDNS_RBTREE_NULL; node = node->right);
+	}
+	return node;
+}
+
+/*
+ * Returns the next node...
+ *
+ */
+ldns_rbnode_t *
+ldns_rbtree_next (ldns_rbnode_t *node)
+{
+	ldns_rbnode_t *parent;
+
+	if (node->right != LDNS_RBTREE_NULL) {
+		/* One right, then keep on going left... */
+		for (node = node->right;
+			node->left != LDNS_RBTREE_NULL;
+			node = node->left);
+	} else {
+		parent = node->parent;
+		while (parent != LDNS_RBTREE_NULL && node == parent->right) {
+			node = parent;
+			parent = parent->parent;
+		}
+		node = parent;
+	}
+	return node;
+}
+
+ldns_rbnode_t *
+ldns_rbtree_previous(ldns_rbnode_t *node)
+{
+	ldns_rbnode_t *parent;
+
+	if (node->left != LDNS_RBTREE_NULL) {
+		/* One left, then keep on going right... */
+		for (node = node->left;
+			node->right != LDNS_RBTREE_NULL;
+			node = node->right);
+	} else {
+		parent = node->parent;
+		while (parent != LDNS_RBTREE_NULL && node == parent->left) {
+			node = parent;
+			parent = parent->parent;
+		}
+		node = parent;
+	}
+	return node;
+}
+
+/**
+ * split off elements number of elements from the start
+ * of the name tree and return a new tree 
+ */
+ldns_rbtree_t *
+ldns_rbtree_split(ldns_rbtree_t *tree,
+			   size_t elements)
+{
+	ldns_rbtree_t *new_tree;
+	ldns_rbnode_t *cur_node;
+	ldns_rbnode_t *move_node;
+	size_t count = 0;
+
+	new_tree = ldns_rbtree_create(tree->cmp);
+
+	cur_node = ldns_rbtree_first(tree);
+	while (count < elements && cur_node != LDNS_RBTREE_NULL) {
+		move_node = ldns_rbtree_delete(tree, cur_node->key);
+		(void)ldns_rbtree_insert(new_tree, move_node);
+		cur_node = ldns_rbtree_first(tree);
+		count++;
+	}
+
+	return new_tree;
+}
+
+/*
+ * add all node from the second tree to the first (removing them from the
+ * second), and fix up nsec(3)s if present
+ */
+void
+ldns_rbtree_join(ldns_rbtree_t *tree1, ldns_rbtree_t *tree2)
+{
+	ldns_traverse_postorder(tree2, ldns_rbtree_insert_vref, tree1);
+}
+
+/** recursive descent traverse */
+static void 
+traverse_post(void (*func)(ldns_rbnode_t*, void*), void* arg, 
+	ldns_rbnode_t* node)
+{
+	if(!node || node == LDNS_RBTREE_NULL)
+		return;
+	/* recurse */
+	traverse_post(func, arg, node->left);
+	traverse_post(func, arg, node->right);
+	/* call user func */
+	(*func)(node, arg);
+}
+
+void 
+ldns_traverse_postorder(ldns_rbtree_t* tree, 
+	void (*func)(ldns_rbnode_t*, void*), void* arg)
+{
+	traverse_post(func, arg, tree->root);
+}
diff --git a/3rdParty/Ldns/src/src/rdata.c b/3rdParty/Ldns/src/src/rdata.c
new file mode 100644
index 0000000..8af16a1
--- /dev/null
+++ b/3rdParty/Ldns/src/src/rdata.c
@@ -0,0 +1,675 @@
+/*
+ * rdata.c
+ *
+ * rdata implementation
+ *
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2004-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+#include <ldns/config.h>
+
+#include <ldns/ldns.h>
+
+/*
+ * Access functions 
+ * do this as functions to get type checking
+ */
+
+/* read */
+size_t
+ldns_rdf_size(const ldns_rdf *rd)
+{
+	assert(rd != NULL);
+	return rd->_size;
+}
+
+ldns_rdf_type
+ldns_rdf_get_type(const ldns_rdf *rd)
+{
+	assert(rd != NULL);
+	return rd->_type;
+}
+
+uint8_t *
+ldns_rdf_data(const ldns_rdf *rd)
+{
+	assert(rd != NULL);
+	return rd->_data;
+}
+
+/* write */
+void
+ldns_rdf_set_size(ldns_rdf *rd, size_t size)
+{
+	assert(rd != NULL);
+	rd->_size = size;
+}
+
+void
+ldns_rdf_set_type(ldns_rdf *rd, ldns_rdf_type type)
+{
+	assert(rd != NULL);
+	rd->_type = type;
+}
+
+void
+ldns_rdf_set_data(ldns_rdf *rd, void *data)
+{
+	/* only copy the pointer */
+	assert(rd != NULL);
+	rd->_data = data;
+}
+
+/* for types that allow it, return
+ * the native/host order type */
+uint8_t
+ldns_rdf2native_int8(const ldns_rdf *rd)
+{
+	uint8_t data;
+
+	/* only allow 8 bit rdfs */
+	if (ldns_rdf_size(rd) != LDNS_RDF_SIZE_BYTE) {
+		return 0;
+	}
+	
+	memcpy(&data, ldns_rdf_data(rd), sizeof(data));
+	return data;
+}
+
+uint16_t
+ldns_rdf2native_int16(const ldns_rdf *rd)
+{
+	uint16_t data;
+
+	/* only allow 16 bit rdfs */
+	if (ldns_rdf_size(rd) != LDNS_RDF_SIZE_WORD) {
+		return 0;
+	}
+	
+	memcpy(&data, ldns_rdf_data(rd), sizeof(data));
+	return ntohs(data);
+}
+
+uint32_t
+ldns_rdf2native_int32(const ldns_rdf *rd)
+{
+	uint32_t data;
+
+	/* only allow 32 bit rdfs */
+	if (ldns_rdf_size(rd) != LDNS_RDF_SIZE_DOUBLEWORD) {
+		return 0;
+	}
+	
+	memcpy(&data, ldns_rdf_data(rd), sizeof(data));
+	return ntohl(data);
+}
+
+time_t
+ldns_rdf2native_time_t(const ldns_rdf *rd)
+{
+	uint32_t data;
+	
+	switch(ldns_rdf_get_type(rd)) {
+		case LDNS_RDF_TYPE_TIME:
+			memcpy(&data, ldns_rdf_data(rd), sizeof(data));
+			return (time_t)ntohl(data);
+		default:
+			return 0;
+	}
+}
+
+ldns_rdf *
+ldns_native2rdf_int8(ldns_rdf_type type, uint8_t value)
+{
+	return ldns_rdf_new_frm_data(type, LDNS_RDF_SIZE_BYTE, &value);
+}
+
+ldns_rdf *
+ldns_native2rdf_int16(ldns_rdf_type type, uint16_t value)
+{
+	uint16_t *rdf_data = LDNS_XMALLOC(uint16_t, 1);
+        ldns_rdf* rdf;
+	if (!rdf_data) {
+		return NULL;
+	}
+	ldns_write_uint16(rdf_data, value);
+	rdf = ldns_rdf_new(type, LDNS_RDF_SIZE_WORD, rdf_data);
+        if(!rdf)
+                LDNS_FREE(rdf_data);
+        return rdf;
+}
+
+ldns_rdf *
+ldns_native2rdf_int32(ldns_rdf_type type, uint32_t value)
+{
+	uint32_t *rdf_data = LDNS_XMALLOC(uint32_t, 1);
+        ldns_rdf* rdf;
+	if (!rdf_data) {
+		return NULL;
+	}
+	ldns_write_uint32(rdf_data, value);
+	rdf = ldns_rdf_new(type, LDNS_RDF_SIZE_DOUBLEWORD, rdf_data);
+        if(!rdf)
+                LDNS_FREE(rdf_data);
+        return rdf;
+}
+
+ldns_rdf *
+ldns_native2rdf_int16_data(size_t size, uint8_t *data)
+{
+	uint8_t *rdf_data = LDNS_XMALLOC(uint8_t, size + 2);
+        ldns_rdf* rdf;
+	if (!rdf_data) {
+		return NULL;
+	}
+	ldns_write_uint16(rdf_data, size);
+	memcpy(rdf_data + 2, data, size);
+	rdf = ldns_rdf_new(LDNS_RDF_TYPE_INT16_DATA, size + 2, rdf_data);
+        if(!rdf)
+                LDNS_FREE(rdf_data);
+        return rdf;
+}
+
+/* note: data must be allocated memory */
+ldns_rdf *
+ldns_rdf_new(ldns_rdf_type type, size_t size, void *data)
+{
+	ldns_rdf *rd;
+	rd = LDNS_MALLOC(ldns_rdf);
+	if (!rd) {
+		return NULL;
+	}
+	ldns_rdf_set_size(rd, size);
+	ldns_rdf_set_type(rd, type);
+	ldns_rdf_set_data(rd, data);
+	return rd;
+}
+
+ldns_rdf *
+ldns_rdf_new_frm_data(ldns_rdf_type type, size_t size, const void *data)
+{
+	ldns_rdf *rdf;
+
+	/* if the size is too big, fail */
+	if (size > LDNS_MAX_RDFLEN) {
+		return NULL;
+	}
+
+	/* allocate space */
+	rdf = LDNS_MALLOC(ldns_rdf);
+	if (!rdf) {
+		return NULL;
+	}
+	rdf->_data = LDNS_XMALLOC(uint8_t, size);
+	if (!rdf->_data) {
+		LDNS_FREE(rdf);
+		return NULL;
+	}
+	
+	/* set the values */
+	ldns_rdf_set_type(rdf, type);
+	ldns_rdf_set_size(rdf, size);
+	memcpy(rdf->_data, data, size);
+
+	return rdf;
+}
+
+ldns_rdf *
+ldns_rdf_clone(const ldns_rdf *rd)
+{
+	assert(rd != NULL);
+	return (ldns_rdf_new_frm_data( ldns_rdf_get_type(rd),
+		ldns_rdf_size(rd), ldns_rdf_data(rd)));
+}
+
+void
+ldns_rdf_deep_free(ldns_rdf *rd)
+{
+	if (rd) {
+		if (rd->_data) {
+			LDNS_FREE(rd->_data);
+		}
+		LDNS_FREE(rd);
+	}
+}
+
+void 
+ldns_rdf_free(ldns_rdf *rd)
+{
+	if (rd) {
+		LDNS_FREE(rd);
+	}
+}
+
+ldns_rdf *
+ldns_rdf_new_frm_str(ldns_rdf_type type, const char *str)
+{
+	ldns_rdf *rdf = NULL;
+	ldns_status status;
+
+	switch (type) {
+	case LDNS_RDF_TYPE_DNAME:
+		status = ldns_str2rdf_dname(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_INT8:
+		status = ldns_str2rdf_int8(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_INT16:
+		status = ldns_str2rdf_int16(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_INT32:
+		status = ldns_str2rdf_int32(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_A:
+		status = ldns_str2rdf_a(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_AAAA:
+		status = ldns_str2rdf_aaaa(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_STR:
+		status = ldns_str2rdf_str(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_APL:
+		status = ldns_str2rdf_apl(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_B64:
+		status = ldns_str2rdf_b64(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_B32_EXT:
+		status = ldns_str2rdf_b32_ext(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_HEX:
+		status = ldns_str2rdf_hex(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_NSEC:
+		status = ldns_str2rdf_nsec(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_TYPE:
+		status = ldns_str2rdf_type(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_CLASS:
+		status = ldns_str2rdf_class(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_CERT_ALG:
+		status = ldns_str2rdf_cert_alg(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_ALG:
+		status = ldns_str2rdf_alg(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_UNKNOWN:
+		status = ldns_str2rdf_unknown(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_TIME:
+		status = ldns_str2rdf_time(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_PERIOD:
+		status = ldns_str2rdf_period(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_TSIG:
+		status = ldns_str2rdf_tsig(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_SERVICE:
+		status = ldns_str2rdf_service(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_LOC:
+		status = ldns_str2rdf_loc(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_WKS:
+		status = ldns_str2rdf_wks(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_NSAP:
+		status = ldns_str2rdf_nsap(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_ATMA:
+		status = ldns_str2rdf_atma(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_IPSECKEY:
+		status = ldns_str2rdf_ipseckey(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_NSEC3_SALT:
+		status = ldns_str2rdf_nsec3_salt(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_NSEC3_NEXT_OWNER:
+		status = ldns_str2rdf_b32_ext(&rdf, str);
+		break;
+	case LDNS_RDF_TYPE_NONE:
+	default:
+		/* default default ??? */
+		status = LDNS_STATUS_ERR;
+		break;
+	}
+	if (LDNS_STATUS_OK == status) {
+		ldns_rdf_set_type(rdf, type);
+		return rdf;
+	}
+	if (rdf) {
+		LDNS_FREE(rdf);
+	}
+	return NULL;
+}
+
+ldns_status
+ldns_rdf_new_frm_fp(ldns_rdf **rdf, ldns_rdf_type type, FILE *fp)
+{
+	return ldns_rdf_new_frm_fp_l(rdf, type, fp, NULL);
+}
+
+ldns_status
+ldns_rdf_new_frm_fp_l(ldns_rdf **rdf, ldns_rdf_type type, FILE *fp, int *line_nr)
+{
+	char *line;
+	ldns_rdf *r;
+	ssize_t t;
+
+	line = LDNS_XMALLOC(char, LDNS_MAX_LINELEN + 1);
+	if (!line) {
+		return LDNS_STATUS_MEM_ERR;
+	}
+
+	/* read an entire line in from the file */
+	if ((t = ldns_fget_token_l(fp, line, LDNS_PARSE_SKIP_SPACE, 0, line_nr)) == -1 || t == 0) {
+		LDNS_FREE(line);
+		return LDNS_STATUS_SYNTAX_RDATA_ERR;
+	}
+	r =  ldns_rdf_new_frm_str(type, (const char*) line);
+	LDNS_FREE(line);
+	if (rdf) {
+		*rdf = r;
+		return LDNS_STATUS_OK;
+	} else {
+		return LDNS_STATUS_NULL;
+	}
+}
+
+ldns_rdf *
+ldns_rdf_address_reverse(ldns_rdf *rd)
+{
+	uint8_t buf_4[LDNS_IP4ADDRLEN];
+	uint8_t buf_6[LDNS_IP6ADDRLEN * 2];
+	ldns_rdf *rev;
+	ldns_rdf *in_addr;
+	ldns_rdf *ret_dname;
+	uint8_t octet;
+	uint8_t nnibble;
+	uint8_t nibble;
+	uint8_t i, j;
+
+	char *char_dname;
+	int nbit;
+
+	if (ldns_rdf_get_type(rd) != LDNS_RDF_TYPE_A &&
+			ldns_rdf_get_type(rd) != LDNS_RDF_TYPE_AAAA) {
+		return NULL;
+	}
+
+	in_addr = NULL;
+	ret_dname = NULL;
+
+	switch(ldns_rdf_get_type(rd)) {
+		case LDNS_RDF_TYPE_A:
+			/* the length of the buffer is 4 */
+			buf_4[3] = ldns_rdf_data(rd)[0];
+			buf_4[2] = ldns_rdf_data(rd)[1];
+			buf_4[1] = ldns_rdf_data(rd)[2];
+			buf_4[0] = ldns_rdf_data(rd)[3];
+			in_addr = ldns_dname_new_frm_str("in-addr.arpa.");
+			if (!in_addr) {
+				return NULL;
+			}
+			/* make a new rdf and convert that back  */
+			rev = ldns_rdf_new_frm_data( LDNS_RDF_TYPE_A,
+				LDNS_IP4ADDRLEN, (void*)&buf_4);
+			if (!rev) {
+				LDNS_FREE(in_addr);
+				return NULL;
+			}
+
+			/* convert rev to a string */
+			char_dname = ldns_rdf2str(rev);
+			if (!char_dname) {
+				LDNS_FREE(in_addr);
+				ldns_rdf_deep_free(rev);
+				return NULL;
+			}
+			/* transform back to rdf with type dname */
+			ret_dname = ldns_dname_new_frm_str(char_dname);
+			if (!ret_dname) {
+				LDNS_FREE(in_addr);
+				ldns_rdf_deep_free(rev);
+				LDNS_FREE(char_dname);
+				return NULL;
+			}
+			/* not needed anymore */
+			ldns_rdf_deep_free(rev);
+			LDNS_FREE(char_dname);
+			break;
+		case LDNS_RDF_TYPE_AAAA:
+			/* some foo magic to reverse the nibbles ... */
+
+			for (nbit = 127; nbit >= 0; nbit = nbit - 4) {
+				/* calculate octett (8 bit) */
+				octet = ( ((unsigned int) nbit) & 0x78) >> 3;
+				/* calculate nibble */
+				nnibble = ( ((unsigned int) nbit) & 0x04) >> 2;
+				/* extract nibble */
+				nibble = (ldns_rdf_data(rd)[octet] & ( 0xf << (4 * (1 -
+						 nnibble)) ) ) >> ( 4 * (1 - 
+						nnibble));
+
+				buf_6[(LDNS_IP6ADDRLEN * 2 - 1) -
+					(octet * 2 + nnibble)] = 
+						(uint8_t)ldns_int_to_hexdigit((int)nibble);
+			}
+
+			char_dname = LDNS_XMALLOC(char, (LDNS_IP6ADDRLEN * 4));
+			if (!char_dname) {
+				return NULL;
+			}
+			char_dname[LDNS_IP6ADDRLEN * 4 - 1] = '\0'; /* closure */
+
+			/* walk the string and add . 's */
+			for (i = 0, j = 0; i < LDNS_IP6ADDRLEN * 2; i++, j = j + 2) {
+				char_dname[j] = (char)buf_6[i];
+				if (i != LDNS_IP6ADDRLEN * 2 - 1) {
+					char_dname[j + 1] = '.';
+				}
+			}
+			in_addr = ldns_dname_new_frm_str("ip6.arpa.");
+			if (!in_addr) {
+				LDNS_FREE(char_dname);
+				return NULL;
+			}
+
+			/* convert rev to a string */
+			ret_dname = ldns_dname_new_frm_str(char_dname);
+			LDNS_FREE(char_dname);
+			if (!ret_dname) {
+				ldns_rdf_deep_free(in_addr);
+				return NULL;
+			}
+			break;
+		default:
+			break;
+	}
+	/* add the suffix */
+	rev = ldns_dname_cat_clone(ret_dname, in_addr);
+
+	ldns_rdf_deep_free(ret_dname);
+	ldns_rdf_deep_free(in_addr);
+	return rev;
+}
+
+ldns_status
+ldns_octet(char *word, size_t *length)
+{
+    char *s; 
+    char *p;
+    *length = 0;
+
+    for (s = p = word; *s != '\0'; s++,p++) {
+        switch (*s) {
+            case '.':
+                if (s[1] == '.') {
+		    return LDNS_STATUS_EMPTY_LABEL;
+                }
+                *p = *s;
+                (*length)++;
+                break;
+            case '\\':
+                if ('0' <= s[1] && s[1] <= '9' &&
+                    '0' <= s[2] && s[2] <= '9' &&
+                    '0' <= s[3] && s[3] <= '9') {
+                    /* \DDD seen */
+                    int val = ((s[1] - '0') * 100 +
+                           (s[2] - '0') * 10 + (s[3] - '0'));
+
+                    if (0 <= val && val <= 255) {
+                        /* this also handles \0 */
+                        s += 3;
+                        *p = val;
+                        (*length)++;
+                    } else {
+                        return LDNS_STATUS_DDD_OVERFLOW;
+                    }
+                } else {
+                    /* an espaced character, like \<space> ? 
+                    * remove the '\' keep the rest */
+                    *p = *++s;
+                    (*length)++;
+                }
+                break;
+            case '\"':
+                /* non quoted " Is either first or the last character in
+                 * the string */
+
+                *p = *++s; /* skip it */
+                (*length)++;
+		/* I'm not sure if this is needed in libdns... MG */
+                if ( *s == '\0' ) {
+                    /* ok, it was the last one */
+                    *p  = '\0'; 
+		    return LDNS_STATUS_OK;
+                }
+                break;
+            default:
+                *p = *s;
+                (*length)++;
+                break;
+        }
+    }
+    *p = '\0';
+    return LDNS_STATUS_OK;
+}
+
+int
+ldns_rdf_compare(const ldns_rdf *rd1, const ldns_rdf *rd2)
+{
+	uint16_t i1, i2, i;
+	uint8_t *d1, *d2;
+
+	/* only when both are not NULL we can say anything about them */
+	if (!rd1 && !rd2) {
+		return 0;
+	}
+	if (!rd1 || !rd2) {
+		return -1;
+	}
+	i1 = ldns_rdf_size(rd1);
+	i2 = ldns_rdf_size(rd2);
+
+	if (i1 < i2) {
+		return -1;
+	} else if (i1 > i2) {
+		return +1;
+	} else {
+		d1 = (uint8_t*)ldns_rdf_data(rd1);
+		d2 = (uint8_t*)ldns_rdf_data(rd2);
+		for(i = 0; i < i1; i++) {
+			if (d1[i] < d2[i]) {
+				return -1;
+			} else if (d1[i] > d2[i]) {
+				return +1;
+			}
+		}
+	}
+	return 0;
+}
+
+uint32_t
+ldns_str2period(const char *nptr, const char **endptr)
+{
+	int sign = 0;
+	uint32_t i = 0;
+	uint32_t seconds = 0;
+
+	for(*endptr = nptr; **endptr; (*endptr)++) {
+		switch (**endptr) {
+			case ' ':
+			case '\t':
+				break;
+			case '-':
+				if(sign == 0) {
+					sign = -1;
+				} else {
+					return seconds;
+				}
+				break;
+			case '+':
+				if(sign == 0) {
+					sign = 1;
+				} else {
+					return seconds;
+				}
+				break;
+			case 's':
+			case 'S':
+				seconds += i;
+				i = 0;
+				break;
+			case 'm':
+			case 'M':
+				seconds += i * 60;
+				i = 0;
+				break;
+			case 'h':
+			case 'H':
+				seconds += i * 60 * 60;
+				i = 0;
+				break;
+			case 'd':
+			case 'D':
+				seconds += i * 60 * 60 * 24;
+				i = 0;
+				break;
+			case 'w':
+			case 'W':
+				seconds += i * 60 * 60 * 24 * 7;
+				i = 0;
+				break;
+			case '0':
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+				i *= 10;
+				i += (**endptr - '0');
+				break;
+			default:
+				seconds += i;
+				/* disregard signedness */
+				return seconds;
+		}
+	}
+	seconds += i;
+	/* disregard signedness */
+	return seconds;
+}
diff --git a/3rdParty/Ldns/src/src/resolver.c b/3rdParty/Ldns/src/src/resolver.c
new file mode 100644
index 0000000..732f2a8
--- /dev/null
+++ b/3rdParty/Ldns/src/src/resolver.c
@@ -0,0 +1,1350 @@
+/*
+ * resolver.c
+ *
+ * resolver implementation
+ *
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2004-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+#include <ldns/config.h>
+
+#include <ldns/ldns.h>
+#include <strings.h>
+
+/* Access function for reading
+ * and setting the different Resolver
+ * options */
+
+/* read */
+uint16_t
+ldns_resolver_port(const ldns_resolver *r)
+{
+	return r->_port;
+}
+
+uint16_t
+ldns_resolver_edns_udp_size(const ldns_resolver *r)
+{
+	        return r->_edns_udp_size;
+}
+
+uint8_t
+ldns_resolver_retry(const ldns_resolver *r)
+{
+	return r->_retry;
+}
+
+uint8_t
+ldns_resolver_retrans(const ldns_resolver *r)
+{
+	return r->_retrans;
+}
+
+bool
+ldns_resolver_fallback(const ldns_resolver *r)
+{
+	return r->_fallback;
+}
+
+uint8_t
+ldns_resolver_ip6(const ldns_resolver *r)
+{
+	return r->_ip6;
+}
+
+bool
+ldns_resolver_recursive(const ldns_resolver *r)
+{
+	return r->_recursive;
+}
+
+bool
+ldns_resolver_debug(const ldns_resolver *r)
+{
+	return r->_debug;
+}
+
+bool
+ldns_resolver_dnsrch(const ldns_resolver *r)
+{
+	return r->_dnsrch;
+}
+
+bool
+ldns_resolver_fail(const ldns_resolver *r)
+{
+	return r->_fail;
+}
+
+bool
+ldns_resolver_defnames(const ldns_resolver *r)
+{
+	return r->_defnames;
+}
+
+ldns_rdf *
+ldns_resolver_domain(const ldns_resolver *r)
+{
+	return r->_domain;
+}
+
+ldns_rdf **
+ldns_resolver_searchlist(const ldns_resolver *r)
+{
+	return r->_searchlist;
+}
+
+ldns_rdf **
+ldns_resolver_nameservers(const ldns_resolver *r)
+{
+	return r->_nameservers;
+}
+
+size_t
+ldns_resolver_nameserver_count(const ldns_resolver *r)
+{
+	return r->_nameserver_count;
+}
+
+bool
+ldns_resolver_dnssec(const ldns_resolver *r)
+{
+	return r->_dnssec;
+}
+
+bool
+ldns_resolver_dnssec_cd(const ldns_resolver *r)
+{
+	return r->_dnssec_cd;
+}
+
+ldns_rr_list *
+ldns_resolver_dnssec_anchors(const ldns_resolver *r)
+{
+    return r->_dnssec_anchors;
+}
+
+bool
+ldns_resolver_trusted_key(const ldns_resolver *r, ldns_rr_list * keys, ldns_rr_list * trusted_keys)
+{
+  size_t i;
+  bool result = false;
+
+  ldns_rr_list * trust_anchors;
+  ldns_rr * cur_rr;
+
+  if (!r || !keys) { return false; }
+
+  trust_anchors = ldns_resolver_dnssec_anchors(r);
+
+  if (!trust_anchors) { return false; }
+
+  for (i = 0; i < ldns_rr_list_rr_count(keys); i++) {
+
+    cur_rr = ldns_rr_list_rr(keys, i);
+    if (ldns_rr_list_contains_rr(trust_anchors, cur_rr)) {
+      if (trusted_keys) { ldns_rr_list_push_rr(trusted_keys, cur_rr); }
+      result = true;
+    }
+  }
+
+  return result;
+}
+
+bool
+ldns_resolver_igntc(const ldns_resolver *r)
+{
+	return r->_igntc;
+}
+
+bool
+ldns_resolver_usevc(const ldns_resolver *r)
+{
+	return r->_usevc;
+}
+
+size_t *
+ldns_resolver_rtt(const ldns_resolver *r)
+{
+	return r->_rtt;
+}
+
+size_t
+ldns_resolver_nameserver_rtt(const ldns_resolver *r, size_t pos)
+{
+	size_t *rtt;
+
+	assert(r != NULL);
+
+	rtt = ldns_resolver_rtt(r);
+
+	if (pos >= ldns_resolver_nameserver_count(r)) {
+		/* error ?*/
+		return 0;
+	} else {
+		return rtt[pos];
+	}
+
+}
+
+struct timeval
+ldns_resolver_timeout(const ldns_resolver *r)
+{
+	return r->_timeout;
+}
+
+char *
+ldns_resolver_tsig_keyname(const ldns_resolver *r)
+{
+	return r->_tsig_keyname;
+}
+
+char *
+ldns_resolver_tsig_algorithm(const ldns_resolver *r)
+{
+	return r->_tsig_algorithm;
+}
+
+char *
+ldns_resolver_tsig_keydata(const ldns_resolver *r)
+{
+	return r->_tsig_keydata;
+}
+
+bool
+ldns_resolver_random(const ldns_resolver *r)
+{
+	return r->_random;
+}
+
+size_t
+ldns_resolver_searchlist_count(const ldns_resolver *r)
+{
+	return r->_searchlist_count;
+}
+
+/* write */
+void
+ldns_resolver_set_port(ldns_resolver *r, uint16_t p)
+{
+	r->_port = p;
+}
+
+ldns_rdf *
+ldns_resolver_pop_nameserver(ldns_resolver *r)
+{
+	ldns_rdf **nameservers;
+	ldns_rdf *pop;
+	size_t ns_count;
+	size_t *rtt;
+
+	assert(r != NULL);
+
+	ns_count = ldns_resolver_nameserver_count(r);
+	nameservers = ldns_resolver_nameservers(r);
+	rtt = ldns_resolver_rtt(r);
+	if (ns_count == 0 || !nameservers) {
+		return NULL;
+	}
+
+	pop = nameservers[ns_count - 1];
+
+	nameservers = LDNS_XREALLOC(nameservers, ldns_rdf *, (ns_count - 1));
+	rtt = LDNS_XREALLOC(rtt, size_t, (ns_count - 1));
+
+        if(nameservers)
+	        ldns_resolver_set_nameservers(r, nameservers);
+        if(rtt)
+	        ldns_resolver_set_rtt(r, rtt);
+	/* decr the count */
+	ldns_resolver_dec_nameserver_count(r);
+	return pop;
+}
+
+ldns_status
+ldns_resolver_push_nameserver(ldns_resolver *r, ldns_rdf *n)
+{
+	ldns_rdf **nameservers;
+	size_t ns_count;
+	size_t *rtt;
+
+	if (ldns_rdf_get_type(n) != LDNS_RDF_TYPE_A &&
+			ldns_rdf_get_type(n) != LDNS_RDF_TYPE_AAAA) {
+		return LDNS_STATUS_ERR;
+	}
+
+	ns_count = ldns_resolver_nameserver_count(r);
+	nameservers = ldns_resolver_nameservers(r);
+	rtt = ldns_resolver_rtt(r);
+
+	/* make room for the next one */
+	if (ns_count == 0) {
+		nameservers = LDNS_XMALLOC(ldns_rdf *, 1);
+	} else {
+		nameservers = LDNS_XREALLOC(nameservers, ldns_rdf *, (ns_count + 1));
+	}
+        if(!nameservers)
+                return LDNS_STATUS_MEM_ERR;
+
+	/* set the new value in the resolver */
+	ldns_resolver_set_nameservers(r, nameservers);
+
+	/* don't forget the rtt */
+	if (ns_count == 0) {
+		rtt = LDNS_XMALLOC(size_t, 1);
+	} else {
+		rtt = LDNS_XREALLOC(rtt, size_t, (ns_count + 1));
+	}
+        if(!rtt)
+                return LDNS_STATUS_MEM_ERR;
+
+	/* slide n in its slot. */
+	/* we clone it here, because then we can free the original
+	 * rr's where it stood */
+	nameservers[ns_count] = ldns_rdf_clone(n);
+	rtt[ns_count] = LDNS_RESOLV_RTT_MIN;
+	ldns_resolver_incr_nameserver_count(r);
+	ldns_resolver_set_rtt(r, rtt);
+	return LDNS_STATUS_OK;
+}
+
+ldns_status
+ldns_resolver_push_nameserver_rr(ldns_resolver *r, ldns_rr *rr)
+{
+	ldns_rdf *address;
+	if ((!rr) || (ldns_rr_get_type(rr) != LDNS_RR_TYPE_A &&
+			ldns_rr_get_type(rr) != LDNS_RR_TYPE_AAAA)) {
+		return LDNS_STATUS_ERR;
+	}
+	address = ldns_rr_rdf(rr, 0); /* extract the ip number */
+	if (address) {
+		return ldns_resolver_push_nameserver(r, address);
+	} else {
+		return LDNS_STATUS_ERR;
+	}
+}
+
+ldns_status
+ldns_resolver_push_nameserver_rr_list(ldns_resolver *r, ldns_rr_list *rrlist)
+{
+	ldns_rr *rr;
+	ldns_status stat;
+	size_t i;
+
+	stat = LDNS_STATUS_OK;
+	if (rrlist) {
+		for(i = 0; i < ldns_rr_list_rr_count(rrlist); i++) {
+			rr = ldns_rr_list_rr(rrlist, i);
+			if (ldns_resolver_push_nameserver_rr(r, rr) != LDNS_STATUS_OK) {
+				stat = LDNS_STATUS_ERR;
+				break;
+			}
+		}
+		return stat;
+	} else {
+		return LDNS_STATUS_ERR;
+	}
+}
+
+void
+ldns_resolver_set_edns_udp_size(ldns_resolver *r, uint16_t s)
+{
+	        r->_edns_udp_size = s;
+}
+
+void
+ldns_resolver_set_recursive(ldns_resolver *r, bool re)
+{
+	r->_recursive = re;
+}
+
+void
+ldns_resolver_set_dnssec(ldns_resolver *r, bool d)
+{
+	r->_dnssec = d;
+}
+
+void
+ldns_resolver_set_dnssec_cd(ldns_resolver *r, bool d)
+{
+	r->_dnssec_cd = d;
+}
+
+void
+ldns_resolver_set_dnssec_anchors(ldns_resolver *r, ldns_rr_list * l)
+{
+  r->_dnssec_anchors = l;
+}
+
+ldns_status
+ldns_resolver_push_dnssec_anchor(ldns_resolver *r, ldns_rr *rr)
+{
+  ldns_rr_list * trust_anchors;
+
+  if ((!rr) || (ldns_rr_get_type(rr) != LDNS_RR_TYPE_DNSKEY)) {
+    return LDNS_STATUS_ERR;
+  }
+
+  if (!(trust_anchors = ldns_resolver_dnssec_anchors(r))) { /* Initialize */
+    trust_anchors = ldns_rr_list_new();
+    ldns_resolver_set_dnssec_anchors(r, trust_anchors);
+  }
+
+  return (ldns_rr_list_push_rr(trust_anchors, ldns_rr_clone(rr))) ? LDNS_STATUS_OK : LDNS_STATUS_ERR;
+}
+
+void
+ldns_resolver_set_igntc(ldns_resolver *r, bool i)
+{
+	r->_igntc = i;
+}
+
+void
+ldns_resolver_set_usevc(ldns_resolver *r, bool vc)
+{
+	r->_usevc = vc;
+}
+
+void
+ldns_resolver_set_debug(ldns_resolver *r, bool d)
+{
+	r->_debug = d;
+}
+
+void
+ldns_resolver_set_ip6(ldns_resolver *r, uint8_t ip6)
+{
+	r->_ip6 = ip6;
+}
+
+void
+ldns_resolver_set_fail(ldns_resolver *r, bool f)
+{
+	r->_fail =f;
+}
+
+void
+ldns_resolver_set_searchlist_count(ldns_resolver *r, size_t c)
+{
+	r->_searchlist_count = c;
+}
+
+void
+ldns_resolver_set_nameserver_count(ldns_resolver *r, size_t c)
+{
+	r->_nameserver_count = c;
+}
+
+void
+ldns_resolver_set_dnsrch(ldns_resolver *r, bool d)
+{
+	r->_dnsrch = d;
+}
+
+void
+ldns_resolver_set_retry(ldns_resolver *r, uint8_t retry)
+{
+	r->_retry = retry;
+}
+
+void
+ldns_resolver_set_retrans(ldns_resolver *r, uint8_t retrans)
+{
+	r->_retrans = retrans;
+}
+
+void
+ldns_resolver_set_fallback(ldns_resolver *r, bool fallback)
+{
+	r->_fallback = fallback;
+}
+
+void
+ldns_resolver_set_nameservers(ldns_resolver *r, ldns_rdf **n)
+{
+	r->_nameservers = n;
+}
+
+void
+ldns_resolver_set_defnames(ldns_resolver *r, bool d)
+{
+	r->_defnames = d;
+}
+
+void
+ldns_resolver_set_rtt(ldns_resolver *r, size_t *rtt)
+{
+	r->_rtt = rtt;
+}
+
+void
+ldns_resolver_set_nameserver_rtt(ldns_resolver *r, size_t pos, size_t value)
+{
+	size_t *rtt;
+
+	assert(r != NULL);
+
+	rtt = ldns_resolver_rtt(r);
+
+	if (pos >= ldns_resolver_nameserver_count(r)) {
+		/* error ?*/
+	} else {
+		rtt[pos] = value;
+	}
+
+}
+
+void
+ldns_resolver_incr_nameserver_count(ldns_resolver *r)
+{
+	size_t c;
+
+	c = ldns_resolver_nameserver_count(r);
+	ldns_resolver_set_nameserver_count(r, ++c);
+}
+
+void
+ldns_resolver_dec_nameserver_count(ldns_resolver *r)
+{
+	size_t c;
+
+	c = ldns_resolver_nameserver_count(r);
+	if (c == 0) {
+		return;
+	} else {
+		ldns_resolver_set_nameserver_count(r, --c);
+	}
+}
+
+void
+ldns_resolver_set_domain(ldns_resolver *r, ldns_rdf *d)
+{
+	r->_domain = d;
+}
+
+void
+ldns_resolver_set_timeout(ldns_resolver *r, struct timeval timeout)
+{
+	r->_timeout.tv_sec = timeout.tv_sec;
+	r->_timeout.tv_usec = timeout.tv_usec;
+}
+
+void
+ldns_resolver_push_searchlist(ldns_resolver *r, ldns_rdf *d)
+{
+	ldns_rdf **searchlist;
+	size_t list_count;
+
+	if (ldns_rdf_get_type(d) != LDNS_RDF_TYPE_DNAME) {
+		return;
+	}
+
+	list_count = ldns_resolver_searchlist_count(r);
+	searchlist = ldns_resolver_searchlist(r);
+
+	searchlist = LDNS_XREALLOC(searchlist, ldns_rdf *, (list_count + 1));
+	if (searchlist) {
+		r->_searchlist = searchlist;
+
+		searchlist[list_count] = ldns_rdf_clone(d);
+		ldns_resolver_set_searchlist_count(r, list_count + 1);
+	} /* no way to report mem err */
+}
+
+void
+ldns_resolver_set_tsig_keyname(ldns_resolver *r, char *tsig_keyname)
+{
+	LDNS_FREE(r->_tsig_keyname);
+	r->_tsig_keyname = strdup(tsig_keyname);
+}
+
+void
+ldns_resolver_set_tsig_algorithm(ldns_resolver *r, char *tsig_algorithm)
+{
+	LDNS_FREE(r->_tsig_algorithm);
+	r->_tsig_algorithm = strdup(tsig_algorithm);
+}
+
+void
+ldns_resolver_set_tsig_keydata(ldns_resolver *r, char *tsig_keydata)
+{
+	LDNS_FREE(r->_tsig_keydata);
+	r->_tsig_keydata = strdup(tsig_keydata);
+}
+
+void
+ldns_resolver_set_random(ldns_resolver *r, bool b)
+{
+	r->_random = b;
+}
+
+/* more sophisticated functions */
+ldns_resolver *
+ldns_resolver_new(void)
+{
+	ldns_resolver *r;
+
+	r = LDNS_MALLOC(ldns_resolver);
+	if (!r) {
+		return NULL;
+	}
+
+	r->_searchlist = NULL;
+	r->_nameservers = NULL;
+	r->_rtt = NULL;
+
+	/* defaults are filled out */
+	ldns_resolver_set_searchlist_count(r, 0);
+	ldns_resolver_set_nameserver_count(r, 0);
+	ldns_resolver_set_usevc(r, 0);
+	ldns_resolver_set_port(r, LDNS_PORT);
+	ldns_resolver_set_domain(r, NULL);
+	ldns_resolver_set_defnames(r, false);
+	ldns_resolver_set_retry(r, 3);
+	ldns_resolver_set_retrans(r, 2);
+	ldns_resolver_set_fallback(r, true);
+	ldns_resolver_set_fail(r, false);
+	ldns_resolver_set_edns_udp_size(r, 0);
+	ldns_resolver_set_dnssec(r, false);
+	ldns_resolver_set_dnssec_cd(r, false);
+	ldns_resolver_set_dnssec_anchors(r, NULL);
+	ldns_resolver_set_ip6(r, LDNS_RESOLV_INETANY);
+	ldns_resolver_set_igntc(r, false);
+	ldns_resolver_set_recursive(r, false);
+	ldns_resolver_set_dnsrch(r, true);
+
+	/* randomize the nameserver to be queried
+	 * when there are multiple
+	 */
+	ldns_resolver_set_random(r, true);
+
+	ldns_resolver_set_debug(r, 0);
+
+	r->_timeout.tv_sec = LDNS_DEFAULT_TIMEOUT_SEC;
+	r->_timeout.tv_usec = LDNS_DEFAULT_TIMEOUT_USEC;
+
+	/* TODO: fd=0 is actually a valid socket (stdin),
+           replace with -1 */
+	r->_socket = 0;
+	r->_axfr_soa_count = 0;
+	r->_axfr_i = 0;
+	r->_cur_axfr_pkt = NULL;
+
+	r->_tsig_keyname = NULL;
+	r->_tsig_keydata = NULL;
+	r->_tsig_algorithm = NULL;
+	return r;
+}
+
+ldns_status
+ldns_resolver_new_frm_fp(ldns_resolver **res, FILE *fp)
+{
+	return ldns_resolver_new_frm_fp_l(res, fp, NULL);
+}
+
+ldns_status
+ldns_resolver_new_frm_fp_l(ldns_resolver **res, FILE *fp, int *line_nr)
+{
+	ldns_resolver *r;
+	const char *keyword[LDNS_RESOLV_KEYWORDS];
+	char word[LDNS_MAX_LINELEN + 1];
+	int8_t expect;
+	uint8_t i;
+	ldns_rdf *tmp;
+#ifdef HAVE_SSL
+	ldns_rr *tmp_rr;
+#endif
+	ssize_t gtr, bgtr;
+	ldns_buffer *b;
+        int lnr = 0, oldline;
+        if(!line_nr) line_nr = &lnr;
+
+	/* do this better
+	 * expect =
+	 * 0: keyword
+	 * 1: default domain dname
+	 * 2: NS aaaa or a record
+	 */
+
+	/* recognized keywords */
+	keyword[LDNS_RESOLV_NAMESERVER] = "nameserver";
+	keyword[LDNS_RESOLV_DEFDOMAIN] = "domain";
+	keyword[LDNS_RESOLV_SEARCH] = "search";
+	/* these two are read but not used atm TODO */
+	keyword[LDNS_RESOLV_SORTLIST] = "sortlist";
+	keyword[LDNS_RESOLV_OPTIONS] = "options";
+	keyword[LDNS_RESOLV_ANCHOR] = "anchor";
+	expect = LDNS_RESOLV_KEYWORD;
+
+	r = ldns_resolver_new();
+	if (!r) {
+		return LDNS_STATUS_MEM_ERR;
+	}
+
+	gtr = 1;
+	word[0] = 0;
+        oldline = *line_nr;
+        expect = LDNS_RESOLV_KEYWORD;
+	while (gtr > 0) {
+		/* check comments */
+		if (word[0] == '#') {
+                        word[0]='x';
+                        if(oldline == *line_nr) {
+                                /* skip until end of line */
+                                int c;
+                                do {
+                                        c = fgetc(fp);
+                                } while(c != EOF && c != '\n');
+                                if(c=='\n' && line_nr) (*line_nr)++;
+                        }
+			/* and read next to prepare for further parsing */
+                        oldline = *line_nr;
+			continue;
+		}
+                oldline = *line_nr;
+		switch(expect) {
+			case LDNS_RESOLV_KEYWORD:
+				/* keyword */
+				gtr = ldns_fget_token_l(fp, word, LDNS_PARSE_NORMAL, 0, line_nr);
+				if (gtr != 0) {
+                                        if(word[0] == '#') continue;
+					for(i = 0; i < LDNS_RESOLV_KEYWORDS; i++) {
+						if (strcasecmp(keyword[i], word) == 0) {
+							/* chosen the keyword and
+							 * expect values carefully
+	        					 */
+							expect = i;
+							break;
+						}
+					}
+					/* no keyword recognized */
+					if (expect == LDNS_RESOLV_KEYWORD) {
+						/* skip line */
+						/*
+						ldns_resolver_deep_free(r);
+						return LDNS_STATUS_SYNTAX_KEYWORD_ERR;
+						*/
+					}
+				}
+				break;
+			case LDNS_RESOLV_DEFDOMAIN:
+				/* default domain dname */
+				gtr = ldns_fget_token_l(fp, word, LDNS_PARSE_NORMAL, 0, line_nr);
+				if (gtr == 0) {
+					return LDNS_STATUS_SYNTAX_MISSING_VALUE_ERR;
+				}
+                                if(word[0] == '#') {
+                                        expect = LDNS_RESOLV_KEYWORD;
+                                        continue;
+                                }
+				tmp = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, word);
+				if (!tmp) {
+					ldns_resolver_deep_free(r);
+					return LDNS_STATUS_SYNTAX_DNAME_ERR;
+				}
+
+				/* DOn't free, because we copy the pointer */
+				ldns_resolver_set_domain(r, tmp);
+				expect = LDNS_RESOLV_KEYWORD;
+				break;
+			case LDNS_RESOLV_NAMESERVER:
+				/* NS aaaa or a record */
+				gtr = ldns_fget_token_l(fp, word, LDNS_PARSE_NORMAL, 0, line_nr);
+				if (gtr == 0) {
+					return LDNS_STATUS_SYNTAX_MISSING_VALUE_ERR;
+				}
+                                if(word[0] == '#') {
+                                        expect = LDNS_RESOLV_KEYWORD;
+                                        continue;
+                                }
+                                if(strchr(word, '%')) {
+                                        /* snip off interface labels,
+                                         * fe80::222:19ff:fe31:4222%eth0 */
+                                        strchr(word, '%')[0]=0;
+                                }
+				tmp = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_AAAA, word);
+				if (!tmp) {
+					/* try ip4 */
+					tmp = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_A, word);
+				}
+				/* could not parse it, exit */
+				if (!tmp) {
+					ldns_resolver_deep_free(r);
+					return LDNS_STATUS_SYNTAX_ERR;
+				}
+				(void)ldns_resolver_push_nameserver(r, tmp);
+				ldns_rdf_deep_free(tmp);
+				expect = LDNS_RESOLV_KEYWORD;
+				break;
+			case LDNS_RESOLV_SEARCH:
+				/* search list domain dname */
+				gtr = ldns_fget_token_l(fp, word, LDNS_PARSE_SKIP_SPACE, 0, line_nr);
+				b = LDNS_MALLOC(ldns_buffer);
+				if(!b) {
+					ldns_resolver_deep_free(r);
+					return LDNS_STATUS_MEM_ERR;
+				}
+
+				ldns_buffer_new_frm_data(b, word, (size_t) gtr);
+				if(ldns_buffer_status(b) != LDNS_STATUS_OK) {
+					LDNS_FREE(b);
+					ldns_resolver_deep_free(r);
+					return LDNS_STATUS_MEM_ERR;
+				}
+				bgtr = ldns_bget_token(b, word, LDNS_PARSE_NORMAL, (size_t) gtr + 1);
+				while (bgtr > 0) {
+					gtr -= bgtr;
+                                        if(word[0] == '#') {
+                                                expect = LDNS_RESOLV_KEYWORD;
+						ldns_buffer_free(b);
+                                                continue;
+                                        }
+					tmp = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, word);
+					if (!tmp) {
+						ldns_resolver_deep_free(r);
+						ldns_buffer_free(b);
+						return LDNS_STATUS_SYNTAX_DNAME_ERR;
+					}
+
+					ldns_resolver_push_searchlist(r, tmp);
+
+					ldns_rdf_deep_free(tmp);
+					bgtr = ldns_bget_token(b, word, LDNS_PARSE_NORMAL,
+					    (size_t) gtr + 1);
+				}
+				ldns_buffer_free(b);
+				gtr = 1;
+				expect = LDNS_RESOLV_KEYWORD;
+				break;
+			case LDNS_RESOLV_SORTLIST:
+				gtr = ldns_fget_token_l(fp, word, LDNS_PARSE_SKIP_SPACE, 0, line_nr);
+				/* sortlist not implemented atm */
+				expect = LDNS_RESOLV_KEYWORD;
+				break;
+			case LDNS_RESOLV_OPTIONS:
+				gtr = ldns_fget_token_l(fp, word, LDNS_PARSE_SKIP_SPACE, 0, line_nr);
+				/* options not implemented atm */
+				expect = LDNS_RESOLV_KEYWORD;
+				break;
+			case LDNS_RESOLV_ANCHOR:
+				/* a file containing a DNSSEC trust anchor */
+				gtr = ldns_fget_token_l(fp, word, LDNS_PARSE_NORMAL, 0, line_nr);
+				if (gtr == 0) {
+					ldns_resolver_deep_free(r);
+					return LDNS_STATUS_SYNTAX_MISSING_VALUE_ERR;
+				}
+                                if(word[0] == '#') {
+                                        expect = LDNS_RESOLV_KEYWORD;
+                                        continue;
+                                }
+
+#ifdef HAVE_SSL
+				tmp_rr = ldns_read_anchor_file(word);
+				(void) ldns_resolver_push_dnssec_anchor(r, tmp_rr);
+				ldns_rr_free(tmp_rr);
+#endif
+				expect = LDNS_RESOLV_KEYWORD;
+				break;
+		}
+	}
+
+	if (res) {
+		*res = r;
+		return LDNS_STATUS_OK;
+	} else {
+		ldns_resolver_deep_free(r);
+		return LDNS_STATUS_NULL;
+	}
+}
+
+ldns_status
+ldns_resolver_new_frm_file(ldns_resolver **res, const char *filename)
+{
+	ldns_resolver *r;
+	FILE *fp;
+	ldns_status s;
+
+	if (!filename) {
+		fp = fopen(LDNS_RESOLV_CONF, "r");
+
+	} else {
+		fp = fopen(filename, "r");
+	}
+	if (!fp) {
+		return LDNS_STATUS_FILE_ERR;
+	}
+
+	s = ldns_resolver_new_frm_fp(&r, fp);
+	fclose(fp);
+	if (s == LDNS_STATUS_OK) {
+		if (res) {
+			*res = r;
+			return LDNS_STATUS_OK;
+		} else  {
+			return LDNS_STATUS_NULL;
+		}
+	}
+	return s;
+}
+
+void
+ldns_resolver_free(ldns_resolver *res)
+{
+	LDNS_FREE(res);
+}
+
+void
+ldns_resolver_deep_free(ldns_resolver *res)
+{
+	size_t i;
+
+	if (res) {
+		if (res->_searchlist) {
+			for (i = 0; i < ldns_resolver_searchlist_count(res); i++) {
+				ldns_rdf_deep_free(res->_searchlist[i]);
+			}
+			LDNS_FREE(res->_searchlist);
+		}
+		if (res->_nameservers) {
+			for (i = 0; i < res->_nameserver_count; i++) {
+				ldns_rdf_deep_free(res->_nameservers[i]);
+			}
+			LDNS_FREE(res->_nameservers);
+		}
+		if (ldns_resolver_domain(res)) {
+			ldns_rdf_deep_free(ldns_resolver_domain(res));
+		}
+		if (res->_tsig_keyname) {
+			LDNS_FREE(res->_tsig_keyname);
+		}
+		if (res->_tsig_keydata) {
+			LDNS_FREE(res->_tsig_keydata);
+		}
+		if (res->_tsig_algorithm) {
+			LDNS_FREE(res->_tsig_algorithm);
+		}
+
+		if (res->_cur_axfr_pkt) {
+			ldns_pkt_free(res->_cur_axfr_pkt);
+		}
+
+		if (res->_rtt) {
+			LDNS_FREE(res->_rtt);
+		}
+		if (res->_dnssec_anchors) {
+			ldns_rr_list_deep_free(res->_dnssec_anchors);
+		}
+		LDNS_FREE(res);
+	}
+}
+
+ldns_pkt *
+ldns_resolver_search(const ldns_resolver *r,const  ldns_rdf *name,
+	ldns_rr_type t, ldns_rr_class c, uint16_t flags)
+{
+
+	char *str_dname;
+	ldns_rdf *new_name;
+	ldns_rdf **search_list;
+	size_t i;
+	ldns_pkt *p;
+
+	str_dname = ldns_rdf2str(name);
+
+	if (ldns_dname_str_absolute(str_dname)) {
+		/* query as-is */
+		return ldns_resolver_query(r, name, t, c, flags);
+	} else if (ldns_resolver_dnsrch(r)) {
+		search_list = ldns_resolver_searchlist(r);
+		for (i = 0; i < ldns_resolver_searchlist_count(r); i++) {
+			new_name = ldns_dname_cat_clone(name, search_list[i]);
+
+			p = ldns_resolver_query(r, new_name, t, c, flags);
+			ldns_rdf_free(new_name);
+			if (p) {
+				if (ldns_pkt_get_rcode(p) == LDNS_RCODE_NOERROR) {
+					return p;
+				} else {
+					ldns_pkt_free(p);
+					p = NULL;
+				}
+			}
+		}
+	}
+	return NULL;
+}
+
+ldns_pkt *
+ldns_resolver_query(const ldns_resolver *r, const ldns_rdf *name,
+	ldns_rr_type t, ldns_rr_class c, uint16_t flags)
+{
+	ldns_rdf *newname;
+	ldns_pkt *pkt;
+	ldns_status status;
+
+	pkt = NULL;
+
+	if (!ldns_resolver_defnames(r)) {
+		status = ldns_resolver_send(&pkt, (ldns_resolver *)r, name,
+				t, c, flags);
+		if (status == LDNS_STATUS_OK) {
+			return pkt;
+		} else {
+			if (pkt) {
+				ldns_pkt_free(pkt);
+			}
+			return NULL;
+		}
+	}
+
+	if (!ldns_resolver_domain(r)) {
+		/* _defnames is set, but the domain is not....?? */
+		status = ldns_resolver_send(&pkt, (ldns_resolver *)r, name,
+				t, c, flags);
+		if (status == LDNS_STATUS_OK) {
+			return pkt;
+		} else {
+			if (pkt) {
+				ldns_pkt_free(pkt);
+			}
+			return NULL;
+		}
+	}
+
+	newname = ldns_dname_cat_clone((const ldns_rdf*)name, ldns_resolver_domain(r));
+	if (!newname) {
+		if (pkt) {
+			ldns_pkt_free(pkt);
+		}
+		return NULL;
+	}
+
+	(void)ldns_resolver_send(&pkt, (ldns_resolver *)r, newname, t, c,
+			flags);
+
+	ldns_rdf_free(newname);
+
+	return pkt;
+}
+
+static size_t *
+ldns_resolver_backup_rtt(ldns_resolver *r)
+{
+	size_t *new_rtt;
+	size_t *old_rtt = ldns_resolver_rtt(r);
+
+	if (old_rtt && ldns_resolver_nameserver_count(r)) {
+		new_rtt = LDNS_XMALLOC(size_t
+				, ldns_resolver_nameserver_count(r));
+		memcpy(new_rtt, old_rtt, sizeof(size_t)
+				* ldns_resolver_nameserver_count(r));
+		ldns_resolver_set_rtt(r, new_rtt);
+		return old_rtt;
+	}
+	return NULL;
+}
+
+static void
+ldns_resolver_restore_rtt(ldns_resolver *r, size_t *old_rtt)
+{
+	size_t *cur_rtt = ldns_resolver_rtt(r);
+
+	if (cur_rtt) {
+		LDNS_FREE(cur_rtt);
+	}
+	ldns_resolver_set_rtt(r, old_rtt);
+}
+
+ldns_status
+ldns_resolver_send_pkt(ldns_pkt **answer, ldns_resolver *r,
+				   ldns_pkt *query_pkt)
+{
+	ldns_pkt *answer_pkt = NULL;
+	ldns_status stat = LDNS_STATUS_OK;
+	size_t *rtt;
+
+	stat = ldns_send(&answer_pkt, (ldns_resolver *)r, query_pkt);
+	if (stat != LDNS_STATUS_OK) {
+		if(answer_pkt) {
+			ldns_pkt_free(answer_pkt);
+			answer_pkt = NULL;
+		}
+	} else {
+		/* if tc=1 fall back to EDNS and/or TCP */
+		/* check for tcp first (otherwise we don't care about tc=1) */
+		if (!ldns_resolver_usevc(r) && ldns_resolver_fallback(r)) {
+			if (ldns_pkt_tc(answer_pkt)) {
+				/* was EDNS0 set? */
+				if (ldns_pkt_edns_udp_size(query_pkt) == 0) {
+					ldns_pkt_set_edns_udp_size(query_pkt
+							, 4096);
+					ldns_pkt_free(answer_pkt);
+					/* Nameservers should not become 
+					 * unreachable because fragments are
+					 * dropped (network error). We might
+					 * still have success with TCP.
+					 * Therefore maintain reachability
+					 * statuses of the nameservers by
+					 * backup and restore the rtt list.
+					 */
+					rtt = ldns_resolver_backup_rtt(r);
+					stat = ldns_send(&answer_pkt, r
+							, query_pkt);
+					ldns_resolver_restore_rtt(r, rtt);
+				}
+				/* either way, if it is still truncated, use TCP */
+				if (stat != LDNS_STATUS_OK ||
+				    ldns_pkt_tc(answer_pkt)) {
+					ldns_resolver_set_usevc(r, true);
+					ldns_pkt_free(answer_pkt);
+					stat = ldns_send(&answer_pkt, r, query_pkt);
+					ldns_resolver_set_usevc(r, false);
+				}
+			}
+		}
+	}
+
+	if (answer) {
+		*answer = answer_pkt;
+	}
+
+	return stat;
+}
+
+ldns_status
+ldns_resolver_prepare_query_pkt(ldns_pkt **query_pkt, ldns_resolver *r,
+                                const ldns_rdf *name, ldns_rr_type t,
+                                ldns_rr_class c, uint16_t flags)
+{
+	struct timeval now;
+
+	/* prepare a question pkt from the parameters
+	 * and then send this */
+	*query_pkt = ldns_pkt_query_new(ldns_rdf_clone(name), t, c, flags);
+	if (!*query_pkt) {
+		return LDNS_STATUS_ERR;
+	}
+
+	/* set DO bit if necessary */
+	if (ldns_resolver_dnssec(r)) {
+		if (ldns_resolver_edns_udp_size(r) == 0) {
+			ldns_resolver_set_edns_udp_size(r, 4096);
+		}
+		ldns_pkt_set_edns_do(*query_pkt, true);
+		if (ldns_resolver_dnssec_cd(r) || (flags & LDNS_CD)) {
+			ldns_pkt_set_cd(*query_pkt, true);
+		}
+	}
+
+	/* transfer the udp_edns_size from the resolver to the packet */
+	if (ldns_resolver_edns_udp_size(r) != 0) {
+		ldns_pkt_set_edns_udp_size(*query_pkt, ldns_resolver_edns_udp_size(r));
+	}
+
+	/* set the timestamp */
+	now.tv_sec = time(NULL);
+	now.tv_usec = 0;
+	ldns_pkt_set_timestamp(*query_pkt, now);
+
+
+	if (ldns_resolver_debug(r)) {
+		ldns_pkt_print(stdout, *query_pkt);
+	}
+
+	/* only set the id if it is not set yet */
+	if (ldns_pkt_id(*query_pkt) == 0) {
+		ldns_pkt_set_random_id(*query_pkt);
+	}
+
+	return LDNS_STATUS_OK;
+}
+
+
+ldns_status
+ldns_resolver_send(ldns_pkt **answer, ldns_resolver *r, const ldns_rdf *name,
+		ldns_rr_type t, ldns_rr_class c, uint16_t flags)
+{
+	ldns_pkt *query_pkt;
+	ldns_pkt *answer_pkt;
+	ldns_status status;
+
+	assert(r != NULL);
+	assert(name != NULL);
+
+	answer_pkt = NULL;
+
+	/* do all the preprocessing here, then fire of an query to
+	 * the network */
+
+	if (0 == t) {
+		t= LDNS_RR_TYPE_A;
+	}
+	if (0 == c) {
+		c= LDNS_RR_CLASS_IN;
+	}
+	if (0 == ldns_resolver_nameserver_count(r)) {
+		return LDNS_STATUS_RES_NO_NS;
+	}
+	if (ldns_rdf_get_type(name) != LDNS_RDF_TYPE_DNAME) {
+		return LDNS_STATUS_RES_QUERY;
+	}
+
+	status = ldns_resolver_prepare_query_pkt(&query_pkt, r, name,
+	                                         t, c, flags);
+	if (status != LDNS_STATUS_OK) {
+		return status;
+	}
+
+	/* if tsig values are set, tsign it */
+	/* TODO: make last 3 arguments optional too? maybe make complete
+	         rr instead of seperate values in resolver (and packet)
+	  Jelte
+	  should this go in pkt_prepare?
+	*/
+	if (ldns_resolver_tsig_keyname(r) && ldns_resolver_tsig_keydata(r)) {
+#ifdef HAVE_SSL
+		status = ldns_pkt_tsig_sign(query_pkt,
+		                            ldns_resolver_tsig_keyname(r),
+		                            ldns_resolver_tsig_keydata(r),
+		                            300, ldns_resolver_tsig_algorithm(r), NULL);
+		if (status != LDNS_STATUS_OK) {
+			return LDNS_STATUS_CRYPTO_TSIG_ERR;
+		}
+#else
+	        return LDNS_STATUS_CRYPTO_TSIG_ERR;
+#endif /* HAVE_SSL */
+	}
+
+	status = ldns_resolver_send_pkt(&answer_pkt, r, query_pkt);
+	ldns_pkt_free(query_pkt);
+
+	/* allows answer to be NULL when not interested in return value */
+	if (answer) {
+		*answer = answer_pkt;
+	}
+	return status;
+}
+
+ldns_rr *
+ldns_axfr_next(ldns_resolver *resolver)
+{
+	ldns_rr *cur_rr;
+	uint8_t *packet_wire;
+	size_t packet_wire_size;
+	ldns_lookup_table *rcode;
+	ldns_status status;
+
+	/* check if start() has been called */
+	if (!resolver || resolver->_socket == 0) {
+		return NULL;
+	}
+
+	if (resolver->_cur_axfr_pkt) {
+		if (resolver->_axfr_i == ldns_pkt_ancount(resolver->_cur_axfr_pkt)) {
+			ldns_pkt_free(resolver->_cur_axfr_pkt);
+			resolver->_cur_axfr_pkt = NULL;
+			return ldns_axfr_next(resolver);
+		}
+		cur_rr = ldns_rr_clone(ldns_rr_list_rr(
+					ldns_pkt_answer(resolver->_cur_axfr_pkt),
+					resolver->_axfr_i));
+		resolver->_axfr_i++;
+		if (ldns_rr_get_type(cur_rr) == LDNS_RR_TYPE_SOA) {
+			resolver->_axfr_soa_count++;
+			if (resolver->_axfr_soa_count >= 2) {
+#ifndef USE_WINSOCK
+				close(resolver->_socket);
+#else
+				closesocket(resolver->_socket);
+#endif
+				resolver->_socket = 0;
+				ldns_pkt_free(resolver->_cur_axfr_pkt);
+				resolver->_cur_axfr_pkt = NULL;
+			}
+		}
+		return cur_rr;
+	} else {
+		packet_wire = ldns_tcp_read_wire(resolver->_socket, &packet_wire_size);
+		if(!packet_wire)
+			return NULL;
+
+		status = ldns_wire2pkt(&resolver->_cur_axfr_pkt, packet_wire,
+				     packet_wire_size);
+		free(packet_wire);
+
+		resolver->_axfr_i = 0;
+		if (status != LDNS_STATUS_OK) {
+			/* TODO: make status return type of this function (...api change) */
+			fprintf(stderr, "Error parsing rr during AXFR: %s\n", ldns_get_errorstr_by_id(status));
+
+			/* RoRi: we must now also close the socket, otherwise subsequent uses of the
+			   same resolver structure will fail because the link is still open or
+			   in an undefined state */
+#ifndef USE_WINSOCK
+			close(resolver->_socket);
+#else
+			closesocket(resolver->_socket);
+#endif
+			resolver->_socket = 0;
+
+			return NULL;
+		} else if (ldns_pkt_get_rcode(resolver->_cur_axfr_pkt) != 0) {
+			rcode = ldns_lookup_by_id(ldns_rcodes, (int) ldns_pkt_get_rcode(resolver->_cur_axfr_pkt));
+			fprintf(stderr, "Error in AXFR: %s\n", rcode->name);
+
+			/* RoRi: we must now also close the socket, otherwise subsequent uses of the
+			   same resolver structure will fail because the link is still open or
+			   in an undefined state */
+#ifndef USE_WINSOCK
+			close(resolver->_socket);
+#else
+			closesocket(resolver->_socket);
+#endif
+			resolver->_socket = 0;
+
+			return NULL;
+		} else {
+			return ldns_axfr_next(resolver);
+		}
+
+	}
+
+}
+
+bool
+ldns_axfr_complete(const ldns_resolver *res)
+{
+	/* complete when soa count is 2? */
+	return res->_axfr_soa_count == 2;
+}
+
+ldns_pkt *
+ldns_axfr_last_pkt(const ldns_resolver *res)
+{
+	return res->_cur_axfr_pkt;
+}
+
+/* random isn't really that good */
+void
+ldns_resolver_nameservers_randomize(ldns_resolver *r)
+{
+	uint16_t i, j;
+	ldns_rdf **ns, *tmp;
+
+	/* should I check for ldns_resolver_random?? */
+	assert(r != NULL);
+
+	ns = ldns_resolver_nameservers(r);
+	for (i = 0; i < ldns_resolver_nameserver_count(r); i++) {
+		j = ldns_get_random() % ldns_resolver_nameserver_count(r);
+		tmp = ns[i];
+		ns[i] = ns[j];
+		ns[j] = tmp;
+	}
+	ldns_resolver_set_nameservers(r, ns);
+}
+
diff --git a/3rdParty/Ldns/src/src/rr.c b/3rdParty/Ldns/src/src/rr.c
new file mode 100644
index 0000000..b61e119
--- /dev/null
+++ b/3rdParty/Ldns/src/src/rr.c
@@ -0,0 +1,2421 @@
+/* rr.c
+ *
+ * access functions for ldns_rr -
+ * a Net::DNS like library for C
+ * LibDNS Team @ NLnet Labs
+ *
+ * (c) NLnet Labs, 2004-2006
+ * See the file LICENSE for the license
+ */
+#include <ldns/config.h>
+
+#include <ldns/ldns.h>
+
+#include <strings.h>
+#include <limits.h>
+
+#include <errno.h>
+
+#define LDNS_SYNTAX_DATALEN 16
+#define LDNS_TTL_DATALEN    21
+#define LDNS_RRLIST_INIT    8
+
+ldns_rr *
+ldns_rr_new(void)
+{
+	ldns_rr *rr;
+	rr = LDNS_MALLOC(ldns_rr);
+        if (!rr) {
+                return NULL;
+	}
+
+	ldns_rr_set_owner(rr, NULL);
+	ldns_rr_set_question(rr, false);
+	ldns_rr_set_rd_count(rr, 0);
+	rr->_rdata_fields = NULL;
+	ldns_rr_set_class(rr, LDNS_RR_CLASS_IN);
+	ldns_rr_set_ttl(rr, LDNS_DEFAULT_TTL);
+        return rr;
+}
+
+ldns_rr *
+ldns_rr_new_frm_type(ldns_rr_type t)
+{
+	ldns_rr *rr;
+	const ldns_rr_descriptor *desc;
+	size_t i;
+
+	rr = LDNS_MALLOC(ldns_rr);
+        if (!rr) {
+                return NULL;
+	}
+
+	desc = ldns_rr_descript(t);
+
+	rr->_rdata_fields = LDNS_XMALLOC(ldns_rdf *, ldns_rr_descriptor_minimum(desc));
+        if(!rr->_rdata_fields) {
+                LDNS_FREE(rr);
+                return NULL;
+        }
+	for (i = 0; i < ldns_rr_descriptor_minimum(desc); i++) {
+		rr->_rdata_fields[i] = NULL;
+	}
+
+	ldns_rr_set_owner(rr, NULL);
+	ldns_rr_set_question(rr, false);
+	/* set the count to minimum */
+	ldns_rr_set_rd_count(rr, ldns_rr_descriptor_minimum(desc));
+	ldns_rr_set_class(rr, LDNS_RR_CLASS_IN);
+	ldns_rr_set_ttl(rr, LDNS_DEFAULT_TTL);
+	ldns_rr_set_type(rr, t);
+	return rr;
+}
+
+void
+ldns_rr_free(ldns_rr *rr)
+{
+	size_t i;
+	if (rr) {
+		if (ldns_rr_owner(rr)) {
+			ldns_rdf_deep_free(ldns_rr_owner(rr));
+		}
+		for (i = 0; i < ldns_rr_rd_count(rr); i++) {
+			ldns_rdf_deep_free(ldns_rr_rdf(rr, i));
+		}
+		LDNS_FREE(rr->_rdata_fields);
+		LDNS_FREE(rr);
+	}
+}
+
+/*
+ * trailing spaces are allowed
+ * leading spaces are not allowed
+ * allow ttl to be optional
+ * class is optional too
+ * if ttl is missing, and default_ttl is 0, use DEF_TTL
+ * allow ttl to be written as 1d3h
+ * So the RR should look like. e.g.
+ * miek.nl. 3600 IN MX 10 elektron.atoom.net
+ * or
+ * miek.nl. 1h IN MX 10 elektron.atoom.net
+ * or
+ * miek.nl. IN MX 10 elektron.atoom.net
+ */
+static ldns_status
+ldns_rr_new_frm_str_internal(ldns_rr **newrr, const char *str,
+                             uint32_t default_ttl, ldns_rdf *origin,
+		             ldns_rdf **prev, bool question)
+{
+	ldns_rr *new;
+	const ldns_rr_descriptor *desc;
+	ldns_rr_type rr_type;
+	ldns_buffer *rr_buf = NULL;
+	ldns_buffer *rd_buf = NULL;
+	uint32_t ttl_val;
+	char  *owner = NULL;
+	char  *ttl = NULL;
+	ldns_rr_class clas_val;
+	char  *clas = NULL;
+	char  *type = NULL;
+	char  *rdata = NULL;
+	char  *rd = NULL;
+	char  *b64 = NULL;
+	size_t rd_strlen;
+	const char *delimiters;
+	ssize_t c;
+	ldns_rdf *owner_dname;
+        const char* endptr;
+        int was_unknown_rr_format = 0;
+	ldns_status status = LDNS_STATUS_OK;
+
+	/* used for types with unknown number of rdatas */
+	bool done;
+	bool quoted;
+
+	ldns_rdf *r = NULL;
+	uint16_t r_cnt;
+	uint16_t r_min;
+	uint16_t r_max;
+        size_t pre_data_pos;
+
+	new = ldns_rr_new();
+
+	owner = LDNS_XMALLOC(char, LDNS_MAX_DOMAINLEN + 1);
+	ttl = LDNS_XMALLOC(char, LDNS_TTL_DATALEN);
+	clas = LDNS_XMALLOC(char, LDNS_SYNTAX_DATALEN);
+	rdata = LDNS_XMALLOC(char, LDNS_MAX_PACKETLEN + 1);
+	rr_buf = LDNS_MALLOC(ldns_buffer);
+	rd_buf = LDNS_MALLOC(ldns_buffer);
+	rd = LDNS_XMALLOC(char, LDNS_MAX_RDFLEN);
+	b64 = LDNS_XMALLOC(char, LDNS_MAX_RDFLEN);
+	if (!new || !owner || !ttl || !clas || !rdata || !rr_buf || !rd_buf || !rd || !b64 ) {
+		status = LDNS_STATUS_MEM_ERR;
+		LDNS_FREE(rr_buf);
+		goto ldnserror;
+	}
+
+	ldns_buffer_new_frm_data(rr_buf, (char*)str, strlen(str));
+
+	/* split the rr in its parts -1 signals trouble */
+	if (ldns_bget_token(rr_buf, owner, "\t\n ", LDNS_MAX_DOMAINLEN) == -1) {
+		status = LDNS_STATUS_SYNTAX_ERR;
+		ldns_buffer_free(rr_buf);
+		goto ldnserror;
+	}
+
+	if (ldns_bget_token(rr_buf, ttl, "\t\n ", LDNS_TTL_DATALEN) == -1) {
+		status = LDNS_STATUS_SYNTAX_TTL_ERR;
+		ldns_buffer_free(rr_buf);
+		goto ldnserror;
+	}
+	ttl_val = (uint32_t) ldns_str2period(ttl, &endptr);
+
+	if (strlen(ttl) > 0 && !isdigit((int) ttl[0])) {
+		/* ah, it's not there or something */
+		if (default_ttl == 0) {
+			ttl_val = LDNS_DEFAULT_TTL;
+		} else {
+			ttl_val = default_ttl;
+		}
+		/* we not ASSUMING the TTL is missing and that
+		 * the rest of the RR is still there. That is
+		 * CLASS TYPE RDATA
+		 * so ttl value we read is actually the class
+		 */
+		clas_val = ldns_get_rr_class_by_name(ttl);
+		/* class can be left out too, assume IN, current
+		 * token must be type
+		 */
+		if (clas_val == 0) {
+			clas_val = LDNS_RR_CLASS_IN;
+			type = LDNS_XMALLOC(char, strlen(ttl) + 1);
+			if(!type) {
+				status = LDNS_STATUS_MEM_ERR;
+				ldns_buffer_free(rr_buf);
+				goto ldnserror;
+			}
+			strncpy(type, ttl, strlen(ttl) + 1);
+		}
+	} else {
+		if (ldns_bget_token(rr_buf, clas, "\t\n ", LDNS_SYNTAX_DATALEN) == -1) {
+			status = LDNS_STATUS_SYNTAX_CLASS_ERR;
+			ldns_buffer_free(rr_buf);
+			goto ldnserror;
+		}
+		clas_val = ldns_get_rr_class_by_name(clas);
+		/* class can be left out too, assume IN, current
+		 * token must be type
+		 */
+		if (clas_val == 0) {
+			clas_val = LDNS_RR_CLASS_IN;
+			type = LDNS_XMALLOC(char, strlen(clas) + 1);
+			if(!type) {
+				status = LDNS_STATUS_MEM_ERR;
+				ldns_buffer_free(rr_buf);
+				goto ldnserror;
+			}
+			strncpy(type, clas, strlen(clas) + 1);
+		}
+	}
+	/* the rest should still be waiting for us */
+
+	if (!type) {
+		type = LDNS_XMALLOC(char, LDNS_SYNTAX_DATALEN);
+		if(!type) {
+			status = LDNS_STATUS_MEM_ERR;
+			ldns_buffer_free(rr_buf);
+			goto ldnserror;
+		}
+		if (ldns_bget_token(rr_buf, type, "\t\n ", LDNS_SYNTAX_DATALEN) == -1) {
+			status = LDNS_STATUS_SYNTAX_TYPE_ERR;
+			ldns_buffer_free(rr_buf);
+			goto ldnserror;
+		}
+	}
+
+	if (ldns_bget_token(rr_buf, rdata, "\0", LDNS_MAX_PACKETLEN) == -1) {
+		/* apparently we are done, and it's only a question RR
+		 * so do not set status and go to ldnserror here
+		*/
+	}
+
+	ldns_buffer_new_frm_data(rd_buf, rdata, strlen(rdata));
+
+	if (strlen(owner) <= 1 && strncmp(owner, "@", 1) == 0) {
+		if (origin) {
+			ldns_rr_set_owner(new, ldns_rdf_clone(origin));
+		} else if (prev && *prev) {
+			ldns_rr_set_owner(new, ldns_rdf_clone(*prev));
+		} else {
+			/* default to root */
+			ldns_rr_set_owner(new, ldns_dname_new_frm_str("."));
+		}
+
+		/* @ also overrides prev */
+		if (prev) {
+			ldns_rdf_deep_free(*prev);
+			*prev = ldns_rdf_clone(ldns_rr_owner(new));
+			if (!*prev) {
+				status = LDNS_STATUS_MEM_ERR;
+				ldns_buffer_free(rr_buf);
+				goto ldnserror;
+			}
+		}
+	} else {
+		if (strlen(owner) == 0) {
+			/* no ownername was given, try prev, if that fails
+			 * origin, else default to root */
+			if (prev && *prev) {
+				ldns_rr_set_owner(new, ldns_rdf_clone(*prev));
+			} else if (origin) {
+				ldns_rr_set_owner(new, ldns_rdf_clone(origin));
+			} else {
+				ldns_rr_set_owner(new, ldns_dname_new_frm_str("."));
+			}
+			if(!ldns_rr_owner(new)) {
+				status = LDNS_STATUS_MEM_ERR;
+				ldns_buffer_free(rr_buf);
+				goto ldnserror;
+			}
+		} else {
+			owner_dname = ldns_dname_new_frm_str(owner);
+			if (!owner_dname) {
+				status = LDNS_STATUS_SYNTAX_ERR;
+				ldns_buffer_free(rr_buf);
+				goto ldnserror;
+			}
+
+			ldns_rr_set_owner(new, owner_dname);
+			if (!ldns_dname_str_absolute(owner) && origin) {
+				if(ldns_dname_cat(ldns_rr_owner(new),
+							origin) != LDNS_STATUS_OK) {
+					status = LDNS_STATUS_SYNTAX_ERR;
+					ldns_buffer_free(rr_buf);
+					goto ldnserror;
+				}
+			}
+			if (prev) {
+				ldns_rdf_deep_free(*prev);
+				*prev = ldns_rdf_clone(ldns_rr_owner(new));
+				if(!*prev) {
+					status = LDNS_STATUS_MEM_ERR;
+					ldns_buffer_free(rr_buf);
+					goto ldnserror;
+				}
+			}
+		}
+	}
+	LDNS_FREE(owner);
+	owner = NULL;
+
+	ldns_rr_set_question(new, question);
+
+	ldns_rr_set_ttl(new, ttl_val);
+	LDNS_FREE(ttl);
+	ttl = NULL;
+
+	ldns_rr_set_class(new, clas_val);
+	LDNS_FREE(clas);
+	clas = NULL;
+
+	rr_type = ldns_get_rr_type_by_name(type);
+	LDNS_FREE(type);
+	type = NULL;
+
+	desc = ldns_rr_descript((uint16_t)rr_type);
+	ldns_rr_set_type(new, rr_type);
+	if (desc) {
+		/* only the rdata remains */
+		r_max = ldns_rr_descriptor_maximum(desc);
+		r_min = ldns_rr_descriptor_minimum(desc);
+	} else {
+		r_min = 0;
+		r_max = 1;
+	}
+
+	/* depending on the rr_type we need to extract
+	 * the rdata differently, e.g. NSEC/NSEC3 */
+	switch(rr_type) {
+		default:
+			done = false;
+
+			for (r_cnt = 0; !done && r_cnt < r_max; r_cnt++) {
+				quoted = false;
+				/* if type = B64, the field may contain spaces */
+				if (ldns_rr_descriptor_field_type(desc,
+					    r_cnt) == LDNS_RDF_TYPE_B64 ||
+				    ldns_rr_descriptor_field_type(desc,
+					    r_cnt) == LDNS_RDF_TYPE_HEX ||
+				    ldns_rr_descriptor_field_type(desc,
+					    r_cnt) == LDNS_RDF_TYPE_LOC ||
+				    ldns_rr_descriptor_field_type(desc,
+					    r_cnt) == LDNS_RDF_TYPE_WKS ||
+				    ldns_rr_descriptor_field_type(desc,
+					    r_cnt) == LDNS_RDF_TYPE_IPSECKEY ||
+				    ldns_rr_descriptor_field_type(desc,
+					    r_cnt) == LDNS_RDF_TYPE_NSEC) {
+					delimiters = "\n\t";
+				} else {
+					delimiters = "\n\t ";
+				}
+
+				if (ldns_rr_descriptor_field_type(desc,
+							r_cnt) == LDNS_RDF_TYPE_STR &&
+							ldns_buffer_remaining(rd_buf) > 0) {
+					/* skip spaces */
+					while (*(ldns_buffer_current(rd_buf)) == ' ') {
+						ldns_buffer_skip(rd_buf, 1);
+					}
+
+					if (*(ldns_buffer_current(rd_buf)) == '\"') {
+						delimiters = "\"\0";
+						ldns_buffer_skip(rd_buf, 1);
+						quoted = true;
+					}
+				}
+
+				/* because number of fields can be variable, we can't
+				   rely on _maximum() only */
+				/* skip spaces */
+				while (ldns_buffer_position(rd_buf) < ldns_buffer_limit(rd_buf) &&
+					*(ldns_buffer_current(rd_buf)) == ' ' && !quoted
+				      ) {
+					ldns_buffer_skip(rd_buf, 1);
+				}
+
+				pre_data_pos = ldns_buffer_position(rd_buf);
+				if ((c = ldns_bget_token(rd_buf, rd, delimiters,
+							LDNS_MAX_RDFLEN)) != -1) {
+					/* hmmz, rfc3597 specifies that any type can be represented with
+					 * \# method, which can contain spaces...
+					 * it does specify size though...
+					 */
+					rd_strlen = strlen(rd);
+
+					/* unknown RR data */
+					if (strncmp(rd, "\\#", 2) == 0 && !quoted && (rd_strlen == 2 || rd[2]==' ')) {
+                                        	uint16_t hex_data_size;
+                                                char *hex_data_str;
+                                                uint16_t cur_hex_data_size;
+
+                                                was_unknown_rr_format = 1;
+                                                /* go back to before \# and skip it while setting delimiters better */
+                                                ldns_buffer_set_position(rd_buf, pre_data_pos);
+					        delimiters = "\n\t ";
+                                                (void)ldns_bget_token(rd_buf, rd, delimiters, LDNS_MAX_RDFLEN);
+                                                /* read rdata octet length */
+						c = ldns_bget_token(rd_buf, rd, delimiters, LDNS_MAX_RDFLEN);
+						if (c == -1) {
+							/* something goes very wrong here */
+                                                        LDNS_FREE(rd);
+                                                        LDNS_FREE(b64);
+                                                        ldns_buffer_free(rd_buf);
+                                                        ldns_buffer_free(rr_buf);
+                                                        LDNS_FREE(rdata);
+                                                        ldns_rr_free(new);
+							return LDNS_STATUS_SYNTAX_RDATA_ERR;
+						}
+						hex_data_size = (uint16_t) atoi(rd);
+						/* copy the hex chars into hex str (which is 2 chars per byte) */
+						hex_data_str = LDNS_XMALLOC(char, 2 * hex_data_size + 1);
+						if (!hex_data_str) {
+							/* malloc error */
+                                                        LDNS_FREE(rd);
+                                                        LDNS_FREE(b64);
+                                                        ldns_buffer_free(rd_buf);
+                                                        ldns_buffer_free(rr_buf);
+                                                        LDNS_FREE(rdata);
+                                                        ldns_rr_free(new);
+							return LDNS_STATUS_SYNTAX_RDATA_ERR;
+						}
+						cur_hex_data_size = 0;
+						while(cur_hex_data_size < 2 * hex_data_size) {
+							c = ldns_bget_token(rd_buf, rd, delimiters, LDNS_MAX_RDFLEN);
+							if (c != -1) {
+								rd_strlen = strlen(rd);
+							}
+							if (c == -1 || (size_t)cur_hex_data_size + rd_strlen > 2 * (size_t)hex_data_size) {
+								LDNS_FREE(hex_data_str);
+								LDNS_FREE(rd);
+								LDNS_FREE(b64);
+								ldns_buffer_free(rd_buf);
+								ldns_buffer_free(rr_buf);
+								LDNS_FREE(rdata);
+								ldns_rr_free(new);
+								return LDNS_STATUS_SYNTAX_RDATA_ERR;
+							}
+							strncpy(hex_data_str + cur_hex_data_size, rd, rd_strlen);
+							cur_hex_data_size += rd_strlen;
+						}
+						hex_data_str[cur_hex_data_size] = '\0';
+
+						/* correct the rdf type */
+						/* if *we* know the type, interpret it as wireformat */
+						if (desc) {
+							size_t hex_pos = 0;
+							uint8_t *hex_data = LDNS_XMALLOC(uint8_t, hex_data_size + 2);
+                                                        ldns_status s;
+                                                        if(!hex_data) {
+                                                                LDNS_FREE(hex_data_str);
+                                                                LDNS_FREE(rd);
+                                                                LDNS_FREE(b64);
+                                                                ldns_buffer_free(rd_buf);
+                                                                ldns_buffer_free(rr_buf);
+                                                                LDNS_FREE(rdata);
+                                                                ldns_rr_free(new);
+                                                                return LDNS_STATUS_MEM_ERR;
+                                                        }
+							ldns_write_uint16(hex_data, hex_data_size);
+							ldns_hexstring_to_data(hex_data + 2, hex_data_str);
+							s = ldns_wire2rdf(new, hex_data,
+							                 hex_data_size+2, &hex_pos);
+                                                        if(s != LDNS_STATUS_OK) {
+                                                                LDNS_FREE(hex_data_str);
+                                                                LDNS_FREE(rd);
+                                                                LDNS_FREE(b64);
+                                                                ldns_buffer_free(rd_buf);
+                                                                ldns_buffer_free(rr_buf);
+                                                                LDNS_FREE(rdata);
+                                                                ldns_rr_free(new);
+                                                                return s;
+                                                        }
+							LDNS_FREE(hex_data);
+						} else {
+							r = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_HEX, hex_data_str);
+                                                        if(!r) {
+                                                                LDNS_FREE(hex_data_str);
+                                                                LDNS_FREE(rd);
+                                                                LDNS_FREE(b64);
+                                                                ldns_buffer_free(rd_buf);
+                                                                ldns_buffer_free(rr_buf);
+                                                                LDNS_FREE(rdata);
+                                                                ldns_rr_free(new);
+                                                                return LDNS_STATUS_MEM_ERR;
+                                                        }
+							ldns_rdf_set_type(r, LDNS_RDF_TYPE_UNKNOWN);
+							if(!ldns_rr_push_rdf(new, r)) {
+                                                                LDNS_FREE(hex_data_str);
+                                                                LDNS_FREE(rd);
+                                                                LDNS_FREE(b64);
+                                                                ldns_buffer_free(rd_buf);
+                                                                ldns_buffer_free(rr_buf);
+                                                                LDNS_FREE(rdata);
+                                                                ldns_rr_free(new);
+                                                                return LDNS_STATUS_MEM_ERR;
+                                                        }
+						}
+						LDNS_FREE(hex_data_str);
+					} else {
+						/* Normal RR */
+						switch(ldns_rr_descriptor_field_type(desc, r_cnt)) {
+						case LDNS_RDF_TYPE_HEX:
+						case LDNS_RDF_TYPE_B64:
+							/* can have spaces, and will always be the last
+							 * record of the rrdata. Read in the rest */
+							if ((c = ldns_bget_token(rd_buf,
+												b64,
+												"\n",
+												LDNS_MAX_RDFLEN))
+							    != -1) {
+								rd = strncat(rd,
+										   b64,
+										   LDNS_MAX_RDFLEN
+										   - strlen(rd) - 1);
+							}
+							r = ldns_rdf_new_frm_str(
+									ldns_rr_descriptor_field_type(desc, r_cnt),
+									rd);
+							break;
+						case LDNS_RDF_TYPE_DNAME:
+							r = ldns_rdf_new_frm_str(
+									ldns_rr_descriptor_field_type(desc, r_cnt),
+									rd);
+
+							/* check if the origin should be used or concatenated */
+							if (r && ldns_rdf_size(r) > 1 && ldns_rdf_data(r)[0] == 1
+								&& ldns_rdf_data(r)[1] == '@') {
+								ldns_rdf_deep_free(r);
+								if (origin) {
+									r = ldns_rdf_clone(origin);
+								} else {
+								     /* if this is the SOA, use its own owner name */
+									if (rr_type == LDNS_RR_TYPE_SOA) {
+										r = ldns_rdf_clone(ldns_rr_owner(new));
+									} else {
+										r = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, ".");
+									}
+								}
+							} else if (r && rd_strlen >= 1 && !ldns_dname_str_absolute(rd) && origin) {
+								if (ldns_dname_cat(r, origin) != LDNS_STATUS_OK) {
+							                LDNS_FREE(rd);
+							                LDNS_FREE(b64);
+							                ldns_buffer_free(rd_buf);
+							                ldns_buffer_free(rr_buf);
+							                LDNS_FREE(rdata);
+							                ldns_rr_free(new);
+									return LDNS_STATUS_ERR;
+								}
+							}
+							break;
+						default:
+							r = ldns_rdf_new_frm_str(
+									ldns_rr_descriptor_field_type(desc, r_cnt),
+									rd);
+							break;
+						}
+						if (r) {
+							ldns_rr_push_rdf(new, r);
+						} else {
+							LDNS_FREE(rd);
+							LDNS_FREE(b64);
+							ldns_buffer_free(rd_buf);
+							ldns_buffer_free(rr_buf);
+							LDNS_FREE(rdata);
+							ldns_rr_free(new);
+							return LDNS_STATUS_SYNTAX_RDATA_ERR;
+						}
+					}
+					if (quoted) {
+						if (ldns_buffer_available(rd_buf, 1)) {
+							ldns_buffer_skip(rd_buf, 1);
+						} else {
+							done = true;
+						}
+					}
+				} else {
+					done = true;
+				}
+			}
+	}
+	LDNS_FREE(rd);
+	LDNS_FREE(b64);
+	ldns_buffer_free(rd_buf);
+	ldns_buffer_free(rr_buf);
+	LDNS_FREE(rdata);
+
+	if (!question && desc && !was_unknown_rr_format && ldns_rr_rd_count(new) < r_min) {
+		ldns_rr_free(new);
+		return LDNS_STATUS_SYNTAX_MISSING_VALUE_ERR;
+	}
+
+	if (newrr) {
+		*newrr = new;
+	}
+	return LDNS_STATUS_OK;
+
+ldnserror:
+	LDNS_FREE(type);
+	LDNS_FREE(owner);
+	LDNS_FREE(ttl);
+	LDNS_FREE(clas);
+	LDNS_FREE(rdata);
+	LDNS_FREE(rd);
+	LDNS_FREE(rd_buf);
+	LDNS_FREE(b64);
+	ldns_rr_free(new);
+    return status;
+}
+
+ldns_status
+ldns_rr_new_frm_str(ldns_rr **newrr, const char *str,
+                    uint32_t default_ttl, ldns_rdf *origin,
+		    ldns_rdf **prev)
+{
+	return ldns_rr_new_frm_str_internal(newrr,
+	                                    str,
+	                                    default_ttl,
+	                                    origin,
+	                                    prev,
+	                                    false);
+}
+
+ldns_status
+ldns_rr_new_question_frm_str(ldns_rr **newrr, const char *str,
+                             ldns_rdf *origin, ldns_rdf **prev)
+{
+	return ldns_rr_new_frm_str_internal(newrr,
+	                                    str,
+	                                    0,
+	                                    origin,
+	                                    prev,
+	                                    true);
+}
+
+ldns_status
+ldns_rr_new_frm_fp(ldns_rr **newrr, FILE *fp, uint32_t *ttl, ldns_rdf **origin, ldns_rdf **prev)
+{
+	return ldns_rr_new_frm_fp_l(newrr, fp, ttl, origin, prev, NULL);
+}
+
+ldns_status
+ldns_rr_new_frm_fp_l(ldns_rr **newrr, FILE *fp, uint32_t *default_ttl, ldns_rdf **origin, ldns_rdf **prev, int *line_nr)
+{
+	char *line;
+	const char *endptr;  /* unused */
+	ldns_rr *rr;
+	uint32_t ttl;
+	ldns_rdf *tmp;
+	ldns_status s;
+	ssize_t size;
+	int offset = 0;
+
+	if (default_ttl) {
+		ttl = *default_ttl;
+	} else {
+		ttl = 0;
+	}
+
+	line = LDNS_XMALLOC(char, LDNS_MAX_LINELEN + 1);
+	if (!line) {
+		return LDNS_STATUS_MEM_ERR;
+	}
+
+	/* read an entire line in from the file */
+	if ((size = ldns_fget_token_l(fp, line, LDNS_PARSE_SKIP_SPACE, LDNS_MAX_LINELEN, line_nr)) == -1) {
+		LDNS_FREE(line);
+		/* if last line was empty, we are now at feof, which is not
+		 * always a parse error (happens when for instance last line
+		 * was a comment)
+		 */
+		return LDNS_STATUS_SYNTAX_ERR;
+	}
+
+	/* we can have the situation, where we've read ok, but still got
+	 * no bytes to play with, in this case size is 0
+	 */
+	if (size == 0) {
+		LDNS_FREE(line);
+		return LDNS_STATUS_SYNTAX_EMPTY;
+	}
+
+	if (strncmp(line, "$ORIGIN", 7) == 0 && isspace(line[7])) {
+		if (*origin) {
+			ldns_rdf_deep_free(*origin);
+			*origin = NULL;
+		}
+		offset = 8;
+		while (isspace(line[offset])) {
+			offset++;
+		}
+		tmp = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, line + offset);
+		if (!tmp) {
+			/* could not parse what next to $ORIGIN */
+			LDNS_FREE(line);
+			return LDNS_STATUS_SYNTAX_DNAME_ERR;
+		}
+		*origin = tmp;
+		s = LDNS_STATUS_SYNTAX_ORIGIN;
+	} else if (strncmp(line, "$TTL", 4) == 0 && isspace(line[4])) {
+		offset = 5;
+		while (isspace(line[offset])) {
+			offset++;
+		}
+		if (default_ttl) {
+			*default_ttl = ldns_str2period(line + offset, &endptr);
+		}
+		s = LDNS_STATUS_SYNTAX_TTL;
+	} else if (strncmp(line, "$INCLUDE", 8) == 0) {
+		s = LDNS_STATUS_SYNTAX_INCLUDE;
+	} else {
+		if (origin && *origin) {
+			s = ldns_rr_new_frm_str(&rr, (const char*) line, ttl, *origin, prev);
+		} else {
+			s = ldns_rr_new_frm_str(&rr, (const char*) line, ttl, NULL, prev);
+		}
+	}
+	LDNS_FREE(line);
+	if (newrr && s == LDNS_STATUS_OK) {
+		*newrr = rr;
+	}
+	return s;
+}
+
+void
+ldns_rr_set_owner(ldns_rr *rr, ldns_rdf *owner)
+{
+	rr->_owner = owner;
+}
+
+void
+ldns_rr_set_question(ldns_rr *rr, bool question)
+{
+   rr->_rr_question = question;
+}
+
+void
+ldns_rr_set_ttl(ldns_rr *rr, uint32_t ttl)
+{
+	rr->_ttl = ttl;
+}
+
+void
+ldns_rr_set_rd_count(ldns_rr *rr, size_t count)
+{
+	rr->_rd_count = count;
+}
+
+void
+ldns_rr_set_type(ldns_rr *rr, ldns_rr_type rr_type)
+{
+	rr->_rr_type = rr_type;
+}
+
+void
+ldns_rr_set_class(ldns_rr *rr, ldns_rr_class rr_class)
+{
+	rr->_rr_class = rr_class;
+}
+
+ldns_rdf *
+ldns_rr_set_rdf(ldns_rr *rr, const ldns_rdf *f, size_t position)
+{
+	size_t rd_count;
+	ldns_rdf *pop;
+
+	rd_count = ldns_rr_rd_count(rr);
+	if (position < rd_count) {
+		/* dicard the old one */
+		pop = rr->_rdata_fields[position];
+		rr->_rdata_fields[position] = (ldns_rdf*)f;
+		return pop;
+	} else {
+		return NULL;
+	}
+}
+
+bool
+ldns_rr_push_rdf(ldns_rr *rr, const ldns_rdf *f)
+{
+	size_t rd_count;
+	ldns_rdf **rdata_fields;
+
+	rd_count = ldns_rr_rd_count(rr);
+
+	/* grow the array */
+	rdata_fields = LDNS_XREALLOC(
+		rr->_rdata_fields, ldns_rdf *, rd_count + 1);
+	if (!rdata_fields) {
+		return false;
+	}
+
+	/* add the new member */
+	rr->_rdata_fields = rdata_fields;
+	rr->_rdata_fields[rd_count] = (ldns_rdf*)f;
+
+	ldns_rr_set_rd_count(rr, rd_count + 1);
+	return true;
+}
+
+ldns_rdf *
+ldns_rr_pop_rdf(ldns_rr *rr)
+{
+	size_t rd_count;
+	ldns_rdf *pop;
+	ldns_rdf** newrd;
+
+	rd_count = ldns_rr_rd_count(rr);
+
+	if (rd_count == 0) {
+		return NULL;
+	}
+
+	pop = rr->_rdata_fields[rd_count - 1];
+
+	/* try to shrink the array */
+	if(rd_count > 1) {
+		newrd = LDNS_XREALLOC(
+			rr->_rdata_fields, ldns_rdf *, rd_count - 1);
+		if(newrd)
+			rr->_rdata_fields = newrd;
+	} else {
+		LDNS_FREE(rr->_rdata_fields);
+	}
+
+	ldns_rr_set_rd_count(rr, rd_count - 1);
+	return pop;
+}
+
+ldns_rdf *
+ldns_rr_rdf(const ldns_rr *rr, size_t nr)
+{
+	if (rr && nr < ldns_rr_rd_count(rr)) {
+		return rr->_rdata_fields[nr];
+	} else {
+		return NULL;
+	}
+}
+
+ldns_rdf *
+ldns_rr_owner(const ldns_rr *rr)
+{
+	return rr->_owner;
+}
+
+bool
+ldns_rr_is_question(const ldns_rr *rr)
+{
+   return rr->_rr_question;
+}
+
+uint32_t
+ldns_rr_ttl(const ldns_rr *rr)
+{
+	return rr->_ttl;
+}
+
+size_t
+ldns_rr_rd_count(const ldns_rr *rr)
+{
+	return rr->_rd_count;
+}
+
+ldns_rr_type
+ldns_rr_get_type(const ldns_rr *rr)
+{
+        return rr->_rr_type;
+}
+
+ldns_rr_class
+ldns_rr_get_class(const ldns_rr *rr)
+{
+        return rr->_rr_class;
+}
+
+/* rr_lists */
+
+size_t
+ldns_rr_list_rr_count(const ldns_rr_list *rr_list)
+{
+	if (rr_list) {
+		return rr_list->_rr_count;
+	} else {
+		return 0;
+	}
+}
+
+ldns_rr *
+ldns_rr_list_set_rr(ldns_rr_list *rr_list, const ldns_rr *r, size_t count)
+{
+	ldns_rr *old;
+
+	if (count > ldns_rr_list_rr_count(rr_list)) {
+		return NULL;
+	}
+
+	old = ldns_rr_list_rr(rr_list, count);
+
+	/* overwrite old's pointer */
+	rr_list->_rrs[count] = (ldns_rr*)r;
+	return old;
+}
+
+void
+ldns_rr_list_set_rr_count(ldns_rr_list *rr_list, size_t count)
+{
+	assert(count <= rr_list->_rr_capacity);
+	rr_list->_rr_count = count;
+}
+
+ldns_rr *
+ldns_rr_list_rr(const ldns_rr_list *rr_list, size_t nr)
+{
+	if (nr < ldns_rr_list_rr_count(rr_list)) {
+		return rr_list->_rrs[nr];
+	} else {
+		return NULL;
+	}
+}
+
+ldns_rr_list *
+ldns_rr_list_new()
+{
+	ldns_rr_list *rr_list = LDNS_MALLOC(ldns_rr_list);
+        if(!rr_list) return NULL;
+	rr_list->_rr_count = 0;
+	rr_list->_rr_capacity = 0;
+	rr_list->_rrs = NULL;
+	return rr_list;
+}
+
+void
+ldns_rr_list_free(ldns_rr_list *rr_list)
+{
+	if (rr_list) {
+		LDNS_FREE(rr_list->_rrs);
+		LDNS_FREE(rr_list);
+	}
+}
+
+void
+ldns_rr_list_deep_free(ldns_rr_list *rr_list)
+{
+	size_t i;
+
+	if (rr_list) {
+		for (i=0; i < ldns_rr_list_rr_count(rr_list); i++) {
+			ldns_rr_free(ldns_rr_list_rr(rr_list, i));
+		}
+		LDNS_FREE(rr_list->_rrs);
+		LDNS_FREE(rr_list);
+	}
+}
+
+
+/* add right to left. So we modify *left! */
+bool
+ldns_rr_list_cat(ldns_rr_list *left, ldns_rr_list *right)
+{
+	size_t r_rr_count;
+	size_t i;
+
+	if (!left) {
+		return false;
+	}
+
+	if (right) {
+		r_rr_count = ldns_rr_list_rr_count(right);
+	} else {
+		r_rr_count = 0;
+	}
+
+	/* push right to left */
+	for(i = 0; i < r_rr_count; i++) {
+		ldns_rr_list_push_rr(left, ldns_rr_list_rr(right, i));
+	}
+	return true;
+}
+
+ldns_rr_list *
+ldns_rr_list_cat_clone(ldns_rr_list *left, ldns_rr_list *right)
+{
+	size_t l_rr_count;
+	size_t r_rr_count;
+	size_t i;
+	ldns_rr_list *cat;
+
+	if (left) {
+		l_rr_count = ldns_rr_list_rr_count(left);
+	} else {
+		return ldns_rr_list_clone(right);
+	}
+
+	if (right) {
+		r_rr_count = ldns_rr_list_rr_count(right);
+	} else {
+		r_rr_count = 0;
+	}
+
+	cat = ldns_rr_list_new();
+
+	if (!cat) {
+		return NULL;
+	}
+
+	/* left */
+	for(i = 0; i < l_rr_count; i++) {
+		ldns_rr_list_push_rr(cat,
+				ldns_rr_clone(ldns_rr_list_rr(left, i)));
+	}
+	/* right */
+	for(i = 0; i < r_rr_count; i++) {
+		ldns_rr_list_push_rr(cat,
+				ldns_rr_clone(ldns_rr_list_rr(right, i)));
+	}
+	return cat;
+}
+
+ldns_rr_list *
+ldns_rr_list_subtype_by_rdf(ldns_rr_list *l, ldns_rdf *r, size_t pos)
+{
+	size_t i;
+	ldns_rr_list *subtyped;
+	ldns_rdf *list_rdf;
+
+	subtyped = ldns_rr_list_new();
+
+	for(i = 0; i < ldns_rr_list_rr_count(l); i++) {
+		list_rdf = ldns_rr_rdf(
+			ldns_rr_list_rr(l, i),
+			pos);
+		if (!list_rdf) {
+			/* pos is too large or any other error */
+			ldns_rr_list_deep_free(subtyped);
+			return NULL;
+		}
+
+		if (ldns_rdf_compare(list_rdf, r) == 0) {
+			/* a match */
+			ldns_rr_list_push_rr(subtyped,
+					ldns_rr_clone(ldns_rr_list_rr(l, i)));
+		}
+	}
+
+	if (ldns_rr_list_rr_count(subtyped) > 0) {
+		return subtyped;
+	} else {
+		ldns_rr_list_free(subtyped);
+		return NULL;
+	}
+}
+
+bool
+ldns_rr_list_push_rr(ldns_rr_list *rr_list, const ldns_rr *rr)
+{
+	size_t rr_count;
+	size_t cap;
+
+	rr_count = ldns_rr_list_rr_count(rr_list);
+	cap = rr_list->_rr_capacity;
+
+	/* grow the array */
+	if(rr_count+1 > cap) {
+		ldns_rr **rrs;
+
+		if(cap == 0)
+			cap = LDNS_RRLIST_INIT;  /* initial list size */
+		else	cap *= 2;
+		rrs = LDNS_XREALLOC(rr_list->_rrs, ldns_rr *, cap);
+		if (!rrs) {
+			return false;
+		}
+		rr_list->_rrs = rrs;
+		rr_list->_rr_capacity = cap;
+	}
+
+	/* add the new member */
+	rr_list->_rrs[rr_count] = (ldns_rr*)rr;
+
+	ldns_rr_list_set_rr_count(rr_list, rr_count + 1);
+	return true;
+}
+
+bool
+ldns_rr_list_push_rr_list(ldns_rr_list *rr_list, const ldns_rr_list *push_list)
+{
+	size_t i;
+
+	for(i = 0; i < ldns_rr_list_rr_count(push_list); i++) {
+		if (!ldns_rr_list_push_rr(rr_list,
+				ldns_rr_list_rr(push_list, i))) {
+			return false;
+		}
+	}
+	return true;
+}
+
+ldns_rr *
+ldns_rr_list_pop_rr(ldns_rr_list *rr_list)
+{
+	size_t rr_count;
+	size_t cap;
+	ldns_rr *pop;
+
+	rr_count = ldns_rr_list_rr_count(rr_list);
+
+	if (rr_count == 0) {
+		return NULL;
+	}
+
+	cap = rr_list->_rr_capacity;
+	pop = ldns_rr_list_rr(rr_list, rr_count - 1);
+
+	/* shrink the array */
+	if(cap > LDNS_RRLIST_INIT && rr_count-1 <= cap/2) {
+                ldns_rr** a;
+		cap /= 2;
+                a = LDNS_XREALLOC(rr_list->_rrs, ldns_rr *, cap);
+                if(a) {
+		        rr_list->_rrs = a;
+		        rr_list->_rr_capacity = cap;
+                }
+	}
+
+	ldns_rr_list_set_rr_count(rr_list, rr_count - 1);
+
+	return pop;
+}
+
+ldns_rr_list *
+ldns_rr_list_pop_rr_list(ldns_rr_list *rr_list, size_t howmany)
+{
+	/* pop a number of rr's and put them in a rr_list */
+	ldns_rr_list *popped;
+	ldns_rr *p;
+	size_t i = howmany;
+
+	popped = ldns_rr_list_new();
+
+	if (!popped) {
+		return NULL;
+	}
+
+
+	while(i > 0 &&
+			(p = ldns_rr_list_pop_rr(rr_list)) != NULL) {
+		ldns_rr_list_push_rr(popped, p);
+		i--;
+	}
+
+	if (i == howmany) {
+		return NULL;
+	} else {
+		return popped;
+	}
+}
+
+
+bool
+ldns_rr_list_contains_rr(const ldns_rr_list *rr_list, ldns_rr *rr)
+{
+	size_t i;
+
+	if (!rr_list || !rr || ldns_rr_list_rr_count(rr_list) == 0) {
+		return false;
+	}
+
+	for (i = 0; i < ldns_rr_list_rr_count(rr_list); i++) {
+		if (rr == ldns_rr_list_rr(rr_list, i)) {
+			return true;
+		} else if (ldns_rr_compare(rr, ldns_rr_list_rr(rr_list, i)) == 0) {
+			return true;
+		}
+	}
+	return false;
+}
+
+bool
+ldns_is_rrset(ldns_rr_list *rr_list)
+{
+	ldns_rr_type t;
+	ldns_rr_class c;
+	ldns_rdf *o;
+	ldns_rr *tmp;
+	size_t i;
+
+	if (!rr_list || ldns_rr_list_rr_count(rr_list) == 0) {
+		return false;
+	}
+
+	tmp = ldns_rr_list_rr(rr_list, 0);
+
+	t = ldns_rr_get_type(tmp);
+	c = ldns_rr_get_class(tmp);
+	o = ldns_rr_owner(tmp);
+
+	/* compare these with the rest of the rr_list, start with 1 */
+	for (i = 1; i < ldns_rr_list_rr_count(rr_list); i++) {
+		tmp = ldns_rr_list_rr(rr_list, i);
+		if (t != ldns_rr_get_type(tmp)) {
+			return false;
+		}
+		if (c != ldns_rr_get_class(tmp)) {
+			return false;
+		}
+		if (ldns_rdf_compare(o, ldns_rr_owner(tmp)) != 0) {
+			return false;
+		}
+	}
+	return true;
+}
+
+bool
+ldns_rr_set_push_rr(ldns_rr_list *rr_list, ldns_rr *rr)
+{
+	size_t rr_count;
+	size_t i;
+	ldns_rr *last;
+
+	assert(rr != NULL);
+
+	rr_count = ldns_rr_list_rr_count(rr_list);
+
+	if (rr_count == 0) {
+		/* nothing there, so checking it is
+		 * not needed */
+		return ldns_rr_list_push_rr(rr_list, rr);
+	} else {
+		/* check with the final rr in the rr_list */
+		last = ldns_rr_list_rr(rr_list, rr_count - 1);
+
+		if (ldns_rr_get_class(last) != ldns_rr_get_class(rr)) {
+			return false;
+		}
+		if (ldns_rr_get_type(last) != ldns_rr_get_type(rr)) {
+			return false;
+		}
+		/* only check if not equal to RRSIG */
+		if (ldns_rr_get_type(rr) != LDNS_RR_TYPE_RRSIG) {
+			if (ldns_rr_ttl(last) != ldns_rr_ttl(rr)) {
+				return false;
+			}
+		}
+		if (ldns_rdf_compare(ldns_rr_owner(last),
+					ldns_rr_owner(rr)) != 0) {
+			return false;
+		}
+		/* ok, still alive - check if the rr already
+		 * exists - if so, dont' add it */
+		for(i = 0; i < rr_count; i++) {
+			if(ldns_rr_compare(
+					ldns_rr_list_rr(rr_list, i), rr) == 0) {
+				return false;
+			}
+		}
+		/* it's safe, push it */
+		return ldns_rr_list_push_rr(rr_list, rr);
+	}
+}
+
+ldns_rr *
+ldns_rr_set_pop_rr(ldns_rr_list *rr_list)
+{
+	return ldns_rr_list_pop_rr(rr_list);
+}
+
+ldns_rr_list *
+ldns_rr_list_pop_rrset(ldns_rr_list *rr_list)
+{
+	ldns_rr_list *rrset;
+	ldns_rr *last_rr = NULL;
+	ldns_rr *next_rr;
+
+	if (!rr_list) {
+		return NULL;
+	}
+
+	rrset = ldns_rr_list_new();
+	if (!last_rr) {
+		last_rr = ldns_rr_list_pop_rr(rr_list);
+		if (!last_rr) {
+			ldns_rr_list_free(rrset);
+			return NULL;
+		} else {
+			ldns_rr_list_push_rr(rrset, last_rr);
+		}
+	}
+
+	if (ldns_rr_list_rr_count(rr_list) > 0) {
+		next_rr = ldns_rr_list_rr(rr_list, ldns_rr_list_rr_count(rr_list) - 1);
+	} else {
+		next_rr = NULL;
+	}
+
+	while (next_rr) {
+		if (
+			ldns_rdf_compare(ldns_rr_owner(next_rr),
+					 ldns_rr_owner(last_rr)) == 0
+			&&
+			ldns_rr_get_type(next_rr) == ldns_rr_get_type(last_rr)
+			&&
+			ldns_rr_get_class(next_rr) == ldns_rr_get_class(last_rr)
+		   ) {
+			ldns_rr_list_push_rr(rrset, ldns_rr_list_pop_rr(rr_list));
+			if (ldns_rr_list_rr_count(rr_list) > 0) {
+				last_rr = next_rr;
+				next_rr = ldns_rr_list_rr(rr_list, ldns_rr_list_rr_count(rr_list) - 1);
+			} else {
+				next_rr = NULL;
+			}
+		} else {
+			next_rr = NULL;
+		}
+	}
+
+	return rrset;
+}
+
+ldns_rr *
+ldns_rr_clone(const ldns_rr *rr)
+{
+	size_t i;
+	ldns_rr *new_rr;
+
+	if (!rr) {
+		return NULL;
+	}
+
+	new_rr = ldns_rr_new();
+	if (!new_rr) {
+		return NULL;
+	}
+	if (ldns_rr_owner(rr)) {
+		ldns_rr_set_owner(new_rr, ldns_rdf_clone(ldns_rr_owner(rr)));
+  	}
+	ldns_rr_set_ttl(new_rr, ldns_rr_ttl(rr));
+	ldns_rr_set_type(new_rr, ldns_rr_get_type(rr));
+	ldns_rr_set_class(new_rr, ldns_rr_get_class(rr));
+	ldns_rr_set_question(new_rr, ldns_rr_is_question(rr));
+
+	for (i = 0; i < ldns_rr_rd_count(rr); i++) {
+        	if (ldns_rr_rdf(rr,i)) {
+        		ldns_rr_push_rdf(new_rr, ldns_rdf_clone(ldns_rr_rdf(rr, i)));
+                }
+	}
+
+	return new_rr;
+}
+
+ldns_rr_list *
+ldns_rr_list_clone(const ldns_rr_list *rrlist)
+{
+	size_t i;
+	ldns_rr_list *new_list;
+	ldns_rr *r;
+
+	if (!rrlist) {
+		return NULL;
+	}
+
+	new_list = ldns_rr_list_new();
+	if (!new_list) {
+		return NULL;
+	}
+	for (i = 0; i < ldns_rr_list_rr_count(rrlist); i++) {
+		r = ldns_rr_clone(
+			ldns_rr_list_rr(rrlist, i)
+		    );
+		if (!r) {
+			/* huh, failure in cloning */
+			ldns_rr_list_deep_free(new_list);
+			return NULL;
+		}
+		ldns_rr_list_push_rr(new_list, r);
+	}
+	return new_list;
+}
+
+
+int
+qsort_rr_compare(const void *a, const void *b)
+{
+	const ldns_rr *rr1 = * (const ldns_rr **) a;
+	const ldns_rr *rr2 = * (const ldns_rr **) b;
+
+	if (rr1 == NULL && rr2 == NULL) {
+		return 0;
+	}
+	if (rr1 == NULL) {
+		return -1;
+	}
+	if (rr2 == NULL) {
+		return 1;
+	}
+	return ldns_rr_compare(rr1, rr2);
+}
+
+int
+qsort_schwartz_rr_compare(const void *a, const void *b)
+{
+	int result = 0;
+	ldns_rr *rr1, *rr2;
+	ldns_buffer *rr1_buf, *rr2_buf;
+	struct ldns_schwartzian_compare_struct *sa = *(struct ldns_schwartzian_compare_struct **) a;
+	struct ldns_schwartzian_compare_struct *sb = *(struct ldns_schwartzian_compare_struct **) b;
+	/* if we are doing 2wire, we need to do lowercasing on the dname (and maybe on the rdata)
+	 * this must be done for comparison only, so we need to have a temp var for both buffers,
+	 * which is only used when the transformed object value isn't there yet
+	 */
+	ldns_rr *canonical_a, *canonical_b;
+
+	rr1 = (ldns_rr *) sa->original_object;
+	rr2 = (ldns_rr *) sb->original_object;
+
+	result = ldns_rr_compare_no_rdata(rr1, rr2);
+
+	if (result == 0) {
+		if (!sa->transformed_object) {
+			canonical_a = ldns_rr_clone(sa->original_object);
+			ldns_rr2canonical(canonical_a);
+			sa->transformed_object = ldns_buffer_new(ldns_rr_uncompressed_size(canonical_a));
+			if (ldns_rr2buffer_wire(sa->transformed_object, canonical_a, LDNS_SECTION_ANY) != LDNS_STATUS_OK) {
+		                ldns_buffer_free((ldns_buffer *)sa->transformed_object);
+                                sa->transformed_object = NULL;
+				ldns_rr_free(canonical_a);
+				return 0;
+			}
+			ldns_rr_free(canonical_a);
+		}
+		if (!sb->transformed_object) {
+			canonical_b = ldns_rr_clone(sb->original_object);
+			ldns_rr2canonical(canonical_b);
+			sb->transformed_object = ldns_buffer_new(ldns_rr_uncompressed_size(canonical_b));
+			if (ldns_rr2buffer_wire(sb->transformed_object, canonical_b, LDNS_SECTION_ANY) != LDNS_STATUS_OK) {
+		                ldns_buffer_free((ldns_buffer *)sa->transformed_object);
+		                ldns_buffer_free((ldns_buffer *)sb->transformed_object);
+                                sa->transformed_object = NULL;
+                                sb->transformed_object = NULL;
+				ldns_rr_free(canonical_b);
+				return 0;
+			}
+			ldns_rr_free(canonical_b);
+		}
+		rr1_buf = (ldns_buffer *) sa->transformed_object;
+		rr2_buf = (ldns_buffer *) sb->transformed_object;
+
+		result = ldns_rr_compare_wire(rr1_buf, rr2_buf);
+	}
+
+	return result;
+}
+
+void
+ldns_rr_list_sort(ldns_rr_list *unsorted)
+{
+	struct ldns_schwartzian_compare_struct **sortables;
+	size_t item_count;
+	size_t i;
+
+	if (unsorted) {
+		item_count = ldns_rr_list_rr_count(unsorted);
+
+		sortables = LDNS_XMALLOC(struct ldns_schwartzian_compare_struct *,
+					 item_count);
+                if(!sortables) return; /* no way to return error */
+		for (i = 0; i < item_count; i++) {
+			sortables[i] = LDNS_XMALLOC(struct ldns_schwartzian_compare_struct, 1);
+                        if(!sortables[i]) {
+                                /* free the allocated parts */
+                                while(i>0) {
+                                        i--;
+                                        LDNS_FREE(sortables[i]);
+                                }
+                                /* no way to return error */
+                                return;
+                        }
+			sortables[i]->original_object = ldns_rr_list_rr(unsorted, i);
+			sortables[i]->transformed_object = NULL;
+		}
+		qsort(sortables,
+		      item_count,
+		      sizeof(struct ldns_schwartzian_compare_struct *),
+		      qsort_schwartz_rr_compare);
+		for (i = 0; i < item_count; i++) {
+			unsorted->_rrs[i] = sortables[i]->original_object;
+			if (sortables[i]->transformed_object) {
+				ldns_buffer_free(sortables[i]->transformed_object);
+			}
+			LDNS_FREE(sortables[i]);
+		}
+		LDNS_FREE(sortables);
+	}
+}
+
+int
+ldns_rr_compare_no_rdata(const ldns_rr *rr1, const ldns_rr *rr2)
+{
+	size_t rr1_len;
+	size_t rr2_len;
+        size_t offset;
+
+	assert(rr1 != NULL);
+	assert(rr2 != NULL);
+
+	rr1_len = ldns_rr_uncompressed_size(rr1);
+	rr2_len = ldns_rr_uncompressed_size(rr2);
+
+	if (ldns_dname_compare(ldns_rr_owner(rr1), ldns_rr_owner(rr2)) < 0) {
+		return -1;
+	} else if (ldns_dname_compare(ldns_rr_owner(rr1), ldns_rr_owner(rr2)) > 0) {
+		return 1;
+	}
+
+        /* should return -1 if rr1 comes before rr2, so need to do rr1 - rr2, not rr2 - rr1 */
+        if (ldns_rr_get_class(rr1) != ldns_rr_get_class(rr2)) {
+            return ldns_rr_get_class(rr1) - ldns_rr_get_class(rr2);
+        }
+
+        /* should return -1 if rr1 comes before rr2, so need to do rr1 - rr2, not rr2 - rr1 */
+        if (ldns_rr_get_type(rr1) != ldns_rr_get_type(rr2)) {
+            return ldns_rr_get_type(rr1) - ldns_rr_get_type(rr2);
+        }
+
+        /* offset is the owername length + ttl + type + class + rdlen == start of wire format rdata */
+        offset = ldns_rdf_size(ldns_rr_owner(rr1)) + 4 + 2 + 2 + 2;
+        /* if either record doesn't have any RDATA... */
+        if (offset > rr1_len || offset > rr2_len) {
+            if (rr1_len == rr2_len) {
+              return 0;
+            }
+            return ((int) rr2_len - (int) rr1_len);
+        }
+
+	return 0;
+}
+
+int ldns_rr_compare_wire(ldns_buffer *rr1_buf, ldns_buffer *rr2_buf)
+{
+        size_t rr1_len, rr2_len, min_len, i, offset;
+
+        rr1_len = ldns_buffer_capacity(rr1_buf);
+        rr2_len = ldns_buffer_capacity(rr2_buf);
+
+        /* jump past dname (checked in earlier part)
+         * and especially past TTL */
+        offset = 0;
+        while (offset < rr1_len && *ldns_buffer_at(rr1_buf, offset) != 0) {
+          offset += *ldns_buffer_at(rr1_buf, offset) + 1;
+        }
+        /* jump to rdata section (PAST the rdata length field, otherwise
+           rrs with different lengths might be sorted erroneously */
+        offset += 11;
+	   min_len = (rr1_len < rr2_len) ? rr1_len : rr2_len;
+        /* Compare RRs RDATA byte for byte. */
+        for(i = offset; i < min_len; i++) {
+                if (*ldns_buffer_at(rr1_buf,i) < *ldns_buffer_at(rr2_buf,i)) {
+                        return -1;
+                } else if (*ldns_buffer_at(rr1_buf,i) > *ldns_buffer_at(rr2_buf,i)) {
+                        return +1;
+                }
+        }
+
+        /* If both RDATAs are the same up to min_len, then the shorter one sorts first. */
+        if (rr1_len < rr2_len) {
+                return -1;
+        } else if (rr1_len > rr2_len) {
+                return +1;
+	}
+        /* The RDATAs are equal. */
+        return 0;
+
+}
+
+int
+ldns_rr_compare(const ldns_rr *rr1, const ldns_rr *rr2)
+{
+	int result;
+	size_t rr1_len, rr2_len;
+
+	ldns_buffer *rr1_buf;
+	ldns_buffer *rr2_buf;
+
+	result = ldns_rr_compare_no_rdata(rr1, rr2);
+	if (result == 0) {
+		rr1_len = ldns_rr_uncompressed_size(rr1);
+		rr2_len = ldns_rr_uncompressed_size(rr2);
+
+		rr1_buf = ldns_buffer_new(rr1_len);
+		rr2_buf = ldns_buffer_new(rr2_len);
+
+		if (ldns_rr2buffer_wire_canonical(rr1_buf,
+								    rr1,
+								    LDNS_SECTION_ANY)
+		    != LDNS_STATUS_OK) {
+			ldns_buffer_free(rr1_buf);
+			ldns_buffer_free(rr2_buf);
+			return 0;
+		}
+		if (ldns_rr2buffer_wire_canonical(rr2_buf,
+								    rr2,
+								    LDNS_SECTION_ANY)
+		    != LDNS_STATUS_OK) {
+			ldns_buffer_free(rr1_buf);
+			ldns_buffer_free(rr2_buf);
+			return 0;
+		}
+
+		result = ldns_rr_compare_wire(rr1_buf, rr2_buf);
+
+		ldns_buffer_free(rr1_buf);
+		ldns_buffer_free(rr2_buf);
+	}
+
+	return result;
+}
+
+/* convert dnskey to a ds with the given algorithm,
+ * then compare the result with the given ds */
+static int
+ldns_rr_compare_ds_dnskey(ldns_rr *ds,
+                          ldns_rr *dnskey)
+{
+	ldns_rr *ds_gen;
+	bool result = false;
+	ldns_hash algo;
+
+	if (!dnskey || !ds ||
+	    ldns_rr_get_type(ds) != LDNS_RR_TYPE_DS ||
+	    ldns_rr_get_type(dnskey) != LDNS_RR_TYPE_DNSKEY) {
+		return false;
+	}
+
+	if (ldns_rr_rdf(ds, 2) == NULL) {
+		return false;
+	}
+	algo = ldns_rdf2native_int8(ldns_rr_rdf(ds, 2));
+
+	ds_gen = ldns_key_rr2ds(dnskey, algo);
+	if (ds_gen) {
+		result = ldns_rr_compare(ds, ds_gen) == 0;
+		ldns_rr_free(ds_gen);
+	}
+	return result;
+}
+
+bool
+ldns_rr_compare_ds(const ldns_rr *orr1, const ldns_rr *orr2)
+{
+	bool result;
+	ldns_rr *rr1 = ldns_rr_clone(orr1);
+	ldns_rr *rr2 = ldns_rr_clone(orr2);
+
+	/* set ttls to zero */
+	ldns_rr_set_ttl(rr1, 0);
+	ldns_rr_set_ttl(rr2, 0);
+
+	if (ldns_rr_get_type(rr1) == LDNS_RR_TYPE_DS &&
+	    ldns_rr_get_type(rr2) == LDNS_RR_TYPE_DNSKEY) {
+		result = ldns_rr_compare_ds_dnskey(rr1, rr2);
+	} else if (ldns_rr_get_type(rr1) == LDNS_RR_TYPE_DNSKEY &&
+	    ldns_rr_get_type(rr2) == LDNS_RR_TYPE_DS) {
+		result = ldns_rr_compare_ds_dnskey(rr2, rr1);
+	} else {
+		result = (ldns_rr_compare(rr1, rr2) == 0);
+	}
+
+	ldns_rr_free(rr1);
+	ldns_rr_free(rr2);
+
+	return result;
+}
+
+int
+ldns_rr_list_compare(const ldns_rr_list *rrl1, const ldns_rr_list *rrl2)
+{
+	size_t i = 0;
+	int rr_cmp;
+
+	assert(rrl1 != NULL);
+	assert(rrl2 != NULL);
+
+	for (i = 0; i < ldns_rr_list_rr_count(rrl1) && i < ldns_rr_list_rr_count(rrl2); i++) {
+		rr_cmp = ldns_rr_compare(ldns_rr_list_rr(rrl1, i), ldns_rr_list_rr(rrl2, i));
+		if (rr_cmp != 0) {
+			return rr_cmp;
+		}
+	}
+
+	if (i == ldns_rr_list_rr_count(rrl1) &&
+	    i != ldns_rr_list_rr_count(rrl2)) {
+		return 1;
+	} else if (i == ldns_rr_list_rr_count(rrl2) &&
+	           i != ldns_rr_list_rr_count(rrl1)) {
+		return -1;
+	} else {
+		return 0;
+	}
+}
+
+size_t
+ldns_rr_uncompressed_size(const ldns_rr *r)
+{
+	size_t rrsize;
+	size_t i;
+
+	rrsize = 0;
+	/* add all the rdf sizes */
+	for(i = 0; i < ldns_rr_rd_count(r); i++) {
+		rrsize += ldns_rdf_size(ldns_rr_rdf(r, i));
+	}
+	/* ownername */
+	rrsize += ldns_rdf_size(ldns_rr_owner(r));
+	rrsize += LDNS_RR_OVERHEAD;
+	return rrsize;
+}
+
+void
+ldns_rr2canonical(ldns_rr *rr)
+{
+	uint16_t i;
+
+	if (!rr) {
+	  return;
+        }
+
+        ldns_dname2canonical(ldns_rr_owner(rr));
+
+	/*
+	 * lowercase the rdata dnames if the rr type is one
+	 * of the list in chapter 7 of RFC3597
+	 */
+	switch(ldns_rr_get_type(rr)) {
+        	case LDNS_RR_TYPE_NS:
+        	case LDNS_RR_TYPE_MD:
+        	case LDNS_RR_TYPE_MF:
+        	case LDNS_RR_TYPE_CNAME:
+        	case LDNS_RR_TYPE_SOA:
+        	case LDNS_RR_TYPE_MB:
+        	case LDNS_RR_TYPE_MG:
+        	case LDNS_RR_TYPE_MR:
+        	case LDNS_RR_TYPE_PTR:
+        	case LDNS_RR_TYPE_MINFO:
+        	case LDNS_RR_TYPE_MX:
+        	case LDNS_RR_TYPE_RP:
+        	case LDNS_RR_TYPE_AFSDB:
+        	case LDNS_RR_TYPE_RT:
+        	case LDNS_RR_TYPE_SIG:
+        	case LDNS_RR_TYPE_PX:
+        	case LDNS_RR_TYPE_NXT:
+        	case LDNS_RR_TYPE_NAPTR:
+        	case LDNS_RR_TYPE_KX:
+        	case LDNS_RR_TYPE_SRV:
+        	case LDNS_RR_TYPE_DNAME:
+        	case LDNS_RR_TYPE_A6:
+			for (i = 0; i < ldns_rr_rd_count(rr); i++) {
+				ldns_dname2canonical(ldns_rr_rdf(rr, i));
+			}
+			return;
+		default:
+			/* do nothing */
+			return;
+	}
+}
+
+void
+ldns_rr_list2canonical(ldns_rr_list *rr_list)
+{
+	size_t i;
+	for (i = 0; i < ldns_rr_list_rr_count(rr_list); i++) {
+		ldns_rr2canonical(ldns_rr_list_rr(rr_list, i));
+	}
+}
+
+uint8_t
+ldns_rr_label_count(ldns_rr *rr)
+{
+	if (!rr) {
+		return 0;
+	}
+	return ldns_dname_label_count(
+			ldns_rr_owner(rr));
+}
+
+/** \cond */
+static const ldns_rdf_type type_0_wireformat[] = { LDNS_RDF_TYPE_UNKNOWN };
+static const ldns_rdf_type type_a_wireformat[] = { LDNS_RDF_TYPE_A };
+static const ldns_rdf_type type_ns_wireformat[] = { LDNS_RDF_TYPE_DNAME };
+static const ldns_rdf_type type_md_wireformat[] = { LDNS_RDF_TYPE_DNAME };
+static const ldns_rdf_type type_mf_wireformat[] = { LDNS_RDF_TYPE_DNAME };
+static const ldns_rdf_type type_cname_wireformat[] = { LDNS_RDF_TYPE_DNAME };
+static const ldns_rdf_type type_soa_wireformat[] = {
+	LDNS_RDF_TYPE_DNAME, LDNS_RDF_TYPE_DNAME, LDNS_RDF_TYPE_INT32, 
+	LDNS_RDF_TYPE_PERIOD, LDNS_RDF_TYPE_PERIOD, LDNS_RDF_TYPE_PERIOD,
+	LDNS_RDF_TYPE_PERIOD
+};
+static const ldns_rdf_type type_mb_wireformat[] = { LDNS_RDF_TYPE_DNAME };
+static const ldns_rdf_type type_mg_wireformat[] = { LDNS_RDF_TYPE_DNAME };
+static const ldns_rdf_type type_mr_wireformat[] = { LDNS_RDF_TYPE_DNAME };
+static const ldns_rdf_type type_wks_wireformat[] = {
+	LDNS_RDF_TYPE_A, LDNS_RDF_TYPE_WKS
+};
+static const ldns_rdf_type type_ptr_wireformat[] = { LDNS_RDF_TYPE_DNAME };
+static const ldns_rdf_type type_hinfo_wireformat[] = {
+	LDNS_RDF_TYPE_STR, LDNS_RDF_TYPE_STR
+};
+static const ldns_rdf_type type_minfo_wireformat[] = {
+	LDNS_RDF_TYPE_DNAME, LDNS_RDF_TYPE_DNAME
+};
+static const ldns_rdf_type type_mx_wireformat[] = {
+	LDNS_RDF_TYPE_INT16, LDNS_RDF_TYPE_DNAME
+};
+static const ldns_rdf_type type_rp_wireformat[] = {
+	LDNS_RDF_TYPE_DNAME, LDNS_RDF_TYPE_DNAME
+};
+static const ldns_rdf_type type_afsdb_wireformat[] = {
+	LDNS_RDF_TYPE_INT16, LDNS_RDF_TYPE_DNAME
+};
+static const ldns_rdf_type type_x25_wireformat[] = { LDNS_RDF_TYPE_STR };
+static const ldns_rdf_type type_isdn_wireformat[] = {
+	LDNS_RDF_TYPE_STR, LDNS_RDF_TYPE_STR
+};
+static const ldns_rdf_type type_rt_wireformat[] = {
+	LDNS_RDF_TYPE_INT16, LDNS_RDF_TYPE_DNAME
+};
+static const ldns_rdf_type type_nsap_wireformat[] = {
+	LDNS_RDF_TYPE_NSAP
+};
+static const ldns_rdf_type type_nsap_ptr_wireformat[] = {
+	LDNS_RDF_TYPE_STR
+};
+static const ldns_rdf_type type_sig_wireformat[] = {
+	LDNS_RDF_TYPE_TYPE, LDNS_RDF_TYPE_ALG, LDNS_RDF_TYPE_INT8, LDNS_RDF_TYPE_INT32,
+	LDNS_RDF_TYPE_TIME, LDNS_RDF_TYPE_TIME, LDNS_RDF_TYPE_INT16,
+	LDNS_RDF_TYPE_DNAME, LDNS_RDF_TYPE_B64
+};
+static const ldns_rdf_type type_key_wireformat[] = {
+	LDNS_RDF_TYPE_INT16, LDNS_RDF_TYPE_INT8, LDNS_RDF_TYPE_INT8, LDNS_RDF_TYPE_B64
+};
+static const ldns_rdf_type type_px_wireformat[] = {
+	LDNS_RDF_TYPE_INT16, LDNS_RDF_TYPE_DNAME, LDNS_RDF_TYPE_DNAME
+};
+static const ldns_rdf_type type_gpos_wireformat[] = {
+	LDNS_RDF_TYPE_STR,
+	LDNS_RDF_TYPE_STR,
+	LDNS_RDF_TYPE_STR
+};
+static const ldns_rdf_type type_aaaa_wireformat[] = { LDNS_RDF_TYPE_AAAA };
+static const ldns_rdf_type type_loc_wireformat[] = { LDNS_RDF_TYPE_LOC };
+static const ldns_rdf_type type_nxt_wireformat[] = {
+	LDNS_RDF_TYPE_DNAME, LDNS_RDF_TYPE_UNKNOWN
+};
+static const ldns_rdf_type type_eid_wireformat[] = {
+	LDNS_RDF_TYPE_HEX
+};
+static const ldns_rdf_type type_nimloc_wireformat[] = {
+	LDNS_RDF_TYPE_HEX
+};
+static const ldns_rdf_type type_srv_wireformat[] = {
+	LDNS_RDF_TYPE_INT16, LDNS_RDF_TYPE_INT16, LDNS_RDF_TYPE_INT16, LDNS_RDF_TYPE_DNAME
+};
+static const ldns_rdf_type type_atma_wireformat[] = {
+	LDNS_RDF_TYPE_ATMA
+};
+static const ldns_rdf_type type_naptr_wireformat[] = {
+	LDNS_RDF_TYPE_INT16, LDNS_RDF_TYPE_INT16, LDNS_RDF_TYPE_STR, LDNS_RDF_TYPE_STR, LDNS_RDF_TYPE_STR, LDNS_RDF_TYPE_DNAME
+};
+static const ldns_rdf_type type_kx_wireformat[] = {
+	LDNS_RDF_TYPE_INT16, LDNS_RDF_TYPE_DNAME
+};
+static const ldns_rdf_type type_cert_wireformat[] = {
+	 LDNS_RDF_TYPE_CERT_ALG, LDNS_RDF_TYPE_INT16, LDNS_RDF_TYPE_ALG, LDNS_RDF_TYPE_B64
+};
+static const ldns_rdf_type type_a6_wireformat[] = { LDNS_RDF_TYPE_UNKNOWN };
+static const ldns_rdf_type type_dname_wireformat[] = { LDNS_RDF_TYPE_DNAME };
+static const ldns_rdf_type type_sink_wireformat[] = { LDNS_RDF_TYPE_INT8,
+	LDNS_RDF_TYPE_INT8, LDNS_RDF_TYPE_INT8, LDNS_RDF_TYPE_B64
+};
+static const ldns_rdf_type type_apl_wireformat[] = {
+	LDNS_RDF_TYPE_APL
+};
+static const ldns_rdf_type type_ds_wireformat[] = {
+	LDNS_RDF_TYPE_INT16, LDNS_RDF_TYPE_ALG, LDNS_RDF_TYPE_INT8, LDNS_RDF_TYPE_HEX
+};
+static const ldns_rdf_type type_sshfp_wireformat[] = {
+	LDNS_RDF_TYPE_INT8, LDNS_RDF_TYPE_INT8, LDNS_RDF_TYPE_HEX
+};
+static const ldns_rdf_type type_ipseckey_wireformat[] = {
+	LDNS_RDF_TYPE_IPSECKEY
+};
+static const ldns_rdf_type type_rrsig_wireformat[] = {
+	LDNS_RDF_TYPE_TYPE, LDNS_RDF_TYPE_ALG, LDNS_RDF_TYPE_INT8, LDNS_RDF_TYPE_INT32,
+	LDNS_RDF_TYPE_TIME, LDNS_RDF_TYPE_TIME, LDNS_RDF_TYPE_INT16, LDNS_RDF_TYPE_DNAME, LDNS_RDF_TYPE_B64
+};
+static const ldns_rdf_type type_nsec_wireformat[] = {
+	LDNS_RDF_TYPE_DNAME, LDNS_RDF_TYPE_NSEC
+};
+static const ldns_rdf_type type_dhcid_wireformat[] = {
+	LDNS_RDF_TYPE_B64
+};
+static const ldns_rdf_type type_talink_wireformat[] = {
+	LDNS_RDF_TYPE_DNAME, LDNS_RDF_TYPE_DNAME
+};
+/* nsec3 is some vars, followed by same type of data of nsec */
+static const ldns_rdf_type type_nsec3_wireformat[] = {
+/*	LDNS_RDF_TYPE_NSEC3_VARS, LDNS_RDF_TYPE_NSEC3_NEXT_OWNER, LDNS_RDF_TYPE_NSEC*/
+	LDNS_RDF_TYPE_INT8, LDNS_RDF_TYPE_INT8, LDNS_RDF_TYPE_INT16, LDNS_RDF_TYPE_NSEC3_SALT, LDNS_RDF_TYPE_NSEC3_NEXT_OWNER, LDNS_RDF_TYPE_NSEC
+};
+
+static const ldns_rdf_type type_nsec3param_wireformat[] = {
+/*	LDNS_RDF_TYPE_NSEC3_PARAMS_VARS*/
+	LDNS_RDF_TYPE_INT8,
+	LDNS_RDF_TYPE_INT8,
+	LDNS_RDF_TYPE_INT16,
+	LDNS_RDF_TYPE_NSEC3_SALT
+};
+
+static const ldns_rdf_type type_dnskey_wireformat[] = {
+	LDNS_RDF_TYPE_INT16,
+	LDNS_RDF_TYPE_INT8,
+	LDNS_RDF_TYPE_ALG,
+	LDNS_RDF_TYPE_B64
+};
+static const ldns_rdf_type type_tsig_wireformat[] = {
+	LDNS_RDF_TYPE_DNAME,
+	LDNS_RDF_TYPE_TSIGTIME,
+	LDNS_RDF_TYPE_INT16,
+	LDNS_RDF_TYPE_INT16_DATA,
+	LDNS_RDF_TYPE_INT16,
+	LDNS_RDF_TYPE_INT16,
+	LDNS_RDF_TYPE_INT16_DATA
+};
+/** \endcond */
+
+/** \cond */
+/* All RR's defined in 1035 are well known and can thus
+ * be compressed. See RFC3597. These RR's are:
+ * CNAME HINFO MB MD MF MG MINFO MR MX NULL NS PTR SOA TXT
+ */
+static ldns_rr_descriptor rdata_field_descriptors[] = {
+	/* 0 */
+	{ 0, NULL, 0, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 1 */
+	{LDNS_RR_TYPE_A, "A", 1, 1, type_a_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 2 */
+	{LDNS_RR_TYPE_NS, "NS", 1, 1, type_ns_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_COMPRESS, 1 },
+	/* 3 */
+	{LDNS_RR_TYPE_MD, "MD", 1, 1, type_md_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_COMPRESS, 1 },
+	/* 4 */
+	{LDNS_RR_TYPE_MF, "MF", 1, 1, type_mf_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_COMPRESS, 1 },
+	/* 5 */
+	{LDNS_RR_TYPE_CNAME, "CNAME", 1, 1, type_cname_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_COMPRESS, 1 },
+	/* 6 */
+	{LDNS_RR_TYPE_SOA, "SOA", 7, 7, type_soa_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_COMPRESS, 2 },
+	/* 7 */
+	{LDNS_RR_TYPE_MB, "MB", 1, 1, type_mb_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_COMPRESS, 1 },
+	/* 8 */
+	{LDNS_RR_TYPE_MG, "MG", 1, 1, type_mg_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_COMPRESS, 1 },
+	/* 9 */
+	{LDNS_RR_TYPE_MR, "MR", 1, 1, type_mr_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_COMPRESS, 1 },
+	/* 10 */
+	{LDNS_RR_TYPE_NULL, "NULL", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 11 */
+	{LDNS_RR_TYPE_WKS, "WKS", 2, 2, type_wks_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 12 */
+	{LDNS_RR_TYPE_PTR, "PTR", 1, 1, type_ptr_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_COMPRESS, 1 },
+	/* 13 */
+	{LDNS_RR_TYPE_HINFO, "HINFO", 2, 2, type_hinfo_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 14 */
+	{LDNS_RR_TYPE_MINFO, "MINFO", 2, 2, type_minfo_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_COMPRESS, 2 },
+	/* 15 */
+	{LDNS_RR_TYPE_MX, "MX", 2, 2, type_mx_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_COMPRESS, 1 },
+	/* 16 */
+	{LDNS_RR_TYPE_TXT, "TXT", 1, 0, NULL, LDNS_RDF_TYPE_STR, LDNS_RR_NO_COMPRESS, 0 },
+	/* 17 */
+	{LDNS_RR_TYPE_RP, "RP", 2, 2, type_rp_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 2 },
+	/* 18 */
+	{LDNS_RR_TYPE_AFSDB, "AFSDB", 2, 2, type_afsdb_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 1 },
+	/* 19 */
+	{LDNS_RR_TYPE_X25, "X25", 1, 1, type_x25_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 20 */
+	{LDNS_RR_TYPE_ISDN, "ISDN", 1, 2, type_isdn_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 21 */
+	{LDNS_RR_TYPE_RT, "RT", 2, 2, type_rt_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 1 },
+	/* 22 */
+	{LDNS_RR_TYPE_NSAP, "NSAP", 1, 1, type_nsap_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 23 */
+	{LDNS_RR_TYPE_NSAP_PTR, "NSAP-PTR", 1, 1, type_nsap_ptr_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 24 */
+	{LDNS_RR_TYPE_SIG, "SIG", 9, 9, type_sig_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 1 },
+	/* 25 */
+	{LDNS_RR_TYPE_KEY, "KEY", 4, 4, type_key_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 26 */
+	{LDNS_RR_TYPE_PX, "PX", 3, 3, type_px_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 2 },
+	/* 27 */
+	{LDNS_RR_TYPE_GPOS, "GPOS", 1, 1, type_gpos_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 28 */
+	{LDNS_RR_TYPE_AAAA, "AAAA", 1, 1, type_aaaa_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 29 */
+	{LDNS_RR_TYPE_LOC, "LOC", 1, 1, type_loc_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 30 */
+	{LDNS_RR_TYPE_NXT, "NXT", 2, 2, type_nxt_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 1 },
+	/* 31 */
+	{LDNS_RR_TYPE_EID, "EID", 1, 1, type_eid_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 32 */
+	{LDNS_RR_TYPE_NIMLOC, "NIMLOC", 1, 1, type_nimloc_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 33 */
+	{LDNS_RR_TYPE_SRV, "SRV", 4, 4, type_srv_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 1 },
+	/* 34 */
+	{LDNS_RR_TYPE_ATMA, "ATMA", 1, 1, type_atma_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 35 */
+	{LDNS_RR_TYPE_NAPTR, "NAPTR", 6, 6, type_naptr_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 1 },
+	/* 36 */
+	{LDNS_RR_TYPE_KX, "KX", 2, 2, type_kx_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 1 },
+	/* 37 */
+	{LDNS_RR_TYPE_CERT, "CERT", 4, 4, type_cert_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 38 */
+	{LDNS_RR_TYPE_A6, "A6", 1, 1, type_a6_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 39 */
+	{LDNS_RR_TYPE_DNAME, "DNAME", 1, 1, type_dname_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 1 },
+	/* 40 */
+	{LDNS_RR_TYPE_SINK, "SINK", 1, 1, type_sink_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 41 */
+	{LDNS_RR_TYPE_OPT, "OPT", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 42 */
+	{LDNS_RR_TYPE_APL, "APL", 0, 0, type_apl_wireformat, LDNS_RDF_TYPE_APL, LDNS_RR_NO_COMPRESS, 0 },
+	/* 43 */
+	{LDNS_RR_TYPE_DS, "DS", 4, 4, type_ds_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 44 */
+	{LDNS_RR_TYPE_SSHFP, "SSHFP", 3, 3, type_sshfp_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 45 */
+	{LDNS_RR_TYPE_IPSECKEY, "IPSECKEY", 1, 1, type_ipseckey_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 46 */
+	{LDNS_RR_TYPE_RRSIG, "RRSIG", 9, 9, type_rrsig_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 1 },
+	/* 47 */
+	{LDNS_RR_TYPE_NSEC, "NSEC", 1, 2, type_nsec_wireformat, LDNS_RDF_TYPE_NSEC, LDNS_RR_NO_COMPRESS, 1 },
+	/* 48 */
+	{LDNS_RR_TYPE_DNSKEY, "DNSKEY", 4, 4, type_dnskey_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 49 */
+{LDNS_RR_TYPE_DHCID, "DHCID", 1, 1, type_dhcid_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 50 */
+	{LDNS_RR_TYPE_NSEC3, "NSEC3", 5, 6, type_nsec3_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 51 */
+{LDNS_RR_TYPE_NSEC3PARAM, "NSEC3PARAM", 4, 4, type_nsec3param_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+	/* 52 */
+{LDNS_RR_TYPE_NULL, "TYPE52", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE53", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE54", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE55", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE56", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE57", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_TALINK, "TALINK", 2, 2, type_talink_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 2 },
+{LDNS_RR_TYPE_NULL, "TYPE59", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE60", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE61", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE62", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE63", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE64", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE65", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE66", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE67", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE68", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE69", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE70", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE71", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE72", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE73", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE74", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE75", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE76", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE77", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE78", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE79", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE80", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE81", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE82", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE83", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE84", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE85", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE86", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE87", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE88", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE89", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE90", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE91", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE92", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE93", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE94", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE95", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE96", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE97", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE98", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_SPF,  "SPF", 1, 0, NULL, LDNS_RDF_TYPE_STR, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE100", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE101", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE102", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE103", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE104", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE105", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE106", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE107", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE108", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE109", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE110", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE111", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE112", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE113", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE114", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE115", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE116", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE117", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE118", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE119", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE120", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE121", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE122", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE123", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE124", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE125", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE126", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE127", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE128", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE129", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE130", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE131", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE132", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE133", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE134", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE135", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE136", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE137", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE138", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE139", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE140", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE141", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE142", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE143", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE144", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE145", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE146", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE147", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE148", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE149", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE150", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE151", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE152", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE153", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE154", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE155", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE156", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE157", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE158", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE159", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE160", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE161", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE162", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE163", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE164", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE165", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE166", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE167", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE168", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE169", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE170", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE171", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE172", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE173", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE174", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE175", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE176", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE177", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE178", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE179", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE180", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE181", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE182", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE183", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE184", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE185", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE186", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE187", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE188", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE189", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE190", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE191", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE192", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE193", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE194", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE195", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE196", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE197", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE198", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE199", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE200", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE201", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE202", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE203", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE204", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE205", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE206", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE207", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE208", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE209", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE210", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE211", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE212", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE213", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE214", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE215", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE216", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE217", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE218", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE219", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE220", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE221", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE222", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE223", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE224", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE225", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE226", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE227", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE228", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE229", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE230", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE231", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE232", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE233", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE234", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE235", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE236", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE237", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE238", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE239", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE240", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE241", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE242", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE243", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE244", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE245", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE246", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE247", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE248", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+{LDNS_RR_TYPE_NULL, "TYPE249", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+/* LDNS_RDF_TYPE_INT16_DATA essentially takes two fields (length and data) and
+ * makes them into one. So, while in rfc 2845 is specified that a TSIG may have 
+ * 8 or 9 rdata fields, by this implementation, the min/max are 7 each. 
+ */
+{LDNS_RR_TYPE_TSIG, "TSIG", 7, 7, type_tsig_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
+/* split in array, no longer contiguous */
+{LDNS_RR_TYPE_DLV, "DLV", 4, 4, type_ds_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }
+};
+/** \endcond */
+
+/**
+ * \def LDNS_RDATA_FIELD_DESCRIPTORS_COUNT
+ * computes the number of rdata fields
+ */
+#define LDNS_RDATA_FIELD_DESCRIPTORS_COUNT \
+	(sizeof(rdata_field_descriptors)/sizeof(rdata_field_descriptors[0]))
+
+const ldns_rr_descriptor *
+ldns_rr_descript(uint16_t type)
+{
+	size_t i;
+	if (type <= LDNS_RDATA_FIELD_DESCRIPTORS_COMMON) {
+		return &rdata_field_descriptors[type];
+	} else {
+		/* because not all array index equals type code */
+		for (i = LDNS_RDATA_FIELD_DESCRIPTORS_COMMON;
+		     i < LDNS_RDATA_FIELD_DESCRIPTORS_COUNT;
+		     i++) {
+		        if (rdata_field_descriptors[i]._type == type) {
+		     		return &rdata_field_descriptors[i];
+			}
+		}
+                return &rdata_field_descriptors[0];
+	}
+}
+
+size_t
+ldns_rr_descriptor_minimum(const ldns_rr_descriptor *descriptor)
+{
+	if (descriptor) {
+		return descriptor->_minimum;
+	} else {
+		return 0;
+	}
+}
+
+size_t
+ldns_rr_descriptor_maximum(const ldns_rr_descriptor *descriptor)
+{
+	if (descriptor) {
+		if (descriptor->_variable != LDNS_RDF_TYPE_NONE) {
+			/* Should really be SIZE_MAX... bad FreeBSD.  */
+			return UINT_MAX;
+		} else {
+			return descriptor->_maximum;
+		}
+	} else {
+		return 0;
+	}
+}
+
+ldns_rdf_type
+ldns_rr_descriptor_field_type(const ldns_rr_descriptor *descriptor,
+                              size_t index)
+{
+	assert(descriptor != NULL);
+	assert(index < descriptor->_maximum
+	       || descriptor->_variable != LDNS_RDF_TYPE_NONE);
+	if (index < descriptor->_maximum) {
+		return descriptor->_wireformat[index];
+	} else {
+		return descriptor->_variable;
+	}
+}
+
+ldns_rr_type
+ldns_get_rr_type_by_name(const char *name)
+{
+	unsigned int i;
+	const char *desc_name;
+	const ldns_rr_descriptor *desc;
+
+	/* TYPEXX representation */
+	if (strlen(name) > 4 && strncasecmp(name, "TYPE", 4) == 0) {
+		return atoi(name + 4);
+	}
+
+	/* Normal types */
+	for (i = 0; i < (unsigned int) LDNS_RDATA_FIELD_DESCRIPTORS_COUNT; i++) {
+		desc = &rdata_field_descriptors[i];
+		desc_name = desc->_name;
+		if(desc_name &&
+		   strlen(name) == strlen(desc_name) &&
+		   strncasecmp(name, desc_name, strlen(desc_name)) == 0) {
+			/* because not all array index equals type code */
+			return desc->_type;
+		}
+	}
+
+	/* special cases for query types */
+	if (strlen(name) == 4 && strncasecmp(name, "IXFR", 4) == 0) {
+		return 251;
+	} else if (strlen(name) == 4 && strncasecmp(name, "AXFR", 4) == 0) {
+		return 252;
+	} else if (strlen(name) == 5 && strncasecmp(name, "MAILB", 5) == 0) {
+		return 253;
+	} else if (strlen(name) == 5 && strncasecmp(name, "MAILA", 5) == 0) {
+		return 254;
+	} else if (strlen(name) == 3 && strncasecmp(name, "ANY", 3) == 0) {
+		return 255;
+	}
+
+	return 0;
+}
+
+ldns_rr_class
+ldns_get_rr_class_by_name(const char *name)
+{
+	ldns_lookup_table *lt;
+
+	/* CLASSXX representation */
+	if (strlen(name) > 5 && strncasecmp(name, "CLASS", 5) == 0) {
+		return atoi(name + 5);
+	}
+
+	/* Normal types */
+	lt = ldns_lookup_by_name(ldns_rr_classes, name);
+
+	if (lt) {
+		return lt->id;
+	}
+	return 0;
+}
+
+
+ldns_rr_type
+ldns_rdf2rr_type(const ldns_rdf *rd)
+{
+        ldns_rr_type r;
+
+        if (!rd) {
+                return 0;
+        }
+
+        if (ldns_rdf_get_type(rd) != LDNS_RDF_TYPE_TYPE) {
+                return 0;
+        }
+
+        r = (ldns_rr_type) ldns_rdf2native_int16(rd);
+        return r;
+}
+
+ldns_rr_type
+ldns_rr_list_type(const ldns_rr_list *rr_list)
+{
+	if (rr_list && ldns_rr_list_rr_count(rr_list) > 0) {
+		return ldns_rr_get_type(ldns_rr_list_rr(rr_list, 0));
+	} else {
+		return 0;
+	}
+}
+
+ldns_rdf *
+ldns_rr_list_owner(const ldns_rr_list *rr_list)
+{
+	if (rr_list && ldns_rr_list_rr_count(rr_list) > 0) {
+		return ldns_rr_owner(ldns_rr_list_rr(rr_list, 0));
+	} else {
+		return NULL;
+	}
+}
diff --git a/3rdParty/Ldns/src/src/rr_functions.c b/3rdParty/Ldns/src/src/rr_functions.c
new file mode 100644
index 0000000..b4847d6
--- /dev/null
+++ b/3rdParty/Ldns/src/src/rr_functions.c
@@ -0,0 +1,419 @@
+/*
+ * rr_function.c
+ *
+ * function that operate on specific rr types
+ *
+ * (c) NLnet Labs, 2004-2006
+ * See the file LICENSE for the license
+ */
+
+/*
+ * These come strait from perldoc Net::DNS::RR::xxx
+ * first the read variant, then the write. This is
+ * not complete.
+ */
+
+#include <ldns/config.h>
+
+#include <ldns/ldns.h>
+
+#include <limits.h>
+#include <strings.h>
+
+/**
+ * return a specific rdf
+ * \param[in] type type of RR
+ * \param[in] rr   the rr itself
+ * \param[in] pos  at which postion to get it
+ * \return the rdf sought
+ */
+static ldns_rdf *
+ldns_rr_function(ldns_rr_type type, const ldns_rr *rr, size_t pos)
+{
+        if (!rr || ldns_rr_get_type(rr) != type) {
+                return NULL;
+        }
+        return ldns_rr_rdf(rr, pos);
+}
+
+/**
+ * set a specific rdf
+ * \param[in] type type of RR
+ * \param[in] rr   the rr itself
+ * \param[in] rdf  the rdf to set
+ * \param[in] pos  at which postion to set it
+ * \return true or false
+ */
+static bool
+ldns_rr_set_function(ldns_rr_type type, ldns_rr *rr, ldns_rdf *rdf, size_t pos)
+{
+        ldns_rdf *pop;
+        if (!rr || ldns_rr_get_type(rr) != type) {
+                return false;
+        }
+        pop = ldns_rr_set_rdf(rr, rdf, pos);
+ 	ldns_rdf_deep_free(pop);
+        return true;
+}
+
+/* A/AAAA records */
+ldns_rdf *
+ldns_rr_a_address(const ldns_rr *r)
+{
+	/* 2 types to check, cannot use the macro */
+	if (!r || (ldns_rr_get_type(r) != LDNS_RR_TYPE_A &&
+			ldns_rr_get_type(r) != LDNS_RR_TYPE_AAAA)) {
+		return NULL;
+	}
+	return ldns_rr_rdf(r, 0);
+}
+
+bool
+ldns_rr_a_set_address(ldns_rr *r, ldns_rdf *f)
+{
+	/* 2 types to check, cannot use the macro... */
+	ldns_rdf *pop;
+	if (!r || (ldns_rr_get_type(r) != LDNS_RR_TYPE_A &&
+			ldns_rr_get_type(r) != LDNS_RR_TYPE_AAAA)) {
+		return false;
+	}
+	pop = ldns_rr_set_rdf(r, f, 0);
+	if (pop) {
+		LDNS_FREE(pop);
+		return true;
+	} else {
+		return false;
+	}
+}
+
+/* NS record */
+ldns_rdf *
+ldns_rr_ns_nsdname(const ldns_rr *r)
+{
+	return ldns_rr_function(LDNS_RR_TYPE_NS, r, 0);
+}
+
+/* MX record */
+ldns_rdf *
+ldns_rr_mx_preference(const ldns_rr *r)
+{
+	return ldns_rr_function(LDNS_RR_TYPE_MX, r, 0);
+}
+
+ldns_rdf *
+ldns_rr_mx_exchange(const ldns_rr *r)
+{
+	return ldns_rr_function(LDNS_RR_TYPE_MX, r, 1);
+}
+
+/* RRSIG record */
+ldns_rdf *
+ldns_rr_rrsig_typecovered(const ldns_rr *r)
+{
+	return ldns_rr_function(LDNS_RR_TYPE_RRSIG, r, 0);
+}
+
+bool
+ldns_rr_rrsig_set_typecovered(ldns_rr *r, ldns_rdf *f)
+{
+	return ldns_rr_set_function(LDNS_RR_TYPE_RRSIG, r, f, 0);
+}
+
+ldns_rdf *
+ldns_rr_rrsig_algorithm(const ldns_rr *r)
+{
+	return ldns_rr_function(LDNS_RR_TYPE_RRSIG, r, 1);
+}
+
+bool
+ldns_rr_rrsig_set_algorithm(ldns_rr *r, ldns_rdf *f)
+{
+	return ldns_rr_set_function(LDNS_RR_TYPE_RRSIG, r, f, 1);
+}
+
+ldns_rdf *
+ldns_rr_rrsig_labels(const ldns_rr *r)
+{
+	return ldns_rr_function(LDNS_RR_TYPE_RRSIG, r, 2);
+}
+
+bool
+ldns_rr_rrsig_set_labels(ldns_rr *r, ldns_rdf *f)
+{
+	return ldns_rr_set_function(LDNS_RR_TYPE_RRSIG, r, f, 2);
+}
+
+ldns_rdf *
+ldns_rr_rrsig_origttl(const ldns_rr *r)
+{
+	return ldns_rr_function(LDNS_RR_TYPE_RRSIG, r, 3);
+}
+
+bool
+ldns_rr_rrsig_set_origttl(ldns_rr *r, ldns_rdf *f)
+{
+	return ldns_rr_set_function(LDNS_RR_TYPE_RRSIG, r, f, 3);
+}
+
+ldns_rdf *
+ldns_rr_rrsig_expiration(const ldns_rr *r)
+{
+	return ldns_rr_function(LDNS_RR_TYPE_RRSIG, r, 4);
+}
+
+bool
+ldns_rr_rrsig_set_expiration(ldns_rr *r, ldns_rdf *f)
+{
+	return ldns_rr_set_function(LDNS_RR_TYPE_RRSIG, r, f, 4);
+}
+
+ldns_rdf *
+ldns_rr_rrsig_inception(const ldns_rr *r)
+{
+	return ldns_rr_function(LDNS_RR_TYPE_RRSIG, r, 5);
+}
+
+bool
+ldns_rr_rrsig_set_inception(ldns_rr *r, ldns_rdf *f)
+{
+	return ldns_rr_set_function(LDNS_RR_TYPE_RRSIG, r, f, 5);
+}
+
+ldns_rdf *
+ldns_rr_rrsig_keytag(const ldns_rr *r)
+{
+	return ldns_rr_function(LDNS_RR_TYPE_RRSIG, r, 6);
+}
+
+bool
+ldns_rr_rrsig_set_keytag(ldns_rr *r, ldns_rdf *f)
+{
+	return ldns_rr_set_function(LDNS_RR_TYPE_RRSIG, r, f, 6);
+}
+
+ldns_rdf *
+ldns_rr_rrsig_signame(const ldns_rr *r)
+{
+	return ldns_rr_function(LDNS_RR_TYPE_RRSIG, r, 7);
+}
+
+bool
+ldns_rr_rrsig_set_signame(ldns_rr *r, ldns_rdf *f)
+{
+	return ldns_rr_set_function(LDNS_RR_TYPE_RRSIG, r, f, 7);
+}
+
+ldns_rdf *
+ldns_rr_rrsig_sig(const ldns_rr *r)
+{
+	return ldns_rr_function(LDNS_RR_TYPE_RRSIG, r, 8);
+}
+
+bool
+ldns_rr_rrsig_set_sig(ldns_rr *r, ldns_rdf *f)
+{
+	return ldns_rr_set_function(LDNS_RR_TYPE_RRSIG, r, f, 8);
+}
+
+/* DNSKEY record */
+ldns_rdf *
+ldns_rr_dnskey_flags(const ldns_rr *r)
+{
+	return ldns_rr_function(LDNS_RR_TYPE_DNSKEY, r, 0);
+}
+
+bool
+ldns_rr_dnskey_set_flags(ldns_rr *r, ldns_rdf *f)
+{
+	return ldns_rr_set_function(LDNS_RR_TYPE_DNSKEY, r, f, 0);
+}
+
+ldns_rdf *
+ldns_rr_dnskey_protocol(const ldns_rr *r)
+{
+	return ldns_rr_function(LDNS_RR_TYPE_DNSKEY, r, 1);
+}
+
+bool
+ldns_rr_dnskey_set_protocol(ldns_rr *r, ldns_rdf *f)
+{
+	return ldns_rr_set_function(LDNS_RR_TYPE_DNSKEY, r, f, 1);
+}
+
+ldns_rdf *
+ldns_rr_dnskey_algorithm(const ldns_rr *r)
+{
+	return ldns_rr_function(LDNS_RR_TYPE_DNSKEY, r, 2);
+}
+
+bool
+ldns_rr_dnskey_set_algorithm(ldns_rr *r, ldns_rdf *f)
+{
+	return ldns_rr_set_function(LDNS_RR_TYPE_DNSKEY, r, f, 2);
+}
+
+ldns_rdf *
+ldns_rr_dnskey_key(const ldns_rr *r)
+{
+	return ldns_rr_function(LDNS_RR_TYPE_DNSKEY, r, 3);
+}
+
+bool
+ldns_rr_dnskey_set_key(ldns_rr *r, ldns_rdf *f)
+{
+	return ldns_rr_set_function(LDNS_RR_TYPE_DNSKEY, r, f, 3);
+}
+
+size_t
+ldns_rr_dnskey_key_size_raw(const unsigned char* keydata,
+                            const size_t len,
+                            const ldns_algorithm alg)
+{
+	/* for DSA keys */
+	uint8_t t;
+	
+	/* for RSA keys */
+	uint16_t exp;
+	uint16_t int16;
+	
+	switch ((ldns_signing_algorithm)alg) {
+	case LDNS_SIGN_DSA:
+	case LDNS_SIGN_DSA_NSEC3:
+		if (len > 0) {
+			t = keydata[0];
+			return (64 + t*8)*8;
+		} else {
+			return 0;
+		}
+		break;
+	case LDNS_SIGN_RSAMD5:
+	case LDNS_SIGN_RSASHA1:
+	case LDNS_SIGN_RSASHA1_NSEC3:
+#ifdef USE_SHA2
+	case LDNS_SIGN_RSASHA256:
+	case LDNS_SIGN_RSASHA512:
+#endif
+		if (len > 0) {
+			if (keydata[0] == 0) {
+				/* big exponent */
+				if (len > 3) {
+					memmove(&int16, keydata + 1, 2);
+					exp = ntohs(int16);
+					return (len - exp - 3)*8;
+				} else {
+					return 0;
+				}
+			} else {
+				exp = keydata[0];
+				return (len-exp-1)*8;
+			}
+		} else {
+			return 0;
+		}
+		break;
+#ifdef USE_GOST
+	case LDNS_SIGN_ECC_GOST:
+		return 512;
+#endif
+#ifdef USE_ECDSA
+        case LDNS_SIGN_ECDSAP256SHA256:
+                return 256;
+        case LDNS_SIGN_ECDSAP384SHA384:
+                return 384;
+#endif
+	case LDNS_SIGN_HMACMD5:
+		return len;
+	default:
+		return 0;
+	}
+}
+
+size_t 
+ldns_rr_dnskey_key_size(const ldns_rr *key) 
+{
+	if (!key || !ldns_rr_dnskey_key(key) 
+			|| !ldns_rr_dnskey_algorithm(key)) {
+		return 0;
+	}
+	return ldns_rr_dnskey_key_size_raw((unsigned char*)ldns_rdf_data(ldns_rr_dnskey_key(key)),
+	                                   ldns_rdf_size(ldns_rr_dnskey_key(key)),
+	                                   ldns_rdf2native_int8(ldns_rr_dnskey_algorithm(key))
+	                                  );
+}
+
+uint32_t ldns_soa_serial_identity(uint32_t ATTR_UNUSED(_), void *data)
+{
+	return (uint32_t) (intptr_t) data;
+}
+
+uint32_t ldns_soa_serial_increment(uint32_t s, void *ATTR_UNUSED(_))
+{
+	return ldns_soa_serial_increment_by(s, (void *)1);
+}
+
+uint32_t ldns_soa_serial_increment_by(uint32_t s, void *data)
+{
+	return s + (intptr_t) data;
+}
+
+uint32_t ldns_soa_serial_datecounter(uint32_t s, void *data)
+{
+	struct tm tm;
+	char s_str[11];
+	uint32_t new_s;
+	time_t t = data ? (time_t) (intptr_t) data : ldns_time(NULL);
+
+	(void) strftime(s_str, 11, "%Y%m%d00", localtime_r(&t, &tm));
+	new_s = (uint32_t) atoi(s_str);
+	return new_s > s ? new_s : s+1;
+}
+
+uint32_t ldns_soa_serial_unixtime(uint32_t s, void *data)
+{
+	uint32_t new_s = data ? (uint32_t) (intptr_t) data 
+			      : (uint32_t) ldns_time(NULL);
+	return new_s > s ? new_s : s+1;
+}
+
+void
+ldns_rr_soa_increment(ldns_rr *soa)
+{
+	ldns_rr_soa_increment_func_data(soa, ldns_soa_serial_increment, NULL);
+}
+
+void
+ldns_rr_soa_increment_func(ldns_rr *soa, ldns_soa_serial_increment_func_t f)
+{
+	ldns_rr_soa_increment_func_data(soa, f, NULL);
+}
+
+void
+ldns_rr_soa_increment_func_data(ldns_rr *soa, 
+		ldns_soa_serial_increment_func_t f, void *data)
+{
+	ldns_rdf *prev_soa_serial_rdf;
+	if ( !soa || !f || ldns_rr_get_type(soa) != LDNS_RR_TYPE_SOA 
+			|| !ldns_rr_rdf(soa, 2)) {
+		return;
+	}
+	prev_soa_serial_rdf = ldns_rr_set_rdf(
+		  soa
+		, ldns_native2rdf_int32(
+			  LDNS_RDF_TYPE_INT32
+			, (*f)( ldns_rdf2native_int32(
+					ldns_rr_rdf(soa, 2))
+			      , data
+			)
+		)
+		, 2
+	);
+	LDNS_FREE(prev_soa_serial_rdf);
+}
+
+void
+ldns_rr_soa_increment_func_int(ldns_rr *soa, 
+		ldns_soa_serial_increment_func_t f, int data)
+{
+	ldns_rr_soa_increment_func_data(soa, f, (void *) (intptr_t) data);
+}
+
diff --git a/3rdParty/Ldns/src/src/sha1.c b/3rdParty/Ldns/src/src/sha1.c
new file mode 100644
index 0000000..5dec680
--- /dev/null
+++ b/3rdParty/Ldns/src/src/sha1.c
@@ -0,0 +1,177 @@
+/*
+ * modified for ldns by Jelte Jansen, original taken from OpenBSD:
+ * 
+ * SHA-1 in C
+ * By Steve Reid <steve@edmweb.com>
+ * 100% Public Domain
+ * 
+ * Test Vectors (from FIPS PUB 180-1)
+ * "abc"
+ *   A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+ * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ *   84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+ * A million repetitions of "a"
+ *   34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */
+/* #define SHA1HANDSOFF * Copies data before messing with it. */
+
+#include <ldns/config.h>
+#include <ldns/ldns.h>
+#include <strings.h>
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
+    |(rol(block->l[i],8)&0x00FF00FF))
+#else
+#define blk0(i) block->l[i]
+#endif
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+    ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+void
+ldns_sha1_transform(uint32_t state[5], const unsigned char buffer[LDNS_SHA1_BLOCK_LENGTH])
+{
+    uint32_t a, b, c, d, e;
+    typedef union {
+        unsigned char c[64];
+        unsigned int l[16];
+    } CHAR64LONG16;
+    CHAR64LONG16* block;
+#ifdef SHA1HANDSOFF
+    unsigned char workspace[LDNS_SHA1_BLOCK_LENGTH];
+
+    block = (CHAR64LONG16 *)workspace;
+    memmove(block, buffer, LDNS_SHA1_BLOCK_LENGTH);
+#else
+    block = (CHAR64LONG16 *)buffer;
+#endif
+    /* Copy context->state[] to working vars */
+    a = state[0];
+    b = state[1];
+    c = state[2];
+    d = state[3];
+    e = state[4];
+
+    /* 4 rounds of 20 operations each. Loop unrolled. */
+    R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+    R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+    R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+    R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+    R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+    R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+    R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+    R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+    R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+    R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+    R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+    R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+    R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+    R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+    R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+    R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+    R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+    R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+    R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+    R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+
+    /* Add the working vars back into context.state[] */
+    state[0] += a;
+    state[1] += b;
+    state[2] += c;
+    state[3] += d;
+    state[4] += e;
+    /* Wipe variables */
+    a = b = c = d = e = 0;
+}
+
+
+/* SHA1Init - Initialize new context */
+
+void
+ldns_sha1_init(ldns_sha1_ctx *context)
+{
+    /* SHA1 initialization constants */
+    context->count = 0;
+    context->state[0] = 0x67452301;
+    context->state[1] = 0xEFCDAB89;
+    context->state[2] = 0x98BADCFE;
+    context->state[3] = 0x10325476;
+    context->state[4] = 0xC3D2E1F0;
+}
+
+
+/* Run your data through this. */
+
+void
+ldns_sha1_update(ldns_sha1_ctx *context, const unsigned char *data, unsigned int len)
+{
+    unsigned int i;
+    unsigned int j;
+
+    j = (unsigned)(uint32_t)((context->count >> 3) & 63);
+    context->count += (len << 3);
+    if ((j + len) > 63) {
+        memmove(&context->buffer[j], data, (i = 64 - j));
+        ldns_sha1_transform(context->state, context->buffer);
+        for ( ; i + 63 < len; i += 64) {
+            ldns_sha1_transform(context->state, &data[i]);
+        }
+        j = 0;
+    }
+    else i = 0;
+    memmove(&context->buffer[j], &data[i], len - i);
+}
+
+
+/* Add padding and return the message digest. */
+
+void
+ldns_sha1_final(unsigned char digest[LDNS_SHA1_DIGEST_LENGTH], ldns_sha1_ctx *context)
+{
+    unsigned int i;
+    unsigned char finalcount[8];
+
+    for (i = 0; i < 8; i++) {
+        finalcount[i] = (unsigned char)((context->count >>
+            ((7 - (i & 7)) * 8)) & 255);  /* Endian independent */
+    }
+    ldns_sha1_update(context, (unsigned char *)"\200", 1);
+    while ((context->count & 504) != 448) {
+        ldns_sha1_update(context, (unsigned char *)"\0", 1);
+    }
+    ldns_sha1_update(context, finalcount, 8);  /* Should cause a SHA1Transform() */
+
+    if (digest != NULL)
+        for (i = 0; i < LDNS_SHA1_DIGEST_LENGTH; i++) {
+            digest[i] = (unsigned char)((context->state[i >> 2] >>
+                ((3 - (i & 3)) * 8)) & 255);
+      }
+#ifdef SHA1HANDSOFF  /* make SHA1Transform overwrite its own static vars */
+    ldns_sha1_transform(context->state, context->buffer);
+#endif
+}
+
+unsigned char *
+ldns_sha1(unsigned char *data, unsigned int data_len, unsigned char *digest)
+{
+    ldns_sha1_ctx ctx;
+    ldns_sha1_init(&ctx);
+    ldns_sha1_update(&ctx, data, data_len);
+    ldns_sha1_final(digest, &ctx);
+    return digest;
+}
diff --git a/3rdParty/Ldns/src/src/sha2.c b/3rdParty/Ldns/src/src/sha2.c
new file mode 100644
index 0000000..6ba4ab5
--- /dev/null
+++ b/3rdParty/Ldns/src/src/sha2.c
@@ -0,0 +1,982 @@
+/*
+ * FILE:	sha2.c
+ * AUTHOR:	Aaron D. Gifford - http://www.aarongifford.com/
+ * 
+ * Copyright (c) 2000-2001, Aaron D. Gifford
+ * All rights reserved.
+ *
+ * Modified by Jelte Jansen to fit in ldns, and not clash with any
+ * system-defined SHA code.
+ * Changes:
+ * - Renamed (external) functions and constants to fit ldns style
+ * - Removed _End and _Data functions
+ * - Added ldns_shaX(data, len, digest) convenience functions
+ * - Removed prototypes of _Transform functions and made those static
+ * 
+ * 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 copyright holder nor the names of contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``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 AUTHOR OR CONTRIBUTOR(S) 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.
+ *
+ * $Id: sha2.c,v 1.1 2001/11/08 00:01:51 adg Exp adg $
+ */
+
+#include <ldns/config.h>
+#include <string.h>	/* memcpy()/memset() or bcopy()/bzero() */
+#include <assert.h>	/* assert() */
+#include <ldns/sha2.h>
+
+/*
+ * ASSERT NOTE:
+ * Some sanity checking code is included using assert().  On my FreeBSD
+ * system, this additional code can be removed by compiling with NDEBUG
+ * defined.  Check your own systems manpage on assert() to see how to
+ * compile WITHOUT the sanity checking code on your system.
+ *
+ * UNROLLED TRANSFORM LOOP NOTE:
+ * You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform
+ * loop version for the hash transform rounds (defined using macros
+ * later in this file).  Either define on the command line, for example:
+ *
+ *   cc -DSHA2_UNROLL_TRANSFORM -o sha2 sha2.c sha2prog.c
+ *
+ * or define below:
+ *
+ *   #define SHA2_UNROLL_TRANSFORM
+ *
+ */
+
+
+/*** SHA-256/384/512 Machine Architecture Definitions *****************/
+/*
+ * BYTE_ORDER NOTE:
+ *
+ * Please make sure that your system defines BYTE_ORDER.  If your
+ * architecture is little-endian, make sure it also defines
+ * LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are
+ * equivilent.
+ *
+ * If your system does not define the above, then you can do so by
+ * hand like this:
+ *
+ *   #define LITTLE_ENDIAN 1234
+ *   #define BIG_ENDIAN    4321
+ *
+ * And for little-endian machines, add:
+ *
+ *   #define BYTE_ORDER LITTLE_ENDIAN 
+ *
+ * Or for big-endian machines:
+ *
+ *   #define BYTE_ORDER BIG_ENDIAN
+ *
+ * The FreeBSD machine this was written on defines BYTE_ORDER
+ * appropriately by including <sys/types.h> (which in turn includes
+ * <machine/endian.h> where the appropriate definitions are actually
+ * made).
+ */
+#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN)
+#error Define BYTE_ORDER to be equal to either LITTLE_ENDIAN or BIG_ENDIAN
+#endif
+
+typedef uint8_t  sha2_byte;	/* Exactly 1 byte */
+typedef uint32_t sha2_word32;	/* Exactly 4 bytes */
+#ifdef S_SPLINT_S
+typedef unsigned long long sha2_word64; /* lint 8 bytes */
+#else
+typedef uint64_t sha2_word64;	/* Exactly 8 bytes */
+#endif
+
+/*** SHA-256/384/512 Various Length Definitions ***********************/
+/* NOTE: Most of these are in sha2.h */
+#define ldns_sha256_SHORT_BLOCK_LENGTH	(LDNS_SHA256_BLOCK_LENGTH - 8)
+#define ldns_sha384_SHORT_BLOCK_LENGTH	(LDNS_SHA384_BLOCK_LENGTH - 16)
+#define ldns_sha512_SHORT_BLOCK_LENGTH	(LDNS_SHA512_BLOCK_LENGTH - 16)
+
+
+/*** ENDIAN REVERSAL MACROS *******************************************/
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define REVERSE32(w,x)	{ \
+	sha2_word32 tmp = (w); \
+	tmp = (tmp >> 16) | (tmp << 16); \
+	(x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \
+}
+#ifndef S_SPLINT_S
+#define REVERSE64(w,x)	{ \
+	sha2_word64 tmp = (w); \
+	tmp = (tmp >> 32) | (tmp << 32); \
+	tmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | \
+	      ((tmp & 0x00ff00ff00ff00ffULL) << 8); \
+	(x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | \
+	      ((tmp & 0x0000ffff0000ffffULL) << 16); \
+}
+#else /* splint */
+#define REVERSE64(w,x) /* splint */
+#endif /* splint */
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+
+/*
+ * Macro for incrementally adding the unsigned 64-bit integer n to the
+ * unsigned 128-bit integer (represented using a two-element array of
+ * 64-bit words):
+ */
+#define ADDINC128(w,n)	{ \
+	(w)[0] += (sha2_word64)(n); \
+	if ((w)[0] < (n)) { \
+		(w)[1]++; \
+	} \
+}
+#ifdef S_SPLINT_S
+#undef ADDINC128
+#define ADDINC128(w,n) /* splint */
+#endif
+
+/*
+ * Macros for copying blocks of memory and for zeroing out ranges
+ * of memory.  Using these macros makes it easy to switch from
+ * using memset()/memcpy() and using bzero()/bcopy().
+ *
+ * Please define either SHA2_USE_MEMSET_MEMCPY or define
+ * SHA2_USE_BZERO_BCOPY depending on which function set you
+ * choose to use:
+ */
+#if !defined(SHA2_USE_MEMSET_MEMCPY) && !defined(SHA2_USE_BZERO_BCOPY)
+/* Default to memset()/memcpy() if no option is specified */
+#define	SHA2_USE_MEMSET_MEMCPY	1
+#endif
+#if defined(SHA2_USE_MEMSET_MEMCPY) && defined(SHA2_USE_BZERO_BCOPY)
+/* Abort with an error if BOTH options are defined */
+#error Define either SHA2_USE_MEMSET_MEMCPY or SHA2_USE_BZERO_BCOPY, not both!
+#endif
+
+#ifdef SHA2_USE_MEMSET_MEMCPY
+#define MEMSET_BZERO(p,l)	memset((p), 0, (l))
+#define MEMCPY_BCOPY(d,s,l)	memcpy((d), (s), (l))
+#endif
+#ifdef SHA2_USE_BZERO_BCOPY
+#define MEMSET_BZERO(p,l)	bzero((p), (l))
+#define MEMCPY_BCOPY(d,s,l)	bcopy((s), (d), (l))
+#endif
+
+
+/*** THE SIX LOGICAL FUNCTIONS ****************************************/
+/*
+ * Bit shifting and rotation (used by the six SHA-XYZ logical functions:
+ *
+ *   NOTE:  The naming of R and S appears backwards here (R is a SHIFT and
+ *   S is a ROTATION) because the SHA-256/384/512 description document
+ *   (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this
+ *   same "backwards" definition.
+ */
+/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */
+#define R(b,x) 		((x) >> (b))
+/* 32-bit Rotate-right (used in SHA-256): */
+#define S32(b,x)	(((x) >> (b)) | ((x) << (32 - (b))))
+/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */
+#define S64(b,x)	(((x) >> (b)) | ((x) << (64 - (b))))
+
+/* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */
+#define Ch(x,y,z)	(((x) & (y)) ^ ((~(x)) & (z)))
+#define Maj(x,y,z)	(((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+
+/* Four of six logical functions used in SHA-256: */
+#define Sigma0_256(x)	(S32(2,  (x)) ^ S32(13, (x)) ^ S32(22, (x)))
+#define Sigma1_256(x)	(S32(6,  (x)) ^ S32(11, (x)) ^ S32(25, (x)))
+#define sigma0_256(x)	(S32(7,  (x)) ^ S32(18, (x)) ^ R(3 ,   (x)))
+#define sigma1_256(x)	(S32(17, (x)) ^ S32(19, (x)) ^ R(10,   (x)))
+
+/* Four of six logical functions used in SHA-384 and SHA-512: */
+#define Sigma0_512(x)	(S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x)))
+#define Sigma1_512(x)	(S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x)))
+#define sigma0_512(x)	(S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7,   (x)))
+#define sigma1_512(x)	(S64(19, (x)) ^ S64(61, (x)) ^ R( 6,   (x)))
+
+/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/
+/* Hash constant words K for SHA-256: */
+static const sha2_word32 K256[64] = {
+	0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,
+	0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
+	0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,
+	0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
+	0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+	0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
+	0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,
+	0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
+	0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,
+	0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+	0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,
+	0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
+	0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,
+	0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
+	0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+	0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+
+/* initial hash value H for SHA-256: */
+static const sha2_word32 ldns_sha256_initial_hash_value[8] = {
+	0x6a09e667UL,
+	0xbb67ae85UL,
+	0x3c6ef372UL,
+	0xa54ff53aUL,
+	0x510e527fUL,
+	0x9b05688cUL,
+	0x1f83d9abUL,
+	0x5be0cd19UL
+};
+
+/* Hash constant words K for SHA-384 and SHA-512: */
+static const sha2_word64 K512[80] = {
+	0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
+	0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
+	0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
+	0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
+	0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
+	0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
+	0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,
+	0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
+	0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
+	0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
+	0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,
+	0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
+	0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,
+	0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
+	0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
+	0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
+	0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,
+	0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
+	0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,
+	0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
+	0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
+	0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
+	0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,
+	0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
+	0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
+	0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
+	0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
+	0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
+	0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,
+	0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
+	0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,
+	0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
+	0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
+	0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
+	0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
+	0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
+	0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,
+	0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
+	0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
+	0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
+};
+
+/* initial hash value H for SHA-384 */
+static const sha2_word64 sha384_initial_hash_value[8] = {
+	0xcbbb9d5dc1059ed8ULL,
+	0x629a292a367cd507ULL,
+	0x9159015a3070dd17ULL,
+	0x152fecd8f70e5939ULL,
+	0x67332667ffc00b31ULL,
+	0x8eb44a8768581511ULL,
+	0xdb0c2e0d64f98fa7ULL,
+	0x47b5481dbefa4fa4ULL
+};
+
+/* initial hash value H for SHA-512 */
+static const sha2_word64 sha512_initial_hash_value[8] = {
+	0x6a09e667f3bcc908ULL,
+	0xbb67ae8584caa73bULL,
+	0x3c6ef372fe94f82bULL,
+	0xa54ff53a5f1d36f1ULL,
+	0x510e527fade682d1ULL,
+	0x9b05688c2b3e6c1fULL,
+	0x1f83d9abfb41bd6bULL,
+	0x5be0cd19137e2179ULL
+};
+
+/*** SHA-256: *********************************************************/
+void ldns_sha256_init(ldns_sha256_CTX* context) {
+	if (context == (ldns_sha256_CTX*)0) {
+		return;
+	}
+	MEMCPY_BCOPY(context->state, ldns_sha256_initial_hash_value, LDNS_SHA256_DIGEST_LENGTH);
+	MEMSET_BZERO(context->buffer, LDNS_SHA256_BLOCK_LENGTH);
+	context->bitcount = 0;
+}
+
+#ifdef SHA2_UNROLL_TRANSFORM
+
+/* Unrolled SHA-256 round macros: */
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+
+#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h)	\
+	REVERSE32(*data++, W256[j]); \
+	T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \
+             K256[j] + W256[j]; \
+	(d) += T1; \
+	(h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
+	j++
+
+
+#else /* BYTE_ORDER == LITTLE_ENDIAN */
+
+#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h)	\
+	T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \
+	     K256[j] + (W256[j] = *data++); \
+	(d) += T1; \
+	(h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
+	j++
+
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+
+#define ROUND256(a,b,c,d,e,f,g,h)	\
+	s0 = W256[(j+1)&0x0f]; \
+	s0 = sigma0_256(s0); \
+	s1 = W256[(j+14)&0x0f]; \
+	s1 = sigma1_256(s1); \
+	T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + K256[j] + \
+	     (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \
+	(d) += T1; \
+	(h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
+	j++
+
+static void ldns_sha256_Transform(ldns_sha256_CTX* context,
+                                  const sha2_word32* data) {
+	sha2_word32	a, b, c, d, e, f, g, h, s0, s1;
+	sha2_word32	T1, *W256;
+	int		j;
+
+	W256 = (sha2_word32*)context->buffer;
+
+	/* initialize registers with the prev. intermediate value */
+	a = context->state[0];
+	b = context->state[1];
+	c = context->state[2];
+	d = context->state[3];
+	e = context->state[4];
+	f = context->state[5];
+	g = context->state[6];
+	h = context->state[7];
+
+	j = 0;
+	do {
+		/* Rounds 0 to 15 (unrolled): */
+		ROUND256_0_TO_15(a,b,c,d,e,f,g,h);
+		ROUND256_0_TO_15(h,a,b,c,d,e,f,g);
+		ROUND256_0_TO_15(g,h,a,b,c,d,e,f);
+		ROUND256_0_TO_15(f,g,h,a,b,c,d,e);
+		ROUND256_0_TO_15(e,f,g,h,a,b,c,d);
+		ROUND256_0_TO_15(d,e,f,g,h,a,b,c);
+		ROUND256_0_TO_15(c,d,e,f,g,h,a,b);
+		ROUND256_0_TO_15(b,c,d,e,f,g,h,a);
+	} while (j < 16);
+
+	/* Now for the remaining rounds to 64: */
+	do {
+		ROUND256(a,b,c,d,e,f,g,h);
+		ROUND256(h,a,b,c,d,e,f,g);
+		ROUND256(g,h,a,b,c,d,e,f);
+		ROUND256(f,g,h,a,b,c,d,e);
+		ROUND256(e,f,g,h,a,b,c,d);
+		ROUND256(d,e,f,g,h,a,b,c);
+		ROUND256(c,d,e,f,g,h,a,b);
+		ROUND256(b,c,d,e,f,g,h,a);
+	} while (j < 64);
+
+	/* Compute the current intermediate hash value */
+	context->state[0] += a;
+	context->state[1] += b;
+	context->state[2] += c;
+	context->state[3] += d;
+	context->state[4] += e;
+	context->state[5] += f;
+	context->state[6] += g;
+	context->state[7] += h;
+
+	/* Clean up */
+	a = b = c = d = e = f = g = h = T1 = 0;
+}
+
+#else /* SHA2_UNROLL_TRANSFORM */
+
+static void ldns_sha256_Transform(ldns_sha256_CTX* context,
+                                  const sha2_word32* data) {
+	sha2_word32	a, b, c, d, e, f, g, h, s0, s1;
+	sha2_word32	T1, T2, *W256;
+	int		j;
+
+	W256 = (sha2_word32*)context->buffer;
+
+	/* initialize registers with the prev. intermediate value */
+	a = context->state[0];
+	b = context->state[1];
+	c = context->state[2];
+	d = context->state[3];
+	e = context->state[4];
+	f = context->state[5];
+	g = context->state[6];
+	h = context->state[7];
+
+	j = 0;
+	do {
+#if BYTE_ORDER == LITTLE_ENDIAN
+		/* Copy data while converting to host byte order */
+		REVERSE32(*data++,W256[j]);
+		/* Apply the SHA-256 compression function to update a..h */
+		T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j];
+#else /* BYTE_ORDER == LITTLE_ENDIAN */
+		/* Apply the SHA-256 compression function to update a..h with copy */
+		T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + (W256[j] = *data++);
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+		T2 = Sigma0_256(a) + Maj(a, b, c);
+		h = g;
+		g = f;
+		f = e;
+		e = d + T1;
+		d = c;
+		c = b;
+		b = a;
+		a = T1 + T2;
+
+		j++;
+	} while (j < 16);
+
+	do {
+		/* Part of the message block expansion: */
+		s0 = W256[(j+1)&0x0f];
+		s0 = sigma0_256(s0);
+		s1 = W256[(j+14)&0x0f];	
+		s1 = sigma1_256(s1);
+
+		/* Apply the SHA-256 compression function to update a..h */
+		T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + 
+		     (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0);
+		T2 = Sigma0_256(a) + Maj(a, b, c);
+		h = g;
+		g = f;
+		f = e;
+		e = d + T1;
+		d = c;
+		c = b;
+		b = a;
+		a = T1 + T2;
+
+		j++;
+	} while (j < 64);
+
+	/* Compute the current intermediate hash value */
+	context->state[0] += a;
+	context->state[1] += b;
+	context->state[2] += c;
+	context->state[3] += d;
+	context->state[4] += e;
+	context->state[5] += f;
+	context->state[6] += g;
+	context->state[7] += h;
+
+	/* Clean up */
+	a = b = c = d = e = f = g = h = T1 = T2 = 0;
+}
+
+#endif /* SHA2_UNROLL_TRANSFORM */
+
+void ldns_sha256_update(ldns_sha256_CTX* context, const sha2_byte *data, size_t len) {
+	size_t freespace, usedspace;
+
+	if (len == 0) {
+		/* Calling with no data is valid - we do nothing */
+		return;
+	}
+
+	/* Sanity check: */
+	assert(context != (ldns_sha256_CTX*)0 && data != (sha2_byte*)0);
+
+	usedspace = (context->bitcount >> 3) % LDNS_SHA256_BLOCK_LENGTH;
+	if (usedspace > 0) {
+		/* Calculate how much free space is available in the buffer */
+		freespace = LDNS_SHA256_BLOCK_LENGTH - usedspace;
+
+		if (len >= freespace) {
+			/* Fill the buffer completely and process it */
+			MEMCPY_BCOPY(&context->buffer[usedspace], data, freespace);
+			context->bitcount += freespace << 3;
+			len -= freespace;
+			data += freespace;
+			ldns_sha256_Transform(context, (sha2_word32*)context->buffer);
+		} else {
+			/* The buffer is not yet full */
+			MEMCPY_BCOPY(&context->buffer[usedspace], data, len);
+			context->bitcount += len << 3;
+			/* Clean up: */
+			usedspace = freespace = 0;
+			return;
+		}
+	}
+	while (len >= LDNS_SHA256_BLOCK_LENGTH) {
+		/* Process as many complete blocks as we can */
+		ldns_sha256_Transform(context, (sha2_word32*)data);
+		context->bitcount += LDNS_SHA256_BLOCK_LENGTH << 3;
+		len -= LDNS_SHA256_BLOCK_LENGTH;
+		data += LDNS_SHA256_BLOCK_LENGTH;
+	}
+	if (len > 0) {
+		/* There's left-overs, so save 'em */
+		MEMCPY_BCOPY(context->buffer, data, len);
+		context->bitcount += len << 3;
+	}
+	/* Clean up: */
+	usedspace = freespace = 0;
+}
+
+void ldns_sha256_final(sha2_byte digest[], ldns_sha256_CTX* context) {
+	sha2_word32	*d = (sha2_word32*)digest;
+	size_t usedspace;
+
+	/* Sanity check: */
+	assert(context != (ldns_sha256_CTX*)0);
+
+	/* If no digest buffer is passed, we don't bother doing this: */
+	if (digest != (sha2_byte*)0) {
+		usedspace = (context->bitcount >> 3) % LDNS_SHA256_BLOCK_LENGTH;
+#if BYTE_ORDER == LITTLE_ENDIAN
+		/* Convert FROM host byte order */
+		REVERSE64(context->bitcount,context->bitcount);
+#endif
+		if (usedspace > 0) {
+			/* Begin padding with a 1 bit: */
+			context->buffer[usedspace++] = 0x80;
+
+			if (usedspace <= ldns_sha256_SHORT_BLOCK_LENGTH) {
+				/* Set-up for the last transform: */
+				MEMSET_BZERO(&context->buffer[usedspace], ldns_sha256_SHORT_BLOCK_LENGTH - usedspace);
+			} else {
+				if (usedspace < LDNS_SHA256_BLOCK_LENGTH) {
+					MEMSET_BZERO(&context->buffer[usedspace], LDNS_SHA256_BLOCK_LENGTH - usedspace);
+				}
+				/* Do second-to-last transform: */
+				ldns_sha256_Transform(context, (sha2_word32*)context->buffer);
+
+				/* And set-up for the last transform: */
+				MEMSET_BZERO(context->buffer, ldns_sha256_SHORT_BLOCK_LENGTH);
+			}
+		} else {
+			/* Set-up for the last transform: */
+			MEMSET_BZERO(context->buffer, ldns_sha256_SHORT_BLOCK_LENGTH);
+
+			/* Begin padding with a 1 bit: */
+			*context->buffer = 0x80;
+		}
+		/* Set the bit count: */
+		*(sha2_word64*)&context->buffer[ldns_sha256_SHORT_BLOCK_LENGTH] = context->bitcount;
+
+		/* final transform: */
+		ldns_sha256_Transform(context, (sha2_word32*)context->buffer);
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+		{
+			/* Convert TO host byte order */
+			int	j;
+			for (j = 0; j < 8; j++) {
+				REVERSE32(context->state[j],context->state[j]);
+				*d++ = context->state[j];
+			}
+		}
+#else
+		MEMCPY_BCOPY(d, context->state, LDNS_SHA256_DIGEST_LENGTH);
+#endif
+	}
+
+	/* Clean up state data: */
+	MEMSET_BZERO(context, sizeof(ldns_sha256_CTX));
+	usedspace = 0;
+}
+
+unsigned char *
+ldns_sha256(unsigned char *data, unsigned int data_len, unsigned char *digest)
+{
+    ldns_sha256_CTX ctx;
+    ldns_sha256_init(&ctx);
+    ldns_sha256_update(&ctx, data, data_len);
+    ldns_sha256_final(digest, &ctx);
+    return digest;
+}
+
+/*** SHA-512: *********************************************************/
+void ldns_sha512_init(ldns_sha512_CTX* context) {
+	if (context == (ldns_sha512_CTX*)0) {
+		return;
+	}
+	MEMCPY_BCOPY(context->state, sha512_initial_hash_value, LDNS_SHA512_DIGEST_LENGTH);
+	MEMSET_BZERO(context->buffer, LDNS_SHA512_BLOCK_LENGTH);
+	context->bitcount[0] = context->bitcount[1] =  0;
+}
+
+#ifdef SHA2_UNROLL_TRANSFORM
+
+/* Unrolled SHA-512 round macros: */
+#if BYTE_ORDER == LITTLE_ENDIAN
+
+#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h)	\
+	REVERSE64(*data++, W512[j]); \
+	T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \
+             K512[j] + W512[j]; \
+	(d) += T1, \
+	(h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)), \
+	j++
+
+
+#else /* BYTE_ORDER == LITTLE_ENDIAN */
+
+#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h)	\
+	T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \
+             K512[j] + (W512[j] = *data++); \
+	(d) += T1; \
+	(h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \
+	j++
+
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+
+#define ROUND512(a,b,c,d,e,f,g,h)	\
+	s0 = W512[(j+1)&0x0f]; \
+	s0 = sigma0_512(s0); \
+	s1 = W512[(j+14)&0x0f]; \
+	s1 = sigma1_512(s1); \
+	T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + K512[j] + \
+             (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); \
+	(d) += T1; \
+	(h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \
+	j++
+
+static void ldns_sha512_Transform(ldns_sha512_CTX* context,
+                                  const sha2_word64* data) {
+	sha2_word64	a, b, c, d, e, f, g, h, s0, s1;
+	sha2_word64	T1, *W512 = (sha2_word64*)context->buffer;
+	int		j;
+
+	/* initialize registers with the prev. intermediate value */
+	a = context->state[0];
+	b = context->state[1];
+	c = context->state[2];
+	d = context->state[3];
+	e = context->state[4];
+	f = context->state[5];
+	g = context->state[6];
+	h = context->state[7];
+
+	j = 0;
+	do {
+		ROUND512_0_TO_15(a,b,c,d,e,f,g,h);
+		ROUND512_0_TO_15(h,a,b,c,d,e,f,g);
+		ROUND512_0_TO_15(g,h,a,b,c,d,e,f);
+		ROUND512_0_TO_15(f,g,h,a,b,c,d,e);
+		ROUND512_0_TO_15(e,f,g,h,a,b,c,d);
+		ROUND512_0_TO_15(d,e,f,g,h,a,b,c);
+		ROUND512_0_TO_15(c,d,e,f,g,h,a,b);
+		ROUND512_0_TO_15(b,c,d,e,f,g,h,a);
+	} while (j < 16);
+
+	/* Now for the remaining rounds up to 79: */
+	do {
+		ROUND512(a,b,c,d,e,f,g,h);
+		ROUND512(h,a,b,c,d,e,f,g);
+		ROUND512(g,h,a,b,c,d,e,f);
+		ROUND512(f,g,h,a,b,c,d,e);
+		ROUND512(e,f,g,h,a,b,c,d);
+		ROUND512(d,e,f,g,h,a,b,c);
+		ROUND512(c,d,e,f,g,h,a,b);
+		ROUND512(b,c,d,e,f,g,h,a);
+	} while (j < 80);
+
+	/* Compute the current intermediate hash value */
+	context->state[0] += a;
+	context->state[1] += b;
+	context->state[2] += c;
+	context->state[3] += d;
+	context->state[4] += e;
+	context->state[5] += f;
+	context->state[6] += g;
+	context->state[7] += h;
+
+	/* Clean up */
+	a = b = c = d = e = f = g = h = T1 = 0;
+}
+
+#else /* SHA2_UNROLL_TRANSFORM */
+
+static void ldns_sha512_Transform(ldns_sha512_CTX* context,
+                                  const sha2_word64* data) {
+	sha2_word64	a, b, c, d, e, f, g, h, s0, s1;
+	sha2_word64	T1, T2, *W512 = (sha2_word64*)context->buffer;
+	int		j;
+
+	/* initialize registers with the prev. intermediate value */
+	a = context->state[0];
+	b = context->state[1];
+	c = context->state[2];
+	d = context->state[3];
+	e = context->state[4];
+	f = context->state[5];
+	g = context->state[6];
+	h = context->state[7];
+
+	j = 0;
+	do {
+#if BYTE_ORDER == LITTLE_ENDIAN
+		/* Convert TO host byte order */
+		REVERSE64(*data++, W512[j]);
+		/* Apply the SHA-512 compression function to update a..h */
+		T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + W512[j];
+#else /* BYTE_ORDER == LITTLE_ENDIAN */
+		/* Apply the SHA-512 compression function to update a..h with copy */
+		T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + (W512[j] = *data++);
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+		T2 = Sigma0_512(a) + Maj(a, b, c);
+		h = g;
+		g = f;
+		f = e;
+		e = d + T1;
+		d = c;
+		c = b;
+		b = a;
+		a = T1 + T2;
+
+		j++;
+	} while (j < 16);
+
+	do {
+		/* Part of the message block expansion: */
+		s0 = W512[(j+1)&0x0f];
+		s0 = sigma0_512(s0);
+		s1 = W512[(j+14)&0x0f];
+		s1 =  sigma1_512(s1);
+
+		/* Apply the SHA-512 compression function to update a..h */
+		T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] +
+		     (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0);
+		T2 = Sigma0_512(a) + Maj(a, b, c);
+		h = g;
+		g = f;
+		f = e;
+		e = d + T1;
+		d = c;
+		c = b;
+		b = a;
+		a = T1 + T2;
+
+		j++;
+	} while (j < 80);
+
+	/* Compute the current intermediate hash value */
+	context->state[0] += a;
+	context->state[1] += b;
+	context->state[2] += c;
+	context->state[3] += d;
+	context->state[4] += e;
+	context->state[5] += f;
+	context->state[6] += g;
+	context->state[7] += h;
+
+	/* Clean up */
+	a = b = c = d = e = f = g = h = T1 = T2 = 0;
+}
+
+#endif /* SHA2_UNROLL_TRANSFORM */
+
+void ldns_sha512_update(ldns_sha512_CTX* context, const sha2_byte *data, size_t len) {
+	size_t freespace, usedspace;
+
+	if (len == 0) {
+		/* Calling with no data is valid - we do nothing */
+		return;
+	}
+
+	/* Sanity check: */
+	assert(context != (ldns_sha512_CTX*)0 && data != (sha2_byte*)0);
+
+	usedspace = (context->bitcount[0] >> 3) % LDNS_SHA512_BLOCK_LENGTH;
+	if (usedspace > 0) {
+		/* Calculate how much free space is available in the buffer */
+		freespace = LDNS_SHA512_BLOCK_LENGTH - usedspace;
+
+		if (len >= freespace) {
+			/* Fill the buffer completely and process it */
+			MEMCPY_BCOPY(&context->buffer[usedspace], data, freespace);
+			ADDINC128(context->bitcount, freespace << 3);
+			len -= freespace;
+			data += freespace;
+			ldns_sha512_Transform(context, (sha2_word64*)context->buffer);
+		} else {
+			/* The buffer is not yet full */
+			MEMCPY_BCOPY(&context->buffer[usedspace], data, len);
+			ADDINC128(context->bitcount, len << 3);
+			/* Clean up: */
+			usedspace = freespace = 0;
+			return;
+		}
+	}
+	while (len >= LDNS_SHA512_BLOCK_LENGTH) {
+		/* Process as many complete blocks as we can */
+		ldns_sha512_Transform(context, (sha2_word64*)data);
+		ADDINC128(context->bitcount, LDNS_SHA512_BLOCK_LENGTH << 3);
+		len -= LDNS_SHA512_BLOCK_LENGTH;
+		data += LDNS_SHA512_BLOCK_LENGTH;
+	}
+	if (len > 0) {
+		/* There's left-overs, so save 'em */
+		MEMCPY_BCOPY(context->buffer, data, len);
+		ADDINC128(context->bitcount, len << 3);
+	}
+	/* Clean up: */
+	usedspace = freespace = 0;
+}
+
+static void ldns_sha512_Last(ldns_sha512_CTX* context) {
+	size_t usedspace;
+
+	usedspace = (context->bitcount[0] >> 3) % LDNS_SHA512_BLOCK_LENGTH;
+#if BYTE_ORDER == LITTLE_ENDIAN
+	/* Convert FROM host byte order */
+	REVERSE64(context->bitcount[0],context->bitcount[0]);
+	REVERSE64(context->bitcount[1],context->bitcount[1]);
+#endif
+	if (usedspace > 0) {
+		/* Begin padding with a 1 bit: */
+		context->buffer[usedspace++] = 0x80;
+
+		if (usedspace <= ldns_sha512_SHORT_BLOCK_LENGTH) {
+			/* Set-up for the last transform: */
+			MEMSET_BZERO(&context->buffer[usedspace], ldns_sha512_SHORT_BLOCK_LENGTH - usedspace);
+		} else {
+			if (usedspace < LDNS_SHA512_BLOCK_LENGTH) {
+				MEMSET_BZERO(&context->buffer[usedspace], LDNS_SHA512_BLOCK_LENGTH - usedspace);
+			}
+			/* Do second-to-last transform: */
+			ldns_sha512_Transform(context, (sha2_word64*)context->buffer);
+
+			/* And set-up for the last transform: */
+			MEMSET_BZERO(context->buffer, LDNS_SHA512_BLOCK_LENGTH - 2);
+		}
+	} else {
+		/* Prepare for final transform: */
+		MEMSET_BZERO(context->buffer, ldns_sha512_SHORT_BLOCK_LENGTH);
+
+		/* Begin padding with a 1 bit: */
+		*context->buffer = 0x80;
+	}
+	/* Store the length of input data (in bits): */
+	*(sha2_word64*)&context->buffer[ldns_sha512_SHORT_BLOCK_LENGTH] = context->bitcount[1];
+	*(sha2_word64*)&context->buffer[ldns_sha512_SHORT_BLOCK_LENGTH+8] = context->bitcount[0];
+
+	/* final transform: */
+	ldns_sha512_Transform(context, (sha2_word64*)context->buffer);
+}
+
+void ldns_sha512_final(sha2_byte digest[], ldns_sha512_CTX* context) {
+	sha2_word64	*d = (sha2_word64*)digest;
+
+	/* Sanity check: */
+	assert(context != (ldns_sha512_CTX*)0);
+
+	/* If no digest buffer is passed, we don't bother doing this: */
+	if (digest != (sha2_byte*)0) {
+		ldns_sha512_Last(context);
+
+		/* Save the hash data for output: */
+#if BYTE_ORDER == LITTLE_ENDIAN
+		{
+			/* Convert TO host byte order */
+			int	j;
+			for (j = 0; j < 8; j++) {
+				REVERSE64(context->state[j],context->state[j]);
+				*d++ = context->state[j];
+			}
+		}
+#else
+		MEMCPY_BCOPY(d, context->state, LDNS_SHA512_DIGEST_LENGTH);
+#endif
+	}
+
+	/* Zero out state data */
+	MEMSET_BZERO(context, sizeof(ldns_sha512_CTX));
+}
+
+unsigned char *
+ldns_sha512(unsigned char *data, unsigned int data_len, unsigned char *digest)
+{
+    ldns_sha512_CTX ctx;
+    ldns_sha512_init(&ctx);
+    ldns_sha512_update(&ctx, data, data_len);
+    ldns_sha512_final(digest, &ctx);
+    return digest;
+}
+
+/*** SHA-384: *********************************************************/
+void ldns_sha384_init(ldns_sha384_CTX* context) {
+	if (context == (ldns_sha384_CTX*)0) {
+		return;
+	}
+	MEMCPY_BCOPY(context->state, sha384_initial_hash_value, LDNS_SHA512_DIGEST_LENGTH);
+	MEMSET_BZERO(context->buffer, LDNS_SHA384_BLOCK_LENGTH);
+	context->bitcount[0] = context->bitcount[1] = 0;
+}
+
+void ldns_sha384_update(ldns_sha384_CTX* context, const sha2_byte* data, size_t len) {
+	ldns_sha512_update((ldns_sha512_CTX*)context, data, len);
+}
+
+void ldns_sha384_final(sha2_byte digest[], ldns_sha384_CTX* context) {
+	sha2_word64	*d = (sha2_word64*)digest;
+
+	/* Sanity check: */
+	assert(context != (ldns_sha384_CTX*)0);
+
+	/* If no digest buffer is passed, we don't bother doing this: */
+	if (digest != (sha2_byte*)0) {
+		ldns_sha512_Last((ldns_sha512_CTX*)context);
+
+		/* Save the hash data for output: */
+#if BYTE_ORDER == LITTLE_ENDIAN
+		{
+			/* Convert TO host byte order */
+			int	j;
+			for (j = 0; j < 6; j++) {
+				REVERSE64(context->state[j],context->state[j]);
+				*d++ = context->state[j];
+			}
+		}
+#else
+		MEMCPY_BCOPY(d, context->state, LDNS_SHA384_DIGEST_LENGTH);
+#endif
+	}
+
+	/* Zero out state data */
+	MEMSET_BZERO(context, sizeof(ldns_sha384_CTX));
+}
+
+unsigned char *
+ldns_sha384(unsigned char *data, unsigned int data_len, unsigned char *digest)
+{
+    ldns_sha384_CTX ctx;
+    ldns_sha384_init(&ctx);
+    ldns_sha384_update(&ctx, data, data_len);
+    ldns_sha384_final(digest, &ctx);
+    return digest;
+}
diff --git a/3rdParty/Ldns/src/src/str2host.c b/3rdParty/Ldns/src/src/str2host.c
new file mode 100644
index 0000000..4ec9d37
--- /dev/null
+++ b/3rdParty/Ldns/src/src/str2host.c
@@ -0,0 +1,1316 @@
+/*
+ * str2host.c
+ *
+ * conversion routines from the presentation format
+ * to the host format
+ *
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2004-2006
+ *
+ * See the file LICENSE for the license
+ */
+#include <ldns/config.h>
+
+#include <ldns/ldns.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#include <time.h>
+
+#include <errno.h>
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#include <limits.h>
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+ldns_status
+ldns_str2rdf_int16(ldns_rdf **rd, const char *shortstr)
+{
+	char *end = NULL;
+	uint16_t *r;
+	r = LDNS_MALLOC(uint16_t);
+        if(!r) return LDNS_STATUS_MEM_ERR;
+
+	*r = htons((uint16_t)strtol((char *)shortstr, &end, 10));
+
+	if(*end != 0) {
+		LDNS_FREE(r);
+		return LDNS_STATUS_INVALID_INT;
+	} else {
+		*rd = ldns_rdf_new_frm_data(
+			LDNS_RDF_TYPE_INT16, sizeof(uint16_t), r);
+		LDNS_FREE(r);
+		return *rd?LDNS_STATUS_OK:LDNS_STATUS_MEM_ERR;
+	}
+}
+
+ldns_status
+ldns_str2rdf_time(ldns_rdf **rd, const char *time)
+{
+	/* convert a time YYYYDDMMHHMMSS to wireformat */
+	uint16_t *r = NULL;
+	struct tm tm;
+	uint32_t l;
+	char *end;
+
+	/* Try to scan the time... */
+	r = (uint16_t*)LDNS_MALLOC(uint32_t);
+        if(!r) return LDNS_STATUS_MEM_ERR;
+
+	memset(&tm, 0, sizeof(tm));
+
+	if (strlen(time) == 14 &&
+	    sscanf(time, "%4d%2d%2d%2d%2d%2d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6
+	   ) {
+	   	tm.tm_year -= 1900;
+	   	tm.tm_mon--;
+	   	/* Check values */
+		if (tm.tm_year < 70) {
+			goto bad_format;
+		}
+		if (tm.tm_mon < 0 || tm.tm_mon > 11) {
+			goto bad_format;
+		}
+		if (tm.tm_mday < 1 || tm.tm_mday > 31) {
+			goto bad_format;
+		}
+
+		if (tm.tm_hour < 0 || tm.tm_hour > 23) {
+			goto bad_format;
+		}
+
+		if (tm.tm_min < 0 || tm.tm_min > 59) {
+			goto bad_format;
+		}
+
+		if (tm.tm_sec < 0 || tm.tm_sec > 59) {
+			goto bad_format;
+		}
+
+		l = htonl(mktime_from_utc(&tm));
+		memcpy(r, &l, sizeof(uint32_t));
+		*rd = ldns_rdf_new_frm_data(
+			LDNS_RDF_TYPE_TIME, sizeof(uint32_t), r);
+		LDNS_FREE(r);
+		return *rd?LDNS_STATUS_OK:LDNS_STATUS_MEM_ERR;
+	} else {
+		/* handle it as 32 bits timestamp */
+		l = htonl((uint32_t)strtol((char*)time, &end, 10));
+		if(*end != 0) {
+			LDNS_FREE(r);
+			return LDNS_STATUS_ERR;
+		} else {
+			memcpy(r, &l, sizeof(uint32_t));
+			*rd = ldns_rdf_new_frm_data(
+				LDNS_RDF_TYPE_INT32, sizeof(uint32_t), r);
+			LDNS_FREE(r);
+		        return *rd?LDNS_STATUS_OK:LDNS_STATUS_MEM_ERR;
+		}
+	}
+
+	bad_format:
+	LDNS_FREE(r);
+	return LDNS_STATUS_INVALID_TIME;
+}
+
+ldns_status
+ldns_str2rdf_nsec3_salt(ldns_rdf **rd, const char *salt_str)
+{
+	uint8_t salt_length;
+	int c;
+	int salt_length_str;
+
+	uint8_t *salt;
+	uint8_t *data;
+	if(rd == NULL) {
+		return LDNS_STATUS_NULL;
+	}
+
+	salt_length_str = (int)strlen(salt_str);
+	if (salt_length_str == 1 && salt_str[0] == '-') {
+		salt_length_str = 0;
+	} else if (salt_length_str % 2 != 0) {
+		return LDNS_STATUS_INVALID_HEX;
+	}
+	if (salt_length_str > 512) {
+		return LDNS_STATUS_INVALID_HEX;
+	}
+
+	salt = LDNS_XMALLOC(uint8_t, salt_length_str / 2);
+        if(!salt) {
+                return LDNS_STATUS_MEM_ERR;
+        }
+	for (c = 0; c < salt_length_str; c += 2) {
+		if (isxdigit((int) salt_str[c]) && isxdigit((int) salt_str[c+1])) {
+			salt[c/2] = (uint8_t) ldns_hexdigit_to_int(salt_str[c]) * 16 +
+					  ldns_hexdigit_to_int(salt_str[c+1]);
+		} else {
+			LDNS_FREE(salt);
+			return LDNS_STATUS_INVALID_HEX;
+		}
+	}
+	salt_length = (uint8_t) (salt_length_str / 2);
+
+	data = LDNS_XMALLOC(uint8_t, 1 + salt_length);
+        if(!data) {
+	        LDNS_FREE(salt);
+                return LDNS_STATUS_MEM_ERR;
+        }
+	data[0] = salt_length;
+	memcpy(&data[1], salt, salt_length);
+	*rd = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_NSEC3_SALT, 1 + salt_length, data);
+	LDNS_FREE(data);
+	LDNS_FREE(salt);
+
+	return *rd?LDNS_STATUS_OK:LDNS_STATUS_MEM_ERR;
+}
+
+ldns_status
+ldns_str2rdf_period(ldns_rdf **rd,const char *period)
+{
+        uint32_t p;
+        const char *end;
+
+        /* Allocate required space... */
+        p = ldns_str2period(period, &end);
+
+        if (*end != 0) {
+		return LDNS_STATUS_ERR;
+        } else {
+                p = (uint32_t) htonl(p);
+		*rd = ldns_rdf_new_frm_data(
+			LDNS_RDF_TYPE_PERIOD, sizeof(uint32_t), &p);
+        }
+	return *rd?LDNS_STATUS_OK:LDNS_STATUS_MEM_ERR;
+}
+
+ldns_status
+ldns_str2rdf_int32(ldns_rdf **rd, const char *longstr)
+{
+	char *end;
+	uint16_t *r = NULL;
+	uint32_t l;
+
+	r = (uint16_t*)LDNS_MALLOC(uint32_t);
+        if(!r) return LDNS_STATUS_MEM_ERR;
+	errno = 0; /* must set to zero before call,
+			note race condition on errno */
+	if(*longstr == '-')
+		l = htonl((uint32_t)strtol((char*)longstr, &end, 10));
+	else	l = htonl((uint32_t)strtoul((char*)longstr, &end, 10));
+
+	if(*end != 0) {
+		LDNS_FREE(r);
+		return LDNS_STATUS_ERR;
+     } else {
+		if (errno == ERANGE) {
+			LDNS_FREE(r);
+			return LDNS_STATUS_SYNTAX_INTEGER_OVERFLOW;
+		}
+		memcpy(r, &l, sizeof(uint32_t));
+		*rd = ldns_rdf_new_frm_data(
+			LDNS_RDF_TYPE_INT32, sizeof(uint32_t), r);
+		LDNS_FREE(r);
+	        return *rd?LDNS_STATUS_OK:LDNS_STATUS_MEM_ERR;
+	}
+}
+
+ldns_status
+ldns_str2rdf_int8(ldns_rdf **rd, const char *bytestr)
+{
+	char *end;
+	uint8_t *r = NULL;
+
+	r = LDNS_MALLOC(uint8_t);
+        if(!r) return LDNS_STATUS_MEM_ERR;
+
+	*r = (uint8_t)strtol((char*)bytestr, &end, 10);
+
+        if(*end != 0) {
+		LDNS_FREE(r);
+		return LDNS_STATUS_ERR;
+        } else {
+		*rd = ldns_rdf_new_frm_data(
+			LDNS_RDF_TYPE_INT8, sizeof(uint8_t), r);
+		LDNS_FREE(r);
+	        return *rd?LDNS_STATUS_OK:LDNS_STATUS_MEM_ERR;
+        }
+}
+
+
+/*
+ * Checks whether the escaped value at **s is an octal value or
+ * a 'normally' escaped character (and not eos)
+ *
+ * The string pointer at *s is increased by either 0 (on error), 1 (on
+ * normal escapes), or 3 (on octals)
+ *
+ * Returns the number of bytes read from the escaped string, or
+ * 0 on error
+ */
+static int
+parse_escape(uint8_t *s, uint8_t *q) {
+	uint8_t val;
+	if (strlen((char *)s) > 3 &&
+	    isdigit((int) s[1]) &&
+	    isdigit((int) s[2]) &&
+	    isdigit((int) s[3])) {
+		/* cast this so it fits */
+		val = (uint8_t) ldns_hexdigit_to_int((char) s[1]) * 100 +
+		                ldns_hexdigit_to_int((char) s[2]) * 10 +
+		                ldns_hexdigit_to_int((char) s[3]);
+		*q = val;
+		return 3;
+	} else {
+		s++;
+		if (*s == '\0' || isdigit((int) *s)) {
+			/* apparently the string terminator
+			 * or a digit has been escaped...
+		         */
+			return 0;
+		}
+		*q = *s;
+		return 1;
+	}
+}
+
+/*
+ * No special care is taken, all dots are translated into
+ * label seperators.
+ * Could be made more efficient....we do 3 memcpy's in total...
+ */
+ldns_status
+ldns_str2rdf_dname(ldns_rdf **d, const char *str)
+{
+	size_t len;
+
+	int esc;
+	uint8_t *s, *q, *pq, label_len;
+	uint8_t buf[LDNS_MAX_DOMAINLEN + 1];
+	*d = NULL;
+
+	len = strlen((char*)str);
+	/* octet representation can make strings a lot longer than actual length */
+	if (len > LDNS_MAX_DOMAINLEN * 4) {
+		return LDNS_STATUS_DOMAINNAME_OVERFLOW;
+	}
+	if (0 == len) {
+		return LDNS_STATUS_DOMAINNAME_UNDERFLOW;
+	}
+
+	/* root label */
+	if (1 == len && *str == '.') {
+		*d = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_DNAME, 1, "\0");
+		return LDNS_STATUS_OK;
+	}
+
+	/* get on with the rest */
+
+	/* s is on the current character in the string
+         * pq points to where the labellength is going to go
+         * label_len keeps track of the current label's length
+	 * q builds the dname inside the buf array
+	 */
+	len = 0;
+	q = buf+1;
+	pq = buf;
+	label_len = 0;
+	for (s = (uint8_t *)str; *s; s++, q++) {
+		if (q > buf + LDNS_MAX_DOMAINLEN) {
+			return LDNS_STATUS_DOMAINNAME_OVERFLOW;
+		}
+		*q = 0;
+		switch (*s) {
+		case '.':
+			if (label_len > LDNS_MAX_LABELLEN) {
+				return LDNS_STATUS_LABEL_OVERFLOW;
+			}
+			if (label_len == 0) {
+				return LDNS_STATUS_EMPTY_LABEL;
+			}
+			len += label_len + 1;
+			*pq = label_len;
+			label_len = 0;
+			pq = q;
+			break;
+		case '\\':
+			/* octet value or literal char */
+			esc = parse_escape(s, q);
+			if (esc > 0) {
+				s += esc;
+				label_len++;
+			} else {
+				return LDNS_STATUS_SYNTAX_BAD_ESCAPE;
+			}
+			break;
+		default:
+			*q = *s;
+			label_len++;
+		}
+	}
+
+	/* add root label if last char was not '.' */
+	if (!ldns_dname_str_absolute(str)) {
+		if (q > buf + LDNS_MAX_DOMAINLEN) {
+			return LDNS_STATUS_DOMAINNAME_OVERFLOW;
+		}
+                if (label_len > LDNS_MAX_LABELLEN) {
+                        return LDNS_STATUS_LABEL_OVERFLOW;
+                }
+                if (label_len == 0) { /* label_len 0 but not . at end? */
+                        return LDNS_STATUS_EMPTY_LABEL;
+                }
+		len += label_len + 1;
+		*pq = label_len;
+		*q = 0;
+	}
+	len++;
+
+	*d = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_DNAME, len, buf);
+	return LDNS_STATUS_OK;
+}
+
+ldns_status
+ldns_str2rdf_a(ldns_rdf **rd, const char *str)
+{
+	in_addr_t address;
+        if (inet_pton(AF_INET, (char*)str, &address) != 1) {
+                return LDNS_STATUS_INVALID_IP4;
+        } else {
+		*rd = ldns_rdf_new_frm_data(
+			LDNS_RDF_TYPE_A, sizeof(address), &address);
+        }
+	return *rd?LDNS_STATUS_OK:LDNS_STATUS_MEM_ERR;
+}
+
+ldns_status
+ldns_str2rdf_aaaa(ldns_rdf **rd, const char *str)
+{
+	uint8_t address[LDNS_IP6ADDRLEN + 1];
+
+	if (inet_pton(AF_INET6, (char*)str, address) != 1) {
+		return LDNS_STATUS_INVALID_IP6;
+	} else {
+		*rd = ldns_rdf_new_frm_data(
+			LDNS_RDF_TYPE_AAAA, sizeof(address) - 1, &address);
+	}
+	return *rd?LDNS_STATUS_OK:LDNS_STATUS_MEM_ERR;
+}
+
+ldns_status
+ldns_str2rdf_str(ldns_rdf **rd, const char *str)
+{
+	uint8_t *data;
+	size_t i, str_i, esc_i;
+
+	if (strlen(str) > 255) {
+		return LDNS_STATUS_INVALID_STR;
+	}
+
+	data = LDNS_XMALLOC(uint8_t, strlen(str) + 1);
+        if(!data) return LDNS_STATUS_MEM_ERR;
+	i = 1;
+
+	for (str_i = 0; str_i < strlen(str); str_i++) {
+		if (str[str_i] == '\\') {
+			/* octet value or literal char */
+			esc_i = (size_t) parse_escape((uint8_t*) &str[str_i], (uint8_t*) &data[i]);
+			if (esc_i == 0) {
+				LDNS_FREE(data);
+				return LDNS_STATUS_SYNTAX_BAD_ESCAPE;
+			}
+			str_i += esc_i;
+		} else {
+			data[i] = (uint8_t) str[str_i];
+		}
+		i++;
+	}
+	data[0] = i - 1;
+	*rd = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_STR, i, data);
+
+	LDNS_FREE(data);
+	return *rd?LDNS_STATUS_OK:LDNS_STATUS_MEM_ERR;
+}
+
+ldns_status
+ldns_str2rdf_apl(ldns_rdf **rd, const char *str)
+{
+	const char *my_str = str;
+
+	char *my_ip_str;
+	size_t ip_str_len;
+
+	uint16_t family;
+	bool negation;
+	uint8_t afdlength = 0;
+	uint8_t *afdpart;
+	uint8_t prefix;
+
+	uint8_t *data;
+
+	size_t i = 0;
+
+	/* [!]afi:address/prefix */
+	if (strlen(my_str) < 2
+			|| strchr(my_str, ':') == NULL
+			|| strchr(my_str, '/') == NULL
+			|| strchr(my_str, ':') > strchr(my_str, '/')) {
+		return LDNS_STATUS_INVALID_STR;
+	}
+
+	if (my_str[0] == '!') {
+		negation = true;
+		my_str += 1;
+	} else {
+		negation = false;
+	}
+
+	family = (uint16_t) atoi(my_str);
+
+	my_str = strchr(my_str, ':') + 1;
+
+	/* need ip addr and only ip addr for inet_pton */
+	ip_str_len = (size_t) (strchr(my_str, '/') - my_str);
+	my_ip_str = LDNS_XMALLOC(char, ip_str_len + 1);
+        if(!my_ip_str) return LDNS_STATUS_MEM_ERR;
+	strncpy(my_ip_str, my_str, ip_str_len + 1);
+	my_ip_str[ip_str_len] = '\0';
+
+	if (family == 1) {
+		/* ipv4 */
+		afdpart = LDNS_XMALLOC(uint8_t, 4);
+                if(!afdpart) {
+                        LDNS_FREE(my_ip_str);
+                        return LDNS_STATUS_MEM_ERR;
+                }
+		if (inet_pton(AF_INET, my_ip_str, afdpart) == 0) {
+                        LDNS_FREE(my_ip_str);
+                        LDNS_FREE(afdpart);
+			return LDNS_STATUS_INVALID_STR;
+		}
+		for (i = 0; i < 4; i++) {
+			if (afdpart[i] != 0) {
+				afdlength = i + 1;
+			}
+		}
+	} else if (family == 2) {
+		/* ipv6 */
+		afdpart = LDNS_XMALLOC(uint8_t, 16);
+                if(!afdpart) {
+                        LDNS_FREE(my_ip_str);
+                        return LDNS_STATUS_MEM_ERR;
+                }
+		if (inet_pton(AF_INET6, my_ip_str, afdpart) == 0) {
+                        LDNS_FREE(my_ip_str);
+                        LDNS_FREE(afdpart);
+			return LDNS_STATUS_INVALID_STR;
+		}
+		for (i = 0; i < 16; i++) {
+			if (afdpart[i] != 0) {
+				afdlength = i + 1;
+			}
+		}
+	} else {
+		/* unknown family */
+		LDNS_FREE(my_ip_str);
+		return LDNS_STATUS_INVALID_STR;
+	}
+
+	my_str = strchr(my_str, '/') + 1;
+	prefix = (uint8_t) atoi(my_str);
+
+	data = LDNS_XMALLOC(uint8_t, 4 + afdlength);
+        if(!data) {
+		LDNS_FREE(my_ip_str);
+		return LDNS_STATUS_INVALID_STR;
+        }
+	ldns_write_uint16(data, family);
+	data[2] = prefix;
+	data[3] = afdlength;
+	if (negation) {
+		/* set bit 1 of byte 3 */
+		data[3] = data[3] | 0x80;
+	}
+
+	memcpy(data + 4, afdpart, afdlength);
+
+	*rd = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_APL, afdlength + 4, data);
+	LDNS_FREE(afdpart);
+	LDNS_FREE(data);
+	LDNS_FREE(my_ip_str);
+
+	return *rd?LDNS_STATUS_OK:LDNS_STATUS_MEM_ERR;
+}
+
+ldns_status
+ldns_str2rdf_b64(ldns_rdf **rd, const char *str)
+{
+	uint8_t *buffer;
+	int16_t i;
+
+	buffer = LDNS_XMALLOC(uint8_t, ldns_b64_ntop_calculate_size(strlen(str)));
+        if(!buffer) {
+                return LDNS_STATUS_MEM_ERR;
+        }
+
+	i = (uint16_t)ldns_b64_pton((const char*)str, buffer,
+						   ldns_b64_ntop_calculate_size(strlen(str)));
+	if (-1 == i) {
+		LDNS_FREE(buffer);
+		return LDNS_STATUS_INVALID_B64;
+	} else {
+		*rd = ldns_rdf_new_frm_data(
+			LDNS_RDF_TYPE_B64, (uint16_t) i, buffer);
+	}
+	LDNS_FREE(buffer);
+
+	return *rd?LDNS_STATUS_OK:LDNS_STATUS_MEM_ERR;
+}
+
+ldns_status
+ldns_str2rdf_b32_ext(ldns_rdf **rd, const char *str)
+{
+	uint8_t *buffer;
+	int i;
+	/* first byte contains length of actual b32 data */
+	uint8_t len = ldns_b32_pton_calculate_size(strlen(str));
+	buffer = LDNS_XMALLOC(uint8_t, len + 1);
+        if(!buffer) {
+                return LDNS_STATUS_MEM_ERR;
+        }
+	buffer[0] = len;
+
+	i = ldns_b32_pton_extended_hex((const char*)str, strlen(str), buffer + 1,
+							 ldns_b32_ntop_calculate_size(strlen(str)));
+	if (i < 0) {
+                LDNS_FREE(buffer);
+		return LDNS_STATUS_INVALID_B32_EXT;
+	} else {
+		*rd = ldns_rdf_new_frm_data(
+			LDNS_RDF_TYPE_B32_EXT, (uint16_t) i + 1, buffer);
+	}
+	LDNS_FREE(buffer);
+
+	return *rd?LDNS_STATUS_OK:LDNS_STATUS_MEM_ERR;
+}
+
+ldns_status
+ldns_str2rdf_hex(ldns_rdf **rd, const char *str)
+{
+	uint8_t *t, *t_orig;
+	int i;
+	size_t len;
+
+	len = strlen(str);
+
+	if (len > LDNS_MAX_RDFLEN * 2) {
+		return LDNS_STATUS_LABEL_OVERFLOW;
+	} else {
+		t = LDNS_XMALLOC(uint8_t, (len / 2) + 1);
+                if(!t) {
+                        return LDNS_STATUS_MEM_ERR;
+                }
+		t_orig = t;
+		/* Now process octet by octet... */
+		while (*str) {
+			*t = 0;
+			if (isspace((int) *str)) {
+				str++;
+			} else {
+				for (i = 16; i >= 1; i -= 15) {
+					while (*str && isspace((int) *str)) { str++; }
+					if (*str) {
+						if (isxdigit((int) *str)) {
+							*t += ldns_hexdigit_to_int(*str) * i;
+						} else {
+                                                        LDNS_FREE(t_orig);
+							return LDNS_STATUS_ERR;
+						}
+						++str;
+					}
+				}
+				++t;
+			}
+		}
+		*rd = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_HEX,
+		                            (size_t) (t - t_orig),
+		                            t_orig);
+		LDNS_FREE(t_orig);
+	}
+	return *rd?LDNS_STATUS_OK:LDNS_STATUS_MEM_ERR;
+}
+
+ldns_status
+ldns_str2rdf_nsec(ldns_rdf **rd, const char *str)
+{
+	const char *delimiters = "\n\t ";
+	char *token = LDNS_XMALLOC(char, LDNS_MAX_RDFLEN);
+	ldns_buffer *str_buf;
+	ssize_t c;
+	uint16_t cur_type;
+	size_t type_count = 0;
+	ldns_rr_type type_list[65536];
+	if(!token) return LDNS_STATUS_MEM_ERR;
+	if(rd == NULL) {
+		LDNS_FREE(token);
+		return LDNS_STATUS_NULL;
+	}
+
+	str_buf = LDNS_MALLOC(ldns_buffer);
+	if(!str_buf) {
+		LDNS_FREE(token);
+		return LDNS_STATUS_MEM_ERR;
+	}
+	ldns_buffer_new_frm_data(str_buf, (char *)str, strlen(str));
+	if(ldns_buffer_status(str_buf) != LDNS_STATUS_OK) {
+		LDNS_FREE(str_buf);
+		LDNS_FREE(token);
+		return LDNS_STATUS_MEM_ERR;
+	}
+
+	while ((c = ldns_bget_token(str_buf, token, delimiters, LDNS_MAX_RDFLEN)) != -1 && c != 0) {
+                if(type_count >= sizeof(type_list)) {
+		        LDNS_FREE(str_buf);
+		        LDNS_FREE(token);
+                        return LDNS_STATUS_ERR;
+                }
+		cur_type = ldns_get_rr_type_by_name(token);
+		type_list[type_count] = cur_type;
+		type_count++;
+	}
+
+	*rd = ldns_dnssec_create_nsec_bitmap(type_list,
+	                                     type_count,
+	                                     LDNS_RR_TYPE_NSEC);
+
+	LDNS_FREE(token);
+	ldns_buffer_free(str_buf);
+	return *rd?LDNS_STATUS_OK:LDNS_STATUS_MEM_ERR;
+}
+
+ldns_status
+ldns_str2rdf_type(ldns_rdf **rd, const char *str)
+{
+	uint16_t type;
+	type = htons(ldns_get_rr_type_by_name(str));
+	/* ldns_rr_type is a 16 bit value */
+	*rd = ldns_rdf_new_frm_data(
+		LDNS_RDF_TYPE_TYPE, sizeof(uint16_t), &type);
+	return *rd?LDNS_STATUS_OK:LDNS_STATUS_MEM_ERR;
+}
+
+ldns_status
+ldns_str2rdf_class(ldns_rdf **rd, const char *str)
+{
+	uint16_t klass;
+	klass = htons(ldns_get_rr_class_by_name(str));
+	/* class is 16 bit */
+	*rd = ldns_rdf_new_frm_data(
+		LDNS_RDF_TYPE_CLASS, sizeof(uint16_t), &klass);
+	return *rd?LDNS_STATUS_OK:LDNS_STATUS_MEM_ERR;
+}
+
+/* An certificate alg field can either be specified as a 8 bits number
+ * or by its symbolic name. Handle both
+ */
+ldns_status
+ldns_str2rdf_cert_alg(ldns_rdf **rd, const char *str)
+{
+	ldns_lookup_table *lt;
+	ldns_status st;
+	uint8_t idd[2];
+	lt = ldns_lookup_by_name(ldns_cert_algorithms, str);
+	st = LDNS_STATUS_OK;
+
+	if (lt) {
+		ldns_write_uint16(idd, (uint16_t) lt->id);
+		*rd = ldns_rdf_new_frm_data(
+			LDNS_RDF_TYPE_INT16, sizeof(uint16_t), idd);
+		if (!*rd) {
+			st = LDNS_STATUS_ERR;
+		}
+	} else {
+		/* try as-is (a number) */
+		st = ldns_str2rdf_int16(rd, str);
+		if (st == LDNS_STATUS_OK &&
+		    ldns_rdf2native_int16(*rd) == 0) {
+			st = LDNS_STATUS_CERT_BAD_ALGORITHM;
+		}
+	}
+
+	return st;
+}
+
+/* An alg field can either be specified as a 8 bits number
+ * or by its symbolic name. Handle both
+ */
+ldns_status
+ldns_str2rdf_alg(ldns_rdf **rd, const char *str)
+{
+	ldns_lookup_table *lt;
+	ldns_status st;
+
+	lt = ldns_lookup_by_name(ldns_algorithms, str);
+	st = LDNS_STATUS_OK;
+
+	if (lt) {
+		/* it was given as a integer */
+		*rd = ldns_native2rdf_int8(LDNS_RDF_TYPE_INT8, (uint8_t) lt->id);
+		if (!*rd) {
+			st = LDNS_STATUS_ERR;
+		}
+	} else {
+		/* try as-is (a number) */
+		st = ldns_str2rdf_int8(rd, str);
+	}
+	return st;
+}
+
+ldns_status
+ldns_str2rdf_unknown(ldns_rdf **rd, const char *str)
+{
+	/* this should be caught in an earlier time (general str2host for
+	   rr's */
+	rd = rd;
+	str = str;
+	return LDNS_STATUS_NOT_IMPL;
+}
+
+ldns_status
+ldns_str2rdf_tsig(ldns_rdf **rd, const char *str)
+{
+	/* there is no strign representation for TSIG rrs */
+	rd = rd;
+	str = str;
+	return LDNS_STATUS_NOT_IMPL;
+}
+
+ldns_status
+ldns_str2rdf_service(ldns_rdf **rd, const char *str)
+{
+	/* is this used? is this actually WKS? or SRV? */
+	rd = rd;
+	str = str;
+	return LDNS_STATUS_NOT_IMPL;
+}
+
+static int
+loc_parse_cm(char* my_str, char** endstr, uint8_t* m, uint8_t* e)
+{
+	/* read <digits>[.<digits>][mM] */
+	/* into mantissa exponent format for LOC type */
+	uint32_t meters = 0, cm = 0, val;
+	while (isblank(*my_str)) {
+		my_str++;
+	}
+	meters = (uint32_t)strtol(my_str, &my_str, 10);
+	if (*my_str == '.') {
+		my_str++;
+		cm = (uint32_t)strtol(my_str, &my_str, 10);
+	}
+	if (meters >= 1) {
+		*e = 2;
+		val = meters;
+	} else	{
+		*e = 0;
+		val = cm;
+	}
+	while(val >= 10) {
+		(*e)++;
+		val /= 10;
+	}
+	*m = (uint8_t)val;
+
+	if (*e > 9)
+		return 0;
+	if (*my_str == 'm' || *my_str == 'M') {
+		my_str++;
+	}
+	*endstr = my_str;
+	return 1;
+}
+
+ldns_status
+ldns_str2rdf_loc(ldns_rdf **rd, const char *str)
+{
+	uint32_t latitude = 0;
+	uint32_t longitude = 0;
+	uint32_t altitude = 0;
+
+	uint8_t *data;
+	uint32_t equator = (uint32_t) ldns_power(2, 31);
+
+	uint32_t h = 0;
+	uint32_t m = 0;
+	uint8_t size_b = 1, size_e = 2;
+	uint8_t horiz_pre_b = 1, horiz_pre_e = 6;
+	uint8_t vert_pre_b = 1, vert_pre_e = 3;
+
+	double s = 0.0;
+	bool northerness;
+	bool easterness;
+
+	char *my_str = (char *) str;
+
+	/* only support version 0 */
+	if (isdigit((int) *my_str)) {
+		h = (uint32_t) strtol(my_str, &my_str, 10);
+	} else {
+		return LDNS_STATUS_INVALID_STR;
+	}
+
+	while (isblank((int) *my_str)) {
+		my_str++;
+	}
+
+	if (isdigit((int) *my_str)) {
+		m = (uint32_t) strtol(my_str, &my_str, 10);
+	} else if (*my_str == 'N' || *my_str == 'S') {
+		goto north;
+	} else {
+		return LDNS_STATUS_INVALID_STR;
+	}
+
+	while (isblank((int) *my_str)) {
+		my_str++;
+	}
+
+	if (isdigit((int) *my_str)) {
+		s = strtod(my_str, &my_str);
+	}
+north:
+	while (isblank((int) *my_str)) {
+		my_str++;
+	}
+
+	if (*my_str == 'N') {
+		northerness = true;
+	} else if (*my_str == 'S') {
+		northerness = false;
+	} else {
+		return LDNS_STATUS_INVALID_STR;
+	}
+
+	my_str++;
+
+	/* store number */
+	s = 1000.0 * s;
+	/* add a little to make floor in conversion a round */
+	s += 0.0005;
+	latitude = (uint32_t) s;
+	latitude += 1000 * 60 * m;
+	latitude += 1000 * 60 * 60 * h;
+	if (northerness) {
+		latitude = equator + latitude;
+	} else {
+		latitude = equator - latitude;
+	}
+	while (isblank(*my_str)) {
+		my_str++;
+	}
+
+	if (isdigit((int) *my_str)) {
+		h = (uint32_t) strtol(my_str, &my_str, 10);
+	} else {
+		return LDNS_STATUS_INVALID_STR;
+	}
+
+	while (isblank((int) *my_str)) {
+		my_str++;
+	}
+
+	if (isdigit((int) *my_str)) {
+		m = (uint32_t) strtol(my_str, &my_str, 10);
+	} else if (*my_str == 'E' || *my_str == 'W') {
+		goto east;
+	} else {
+		return LDNS_STATUS_INVALID_STR;
+	}
+
+	while (isblank(*my_str)) {
+		my_str++;
+	}
+
+	if (isdigit((int) *my_str)) {
+		s = strtod(my_str, &my_str);
+	}
+
+east:
+	while (isblank(*my_str)) {
+		my_str++;
+	}
+
+	if (*my_str == 'E') {
+		easterness = true;
+	} else if (*my_str == 'W') {
+		easterness = false;
+	} else {
+		return LDNS_STATUS_INVALID_STR;
+	}
+
+	my_str++;
+
+	/* store number */
+	s *= 1000.0;
+	/* add a little to make floor in conversion a round */
+	s += 0.0005;
+	longitude = (uint32_t) s;
+	longitude += 1000 * 60 * m;
+	longitude += 1000 * 60 * 60 * h;
+
+	if (easterness) {
+		longitude += equator;
+	} else {
+		longitude = equator - longitude;
+	}
+
+	altitude = (uint32_t)(strtod(my_str, &my_str)*100.0 +
+		10000000.0 + 0.5);
+	if (*my_str == 'm' || *my_str == 'M') {
+		my_str++;
+	}
+
+	if (strlen(my_str) > 0) {
+		if(!loc_parse_cm(my_str, &my_str, &size_b, &size_e))
+			return LDNS_STATUS_INVALID_STR;
+	}
+
+	if (strlen(my_str) > 0) {
+		if(!loc_parse_cm(my_str, &my_str, &horiz_pre_b, &horiz_pre_e))
+			return LDNS_STATUS_INVALID_STR;
+	}
+
+	if (strlen(my_str) > 0) {
+		if(!loc_parse_cm(my_str, &my_str, &vert_pre_b, &vert_pre_e))
+			return LDNS_STATUS_INVALID_STR;
+	}
+
+	data = LDNS_XMALLOC(uint8_t, 16);
+        if(!data) {
+                return LDNS_STATUS_MEM_ERR;
+        }
+	data[0] = 0;
+	data[1] = 0;
+	data[1] = ((size_b << 4) & 0xf0) | (size_e & 0x0f);
+	data[2] = ((horiz_pre_b << 4) & 0xf0) | (horiz_pre_e & 0x0f);
+	data[3] = ((vert_pre_b << 4) & 0xf0) | (vert_pre_e & 0x0f);
+	ldns_write_uint32(data + 4, latitude);
+	ldns_write_uint32(data + 8, longitude);
+	ldns_write_uint32(data + 12, altitude);
+
+	*rd = ldns_rdf_new_frm_data(
+		LDNS_RDF_TYPE_LOC, 16, data);
+
+	LDNS_FREE(data);
+	return *rd?LDNS_STATUS_OK:LDNS_STATUS_MEM_ERR;
+}
+
+ldns_status
+ldns_str2rdf_wks(ldns_rdf **rd, const char *str)
+{
+	uint8_t *bitmap = NULL;
+	uint8_t *data;
+	int bm_len = 0;
+
+	struct protoent *proto = NULL;
+	struct servent *serv = NULL;
+	int serv_port;
+
+	ldns_buffer *str_buf;
+
+	char *proto_str = NULL;
+	char *token;
+	if(strlen(str) == 0)
+		token = LDNS_XMALLOC(char, 50);
+	else 	token = LDNS_XMALLOC(char, strlen(str)+2);
+	if(!token) return LDNS_STATUS_MEM_ERR;
+
+	str_buf = LDNS_MALLOC(ldns_buffer);
+	if(!str_buf) {LDNS_FREE(token); return LDNS_STATUS_MEM_ERR;}
+	ldns_buffer_new_frm_data(str_buf, (char *)str, strlen(str));
+	if(ldns_buffer_status(str_buf) != LDNS_STATUS_OK) {
+		LDNS_FREE(str_buf);
+		LDNS_FREE(token);
+		return LDNS_STATUS_MEM_ERR;
+	}
+
+	while(ldns_bget_token(str_buf, token, "\t\n ", strlen(str)) > 0) {
+		if (!proto_str) {
+			proto_str = strdup(token);
+			if (!proto_str) {
+				LDNS_FREE(bitmap);
+				LDNS_FREE(token);
+	                        ldns_buffer_free(str_buf);
+				return LDNS_STATUS_INVALID_STR;
+			}
+		} else {
+			serv = getservbyname(token, proto_str);
+			if (serv) {
+				serv_port = (int) ntohs((uint16_t) serv->s_port);
+			} else {
+				serv_port = atoi(token);
+			}
+			if (serv_port / 8 >= bm_len) {
+				uint8_t *b2 = LDNS_XREALLOC(bitmap, uint8_t, (serv_port / 8) + 1);
+                                if(!b2) {
+					LDNS_FREE(bitmap);
+				        LDNS_FREE(token);
+	                                ldns_buffer_free(str_buf);
+				        free(proto_str);
+				        return LDNS_STATUS_INVALID_STR;
+                                }
+				bitmap = b2;
+				/* set to zero to be sure */
+				for (; bm_len <= serv_port / 8; bm_len++) {
+					bitmap[bm_len] = 0;
+				}
+			}
+			ldns_set_bit(bitmap + (serv_port / 8), 7 - (serv_port % 8), true);
+		}
+	}
+
+	if (!proto_str || !bitmap) {
+		LDNS_FREE(bitmap);
+		LDNS_FREE(token);
+	        ldns_buffer_free(str_buf);
+	        free(proto_str);
+		return LDNS_STATUS_INVALID_STR;
+	}
+
+	data = LDNS_XMALLOC(uint8_t, bm_len + 1);
+        if(!data) {
+	        LDNS_FREE(token);
+	        ldns_buffer_free(str_buf);
+	        LDNS_FREE(bitmap);
+	        free(proto_str);
+	        return LDNS_STATUS_INVALID_STR;
+        }
+    if (proto_str)
+		proto = getprotobyname(proto_str);
+	if (proto) {
+		data[0] = (uint8_t) proto->p_proto;
+	} else if (proto_str) {
+		data[0] = (uint8_t) atoi(proto_str);
+	} else {
+		data[0] = 0;
+	}
+	memcpy(data + 1, bitmap, (size_t) bm_len);
+
+	*rd = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_WKS, (uint16_t) (bm_len + 1), data);
+
+	LDNS_FREE(data);
+	LDNS_FREE(token);
+	ldns_buffer_free(str_buf);
+	LDNS_FREE(bitmap);
+	free(proto_str);
+#ifdef HAVE_ENDSERVENT
+	endservent();
+#endif
+#ifdef HAVE_ENDPROTOENT
+	endprotoent();
+#endif
+
+	if(!*rd) return LDNS_STATUS_MEM_ERR;
+
+	return LDNS_STATUS_OK;
+}
+
+ldns_status
+ldns_str2rdf_nsap(ldns_rdf **rd, const char *str)
+{
+    size_t len, i;
+    char* nsap_str = (char*) str;
+
+	/* just a hex string with optional dots? */
+	if (str[0] != '0' || str[1] != 'x') {
+		return LDNS_STATUS_INVALID_STR;
+	} else {
+		len = strlen(str);
+		for (i=0; i < len; i++) {
+			if (nsap_str[i] == '.')
+				nsap_str[i] = ' ';
+        }
+		return ldns_str2rdf_hex(rd, str+2);
+	}
+}
+
+ldns_status
+ldns_str2rdf_atma(ldns_rdf **rd, const char *str)
+{
+    size_t len, i;
+    char* atma_str = (char*) str;
+	ldns_status status;
+
+	/* just a hex string with optional dots? */
+	len = strlen(str);
+	for (i=0; i < len; i++) {
+		if (atma_str[i] == '.')
+			atma_str[i] = ' ';
+	}
+	status = ldns_str2rdf_hex(rd, str);
+    if (status != LDNS_STATUS_OK) {
+		; /* probably in e.164 format than */
+	}
+	return status;
+}
+
+ldns_status
+ldns_str2rdf_ipseckey(ldns_rdf **rd, const char *str)
+{
+	uint8_t precedence = 0;
+	uint8_t gateway_type = 0;
+	uint8_t algorithm = 0;
+	char* gateway = NULL;
+	char* publickey = NULL;
+	uint8_t *data;
+	ldns_buffer *str_buf;
+	char *token;
+	int token_count = 0;
+	int ipseckey_len = 0;
+	ldns_rdf* gateway_rdf = NULL;
+	ldns_rdf* publickey_rdf = NULL;
+	ldns_status status = LDNS_STATUS_OK;
+	
+	if(strlen(str) == 0)
+		token = LDNS_XMALLOC(char, 256);
+	else	token = LDNS_XMALLOC(char, strlen(str)+2);
+	if(!token) return LDNS_STATUS_MEM_ERR;
+
+	str_buf = LDNS_MALLOC(ldns_buffer);
+	if(!str_buf) {LDNS_FREE(token); return LDNS_STATUS_MEM_ERR;}
+	ldns_buffer_new_frm_data(str_buf, (char *)str, strlen(str));
+	if(ldns_buffer_status(str_buf) != LDNS_STATUS_OK) {
+		LDNS_FREE(str_buf);
+		LDNS_FREE(token);
+		return LDNS_STATUS_MEM_ERR;
+	}
+	while(ldns_bget_token(str_buf, token, "\t\n ", strlen(str)) > 0) {
+		switch (token_count) {
+				case 0:
+					precedence = (uint8_t)atoi(token);
+					break;
+				case 1:
+					gateway_type = (uint8_t)atoi(token);
+					break;
+				case 2:
+					algorithm = (uint8_t)atoi(token);
+					break;
+				case 3:
+					gateway = strdup(token);
+					if (!gateway || (gateway_type == 0 &&
+							(token[0] != '.' || token[1] != '\0'))) {
+						LDNS_FREE(gateway);
+						LDNS_FREE(token);
+						ldns_buffer_free(str_buf);
+						return LDNS_STATUS_INVALID_STR;
+					}
+					break;
+				case 4:
+					publickey = strdup(token);
+					break;
+				default:
+					LDNS_FREE(token);
+					ldns_buffer_free(str_buf);
+					return LDNS_STATUS_INVALID_STR;
+					break;
+		}
+		token_count++;
+	}
+
+	if (!gateway || !publickey) {
+		if (gateway)
+			LDNS_FREE(gateway);
+		if (publickey)
+			LDNS_FREE(publickey);
+		LDNS_FREE(token);
+		ldns_buffer_free(str_buf);
+		return LDNS_STATUS_INVALID_STR;
+	}
+
+	if (gateway_type == 1) {
+		status = ldns_str2rdf_a(&gateway_rdf, gateway);
+	} else if (gateway_type == 2) {
+		status = ldns_str2rdf_aaaa(&gateway_rdf, gateway);
+	} else if (gateway_type == 3) {
+		status = ldns_str2rdf_dname(&gateway_rdf, gateway);
+	}
+
+	if (status != LDNS_STATUS_OK) {
+		if (gateway)
+			LDNS_FREE(gateway);
+		if (publickey)
+			LDNS_FREE(publickey);
+		LDNS_FREE(token);
+		ldns_buffer_free(str_buf);
+		return LDNS_STATUS_INVALID_STR;
+	}
+
+	status = ldns_str2rdf_b64(&publickey_rdf, publickey);
+
+	if (status != LDNS_STATUS_OK) {
+		if (gateway)
+			LDNS_FREE(gateway);
+		if (publickey)
+			LDNS_FREE(publickey);
+		LDNS_FREE(token);
+		ldns_buffer_free(str_buf);
+		if (gateway_rdf) ldns_rdf_free(gateway_rdf);
+		return LDNS_STATUS_INVALID_STR;
+	}
+
+	/* now copy all into one ipseckey rdf */
+	if (gateway_type)
+		ipseckey_len = 3 + (int)ldns_rdf_size(gateway_rdf) + (int)ldns_rdf_size(publickey_rdf);
+	else
+		ipseckey_len = 3 + (int)ldns_rdf_size(publickey_rdf);
+
+	data = LDNS_XMALLOC(uint8_t, ipseckey_len);
+	if(!data) {
+		if (gateway)
+			LDNS_FREE(gateway);
+		if (publickey)
+			LDNS_FREE(publickey);
+		LDNS_FREE(token);
+		ldns_buffer_free(str_buf);
+		if (gateway_rdf) ldns_rdf_free(gateway_rdf);
+		if (publickey_rdf) ldns_rdf_free(publickey_rdf);
+		return LDNS_STATUS_MEM_ERR;
+	}
+
+	data[0] = precedence;
+	data[1] = gateway_type;
+	data[2] = algorithm;
+
+	if (gateway_type) {
+		memcpy(data + 3,
+			ldns_rdf_data(gateway_rdf), ldns_rdf_size(gateway_rdf));
+		memcpy(data + 3 + ldns_rdf_size(gateway_rdf),
+			ldns_rdf_data(publickey_rdf), ldns_rdf_size(publickey_rdf));
+	} else {
+		memcpy(data + 3,
+			ldns_rdf_data(publickey_rdf), ldns_rdf_size(publickey_rdf));
+	}
+
+	*rd = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_IPSECKEY, (uint16_t) ipseckey_len, data);
+
+	if (gateway)
+		LDNS_FREE(gateway);
+	if (publickey)
+		LDNS_FREE(publickey);
+	LDNS_FREE(token);
+	ldns_buffer_free(str_buf);
+	ldns_rdf_free(gateway_rdf);
+	ldns_rdf_free(publickey_rdf);
+	LDNS_FREE(data);
+	if(!*rd) return LDNS_STATUS_MEM_ERR;
+	return LDNS_STATUS_OK;
+}
diff --git a/3rdParty/Ldns/src/src/tsig.c b/3rdParty/Ldns/src/src/tsig.c
new file mode 100644
index 0000000..90c20a0
--- /dev/null
+++ b/3rdParty/Ldns/src/src/tsig.c
@@ -0,0 +1,472 @@
+/*
+ * tsig.c
+ *
+ * contains the functions needed for TSIG [RFC2845]
+ *
+ * (c) 2005-2006 NLnet Labs
+ * See the file LICENSE for the license
+ */
+
+#include <ldns/config.h>
+
+#include <ldns/ldns.h>
+
+#include <strings.h>
+
+#ifdef HAVE_SSL
+#include <openssl/hmac.h>
+#include <openssl/md5.h>
+#endif /* HAVE_SSL */
+
+char *
+ldns_tsig_algorithm(ldns_tsig_credentials *tc)
+{
+	return tc->algorithm;
+}
+
+char *
+ldns_tsig_keyname(ldns_tsig_credentials *tc)
+{
+	return tc->keyname;
+}
+
+char *
+ldns_tsig_keydata(ldns_tsig_credentials *tc)
+{
+	return tc->keydata;
+}
+
+char *
+ldns_tsig_keyname_clone(ldns_tsig_credentials *tc)
+{
+	return strdup(tc->keyname);
+}
+
+char *
+ldns_tsig_keydata_clone(ldns_tsig_credentials *tc)
+{
+	return strdup(tc->keydata);
+}
+
+/*
+ *  Makes an exact copy of the wire, but with the tsig rr removed
+ */
+uint8_t *
+ldns_tsig_prepare_pkt_wire(uint8_t *wire, size_t wire_len, size_t *result_len)
+{
+	uint8_t *wire2 = NULL;
+	uint16_t qd_count;
+	uint16_t an_count;
+	uint16_t ns_count;
+	uint16_t ar_count;
+	ldns_rr *rr;
+
+	size_t pos;
+	uint16_t i;
+
+	ldns_status status;
+
+	if(wire_len < LDNS_HEADER_SIZE) {
+		return NULL;
+	}
+	/* fake parse the wire */
+	qd_count = LDNS_QDCOUNT(wire);
+	an_count = LDNS_ANCOUNT(wire);
+	ns_count = LDNS_NSCOUNT(wire);
+	ar_count = LDNS_ARCOUNT(wire);
+
+	if (ar_count > 0) {
+		ar_count--;
+	} else {
+		return NULL;
+	}
+
+	pos = LDNS_HEADER_SIZE;
+
+	for (i = 0; i < qd_count; i++) {
+		status = ldns_wire2rr(&rr, wire, wire_len, &pos, LDNS_SECTION_QUESTION);
+		if (status != LDNS_STATUS_OK) {
+			return NULL;
+		}
+		ldns_rr_free(rr);
+	}
+
+	for (i = 0; i < an_count; i++) {
+		status = ldns_wire2rr(&rr, wire, wire_len, &pos, LDNS_SECTION_ANSWER);
+		if (status != LDNS_STATUS_OK) {
+			return NULL;
+		}
+		ldns_rr_free(rr);
+	}
+
+	for (i = 0; i < ns_count; i++) {
+		status = ldns_wire2rr(&rr, wire, wire_len, &pos, LDNS_SECTION_AUTHORITY);
+		if (status != LDNS_STATUS_OK) {
+			return NULL;
+		}
+		ldns_rr_free(rr);
+	}
+
+	for (i = 0; i < ar_count; i++) {
+		status = ldns_wire2rr(&rr, wire, wire_len, &pos,
+				LDNS_SECTION_ADDITIONAL);
+		if (status != LDNS_STATUS_OK) {
+			return NULL;
+		}
+		ldns_rr_free(rr);
+	}
+
+	*result_len = pos;
+	wire2 = LDNS_XMALLOC(uint8_t, *result_len);
+	if(!wire2) {
+		return NULL;
+	}
+	memcpy(wire2, wire, *result_len);
+
+	ldns_write_uint16(wire2 + LDNS_ARCOUNT_OFF, ar_count);
+
+	return wire2;
+}
+
+#ifdef HAVE_SSL
+static const EVP_MD *
+ldns_digest_function(char *name)
+{
+	/* these are the mandatory algorithms from RFC4635 */
+	/* The optional algorithms are not yet implemented */
+	if (strlen(name) == 12 
+			&& strncasecmp(name, "hmac-sha256.", 11) == 0) {
+#ifdef HAVE_EVP_SHA256
+		return EVP_sha256();
+#else
+		return NULL;
+#endif
+	} else if (strlen(name) == 10
+			&& strncasecmp(name, "hmac-sha1.", 9) == 0) {
+		return EVP_sha1();
+	} else if (strlen(name) == 25 
+			&& strncasecmp(name, "hmac-md5.sig-alg.reg.int.", 25) 
+			== 0) {
+		return EVP_md5();
+	} else {
+		return NULL;
+	}
+}
+#endif
+
+#ifdef HAVE_SSL
+static ldns_status
+ldns_tsig_mac_new(ldns_rdf **tsig_mac, uint8_t *pkt_wire, size_t pkt_wire_size,
+		const char *key_data, ldns_rdf *key_name_rdf, ldns_rdf *fudge_rdf,
+		ldns_rdf *algorithm_rdf, ldns_rdf *time_signed_rdf, ldns_rdf *error_rdf,
+		ldns_rdf *other_data_rdf, ldns_rdf *orig_mac_rdf, int tsig_timers_only)
+{
+	ldns_status status;
+	char *wireformat;
+	int wiresize;
+	unsigned char *mac_bytes = NULL;
+	unsigned char *key_bytes = NULL;
+	int key_size;
+	const EVP_MD *digester;
+	char *algorithm_name = NULL;
+	unsigned int md_len = EVP_MAX_MD_SIZE;
+	ldns_rdf *result = NULL;
+	ldns_buffer *data_buffer = NULL;
+	ldns_rdf *canonical_key_name_rdf = NULL;
+	ldns_rdf *canonical_algorithm_rdf = NULL;
+	
+	if (key_name_rdf == NULL || algorithm_rdf == NULL) {
+		return LDNS_STATUS_NULL;
+	}
+	canonical_key_name_rdf  = ldns_rdf_clone(key_name_rdf);
+	canonical_algorithm_rdf = ldns_rdf_clone(algorithm_rdf);
+
+	if (canonical_key_name_rdf == NULL 
+			|| canonical_algorithm_rdf  == NULL) {
+		return LDNS_STATUS_MEM_ERR;
+	}
+	/*
+	 * prepare the digestable information
+	 */
+	data_buffer = ldns_buffer_new(LDNS_MAX_PACKETLEN);
+	if (!data_buffer) {
+		status = LDNS_STATUS_MEM_ERR;
+		goto clean;
+	}
+	/* if orig_mac is not NULL, add it too */
+	if (orig_mac_rdf) {
+		(void) ldns_rdf2buffer_wire(data_buffer, orig_mac_rdf);
+ 	}
+	ldns_buffer_write(data_buffer, pkt_wire, pkt_wire_size);
+	if (!tsig_timers_only) {
+		ldns_dname2canonical(canonical_key_name_rdf);
+		(void)ldns_rdf2buffer_wire(data_buffer, 
+				canonical_key_name_rdf);
+		ldns_buffer_write_u16(data_buffer, LDNS_RR_CLASS_ANY);
+		ldns_buffer_write_u32(data_buffer, 0);
+		ldns_dname2canonical(canonical_algorithm_rdf);
+		(void)ldns_rdf2buffer_wire(data_buffer, 
+				canonical_algorithm_rdf);
+	}
+	(void)ldns_rdf2buffer_wire(data_buffer, time_signed_rdf);
+	(void)ldns_rdf2buffer_wire(data_buffer, fudge_rdf);
+	if (!tsig_timers_only) {
+		(void)ldns_rdf2buffer_wire(data_buffer, error_rdf);
+		(void)ldns_rdf2buffer_wire(data_buffer, other_data_rdf);
+	}
+
+	wireformat = (char *) data_buffer->_data;
+	wiresize = (int) ldns_buffer_position(data_buffer);
+
+	algorithm_name = ldns_rdf2str(algorithm_rdf);
+	if(!algorithm_name) {
+		status = LDNS_STATUS_MEM_ERR;
+		goto clean;
+	}
+
+	/* prepare the key */
+	key_bytes = LDNS_XMALLOC(unsigned char,
+			ldns_b64_pton_calculate_size(strlen(key_data)));
+	if(!key_bytes) {
+		status = LDNS_STATUS_MEM_ERR;
+		goto clean;
+	}
+	key_size = ldns_b64_pton(key_data, key_bytes,
+	ldns_b64_pton_calculate_size(strlen(key_data)));
+	if (key_size < 0) {
+		status = LDNS_STATUS_INVALID_B64;
+		goto clean;
+	}
+	/* hmac it */
+	/* 2 spare bytes for the length */
+	mac_bytes = LDNS_XMALLOC(unsigned char, md_len+2);
+	if(!mac_bytes) {
+		status = LDNS_STATUS_MEM_ERR;
+		goto clean;
+	}
+	memset(mac_bytes, 0, md_len+2);
+
+	digester = ldns_digest_function(algorithm_name);
+
+	if (digester) {
+		(void) HMAC(digester, key_bytes, key_size, (void *)wireformat,
+		            (size_t) wiresize, mac_bytes + 2, &md_len);
+
+		ldns_write_uint16(mac_bytes, md_len);
+		result = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_INT16_DATA, md_len + 2,
+				mac_bytes);
+	} else {
+		status = LDNS_STATUS_CRYPTO_UNKNOWN_ALGO;
+		goto clean;
+	}
+	*tsig_mac = result;
+	status = LDNS_STATUS_OK;
+  clean:
+	LDNS_FREE(mac_bytes);
+	LDNS_FREE(key_bytes);
+	LDNS_FREE(algorithm_name);
+	ldns_buffer_free(data_buffer);
+	ldns_rdf_free(canonical_algorithm_rdf);
+	ldns_rdf_free(canonical_key_name_rdf);
+	return status;
+}
+#endif /*  HAVE_SSL */
+
+
+#ifdef HAVE_SSL
+bool
+ldns_pkt_tsig_verify(ldns_pkt *pkt, uint8_t *wire, size_t wirelen, const char *key_name,
+	const char *key_data, ldns_rdf *orig_mac_rdf)
+{
+	return ldns_pkt_tsig_verify_next(pkt, wire, wirelen, key_name, key_data, orig_mac_rdf, 0);
+}
+
+bool
+ldns_pkt_tsig_verify_next(ldns_pkt *pkt, uint8_t *wire, size_t wirelen, const char* key_name,
+	const char *key_data, ldns_rdf *orig_mac_rdf, int tsig_timers_only)
+{
+	ldns_rdf *fudge_rdf;
+	ldns_rdf *algorithm_rdf;
+	ldns_rdf *time_signed_rdf;
+	ldns_rdf *orig_id_rdf;
+	ldns_rdf *error_rdf;
+	ldns_rdf *other_data_rdf;
+	ldns_rdf *pkt_mac_rdf;
+	ldns_rdf *my_mac_rdf;
+	ldns_rdf *key_name_rdf = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, key_name);
+	uint16_t pkt_id, orig_pkt_id;
+	ldns_status status;
+
+	uint8_t *prepared_wire = NULL;
+	size_t prepared_wire_size = 0;
+
+	ldns_rr *orig_tsig = ldns_pkt_tsig(pkt);
+
+	if (!orig_tsig || ldns_rr_rd_count(orig_tsig) <= 6) {
+		ldns_rdf_deep_free(key_name_rdf);
+		return false;
+	}
+	algorithm_rdf = ldns_rr_rdf(orig_tsig, 0);
+	time_signed_rdf = ldns_rr_rdf(orig_tsig, 1);
+	fudge_rdf = ldns_rr_rdf(orig_tsig, 2);
+	pkt_mac_rdf = ldns_rr_rdf(orig_tsig, 3);
+	orig_id_rdf = ldns_rr_rdf(orig_tsig, 4);
+	error_rdf = ldns_rr_rdf(orig_tsig, 5);
+	other_data_rdf = ldns_rr_rdf(orig_tsig, 6);
+
+	/* remove temporarily */
+	ldns_pkt_set_tsig(pkt, NULL);
+	/* temporarily change the id to the original id */
+	pkt_id = ldns_pkt_id(pkt);
+	orig_pkt_id = ldns_rdf2native_int16(orig_id_rdf);
+	ldns_pkt_set_id(pkt, orig_pkt_id);
+
+	prepared_wire = ldns_tsig_prepare_pkt_wire(wire, wirelen, &prepared_wire_size);
+
+	status = ldns_tsig_mac_new(&my_mac_rdf, prepared_wire, prepared_wire_size,
+			key_data, key_name_rdf, fudge_rdf, algorithm_rdf,
+			time_signed_rdf, error_rdf, other_data_rdf, orig_mac_rdf, tsig_timers_only);
+
+	LDNS_FREE(prepared_wire);
+
+	if (status != LDNS_STATUS_OK) {
+		ldns_rdf_deep_free(key_name_rdf);
+		return false;
+	}
+	/* Put back the values */
+	ldns_pkt_set_tsig(pkt, orig_tsig);
+	ldns_pkt_set_id(pkt, pkt_id);
+
+	ldns_rdf_deep_free(key_name_rdf);
+
+	if (ldns_rdf_compare(pkt_mac_rdf, my_mac_rdf) == 0) {
+		ldns_rdf_deep_free(my_mac_rdf);
+		return true;
+	} else {
+		ldns_rdf_deep_free(my_mac_rdf);
+		return false;
+	}
+}
+#endif /* HAVE_SSL */
+
+#ifdef HAVE_SSL
+ldns_status
+ldns_pkt_tsig_sign(ldns_pkt *pkt, const char *key_name, const char *key_data,
+	uint16_t fudge, const char *algorithm_name, ldns_rdf *query_mac)
+{
+	return ldns_pkt_tsig_sign_next(pkt, key_name, key_data, fudge, algorithm_name, query_mac, 0);
+}
+
+ldns_status
+ldns_pkt_tsig_sign_next(ldns_pkt *pkt, const char *key_name, const char *key_data,
+	uint16_t fudge, const char *algorithm_name, ldns_rdf *query_mac, int tsig_timers_only)
+{
+	ldns_rr *tsig_rr;
+	ldns_rdf *key_name_rdf = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, key_name);
+	ldns_rdf *fudge_rdf = NULL;
+	ldns_rdf *orig_id_rdf = NULL;
+	ldns_rdf *algorithm_rdf;
+	ldns_rdf *error_rdf = NULL;
+	ldns_rdf *mac_rdf = NULL;
+	ldns_rdf *other_data_rdf = NULL;
+
+	ldns_status status = LDNS_STATUS_OK;
+
+	uint8_t *pkt_wire = NULL;
+	size_t pkt_wire_len;
+
+	struct timeval tv_time_signed;
+	uint8_t *time_signed = NULL;
+	ldns_rdf *time_signed_rdf = NULL;
+
+	algorithm_rdf = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, algorithm_name);
+	if(!key_name_rdf || !algorithm_rdf) {
+		status = LDNS_STATUS_MEM_ERR;
+		goto clean;
+	}
+
+	/* eww don't have create tsigtime rdf yet :( */
+	/* bleh :p */
+	if (gettimeofday(&tv_time_signed, NULL) == 0) {
+		time_signed = LDNS_XMALLOC(uint8_t, 6);
+		if(!time_signed) {
+			status = LDNS_STATUS_MEM_ERR;
+			goto clean;
+		}
+		ldns_write_uint64_as_uint48(time_signed,
+				(uint64_t)tv_time_signed.tv_sec);
+	} else {
+		status = LDNS_STATUS_INTERNAL_ERR;
+		goto clean;
+	}
+
+	time_signed_rdf = ldns_rdf_new(LDNS_RDF_TYPE_TSIGTIME, 6, time_signed);
+	if(!time_signed_rdf) {
+		LDNS_FREE(time_signed);
+		status = LDNS_STATUS_MEM_ERR;
+		goto clean;
+	}
+
+	fudge_rdf = ldns_native2rdf_int16(LDNS_RDF_TYPE_INT16, fudge);
+
+	orig_id_rdf = ldns_native2rdf_int16(LDNS_RDF_TYPE_INT16, ldns_pkt_id(pkt));
+
+	error_rdf = ldns_native2rdf_int16(LDNS_RDF_TYPE_INT16, 0);
+
+	other_data_rdf = ldns_native2rdf_int16_data(0, NULL);
+
+	if(!fudge_rdf || !orig_id_rdf || !error_rdf || !other_data_rdf) {
+		status = LDNS_STATUS_MEM_ERR;
+		goto clean;
+	}
+
+	if (ldns_pkt2wire(&pkt_wire, pkt, &pkt_wire_len) != LDNS_STATUS_OK) {
+		status = LDNS_STATUS_ERR;
+		goto clean;
+	}
+
+	status = ldns_tsig_mac_new(&mac_rdf, pkt_wire, pkt_wire_len,
+			key_data, key_name_rdf, fudge_rdf, algorithm_rdf,
+			time_signed_rdf, error_rdf, other_data_rdf, query_mac, tsig_timers_only);
+
+	if (!mac_rdf) {
+		goto clean;
+	}
+
+	LDNS_FREE(pkt_wire);
+
+	/* Create the TSIG RR */
+	tsig_rr = ldns_rr_new();
+	if(!tsig_rr) {
+		status = LDNS_STATUS_MEM_ERR;
+		goto clean;
+	}
+	ldns_rr_set_owner(tsig_rr, key_name_rdf);
+	ldns_rr_set_class(tsig_rr, LDNS_RR_CLASS_ANY);
+	ldns_rr_set_type(tsig_rr, LDNS_RR_TYPE_TSIG);
+	ldns_rr_set_ttl(tsig_rr, 0);
+
+	ldns_rr_push_rdf(tsig_rr, algorithm_rdf);
+	ldns_rr_push_rdf(tsig_rr, time_signed_rdf);
+	ldns_rr_push_rdf(tsig_rr, fudge_rdf);
+	ldns_rr_push_rdf(tsig_rr, mac_rdf);
+	ldns_rr_push_rdf(tsig_rr, orig_id_rdf);
+	ldns_rr_push_rdf(tsig_rr, error_rdf);
+	ldns_rr_push_rdf(tsig_rr, other_data_rdf);
+
+	ldns_pkt_set_tsig(pkt, tsig_rr);
+
+	return status;
+
+  clean:
+	LDNS_FREE(pkt_wire);
+	ldns_rdf_free(key_name_rdf);
+	ldns_rdf_free(algorithm_rdf);
+	ldns_rdf_free(time_signed_rdf);
+	ldns_rdf_free(fudge_rdf);
+	ldns_rdf_free(orig_id_rdf);
+	ldns_rdf_free(error_rdf);
+	ldns_rdf_free(other_data_rdf);
+	return status;
+}
+#endif /* HAVE_SSL */
diff --git a/3rdParty/Ldns/src/src/update.c b/3rdParty/Ldns/src/src/update.c
new file mode 100644
index 0000000..96f72ce
--- /dev/null
+++ b/3rdParty/Ldns/src/src/update.c
@@ -0,0 +1,318 @@
+/* update.c
+ *
+ * Functions for RFC 2136 Dynamic Update
+ *
+ * Copyright (c) 2005-2008, NLnet Labs. All rights reserved.
+ *
+ * See LICENSE for the license.
+ */
+
+#include <ldns/config.h>
+
+#include <ldns/ldns.h>
+
+#include <strings.h>
+#include <stdlib.h>
+#include <limits.h>
+
+/*
+ * RFC 2136 sections mapped to RFC 1035:
+ *              zone/ZO -- QD/question
+ *     prerequisites/PR -- AN/answers
+ *           updates/UP -- NS/authority records
+ *   additional data/AD -- AR/additional records
+ */
+
+ldns_pkt *
+ldns_update_pkt_new(ldns_rdf *zone_rdf, ldns_rr_class c,
+    ldns_rr_list *pr_rrlist, ldns_rr_list *up_rrlist, ldns_rr_list *ad_rrlist)
+{
+	ldns_pkt *p;
+
+	if (!zone_rdf || !up_rrlist) {
+		return NULL;
+	}
+
+	if (c == 0) { 
+		c = LDNS_RR_CLASS_IN;
+	}
+
+	/* Create packet, fill in Zone Section. */
+	p = ldns_pkt_query_new(zone_rdf, LDNS_RR_TYPE_SOA, c, LDNS_RD);
+	if (!p) {
+		return NULL;
+	}
+	zone_rdf = NULL; /* No longer safe to use. */
+
+	ldns_pkt_set_opcode(p, LDNS_PACKET_UPDATE);
+
+	ldns_rr_list_deep_free(p->_authority);
+
+	ldns_pkt_set_authority(p, ldns_rr_list_clone(up_rrlist));
+
+	ldns_update_set_upcount(p, ldns_rr_list_rr_count(up_rrlist));
+
+	if (pr_rrlist) {
+		ldns_rr_list_deep_free(p->_answer); /*XXX access function */
+		ldns_pkt_set_answer(p, ldns_rr_list_clone(pr_rrlist));
+		ldns_update_set_prcount(p, ldns_rr_list_rr_count(pr_rrlist));
+	}
+
+	if (ad_rrlist) {
+		ldns_rr_list_deep_free(p->_additional);
+		ldns_pkt_set_additional(p, ldns_rr_list_clone(ad_rrlist));
+		ldns_update_set_adcount(p, ldns_rr_list_rr_count(ad_rrlist));
+	}
+	return p;
+}
+
+ldns_status
+ldns_update_pkt_tsig_add(ldns_pkt *p, ldns_resolver *r)
+{
+#ifdef HAVE_SSL
+	uint16_t fudge = 300; /* Recommended fudge. [RFC2845 6.4]  */
+	if (ldns_resolver_tsig_keyname(r) && ldns_resolver_tsig_keydata(r))
+		return ldns_pkt_tsig_sign(p, ldns_resolver_tsig_keyname(r),
+		    ldns_resolver_tsig_keydata(r), fudge,
+		    ldns_resolver_tsig_algorithm(r), NULL);
+#else
+	/* do nothing */
+	(void)p;
+	(void)r;
+#endif /* HAVE_SSL */
+	/* No TSIG to do. */
+	return LDNS_STATUS_OK;
+}
+
+/* Move to higher.c or similar? */
+/* XXX doc */
+ldns_status
+ldns_update_soa_mname(ldns_rdf *zone, ldns_resolver *r,
+    ldns_rr_class c, ldns_rdf **mname)
+{
+	ldns_rr		*soa_rr;
+	ldns_pkt	*query, *resp;
+
+	/* Nondestructive, so clone 'zone' here */
+	query = ldns_pkt_query_new(ldns_rdf_clone(zone), LDNS_RR_TYPE_SOA,
+	    c, LDNS_RD);
+	if (!query) {
+		return LDNS_STATUS_ERR;
+	}
+
+	ldns_pkt_set_random_id(query);
+	if (ldns_resolver_send_pkt(&resp, r, query) != LDNS_STATUS_OK) {
+		ldns_pkt_free(query);
+		return LDNS_STATUS_ERR;
+	}
+	ldns_pkt_free(query);
+	if (!resp) {
+		return LDNS_STATUS_ERR;
+	}
+
+	/* Expect a SOA answer. */
+	*mname = NULL;
+	while ((soa_rr = ldns_rr_list_pop_rr(ldns_pkt_answer(resp)))) {
+		if (ldns_rr_get_type(soa_rr) != LDNS_RR_TYPE_SOA
+				|| ldns_rr_rdf(soa_rr, 0) == NULL)
+			continue;
+		/* [RFC1035 3.3.13] */
+		*mname = ldns_rdf_clone(ldns_rr_rdf(soa_rr, 0));
+		break;
+	}
+	ldns_pkt_free(resp);
+
+	return *mname ? LDNS_STATUS_OK : LDNS_STATUS_ERR;
+}
+
+/* Try to get zone and MNAME from SOA queries. */
+ldns_status
+ldns_update_soa_zone_mname(const char *fqdn, ldns_resolver *r,
+    ldns_rr_class c, ldns_rdf **zone_rdf, ldns_rdf **mname_rdf)
+{
+	ldns_rr		*soa_rr, *rr;
+	ldns_rdf	*soa_zone = NULL, *soa_mname = NULL;
+	ldns_rdf	*ipaddr, *fqdn_rdf, *tmp;
+	ldns_rdf	**nslist;
+	ldns_pkt	*query, *resp;
+	size_t		i;
+
+	/* 
+	 * XXX Ok, this cannot be the best way to find this...?
+	 * XXX (I run into weird cache-related stuff here)
+	 */
+
+	/* Step 1 - first find a nameserver that should know *something* */
+	fqdn_rdf = ldns_dname_new_frm_str(fqdn);
+	query = ldns_pkt_query_new(fqdn_rdf, LDNS_RR_TYPE_SOA, c, LDNS_RD);
+	if (!query) {
+		return LDNS_STATUS_ERR;
+	}
+	fqdn_rdf = NULL;
+
+	ldns_pkt_set_random_id(query);
+	if (ldns_resolver_send_pkt(&resp, r, query) != LDNS_STATUS_OK) {
+		ldns_pkt_free(query);
+		return LDNS_STATUS_ERR;
+	}
+	ldns_pkt_free(query);
+	if (!resp) {
+		return LDNS_STATUS_ERR;
+	}
+
+	/* XXX Is it safe to only look in authority section here? */
+	while ((soa_rr = ldns_rr_list_pop_rr(ldns_pkt_authority(resp)))) {
+		if (ldns_rr_get_type(soa_rr) != LDNS_RR_TYPE_SOA
+				|| ldns_rr_rdf(soa_rr, 0) == NULL)
+			continue;
+		/* [RFC1035 3.3.13] */
+		soa_mname = ldns_rdf_clone(ldns_rr_rdf(soa_rr, 0));
+		break;
+	}
+	ldns_pkt_free(resp);
+	if (!soa_rr) {
+		return LDNS_STATUS_ERR;
+	}
+
+	/* Step 2 - find SOA MNAME IP address, add to resolver */
+	query = ldns_pkt_query_new(soa_mname, LDNS_RR_TYPE_A, c, LDNS_RD);
+	if (!query) {
+		return LDNS_STATUS_ERR;
+	}
+	soa_mname = NULL;
+
+	ldns_pkt_set_random_id(query);
+	if (ldns_resolver_send_pkt(&resp, r, query) != LDNS_STATUS_OK) {
+		ldns_pkt_free(query);
+		return LDNS_STATUS_ERR;
+	}
+	ldns_pkt_free(query);
+	if (!resp) {
+		return LDNS_STATUS_ERR;
+	}
+
+	if (ldns_pkt_ancount(resp) == 0) {
+		ldns_pkt_free(resp);
+		return LDNS_STATUS_ERR;
+	}
+
+	/* XXX There may be more than one answer RR here. */
+	rr = ldns_rr_list_pop_rr(ldns_pkt_answer(resp));
+	ipaddr = ldns_rr_rdf(rr, 0);
+
+	/* Put the SOA mname IP first in the nameserver list. */
+	nslist = ldns_resolver_nameservers(r);
+	for (i = 0; i < ldns_resolver_nameserver_count(r); i++) {
+		if (ldns_rdf_compare(ipaddr, nslist[i]) == 0) {
+			if (i) {
+				tmp = nslist[0];
+				nslist[0] = nslist[i];
+				nslist[i] = tmp;
+			}
+			break;
+		}
+	}
+	if (i >= ldns_resolver_nameserver_count(r)) {
+		/* SOA mname was not part of the resolver so add it first. */
+		(void) ldns_resolver_push_nameserver(r, ipaddr);
+		nslist = ldns_resolver_nameservers(r);
+		i = ldns_resolver_nameserver_count(r) - 1;
+		tmp = nslist[0];
+		nslist[0] = nslist[i];
+		nslist[i] = tmp;
+	}
+	ldns_pkt_free(resp);
+
+	/* Make sure to ask the first in the list, i.e SOA mname */
+	ldns_resolver_set_random(r, false);
+
+	/* Step 3 - Redo SOA query, sending to SOA MNAME directly. */
+	fqdn_rdf = ldns_dname_new_frm_str(fqdn);
+	query = ldns_pkt_query_new(fqdn_rdf, LDNS_RR_TYPE_SOA, c, LDNS_RD);
+	if (!query) {
+		return LDNS_STATUS_ERR;
+	}
+	fqdn_rdf = NULL;
+
+	ldns_pkt_set_random_id(query);
+	if (ldns_resolver_send_pkt(&resp, r, query) != LDNS_STATUS_OK) {
+		ldns_pkt_free(query);
+		return LDNS_STATUS_ERR;
+	}
+	ldns_pkt_free(query);
+	if (!resp) {
+		return LDNS_STATUS_ERR;
+	}
+
+	/* XXX Is it safe to only look in authority section here, too? */
+	while ((soa_rr = ldns_rr_list_pop_rr(ldns_pkt_authority(resp)))) {
+		if (ldns_rr_get_type(soa_rr) != LDNS_RR_TYPE_SOA
+				|| ldns_rr_rdf(soa_rr, 0) == NULL)
+			continue;
+		/* [RFC1035 3.3.13] */
+		soa_mname = ldns_rdf_clone(ldns_rr_rdf(soa_rr, 0));
+		soa_zone = ldns_rdf_clone(ldns_rr_owner(soa_rr));
+		break;
+	}
+	ldns_pkt_free(resp);
+	if (!soa_rr) {
+		return LDNS_STATUS_ERR;
+	}
+
+	/* That seems to have worked, pass results to caller. */
+	*zone_rdf = soa_zone;
+	*mname_rdf = soa_mname;
+	return LDNS_STATUS_OK;
+}	
+
+/*
+ * ldns_update_{get,set}_{zo,pr,up,ad}count
+ */
+
+uint16_t
+ldns_update_zocount(const ldns_pkt *p)
+{
+	return ldns_pkt_qdcount(p);
+}
+
+uint16_t
+ldns_update_prcount(const ldns_pkt *p)
+{
+	return ldns_pkt_ancount(p);
+}
+
+uint16_t
+ldns_update_upcount(const ldns_pkt *p)
+{
+	return ldns_pkt_nscount(p);
+}
+
+uint16_t
+ldns_update_ad(const ldns_pkt *p)
+{
+	return ldns_pkt_arcount(p);
+}
+
+void
+ldns_update_set_zo(ldns_pkt *p, uint16_t v)
+{
+	ldns_pkt_set_qdcount(p, v);
+}
+
+void
+ldns_update_set_prcount(ldns_pkt *p, uint16_t v)
+{
+	ldns_pkt_set_ancount(p, v);
+}
+
+void
+ldns_update_set_upcount(ldns_pkt *p, uint16_t v)
+{
+	ldns_pkt_set_nscount(p, v);
+}
+
+void
+ldns_update_set_adcount(ldns_pkt *p, uint16_t v)
+{
+	ldns_pkt_set_arcount(p, v);
+}
diff --git a/3rdParty/Ldns/src/src/util.c b/3rdParty/Ldns/src/src/util.c
new file mode 100644
index 0000000..f49a30d
--- /dev/null
+++ b/3rdParty/Ldns/src/src/util.c
@@ -0,0 +1,488 @@
+/*
+ * util.c
+ *
+ * some general memory functions
+ *
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2004-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+#include <ldns/config.h>
+
+#include <ldns/rdata.h>
+#include <ldns/rr.h>
+#include <ldns/util.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <time.h>
+
+#ifdef HAVE_SSL
+#include <openssl/rand.h>
+#endif
+
+/* put this here tmp. for debugging */
+void
+xprintf_rdf(ldns_rdf *rd)
+{
+	/* assume printable string */
+	fprintf(stderr, "size\t:%u\n", (unsigned int)ldns_rdf_size(rd));
+	fprintf(stderr, "type\t:%u\n", (unsigned int)ldns_rdf_get_type(rd));
+	fprintf(stderr, "data\t:[%.*s]\n", (int)ldns_rdf_size(rd),
+			(char*)ldns_rdf_data(rd));
+}
+
+void
+xprintf_rr(ldns_rr *rr)
+{
+	/* assume printable string */
+	uint16_t count, i;
+
+	count = ldns_rr_rd_count(rr);
+
+	for(i = 0; i < count; i++) {
+		fprintf(stderr, "print rd %u\n", (unsigned int) i);
+		xprintf_rdf(rr->_rdata_fields[i]);
+	}
+}
+
+void xprintf_hex(uint8_t *data, size_t len)
+{
+	size_t i;
+	for (i = 0; i < len; i++) {
+		if (i > 0 && i % 20 == 0) {
+			printf("\t; %u - %u\n", (unsigned int) i - 19, (unsigned int) i);
+		}
+		printf("%02x ", (unsigned int) data[i]);
+	}
+	printf("\n");
+}
+
+ldns_lookup_table *
+ldns_lookup_by_name(ldns_lookup_table *table, const char *name)
+{
+	while (table->name != NULL) {
+		if (strcasecmp(name, table->name) == 0)
+			return table;
+		table++;
+	}
+	return NULL;
+}
+
+ldns_lookup_table *
+ldns_lookup_by_id(ldns_lookup_table *table, int id)
+{
+	while (table->name != NULL) {
+		if (table->id == id)
+			return table;
+		table++;
+	}
+	return NULL;
+}
+
+int
+ldns_get_bit(uint8_t bits[], size_t index)
+{
+	/*
+	 * The bits are counted from left to right, so bit #0 is the
+	 * left most bit.
+	 */
+	return (int) (bits[index / 8] & (1 << (7 - index % 8)));
+}
+
+int
+ldns_get_bit_r(uint8_t bits[], size_t index)
+{
+	/*
+	 * The bits are counted from right to left, so bit #0 is the
+	 * right most bit.
+	 */
+	return (int) bits[index / 8] & (1 << (index % 8));
+}
+
+void
+ldns_set_bit(uint8_t *byte, int bit_nr, bool value)
+{
+	if (bit_nr >= 0 && bit_nr < 8) {
+		if (value) {
+			*byte = *byte | (0x01 << bit_nr);
+		} else {
+			*byte = *byte & ~(0x01 << bit_nr);
+		}
+	}
+}
+
+int
+ldns_hexdigit_to_int(char ch)
+{
+	switch (ch) {
+	case '0': return 0;
+	case '1': return 1;
+	case '2': return 2;
+	case '3': return 3;
+	case '4': return 4;
+	case '5': return 5;
+	case '6': return 6;
+	case '7': return 7;
+	case '8': return 8;
+	case '9': return 9;
+	case 'a': case 'A': return 10;
+	case 'b': case 'B': return 11;
+	case 'c': case 'C': return 12;
+	case 'd': case 'D': return 13;
+	case 'e': case 'E': return 14;
+	case 'f': case 'F': return 15;
+	default:
+		return -1;
+	}
+}
+
+char
+ldns_int_to_hexdigit(int i)
+{
+	switch (i) {
+	case 0: return '0';
+	case 1: return '1';
+	case 2: return '2';
+	case 3: return '3';
+	case 4: return '4';
+	case 5: return '5';
+	case 6: return '6';
+	case 7: return '7';
+	case 8: return '8';
+	case 9: return '9';
+	case 10: return 'a';
+	case 11: return 'b';
+	case 12: return 'c';
+	case 13: return 'd';
+	case 14: return 'e';
+	case 15: return 'f';
+	default:
+		abort();
+	}
+}
+
+int
+ldns_hexstring_to_data(uint8_t *data, const char *str)
+{
+	size_t i;
+
+	if (!str || !data) {
+		return -1;
+	}
+
+	if (strlen(str) % 2 != 0) {
+		return -2;
+	}
+
+	for (i = 0; i < strlen(str) / 2; i++) {
+		data[i] =
+			16 * (uint8_t) ldns_hexdigit_to_int(str[i*2]) +
+			(uint8_t) ldns_hexdigit_to_int(str[i*2 + 1]);
+	}
+
+	return (int) i;
+}
+
+const char *
+ldns_version(void)
+{
+	return (char*)LDNS_VERSION;
+}
+
+/* Number of days per month (except for February in leap years). */
+static const int mdays[] = {
+	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+#define LDNS_MOD(x,y) (((x) % (y) < 0) ? ((x) % (y) + (y)) : ((x) % (y)))
+#define LDNS_DIV(x,y) (((x) % (y) < 0) ? ((x) / (y) -  1 ) : ((x) / (y)))
+
+static int
+is_leap_year(int year)
+{
+	return LDNS_MOD(year,   4) == 0 && (LDNS_MOD(year, 100) != 0 
+	    || LDNS_MOD(year, 400) == 0);
+}
+
+static int
+leap_days(int y1, int y2)
+{
+	--y1;
+	--y2;
+	return (LDNS_DIV(y2,   4) - LDNS_DIV(y1,   4)) - 
+	       (LDNS_DIV(y2, 100) - LDNS_DIV(y1, 100)) +
+	       (LDNS_DIV(y2, 400) - LDNS_DIV(y1, 400));
+}
+
+/*
+ * Code adapted from Python 2.4.1 sources (Lib/calendar.py).
+ */
+time_t
+mktime_from_utc(const struct tm *tm)
+{
+	int year = 1900 + tm->tm_year;
+	time_t days = 365 * ((time_t) year - 1970) + leap_days(1970, year);
+	time_t hours;
+	time_t minutes;
+	time_t seconds;
+	int i;
+
+	for (i = 0; i < tm->tm_mon; ++i) {
+		days += mdays[i];
+	}
+	if (tm->tm_mon > 1 && is_leap_year(year)) {
+		++days;
+	}
+	days += tm->tm_mday - 1;
+
+	hours = days * 24 + tm->tm_hour;
+	minutes = hours * 60 + tm->tm_min;
+	seconds = minutes * 60 + tm->tm_sec;
+
+	return seconds;
+}
+
+#if SIZEOF_TIME_T <= 4
+
+static void
+ldns_year_and_yday_from_days_since_epoch(int64_t days, struct tm *result)
+{
+	int year = 1970;
+	int new_year;
+
+	while (days < 0 || days >= (int64_t) (is_leap_year(year) ? 366 : 365)) {
+		new_year = year + (int) LDNS_DIV(days, 365);
+		days -= (new_year - year) * 365;
+		days -= leap_days(year, new_year);
+		year  = new_year;
+	}
+	result->tm_year = year;
+	result->tm_yday = (int) days;
+}
+
+/* Number of days per month in a leap year. */
+static const int leap_year_mdays[] = {
+	31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+static void
+ldns_mon_and_mday_from_year_and_yday(struct tm *result)
+{
+	int idays = result->tm_yday;
+	const int *mon_lengths = is_leap_year(result->tm_year) ? 
+					leap_year_mdays : mdays;
+
+	result->tm_mon = 0;
+	while  (idays >= mon_lengths[result->tm_mon]) {
+		idays -= mon_lengths[result->tm_mon++];
+	}
+	result->tm_mday = idays + 1;
+}
+
+static void
+ldns_wday_from_year_and_yday(struct tm *result)
+{
+	result->tm_wday = 4 /* 1-1-1970 was a thursday */
+			+ LDNS_MOD((result->tm_year - 1970), 7) * LDNS_MOD(365, 7)
+			+ leap_days(1970, result->tm_year)
+			+ result->tm_yday;
+	result->tm_wday = LDNS_MOD(result->tm_wday, 7);
+	if (result->tm_wday < 0) {
+		result->tm_wday += 7;
+	}
+}
+
+static struct tm *
+ldns_gmtime64_r(int64_t clock, struct tm *result)
+{
+	result->tm_isdst = 0;
+	result->tm_sec   = (int) LDNS_MOD(clock, 60);
+	clock            =       LDNS_DIV(clock, 60);
+	result->tm_min   = (int) LDNS_MOD(clock, 60);
+	clock            =       LDNS_DIV(clock, 60);
+	result->tm_hour  = (int) LDNS_MOD(clock, 24);
+	clock            =       LDNS_DIV(clock, 24);
+
+	ldns_year_and_yday_from_days_since_epoch(clock, result);
+	ldns_mon_and_mday_from_year_and_yday(result);
+	ldns_wday_from_year_and_yday(result);
+	result->tm_year -= 1900;
+
+	return result;
+}
+
+#endif /* SIZEOF_TIME_T <= 4 */
+
+static int64_t
+ldns_serial_arithmitics_time(int32_t time, time_t now)
+{
+	int32_t offset = time - (int32_t) now;
+	return (int64_t) now + offset;
+}
+
+
+struct tm *
+ldns_serial_arithmitics_gmtime_r(int32_t time, time_t now, struct tm *result)
+{
+#if SIZEOF_TIME_T <= 4
+	int64_t secs_since_epoch = ldns_serial_arithmitics_time(time, now);
+	return  ldns_gmtime64_r(secs_since_epoch, result);
+#else
+	time_t  secs_since_epoch = ldns_serial_arithmitics_time(time, now);
+	return  gmtime_r(&secs_since_epoch, result);
+#endif
+}
+
+/**
+ * Init the random source
+ * applications should call this if they need entropy data within ldns
+ * If openSSL is available, it is automatically seeded from /dev/urandom
+ * or /dev/random
+ *
+ * If you need more entropy, or have no openssl available, this function
+ * MUST be called at the start of the program
+ *
+ * If openssl *is* available, this function just adds more entropy
+ **/
+int
+ldns_init_random(FILE *fd, unsigned int size)
+{
+	/* if fp is given, seed srandom with data from file
+	   otherwise use /dev/urandom */
+	FILE *rand_f;
+	uint8_t *seed;
+	size_t read = 0;
+	unsigned int seed_i;
+	struct timeval tv;
+
+	/* we'll need at least sizeof(unsigned int) bytes for the
+	   standard prng seed */
+	if (size < (unsigned int) sizeof(seed_i)){
+		size = (unsigned int) sizeof(seed_i);
+	}
+
+	seed = LDNS_XMALLOC(uint8_t, size);
+        if(!seed) {
+		return 1;
+        }
+
+	if (!fd) {
+		if ((rand_f = fopen("/dev/urandom", "r")) == NULL) {
+			/* no readable /dev/urandom, try /dev/random */
+			if ((rand_f = fopen("/dev/random", "r")) == NULL) {
+				/* no readable /dev/random either, and no entropy
+				   source given. we'll have to improvise */
+				for (read = 0; read < size; read++) {
+					gettimeofday(&tv, NULL);
+					seed[read] = (uint8_t) (tv.tv_usec % 256);
+				}
+			} else {
+				read = fread(seed, 1, size, rand_f);
+			}
+		} else {
+			read = fread(seed, 1, size, rand_f);
+		}
+	} else {
+		rand_f = fd;
+		read = fread(seed, 1, size, rand_f);
+	}
+
+	if (read < size) {
+		LDNS_FREE(seed);
+		return 1;
+	} else {
+#ifdef HAVE_SSL
+		/* Seed the OpenSSL prng (most systems have it seeded
+		   automatically, in that case this call just adds entropy */
+		RAND_seed(seed, (int) size);
+#else
+		/* Seed the standard prng, only uses the first
+		 * unsigned sizeof(unsiged int) bytes found in the entropy pool
+		 */
+		memcpy(&seed_i, seed, sizeof(seed_i));
+		srandom(seed_i);
+#endif
+		LDNS_FREE(seed);
+	}
+
+	if (!fd) {
+                if (rand_f) fclose(rand_f);
+	}
+
+	return 0;
+}
+
+/**
+ * Get random number.
+ *
+ */
+uint16_t
+ldns_get_random(void)
+{
+        uint16_t rid = 0;
+#ifdef HAVE_SSL
+        if (RAND_bytes((unsigned char*)&rid, 2) != 1) {
+                rid = (uint16_t) random();
+        }
+#else
+        rid = (uint16_t) random();
+#endif
+	return rid;
+}
+
+/*
+ * BubbleBabble code taken from OpenSSH
+ * Copyright (c) 2001 Carsten Raskgaard.  All rights reserved.
+ */
+char *
+ldns_bubblebabble(uint8_t *data, size_t len)
+{
+	char vowels[] = { 'a', 'e', 'i', 'o', 'u', 'y' };
+	char consonants[] = { 'b', 'c', 'd', 'f', 'g', 'h', 'k', 'l', 'm',
+	    'n', 'p', 'r', 's', 't', 'v', 'z', 'x' };
+	size_t i, j = 0, rounds, seed = 1;
+	char *retval;
+
+	rounds = (len / 2) + 1;
+	retval = LDNS_XMALLOC(char, rounds * 6);
+	if(!retval) return NULL;
+	retval[j++] = 'x';
+	for (i = 0; i < rounds; i++) {
+		size_t idx0, idx1, idx2, idx3, idx4;
+		if ((i + 1 < rounds) || (len % 2 != 0)) {
+			idx0 = (((((size_t)(data[2 * i])) >> 6) & 3) +
+			    seed) % 6;
+			idx1 = (((size_t)(data[2 * i])) >> 2) & 15;
+			idx2 = ((((size_t)(data[2 * i])) & 3) +
+			    (seed / 6)) % 6;
+			retval[j++] = vowels[idx0];
+			retval[j++] = consonants[idx1];
+			retval[j++] = vowels[idx2];
+			if ((i + 1) < rounds) {
+				idx3 = (((size_t)(data[(2 * i) + 1])) >> 4) & 15;
+				idx4 = (((size_t)(data[(2 * i) + 1]))) & 15;
+				retval[j++] = consonants[idx3];
+				retval[j++] = '-';
+				retval[j++] = consonants[idx4];
+				seed = ((seed * 5) +
+				    ((((size_t)(data[2 * i])) * 7) +
+				    ((size_t)(data[(2 * i) + 1])))) % 36;
+			}
+		} else {
+			idx0 = seed % 6;
+			idx1 = 16;
+			idx2 = seed / 6;
+			retval[j++] = vowels[idx0];
+			retval[j++] = consonants[idx1];
+			retval[j++] = vowels[idx2];
+		}
+	}
+	retval[j++] = 'x';
+	retval[j++] = '\0';
+	return retval;
+}
diff --git a/3rdParty/Ldns/src/src/wire2host.c b/3rdParty/Ldns/src/src/wire2host.c
new file mode 100644
index 0000000..e87fcdf
--- /dev/null
+++ b/3rdParty/Ldns/src/src/wire2host.c
@@ -0,0 +1,456 @@
+/*
+ * wire2host.c
+ *
+ * conversion routines from the wire to the host
+ * format.
+ * This will usually just a re-ordering of the
+ * data (as we store it in network format)
+ *
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2004-2006
+ *
+ * See the file LICENSE for the license
+ */
+
+
+#include <ldns/config.h>
+
+#include <ldns/ldns.h>
+/*#include <ldns/wire2host.h>*/
+
+#include <strings.h>
+#include <limits.h>
+
+
+
+/*
+ * Set of macro's to deal with the dns message header as specified
+ * in RFC1035 in portable way.
+ *
+ */
+
+/*
+ *
+ *                                    1  1  1  1  1  1
+ *      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+ *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *    |                      ID                       |
+ *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *    |QR|   Opcode  |AA|TC|RD|RA| Z|AD|CD|   RCODE   |
+ *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *    |                    QDCOUNT                    |
+ *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *    |                    ANCOUNT                    |
+ *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *    |                    NSCOUNT                    |
+ *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *    |                    ARCOUNT                    |
+ *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *
+ */
+
+
+/* allocates memory to *dname! */
+ldns_status
+ldns_wire2dname(ldns_rdf **dname, const uint8_t *wire, size_t max, size_t *pos)
+{
+	uint8_t label_size;
+	uint16_t pointer_target;
+	uint8_t pointer_target_buf[2];
+	size_t dname_pos = 0;
+	size_t uncompressed_length = 0;
+	size_t compression_pos = 0;
+	uint8_t tmp_dname[LDNS_MAX_DOMAINLEN];
+	unsigned int pointer_count = 0;
+
+	if (*pos >= max) {
+		return LDNS_STATUS_PACKET_OVERFLOW;
+	}
+
+	label_size = wire[*pos];
+	while (label_size > 0) {
+		/* compression */
+		while (label_size >= 192) {
+			if (compression_pos == 0) {
+				compression_pos = *pos + 2;
+			}
+
+			pointer_count++;
+
+			/* remove first two bits */
+			if (*pos + 2 > max) {
+				return LDNS_STATUS_PACKET_OVERFLOW;
+			}
+			pointer_target_buf[0] = wire[*pos] & 63;
+			pointer_target_buf[1] = wire[*pos + 1];
+			pointer_target = ldns_read_uint16(pointer_target_buf);
+
+			if (pointer_target == 0) {
+				return LDNS_STATUS_INVALID_POINTER;
+			} else if (pointer_target >= max) {
+				return LDNS_STATUS_INVALID_POINTER;
+			} else if (pointer_count > LDNS_MAX_POINTERS) {
+				return LDNS_STATUS_INVALID_POINTER;
+			}
+			*pos = pointer_target;
+			label_size = wire[*pos];
+		}
+		if(label_size == 0)
+			break; /* break from pointer to 0 byte */
+		if (label_size > LDNS_MAX_LABELLEN) {
+			return LDNS_STATUS_LABEL_OVERFLOW;
+		}
+		if (*pos + 1 + label_size > max) {
+			return LDNS_STATUS_LABEL_OVERFLOW;
+		}
+
+		/* check space for labelcount itself */
+		if (dname_pos + 1 > LDNS_MAX_DOMAINLEN) {
+			return LDNS_STATUS_DOMAINNAME_OVERFLOW;
+		}
+		tmp_dname[dname_pos] = label_size;
+		if (label_size > 0) {
+			dname_pos++;
+		}
+		*pos = *pos + 1;
+		if (dname_pos + label_size > LDNS_MAX_DOMAINLEN) {
+			return LDNS_STATUS_DOMAINNAME_OVERFLOW;
+		}
+		memcpy(&tmp_dname[dname_pos], &wire[*pos], label_size);
+		uncompressed_length += label_size + 1;
+		dname_pos += label_size;
+		*pos = *pos + label_size;
+
+		if (*pos < max) {
+			label_size = wire[*pos];
+		}
+	}
+
+	if (compression_pos > 0) {
+		*pos = compression_pos;
+	} else {
+		*pos = *pos + 1;
+	}
+
+	if (dname_pos >= LDNS_MAX_DOMAINLEN) {
+		return LDNS_STATUS_DOMAINNAME_OVERFLOW;
+	}
+
+	tmp_dname[dname_pos] = 0;
+	dname_pos++;
+
+	*dname = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_DNAME,
+			(uint16_t) dname_pos, tmp_dname);
+	if (!*dname) {
+		return LDNS_STATUS_MEM_ERR;
+	}
+	return LDNS_STATUS_OK;
+}
+
+/* maybe make this a goto error so data can be freed or something/ */
+#define LDNS_STATUS_CHECK_RETURN(st) {if (st != LDNS_STATUS_OK) { return st; }}
+#define LDNS_STATUS_CHECK_GOTO(st, label) {if (st != LDNS_STATUS_OK) { /*printf("STG %s:%d: status code %d\n", __FILE__, __LINE__, st);*/  goto label; }}
+
+ldns_status
+ldns_wire2rdf(ldns_rr *rr, const uint8_t *wire, size_t max, size_t *pos)
+{
+	size_t end;
+	size_t cur_rdf_length;
+	uint8_t rdf_index;
+	uint8_t *data;
+	uint16_t rd_length;
+	ldns_rdf *cur_rdf = NULL;
+	ldns_rdf_type cur_rdf_type;
+	const ldns_rr_descriptor *descriptor = ldns_rr_descript(ldns_rr_get_type(rr));
+	ldns_status status;
+
+	if (*pos + 2 > max) {
+		return LDNS_STATUS_PACKET_OVERFLOW;
+	}
+
+	rd_length = ldns_read_uint16(&wire[*pos]);
+	*pos = *pos + 2;
+
+	if (*pos + rd_length > max) {
+		return LDNS_STATUS_PACKET_OVERFLOW;
+	}
+
+	end = *pos + (size_t) rd_length;
+
+	for (rdf_index = 0;
+	     rdf_index < ldns_rr_descriptor_maximum(descriptor); rdf_index++) {
+		if (*pos >= end) {
+			break;
+		}
+		cur_rdf_length = 0;
+
+		cur_rdf_type = ldns_rr_descriptor_field_type(descriptor, rdf_index);
+		/* handle special cases immediately, set length
+		   for fixed length rdata and do them below */
+		switch (cur_rdf_type) {
+		case LDNS_RDF_TYPE_DNAME:
+			status = ldns_wire2dname(&cur_rdf, wire, max, pos);
+			LDNS_STATUS_CHECK_RETURN(status);
+			break;
+		case LDNS_RDF_TYPE_CLASS:
+		case LDNS_RDF_TYPE_ALG:
+		case LDNS_RDF_TYPE_INT8:
+			cur_rdf_length = LDNS_RDF_SIZE_BYTE;
+			break;
+		case LDNS_RDF_TYPE_TYPE:
+		case LDNS_RDF_TYPE_INT16:
+		case LDNS_RDF_TYPE_CERT_ALG:
+			cur_rdf_length = LDNS_RDF_SIZE_WORD;
+			break;
+		case LDNS_RDF_TYPE_TIME:
+		case LDNS_RDF_TYPE_INT32:
+		case LDNS_RDF_TYPE_A:
+		case LDNS_RDF_TYPE_PERIOD:
+			cur_rdf_length = LDNS_RDF_SIZE_DOUBLEWORD;
+			break;
+		case LDNS_RDF_TYPE_TSIGTIME:
+			cur_rdf_length = LDNS_RDF_SIZE_6BYTES;
+			break;
+		case LDNS_RDF_TYPE_AAAA:
+			cur_rdf_length = LDNS_RDF_SIZE_16BYTES;
+			break;
+		case LDNS_RDF_TYPE_STR:
+		case LDNS_RDF_TYPE_NSEC3_SALT:
+			/* len is stored in first byte
+			 * it should be in the rdf too, so just
+			 * copy len+1 from this position
+			 */
+			cur_rdf_length = ((size_t) wire[*pos]) + 1;
+			break;
+		case LDNS_RDF_TYPE_INT16_DATA:
+			cur_rdf_length = (size_t) ldns_read_uint16(&wire[*pos]) + 2;
+			break;
+		case LDNS_RDF_TYPE_B32_EXT:
+		case LDNS_RDF_TYPE_NSEC3_NEXT_OWNER:
+			/* length is stored in first byte */
+			cur_rdf_length = ((size_t) wire[*pos]) + 1;
+			break;
+		case LDNS_RDF_TYPE_APL:
+		case LDNS_RDF_TYPE_B64:
+		case LDNS_RDF_TYPE_HEX:
+		case LDNS_RDF_TYPE_NSEC:
+		case LDNS_RDF_TYPE_UNKNOWN:
+		case LDNS_RDF_TYPE_SERVICE:
+		case LDNS_RDF_TYPE_LOC:
+		case LDNS_RDF_TYPE_WKS:
+		case LDNS_RDF_TYPE_NSAP:
+		case LDNS_RDF_TYPE_ATMA:
+		case LDNS_RDF_TYPE_IPSECKEY:
+		case LDNS_RDF_TYPE_TSIG:
+		case LDNS_RDF_TYPE_NONE:
+			/*
+			 * Read to end of rr rdata
+			 */
+			cur_rdf_length = end - *pos;
+			break;
+		}
+
+		/* fixed length rdata */
+		if (cur_rdf_length > 0) {
+			if (cur_rdf_length + *pos > end) {
+				return LDNS_STATUS_PACKET_OVERFLOW;
+			}
+			data = LDNS_XMALLOC(uint8_t, rd_length);
+			if (!data) {
+				return LDNS_STATUS_MEM_ERR;
+			}
+			memcpy(data, &wire[*pos], cur_rdf_length);
+
+			cur_rdf = ldns_rdf_new(cur_rdf_type, cur_rdf_length, data);
+			*pos = *pos + cur_rdf_length;
+		}
+
+		if (cur_rdf) {
+			ldns_rr_push_rdf(rr, cur_rdf);
+			cur_rdf = NULL;
+		}
+	}
+
+	return LDNS_STATUS_OK;
+}
+
+
+/* TODO:
+         can *pos be incremented at READ_INT? or maybe use something like
+         RR_CLASS(wire)?
+	 uhhm Jelte??
+*/
+ldns_status
+ldns_wire2rr(ldns_rr **rr_p, const uint8_t *wire, size_t max,
+             size_t *pos, ldns_pkt_section section)
+{
+	ldns_rdf *owner = NULL;
+	ldns_rr *rr = ldns_rr_new();
+	ldns_status status;
+
+	status = ldns_wire2dname(&owner, wire, max, pos);
+	LDNS_STATUS_CHECK_GOTO(status, status_error);
+
+	ldns_rr_set_owner(rr, owner);
+
+	if (*pos + 4 > max) {
+		status = LDNS_STATUS_PACKET_OVERFLOW;
+		goto status_error;
+	}
+
+	ldns_rr_set_type(rr, ldns_read_uint16(&wire[*pos]));
+	*pos = *pos + 2;
+
+	ldns_rr_set_class(rr, ldns_read_uint16(&wire[*pos]));
+	*pos = *pos + 2;
+
+	if (section != LDNS_SECTION_QUESTION) {
+		if (*pos + 4 > max) {
+			status = LDNS_STATUS_PACKET_OVERFLOW;
+			goto status_error;
+		}
+		ldns_rr_set_ttl(rr, ldns_read_uint32(&wire[*pos]));
+
+		*pos = *pos + 4;
+		status = ldns_wire2rdf(rr, wire, max, pos);
+
+		LDNS_STATUS_CHECK_GOTO(status, status_error);
+        ldns_rr_set_question(rr, false);
+	} else {
+        ldns_rr_set_question(rr, true);
+    }
+
+	*rr_p = rr;
+	return LDNS_STATUS_OK;
+
+status_error:
+	ldns_rr_free(rr);
+	return status;
+}
+
+static ldns_status
+ldns_wire2pkt_hdr(ldns_pkt *packet, const uint8_t *wire, size_t max, size_t *pos)
+{
+	if (*pos + LDNS_HEADER_SIZE > max) {
+		return LDNS_STATUS_WIRE_INCOMPLETE_HEADER;
+	} else {
+		ldns_pkt_set_id(packet, LDNS_ID_WIRE(wire));
+		ldns_pkt_set_qr(packet, LDNS_QR_WIRE(wire));
+		ldns_pkt_set_opcode(packet, LDNS_OPCODE_WIRE(wire));
+		ldns_pkt_set_aa(packet, LDNS_AA_WIRE(wire));
+		ldns_pkt_set_tc(packet, LDNS_TC_WIRE(wire));
+		ldns_pkt_set_rd(packet, LDNS_RD_WIRE(wire));
+		ldns_pkt_set_ra(packet, LDNS_RA_WIRE(wire));
+		ldns_pkt_set_ad(packet, LDNS_AD_WIRE(wire));
+		ldns_pkt_set_cd(packet, LDNS_CD_WIRE(wire));
+		ldns_pkt_set_rcode(packet, LDNS_RCODE_WIRE(wire));
+
+		ldns_pkt_set_qdcount(packet, LDNS_QDCOUNT(wire));
+		ldns_pkt_set_ancount(packet, LDNS_ANCOUNT(wire));
+		ldns_pkt_set_nscount(packet, LDNS_NSCOUNT(wire));
+		ldns_pkt_set_arcount(packet, LDNS_ARCOUNT(wire));
+
+		*pos += LDNS_HEADER_SIZE;
+
+		return LDNS_STATUS_OK;
+	}
+}
+
+ldns_status
+ldns_buffer2pkt_wire(ldns_pkt **packet, ldns_buffer *buffer)
+{
+	/* lazy */
+	return ldns_wire2pkt(packet, ldns_buffer_begin(buffer),
+				ldns_buffer_limit(buffer));
+
+}
+
+ldns_status
+ldns_wire2pkt(ldns_pkt **packet_p, const uint8_t *wire, size_t max)
+{
+	size_t pos = 0;
+	uint16_t i;
+	ldns_rr *rr;
+	ldns_pkt *packet = ldns_pkt_new();
+	ldns_status status = LDNS_STATUS_OK;
+	int have_edns = 0;
+
+	uint8_t data[4];
+
+	status = ldns_wire2pkt_hdr(packet, wire, max, &pos);
+	LDNS_STATUS_CHECK_GOTO(status, status_error);
+
+	for (i = 0; i < ldns_pkt_qdcount(packet); i++) {
+
+		status = ldns_wire2rr(&rr, wire, max, &pos, LDNS_SECTION_QUESTION);
+		if (status == LDNS_STATUS_PACKET_OVERFLOW) {
+			status = LDNS_STATUS_WIRE_INCOMPLETE_QUESTION;
+		}
+		LDNS_STATUS_CHECK_GOTO(status, status_error);
+		if (!ldns_rr_list_push_rr(ldns_pkt_question(packet), rr)) {
+			ldns_pkt_free(packet);
+			return LDNS_STATUS_INTERNAL_ERR;
+		}
+	}
+	for (i = 0; i < ldns_pkt_ancount(packet); i++) {
+		status = ldns_wire2rr(&rr, wire, max, &pos, LDNS_SECTION_ANSWER);
+		if (status == LDNS_STATUS_PACKET_OVERFLOW) {
+			status = LDNS_STATUS_WIRE_INCOMPLETE_ANSWER;
+		}
+		LDNS_STATUS_CHECK_GOTO(status, status_error);
+		if (!ldns_rr_list_push_rr(ldns_pkt_answer(packet), rr)) {
+			ldns_pkt_free(packet);
+			return LDNS_STATUS_INTERNAL_ERR;
+		}
+	}
+	for (i = 0; i < ldns_pkt_nscount(packet); i++) {
+		status = ldns_wire2rr(&rr, wire, max, &pos, LDNS_SECTION_AUTHORITY);
+		if (status == LDNS_STATUS_PACKET_OVERFLOW) {
+			status = LDNS_STATUS_WIRE_INCOMPLETE_AUTHORITY;
+		}
+		LDNS_STATUS_CHECK_GOTO(status, status_error);
+		if (!ldns_rr_list_push_rr(ldns_pkt_authority(packet), rr)) {
+			ldns_pkt_free(packet);
+			return LDNS_STATUS_INTERNAL_ERR;
+		}
+	}
+	for (i = 0; i < ldns_pkt_arcount(packet); i++) {
+		status = ldns_wire2rr(&rr, wire, max, &pos, LDNS_SECTION_ADDITIONAL);
+		if (status == LDNS_STATUS_PACKET_OVERFLOW) {
+			status = LDNS_STATUS_WIRE_INCOMPLETE_ADDITIONAL;
+		}
+		LDNS_STATUS_CHECK_GOTO(status, status_error);
+
+		if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_OPT) {
+			ldns_pkt_set_edns_udp_size(packet, ldns_rr_get_class(rr));
+			ldns_write_uint32(data, ldns_rr_ttl(rr));
+			ldns_pkt_set_edns_extended_rcode(packet, data[0]);
+			ldns_pkt_set_edns_version(packet, data[1]);
+			ldns_pkt_set_edns_z(packet, ldns_read_uint16(&data[2]));
+			/* edns might not have rdfs */
+			if (ldns_rr_rdf(rr, 0)) {
+				ldns_pkt_set_edns_data(packet, ldns_rdf_clone(ldns_rr_rdf(rr, 0)));
+			}
+			ldns_rr_free(rr);
+			have_edns += 1;
+		} else if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_TSIG) {
+			ldns_pkt_set_tsig(packet, rr);
+			ldns_pkt_set_arcount(packet, ldns_pkt_arcount(packet) - 1);
+		} else if (!ldns_rr_list_push_rr(ldns_pkt_additional(packet), rr)) {
+			ldns_pkt_free(packet);
+			return LDNS_STATUS_INTERNAL_ERR;
+		}
+	}
+	ldns_pkt_set_size(packet, max);
+	if(have_edns)
+		ldns_pkt_set_arcount(packet, ldns_pkt_arcount(packet)
+                        - have_edns);
+
+	*packet_p = packet;
+	return status;
+
+status_error:
+	ldns_pkt_free(packet);
+	return status;
+}
diff --git a/3rdParty/Ldns/src/src/zone.c b/3rdParty/Ldns/src/src/zone.c
new file mode 100644
index 0000000..0616a14
--- /dev/null
+++ b/3rdParty/Ldns/src/src/zone.c
@@ -0,0 +1,431 @@
+/* zone.c
+ *
+ * Functions for ldns_zone structure
+ * a Net::DNS like library for C
+ *
+ * (c) NLnet Labs, 2005-2006
+ * See the file LICENSE for the license
+ */
+#include <ldns/config.h>
+
+#include <ldns/ldns.h>
+
+#include <strings.h>
+#include <limits.h>
+
+ldns_rr *
+ldns_zone_soa(const ldns_zone *z)
+{
+        return z->_soa;
+}
+
+size_t
+ldns_zone_rr_count(const ldns_zone *z)
+{
+	return ldns_rr_list_rr_count(z->_rrs);
+}
+
+void
+ldns_zone_set_soa(ldns_zone *z, ldns_rr *soa)
+{
+	z->_soa = soa;
+}
+
+ldns_rr_list *
+ldns_zone_rrs(const ldns_zone *z)
+{
+	return z->_rrs;
+}
+
+void
+ldns_zone_set_rrs(ldns_zone *z, ldns_rr_list *rrlist)
+{
+	z->_rrs = rrlist;
+}
+
+bool
+ldns_zone_push_rr_list(ldns_zone *z, ldns_rr_list *list)
+{
+	return ldns_rr_list_cat(ldns_zone_rrs(z), list);
+
+}
+
+bool
+ldns_zone_push_rr(ldns_zone *z, ldns_rr *rr)
+{
+	return ldns_rr_list_push_rr( ldns_zone_rrs(z), rr);
+}
+
+/* return a clone of the given rr list, without the glue records
+ * rr list should be the complete zone
+ * if present, stripped records are added to the list *glue_records
+ */
+ldns_rr_list *
+ldns_zone_strip_glue_rrs(const ldns_rdf *zone_name, const ldns_rr_list *rrs, ldns_rr_list *glue_rrs)
+{
+	ldns_rr_list *new_list;
+
+	/* when do we find glue? It means we find an IP address
+	 * (AAAA/A) for a nameserver listed in the zone
+	 *
+	 * Alg used here:
+	 * first find all the zonecuts (NS records)
+	 * find all the AAAA or A records (can be done it the 
+	 * above loop).
+	 *
+	 * Check if the aaaa/a list are subdomains under the
+	 * NS domains.
+	 * If yes -> glue, if no -> not glue
+	 */
+
+	ldns_rr_list *zone_cuts;
+	ldns_rr_list *addr;
+	ldns_rr *r, *ns, *a;
+	ldns_rdf *dname_a, *ns_owner;
+	uint16_t i,j;
+
+	new_list = NULL;
+	zone_cuts = NULL;
+	addr = NULL;
+
+	new_list = ldns_rr_list_new();
+	if (!new_list) goto memory_error;
+	zone_cuts = ldns_rr_list_new();
+	if (!zone_cuts) goto memory_error;
+	addr = ldns_rr_list_new();
+	if (!addr) goto memory_error;
+
+	for(i = 0; i < ldns_rr_list_rr_count(rrs); i++) {
+		r = ldns_rr_list_rr(rrs, i);
+		if (ldns_rr_get_type(r) == LDNS_RR_TYPE_A ||
+				ldns_rr_get_type(r) == LDNS_RR_TYPE_AAAA) {
+			/* possibly glue */
+			if (!ldns_rr_list_push_rr(addr, r)) goto memory_error;
+			continue;
+		}
+		if (ldns_rr_get_type(r) == LDNS_RR_TYPE_NS) {
+			/* multiple zones will end up here -
+			 * for now; not a problem
+			 */
+			/* don't add NS records for the current zone itself */
+			if (ldns_rdf_compare(ldns_rr_owner(r), 
+						zone_name) != 0) {
+				if (!ldns_rr_list_push_rr(zone_cuts, r)) goto memory_error;
+			}
+			continue;
+		}
+	}
+
+	/* will sorting make it quicker ?? */
+	for(i = 0; i < ldns_rr_list_rr_count(zone_cuts); i++) {
+		ns = ldns_rr_list_rr(zone_cuts, i);
+		ns_owner = ldns_rr_owner(ns);
+		for(j = 0; j < ldns_rr_list_rr_count(addr); j++) {
+			a = ldns_rr_list_rr(addr, j);
+			dname_a = ldns_rr_owner(a);
+			
+			if (ldns_dname_is_subdomain(dname_a, ns_owner)) {
+				/* GLUE! */
+				if (glue_rrs) {
+					if (!ldns_rr_list_push_rr(glue_rrs, a)) goto memory_error;
+				}
+				break;
+			} else {
+				if (!ldns_rr_list_push_rr(new_list, a)) goto memory_error;
+			}
+		}
+	}
+	
+	ldns_rr_list_free(addr);
+	ldns_rr_list_free(zone_cuts);
+
+	return new_list;
+
+memory_error:
+	if (new_list) {
+		ldns_rr_list_free(new_list);
+	}
+	if (zone_cuts) {
+		ldns_rr_list_free(zone_cuts);
+	}
+	if (addr) {
+		ldns_rr_list_free(addr);
+	}
+	return NULL;
+}
+
+/*
+ * Get the list of glue records in a zone
+ * XXX: there should be a way for this to return error, other than NULL, 
+ *      since NULL is a valid return
+ */
+ldns_rr_list *
+ldns_zone_glue_rr_list(const ldns_zone *z)
+{
+	/* when do we find glue? It means we find an IP address
+	 * (AAAA/A) for a nameserver listed in the zone
+	 *
+	 * Alg used here:
+	 * first find all the zonecuts (NS records)
+	 * find all the AAAA or A records (can be done it the 
+	 * above loop).
+	 *
+	 * Check if the aaaa/a list are subdomains under the
+	 * NS domains.
+	 * If yes -> glue, if no -> not glue
+	 */
+
+	ldns_rr_list *zone_cuts;
+	ldns_rr_list *addr;
+	ldns_rr_list *glue;
+	ldns_rr *r, *ns, *a;
+	ldns_rdf *dname_a, *ns_owner;
+	size_t i,j;
+
+	zone_cuts = NULL;
+	addr = NULL;
+	glue = NULL;
+
+	/* we cannot determine glue in a 'zone' without a SOA */
+	if (!ldns_zone_soa(z)) {
+		return NULL;
+	}
+
+	zone_cuts = ldns_rr_list_new();
+	if (!zone_cuts) goto memory_error;
+	addr = ldns_rr_list_new();
+	if (!addr) goto memory_error;
+	glue = ldns_rr_list_new();
+	if (!glue) goto memory_error;
+
+	for(i = 0; i < ldns_zone_rr_count(z); i++) {
+		r = ldns_rr_list_rr(ldns_zone_rrs(z), i);
+		if (ldns_rr_get_type(r) == LDNS_RR_TYPE_A ||
+				ldns_rr_get_type(r) == LDNS_RR_TYPE_AAAA) {
+			/* possibly glue */
+			if (!ldns_rr_list_push_rr(addr, r)) goto memory_error;
+			continue;
+		}
+		if (ldns_rr_get_type(r) == LDNS_RR_TYPE_NS) {
+			/* multiple zones will end up here -
+			 * for now; not a problem
+			 */
+			/* don't add NS records for the current zone itself */
+			if (ldns_rdf_compare(ldns_rr_owner(r), 
+						ldns_rr_owner(ldns_zone_soa(z))) != 0) {
+				if (!ldns_rr_list_push_rr(zone_cuts, r)) goto memory_error;
+			}
+			continue;
+		}
+	}
+
+	/* will sorting make it quicker ?? */
+	for(i = 0; i < ldns_rr_list_rr_count(zone_cuts); i++) {
+		ns = ldns_rr_list_rr(zone_cuts, i);
+		ns_owner = ldns_rr_owner(ns);
+
+		for(j = 0; j < ldns_rr_list_rr_count(addr); j++) {
+			a = ldns_rr_list_rr(addr, j);
+			dname_a = ldns_rr_owner(a);
+
+			if (ldns_dname_is_subdomain(dname_a, ns_owner) ||
+				ldns_dname_compare(dname_a, ns_owner) == 0) {
+				/* GLUE! */
+				if (!ldns_rr_list_push_rr(glue, a)) goto memory_error;
+			}
+		}
+	}
+	
+	ldns_rr_list_free(addr);
+	ldns_rr_list_free(zone_cuts);
+
+	if (ldns_rr_list_rr_count(glue) == 0) {
+		ldns_rr_list_free(glue);
+		return NULL;
+	} else {
+		return glue;
+	}
+
+memory_error:
+	if (zone_cuts) {
+		LDNS_FREE(zone_cuts);
+	}
+	if (addr) {
+		ldns_rr_list_free(addr);
+	}
+	if (glue) {
+		ldns_rr_list_free(glue);
+	}
+	return NULL;
+}
+
+ldns_zone *
+ldns_zone_new(void)
+{
+	ldns_zone *z;
+
+	z = LDNS_MALLOC(ldns_zone);
+	if (!z) {
+		return NULL;
+	}
+
+	z->_rrs = ldns_rr_list_new();
+	if (!z->_rrs) {
+		LDNS_FREE(z);
+		return NULL;
+	}
+	ldns_zone_set_soa(z, NULL);
+	return z;
+}
+
+/* we regocnize:
+ * $TTL, $ORIGIN
+ */
+ldns_status
+ldns_zone_new_frm_fp(ldns_zone **z, FILE *fp, ldns_rdf *origin, uint32_t ttl, ldns_rr_class c)
+{
+	return ldns_zone_new_frm_fp_l(z, fp, origin, ttl, c, NULL);
+}
+
+/* XXX: class is never used */
+ldns_status
+ldns_zone_new_frm_fp_l(ldns_zone **z, FILE *fp, ldns_rdf *origin, uint32_t ttl, 
+        ldns_rr_class ATTR_UNUSED(c), int *line_nr)
+{
+	ldns_zone *newzone;
+	ldns_rr *rr;
+	uint32_t my_ttl;
+	ldns_rdf *my_origin;
+	ldns_rdf *my_prev;
+	bool soa_seen = false; 	/* 2 soa are an error */
+	ldns_status s;
+	ldns_status ret;
+
+	/* most cases of error are memory problems */
+	ret = LDNS_STATUS_MEM_ERR;
+
+	newzone = NULL;
+	my_origin = NULL;
+	my_prev = NULL;
+
+	my_ttl    = ttl;
+	
+	if (origin) {
+		my_origin = ldns_rdf_clone(origin);
+		if (!my_origin) goto error;
+		/* also set the prev */
+		my_prev   = ldns_rdf_clone(origin);
+		if (!my_prev) goto error;
+	}
+
+	newzone = ldns_zone_new();
+	if (!newzone) goto error;
+
+	while(!feof(fp)) {
+		s = ldns_rr_new_frm_fp_l(&rr, fp, &my_ttl, &my_origin, &my_prev, line_nr);
+		switch (s) {
+		case LDNS_STATUS_OK:
+			if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_SOA) {
+				if (soa_seen) {
+					/* second SOA 
+					 * just skip, maybe we want to say
+					 * something??? */
+					ldns_rr_free(rr);
+					continue;
+				}
+				soa_seen = true;
+				ldns_zone_set_soa(newzone, rr);
+				/* set origin to soa if not specified */
+				if (!my_origin) {
+					my_origin = ldns_rdf_clone(ldns_rr_owner(rr));
+				}
+				continue;
+			}
+			
+			/* a normal RR - as sofar the DNS is normal */
+			if (!ldns_zone_push_rr(newzone, rr)) goto error;
+
+		case LDNS_STATUS_SYNTAX_EMPTY:
+			/* empty line was seen */
+		case LDNS_STATUS_SYNTAX_TTL:
+			/* the function set the ttl */
+			break;
+		case LDNS_STATUS_SYNTAX_ORIGIN:
+			/* the function set the origin */
+			break;
+		case LDNS_STATUS_SYNTAX_INCLUDE:
+			ret = LDNS_STATUS_SYNTAX_INCLUDE_ERR_NOTIMPL;
+			break;
+		default:
+			ret = s;
+			goto error;
+		}
+	}
+
+	if (my_origin) {
+		ldns_rdf_deep_free(my_origin);
+	}
+	if (my_prev) {
+		ldns_rdf_deep_free(my_prev);
+	}
+	if (z) {
+		*z = newzone;
+	} else {
+		ldns_zone_free(newzone);
+	}
+
+	return LDNS_STATUS_OK;
+
+error:
+	if (my_origin) {
+		ldns_rdf_deep_free(my_origin);
+	}
+	if (my_prev) {
+		ldns_rdf_deep_free(my_prev);
+	}
+	if (newzone) {
+		ldns_zone_free(newzone);
+	}
+	return ret;
+}
+
+void
+ldns_zone_sort(ldns_zone *zone)
+{
+	ldns_rr_list *zrr;
+	assert(zone != NULL);
+
+	zrr = ldns_zone_rrs(zone);
+	ldns_rr_list_sort(zrr);
+}
+
+#if 0
+/**
+ * ixfr function. Work on a ldns_zone and remove and add
+ * the rrs from the rrlist
+ * \param[in] z the zone to work on
+ * \param[in] del rr_list to remove from the zone
+ * \param[in] add rr_list to add to the zone
+ * \return Tja, wat zouden we eens returnen TODO
+ */
+void
+ldns_zone_ixfr_del_add(ldns_zone *z, ldns_rr_list *del, ldns_rr_list *add)
+{
+	
+}
+#endif
+
+void
+ldns_zone_free(ldns_zone *zone) 
+{
+	ldns_rr_list_free(zone->_rrs);
+	LDNS_FREE(zone);
+}
+
+void
+ldns_zone_deep_free(ldns_zone *zone) 
+{
+	ldns_rr_free(zone->_soa);
+	ldns_rr_list_deep_free(zone->_rrs);
+	LDNS_FREE(zone);
+}
diff --git a/3rdParty/Unbound/SConscript b/3rdParty/Unbound/SConscript
new file mode 100644
index 0000000..c34abe8
--- /dev/null
+++ b/3rdParty/Unbound/SConscript
@@ -0,0 +1,102 @@
+Import("env")
+
+if env.get("UNBOUND_BUNDLED", False) :
+
+	if env["PLATFORM"] == "win32" :
+		cppflags = ["/I" + Dir("#/3rdParty/Unbound/src/src").abspath]
+	else :
+		cppflags = [("-isystem", Dir("#/3rdParty/Unbound/src/src").abspath)]
+
+
+################################################################################
+# Flags
+################################################################################
+    
+	if env["SCONS_STAGE"] == "flags" :
+		env["UNBOUND_FLAGS"] = {
+				"CPPPATH":  [Dir("src/src/libunbound")],
+				"CPPFLAGS": cppflags,
+				"LIBPATH": [env.Dir(".")],
+				"LIBS": ["Swiften_Unbound"],
+			}
+
+################################################################################
+# Build
+################################################################################
+
+	if env["SCONS_STAGE"] == "build" :
+		myenv = env.Clone()
+		myenv.Append(CPPDEFINES = [("UNBOUND_STATICLIB")])
+		myenv.Append(CPPPATH = ["../Ldns/src/include", "src/src", "."])
+
+		myenv.Install("include", [
+            
+			])
+		myenv.StaticLibrary("Swiften_Unbound", [
+            "src/src/daemon/acl_list.c",
+            "src/src/daemon/cachedump.c",
+            "src/src/daemon/daemon.c",
+            "src/src/daemon/remote.c",
+            "src/src/daemon/stats.c",
+            "src/src/daemon/unbound.c",
+            # duplicate symbols: "src/src/daemon/worker.c",
+            "src/src/iterator/iter_delegpt.c",
+            "src/src/iterator/iter_donotq.c",
+            "src/src/iterator/iter_fwd.c",
+            "src/src/iterator/iter_hints.c",
+            "src/src/iterator/iter_priv.c",
+            "src/src/iterator/iter_resptype.c",
+            "src/src/iterator/iter_scrub.c",
+            "src/src/iterator/iter_utils.c",
+            "src/src/iterator/iterator.c",
+            "src/src/libunbound/context.c",
+            "src/src/libunbound/libunbound.c",
+            "src/src/libunbound/libworker.c",
+            "src/src/services/cache/dns.c",
+            "src/src/services/cache/infra.c",
+            "src/src/services/cache/rrset.c",
+            "src/src/services/listen_dnsport.c",
+            "src/src/services/localzone.c",
+            "src/src/services/mesh.c",
+            "src/src/services/modstack.c",
+            "src/src/services/outbound_list.c",
+            "src/src/services/outside_network.c",
+            "src/src/util/alloc.c",
+            "src/src/util/config_file.c",
+            "src/src/util/configlexer.c",
+            "src/src/util/configparser.c",
+            "src/src/util/data/dname.c",
+            "src/src/util/data/msgencode.c",
+            "src/src/util/data/msgparse.c",
+            "src/src/util/data/msgreply.c",
+            "src/src/util/data/packed_rrset.c",
+            "src/src/util/fptr_wlist.c",
+            "src/src/util/locks.c",
+            "src/src/util/log.c",
+            "src/src/util/mini_event.c",
+            "src/src/util/module.c",
+            "src/src/util/net_help.c",
+            "src/src/util/netevent.c",
+            "src/src/util/random.c",
+            "src/src/util/rbtree.c",
+            "src/src/util/regional.c",
+            "src/src/util/rtt.c",
+            "src/src/util/storage/dnstree.c",
+            "src/src/util/storage/lookup3.c",
+            "src/src/util/storage/lruhash.c",
+            "src/src/util/storage/slabhash.c",
+            "src/src/util/timehist.c",
+            "src/src/util/tube.c",
+            #src/src/util/winsock_event.c
+            "src/src/validator/autotrust.c",
+            "src/src/validator/val_anchor.c",
+            "src/src/validator/val_kcache.c",
+            "src/src/validator/val_kentry.c",
+            "src/src/validator/val_neg.c",
+            "src/src/validator/val_nsec.c",
+            "src/src/validator/val_nsec3.c",
+            "src/src/validator/val_sigcrypt.c",
+            "src/src/validator/val_utils.c",
+            "src/src/validator/validator.c",
+            
+			])
diff --git a/3rdParty/Unbound/src/src/LICENSE b/3rdParty/Unbound/src/src/LICENSE
new file mode 100644
index 0000000..c248049
--- /dev/null
+++ b/3rdParty/Unbound/src/src/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2007, NLnet Labs. All rights reserved.
+
+This software is open source.
+
+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.
+
+Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
diff --git a/3rdParty/Unbound/src/src/config.h b/3rdParty/Unbound/src/src/config.h
new file mode 100644
index 0000000..f9c0061
--- /dev/null
+++ b/3rdParty/Unbound/src/src/config.h
@@ -0,0 +1,885 @@
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Directory to chroot to */
+#define CHROOT_DIR ""
+
+/* Pathname to the Unbound configuration file */
+#define CONFIGFILE "/usr/local/etc/unbound/unbound.conf"
+
+/* configure flags */
+#define CONFIGURE_BUILD_WITH " '--disable-gost' '--with-ldns=/Users/tobias/Downloads/ldns-1.6.12'"
+
+/* configure date */
+#define CONFIGURE_DATE "Fri Mar  9 23:52:43 CET 2012"
+
+/* configure target system */
+#define CONFIGURE_TARGET "x86_64-apple-darwin10.8.0"
+
+/* Define this if on macOSX10.4-darwin8 and setreuid and setregid do not work
+   */
+/* #undef DARWIN_BROKEN_SETREUID */
+
+/* Whether daemon is deprecated */
+#define DEPRECATED_DAEMON 1
+
+/* Define if you want to use debug lock checking (slow). */
+/* #undef ENABLE_LOCK_CHECKS */
+
+/* Define this if you enabled-allsymbols from libunbound to link binaries to
+   it for smaller install size, but the libunbound export table is polluted by
+   internal symbols */
+/* #undef EXPORT_ALL_SYMBOLS */
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#define HAVE_ARPA_INET_H 1
+
+/* Whether the C compiler accepts the "format" attribute */
+#define HAVE_ATTR_FORMAT 1
+
+/* Whether the C compiler accepts the "unused" attribute */
+#define HAVE_ATTR_UNUSED 1
+
+/* Define to 1 if your system has a working `chown' function. */
+#define HAVE_CHOWN 1
+
+/* Define to 1 if you have the `chroot' function. */
+#define HAVE_CHROOT 1
+
+/* Define to 1 if you have the `ctime_r' function. */
+#define HAVE_CTIME_R 1
+
+/* Define to 1 if you have the `daemon' function. */
+#define HAVE_DAEMON 1
+
+/* Define to 1 if you have the declaration of `sk_SSL_COMP_pop_free', and to 0
+   if you don't. */
+#define HAVE_DECL_SK_SSL_COMP_POP_FREE 1
+
+/* Define to 1 if you have the declaration of
+   `SSL_COMP_get_compression_methods', and to 0 if you don't. */
+#define HAVE_DECL_SSL_COMP_GET_COMPRESSION_METHODS 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the `event_base_free' function. */
+/* #undef HAVE_EVENT_BASE_FREE */
+
+/* Define to 1 if you have the `event_base_get_method' function. */
+/* #undef HAVE_EVENT_BASE_GET_METHOD */
+
+/* Define to 1 if you have the `event_base_new' function. */
+/* #undef HAVE_EVENT_BASE_NEW */
+
+/* Define to 1 if you have the `event_base_once' function. */
+/* #undef HAVE_EVENT_BASE_ONCE */
+
+/* Define to 1 if you have the <event.h> header file. */
+/* #undef HAVE_EVENT_H */
+
+/* Define to 1 if you have the `EVP_sha1' function. */
+#define HAVE_EVP_SHA1 1
+
+/* Define to 1 if you have the `EVP_sha256' function. */
+#define HAVE_EVP_SHA256 1
+
+/* Define to 1 if you have the `EVP_sha512' function. */
+#define HAVE_EVP_SHA512 1
+
+/* Define to 1 if you have the `ev_default_loop' function. */
+/* #undef HAVE_EV_DEFAULT_LOOP */
+
+/* Define to 1 if you have the `ev_loop' function. */
+/* #undef HAVE_EV_LOOP */
+
+/* Define to 1 if you have the <expat.h> header file. */
+#define HAVE_EXPAT_H 1
+
+/* Define to 1 if you have the `fcntl' function. */
+#define HAVE_FCNTL 1
+
+/* Define to 1 if you have the `fork' function. */
+#define HAVE_FORK 1
+
+/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
+#define HAVE_FSEEKO 1
+
+/* Whether getaddrinfo is available */
+#define HAVE_GETADDRINFO 1
+
+/* Define to 1 if you have the <getopt.h> header file. */
+#define HAVE_GETOPT_H 1
+
+/* Define to 1 if you have the `getpwnam' function. */
+#define HAVE_GETPWNAM 1
+
+/* Define to 1 if you have the `getrlimit' function. */
+#define HAVE_GETRLIMIT 1
+
+/* Define to 1 if you have the `glob' function. */
+#define HAVE_GLOB 1
+
+/* Define to 1 if you have the <glob.h> header file. */
+#define HAVE_GLOB_H 1
+
+/* Define to 1 if you have the `gmtime_r' function. */
+#define HAVE_GMTIME_R 1
+
+/* Define to 1 if you have the <grp.h> header file. */
+#define HAVE_GRP_H 1
+
+/* If you have HMAC_CTX_init */
+#define HAVE_HMAC_CTX_INIT 1
+
+/* Define to 1 if you have the `inet_aton' function. */
+#define HAVE_INET_ATON 1
+
+/* Define to 1 if you have the `inet_ntop' function. */
+#define HAVE_INET_NTOP 1
+
+/* Define to 1 if you have the `inet_pton' function. */
+#define HAVE_INET_PTON 1
+
+/* Define to 1 if you have the `initgroups' function. */
+#define HAVE_INITGROUPS 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* if the function 'ioctlsocket' is available */
+/* #undef HAVE_IOCTLSOCKET */
+
+/* Define to 1 if you have the <iphlpapi.h> header file. */
+/* #undef HAVE_IPHLPAPI_H */
+
+/* Define to 1 if you have the `kill' function. */
+#define HAVE_KILL 1
+
+/* Define to 1 if you have the `ldns_key_EVP_unload_gost' function. */
+/* #undef HAVE_LDNS_KEY_EVP_UNLOAD_GOST */
+
+/* Define to 1 if you have the <ldns/ldns.h> header file. */
+#define HAVE_LDNS_LDNS_H 1
+
+/* Define to 1 if you have the `ldns' library (-lldns). */
+#define HAVE_LIBLDNS 1
+
+/* Define to 1 if you have the `localtime_r' function. */
+#define HAVE_LOCALTIME_R 1
+
+/* Define to 1 if you have the <login_cap.h> header file. */
+/* #undef HAVE_LOGIN_CAP_H */
+
+/* If have GNU libc compatible malloc */
+#define HAVE_MALLOC 1
+
+/* Define to 1 if you have the `memmove' function. */
+#define HAVE_MEMMOVE 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <netdb.h> header file. */
+#define HAVE_NETDB_H 1
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#define HAVE_NETINET_IN_H 1
+
+/* Define to 1 if you have the `OPENSSL_config' function. */
+#define HAVE_OPENSSL_CONFIG 1
+
+/* Define to 1 if you have the <openssl/conf.h> header file. */
+#define HAVE_OPENSSL_CONF_H 1
+
+/* Define to 1 if you have the <openssl/engine.h> header file. */
+#define HAVE_OPENSSL_ENGINE_H 1
+
+/* Define to 1 if you have the <openssl/err.h> header file. */
+#define HAVE_OPENSSL_ERR_H 1
+
+/* Define to 1 if you have the <openssl/rand.h> header file. */
+#define HAVE_OPENSSL_RAND_H 1
+
+/* Define to 1 if you have the <openssl/ssl.h> header file. */
+#define HAVE_OPENSSL_SSL_H 1
+
+/* Define if you have POSIX threads libraries and header files. */
+#define HAVE_PTHREAD 1
+
+/* Define to 1 if the system has the type `pthread_rwlock_t'. */
+#define HAVE_PTHREAD_RWLOCK_T 1
+
+/* Define to 1 if the system has the type `pthread_spinlock_t'. */
+/* #undef HAVE_PTHREAD_SPINLOCK_T */
+
+/* Define to 1 if you have the <pwd.h> header file. */
+#define HAVE_PWD_H 1
+
+/* Define if you have Python libraries and header files. */
+/* #undef HAVE_PYTHON */
+
+/* Define to 1 if you have the `random' function. */
+#define HAVE_RANDOM 1
+
+/* Define to 1 if you have the `recvmsg' function. */
+#define HAVE_RECVMSG 1
+
+/* Define to 1 if you have the `sbrk' function. */
+#define HAVE_SBRK 1
+
+/* Define to 1 if you have the `sendmsg' function. */
+#define HAVE_SENDMSG 1
+
+/* Define to 1 if you have the `setregid' function. */
+#define HAVE_SETREGID 1
+
+/* Define to 1 if you have the `setresgid' function. */
+/* #undef HAVE_SETRESGID */
+
+/* Define to 1 if you have the `setresuid' function. */
+/* #undef HAVE_SETRESUID */
+
+/* Define to 1 if you have the `setreuid' function. */
+#define HAVE_SETREUID 1
+
+/* Define to 1 if you have the `setrlimit' function. */
+#define HAVE_SETRLIMIT 1
+
+/* Define to 1 if you have the `setsid' function. */
+#define HAVE_SETSID 1
+
+/* Define to 1 if you have the `setusercontext' function. */
+/* #undef HAVE_SETUSERCONTEXT */
+
+/* Define to 1 if you have the `sigprocmask' function. */
+#define HAVE_SIGPROCMASK 1
+
+/* Define to 1 if you have the `sleep' function. */
+#define HAVE_SLEEP 1
+
+/* Define to 1 if you have the `snprintf' function. */
+#define HAVE_SNPRINTF 1
+
+/* Define to 1 if you have the `socketpair' function. */
+#define HAVE_SOCKETPAIR 1
+
+/* Using Solaris threads */
+/* #undef HAVE_SOLARIS_THREADS */
+
+/* Define to 1 if you have the `srandom' function. */
+#define HAVE_SRANDOM 1
+
+/* Define if you have the SSL libraries installed. */
+#define HAVE_SSL /**/
+
+/* Define to 1 if you have the <stdarg.h> header file. */
+#define HAVE_STDARG_H 1
+
+/* Define to 1 if you have the <stdbool.h> header file. */
+#define HAVE_STDBOOL_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `strftime' function. */
+#define HAVE_STRFTIME 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strlcpy' function. */
+#define HAVE_STRLCPY 1
+
+/* Define to 1 if you have the `strptime' function. */
+#define HAVE_STRPTIME 1
+
+/* Define if you have Swig libraries and header files. */
+/* #undef HAVE_SWIG */
+
+/* Define to 1 if you have the <syslog.h> header file. */
+#define HAVE_SYSLOG_H 1
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/resource.h> header file. */
+#define HAVE_SYS_RESOURCE_H 1
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#define HAVE_SYS_SOCKET_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+#define HAVE_SYS_UIO_H 1
+
+/* Define to 1 if you have the <sys/wait.h> header file. */
+#define HAVE_SYS_WAIT_H 1
+
+/* Define to 1 if you have the <time.h> header file. */
+#define HAVE_TIME_H 1
+
+/* Define to 1 if you have the `tzset' function. */
+#define HAVE_TZSET 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `usleep' function. */
+#define HAVE_USLEEP 1
+
+/* Define to 1 if you have the `vfork' function. */
+#define HAVE_VFORK 1
+
+/* Define to 1 if you have the <vfork.h> header file. */
+/* #undef HAVE_VFORK_H */
+
+/* Define to 1 if you have the <windows.h> header file. */
+/* #undef HAVE_WINDOWS_H */
+
+/* Using Windows threads */
+/* #undef HAVE_WINDOWS_THREADS */
+
+/* Define to 1 if you have the <winsock2.h> header file. */
+/* #undef HAVE_WINSOCK2_H */
+
+/* Define to 1 if `fork' works. */
+#define HAVE_WORKING_FORK 1
+
+/* Define to 1 if `vfork' works. */
+#define HAVE_WORKING_VFORK 1
+
+/* Define to 1 if you have the `writev' function. */
+#define HAVE_WRITEV 1
+
+/* Define to 1 if you have the <ws2tcpip.h> header file. */
+/* #undef HAVE_WS2TCPIP_H */
+
+/* Define to 1 if you have the `_beginthreadex' function. */
+/* #undef HAVE__BEGINTHREADEX */
+
+/* if lex has yylex_destroy */
+#define LEX_HAS_YYLEX_DESTROY 1
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#define LT_OBJDIR ".libs/"
+
+/* Define to the maximum message length to pass to syslog. */
+#define MAXSYSLOGMSGLEN 10240
+
+/* Define if memcmp() does not compare unsigned bytes */
+/* #undef MEMCMP_IS_BROKEN */
+
+/* Define if mkdir has one argument. */
+/* #undef MKDIR_HAS_ONE_ARG */
+
+/* Define if the network stack does not fully support nonblocking io (causes
+   lower performance). */
+/* #undef NONBLOCKING_IS_BROKEN */
+
+/* Put -D_ALL_SOURCE define in config.h */
+/* #undef OMITTED__D_ALL_SOURCE */
+
+/* Put -D_BSD_SOURCE define in config.h */
+/* #undef OMITTED__D_BSD_SOURCE */
+
+/* Put -D_GNU_SOURCE define in config.h */
+/* #undef OMITTED__D_GNU_SOURCE */
+
+/* Put -D_LARGEFILE_SOURCE=1 define in config.h */
+/* #undef OMITTED__D_LARGEFILE_SOURCE_1 */
+
+/* Put -D_POSIX_C_SOURCE=200112 define in config.h */
+/* #undef OMITTED__D_POSIX_C_SOURCE_200112 */
+
+/* Put -D_XOPEN_SOURCE=600 define in config.h */
+/* #undef OMITTED__D_XOPEN_SOURCE_600 */
+
+/* Put -D_XOPEN_SOURCE_EXTENDED=1 define in config.h */
+/* #undef OMITTED__D_XOPEN_SOURCE_EXTENDED_1 */
+
+/* Put -D__EXTENSIONS__ define in config.h */
+/* #undef OMITTED__D__EXTENSIONS__ */
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "unbound-bugs@nlnetlabs.nl"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "unbound"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "unbound 1.4.16"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "unbound"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "1.4.16"
+
+/* default pidfile location */
+#define PIDFILE "/usr/local/etc/unbound/unbound.pid"
+
+/* Define to necessary symbol if this constant uses a non-standard name on
+   your system. */
+/* #undef PTHREAD_CREATE_JOINABLE */
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#define RETSIGTYPE void
+
+/* default rootkey location */
+#define ROOT_ANCHOR_FILE "/usr/local/etc/unbound/root.key"
+
+/* default rootcert location */
+#define ROOT_CERT_FILE "/usr/local/etc/unbound/icannbundle.pem"
+
+/* version number for resource files */
+#define RSRC_PACKAGE_VERSION 1,4,1,6
+
+/* Directory to chdir to */
+#define RUN_DIR "/usr/local/etc/unbound"
+
+/* Shared data */
+#define SHARE_DIR "/usr/local/etc/unbound"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* use default strptime. */
+/* #undef STRPTIME_WORKS */
+
+/* Use win32 resources and API */
+/* #undef UB_ON_WINDOWS */
+
+/* default username */
+#define UB_USERNAME "unbound"
+
+/* use to enable lightweight alloc assertions, for debug use */
+/* #undef UNBOUND_ALLOC_LITE */
+
+/* use malloc not regions, for debug use */
+/* #undef UNBOUND_ALLOC_NONREGIONAL */
+
+/* use statistics for allocs and frees, for debug use */
+/* #undef UNBOUND_ALLOC_STATS */
+
+/* define this to enable debug checks. */
+/* #undef UNBOUND_DEBUG */
+
+/* Define this to enable GOST support. */
+/* #undef USE_GOST */
+
+/* Define if you want to use internal select based events */
+#define USE_MINI_EVENT 1
+
+/* Define this to enable SHA256 and SHA512 support. */
+#define USE_SHA2 1
+
+/* Enable extensions on AIX 3, Interix.  */
+#ifndef _ALL_SOURCE
+# define _ALL_SOURCE 1
+#endif
+/* Enable GNU extensions on systems that have them.  */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+/* Enable threading extensions on Solaris.  */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+/* Enable extensions on HP NonStop.  */
+#ifndef _TANDEM_SOURCE
+# define _TANDEM_SOURCE 1
+#endif
+/* Enable general extensions on Solaris.  */
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__ 1
+#endif
+
+
+/* Whether the windows socket API is used */
+/* #undef USE_WINSOCK */
+
+/* the version of the windows API enabled */
+#define WINVER 0x0502
+
+/* Define if you want Python module. */
+/* #undef WITH_PYTHONMODULE */
+
+/* Define if you want PyUnbound. */
+/* #undef WITH_PYUNBOUND */
+
+/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a
+   `char[]'. */
+#define YYTEXT_POINTER 1
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+/* #undef _FILE_OFFSET_BITS */
+
+/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
+/* #undef _LARGEFILE_SOURCE */
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
+
+/* Define to 1 if on MINIX. */
+/* #undef _MINIX */
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+   this defined. */
+/* #undef _POSIX_1_SOURCE */
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+/* #undef _POSIX_SOURCE */
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef gid_t */
+
+/* in_addr_t */
+/* #undef in_addr_t */
+
+/* in_port_t */
+/* #undef in_port_t */
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+   calls it, or to nothing if 'inline' is not supported under any name.  */
+#ifndef __cplusplus
+/* #undef inline */
+#endif
+
+/* Define to `short' if <sys/types.h> does not define. */
+/* #undef int16_t */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef int32_t */
+
+/* Define to `long long' if <sys/types.h> does not define. */
+/* #undef int64_t */
+
+/* Define to `signed char' if <sys/types.h> does not define. */
+/* #undef int8_t */
+
+/* Define if replacement function should be used. */
+/* #undef malloc */
+
+/* Define to `long int' if <sys/types.h> does not define. */
+/* #undef off_t */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef pid_t */
+
+/* Define to 'int' if not defined */
+/* #undef rlim_t */
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to 'int' if not defined */
+/* #undef socklen_t */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef ssize_t */
+
+/* Define to 'unsigned char if not defined */
+/* #undef u_char */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef uid_t */
+
+/* Define to `unsigned short' if <sys/types.h> does not define. */
+/* #undef uint16_t */
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef uint32_t */
+
+/* Define to `unsigned long long' if <sys/types.h> does not define. */
+/* #undef uint64_t */
+
+/* Define to `unsigned char' if <sys/types.h> does not define. */
+/* #undef uint8_t */
+
+/* Define as `fork' if `vfork' does not work. */
+/* #undef vfork */
+
+#if defined(OMITTED__D_GNU_SOURCE) && !defined(_GNU_SOURCE)
+#define _GNU_SOURCE 1
+#endif 
+
+#if defined(OMITTED__D_BSD_SOURCE) && !defined(_BSD_SOURCE)
+#define _BSD_SOURCE 1
+#endif 
+
+#if defined(OMITTED__D__EXTENSIONS__) && !defined(__EXTENSIONS__)
+#define __EXTENSIONS__ 1
+#endif 
+
+#if defined(OMITTED__D_POSIX_C_SOURCE_200112) && !defined(_POSIX_C_SOURCE)
+#define _POSIX_C_SOURCE 200112
+#endif 
+
+#if defined(OMITTED__D_XOPEN_SOURCE_600) && !defined(_XOPEN_SOURCE)
+#define _XOPEN_SOURCE 600
+#endif 
+
+#if defined(OMITTED__D_XOPEN_SOURCE_EXTENDED_1) && !defined(_XOPEN_SOURCE_EXTENDED)
+#define _XOPEN_SOURCE_EXTENDED 1
+#endif 
+
+#if defined(OMITTED__D_ALL_SOURCE) && !defined(_ALL_SOURCE)
+#define _ALL_SOURCE 1
+#endif 
+
+#if defined(OMITTED__D_LARGEFILE_SOURCE_1) && !defined(_LARGEFILE_SOURCE)
+#define _LARGEFILE_SOURCE 1
+#endif 
+
+
+
+
+#ifndef UNBOUND_DEBUG
+#  define NDEBUG
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+#include <errno.h>
+
+#if HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_WINSOCK2_H
+#include <winsock2.h>
+#endif
+
+#ifdef HAVE_WS2TCPIP_H
+#include <ws2tcpip.h>
+#endif
+
+
+ 
+#ifdef HAVE_ATTR_FORMAT
+#  define ATTR_FORMAT(archetype, string_index, first_to_check) \
+    __attribute__ ((format (archetype, string_index, first_to_check)))
+#else /* !HAVE_ATTR_FORMAT */
+#  define ATTR_FORMAT(archetype, string_index, first_to_check) /* empty */
+#endif /* !HAVE_ATTR_FORMAT */
+
+
+#if defined(DOXYGEN)
+#  define ATTR_UNUSED(x)  x
+#elif defined(__cplusplus)
+#  define ATTR_UNUSED(x)
+#elif defined(HAVE_ATTR_UNUSED)
+#  define ATTR_UNUSED(x)  x __attribute__((unused))
+#else /* !HAVE_ATTR_UNUSED */
+#  define ATTR_UNUSED(x)  x
+#endif /* !HAVE_ATTR_UNUSED */
+
+
+#ifndef HAVE_FSEEKO
+#define fseeko fseek
+#define ftello ftell
+#endif /* HAVE_FSEEKO */
+
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 256
+#endif
+
+
+#ifndef HAVE_SNPRINTF
+#define snprintf snprintf_unbound
+#define vsnprintf vsnprintf_unbound
+#include <stdarg.h>
+int snprintf (char *str, size_t count, const char *fmt, ...);
+int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
+#endif /* HAVE_SNPRINTF */
+
+
+#ifndef HAVE_INET_PTON
+#define inet_pton inet_pton_unbound
+int inet_pton(int af, const char* src, void* dst);
+#endif /* HAVE_INET_PTON */
+
+
+#ifndef HAVE_INET_NTOP
+#define inet_ntop inet_ntop_unbound
+const char *inet_ntop(int af, const void *src, char *dst, size_t size);
+#endif
+
+
+#ifndef HAVE_INET_ATON
+#define inet_aton inet_aton_unbound
+int inet_aton(const char *cp, struct in_addr *addr);
+#endif
+
+
+#ifndef HAVE_MEMMOVE
+#define memmove memmove_unbound
+void *memmove(void *dest, const void *src, size_t n);
+#endif
+
+
+#ifndef HAVE_STRLCPY
+#define strlcpy strlcpy_unbound
+size_t strlcpy(char *dst, const char *src, size_t siz);
+#endif
+
+
+#ifndef HAVE_GMTIME_R
+#define gmtime_r gmtime_r_unbound
+struct tm *gmtime_r(const time_t *timep, struct tm *result);
+#endif
+
+
+#ifndef HAVE_SLEEP
+#define sleep(x) Sleep((x)*1000) /* on win32 */
+#endif /* HAVE_SLEEP */
+
+
+#ifndef HAVE_USLEEP
+#define usleep(x) Sleep((x)/1000 + 1) /* on win32 */
+#endif /* HAVE_USLEEP */
+
+
+#ifndef HAVE_RANDOM
+#define random rand /* on win32, for tests only (bad random) */
+#endif /* HAVE_RANDOM */
+
+
+#ifndef HAVE_SRANDOM
+#define srandom(x) srand(x) /* on win32, for tests only (bad random) */
+#endif /* HAVE_SRANDOM */
+
+
+/* detect if we need to cast to unsigned int for FD_SET to avoid warnings */
+#ifdef HAVE_WINSOCK2_H
+#define FD_SET_T (u_int)
+#else
+#define FD_SET_T 
+#endif
+
+
+#ifndef IPV6_MIN_MTU
+#define IPV6_MIN_MTU 1280
+#endif /* IPV6_MIN_MTU */
+
+
+#ifdef MEMCMP_IS_BROKEN
+#  ifdef memcmp
+/* #  undef memcmp */
+#  endif
+#define memcmp memcmp_unbound
+int memcmp(const void *x, const void *y, size_t n);
+#endif
+
+
+
+#ifndef HAVE_CTIME_R
+#define ctime_r unbound_ctime_r
+char *ctime_r(const time_t *timep, char *buf);
+#endif
+
+#if !defined(HAVE_STRPTIME) || !defined(STRPTIME_WORKS)
+#define strptime unbound_strptime
+struct tm;
+char *strptime(const char *s, const char *format, struct tm *tm);
+#endif
+
+#if defined(HAVE_EVENT_H) && !defined(HAVE_EVENT_BASE_ONCE) && !(defined(HAVE_EV_LOOP) || defined(HAVE_EV_DEFAULT_LOOP)) && (defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS))
+   /* using version of libevent that is not threadsafe. */
+#  define LIBEVENT_SIGNAL_PROBLEM 1
+#endif
+
+#ifndef CHECKED_INET6
+#  define CHECKED_INET6
+#  ifdef AF_INET6
+#    define INET6
+#  else
+#    define AF_INET6        28
+#  endif
+#endif /* CHECKED_INET6 */
+
+/* maximum nesting of included files */
+#define MAXINCLUDES 10
+#ifndef HAVE_GETADDRINFO
+struct sockaddr_storage;
+#include "compat/fake-rfc2553.h"
+#endif
+
+#ifdef UNBOUND_ALLOC_STATS
+#  define malloc(s) unbound_stat_malloc_log(s, __FILE__, __LINE__, __func__)
+#  define calloc(n,s) unbound_stat_calloc_log(n, s, __FILE__, __LINE__, __func__)
+#  define free(p) unbound_stat_free_log(p, __FILE__, __LINE__, __func__)
+#  define realloc(p,s) unbound_stat_realloc_log(p, s, __FILE__, __LINE__, __func__)
+void *unbound_stat_malloc(size_t size);
+void *unbound_stat_calloc(size_t nmemb, size_t size);
+void unbound_stat_free(void *ptr);
+void *unbound_stat_realloc(void *ptr, size_t size);
+void *unbound_stat_malloc_log(size_t size, const char* file, int line,
+	const char* func);
+void *unbound_stat_calloc_log(size_t nmemb, size_t size, const char* file,
+	int line, const char* func);
+void unbound_stat_free_log(void *ptr, const char* file, int line,
+	const char* func);
+void *unbound_stat_realloc_log(void *ptr, size_t size, const char* file,
+	int line, const char* func);
+#elif defined(UNBOUND_ALLOC_LITE)
+#  include "util/alloc.h"
+#endif /* UNBOUND_ALLOC_LITE and UNBOUND_ALLOC_STATS */
+
+/** default port for DNS traffic. */
+#define UNBOUND_DNS_PORT 53
+/** default port for unbound control traffic, registered port with IANA,
+    ub-dns-control  8953/tcp    unbound dns nameserver control */
+#define UNBOUND_CONTROL_PORT 8953
+/** the version of unbound-control that this software implements */
+#define UNBOUND_CONTROL_VERSION 1
+
+
diff --git a/3rdParty/Unbound/src/src/daemon/acl_list.c b/3rdParty/Unbound/src/src/daemon/acl_list.c
new file mode 100644
index 0000000..48c8e0f
--- /dev/null
+++ b/3rdParty/Unbound/src/src/daemon/acl_list.c
@@ -0,0 +1,176 @@
+/*
+ * daemon/acl_list.h - client access control storage for the server.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file helps the server keep out queries from outside sources, that
+ * should not be answered.
+ */
+#include "config.h"
+#include "daemon/acl_list.h"
+#include "util/regional.h"
+#include "util/log.h"
+#include "util/config_file.h"
+#include "util/net_help.h"
+
+struct acl_list* 
+acl_list_create(void)
+{
+	struct acl_list* acl = (struct acl_list*)calloc(1,
+		sizeof(struct acl_list));
+	if(!acl)
+		return NULL;
+	acl->region = regional_create();
+	if(!acl->region) {
+		acl_list_delete(acl);
+		return NULL;
+	}
+	return acl;
+}
+
+void 
+acl_list_delete(struct acl_list* acl)
+{
+	if(!acl) 
+		return;
+	regional_destroy(acl->region);
+	free(acl);
+}
+
+/** insert new address into acl_list structure */
+static int
+acl_list_insert(struct acl_list* acl, struct sockaddr_storage* addr, 
+	socklen_t addrlen, int net, enum acl_access control, 
+	int complain_duplicates)
+{
+	struct acl_addr* node = regional_alloc(acl->region,
+		sizeof(struct acl_addr));
+	if(!node)
+		return 0;
+	node->control = control;
+	if(!addr_tree_insert(&acl->tree, &node->node, addr, addrlen, net)) {
+		if(complain_duplicates)
+			verbose(VERB_QUERY, "duplicate acl address ignored.");
+	}
+	return 1;
+}
+
+/** apply acl_list string */
+static int
+acl_list_str_cfg(struct acl_list* acl, const char* str, const char* s2,
+	int complain_duplicates)
+{
+	struct sockaddr_storage addr;
+	int net;
+	socklen_t addrlen;
+	enum acl_access control;
+	if(strcmp(s2, "allow") == 0)
+		control = acl_allow;
+	else if(strcmp(s2, "deny") == 0)
+		control = acl_deny;
+	else if(strcmp(s2, "refuse") == 0)
+		control = acl_refuse;
+	else if(strcmp(s2, "allow_snoop") == 0)
+		control = acl_allow_snoop;
+	else {
+		log_err("access control type %s unknown", str);
+		return 0;
+	}
+	if(!netblockstrtoaddr(str, UNBOUND_DNS_PORT, &addr, &addrlen, &net)) {
+		log_err("cannot parse access control: %s %s", str, s2);
+		return 0;
+	}
+	if(!acl_list_insert(acl, &addr, addrlen, net, control, 
+		complain_duplicates)) {
+		log_err("out of memory");
+		return 0;
+	}
+	return 1;
+}
+
+/** read acl_list config */
+static int 
+read_acl_list(struct acl_list* acl, struct config_file* cfg)
+{
+	struct config_str2list* p;
+	for(p = cfg->acls; p; p = p->next) {
+		log_assert(p->str && p->str2);
+		if(!acl_list_str_cfg(acl, p->str, p->str2, 1))
+			return 0;
+	}
+	return 1;
+}
+
+int 
+acl_list_apply_cfg(struct acl_list* acl, struct config_file* cfg)
+{
+	regional_free_all(acl->region);
+	addr_tree_init(&acl->tree);
+	if(!read_acl_list(acl, cfg))
+		return 0;
+	/* insert defaults, with '0' to ignore them if they are duplicates */
+	if(!acl_list_str_cfg(acl, "0.0.0.0/0", "refuse", 0))
+		return 0;
+	if(!acl_list_str_cfg(acl, "127.0.0.0/8", "allow", 0))
+		return 0;
+	if(cfg->do_ip6) {
+		if(!acl_list_str_cfg(acl, "::0/0", "refuse", 0))
+			return 0;
+		if(!acl_list_str_cfg(acl, "::1", "allow", 0))
+			return 0;
+		if(!acl_list_str_cfg(acl, "::ffff:127.0.0.1", "allow", 0))
+			return 0;
+	}
+	addr_tree_init_parents(&acl->tree);
+	return 1;
+}
+
+enum acl_access 
+acl_list_lookup(struct acl_list* acl, struct sockaddr_storage* addr,
+        socklen_t addrlen)
+{
+	struct acl_addr* r = (struct acl_addr*)addr_tree_lookup(&acl->tree,
+		addr, addrlen);
+	if(r) return r->control;
+	return acl_deny;
+}
+
+size_t 
+acl_list_get_mem(struct acl_list* acl)
+{
+	if(!acl) return 0;
+	return sizeof(*acl) + regional_get_mem(acl->region);
+}
diff --git a/3rdParty/Unbound/src/src/daemon/acl_list.h b/3rdParty/Unbound/src/src/daemon/acl_list.h
new file mode 100644
index 0000000..03ac301
--- /dev/null
+++ b/3rdParty/Unbound/src/src/daemon/acl_list.h
@@ -0,0 +1,125 @@
+/*
+ * daemon/acl_list.h - client access control storage for the server.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file keeps track of the list of clients that are allowed to 
+ * access the server.
+ */
+
+#ifndef DAEMON_ACL_LIST_H
+#define DAEMON_ACL_LIST_H
+#include "util/storage/dnstree.h"
+struct config_file;
+struct regional;
+
+/**
+ * Enumeration of access control options for an address range.
+ * Allow or deny access.
+ */
+enum acl_access {
+	/** disallow any access whatsoever, drop it */
+	acl_deny = 0,
+	/** disallow access, send a polite 'REFUSED' reply */
+	acl_refuse,
+	/** allow full access for recursion (+RD) queries */
+	acl_allow,
+	/** allow full access for all queries, recursion and cache snooping */
+	acl_allow_snoop
+};
+
+/**
+ * Access control storage structure
+ */
+struct acl_list {
+	/** regional for allocation */
+	struct regional* region;
+	/** 
+	 * Tree of the addresses that are allowed/blocked.
+	 * contents of type acl_addr.
+	 */
+	rbtree_t tree;
+};
+
+/**
+ *
+ * An address span with access control information
+ */
+struct acl_addr {
+	/** node in address tree */
+	struct addr_tree_node node;
+	/** access control on this netblock */
+	enum acl_access control;
+};
+
+/**
+ * Create acl structure 
+ * @return new structure or NULL on error.
+ */
+struct acl_list* acl_list_create(void);
+
+/**
+ * Delete acl structure.
+ * @param acl: to delete.
+ */
+void acl_list_delete(struct acl_list* acl);
+
+/**
+ * Process access control config.
+ * @param acl: where to store.
+ * @param cfg: config options.
+ * @return 0 on error.
+ */
+int acl_list_apply_cfg(struct acl_list* acl, struct config_file* cfg);
+
+/**
+ * Lookup address to see its access control status.
+ * @param acl: structure for address storage.
+ * @param addr: address to check
+ * @param addrlen: length of addr.
+ * @return: what to do with message from this address.
+ */
+enum acl_access acl_list_lookup(struct acl_list* acl, 
+	struct sockaddr_storage* addr, socklen_t addrlen);
+
+/**
+ * Get memory used by acl structure.
+ * @param acl: structure for address storage.
+ * @return bytes in use.
+ */
+size_t acl_list_get_mem(struct acl_list* acl);
+
+#endif /* DAEMON_ACL_LIST_H */
diff --git a/3rdParty/Unbound/src/src/daemon/cachedump.c b/3rdParty/Unbound/src/src/daemon/cachedump.c
new file mode 100644
index 0000000..e5be856
--- /dev/null
+++ b/3rdParty/Unbound/src/src/daemon/cachedump.c
@@ -0,0 +1,988 @@
+/*
+ * daemon/cachedump.c - dump the cache to text format.
+ *
+ * Copyright (c) 2008, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to read and write the cache(s)
+ * to text format.
+ */
+#include "config.h"
+#include <ldns/ldns.h>
+#include "daemon/cachedump.h"
+#include "daemon/remote.h"
+#include "daemon/worker.h"
+#include "daemon/daemon.h"
+#include "services/cache/rrset.h"
+#include "services/cache/dns.h"
+#include "services/cache/infra.h"
+#include "services/modstack.h"
+#include "util/data/msgreply.h"
+#include "util/regional.h"
+#include "util/net_help.h"
+#include "util/data/dname.h"
+#include "iterator/iterator.h"
+#include "iterator/iter_delegpt.h"
+#include "iterator/iter_utils.h"
+#include "iterator/iter_fwd.h"
+#include "iterator/iter_hints.h"
+
+/** convert to ldns rr */
+static ldns_rr*
+to_rr(struct ub_packed_rrset_key* k, struct packed_rrset_data* d, 
+	uint32_t now, size_t i, uint16_t type)
+{
+	ldns_rr* rr = ldns_rr_new();
+	ldns_rdf* rdf;
+	ldns_status status;
+	size_t pos;
+	log_assert(i < d->count + d->rrsig_count);
+	if(!rr) {
+		return NULL;
+	}
+	ldns_rr_set_type(rr, type);
+	ldns_rr_set_class(rr, ntohs(k->rk.rrset_class));
+	if(d->rr_ttl[i] < now)
+		ldns_rr_set_ttl(rr, 0);
+	else	ldns_rr_set_ttl(rr, d->rr_ttl[i] - now);
+	pos = 0;
+	status = ldns_wire2dname(&rdf, k->rk.dname, k->rk.dname_len, &pos);
+	if(status != LDNS_STATUS_OK) {
+		/* we drop detailed error in status */
+		ldns_rr_free(rr);
+		return NULL;
+	}
+	ldns_rr_set_owner(rr, rdf);
+	pos = 0;
+	status = ldns_wire2rdf(rr, d->rr_data[i], d->rr_len[i], &pos);
+	if(status != LDNS_STATUS_OK) {
+		/* we drop detailed error in status */
+		ldns_rr_free(rr);
+		return NULL;
+	}
+	return rr;
+}
+
+/** dump one rrset zonefile line */
+static int
+dump_rrset_line(SSL* ssl, struct ub_packed_rrset_key* k,
+        struct packed_rrset_data* d, uint32_t now, size_t i, uint16_t type)
+{
+	char* s;
+	ldns_rr* rr = to_rr(k, d, now, i, type);
+	if(!rr) {
+		return ssl_printf(ssl, "BADRR\n");
+	}
+	s = ldns_rr2str(rr);
+	ldns_rr_free(rr);
+	if(!s) {
+		return ssl_printf(ssl, "BADRR\n");
+	}
+	if(!ssl_printf(ssl, "%s", s)) {
+		free(s);
+		return 0;
+	}
+	free(s);
+	return 1;
+}
+
+/** dump rrset key and data info */
+static int
+dump_rrset(SSL* ssl, struct ub_packed_rrset_key* k, 
+	struct packed_rrset_data* d, uint32_t now)
+{
+	size_t i;
+	/* rd lock held by caller */
+	if(!k || !d) return 1;
+	if(d->ttl < now) return 1; /* expired */
+
+	/* meta line */
+	if(!ssl_printf(ssl, ";rrset%s %u %u %u %d %d\n",
+		(k->rk.flags & PACKED_RRSET_NSEC_AT_APEX)?" nsec_apex":"",
+		(unsigned)(d->ttl - now),
+		(unsigned)d->count, (unsigned)d->rrsig_count,
+		(int)d->trust, (int)d->security
+		)) 
+		return 0;
+	for(i=0; i<d->count; i++) {
+		if(!dump_rrset_line(ssl, k, d, now, i, ntohs(k->rk.type)))
+			return 0;
+	}
+	for(i=0; i<d->rrsig_count; i++) {
+		if(!dump_rrset_line(ssl, k, d, now, i+d->count, 
+			LDNS_RR_TYPE_RRSIG))
+			return 0;
+	}
+	
+	return 1;
+}
+
+/** dump lruhash rrset cache */
+static int
+dump_rrset_lruhash(SSL* ssl, struct lruhash* h, uint32_t now)
+{
+	struct lruhash_entry* e;
+	/* lruhash already locked by caller */
+	/* walk in order of lru; best first */
+	for(e=h->lru_start; e; e = e->lru_next) {
+		lock_rw_rdlock(&e->lock);
+		if(!dump_rrset(ssl, (struct ub_packed_rrset_key*)e->key,
+			(struct packed_rrset_data*)e->data, now)) {
+			lock_rw_unlock(&e->lock);
+			return 0;
+		}
+		lock_rw_unlock(&e->lock);
+	}
+	return 1;
+}
+
+/** dump rrset cache */
+static int
+dump_rrset_cache(SSL* ssl, struct worker* worker)
+{
+	struct rrset_cache* r = worker->env.rrset_cache;
+	size_t slab;
+	if(!ssl_printf(ssl, "START_RRSET_CACHE\n")) return 0;
+	for(slab=0; slab<r->table.size; slab++) {
+		lock_quick_lock(&r->table.array[slab]->lock);
+		if(!dump_rrset_lruhash(ssl, r->table.array[slab],
+			*worker->env.now)) {
+			lock_quick_unlock(&r->table.array[slab]->lock);
+			return 0;
+		}
+		lock_quick_unlock(&r->table.array[slab]->lock);
+	}
+	return ssl_printf(ssl, "END_RRSET_CACHE\n");
+}
+
+/** dump message to rrset reference */
+static int
+dump_msg_ref(SSL* ssl, struct ub_packed_rrset_key* k)
+{
+	ldns_rdf* rdf;
+	ldns_status status;
+	size_t pos;
+	char* nm, *tp, *cl;
+
+	pos = 0;
+	status = ldns_wire2dname(&rdf, k->rk.dname, k->rk.dname_len, &pos);
+	if(status != LDNS_STATUS_OK) {
+		return ssl_printf(ssl, "BADREF\n");
+	}
+	nm = ldns_rdf2str(rdf);
+	ldns_rdf_deep_free(rdf);
+	tp = ldns_rr_type2str(ntohs(k->rk.type));
+	cl = ldns_rr_class2str(ntohs(k->rk.rrset_class));
+	if(!nm || !cl || !tp) {
+		free(nm);
+		free(tp);
+		free(cl);
+		return ssl_printf(ssl, "BADREF\n");
+	}
+	if(!ssl_printf(ssl, "%s %s %s %d\n", nm, cl, tp, (int)k->rk.flags)) {
+		free(nm);
+		free(tp);
+		free(cl);
+		return 0;
+	}
+	free(nm);
+	free(tp);
+	free(cl);
+
+	return 1;
+}
+
+/** dump message entry */
+static int
+dump_msg(SSL* ssl, struct query_info* k, struct reply_info* d, 
+	uint32_t now)
+{
+	size_t i;
+	char* nm, *tp, *cl;
+	ldns_rdf* rdf;
+	ldns_status status;
+	size_t pos;
+	if(!k || !d) return 1;
+	if(d->ttl < now) return 1; /* expired */
+	
+	pos = 0;
+	status = ldns_wire2dname(&rdf, k->qname, k->qname_len, &pos);
+	if(status != LDNS_STATUS_OK) {
+		return 1; /* skip this entry */
+	}
+	nm = ldns_rdf2str(rdf);
+	ldns_rdf_deep_free(rdf);
+	tp = ldns_rr_type2str(k->qtype);
+	cl = ldns_rr_class2str(k->qclass);
+	if(!nm || !tp || !cl) {
+		free(nm);
+		free(tp);
+		free(cl);
+		return 1; /* skip this entry */
+	}
+	if(!rrset_array_lock(d->ref, d->rrset_count, now)) {
+		/* rrsets have timed out or do not exist */
+		free(nm);
+		free(tp);
+		free(cl);
+		return 1; /* skip this entry */
+	}
+	
+	/* meta line */
+	if(!ssl_printf(ssl, "msg %s %s %s %d %d %u %d %u %u %u\n",
+			nm, cl, tp,
+			(int)d->flags, (int)d->qdcount, 
+			(unsigned)(d->ttl-now), (int)d->security,
+			(unsigned)d->an_numrrsets, 
+			(unsigned)d->ns_numrrsets,
+			(unsigned)d->ar_numrrsets)) {
+		free(nm);
+		free(tp);
+		free(cl);
+		rrset_array_unlock(d->ref, d->rrset_count);
+		return 0;
+	}
+	free(nm);
+	free(tp);
+	free(cl);
+	
+	for(i=0; i<d->rrset_count; i++) {
+		if(!dump_msg_ref(ssl, d->rrsets[i])) {
+			rrset_array_unlock(d->ref, d->rrset_count);
+			return 0;
+		}
+	}
+	rrset_array_unlock(d->ref, d->rrset_count);
+
+	return 1;
+}
+
+/** copy msg to worker pad */
+static int
+copy_msg(struct regional* region, struct lruhash_entry* e, 
+	struct query_info** k, struct reply_info** d)
+{
+	struct reply_info* rep = (struct reply_info*)e->data;
+	*d = (struct reply_info*)regional_alloc_init(region, e->data,
+		sizeof(struct reply_info) + 
+		sizeof(struct rrset_ref) * (rep->rrset_count-1) +
+		sizeof(struct ub_packed_rrset_key*) * rep->rrset_count);
+	if(!*d)
+		return 0;
+	(*d)->rrsets = (struct ub_packed_rrset_key**)(
+		(uint8_t*)(&((*d)->ref[0])) + 
+		sizeof(struct rrset_ref) * rep->rrset_count);
+	*k = (struct query_info*)regional_alloc_init(region, 
+		e->key, sizeof(struct query_info));
+	if(!*k)
+		return 0;
+	(*k)->qname = regional_alloc_init(region, 
+		(*k)->qname, (*k)->qname_len);
+	return (*k)->qname != NULL;
+}
+
+/** dump lruhash msg cache */
+static int
+dump_msg_lruhash(SSL* ssl, struct worker* worker, struct lruhash* h)
+{
+	struct lruhash_entry* e;
+	struct query_info* k;
+	struct reply_info* d;
+
+	/* lruhash already locked by caller */
+	/* walk in order of lru; best first */
+	for(e=h->lru_start; e; e = e->lru_next) {
+		regional_free_all(worker->scratchpad);
+		lock_rw_rdlock(&e->lock);
+		/* make copy of rrset in worker buffer */
+		if(!copy_msg(worker->scratchpad, e, &k, &d)) {
+			lock_rw_unlock(&e->lock);
+			return 0;
+		}
+		lock_rw_unlock(&e->lock);
+		/* release lock so we can lookup the rrset references 
+		 * in the rrset cache */
+		if(!dump_msg(ssl, k, d, *worker->env.now)) {
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/** dump msg cache */
+static int
+dump_msg_cache(SSL* ssl, struct worker* worker)
+{
+	struct slabhash* sh = worker->env.msg_cache;
+	size_t slab;
+	if(!ssl_printf(ssl, "START_MSG_CACHE\n")) return 0;
+	for(slab=0; slab<sh->size; slab++) {
+		lock_quick_lock(&sh->array[slab]->lock);
+		if(!dump_msg_lruhash(ssl, worker, sh->array[slab])) {
+			lock_quick_unlock(&sh->array[slab]->lock);
+			return 0;
+		}
+		lock_quick_unlock(&sh->array[slab]->lock);
+	}
+	return ssl_printf(ssl, "END_MSG_CACHE\n");
+}
+
+int
+dump_cache(SSL* ssl, struct worker* worker)
+{
+	if(!dump_rrset_cache(ssl, worker))
+		return 0;
+	if(!dump_msg_cache(ssl, worker))
+		return 0;
+	return ssl_printf(ssl, "EOF\n");
+}
+
+/** read a line from ssl into buffer */
+static int
+ssl_read_buf(SSL* ssl, ldns_buffer* buf)
+{
+	return ssl_read_line(ssl, (char*)ldns_buffer_begin(buf), 
+		ldns_buffer_capacity(buf));
+}
+
+/** check fixed text on line */
+static int
+read_fixed(SSL* ssl, ldns_buffer* buf, const char* str)
+{
+	if(!ssl_read_buf(ssl, buf)) return 0;
+	return (strcmp((char*)ldns_buffer_begin(buf), str) == 0);
+}
+
+/** load an RR into rrset */
+static int
+load_rr(SSL* ssl, ldns_buffer* buf, struct regional* region,
+	struct ub_packed_rrset_key* rk, struct packed_rrset_data* d,
+	unsigned int i, int is_rrsig, int* go_on, uint32_t now)
+{
+	ldns_rr* rr;
+	ldns_status status;
+
+	/* read the line */
+	if(!ssl_read_buf(ssl, buf))
+		return 0;
+	if(strncmp((char*)ldns_buffer_begin(buf), "BADRR\n", 6) == 0) {
+		*go_on = 0;
+		return 1;
+	}
+	status = ldns_rr_new_frm_str(&rr, (char*)ldns_buffer_begin(buf),
+		LDNS_DEFAULT_TTL, NULL, NULL);
+	if(status != LDNS_STATUS_OK) {
+		log_warn("error cannot parse rr: %s: %s",
+			ldns_get_errorstr_by_id(status),
+			(char*)ldns_buffer_begin(buf));
+		return 0;
+	}
+	if(is_rrsig && ldns_rr_get_type(rr) != LDNS_RR_TYPE_RRSIG) {
+		log_warn("error expected rrsig but got %s",
+			(char*)ldns_buffer_begin(buf));
+		return 0;
+	}
+
+	/* convert ldns rr into packed_rr */
+	d->rr_ttl[i] = ldns_rr_ttl(rr) + now;
+	ldns_buffer_clear(buf);
+	ldns_buffer_skip(buf, 2);
+	status = ldns_rr_rdata2buffer_wire(buf, rr);
+	if(status != LDNS_STATUS_OK) {
+		log_warn("error cannot rr2wire: %s",
+			ldns_get_errorstr_by_id(status));
+		ldns_rr_free(rr);
+		return 0;
+	}
+	ldns_buffer_flip(buf);
+	ldns_buffer_write_u16_at(buf, 0, ldns_buffer_limit(buf) - 2);
+
+	d->rr_len[i] = ldns_buffer_limit(buf);
+	d->rr_data[i] = (uint8_t*)regional_alloc_init(region, 
+		ldns_buffer_begin(buf), ldns_buffer_limit(buf));
+	if(!d->rr_data[i]) {
+		ldns_rr_free(rr);
+		log_warn("error out of memory");
+		return 0;
+	}
+
+	/* if first entry, fill the key structure */
+	if(i==0) {
+		rk->rk.type = htons(ldns_rr_get_type(rr));
+		rk->rk.rrset_class = htons(ldns_rr_get_class(rr));
+		ldns_buffer_clear(buf);
+		status = ldns_dname2buffer_wire(buf, ldns_rr_owner(rr));
+		if(status != LDNS_STATUS_OK) {
+			log_warn("error cannot dname2buffer: %s",
+				ldns_get_errorstr_by_id(status));
+			ldns_rr_free(rr);
+			return 0;
+		}
+		ldns_buffer_flip(buf);
+		rk->rk.dname_len = ldns_buffer_limit(buf);
+		rk->rk.dname = regional_alloc_init(region, 
+			ldns_buffer_begin(buf), ldns_buffer_limit(buf));
+		if(!rk->rk.dname) {
+			log_warn("error out of memory");
+			ldns_rr_free(rr);
+			return 0;
+		}
+	}
+	ldns_rr_free(rr);
+
+	return 1;
+}
+
+/** move entry into cache */
+static int
+move_into_cache(struct ub_packed_rrset_key* k, 
+	struct packed_rrset_data* d, struct worker* worker)
+{
+	struct ub_packed_rrset_key* ak;
+	struct packed_rrset_data* ad;
+	size_t s, i, num = d->count + d->rrsig_count;
+	struct rrset_ref ref;
+	uint8_t* p;
+
+	ak = alloc_special_obtain(&worker->alloc);
+	if(!ak) {
+		log_warn("error out of memory");
+		return 0;
+	}
+	ak->entry.data = NULL;
+	ak->rk = k->rk;
+	ak->entry.hash = rrset_key_hash(&k->rk);
+	ak->rk.dname = (uint8_t*)memdup(k->rk.dname, k->rk.dname_len);
+	if(!ak->rk.dname) {
+		log_warn("error out of memory");
+		ub_packed_rrset_parsedelete(ak, &worker->alloc);
+		return 0;
+	}
+	s = sizeof(*ad) + (sizeof(size_t) + sizeof(uint8_t*) + 
+		sizeof(uint32_t))* num;
+	for(i=0; i<num; i++)
+		s += d->rr_len[i];
+	ad = (struct packed_rrset_data*)malloc(s);
+	if(!ad) {
+		log_warn("error out of memory");
+		ub_packed_rrset_parsedelete(ak, &worker->alloc);
+		return 0;
+	}
+	p = (uint8_t*)ad;
+	memmove(p, d, sizeof(*ad));
+	p += sizeof(*ad);
+	memmove(p, &d->rr_len[0], sizeof(size_t)*num);
+	p += sizeof(size_t)*num;
+	memmove(p, &d->rr_data[0], sizeof(uint8_t*)*num);
+	p += sizeof(uint8_t*)*num;
+	memmove(p, &d->rr_ttl[0], sizeof(uint32_t)*num);
+	p += sizeof(uint32_t)*num;
+	for(i=0; i<num; i++) {
+		memmove(p, d->rr_data[i], d->rr_len[i]);
+		p += d->rr_len[i];
+	}
+	packed_rrset_ptr_fixup(ad);
+
+	ak->entry.data = ad;
+
+	ref.key = ak;
+	ref.id = ak->id;
+	(void)rrset_cache_update(worker->env.rrset_cache, &ref,
+		&worker->alloc, *worker->env.now);
+	return 1;
+}
+
+/** load an rrset entry */
+static int
+load_rrset(SSL* ssl, ldns_buffer* buf, struct worker* worker)
+{
+	char* s = (char*)ldns_buffer_begin(buf);
+	struct regional* region = worker->scratchpad;
+	struct ub_packed_rrset_key* rk;
+	struct packed_rrset_data* d;
+	unsigned int ttl, rr_count, rrsig_count, trust, security;
+	unsigned int i;
+	int go_on = 1;
+	regional_free_all(region);
+
+	rk = (struct ub_packed_rrset_key*)regional_alloc_zero(region, 
+		sizeof(*rk));
+	d = (struct packed_rrset_data*)regional_alloc_zero(region, sizeof(*d));
+	if(!rk || !d) {
+		log_warn("error out of memory");
+		return 0;
+	}
+
+	if(strncmp(s, ";rrset", 6) != 0) {
+		log_warn("error expected ';rrset' but got %s", s);
+		return 0;
+	}
+	s += 6;
+	if(strncmp(s, " nsec_apex", 10) == 0) {
+		s += 10;
+		rk->rk.flags |= PACKED_RRSET_NSEC_AT_APEX;
+	}
+	if(sscanf(s, " %u %u %u %u %u", &ttl, &rr_count, &rrsig_count,
+		&trust, &security) != 5) {
+		log_warn("error bad rrset spec %s", s);
+		return 0;
+	}
+	if(rr_count == 0 && rrsig_count == 0) {
+		log_warn("bad rrset without contents");
+		return 0;
+	}
+	d->count = (size_t)rr_count;
+	d->rrsig_count = (size_t)rrsig_count;
+	d->security = (enum sec_status)security;
+	d->trust = (enum rrset_trust)trust;
+	d->ttl = (uint32_t)ttl + *worker->env.now;
+
+	d->rr_len = regional_alloc_zero(region, 
+		sizeof(size_t)*(d->count+d->rrsig_count));
+	d->rr_ttl = regional_alloc_zero(region, 
+		sizeof(uint32_t)*(d->count+d->rrsig_count));
+	d->rr_data = regional_alloc_zero(region, 
+		sizeof(uint8_t*)*(d->count+d->rrsig_count));
+	if(!d->rr_len || !d->rr_ttl || !d->rr_data) {
+		log_warn("error out of memory");
+		return 0;
+	}
+	
+	/* read the rr's themselves */
+	for(i=0; i<rr_count; i++) {
+		if(!load_rr(ssl, buf, region, rk, d, i, 0, 
+			&go_on, *worker->env.now)) {
+			log_warn("could not read rr %u", i);
+			return 0;
+		}
+	}
+	for(i=0; i<rrsig_count; i++) {
+		if(!load_rr(ssl, buf, region, rk, d, i+rr_count, 1, 
+			&go_on, *worker->env.now)) {
+			log_warn("could not read rrsig %u", i);
+			return 0;
+		}
+	}
+	if(!go_on) {
+		/* skip this entry */
+		return 1;
+	}
+
+	return move_into_cache(rk, d, worker);
+}
+
+/** load rrset cache */
+static int
+load_rrset_cache(SSL* ssl, struct worker* worker)
+{
+	ldns_buffer* buf = worker->env.scratch_buffer;
+	if(!read_fixed(ssl, buf, "START_RRSET_CACHE")) return 0;
+	while(ssl_read_buf(ssl, buf) && 
+		strcmp((char*)ldns_buffer_begin(buf), "END_RRSET_CACHE")!=0) {
+		if(!load_rrset(ssl, buf, worker))
+			return 0;
+	}
+	return 1;
+}
+
+/** read qinfo from next three words */
+static char*
+load_qinfo(char* str, struct query_info* qinfo, ldns_buffer* buf, 
+	struct regional* region)
+{
+	/* s is part of the buf */
+	char* s = str;
+	ldns_rr* rr;
+	ldns_status status;
+
+	/* skip three words */
+	s = strchr(str, ' ');
+	if(s) s = strchr(s+1, ' ');
+	if(s) s = strchr(s+1, ' ');
+	if(!s) {
+		log_warn("error line too short, %s", str);
+		return NULL;
+	}
+	s[0] = 0;
+	s++;
+
+	/* parse them */
+	status = ldns_rr_new_question_frm_str(&rr, str, NULL, NULL);
+	if(status != LDNS_STATUS_OK) {
+		log_warn("error cannot parse: %s %s",
+			ldns_get_errorstr_by_id(status), str);
+		return NULL;
+	}
+	qinfo->qtype = ldns_rr_get_type(rr);
+	qinfo->qclass = ldns_rr_get_class(rr);
+	ldns_buffer_clear(buf);
+	status = ldns_dname2buffer_wire(buf, ldns_rr_owner(rr));
+	ldns_rr_free(rr);
+	if(status != LDNS_STATUS_OK) {
+		log_warn("error cannot dname2wire: %s", 
+			ldns_get_errorstr_by_id(status));
+		return NULL;
+	}
+	ldns_buffer_flip(buf);
+	qinfo->qname_len = ldns_buffer_limit(buf);
+	qinfo->qname = (uint8_t*)regional_alloc_init(region, 
+		ldns_buffer_begin(buf), ldns_buffer_limit(buf));
+	if(!qinfo->qname) {
+		log_warn("error out of memory");
+		return NULL;
+	}
+
+	return s;
+}
+
+/** load a msg rrset reference */
+static int
+load_ref(SSL* ssl, ldns_buffer* buf, struct worker* worker, 
+	struct regional *region, struct ub_packed_rrset_key** rrset, 
+	int* go_on)
+{
+	char* s = (char*)ldns_buffer_begin(buf);
+	struct query_info qinfo;
+	unsigned int flags;
+	struct ub_packed_rrset_key* k;
+
+	/* read line */
+	if(!ssl_read_buf(ssl, buf))
+		return 0;
+	if(strncmp(s, "BADREF", 6) == 0) {
+		*go_on = 0; /* its bad, skip it and skip message */
+		return 1;
+	}
+
+	s = load_qinfo(s, &qinfo, buf, region);
+	if(!s) {
+		return 0;
+	}
+	if(sscanf(s, " %u", &flags) != 1) {
+		log_warn("error cannot parse flags: %s", s);
+		return 0;
+	}
+
+	/* lookup in cache */
+	k = rrset_cache_lookup(worker->env.rrset_cache, qinfo.qname,
+		qinfo.qname_len, qinfo.qtype, qinfo.qclass,
+		(uint32_t)flags, *worker->env.now, 0);
+	if(!k) {
+		/* not found or expired */
+		*go_on = 0;
+		return 1;
+	}
+
+	/* store in result */
+	*rrset = packed_rrset_copy_region(k, region, *worker->env.now);
+	lock_rw_unlock(&k->entry.lock);
+
+	return (*rrset != NULL);
+}
+
+/** load a msg entry */
+static int
+load_msg(SSL* ssl, ldns_buffer* buf, struct worker* worker)
+{
+	struct regional* region = worker->scratchpad;
+	struct query_info qinf;
+	struct reply_info rep;
+	char* s = (char*)ldns_buffer_begin(buf);
+	unsigned int flags, qdcount, ttl, security, an, ns, ar;
+	size_t i;
+	int go_on = 1;
+
+	regional_free_all(region);
+
+	if(strncmp(s, "msg ", 4) != 0) {
+		log_warn("error expected msg but got %s", s);
+		return 0;
+	}
+	s += 4;
+	s = load_qinfo(s, &qinf, buf, region);
+	if(!s) {
+		return 0;
+	}
+
+	/* read remainder of line */
+	if(sscanf(s, " %u %u %u %u %u %u %u", &flags, &qdcount, &ttl, 
+		&security, &an, &ns, &ar) != 7) {
+		log_warn("error cannot parse numbers: %s", s);
+		return 0;
+	}
+	rep.flags = (uint16_t)flags;
+	rep.qdcount = (uint16_t)qdcount;
+	rep.ttl = (uint32_t)ttl;
+	rep.prefetch_ttl = PREFETCH_TTL_CALC(rep.ttl);
+	rep.security = (enum sec_status)security;
+	rep.an_numrrsets = (size_t)an;
+	rep.ns_numrrsets = (size_t)ns;
+	rep.ar_numrrsets = (size_t)ar;
+	rep.rrset_count = (size_t)an+(size_t)ns+(size_t)ar;
+	rep.rrsets = (struct ub_packed_rrset_key**)regional_alloc_zero(
+		region, sizeof(struct ub_packed_rrset_key*)*rep.rrset_count);
+
+	/* fill repinfo with references */
+	for(i=0; i<rep.rrset_count; i++) {
+		if(!load_ref(ssl, buf, worker, region, &rep.rrsets[i], 
+			&go_on)) {
+			return 0;
+		}
+	}
+
+	if(!go_on) 
+		return 1; /* skip this one, not all references satisfied */
+
+	if(!dns_cache_store(&worker->env, &qinf, &rep, 0, 0, NULL)) {
+		log_warn("error out of memory");
+		return 0;
+	}
+	return 1;
+}
+
+/** load msg cache */
+static int
+load_msg_cache(SSL* ssl, struct worker* worker)
+{
+	ldns_buffer* buf = worker->env.scratch_buffer;
+	if(!read_fixed(ssl, buf, "START_MSG_CACHE")) return 0;
+	while(ssl_read_buf(ssl, buf) && 
+		strcmp((char*)ldns_buffer_begin(buf), "END_MSG_CACHE")!=0) {
+		if(!load_msg(ssl, buf, worker))
+			return 0;
+	}
+	return 1;
+}
+
+int
+load_cache(SSL* ssl, struct worker* worker)
+{
+	if(!load_rrset_cache(ssl, worker))
+		return 0;
+	if(!load_msg_cache(ssl, worker))
+		return 0;
+	return read_fixed(ssl, worker->env.scratch_buffer, "EOF");
+}
+
+/** print details on a delegation point */
+static void
+print_dp_details(SSL* ssl, struct worker* worker, struct delegpt* dp)
+{
+	char buf[257];
+	struct delegpt_addr* a;
+	int lame, dlame, rlame, rto, edns_vs, to, delay, entry_ttl;
+	struct rtt_info ri;
+	uint8_t edns_lame_known;
+	for(a = dp->target_list; a; a = a->next_target) {
+		addr_to_str(&a->addr, a->addrlen, buf, sizeof(buf));
+		if(!ssl_printf(ssl, "%-16s\t", buf))
+			return;
+		if(a->bogus) {
+			if(!ssl_printf(ssl, "Address is BOGUS. ")) 
+				return;
+		}
+		/* lookup in infra cache */
+		delay=0;
+		entry_ttl = infra_get_host_rto(worker->env.infra_cache,
+			&a->addr, a->addrlen, dp->name, dp->namelen,
+			&ri, &delay, *worker->env.now);
+		if(entry_ttl == -2 && ri.rto >= USEFUL_SERVER_TOP_TIMEOUT) {
+			if(!ssl_printf(ssl, "expired, rto %d msec.\n", ri.rto))
+				return;
+			continue;
+		}
+		if(entry_ttl == -1 || entry_ttl == -2) {
+			if(!ssl_printf(ssl, "not in infra cache.\n"))
+				return;
+			continue; /* skip stuff not in infra cache */
+		}
+
+		/* uses type_A because most often looked up, but other
+		 * lameness won't be reported then */
+		if(!infra_get_lame_rtt(worker->env.infra_cache, 
+			&a->addr, a->addrlen, dp->name, dp->namelen,
+			LDNS_RR_TYPE_A, &lame, &dlame, &rlame, &rto,
+			*worker->env.now)) {
+			if(!ssl_printf(ssl, "not in infra cache.\n"))
+				return;
+			continue; /* skip stuff not in infra cache */
+		}
+		if(!ssl_printf(ssl, "%s%s%s%srto %d msec, ttl %d, ping %d "
+			"var %d rtt %d",
+			lame?"LAME ":"", dlame?"NoDNSSEC ":"",
+			a->lame?"AddrWasParentSide ":"",
+			rlame?"NoAuthButRecursive ":"", rto, entry_ttl,
+			ri.srtt, ri.rttvar, rtt_notimeout(&ri)))
+			return;
+		if(delay)
+			if(!ssl_printf(ssl, ", probedelay %d", delay))
+				return;
+		if(infra_host(worker->env.infra_cache, &a->addr, a->addrlen,
+			dp->name, dp->namelen, *worker->env.now, &edns_vs,
+			&edns_lame_known, &to)) {
+			if(edns_vs == -1) {
+				if(!ssl_printf(ssl, ", noEDNS%s.",
+					edns_lame_known?" probed":" assumed"))
+					return;
+			} else {
+				if(!ssl_printf(ssl, ", EDNS %d%s.", edns_vs,
+					edns_lame_known?" probed":" assumed"))
+					return;
+			}
+		}
+		if(!ssl_printf(ssl, "\n"))
+			return;
+	}
+}
+
+/** print main dp info */
+static void
+print_dp_main(SSL* ssl, struct delegpt* dp, struct dns_msg* msg)
+{
+	size_t i, n_ns, n_miss, n_addr, n_res, n_avail;
+
+	/* print the dp */
+	if(msg)
+	    for(i=0; i<msg->rep->rrset_count; i++) {
+		struct ub_packed_rrset_key* k = msg->rep->rrsets[i];
+		struct packed_rrset_data* d = 
+			(struct packed_rrset_data*)k->entry.data;
+		if(d->security == sec_status_bogus) {
+			if(!ssl_printf(ssl, "Address is BOGUS:\n"))
+				return;
+		}
+		if(!dump_rrset(ssl, k, d, 0))
+			return;
+	    }
+	delegpt_count_ns(dp, &n_ns, &n_miss);
+	delegpt_count_addr(dp, &n_addr, &n_res, &n_avail);
+	/* since dp has not been used by iterator, all are available*/
+	if(!ssl_printf(ssl, "Delegation with %d names, of which %d "
+		"can be examined to query further addresses.\n"
+		"%sIt provides %d IP addresses.\n", 
+		(int)n_ns, (int)n_miss, (dp->bogus?"It is BOGUS. ":""),
+		(int)n_addr))
+		return;
+}
+
+int print_deleg_lookup(SSL* ssl, struct worker* worker, uint8_t* nm,
+	size_t nmlen, int ATTR_UNUSED(nmlabs))
+{
+	/* deep links into the iterator module */
+	struct delegpt* dp;
+	struct dns_msg* msg;
+	struct regional* region = worker->scratchpad;
+	char b[260];
+	struct query_info qinfo;
+	struct iter_hints_stub* stub;
+	struct iter_env* ie;
+	regional_free_all(region);
+	qinfo.qname = nm;
+	qinfo.qname_len = nmlen;
+	qinfo.qtype = LDNS_RR_TYPE_A;
+	qinfo.qclass = LDNS_RR_CLASS_IN;
+
+	if(modstack_find(&worker->daemon->mods, "iterator") == -1) {
+		return ssl_printf(ssl, "error: no iterator module\n");
+	}
+	ie = (struct iter_env*)worker->env.modinfo[modstack_find(&worker->
+		daemon->mods, "iterator")];
+
+	dname_str(nm, b);
+	if(!ssl_printf(ssl, "The following name servers are used for lookup "
+		"of %s\n", b)) 
+		return 0;
+	
+	dp = forwards_lookup(worker->env.fwds, nm, qinfo.qclass);
+	if(dp) {
+		if(!ssl_printf(ssl, "forwarding request:\n"))
+			return 0;
+		print_dp_main(ssl, dp, NULL);
+		print_dp_details(ssl, worker, dp);
+		return 1;
+	}
+	
+	while(1) {
+		dp = dns_cache_find_delegation(&worker->env, nm, nmlen, 
+			qinfo.qtype, qinfo.qclass, region, &msg, 
+			*worker->env.now);
+		if(!dp) {
+			return ssl_printf(ssl, "no delegation from "
+				"cache; goes to configured roots\n");
+		}
+		/* go up? */
+		if(iter_dp_is_useless(&qinfo, BIT_RD, dp)) {
+			print_dp_main(ssl, dp, msg);
+			print_dp_details(ssl, worker, dp);
+			if(!ssl_printf(ssl, "cache delegation was "
+				"useless (no IP addresses)\n"))
+				return 0;
+			if(dname_is_root(nm)) {
+				/* goes to root config */
+				return ssl_printf(ssl, "no delegation from "
+					"cache; goes to configured roots\n");
+			} else {
+				/* useless, goes up */
+				nm = dp->name;
+				nmlen = dp->namelen;
+				dname_remove_label(&nm, &nmlen);
+				dname_str(nm, b);
+				if(!ssl_printf(ssl, "going up, lookup %s\n", b))
+					return 0;
+				continue;
+			}
+		} 
+		stub = hints_lookup_stub(ie->hints, nm, qinfo.qclass, dp);
+		if(stub) {
+			if(stub->noprime) {
+				if(!ssl_printf(ssl, "The noprime stub servers "
+					"are used:\n"))
+					return 0;
+			} else {
+				if(!ssl_printf(ssl, "The stub is primed "
+						"with servers:\n"))
+					return 0;
+			}
+			print_dp_main(ssl, stub->dp, NULL);
+			print_dp_details(ssl, worker, stub->dp);
+		} else {
+			print_dp_main(ssl, dp, msg);
+			print_dp_details(ssl, worker, dp);
+		}
+		break;
+	}
+
+	return 1;
+}
diff --git a/3rdParty/Unbound/src/src/daemon/cachedump.h b/3rdParty/Unbound/src/src/daemon/cachedump.h
new file mode 100644
index 0000000..da9804f
--- /dev/null
+++ b/3rdParty/Unbound/src/src/daemon/cachedump.h
@@ -0,0 +1,107 @@
+/*
+ * daemon/cachedump.h - dump the cache to text format.
+ *
+ * Copyright (c) 2008, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to read and write the cache(s)
+ * to text format.
+ *
+ * The format of the file is as follows:
+ * [RRset cache]
+ * [Message cache]
+ * EOF		-- fixed string "EOF" before end of the file.
+ *
+ * The RRset cache is:
+ * START_RRSET_CACHE
+ * [rrset]*
+ * END_RRSET_CACHE
+ *
+ * rrset is:
+ * ;rrset [nsec_apex] TTL rr_count rrsig_count trust security
+ * resource records, one per line, in zonefile format
+ * rrsig records, one per line, in zonefile format
+ * If the text conversion fails, BADRR is printed on the line.
+ *
+ * The Message cache is:
+ * START_MSG_CACHE
+ * [msg]*
+ * END_MSG_CACHE
+ *
+ * msg is:
+ * msg name class type flags qdcount ttl security an ns ar
+ * list of rrset references, one per line. If conversion fails, BADREF
+ * reference is:
+ * name class type flags
+ *
+ * Expired cache entries are not printed.
+ */
+
+#ifndef DAEMON_DUMPCACHE_H
+#define DAEMON_DUMPCACHE_H
+struct worker;
+
+/**
+ * Dump cache(s) to text
+ * @param ssl: to print to
+ * @param worker: worker that is available (buffers, etc) and has 
+ * 	ptrs to the caches.
+ * @return false on ssl print error.
+ */
+int dump_cache(SSL* ssl, struct worker* worker);
+
+/**
+ * Load cache(s) from text 
+ * @param ssl: to read from 
+ * @param worker: worker that is available (buffers, etc) and has 
+ * 	ptrs to the caches.
+ * @return false on ssl error.
+ */
+int load_cache(SSL* ssl, struct worker* worker);
+
+/**
+ * Print the delegation used to lookup for this name.
+ * @param ssl: to read from 
+ * @param worker: worker that is available (buffers, etc) and has 
+ * 	ptrs to the caches.
+ * @param nm: name to lookup
+ * @param nmlen: length of name.
+ * @param nmlabs: labels in name.
+ * @return false on ssl error.
+ */
+int print_deleg_lookup(SSL* ssl, struct worker* worker, uint8_t* nm,
+	size_t nmlen, int nmlabs);
+
+#endif /* DAEMON_DUMPCACHE_H */
diff --git a/3rdParty/Unbound/src/src/daemon/daemon.c b/3rdParty/Unbound/src/src/daemon/daemon.c
new file mode 100644
index 0000000..9d6ce9f
--- /dev/null
+++ b/3rdParty/Unbound/src/src/daemon/daemon.c
@@ -0,0 +1,589 @@
+/*
+ * daemon/daemon.c - collection of workers that handles requests.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * The daemon consists of global settings and a number of workers.
+ */
+
+#include "config.h"
+#ifdef HAVE_OPENSSL_ERR_H
+#include <openssl/err.h>
+#endif
+
+#ifdef HAVE_OPENSSL_RAND_H
+#include <openssl/rand.h>
+#endif
+
+#ifdef HAVE_OPENSSL_CONF_H
+#include <openssl/conf.h>
+#endif
+
+#ifdef HAVE_OPENSSL_ENGINE_H
+#include <openssl/engine.h>
+#endif
+#include <ldns/ldns.h>
+#include "daemon/daemon.h"
+#include "daemon/worker.h"
+#include "daemon/remote.h"
+#include "daemon/acl_list.h"
+#include "util/log.h"
+#include "util/config_file.h"
+#include "util/data/msgreply.h"
+#include "util/storage/lookup3.h"
+#include "util/storage/slabhash.h"
+#include "services/listen_dnsport.h"
+#include "services/cache/rrset.h"
+#include "services/cache/infra.h"
+#include "services/localzone.h"
+#include "services/modstack.h"
+#include "util/module.h"
+#include "util/random.h"
+#include "util/tube.h"
+#include <signal.h>
+
+/** How many quit requests happened. */
+static int sig_record_quit = 0;
+/** How many reload requests happened. */
+static int sig_record_reload = 0;
+
+#if HAVE_DECL_SSL_COMP_GET_COMPRESSION_METHODS
+/** cleaner ssl memory freeup */
+static void* comp_meth = NULL;
+#endif
+#ifdef LEX_HAS_YYLEX_DESTROY
+/** remove buffers for parsing and init */
+int ub_c_lex_destroy(void);
+#endif
+
+/** used when no other sighandling happens, so we don't die
+  * when multiple signals in quick succession are sent to us. 
+  * @param sig: signal number.
+  * @return signal handler return type (void or int).
+  */
+static RETSIGTYPE record_sigh(int sig)
+{
+#ifdef LIBEVENT_SIGNAL_PROBLEM
+	verbose(VERB_OPS, "quit on signal, no cleanup and statistics, "
+		"because installed libevent version is not threadsafe");
+	exit(0);
+#endif 
+	switch(sig)
+	{
+		case SIGTERM:
+#ifdef SIGQUIT
+		case SIGQUIT:
+#endif
+#ifdef SIGBREAK
+		case SIGBREAK:
+#endif
+		case SIGINT:
+			sig_record_quit++;
+			break;
+#ifdef SIGHUP
+		case SIGHUP:
+			sig_record_reload++;
+			break;
+#endif
+#ifdef SIGPIPE
+		case SIGPIPE:
+			break;
+#endif
+		default:
+			log_err("ignoring signal %d", sig);
+	}
+}
+
+/** 
+ * Signal handling during the time when netevent is disabled.
+ * Stores signals to replay later.
+ */
+static void
+signal_handling_record(void)
+{
+	if( signal(SIGTERM, record_sigh) == SIG_ERR ||
+#ifdef SIGQUIT
+		signal(SIGQUIT, record_sigh) == SIG_ERR ||
+#endif
+#ifdef SIGBREAK
+		signal(SIGBREAK, record_sigh) == SIG_ERR ||
+#endif
+#ifdef SIGHUP
+		signal(SIGHUP, record_sigh) == SIG_ERR ||
+#endif
+#ifdef SIGPIPE
+		signal(SIGPIPE, SIG_IGN) == SIG_ERR ||
+#endif
+		signal(SIGINT, record_sigh) == SIG_ERR
+		)
+		log_err("install sighandler: %s", strerror(errno));
+}
+
+/**
+ * Replay old signals.
+ * @param wrk: worker that handles signals.
+ */
+static void
+signal_handling_playback(struct worker* wrk)
+{
+#ifdef SIGHUP
+	if(sig_record_reload)
+		worker_sighandler(SIGHUP, wrk);
+#endif
+	if(sig_record_quit)
+		worker_sighandler(SIGTERM, wrk);
+	sig_record_quit = 0;
+	sig_record_reload = 0;
+}
+
+struct daemon* 
+daemon_init(void)
+{
+	struct daemon* daemon = (struct daemon*)calloc(1, 
+		sizeof(struct daemon));
+#ifdef USE_WINSOCK
+	int r;
+	WSADATA wsa_data;
+#endif
+	if(!daemon)
+		return NULL;
+#ifdef USE_WINSOCK
+	r = WSAStartup(MAKEWORD(2,2), &wsa_data);
+	if(r != 0) {
+		fatal_exit("could not init winsock. WSAStartup: %s",
+			wsa_strerror(r));
+	}
+#endif /* USE_WINSOCK */
+	signal_handling_record();
+	checklock_start();
+	ERR_load_crypto_strings();
+	ERR_load_SSL_strings();
+#ifdef HAVE_OPENSSL_CONFIG
+	OPENSSL_config("unbound");
+#endif
+#ifdef USE_GOST
+	(void)ldns_key_EVP_load_gost_id();
+#endif
+	OpenSSL_add_all_algorithms();
+#if HAVE_DECL_SSL_COMP_GET_COMPRESSION_METHODS
+	/* grab the COMP method ptr because openssl leaks it */
+	comp_meth = (void*)SSL_COMP_get_compression_methods();
+#endif
+	(void)SSL_library_init();
+#ifdef HAVE_TZSET
+	/* init timezone info while we are not chrooted yet */
+	tzset();
+#endif
+	/* open /dev/random if needed */
+	ub_systemseed((unsigned)time(NULL)^(unsigned)getpid()^0xe67);
+	daemon->need_to_exit = 0;
+	modstack_init(&daemon->mods);
+	if(!(daemon->env = (struct module_env*)calloc(1, 
+		sizeof(*daemon->env)))) {
+		free(daemon);
+		return NULL;
+	}
+	alloc_init(&daemon->superalloc, NULL, 0);
+	daemon->acl = acl_list_create();
+	if(!daemon->acl) {
+		free(daemon->env);
+		free(daemon);
+		return NULL;
+	}
+	if(gettimeofday(&daemon->time_boot, NULL) < 0)
+		log_err("gettimeofday: %s", strerror(errno));
+	daemon->time_last_stat = daemon->time_boot;
+	return daemon;	
+}
+
+int 
+daemon_open_shared_ports(struct daemon* daemon)
+{
+	log_assert(daemon);
+	if(daemon->cfg->port != daemon->listening_port) {
+		listening_ports_free(daemon->ports);
+		if(!(daemon->ports=listening_ports_open(daemon->cfg)))
+			return 0;
+		daemon->listening_port = daemon->cfg->port;
+	}
+	if(!daemon->cfg->remote_control_enable && daemon->rc_port) {
+		listening_ports_free(daemon->rc_ports);
+		daemon->rc_ports = NULL;
+		daemon->rc_port = 0;
+	}
+	if(daemon->cfg->remote_control_enable && 
+		daemon->cfg->control_port != daemon->rc_port) {
+		listening_ports_free(daemon->rc_ports);
+		if(!(daemon->rc_ports=daemon_remote_open_ports(daemon->cfg)))
+			return 0;
+		daemon->rc_port = daemon->cfg->control_port;
+	}
+	return 1;
+}
+
+/**
+ * Setup modules. setup module stack.
+ * @param daemon: the daemon
+ */
+static void daemon_setup_modules(struct daemon* daemon)
+{
+	daemon->env->cfg = daemon->cfg;
+	daemon->env->alloc = &daemon->superalloc;
+	daemon->env->worker = NULL;
+	daemon->env->need_to_validate = 0; /* set by module init below */
+	if(!modstack_setup(&daemon->mods, daemon->cfg->module_conf, 
+		daemon->env)) {
+		fatal_exit("failed to setup modules");
+	}
+}
+
+/**
+ * Obtain allowed port numbers, concatenate the list, and shuffle them
+ * (ready to be handed out to threads).
+ * @param daemon: the daemon. Uses rand and cfg.
+ * @param shufport: the portlist output.
+ * @return number of ports available.
+ */
+static int daemon_get_shufport(struct daemon* daemon, int* shufport)
+{
+	int i, n, k, temp;
+	int avail = 0;
+	for(i=0; i<65536; i++) {
+		if(daemon->cfg->outgoing_avail_ports[i]) {
+			shufport[avail++] = daemon->cfg->
+				outgoing_avail_ports[i];
+		}
+	}
+	if(avail == 0)
+		fatal_exit("no ports are permitted for UDP, add "
+			"with outgoing-port-permit");
+        /* Knuth shuffle */
+	n = avail;
+	while(--n > 0) {
+		k = ub_random_max(daemon->rand, n+1); /* 0<= k<= n */
+		temp = shufport[k];
+		shufport[k] = shufport[n];
+		shufport[n] = temp;
+	}
+	return avail;
+}
+
+/**
+ * Allocate empty worker structures. With backptr and thread-number,
+ * from 0..numthread initialised. Used as user arguments to new threads.
+ * Creates the daemon random generator if it does not exist yet.
+ * The random generator stays existing between reloads with a unique state.
+ * @param daemon: the daemon with (new) config settings.
+ */
+static void 
+daemon_create_workers(struct daemon* daemon)
+{
+	int i, numport;
+	int* shufport;
+	log_assert(daemon && daemon->cfg);
+	if(!daemon->rand) {
+		unsigned int seed = (unsigned int)time(NULL) ^ 
+			(unsigned int)getpid() ^ 0x438;
+		daemon->rand = ub_initstate(seed, NULL);
+		if(!daemon->rand)
+			fatal_exit("could not init random generator");
+	}
+	hash_set_raninit((uint32_t)ub_random(daemon->rand));
+	shufport = (int*)calloc(65536, sizeof(int));
+	if(!shufport)
+		fatal_exit("out of memory during daemon init");
+	numport = daemon_get_shufport(daemon, shufport);
+	verbose(VERB_ALGO, "total of %d outgoing ports available", numport);
+	
+	daemon->num = (daemon->cfg->num_threads?daemon->cfg->num_threads:1);
+	daemon->workers = (struct worker**)calloc((size_t)daemon->num, 
+		sizeof(struct worker*));
+	for(i=0; i<daemon->num; i++) {
+		if(!(daemon->workers[i] = worker_create(daemon, i,
+			shufport+numport*i/daemon->num, 
+			numport*(i+1)/daemon->num - numport*i/daemon->num)))
+			/* the above is not ports/numthr, due to rounding */
+			fatal_exit("could not create worker");
+	}
+	free(shufport);
+}
+
+#ifdef THREADS_DISABLED
+/**
+ * Close all pipes except for the numbered thread.
+ * @param daemon: daemon to close pipes in.
+ * @param thr: thread number 0..num-1 of thread to skip.
+ */
+static void close_other_pipes(struct daemon* daemon, int thr)
+{
+	int i;
+	for(i=0; i<daemon->num; i++)
+		if(i!=thr) {
+			if(i==0) {
+				/* only close read part, need to write stats */
+				tube_close_read(daemon->workers[i]->cmd);
+			} else {
+				/* complete close channel to others */
+				tube_delete(daemon->workers[i]->cmd);
+				daemon->workers[i]->cmd = NULL;
+			}
+		}
+}
+#endif /* THREADS_DISABLED */
+
+/**
+ * Function to start one thread. 
+ * @param arg: user argument.
+ * @return: void* user return value could be used for thread_join results.
+ */
+static void* 
+thread_start(void* arg)
+{
+	struct worker* worker = (struct worker*)arg;
+	log_thread_set(&worker->thread_num);
+	ub_thread_blocksigs();
+#ifdef THREADS_DISABLED
+	/* close pipe ends used by main */
+	tube_close_write(worker->cmd);
+	close_other_pipes(worker->daemon, worker->thread_num);
+#endif
+	if(!worker_init(worker, worker->daemon->cfg, worker->daemon->ports, 0))
+		fatal_exit("Could not initialize thread");
+
+	worker_work(worker);
+	return NULL;
+}
+
+/**
+ * Fork and init the other threads. Main thread returns for special handling.
+ * @param daemon: the daemon with other threads to fork.
+ */
+static void
+daemon_start_others(struct daemon* daemon)
+{
+	int i;
+	log_assert(daemon);
+	verbose(VERB_ALGO, "start threads");
+	/* skip i=0, is this thread */
+	for(i=1; i<daemon->num; i++) {
+		ub_thread_create(&daemon->workers[i]->thr_id,
+			thread_start, daemon->workers[i]);
+#ifdef THREADS_DISABLED
+		/* close pipe end of child */
+		tube_close_read(daemon->workers[i]->cmd);
+#endif /* no threads */
+	}
+}
+
+/**
+ * Stop the other threads.
+ * @param daemon: the daemon with other threads.
+ */
+static void
+daemon_stop_others(struct daemon* daemon)
+{
+	int i;
+	log_assert(daemon);
+	verbose(VERB_ALGO, "stop threads");
+	/* skip i=0, is this thread */
+	/* use i=0 buffer for sending cmds; because we are #0 */
+	for(i=1; i<daemon->num; i++) {
+		worker_send_cmd(daemon->workers[i], worker_cmd_quit);
+	}
+	/* wait for them to quit */
+	for(i=1; i<daemon->num; i++) {
+		/* join it to make sure its dead */
+		verbose(VERB_ALGO, "join %d", i);
+		ub_thread_join(daemon->workers[i]->thr_id);
+		verbose(VERB_ALGO, "join success %d", i);
+	}
+}
+
+void 
+daemon_fork(struct daemon* daemon)
+{
+	log_assert(daemon);
+	if(!acl_list_apply_cfg(daemon->acl, daemon->cfg))
+		fatal_exit("Could not setup access control list");
+	if(!(daemon->local_zones = local_zones_create()))
+		fatal_exit("Could not create local zones: out of memory");
+	if(!local_zones_apply_cfg(daemon->local_zones, daemon->cfg))
+		fatal_exit("Could not set up local zones");
+
+	/* setup modules */
+	daemon_setup_modules(daemon);
+
+	/* first create all the worker structures, so we can pass
+	 * them to the newly created threads. 
+	 */
+	daemon_create_workers(daemon);
+
+#if defined(HAVE_EV_LOOP) || defined(HAVE_EV_DEFAULT_LOOP)
+	/* in libev the first inited base gets signals */
+	if(!worker_init(daemon->workers[0], daemon->cfg, daemon->ports, 1))
+		fatal_exit("Could not initialize main thread");
+#endif
+	
+	/* Now create the threads and init the workers.
+	 * By the way, this is thread #0 (the main thread).
+	 */
+	daemon_start_others(daemon);
+
+	/* Special handling for the main thread. This is the thread
+	 * that handles signals and remote control.
+	 */
+#if !(defined(HAVE_EV_LOOP) || defined(HAVE_EV_DEFAULT_LOOP))
+	/* libevent has the last inited base get signals (or any base) */
+	if(!worker_init(daemon->workers[0], daemon->cfg, daemon->ports, 1))
+		fatal_exit("Could not initialize main thread");
+#endif
+	signal_handling_playback(daemon->workers[0]);
+
+	/* Start resolver service on main thread. */
+	log_info("start of service (%s).", PACKAGE_STRING);
+	worker_work(daemon->workers[0]);
+	log_info("service stopped (%s).", PACKAGE_STRING);
+
+	/* we exited! a signal happened! Stop other threads */
+	daemon_stop_others(daemon);
+
+	daemon->need_to_exit = daemon->workers[0]->need_to_exit;
+}
+
+void 
+daemon_cleanup(struct daemon* daemon)
+{
+	int i;
+	log_assert(daemon);
+	/* before stopping main worker, handle signals ourselves, so we
+	   don't die on multiple reload signals for example. */
+	signal_handling_record();
+	log_thread_set(NULL);
+	/* clean up caches because
+	 * a) RRset IDs will be recycled after a reload, causing collisions
+	 * b) validation config can change, thus rrset, msg, keycache clear 
+	 * The infra cache is kept, the timing and edns info is still valid */
+	slabhash_clear(&daemon->env->rrset_cache->table);
+	slabhash_clear(daemon->env->msg_cache);
+	local_zones_delete(daemon->local_zones);
+	daemon->local_zones = NULL;
+	/* key cache is cleared by module desetup during next daemon_init() */
+	daemon_remote_clear(daemon->rc);
+	for(i=0; i<daemon->num; i++)
+		worker_delete(daemon->workers[i]);
+	free(daemon->workers);
+	daemon->workers = NULL;
+	daemon->num = 0;
+	daemon->cfg = NULL;
+}
+
+void 
+daemon_delete(struct daemon* daemon)
+{
+	if(!daemon)
+		return;
+	modstack_desetup(&daemon->mods, daemon->env);
+	daemon_remote_delete(daemon->rc);
+	listening_ports_free(daemon->ports);
+	listening_ports_free(daemon->rc_ports);
+	if(daemon->env) {
+		slabhash_delete(daemon->env->msg_cache);
+		rrset_cache_delete(daemon->env->rrset_cache);
+		infra_delete(daemon->env->infra_cache);
+	}
+	ub_randfree(daemon->rand);
+	alloc_clear(&daemon->superalloc);
+	acl_list_delete(daemon->acl);
+	free(daemon->chroot);
+	free(daemon->pidfile);
+	free(daemon->env);
+	SSL_CTX_free((SSL_CTX*)daemon->listen_sslctx);
+	SSL_CTX_free((SSL_CTX*)daemon->connect_sslctx);
+	free(daemon);
+#ifdef LEX_HAS_YYLEX_DESTROY
+	/* lex cleanup */
+	ub_c_lex_destroy();
+#endif
+	/* libcrypto cleanup */
+#if defined(USE_GOST) && defined(HAVE_LDNS_KEY_EVP_UNLOAD_GOST)
+	ldns_key_EVP_unload_gost();
+#endif
+#if HAVE_DECL_SSL_COMP_GET_COMPRESSION_METHODS && HAVE_DECL_SK_SSL_COMP_POP_FREE
+#ifndef S_SPLINT_S
+	sk_SSL_COMP_pop_free(comp_meth, (void(*)())CRYPTO_free);
+#endif
+#endif
+#ifdef HAVE_OPENSSL_CONFIG
+	EVP_cleanup();
+	ENGINE_cleanup();
+	CONF_modules_free();
+#endif
+	CRYPTO_cleanup_all_ex_data(); /* safe, no more threads right now */
+	ERR_remove_state(0);
+	ERR_free_strings();
+	RAND_cleanup();
+	checklock_stop();
+#ifdef USE_WINSOCK
+	if(WSACleanup() != 0) {
+		log_err("Could not WSACleanup: %s", 
+			wsa_strerror(WSAGetLastError()));
+	}
+#endif
+}
+
+void daemon_apply_cfg(struct daemon* daemon, struct config_file* cfg)
+{
+        daemon->cfg = cfg;
+	config_apply(cfg);
+	if(!daemon->env->msg_cache ||
+	   cfg->msg_cache_size != slabhash_get_size(daemon->env->msg_cache) ||
+	   cfg->msg_cache_slabs != daemon->env->msg_cache->size) {
+		slabhash_delete(daemon->env->msg_cache);
+		daemon->env->msg_cache = slabhash_create(cfg->msg_cache_slabs,
+			HASH_DEFAULT_STARTARRAY, cfg->msg_cache_size,
+			msgreply_sizefunc, query_info_compare,
+			query_entry_delete, reply_info_delete, NULL);
+		if(!daemon->env->msg_cache) {
+			fatal_exit("malloc failure updating config settings");
+		}
+	}
+	if((daemon->env->rrset_cache = rrset_cache_adjust(
+		daemon->env->rrset_cache, cfg, &daemon->superalloc)) == 0)
+		fatal_exit("malloc failure updating config settings");
+	if((daemon->env->infra_cache = infra_adjust(daemon->env->infra_cache,
+		cfg))==0)
+		fatal_exit("malloc failure updating config settings");
+}
diff --git a/3rdParty/Unbound/src/src/daemon/daemon.h b/3rdParty/Unbound/src/src/daemon/daemon.h
new file mode 100644
index 0000000..8e47ea0
--- /dev/null
+++ b/3rdParty/Unbound/src/src/daemon/daemon.h
@@ -0,0 +1,150 @@
+/*
+ * daemon/daemon.h - collection of workers that handles requests.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * The daemon consists of global settings and a number of workers.
+ */
+
+#ifndef DAEMON_H
+#define DAEMON_H
+
+#include "util/locks.h"
+#include "util/alloc.h"
+#include "services/modstack.h"
+#ifdef UB_ON_WINDOWS
+#  include "util/winsock_event.h"
+#endif
+struct config_file;
+struct worker;
+struct listen_port;
+struct slabhash;
+struct module_env;
+struct rrset_cache;
+struct acl_list;
+struct local_zones;
+struct ub_randstate;
+struct daemon_remote;
+
+/**
+ * Structure holding worker list.
+ * Holds globally visible information.
+ */
+struct daemon {
+	/** The config settings */
+	struct config_file* cfg;
+	/** the chroot dir in use, NULL if none */
+	char* chroot;
+	/** pidfile that is used */
+	char* pidfile;
+	/** port number that has ports opened. */
+	int listening_port;
+	/** listening ports, opened, to be shared by threads */
+	struct listen_port* ports;
+	/** port number for remote that has ports opened. */
+	int rc_port;
+	/** listening ports for remote control */
+	struct listen_port* rc_ports;
+	/** remote control connections management (for first worker) */
+	struct daemon_remote* rc;
+	/** ssl context for listening to dnstcp over ssl, and connecting ssl */
+	void* listen_sslctx, *connect_sslctx;
+	/** num threads allocated */
+	int num;
+	/** the worker entries */
+	struct worker** workers;
+	/** do we need to exit unbound (or is it only a reload?) */
+	int need_to_exit;
+	/** master random table ; used for port div between threads on reload*/
+	struct ub_randstate* rand;
+	/** master allocation cache */
+	struct alloc_cache superalloc;
+	/** the module environment master value, copied and changed by threads*/
+	struct module_env* env;
+	/** stack of module callbacks */
+	struct module_stack mods;
+	/** access control, which client IPs are allowed to connect */
+	struct acl_list* acl;
+	/** local authority zones */
+	struct local_zones* local_zones;
+	/** last time of statistics printout */
+	struct timeval time_last_stat;
+	/** time when daemon started */
+	struct timeval time_boot;
+};
+
+/**
+ * Initialize daemon structure.
+ * @return: The daemon structure, or NULL on error.
+ */
+struct daemon* daemon_init(void);
+
+/**
+ * Open shared listening ports (if needed).
+ * The cfg member pointer must have been set for the daemon.
+ * @param daemon: the daemon.
+ * @return: false on error.
+ */
+int daemon_open_shared_ports(struct daemon* daemon);
+
+/**
+ * Fork workers and start service.
+ * When the routine exits, it is no longer forked.
+ * @param daemon: the daemon.
+ */
+void daemon_fork(struct daemon* daemon);
+
+/**
+ * Close off the worker thread information.
+ * Bring the daemon back into state ready for daemon_fork again.
+ * @param daemon: the daemon.
+ */
+void daemon_cleanup(struct daemon* daemon);
+
+/**
+ * Delete workers, close listening ports.
+ * @param daemon: the daemon.
+ */
+void daemon_delete(struct daemon* daemon);
+
+/**
+ * Apply config settings.
+ * @param daemon: the daemon.
+ * @param cfg: new config settings.
+ */
+void daemon_apply_cfg(struct daemon* daemon, struct config_file* cfg);
+
+#endif /* DAEMON_H */
diff --git a/3rdParty/Unbound/src/src/daemon/remote.c b/3rdParty/Unbound/src/src/daemon/remote.c
new file mode 100644
index 0000000..a2b2204
--- /dev/null
+++ b/3rdParty/Unbound/src/src/daemon/remote.c
@@ -0,0 +1,1975 @@
+/*
+ * daemon/remote.c - remote control for the unbound daemon.
+ *
+ * Copyright (c) 2008, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains the remote control functionality for the daemon.
+ * The remote control can be performed using either the commandline
+ * unbound-control tool, or a SSLv3/TLS capable web browser. 
+ * The channel is secured using SSLv3 or TLSv1, and certificates.
+ * Both the server and the client(control tool) have their own keys.
+ */
+#include "config.h"
+#ifdef HAVE_OPENSSL_ERR_H
+#include <openssl/err.h>
+#endif
+#include <ctype.h>
+#include <ldns/ldns.h>
+#include "daemon/remote.h"
+#include "daemon/worker.h"
+#include "daemon/daemon.h"
+#include "daemon/stats.h"
+#include "daemon/cachedump.h"
+#include "util/log.h"
+#include "util/config_file.h"
+#include "util/net_help.h"
+#include "util/module.h"
+#include "services/listen_dnsport.h"
+#include "services/cache/rrset.h"
+#include "services/cache/infra.h"
+#include "services/mesh.h"
+#include "services/localzone.h"
+#include "util/storage/slabhash.h"
+#include "util/fptr_wlist.h"
+#include "util/data/dname.h"
+#include "validator/validator.h"
+#include "validator/val_kcache.h"
+#include "validator/val_kentry.h"
+#include "iterator/iterator.h"
+#include "iterator/iter_fwd.h"
+#include "iterator/iter_hints.h"
+#include "iterator/iter_delegpt.h"
+#include "services/outbound_list.h"
+#include "services/outside_network.h"
+
+#ifdef HAVE_SYS_TYPES_H
+#  include <sys/types.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+/* just for portability */
+#ifdef SQ
+#undef SQ
+#endif
+
+/** what to put on statistics lines between var and value, ": " or "=" */
+#define SQ "="
+/** if true, inhibits a lot of =0 lines from the stats output */
+static const int inhibit_zero = 1;
+
+/** subtract timers and the values do not overflow or become negative */
+static void
+timeval_subtract(struct timeval* d, const struct timeval* end, 
+	const struct timeval* start)
+{
+#ifndef S_SPLINT_S
+	time_t end_usec = end->tv_usec;
+	d->tv_sec = end->tv_sec - start->tv_sec;
+	if(end_usec < start->tv_usec) {
+		end_usec += 1000000;
+		d->tv_sec--;
+	}
+	d->tv_usec = end_usec - start->tv_usec;
+#endif
+}
+
+/** divide sum of timers to get average */
+static void
+timeval_divide(struct timeval* avg, const struct timeval* sum, size_t d)
+{
+#ifndef S_SPLINT_S
+	size_t leftover;
+	if(d == 0) {
+		avg->tv_sec = 0;
+		avg->tv_usec = 0;
+		return;
+	}
+	avg->tv_sec = sum->tv_sec / d;
+	avg->tv_usec = sum->tv_usec / d;
+	/* handle fraction from seconds divide */
+	leftover = sum->tv_sec - avg->tv_sec*d;
+	avg->tv_usec += (leftover*1000000)/d;
+#endif
+}
+
+struct daemon_remote*
+daemon_remote_create(struct config_file* cfg)
+{
+	char* s_cert;
+	char* s_key;
+	struct daemon_remote* rc = (struct daemon_remote*)calloc(1, 
+		sizeof(*rc));
+	if(!rc) {
+		log_err("out of memory in daemon_remote_create");
+		return NULL;
+	}
+	rc->max_active = 10;
+
+	if(!cfg->remote_control_enable) {
+		rc->ctx = NULL;
+		return rc;
+	}
+	rc->ctx = SSL_CTX_new(SSLv23_server_method());
+	if(!rc->ctx) {
+		log_crypto_err("could not SSL_CTX_new");
+		free(rc);
+		return NULL;
+	}
+	/* no SSLv2 because has defects */
+	if(!(SSL_CTX_set_options(rc->ctx, SSL_OP_NO_SSLv2) & SSL_OP_NO_SSLv2)){
+		log_crypto_err("could not set SSL_OP_NO_SSLv2");
+		daemon_remote_delete(rc);
+		return NULL;
+	}
+	s_cert = fname_after_chroot(cfg->server_cert_file, cfg, 1);
+	s_key = fname_after_chroot(cfg->server_key_file, cfg, 1);
+	if(!s_cert || !s_key) {
+		log_err("out of memory in remote control fname");
+		goto setup_error;
+	}
+	verbose(VERB_ALGO, "setup SSL certificates");
+	if (!SSL_CTX_use_certificate_file(rc->ctx,s_cert,SSL_FILETYPE_PEM)) {
+		log_err("Error for server-cert-file: %s", s_cert);
+		log_crypto_err("Error in SSL_CTX use_certificate_file");
+		goto setup_error;
+	}
+	if(!SSL_CTX_use_PrivateKey_file(rc->ctx,s_key,SSL_FILETYPE_PEM)) {
+		log_err("Error for server-key-file: %s", s_key);
+		log_crypto_err("Error in SSL_CTX use_PrivateKey_file");
+		goto setup_error;
+	}
+	if(!SSL_CTX_check_private_key(rc->ctx)) {
+		log_err("Error for server-key-file: %s", s_key);
+		log_crypto_err("Error in SSL_CTX check_private_key");
+		goto setup_error;
+	}
+	if(!SSL_CTX_load_verify_locations(rc->ctx, s_cert, NULL)) {
+		log_crypto_err("Error setting up SSL_CTX verify locations");
+	setup_error:
+		free(s_cert);
+		free(s_key);
+		daemon_remote_delete(rc);
+		return NULL;
+	}
+	SSL_CTX_set_client_CA_list(rc->ctx, SSL_load_client_CA_file(s_cert));
+	SSL_CTX_set_verify(rc->ctx, SSL_VERIFY_PEER, NULL);
+	free(s_cert);
+	free(s_key);
+
+	return rc;
+}
+
+void daemon_remote_clear(struct daemon_remote* rc)
+{
+	struct rc_state* p, *np;
+	if(!rc) return;
+	/* but do not close the ports */
+	listen_list_delete(rc->accept_list);
+	rc->accept_list = NULL;
+	/* do close these sockets */
+	p = rc->busy_list;
+	while(p) {
+		np = p->next;
+		if(p->ssl)
+			SSL_free(p->ssl);
+		comm_point_delete(p->c);
+		free(p);
+		p = np;
+	}
+	rc->busy_list = NULL;
+	rc->active = 0;
+	rc->worker = NULL;
+}
+
+void daemon_remote_delete(struct daemon_remote* rc)
+{
+	if(!rc) return;
+	daemon_remote_clear(rc);
+	if(rc->ctx) {
+		SSL_CTX_free(rc->ctx);
+	}
+	free(rc);
+}
+
+/**
+ * Add and open a new control port
+ * @param ip: ip str
+ * @param nr: port nr
+ * @param list: list head
+ * @param noproto_is_err: if lack of protocol support is an error.
+ * @return false on failure.
+ */
+static int
+add_open(const char* ip, int nr, struct listen_port** list, int noproto_is_err)
+{
+	struct addrinfo hints;
+	struct addrinfo* res;
+	struct listen_port* n;
+	int noproto;
+	int fd, r;
+	char port[15];
+	snprintf(port, sizeof(port), "%d", nr);
+	port[sizeof(port)-1]=0;
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
+	if((r = getaddrinfo(ip, port, &hints, &res)) != 0 || !res) {
+#ifdef USE_WINSOCK
+		if(!noproto_is_err && r == EAI_NONAME) {
+			/* tried to lookup the address as name */
+			return 1; /* return success, but do nothing */
+		}
+#endif /* USE_WINSOCK */
+                log_err("control interface %s:%s getaddrinfo: %s %s",
+			ip?ip:"default", port, gai_strerror(r),
+#ifdef EAI_SYSTEM
+			r==EAI_SYSTEM?(char*)strerror(errno):""
+#else
+			""
+#endif
+			);
+		return 0;
+	}
+
+	/* open fd */
+	fd = create_tcp_accept_sock(res, 1, &noproto);
+	freeaddrinfo(res);
+	if(fd == -1 && noproto) {
+		if(!noproto_is_err)
+			return 1; /* return success, but do nothing */
+		log_err("cannot open control interface %s %d : "
+			"protocol not supported", ip, nr);
+		return 0;
+	}
+	if(fd == -1) {
+		log_err("cannot open control interface %s %d", ip, nr);
+		return 0;
+	}
+
+	/* alloc */
+	n = (struct listen_port*)calloc(1, sizeof(*n));
+	if(!n) {
+#ifndef USE_WINSOCK
+		close(fd);
+#else
+		closesocket(fd);
+#endif
+		log_err("out of memory");
+		return 0;
+	}
+	n->next = *list;
+	*list = n;
+	n->fd = fd;
+	return 1;
+}
+
+struct listen_port* daemon_remote_open_ports(struct config_file* cfg)
+{
+	struct listen_port* l = NULL;
+	log_assert(cfg->remote_control_enable && cfg->control_port);
+	if(cfg->control_ifs) {
+		struct config_strlist* p;
+		for(p = cfg->control_ifs; p; p = p->next) {
+			if(!add_open(p->str, cfg->control_port, &l, 1)) {
+				listening_ports_free(l);
+				return NULL;
+			}
+		}
+	} else {
+		/* defaults */
+		if(cfg->do_ip6 &&
+			!add_open("::1", cfg->control_port, &l, 0)) {
+			listening_ports_free(l);
+			return NULL;
+		}
+		if(cfg->do_ip4 &&
+			!add_open("127.0.0.1", cfg->control_port, &l, 1)) {
+			listening_ports_free(l);
+			return NULL;
+		}
+	}
+	return l;
+}
+
+/** open accept commpoint */
+static int
+accept_open(struct daemon_remote* rc, int fd)
+{
+	struct listen_list* n = (struct listen_list*)malloc(sizeof(*n));
+	if(!n) {
+		log_err("out of memory");
+		return 0;
+	}
+	n->next = rc->accept_list;
+	rc->accept_list = n;
+	/* open commpt */
+	n->com = comm_point_create_raw(rc->worker->base, fd, 0, 
+		&remote_accept_callback, rc);
+	if(!n->com)
+		return 0;
+	/* keep this port open, its fd is kept in the rc portlist */
+	n->com->do_not_close = 1;
+	return 1;
+}
+
+int daemon_remote_open_accept(struct daemon_remote* rc, 
+	struct listen_port* ports, struct worker* worker)
+{
+	struct listen_port* p;
+	rc->worker = worker;
+	for(p = ports; p; p = p->next) {
+		if(!accept_open(rc, p->fd)) {
+			log_err("could not create accept comm point");
+			return 0;
+		}
+	}
+	return 1;
+}
+
+int remote_accept_callback(struct comm_point* c, void* arg, int err, 
+	struct comm_reply* ATTR_UNUSED(rep))
+{
+	struct daemon_remote* rc = (struct daemon_remote*)arg;
+	struct sockaddr_storage addr;
+	socklen_t addrlen;
+	int newfd;
+	struct rc_state* n;
+	if(err != NETEVENT_NOERROR) {
+		log_err("error %d on remote_accept_callback", err);
+		return 0;
+	}
+	/* perform the accept */
+	newfd = comm_point_perform_accept(c, &addr, &addrlen);
+	if(newfd == -1)
+		return 0;
+	/* create new commpoint unless we are servicing already */
+	if(rc->active >= rc->max_active) {
+		log_warn("drop incoming remote control: too many connections");
+	close_exit:
+#ifndef USE_WINSOCK
+		close(newfd);
+#else
+		closesocket(newfd);
+#endif
+		return 0;
+	}
+
+	/* setup commpoint to service the remote control command */
+	n = (struct rc_state*)calloc(1, sizeof(*n));
+	if(!n) {
+		log_err("out of memory");
+		goto close_exit;
+	}
+	/* start in reading state */
+	n->c = comm_point_create_raw(rc->worker->base, newfd, 0, 
+		&remote_control_callback, n);
+	if(!n->c) {
+		log_err("out of memory");
+		free(n);
+		goto close_exit;
+	}
+	log_addr(VERB_QUERY, "new control connection from", &addr, addrlen);
+	n->c->do_not_close = 0;
+	comm_point_stop_listening(n->c);
+	comm_point_start_listening(n->c, -1, REMOTE_CONTROL_TCP_TIMEOUT);
+	memcpy(&n->c->repinfo.addr, &addr, addrlen);
+	n->c->repinfo.addrlen = addrlen;
+	n->shake_state = rc_hs_read;
+	n->ssl = SSL_new(rc->ctx);
+	if(!n->ssl) {
+		log_crypto_err("could not SSL_new");
+		comm_point_delete(n->c);
+		free(n);
+		goto close_exit;
+	}
+	SSL_set_accept_state(n->ssl);
+        (void)SSL_set_mode(n->ssl, SSL_MODE_AUTO_RETRY);
+	if(!SSL_set_fd(n->ssl, newfd)) {
+		log_crypto_err("could not SSL_set_fd");
+		SSL_free(n->ssl);
+		comm_point_delete(n->c);
+		free(n);
+		goto close_exit;
+	}
+
+	n->rc = rc;
+	n->next = rc->busy_list;
+	rc->busy_list = n;
+	rc->active ++;
+
+	/* perform the first nonblocking read already, for windows, 
+	 * so it can return wouldblock. could be faster too. */
+	(void)remote_control_callback(n->c, n, NETEVENT_NOERROR, NULL);
+	return 0;
+}
+
+/** delete from list */
+static void
+state_list_remove_elem(struct rc_state** list, struct comm_point* c)
+{
+	while(*list) {
+		if( (*list)->c == c) {
+			*list = (*list)->next;
+			return;
+		}
+		list = &(*list)->next;
+	}
+}
+
+/** decrease active count and remove commpoint from busy list */
+static void
+clean_point(struct daemon_remote* rc, struct rc_state* s)
+{
+	state_list_remove_elem(&rc->busy_list, s->c);
+	rc->active --;
+	if(s->ssl) {
+		SSL_shutdown(s->ssl);
+		SSL_free(s->ssl);
+	}
+	comm_point_delete(s->c);
+	free(s);
+}
+
+int
+ssl_print_text(SSL* ssl, const char* text)
+{
+	int r;
+	if(!ssl) 
+		return 0;
+	ERR_clear_error();
+	if((r=SSL_write(ssl, text, (int)strlen(text))) <= 0) {
+		if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) {
+			verbose(VERB_QUERY, "warning, in SSL_write, peer "
+				"closed connection");
+			return 0;
+		}
+		log_crypto_err("could not SSL_write");
+		return 0;
+	}
+	return 1;
+}
+
+/** print text over the ssl connection */
+static int
+ssl_print_vmsg(SSL* ssl, const char* format, va_list args)
+{
+	char msg[1024];
+	vsnprintf(msg, sizeof(msg), format, args);
+	return ssl_print_text(ssl, msg);
+}
+
+/** printf style printing to the ssl connection */
+int ssl_printf(SSL* ssl, const char* format, ...)
+{
+	va_list args;
+	int ret;
+	va_start(args, format);
+	ret = ssl_print_vmsg(ssl, format, args);
+	va_end(args);
+	return ret;
+}
+
+int
+ssl_read_line(SSL* ssl, char* buf, size_t max)
+{
+	int r;
+	size_t len = 0;
+	if(!ssl)
+		return 0;
+	while(len < max) {
+		ERR_clear_error();
+		if((r=SSL_read(ssl, buf+len, 1)) <= 0) {
+			if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) {
+				buf[len] = 0;
+				return 1;
+			}
+			log_crypto_err("could not SSL_read");
+			return 0;
+		}
+		if(buf[len] == '\n') {
+			/* return string without \n */
+			buf[len] = 0;
+			return 1;
+		}
+		len++;
+	}
+	buf[max-1] = 0;
+	log_err("control line too long (%d): %s", (int)max, buf);
+	return 0;
+}
+
+/** skip whitespace, return new pointer into string */
+static char*
+skipwhite(char* str)
+{
+	/* EOS \0 is not a space */
+	while( isspace(*str) ) 
+		str++;
+	return str;
+}
+
+/** send the OK to the control client */
+static void send_ok(SSL* ssl)
+{
+	(void)ssl_printf(ssl, "ok\n");
+}
+
+/** do the stop command */
+static void
+do_stop(SSL* ssl, struct daemon_remote* rc)
+{
+	rc->worker->need_to_exit = 1;
+	comm_base_exit(rc->worker->base);
+	send_ok(ssl);
+}
+
+/** do the reload command */
+static void
+do_reload(SSL* ssl, struct daemon_remote* rc)
+{
+	rc->worker->need_to_exit = 0;
+	comm_base_exit(rc->worker->base);
+	send_ok(ssl);
+}
+
+/** do the verbosity command */
+static void
+do_verbosity(SSL* ssl, char* str)
+{
+	int val = atoi(str);
+	if(val == 0 && strcmp(str, "0") != 0) {
+		ssl_printf(ssl, "error in verbosity number syntax: %s\n", str);
+		return;
+	}
+	verbosity = val;
+	send_ok(ssl);
+}
+
+/** print stats from statinfo */
+static int
+print_stats(SSL* ssl, const char* nm, struct stats_info* s)
+{
+	struct timeval avg;
+	if(!ssl_printf(ssl, "%s.num.queries"SQ"%u\n", nm, 
+		(unsigned)s->svr.num_queries)) return 0;
+	if(!ssl_printf(ssl, "%s.num.cachehits"SQ"%u\n", nm, 
+		(unsigned)(s->svr.num_queries 
+			- s->svr.num_queries_missed_cache))) return 0;
+	if(!ssl_printf(ssl, "%s.num.cachemiss"SQ"%u\n", nm, 
+		(unsigned)s->svr.num_queries_missed_cache)) return 0;
+	if(!ssl_printf(ssl, "%s.num.prefetch"SQ"%u\n", nm, 
+		(unsigned)s->svr.num_queries_prefetch)) return 0;
+	if(!ssl_printf(ssl, "%s.num.recursivereplies"SQ"%u\n", nm, 
+		(unsigned)s->mesh_replies_sent)) return 0;
+	if(!ssl_printf(ssl, "%s.requestlist.avg"SQ"%g\n", nm,
+		(s->svr.num_queries_missed_cache+s->svr.num_queries_prefetch)?
+			(double)s->svr.sum_query_list_size/
+			(s->svr.num_queries_missed_cache+
+			s->svr.num_queries_prefetch) : 0.0)) return 0;
+	if(!ssl_printf(ssl, "%s.requestlist.max"SQ"%u\n", nm,
+		(unsigned)s->svr.max_query_list_size)) return 0;
+	if(!ssl_printf(ssl, "%s.requestlist.overwritten"SQ"%u\n", nm,
+		(unsigned)s->mesh_jostled)) return 0;
+	if(!ssl_printf(ssl, "%s.requestlist.exceeded"SQ"%u\n", nm,
+		(unsigned)s->mesh_dropped)) return 0;
+	if(!ssl_printf(ssl, "%s.requestlist.current.all"SQ"%u\n", nm,
+		(unsigned)s->mesh_num_states)) return 0;
+	if(!ssl_printf(ssl, "%s.requestlist.current.user"SQ"%u\n", nm,
+		(unsigned)s->mesh_num_reply_states)) return 0;
+	timeval_divide(&avg, &s->mesh_replies_sum_wait, s->mesh_replies_sent);
+	if(!ssl_printf(ssl, "%s.recursion.time.avg"SQ"%d.%6.6d\n", nm,
+		(int)avg.tv_sec, (int)avg.tv_usec)) return 0;
+	if(!ssl_printf(ssl, "%s.recursion.time.median"SQ"%g\n", nm, 
+		s->mesh_time_median)) return 0;
+	return 1;
+}
+
+/** print stats for one thread */
+static int
+print_thread_stats(SSL* ssl, int i, struct stats_info* s)
+{
+	char nm[16];
+	snprintf(nm, sizeof(nm), "thread%d", i);
+	nm[sizeof(nm)-1]=0;
+	return print_stats(ssl, nm, s);
+}
+
+/** print long number */
+static int
+print_longnum(SSL* ssl, char* desc, size_t x)
+{
+	if(x > 1024*1024*1024) {
+		/* more than a Gb */
+		size_t front = x / (size_t)1000000;
+		size_t back = x % (size_t)1000000;
+		return ssl_printf(ssl, "%s%u%6.6u\n", desc, 
+			(unsigned)front, (unsigned)back);
+	} else {
+		return ssl_printf(ssl, "%s%u\n", desc, (unsigned)x);
+	}
+}
+
+/** print mem stats */
+static int
+print_mem(SSL* ssl, struct worker* worker, struct daemon* daemon)
+{
+	int m;
+	size_t msg, rrset, val, iter;
+#ifdef HAVE_SBRK
+	extern void* unbound_start_brk;
+	void* cur = sbrk(0);
+	if(!print_longnum(ssl, "mem.total.sbrk"SQ, 
+		(size_t)((char*)cur - (char*)unbound_start_brk))) return 0;
+#endif /* HAVE_SBRK */
+	msg = slabhash_get_mem(daemon->env->msg_cache);
+	rrset = slabhash_get_mem(&daemon->env->rrset_cache->table);
+	val=0;
+	iter=0;
+	m = modstack_find(&worker->env.mesh->mods, "validator");
+	if(m != -1) {
+		fptr_ok(fptr_whitelist_mod_get_mem(worker->env.mesh->
+			mods.mod[m]->get_mem));
+		val = (*worker->env.mesh->mods.mod[m]->get_mem)
+			(&worker->env, m);
+	}
+	m = modstack_find(&worker->env.mesh->mods, "iterator");
+	if(m != -1) {
+		fptr_ok(fptr_whitelist_mod_get_mem(worker->env.mesh->
+			mods.mod[m]->get_mem));
+		iter = (*worker->env.mesh->mods.mod[m]->get_mem)
+			(&worker->env, m);
+	}
+
+	if(!print_longnum(ssl, "mem.cache.rrset"SQ, rrset))
+		return 0;
+	if(!print_longnum(ssl, "mem.cache.message"SQ, msg))
+		return 0;
+	if(!print_longnum(ssl, "mem.mod.iterator"SQ, iter))
+		return 0;
+	if(!print_longnum(ssl, "mem.mod.validator"SQ, val))
+		return 0;
+	return 1;
+}
+
+/** print uptime stats */
+static int
+print_uptime(SSL* ssl, struct worker* worker, int reset)
+{
+	struct timeval now = *worker->env.now_tv;
+	struct timeval up, dt;
+	timeval_subtract(&up, &now, &worker->daemon->time_boot);
+	timeval_subtract(&dt, &now, &worker->daemon->time_last_stat);
+	if(reset)
+		worker->daemon->time_last_stat = now;
+	if(!ssl_printf(ssl, "time.now"SQ"%d.%6.6d\n", 
+		(unsigned)now.tv_sec, (unsigned)now.tv_usec)) return 0;
+	if(!ssl_printf(ssl, "time.up"SQ"%d.%6.6d\n", 
+		(unsigned)up.tv_sec, (unsigned)up.tv_usec)) return 0;
+	if(!ssl_printf(ssl, "time.elapsed"SQ"%d.%6.6d\n", 
+		(unsigned)dt.tv_sec, (unsigned)dt.tv_usec)) return 0;
+	return 1;
+}
+
+/** print extended histogram */
+static int
+print_hist(SSL* ssl, struct stats_info* s)
+{
+	struct timehist* hist;
+	size_t i;
+	hist = timehist_setup();
+	if(!hist) {
+		log_err("out of memory");
+		return 0;
+	}
+	timehist_import(hist, s->svr.hist, NUM_BUCKETS_HIST);
+	for(i=0; i<hist->num; i++) {
+		if(!ssl_printf(ssl, 
+			"histogram.%6.6d.%6.6d.to.%6.6d.%6.6d=%u\n",
+			(int)hist->buckets[i].lower.tv_sec,
+			(int)hist->buckets[i].lower.tv_usec,
+			(int)hist->buckets[i].upper.tv_sec,
+			(int)hist->buckets[i].upper.tv_usec,
+			(unsigned)hist->buckets[i].count)) {
+			timehist_delete(hist);
+			return 0;
+		}
+	}
+	timehist_delete(hist);
+	return 1;
+}
+
+/** print extended stats */
+static int
+print_ext(SSL* ssl, struct stats_info* s)
+{
+	int i;
+	char nm[16];
+	const ldns_rr_descriptor* desc;
+	const ldns_lookup_table* lt;
+	/* TYPE */
+	for(i=0; i<STATS_QTYPE_NUM; i++) {
+		if(inhibit_zero && s->svr.qtype[i] == 0)
+			continue;
+		desc = ldns_rr_descript((uint16_t)i);
+		if(desc && desc->_name) {
+			snprintf(nm, sizeof(nm), "%s", desc->_name);
+		} else if (i == LDNS_RR_TYPE_IXFR) {
+			snprintf(nm, sizeof(nm), "IXFR");
+		} else if (i == LDNS_RR_TYPE_AXFR) {
+			snprintf(nm, sizeof(nm), "AXFR");
+		} else if (i == LDNS_RR_TYPE_MAILA) {
+			snprintf(nm, sizeof(nm), "MAILA");
+		} else if (i == LDNS_RR_TYPE_MAILB) {
+			snprintf(nm, sizeof(nm), "MAILB");
+		} else if (i == LDNS_RR_TYPE_ANY) {
+			snprintf(nm, sizeof(nm), "ANY");
+		} else {
+			snprintf(nm, sizeof(nm), "TYPE%d", i);
+		}
+		if(!ssl_printf(ssl, "num.query.type.%s"SQ"%u\n", 
+			nm, (unsigned)s->svr.qtype[i])) return 0;
+	}
+	if(!inhibit_zero || s->svr.qtype_big) {
+		if(!ssl_printf(ssl, "num.query.type.other"SQ"%u\n", 
+			(unsigned)s->svr.qtype_big)) return 0;
+	}
+	/* CLASS */
+	for(i=0; i<STATS_QCLASS_NUM; i++) {
+		if(inhibit_zero && s->svr.qclass[i] == 0)
+			continue;
+		lt = ldns_lookup_by_id(ldns_rr_classes, i);
+		if(lt && lt->name) {
+			snprintf(nm, sizeof(nm), "%s", lt->name);
+		} else {
+			snprintf(nm, sizeof(nm), "CLASS%d", i);
+		}
+		if(!ssl_printf(ssl, "num.query.class.%s"SQ"%u\n", 
+			nm, (unsigned)s->svr.qclass[i])) return 0;
+	}
+	if(!inhibit_zero || s->svr.qclass_big) {
+		if(!ssl_printf(ssl, "num.query.class.other"SQ"%u\n", 
+			(unsigned)s->svr.qclass_big)) return 0;
+	}
+	/* OPCODE */
+	for(i=0; i<STATS_OPCODE_NUM; i++) {
+		if(inhibit_zero && s->svr.qopcode[i] == 0)
+			continue;
+		lt = ldns_lookup_by_id(ldns_opcodes, i);
+		if(lt && lt->name) {
+			snprintf(nm, sizeof(nm), "%s", lt->name);
+		} else {
+			snprintf(nm, sizeof(nm), "OPCODE%d", i);
+		}
+		if(!ssl_printf(ssl, "num.query.opcode.%s"SQ"%u\n", 
+			nm, (unsigned)s->svr.qopcode[i])) return 0;
+	}
+	/* transport */
+	if(!ssl_printf(ssl, "num.query.tcp"SQ"%u\n", 
+		(unsigned)s->svr.qtcp)) return 0;
+	if(!ssl_printf(ssl, "num.query.ipv6"SQ"%u\n", 
+		(unsigned)s->svr.qipv6)) return 0;
+	/* flags */
+	if(!ssl_printf(ssl, "num.query.flags.QR"SQ"%u\n", 
+		(unsigned)s->svr.qbit_QR)) return 0;
+	if(!ssl_printf(ssl, "num.query.flags.AA"SQ"%u\n", 
+		(unsigned)s->svr.qbit_AA)) return 0;
+	if(!ssl_printf(ssl, "num.query.flags.TC"SQ"%u\n", 
+		(unsigned)s->svr.qbit_TC)) return 0;
+	if(!ssl_printf(ssl, "num.query.flags.RD"SQ"%u\n", 
+		(unsigned)s->svr.qbit_RD)) return 0;
+	if(!ssl_printf(ssl, "num.query.flags.RA"SQ"%u\n", 
+		(unsigned)s->svr.qbit_RA)) return 0;
+	if(!ssl_printf(ssl, "num.query.flags.Z"SQ"%u\n", 
+		(unsigned)s->svr.qbit_Z)) return 0;
+	if(!ssl_printf(ssl, "num.query.flags.AD"SQ"%u\n", 
+		(unsigned)s->svr.qbit_AD)) return 0;
+	if(!ssl_printf(ssl, "num.query.flags.CD"SQ"%u\n", 
+		(unsigned)s->svr.qbit_CD)) return 0;
+	if(!ssl_printf(ssl, "num.query.edns.present"SQ"%u\n", 
+		(unsigned)s->svr.qEDNS)) return 0;
+	if(!ssl_printf(ssl, "num.query.edns.DO"SQ"%u\n", 
+		(unsigned)s->svr.qEDNS_DO)) return 0;
+
+	/* RCODE */
+	for(i=0; i<STATS_RCODE_NUM; i++) {
+		if(inhibit_zero && s->svr.ans_rcode[i] == 0)
+			continue;
+		lt = ldns_lookup_by_id(ldns_rcodes, i);
+		if(lt && lt->name) {
+			snprintf(nm, sizeof(nm), "%s", lt->name);
+		} else {
+			snprintf(nm, sizeof(nm), "RCODE%d", i);
+		}
+		if(!ssl_printf(ssl, "num.answer.rcode.%s"SQ"%u\n", 
+			nm, (unsigned)s->svr.ans_rcode[i])) return 0;
+	}
+	if(!inhibit_zero || s->svr.ans_rcode_nodata) {
+		if(!ssl_printf(ssl, "num.answer.rcode.nodata"SQ"%u\n", 
+			(unsigned)s->svr.ans_rcode_nodata)) return 0;
+	}
+	/* validation */
+	if(!ssl_printf(ssl, "num.answer.secure"SQ"%u\n", 
+		(unsigned)s->svr.ans_secure)) return 0;
+	if(!ssl_printf(ssl, "num.answer.bogus"SQ"%u\n", 
+		(unsigned)s->svr.ans_bogus)) return 0;
+	if(!ssl_printf(ssl, "num.rrset.bogus"SQ"%u\n", 
+		(unsigned)s->svr.rrset_bogus)) return 0;
+	/* threat detection */
+	if(!ssl_printf(ssl, "unwanted.queries"SQ"%u\n", 
+		(unsigned)s->svr.unwanted_queries)) return 0;
+	if(!ssl_printf(ssl, "unwanted.replies"SQ"%u\n", 
+		(unsigned)s->svr.unwanted_replies)) return 0;
+	return 1;
+}
+
+/** do the stats command */
+static void
+do_stats(SSL* ssl, struct daemon_remote* rc, int reset)
+{
+	struct daemon* daemon = rc->worker->daemon;
+	struct stats_info total;
+	struct stats_info s;
+	int i;
+	log_assert(daemon->num > 0);
+	/* gather all thread statistics in one place */
+	for(i=0; i<daemon->num; i++) {
+		server_stats_obtain(rc->worker, daemon->workers[i], &s, reset);
+		if(!print_thread_stats(ssl, i, &s))
+			return;
+		if(i == 0)
+			total = s;
+		else	server_stats_add(&total, &s);
+	}
+	/* print the thread statistics */
+	total.mesh_time_median /= (double)daemon->num;
+	if(!print_stats(ssl, "total", &total)) 
+		return;
+	if(!print_uptime(ssl, rc->worker, reset))
+		return;
+	if(daemon->cfg->stat_extended) {
+		if(!print_mem(ssl, rc->worker, daemon)) 
+			return;
+		if(!print_hist(ssl, &total))
+			return;
+		if(!print_ext(ssl, &total))
+			return;
+	}
+}
+
+/** parse commandline argument domain name */
+static int
+parse_arg_name(SSL* ssl, char* str, uint8_t** res, size_t* len, int* labs)
+{
+	ldns_rdf* rdf;
+	*res = NULL;
+	*len = 0;
+	*labs = 0;
+	rdf = ldns_dname_new_frm_str(str);
+	if(!rdf) {
+		ssl_printf(ssl, "error cannot parse name %s\n", str);
+		return 0;
+	}
+	*res = memdup(ldns_rdf_data(rdf), ldns_rdf_size(rdf));
+	ldns_rdf_deep_free(rdf);
+	if(!*res) {
+		ssl_printf(ssl, "error out of memory\n");
+		return 0;
+	}
+	*labs = dname_count_size_labels(*res, len);
+	return 1;
+}
+
+/** find second argument, modifies string */
+static int
+find_arg2(SSL* ssl, char* arg, char** arg2)
+{
+	char* as = strchr(arg, ' ');
+	char* at = strchr(arg, '\t');
+	if(as && at) {
+		if(at < as)
+			as = at;
+		as[0]=0;
+		*arg2 = skipwhite(as+1);
+	} else if(as) {
+		as[0]=0;
+		*arg2 = skipwhite(as+1);
+	} else if(at) {
+		at[0]=0;
+		*arg2 = skipwhite(at+1);
+	} else {
+		ssl_printf(ssl, "error could not find next argument "
+			"after %s\n", arg);
+		return 0;
+	}
+	return 1;
+}
+
+/** Add a new zone */
+static void
+do_zone_add(SSL* ssl, struct worker* worker, char* arg)
+{
+	uint8_t* nm;
+	int nmlabs;
+	size_t nmlen;
+	char* arg2;
+	enum localzone_type t;
+	struct local_zone* z;
+	if(!find_arg2(ssl, arg, &arg2))
+		return;
+	if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
+		return;
+	if(!local_zone_str2type(arg2, &t)) {
+		ssl_printf(ssl, "error not a zone type. %s\n", arg2);
+		free(nm);
+		return;
+	}
+	lock_quick_lock(&worker->daemon->local_zones->lock);
+	if((z=local_zones_find(worker->daemon->local_zones, nm, nmlen, 
+		nmlabs, LDNS_RR_CLASS_IN))) {
+		/* already present in tree */
+		lock_rw_wrlock(&z->lock);
+		z->type = t; /* update type anyway */
+		lock_rw_unlock(&z->lock);
+		free(nm);
+		lock_quick_unlock(&worker->daemon->local_zones->lock);
+		send_ok(ssl);
+		return;
+	}
+	if(!local_zones_add_zone(worker->daemon->local_zones, nm, nmlen, 
+		nmlabs, LDNS_RR_CLASS_IN, t)) {
+		lock_quick_unlock(&worker->daemon->local_zones->lock);
+		ssl_printf(ssl, "error out of memory\n");
+		return;
+	}
+	lock_quick_unlock(&worker->daemon->local_zones->lock);
+	send_ok(ssl);
+}
+
+/** Remove a zone */
+static void
+do_zone_remove(SSL* ssl, struct worker* worker, char* arg)
+{
+	uint8_t* nm;
+	int nmlabs;
+	size_t nmlen;
+	struct local_zone* z;
+	if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
+		return;
+	lock_quick_lock(&worker->daemon->local_zones->lock);
+	if((z=local_zones_find(worker->daemon->local_zones, nm, nmlen, 
+		nmlabs, LDNS_RR_CLASS_IN))) {
+		/* present in tree */
+		local_zones_del_zone(worker->daemon->local_zones, z);
+	}
+	lock_quick_unlock(&worker->daemon->local_zones->lock);
+	free(nm);
+	send_ok(ssl);
+}
+
+/** Add new RR data */
+static void
+do_data_add(SSL* ssl, struct worker* worker, char* arg)
+{
+	if(!local_zones_add_RR(worker->daemon->local_zones, arg,
+		worker->env.scratch_buffer)) {
+		ssl_printf(ssl,"error in syntax or out of memory, %s\n", arg);
+		return;
+	}
+	send_ok(ssl);
+}
+
+/** Remove RR data */
+static void
+do_data_remove(SSL* ssl, struct worker* worker, char* arg)
+{
+	uint8_t* nm;
+	int nmlabs;
+	size_t nmlen;
+	if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
+		return;
+	local_zones_del_data(worker->daemon->local_zones, nm,
+		nmlen, nmlabs, LDNS_RR_CLASS_IN);
+	free(nm);
+	send_ok(ssl);
+}
+
+/** cache lookup of nameservers */
+static void
+do_lookup(SSL* ssl, struct worker* worker, char* arg)
+{
+	uint8_t* nm;
+	int nmlabs;
+	size_t nmlen;
+	if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
+		return;
+	(void)print_deleg_lookup(ssl, worker, nm, nmlen, nmlabs);
+	free(nm);
+}
+
+/** flush something from rrset and msg caches */
+static void
+do_cache_remove(struct worker* worker, uint8_t* nm, size_t nmlen,
+	uint16_t t, uint16_t c)
+{
+	hashvalue_t h;
+	struct query_info k;
+	rrset_cache_remove(worker->env.rrset_cache, nm, nmlen, t, c, 0);
+	if(t == LDNS_RR_TYPE_SOA)
+		rrset_cache_remove(worker->env.rrset_cache, nm, nmlen, t, c,
+			PACKED_RRSET_SOA_NEG);
+	k.qname = nm;
+	k.qname_len = nmlen;
+	k.qtype = t;
+	k.qclass = c;
+	h = query_info_hash(&k);
+	slabhash_remove(worker->env.msg_cache, h, &k);
+}
+
+/** flush a type */
+static void
+do_flush_type(SSL* ssl, struct worker* worker, char* arg)
+{
+	uint8_t* nm;
+	int nmlabs;
+	size_t nmlen;
+	char* arg2;
+	uint16_t t;
+	if(!find_arg2(ssl, arg, &arg2))
+		return;
+	if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
+		return;
+	t = ldns_get_rr_type_by_name(arg2);
+	do_cache_remove(worker, nm, nmlen, t, LDNS_RR_CLASS_IN);
+	
+	free(nm);
+	send_ok(ssl);
+}
+
+/** flush statistics */
+static void
+do_flush_stats(SSL* ssl, struct worker* worker)
+{
+	worker_stats_clear(worker);
+	send_ok(ssl);
+}
+
+/**
+ * Local info for deletion functions
+ */
+struct del_info {
+	/** worker */
+	struct worker* worker;
+	/** name to delete */
+	uint8_t* name;
+	/** length */
+	size_t len;
+	/** labels */
+	int labs;
+	/** now */
+	uint32_t now;
+	/** time to invalidate to */
+	uint32_t expired;
+	/** number of rrsets removed */
+	size_t num_rrsets;
+	/** number of msgs removed */
+	size_t num_msgs;
+	/** number of key entries removed */
+	size_t num_keys;
+	/** length of addr */
+	socklen_t addrlen;
+	/** socket address for host deletion */
+	struct sockaddr_storage addr;
+};
+
+/** callback to delete hosts in infra cache */
+static void
+infra_del_host(struct lruhash_entry* e, void* arg)
+{
+	/* entry is locked */
+	struct del_info* inf = (struct del_info*)arg;
+	struct infra_key* k = (struct infra_key*)e->key;
+	if(sockaddr_cmp(&inf->addr, inf->addrlen, &k->addr, k->addrlen) == 0) {
+		struct infra_data* d = (struct infra_data*)e->data;
+		if(d->ttl >= inf->now) {
+			d->ttl = inf->expired;
+			inf->num_keys++;
+		}
+	}
+}
+
+/** flush infra cache */
+static void
+do_flush_infra(SSL* ssl, struct worker* worker, char* arg)
+{
+	struct sockaddr_storage addr;
+	socklen_t len;
+	struct del_info inf;
+	if(strcmp(arg, "all") == 0) {
+		slabhash_clear(worker->env.infra_cache->hosts);
+		send_ok(ssl);
+		return;
+	}
+	if(!ipstrtoaddr(arg, UNBOUND_DNS_PORT, &addr, &len)) {
+		(void)ssl_printf(ssl, "error parsing ip addr: '%s'\n", arg);
+		return;
+	}
+	/* delete all entries from cache */
+	/* what we do is to set them all expired */
+	inf.worker = worker;
+	inf.name = 0;
+	inf.len = 0;
+	inf.labs = 0;
+	inf.now = *worker->env.now;
+	inf.expired = *worker->env.now;
+	inf.expired -= 3; /* handle 3 seconds skew between threads */
+	inf.num_rrsets = 0;
+	inf.num_msgs = 0;
+	inf.num_keys = 0;
+	inf.addrlen = len;
+	memmove(&inf.addr, &addr, len);
+	slabhash_traverse(worker->env.infra_cache->hosts, 1, &infra_del_host,
+		&inf);
+	send_ok(ssl);
+}
+
+/** flush requestlist */
+static void
+do_flush_requestlist(SSL* ssl, struct worker* worker)
+{
+	mesh_delete_all(worker->env.mesh);
+	send_ok(ssl);
+}
+
+/** callback to delete rrsets in a zone */
+static void
+zone_del_rrset(struct lruhash_entry* e, void* arg)
+{
+	/* entry is locked */
+	struct del_info* inf = (struct del_info*)arg;
+	struct ub_packed_rrset_key* k = (struct ub_packed_rrset_key*)e->key;
+	if(dname_subdomain_c(k->rk.dname, inf->name)) {
+		struct packed_rrset_data* d = 
+			(struct packed_rrset_data*)e->data;
+		if(d->ttl >= inf->now) {
+			d->ttl = inf->expired;
+			inf->num_rrsets++;
+		}
+	}
+}
+
+/** callback to delete messages in a zone */
+static void
+zone_del_msg(struct lruhash_entry* e, void* arg)
+{
+	/* entry is locked */
+	struct del_info* inf = (struct del_info*)arg;
+	struct msgreply_entry* k = (struct msgreply_entry*)e->key;
+	if(dname_subdomain_c(k->key.qname, inf->name)) {
+		struct reply_info* d = (struct reply_info*)e->data;
+		if(d->ttl >= inf->now) {
+			d->ttl = inf->expired;
+			inf->num_msgs++;
+		}
+	}
+}
+
+/** callback to delete keys in zone */
+static void
+zone_del_kcache(struct lruhash_entry* e, void* arg)
+{
+	/* entry is locked */
+	struct del_info* inf = (struct del_info*)arg;
+	struct key_entry_key* k = (struct key_entry_key*)e->key;
+	if(dname_subdomain_c(k->name, inf->name)) {
+		struct key_entry_data* d = (struct key_entry_data*)e->data;
+		if(d->ttl >= inf->now) {
+			d->ttl = inf->expired;
+			inf->num_keys++;
+		}
+	}
+}
+
+/** remove all rrsets and keys from zone from cache */
+static void
+do_flush_zone(SSL* ssl, struct worker* worker, char* arg)
+{
+	uint8_t* nm;
+	int nmlabs;
+	size_t nmlen;
+	struct del_info inf;
+	if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
+		return;
+	/* delete all RRs and key entries from zone */
+	/* what we do is to set them all expired */
+	inf.worker = worker;
+	inf.name = nm;
+	inf.len = nmlen;
+	inf.labs = nmlabs;
+	inf.now = *worker->env.now;
+	inf.expired = *worker->env.now;
+	inf.expired -= 3; /* handle 3 seconds skew between threads */
+	inf.num_rrsets = 0;
+	inf.num_msgs = 0;
+	inf.num_keys = 0;
+	slabhash_traverse(&worker->env.rrset_cache->table, 1, 
+		&zone_del_rrset, &inf);
+
+	slabhash_traverse(worker->env.msg_cache, 1, &zone_del_msg, &inf);
+
+	/* and validator cache */
+	if(worker->env.key_cache) {
+		slabhash_traverse(worker->env.key_cache->slab, 1, 
+			&zone_del_kcache, &inf);
+	}
+
+	free(nm);
+
+	(void)ssl_printf(ssl, "ok removed %u rrsets, %u messages "
+		"and %u key entries\n", (unsigned)inf.num_rrsets, 
+		(unsigned)inf.num_msgs, (unsigned)inf.num_keys);
+}
+
+/** remove name rrset from cache */
+static void
+do_flush_name(SSL* ssl, struct worker* w, char* arg)
+{
+	uint8_t* nm;
+	int nmlabs;
+	size_t nmlen;
+	if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
+		return;
+	do_cache_remove(w, nm, nmlen, LDNS_RR_TYPE_A, LDNS_RR_CLASS_IN);
+	do_cache_remove(w, nm, nmlen, LDNS_RR_TYPE_AAAA, LDNS_RR_CLASS_IN);
+	do_cache_remove(w, nm, nmlen, LDNS_RR_TYPE_NS, LDNS_RR_CLASS_IN);
+	do_cache_remove(w, nm, nmlen, LDNS_RR_TYPE_SOA, LDNS_RR_CLASS_IN);
+	do_cache_remove(w, nm, nmlen, LDNS_RR_TYPE_CNAME, LDNS_RR_CLASS_IN);
+	do_cache_remove(w, nm, nmlen, LDNS_RR_TYPE_DNAME, LDNS_RR_CLASS_IN);
+	do_cache_remove(w, nm, nmlen, LDNS_RR_TYPE_MX, LDNS_RR_CLASS_IN);
+	do_cache_remove(w, nm, nmlen, LDNS_RR_TYPE_PTR, LDNS_RR_CLASS_IN);
+	do_cache_remove(w, nm, nmlen, LDNS_RR_TYPE_SRV, LDNS_RR_CLASS_IN);
+	do_cache_remove(w, nm, nmlen, LDNS_RR_TYPE_NAPTR, LDNS_RR_CLASS_IN);
+	
+	free(nm);
+	send_ok(ssl);
+}
+
+/** printout a delegation point info */
+static int
+ssl_print_name_dp(SSL* ssl, char* str, uint8_t* nm, uint16_t dclass,
+	struct delegpt* dp)
+{
+	char buf[257];
+	struct delegpt_ns* ns;
+	struct delegpt_addr* a;
+	int f = 0;
+	if(str) { /* print header for forward, stub */
+		char* c = ldns_rr_class2str(dclass);
+		dname_str(nm, buf);
+		if(!ssl_printf(ssl, "%s %s %s: ", buf, c, str)) {
+			free(c);
+			return 0;
+		}
+		free(c);
+	}
+	for(ns = dp->nslist; ns; ns = ns->next) {
+		dname_str(ns->name, buf);
+		if(!ssl_printf(ssl, "%s%s", (f?" ":""), buf))
+			return 0;
+		f = 1;
+	}
+	for(a = dp->target_list; a; a = a->next_target) {
+		addr_to_str(&a->addr, a->addrlen, buf, sizeof(buf));
+		if(!ssl_printf(ssl, "%s%s", (f?" ":""), buf))
+			return 0;
+		f = 1;
+	}
+	return ssl_printf(ssl, "\n");
+}
+
+
+/** print root forwards */
+static int
+print_root_fwds(SSL* ssl, struct iter_forwards* fwds, uint8_t* root)
+{
+	struct delegpt* dp;
+	dp = forwards_lookup(fwds, root, LDNS_RR_CLASS_IN);
+	if(!dp)
+		return ssl_printf(ssl, "off (using root hints)\n");
+	/* if dp is returned it must be the root */
+	log_assert(query_dname_compare(dp->name, root)==0);
+	return ssl_print_name_dp(ssl, NULL, root, LDNS_RR_CLASS_IN, dp);
+}
+
+/** parse args into delegpt */
+static struct delegpt*
+parse_delegpt(SSL* ssl, struct regional* region, char* args, uint8_t* root)
+{
+	/* parse args and add in */
+	char* p = args;
+	char* todo;
+	struct delegpt* dp = delegpt_create(region);
+	struct sockaddr_storage addr;
+	socklen_t addrlen;
+	if(!dp || !delegpt_set_name(dp, region, root)) {
+		(void)ssl_printf(ssl, "error out of memory\n");
+		return NULL;
+	}
+	while(p) {
+		todo = p;
+		p = strchr(p, ' '); /* find next spot, if any */
+		if(p) {
+			*p++ = 0;	/* end this spot */
+			p = skipwhite(p); /* position at next spot */
+		}
+		/* parse address */
+		if(!extstrtoaddr(todo, &addr, &addrlen)) {
+			(void)ssl_printf(ssl, "error cannot parse"
+				" IP address '%s'\n", todo);
+			return NULL;
+		}
+		/* add address */
+		if(!delegpt_add_addr(dp, region, &addr, addrlen, 0, 0)) {
+			(void)ssl_printf(ssl, "error out of memory\n");
+			return NULL;
+		}
+	}
+	return dp;
+}
+
+/** do the status command */
+static void
+do_forward(SSL* ssl, struct worker* worker, char* args)
+{
+	struct iter_forwards* fwd = worker->env.fwds;
+	uint8_t* root = (uint8_t*)"\000";
+	if(!fwd) {
+		(void)ssl_printf(ssl, "error: structure not allocated\n");
+		return;
+	}
+	if(args == NULL || args[0] == 0) {
+		(void)print_root_fwds(ssl, fwd, root);
+		return;
+	}
+	/* set root forwards for this thread. since we are in remote control
+	 * the actual mesh is not running, so we can freely edit it. */
+	/* delete all the existing queries first */
+	mesh_delete_all(worker->env.mesh);
+	/* reset the fwd structure ; the cfg is unchanged (shared by threads)*/
+	/* this reset frees up memory */
+	forwards_apply_cfg(fwd, worker->env.cfg);
+	if(strcmp(args, "off") == 0) {
+		forwards_delete_zone(fwd, LDNS_RR_CLASS_IN, root);
+	} else {
+		struct delegpt* dp;
+		if(!(dp = parse_delegpt(ssl, fwd->region, args, root)))
+			return;
+		if(!forwards_add_zone(fwd, LDNS_RR_CLASS_IN, dp)) {
+			(void)ssl_printf(ssl, "error out of memory\n");
+			return;
+		}
+	}
+	send_ok(ssl);
+}
+
+/** do the status command */
+static void
+do_status(SSL* ssl, struct worker* worker)
+{
+	int i;
+	time_t uptime;
+	if(!ssl_printf(ssl, "version: %s\n", PACKAGE_VERSION))
+		return;
+	if(!ssl_printf(ssl, "verbosity: %d\n", verbosity))
+		return;
+	if(!ssl_printf(ssl, "threads: %d\n", worker->daemon->num))
+		return;
+	if(!ssl_printf(ssl, "modules: %d [", worker->daemon->mods.num))
+		return;
+	for(i=0; i<worker->daemon->mods.num; i++) {
+		if(!ssl_printf(ssl, " %s", worker->daemon->mods.mod[i]->name))
+			return;
+	}
+	if(!ssl_printf(ssl, " ]\n"))
+		return;
+	uptime = (time_t)time(NULL) - (time_t)worker->daemon->time_boot.tv_sec;
+	if(!ssl_printf(ssl, "uptime: %u seconds\n", (unsigned)uptime))
+		return;
+	if(!ssl_printf(ssl, "unbound (pid %d) is running...\n",
+		(int)getpid()))
+		return;
+}
+
+/** get age for the mesh state */
+static void
+get_mesh_age(struct mesh_state* m, char* buf, size_t len, 
+	struct module_env* env)
+{
+	if(m->reply_list) {
+		struct timeval d;
+		struct mesh_reply* r = m->reply_list;
+		/* last reply is the oldest */
+		while(r && r->next)
+			r = r->next;
+		timeval_subtract(&d, env->now_tv, &r->start_time);
+		snprintf(buf, len, "%d.%6.6d", (int)d.tv_sec, (int)d.tv_usec);
+	} else {
+		snprintf(buf, len, "-");
+	}
+}
+
+/** get status of a mesh state */
+static void
+get_mesh_status(struct mesh_area* mesh, struct mesh_state* m, 
+	char* buf, size_t len)
+{
+	enum module_ext_state s = m->s.ext_state[m->s.curmod];
+	const char *modname = mesh->mods.mod[m->s.curmod]->name;
+	size_t l;
+	if(strcmp(modname, "iterator") == 0 && s == module_wait_reply &&
+		m->s.minfo[m->s.curmod]) {
+		/* break into iterator to find out who its waiting for */
+		struct iter_qstate* qstate = (struct iter_qstate*)
+			m->s.minfo[m->s.curmod];
+		struct outbound_list* ol = &qstate->outlist;
+		struct outbound_entry* e;
+		snprintf(buf, len, "%s wait for", modname);
+		l = strlen(buf);
+		buf += l; len -= l;
+		if(ol->first == NULL)
+			snprintf(buf, len, " (empty_list)");
+		for(e = ol->first; e; e = e->next) {
+			snprintf(buf, len, " ");
+			l = strlen(buf);
+			buf += l; len -= l;
+			addr_to_str(&e->qsent->addr, e->qsent->addrlen, 
+				buf, len);
+			l = strlen(buf);
+			buf += l; len -= l;
+		}
+	} else if(s == module_wait_subquery) {
+		/* look in subs from mesh state to see what */
+		char nm[257];
+		struct mesh_state_ref* sub;
+		snprintf(buf, len, "%s wants", modname);
+		l = strlen(buf);
+		buf += l; len -= l;
+		if(m->sub_set.count == 0)
+			snprintf(buf, len, " (empty_list)");
+		RBTREE_FOR(sub, struct mesh_state_ref*, &m->sub_set) {
+			char* t = ldns_rr_type2str(sub->s->s.qinfo.qtype);
+			char* c = ldns_rr_class2str(sub->s->s.qinfo.qclass);
+			dname_str(sub->s->s.qinfo.qname, nm);
+			snprintf(buf, len, " %s %s %s", t, c, nm);
+			l = strlen(buf);
+			buf += l; len -= l;
+			free(t);
+			free(c);
+		}
+	} else {
+		snprintf(buf, len, "%s is %s", modname, strextstate(s));
+	}
+}
+
+/** do the dump_requestlist command */
+static void
+do_dump_requestlist(SSL* ssl, struct worker* worker)
+{
+	struct mesh_area* mesh;
+	struct mesh_state* m;
+	int num = 0;
+	char buf[257];
+	char timebuf[32];
+	char statbuf[10240];
+	if(!ssl_printf(ssl, "thread #%d\n", worker->thread_num))
+		return;
+	if(!ssl_printf(ssl, "#   type cl name    seconds    module status\n"))
+		return;
+	/* show worker mesh contents */
+	mesh = worker->env.mesh;
+	if(!mesh) return;
+	RBTREE_FOR(m, struct mesh_state*, &mesh->all) {
+		char* t = ldns_rr_type2str(m->s.qinfo.qtype);
+		char* c = ldns_rr_class2str(m->s.qinfo.qclass);
+		dname_str(m->s.qinfo.qname, buf);
+		get_mesh_age(m, timebuf, sizeof(timebuf), &worker->env);
+		get_mesh_status(mesh, m, statbuf, sizeof(statbuf));
+		if(!ssl_printf(ssl, "%3d %4s %2s %s %s %s\n", 
+			num, t, c, buf, timebuf, statbuf)) {
+			free(t);
+			free(c);
+			return;
+		}
+		num++;
+		free(t);
+		free(c);
+	}
+}
+
+/** structure for argument data for dump infra host */
+struct infra_arg {
+	/** the infra cache */
+	struct infra_cache* infra;
+	/** the SSL connection */
+	SSL* ssl;
+	/** the time now */
+	uint32_t now;
+};
+
+/** callback for every host element in the infra cache */
+static void
+dump_infra_host(struct lruhash_entry* e, void* arg)
+{
+	struct infra_arg* a = (struct infra_arg*)arg;
+	struct infra_key* k = (struct infra_key*)e->key;
+	struct infra_data* d = (struct infra_data*)e->data;
+	char ip_str[1024];
+	char name[257];
+	addr_to_str(&k->addr, k->addrlen, ip_str, sizeof(ip_str));
+	dname_str(k->zonename, name);
+	/* skip expired stuff (only backed off) */
+	if(d->ttl < a->now) {
+		if(d->rtt.rto >= USEFUL_SERVER_TOP_TIMEOUT) {
+			if(!ssl_printf(a->ssl, "%s %s expired rto %d\n", ip_str,
+				name, d->rtt.rto)) return;
+		}
+		return;
+	}
+	if(!ssl_printf(a->ssl, "%s %s ttl %d ping %d var %d rtt %d rto %d "
+		"ednsknown %d edns %d delay %d lame dnssec %d rec %d A %d "
+		"other %d\n", ip_str, name, (int)(d->ttl - a->now),
+		d->rtt.srtt, d->rtt.rttvar, rtt_notimeout(&d->rtt), d->rtt.rto,
+		(int)d->edns_lame_known, (int)d->edns_version,
+		(int)(a->now<d->probedelay?d->probedelay-a->now:0),
+		(int)d->isdnsseclame, (int)d->rec_lame, (int)d->lame_type_A,
+		(int)d->lame_other))
+		return;
+}
+
+/** do the dump_infra command */
+static void
+do_dump_infra(SSL* ssl, struct worker* worker)
+{
+	struct infra_arg arg;
+	arg.infra = worker->env.infra_cache;
+	arg.ssl = ssl;
+	arg.now = *worker->env.now;
+	slabhash_traverse(arg.infra->hosts, 0, &dump_infra_host, (void*)&arg);
+}
+
+/** do the log_reopen command */
+static void
+do_log_reopen(SSL* ssl, struct worker* worker)
+{
+	struct config_file* cfg = worker->env.cfg;
+	send_ok(ssl);
+	log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir);
+}
+
+/** do the set_option command */
+static void
+do_set_option(SSL* ssl, struct worker* worker, char* arg)
+{
+	char* arg2;
+	if(!find_arg2(ssl, arg, &arg2))
+		return;
+	if(!config_set_option(worker->env.cfg, arg, arg2)) {
+		(void)ssl_printf(ssl, "error setting option\n");
+		return;
+	}
+	send_ok(ssl);
+}
+
+/* routine to printout option values over SSL */
+void remote_get_opt_ssl(char* line, void* arg)
+{
+	SSL* ssl = (SSL*)arg;
+	(void)ssl_printf(ssl, "%s\n", line);
+}
+
+/** do the get_option command */
+static void
+do_get_option(SSL* ssl, struct worker* worker, char* arg)
+{
+	int r;
+	r = config_get_option(worker->env.cfg, arg, remote_get_opt_ssl, ssl);
+	if(!r) {
+		(void)ssl_printf(ssl, "error unknown option\n");
+		return;
+	}
+}
+
+/** do the list_forwards command */
+static void
+do_list_forwards(SSL* ssl, struct worker* worker)
+{
+	/* since its a per-worker structure no locks needed */
+	struct iter_forwards* fwds = worker->env.fwds;
+	struct iter_forward_zone* z;
+	RBTREE_FOR(z, struct iter_forward_zone*, fwds->tree) {
+		if(!z->dp) continue; /* skip empty marker for stub */
+		if(!ssl_print_name_dp(ssl, "forward", z->name, z->dclass,
+			z->dp))
+			return;
+	}
+}
+
+/** do the list_stubs command */
+static void
+do_list_stubs(SSL* ssl, struct worker* worker)
+{
+	/* readonly structure */
+	int m;
+	struct iter_hints_stub* z;
+	struct iter_env* ie;
+	m = modstack_find(&worker->env.mesh->mods, "iterator");
+	if(m == -1) {
+		(void)ssl_printf(ssl, "error no iterator module\n");
+		return;
+	}
+	ie = (struct iter_env*)worker->env.modinfo[m];
+	RBTREE_FOR(z, struct iter_hints_stub*, &ie->hints->tree) {
+		if(!ssl_print_name_dp(ssl, 
+			z->noprime?"stub noprime":"stub prime", z->node.name,
+			z->node.dclass, z->dp))
+			return;
+	}
+}
+
+/** do the list_local_zones command */
+static void
+do_list_local_zones(SSL* ssl, struct worker* worker)
+{
+	struct local_zones* zones = worker->daemon->local_zones;
+	struct local_zone* z;
+	char buf[257];
+	lock_quick_lock(&zones->lock);
+	RBTREE_FOR(z, struct local_zone*, &zones->ztree) {
+		lock_rw_rdlock(&z->lock);
+		dname_str(z->name, buf);
+		(void)ssl_printf(ssl, "%s %s\n", buf, 
+			local_zone_type2str(z->type));
+		lock_rw_unlock(&z->lock);
+	}
+	lock_quick_unlock(&zones->lock);
+}
+
+/** do the list_local_data command */
+static void
+do_list_local_data(SSL* ssl, struct worker* worker)
+{
+	struct local_zones* zones = worker->daemon->local_zones;
+	struct local_zone* z;
+	struct local_data* d;
+	struct local_rrset* p;
+	lock_quick_lock(&zones->lock);
+	RBTREE_FOR(z, struct local_zone*, &zones->ztree) {
+		lock_rw_rdlock(&z->lock);
+		RBTREE_FOR(d, struct local_data*, &z->data) {
+			for(p = d->rrsets; p; p = p->next) {
+				ldns_rr_list* rr = packed_rrset_to_rr_list(
+					p->rrset, worker->env.scratch_buffer);
+				char* str = ldns_rr_list2str(rr);
+				(void)ssl_printf(ssl, "%s", str);
+				free(str);
+				ldns_rr_list_free(rr);
+			}
+		}
+		lock_rw_unlock(&z->lock);
+	}
+	lock_quick_unlock(&zones->lock);
+}
+
+/** tell other processes to execute the command */
+static void
+distribute_cmd(struct daemon_remote* rc, SSL* ssl, char* cmd)
+{
+	int i;
+	if(!cmd || !ssl) 
+		return;
+	/* skip i=0 which is me */
+	for(i=1; i<rc->worker->daemon->num; i++) {
+		worker_send_cmd(rc->worker->daemon->workers[i],
+			worker_cmd_remote);
+		if(!tube_write_msg(rc->worker->daemon->workers[i]->cmd,
+			(uint8_t*)cmd, strlen(cmd)+1, 0)) {
+			ssl_printf(ssl, "error could not distribute cmd\n");
+			return;
+		}
+	}
+}
+
+/** check for name with end-of-string, space or tab after it */
+static int
+cmdcmp(char* p, const char* cmd, size_t len)
+{
+	return strncmp(p,cmd,len)==0 && (p[len]==0||p[len]==' '||p[len]=='\t');
+}
+
+/** execute a remote control command */
+static void
+execute_cmd(struct daemon_remote* rc, SSL* ssl, char* cmd, 
+	struct worker* worker)
+{
+	char* p = skipwhite(cmd);
+	/* compare command */
+	if(cmdcmp(p, "stop", 4)) {
+		do_stop(ssl, rc);
+		return;
+	} else if(cmdcmp(p, "reload", 6)) {
+		do_reload(ssl, rc);
+		return;
+	} else if(cmdcmp(p, "stats_noreset", 13)) {
+		do_stats(ssl, rc, 0);
+		return;
+	} else if(cmdcmp(p, "stats", 5)) {
+		do_stats(ssl, rc, 1);
+		return;
+	} else if(cmdcmp(p, "status", 6)) {
+		do_status(ssl, worker);
+		return;
+	} else if(cmdcmp(p, "dump_cache", 10)) {
+		(void)dump_cache(ssl, worker);
+		return;
+	} else if(cmdcmp(p, "load_cache", 10)) {
+		if(load_cache(ssl, worker)) send_ok(ssl);
+		return;
+	} else if(cmdcmp(p, "list_forwards", 13)) {
+		do_list_forwards(ssl, worker);
+		return;
+	} else if(cmdcmp(p, "list_stubs", 10)) {
+		do_list_stubs(ssl, worker);
+		return;
+	} else if(cmdcmp(p, "list_local_zones", 16)) {
+		do_list_local_zones(ssl, worker);
+		return;
+	} else if(cmdcmp(p, "list_local_data", 15)) {
+		do_list_local_data(ssl, worker);
+		return;
+	} else if(cmdcmp(p, "forward", 7)) {
+		/* must always distribute this cmd */
+		if(rc) distribute_cmd(rc, ssl, cmd);
+		do_forward(ssl, worker, skipwhite(p+7));
+		return;
+	} else if(cmdcmp(p, "flush_stats", 11)) {
+		/* must always distribute this cmd */
+		if(rc) distribute_cmd(rc, ssl, cmd);
+		do_flush_stats(ssl, worker);
+		return;
+	} else if(cmdcmp(p, "flush_requestlist", 17)) {
+		/* must always distribute this cmd */
+		if(rc) distribute_cmd(rc, ssl, cmd);
+		do_flush_requestlist(ssl, worker);
+		return;
+	} else if(cmdcmp(p, "lookup", 6)) {
+		do_lookup(ssl, worker, skipwhite(p+6));
+		return;
+	}
+
+#ifdef THREADS_DISABLED
+	/* other processes must execute the command as well */
+	/* commands that should not be distributed, returned above. */
+	if(rc) { /* only if this thread is the master (rc) thread */
+		/* done before the code below, which may split the string */
+		distribute_cmd(rc, ssl, cmd);
+	}
+#endif
+	if(cmdcmp(p, "verbosity", 9)) {
+		do_verbosity(ssl, skipwhite(p+9));
+	} else if(cmdcmp(p, "local_zone_remove", 17)) {
+		do_zone_remove(ssl, worker, skipwhite(p+17));
+	} else if(cmdcmp(p, "local_zone", 10)) {
+		do_zone_add(ssl, worker, skipwhite(p+10));
+	} else if(cmdcmp(p, "local_data_remove", 17)) {
+		do_data_remove(ssl, worker, skipwhite(p+17));
+	} else if(cmdcmp(p, "local_data", 10)) {
+		do_data_add(ssl, worker, skipwhite(p+10));
+	} else if(cmdcmp(p, "flush_zone", 10)) {
+		do_flush_zone(ssl, worker, skipwhite(p+10));
+	} else if(cmdcmp(p, "flush_type", 10)) {
+		do_flush_type(ssl, worker, skipwhite(p+10));
+	} else if(cmdcmp(p, "flush_infra", 11)) {
+		do_flush_infra(ssl, worker, skipwhite(p+11));
+	} else if(cmdcmp(p, "flush", 5)) {
+		do_flush_name(ssl, worker, skipwhite(p+5));
+	} else if(cmdcmp(p, "dump_requestlist", 16)) {
+		do_dump_requestlist(ssl, worker);
+	} else if(cmdcmp(p, "dump_infra", 10)) {
+		do_dump_infra(ssl, worker);
+	} else if(cmdcmp(p, "log_reopen", 10)) {
+		do_log_reopen(ssl, worker);
+	} else if(cmdcmp(p, "set_option", 10)) {
+		do_set_option(ssl, worker, skipwhite(p+10));
+	} else if(cmdcmp(p, "get_option", 10)) {
+		do_get_option(ssl, worker, skipwhite(p+10));
+	} else {
+		(void)ssl_printf(ssl, "error unknown command '%s'\n", p);
+	}
+}
+
+void 
+daemon_remote_exec(struct worker* worker)
+{
+	/* read the cmd string */
+	uint8_t* msg = NULL;
+	uint32_t len = 0;
+	if(!tube_read_msg(worker->cmd, &msg, &len, 0)) {
+		log_err("daemon_remote_exec: tube_read_msg failed");
+		return;
+	}
+	verbose(VERB_ALGO, "remote exec distributed: %s", (char*)msg);
+	execute_cmd(NULL, NULL, (char*)msg, worker);
+	free(msg);
+}
+
+/** handle remote control request */
+static void
+handle_req(struct daemon_remote* rc, struct rc_state* s, SSL* ssl)
+{
+	int r;
+	char pre[10];
+	char magic[7];
+	char buf[1024];
+#ifdef USE_WINSOCK
+	/* makes it possible to set the socket blocking again. */
+	/* basically removes it from winsock_event ... */
+	WSAEventSelect(s->c->fd, NULL, 0);
+#endif
+	fd_set_block(s->c->fd);
+
+	/* try to read magic UBCT[version]_space_ string */
+	ERR_clear_error();
+	if((r=SSL_read(ssl, magic, (int)sizeof(magic)-1)) <= 0) {
+		if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN)
+			return;
+		log_crypto_err("could not SSL_read");
+		return;
+	}
+	magic[6] = 0;
+	if( r != 6 || strncmp(magic, "UBCT", 4) != 0) {
+		verbose(VERB_QUERY, "control connection has bad magic string");
+		/* probably wrong tool connected, ignore it completely */
+		return;
+	}
+
+	/* read the command line */
+	if(!ssl_read_line(ssl, buf, sizeof(buf))) {
+		return;
+	}
+	snprintf(pre, sizeof(pre), "UBCT%d ", UNBOUND_CONTROL_VERSION);
+	if(strcmp(magic, pre) != 0) {
+		verbose(VERB_QUERY, "control connection had bad "
+			"version %s, cmd: %s", magic, buf);
+		ssl_printf(ssl, "error version mismatch\n");
+		return;
+	}
+	verbose(VERB_DETAIL, "control cmd: %s", buf);
+
+	/* figure out what to do */
+	execute_cmd(rc, ssl, buf, rc->worker);
+}
+
+int remote_control_callback(struct comm_point* c, void* arg, int err, 
+	struct comm_reply* ATTR_UNUSED(rep))
+{
+	struct rc_state* s = (struct rc_state*)arg;
+	struct daemon_remote* rc = s->rc;
+	int r;
+	if(err != NETEVENT_NOERROR) {
+		if(err==NETEVENT_TIMEOUT) 
+			log_err("remote control timed out");
+		clean_point(rc, s);
+		return 0;
+	}
+	/* (continue to) setup the SSL connection */
+	ERR_clear_error();
+	r = SSL_do_handshake(s->ssl);
+	if(r != 1) {
+		int r2 = SSL_get_error(s->ssl, r);
+		if(r2 == SSL_ERROR_WANT_READ) {
+			if(s->shake_state == rc_hs_read) {
+				/* try again later */
+				return 0;
+			}
+			s->shake_state = rc_hs_read;
+			comm_point_listen_for_rw(c, 1, 0);
+			return 0;
+		} else if(r2 == SSL_ERROR_WANT_WRITE) {
+			if(s->shake_state == rc_hs_write) {
+				/* try again later */
+				return 0;
+			}
+			s->shake_state = rc_hs_write;
+			comm_point_listen_for_rw(c, 0, 1);
+			return 0;
+		} else {
+			if(r == 0)
+				log_err("remote control connection closed prematurely");
+			log_addr(1, "failed connection from",
+				&s->c->repinfo.addr, s->c->repinfo.addrlen);
+			log_crypto_err("remote control failed ssl");
+			clean_point(rc, s);
+			return 0;
+		}
+	}
+	s->shake_state = rc_none;
+
+	/* once handshake has completed, check authentication */
+	if(SSL_get_verify_result(s->ssl) == X509_V_OK) {
+		X509* x = SSL_get_peer_certificate(s->ssl);
+		if(!x) {
+			verbose(VERB_DETAIL, "remote control connection "
+				"provided no client certificate");
+			clean_point(rc, s);
+			return 0;
+		}
+		verbose(VERB_ALGO, "remote control connection authenticated");
+		X509_free(x);
+	} else {
+		verbose(VERB_DETAIL, "remote control connection failed to "
+			"authenticate with client certificate");
+		clean_point(rc, s);
+		return 0;
+	}
+
+	/* if OK start to actually handle the request */
+	handle_req(rc, s, s->ssl);
+
+	verbose(VERB_ALGO, "remote control operation completed");
+	clean_point(rc, s);
+	return 0;
+}
diff --git a/3rdParty/Unbound/src/src/daemon/remote.h b/3rdParty/Unbound/src/src/daemon/remote.h
new file mode 100644
index 0000000..1edc1b8
--- /dev/null
+++ b/3rdParty/Unbound/src/src/daemon/remote.h
@@ -0,0 +1,180 @@
+/*
+ * daemon/remote.h - remote control for the unbound daemon.
+ *
+ * Copyright (c) 2008, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains the remote control functionality for the daemon.
+ * The remote control can be performed using either the commandline
+ * unbound-control tool, or a SSLv3/TLS capable web browser. 
+ * The channel is secured using SSLv3 or TLSv1, and certificates.
+ * Both the server and the client(control tool) have their own keys.
+ */
+
+#ifndef DAEMON_REMOTE_H
+#define DAEMON_REMOTE_H
+#ifdef HAVE_OPENSSL_SSL_H
+#include "openssl/ssl.h"
+#endif
+struct config_file;
+struct listen_list;
+struct listen_port;
+struct worker;
+struct comm_reply;
+struct comm_point;
+struct daemon_remote;
+
+/** number of seconds timeout on incoming remote control handshake */
+#define REMOTE_CONTROL_TCP_TIMEOUT 120
+
+/**
+ * a busy control command connection, SSL state
+ */
+struct rc_state {
+	/** the next item in list */
+	struct rc_state* next;
+	/** the commpoint */
+	struct comm_point* c;
+	/** in the handshake part */
+	enum { rc_none, rc_hs_read, rc_hs_write } shake_state;
+	/** the ssl state */
+	SSL* ssl;
+	/** the rc this is part of */
+	struct daemon_remote* rc;
+};
+
+/**
+ * The remote control tool state.
+ * The state is only created for the first thread, other threads
+ * are called from this thread.  Only the first threads listens to
+ * the control port.  The other threads do not, but are called on the
+ * command channel(pipe) from the first thread.
+ */
+struct daemon_remote {
+	/** the worker for this remote control */
+	struct worker* worker;
+	/** commpoints for accepting remote control connections */
+	struct listen_list* accept_list;
+	/** number of active commpoints that are handling remote control */
+	int active;
+	/** max active commpoints */
+	int max_active;
+	/** current commpoints busy; should be a short list, malloced */
+	struct rc_state* busy_list;
+	/** the SSL context for creating new SSL streams */
+	SSL_CTX* ctx;
+};
+
+/**
+ * Create new remote control state for the daemon.
+ * @param cfg: config file with key file settings.
+ * @return new state, or NULL on failure.
+ */
+struct daemon_remote* daemon_remote_create(struct config_file* cfg);
+
+/**
+ * remote control state to delete.
+ * @param rc: state to delete.
+ */
+void daemon_remote_delete(struct daemon_remote* rc);
+
+/**
+ * remote control state to clear up. Busy and accept points are closed.
+ * Does not delete the rc itself, or the ssl context (with its keys).
+ * @param rc: state to clear.
+ */
+void daemon_remote_clear(struct daemon_remote* rc);
+
+/**
+ * Open and create listening ports for remote control.
+ * @param cfg: config options.
+ * @return list of ports or NULL on failure.
+ *	can be freed with listening_ports_free().
+ */
+struct listen_port* daemon_remote_open_ports(struct config_file* cfg);
+
+/**
+ * Setup comm points for accepting remote control connections.
+ * @param rc: state
+ * @param ports: already opened ports.
+ * @param worker: worker with communication base. and links to command channels.
+ * @return false on error.
+ */
+int daemon_remote_open_accept(struct daemon_remote* rc, 
+	struct listen_port* ports, struct worker* worker);
+
+/**
+ * Handle nonthreaded remote cmd execution.
+ * @param worker: this worker (the remote worker).
+ */
+void daemon_remote_exec(struct worker* worker);
+
+/** handle remote control accept callbacks */
+int remote_accept_callback(struct comm_point*, void*, int, struct comm_reply*);
+
+/** handle remote control data callbacks */
+int remote_control_callback(struct comm_point*, void*, int, struct comm_reply*);
+
+/** 
+ * Print fixed line of text over ssl connection in blocking mode
+ * @param ssl: print to
+ * @param text: the text.
+ * @return false on connection failure.
+ */
+int ssl_print_text(SSL* ssl, const char* text);
+
+/** 
+ * printf style printing to the ssl connection
+ * @param ssl: the SSL connection to print to. Blocking.
+ * @param format: printf style format string.
+ * @return success or false on a network failure.
+ */
+int ssl_printf(SSL* ssl, const char* format, ...)
+        ATTR_FORMAT(printf, 2, 3);
+
+/**
+ * Read until \n is encountered
+ * If SSL signals EOF, the string up to then is returned (without \n).
+ * @param ssl: the SSL connection to read from. blocking.
+ * @param buf: buffer to read to.
+ * @param max: size of buffer.
+ * @return false on connection failure.
+ */
+int ssl_read_line(SSL* ssl, char* buf, size_t max);
+
+/** routine to printout option values over SSL */
+void remote_get_opt_ssl(char* line, void* arg);
+
+#endif /* DAEMON_REMOTE_H */
diff --git a/3rdParty/Unbound/src/src/daemon/stats.c b/3rdParty/Unbound/src/src/daemon/stats.c
new file mode 100644
index 0000000..9a1a7d2
--- /dev/null
+++ b/3rdParty/Unbound/src/src/daemon/stats.c
@@ -0,0 +1,303 @@
+/*
+ * daemon/stats.c - collect runtime performance indicators.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file describes the data structure used to collect runtime performance
+ * numbers. These 'statistics' may be of interest to the operator.
+ */
+#include "config.h"
+#include <ldns/wire2host.h>
+#include "daemon/stats.h"
+#include "daemon/worker.h"
+#include "daemon/daemon.h"
+#include "services/mesh.h"
+#include "services/outside_network.h"
+#include "util/config_file.h"
+#include "util/tube.h"
+#include "util/timehist.h"
+#include "util/net_help.h"
+#include "validator/validator.h"
+
+/** add timers and the values do not overflow or become negative */
+static void
+timeval_add(struct timeval* d, const struct timeval* add)
+{
+#ifndef S_SPLINT_S
+	d->tv_sec += add->tv_sec;
+	d->tv_usec += add->tv_usec;
+	if(d->tv_usec > 1000000) {
+		d->tv_usec -= 1000000;
+		d->tv_sec++;
+	}
+#endif
+}
+
+void server_stats_init(struct server_stats* stats, struct config_file* cfg)
+{
+	memset(stats, 0, sizeof(*stats));
+	stats->extended = cfg->stat_extended;
+}
+
+void server_stats_querymiss(struct server_stats* stats, struct worker* worker)
+{
+	stats->num_queries_missed_cache++;
+	stats->sum_query_list_size += worker->env.mesh->all.count;
+	if(worker->env.mesh->all.count > stats->max_query_list_size)
+		stats->max_query_list_size = worker->env.mesh->all.count;
+}
+
+void server_stats_prefetch(struct server_stats* stats, struct worker* worker)
+{
+	stats->num_queries_prefetch++;
+	/* changes the query list size so account that, like a querymiss */
+	stats->sum_query_list_size += worker->env.mesh->all.count;
+	if(worker->env.mesh->all.count > stats->max_query_list_size)
+		stats->max_query_list_size = worker->env.mesh->all.count;
+}
+
+void server_stats_log(struct server_stats* stats, struct worker* worker,
+	int threadnum)
+{
+	log_info("server stats for thread %d: %u queries, "
+		"%u answers from cache, %u recursions, %u prefetch", 
+		threadnum, (unsigned)stats->num_queries, 
+		(unsigned)(stats->num_queries - 
+			stats->num_queries_missed_cache),
+		(unsigned)stats->num_queries_missed_cache,
+		(unsigned)stats->num_queries_prefetch);
+	log_info("server stats for thread %d: requestlist max %u avg %g "
+		"exceeded %u jostled %u", threadnum,
+		(unsigned)stats->max_query_list_size,
+		(stats->num_queries_missed_cache+stats->num_queries_prefetch)?
+			(double)stats->sum_query_list_size/
+			(stats->num_queries_missed_cache+
+			stats->num_queries_prefetch) : 0.0,
+		(unsigned)worker->env.mesh->stats_dropped,
+		(unsigned)worker->env.mesh->stats_jostled);
+}
+
+/** get rrsets bogus number from validator */
+static size_t
+get_rrset_bogus(struct worker* worker)
+{
+	int m = modstack_find(&worker->env.mesh->mods, "validator");
+	struct val_env* ve;
+	size_t r;
+	if(m == -1)
+		return 0;
+	ve = (struct val_env*)worker->env.modinfo[m];
+	lock_basic_lock(&ve->bogus_lock);
+	r = ve->num_rrset_bogus;
+	if(!worker->env.cfg->stat_cumulative)
+		ve->num_rrset_bogus = 0;
+	lock_basic_unlock(&ve->bogus_lock);
+	return r;
+}
+
+void
+server_stats_compile(struct worker* worker, struct stats_info* s, int reset)
+{
+	int i;
+
+	s->svr = worker->stats;
+	s->mesh_num_states = worker->env.mesh->all.count;
+	s->mesh_num_reply_states = worker->env.mesh->num_reply_states;
+	s->mesh_jostled = worker->env.mesh->stats_jostled;
+	s->mesh_dropped = worker->env.mesh->stats_dropped;
+	s->mesh_replies_sent = worker->env.mesh->replies_sent;
+	s->mesh_replies_sum_wait = worker->env.mesh->replies_sum_wait;
+	s->mesh_time_median = timehist_quartile(worker->env.mesh->histogram,
+		0.50);
+
+	/* add in the values from the mesh */
+	s->svr.ans_secure += worker->env.mesh->ans_secure;
+	s->svr.ans_bogus += worker->env.mesh->ans_bogus;
+	s->svr.ans_rcode_nodata += worker->env.mesh->ans_nodata;
+	for(i=0; i<16; i++)
+		s->svr.ans_rcode[i] += worker->env.mesh->ans_rcode[i];
+	timehist_export(worker->env.mesh->histogram, s->svr.hist, 
+		NUM_BUCKETS_HIST);
+	/* values from outside network */
+	s->svr.unwanted_replies = worker->back->unwanted_replies;
+
+	/* get and reset validator rrset bogus number */
+	s->svr.rrset_bogus = get_rrset_bogus(worker);
+
+	if(reset && !worker->env.cfg->stat_cumulative) {
+		worker_stats_clear(worker);
+	}
+}
+
+void server_stats_obtain(struct worker* worker, struct worker* who,
+	struct stats_info* s, int reset)
+{
+	uint8_t *reply = NULL;
+	uint32_t len = 0;
+	if(worker == who) {
+		/* just fill it in */
+		server_stats_compile(worker, s, reset);
+		return;
+	}
+	/* communicate over tube */
+	verbose(VERB_ALGO, "write stats cmd");
+	if(reset)
+		worker_send_cmd(who, worker_cmd_stats);
+	else 	worker_send_cmd(who, worker_cmd_stats_noreset);
+	verbose(VERB_ALGO, "wait for stats reply");
+	if(!tube_read_msg(worker->cmd, &reply, &len, 0))
+		fatal_exit("failed to read stats over cmd channel");
+	if(len != (uint32_t)sizeof(*s))
+		fatal_exit("stats on cmd channel wrong length %d %d",
+			(int)len, (int)sizeof(*s));
+	memcpy(s, reply, (size_t)len);
+	free(reply);
+}
+
+void server_stats_reply(struct worker* worker, int reset)
+{
+	struct stats_info s;
+	server_stats_compile(worker, &s, reset);
+	verbose(VERB_ALGO, "write stats replymsg");
+	if(!tube_write_msg(worker->daemon->workers[0]->cmd, 
+		(uint8_t*)&s, sizeof(s), 0))
+		fatal_exit("could not write stat values over cmd channel");
+}
+
+void server_stats_add(struct stats_info* total, struct stats_info* a)
+{
+	total->svr.num_queries += a->svr.num_queries;
+	total->svr.num_queries_missed_cache += a->svr.num_queries_missed_cache;
+	total->svr.num_queries_prefetch += a->svr.num_queries_prefetch;
+	total->svr.sum_query_list_size += a->svr.sum_query_list_size;
+	/* the max size reached is upped to higher of both */
+	if(a->svr.max_query_list_size > total->svr.max_query_list_size)
+		total->svr.max_query_list_size = a->svr.max_query_list_size;
+
+	if(a->svr.extended) {
+		int i;
+		total->svr.qtype_big += a->svr.qtype_big;
+		total->svr.qclass_big += a->svr.qclass_big;
+		total->svr.qtcp += a->svr.qtcp;
+		total->svr.qipv6 += a->svr.qipv6;
+		total->svr.qbit_QR += a->svr.qbit_QR;
+		total->svr.qbit_AA += a->svr.qbit_AA;
+		total->svr.qbit_TC += a->svr.qbit_TC;
+		total->svr.qbit_RD += a->svr.qbit_RD;
+		total->svr.qbit_RA += a->svr.qbit_RA;
+		total->svr.qbit_Z += a->svr.qbit_Z;
+		total->svr.qbit_AD += a->svr.qbit_AD;
+		total->svr.qbit_CD += a->svr.qbit_CD;
+		total->svr.qEDNS += a->svr.qEDNS;
+		total->svr.qEDNS_DO += a->svr.qEDNS_DO;
+		total->svr.ans_rcode_nodata += a->svr.ans_rcode_nodata;
+		total->svr.ans_secure += a->svr.ans_secure;
+		total->svr.ans_bogus += a->svr.ans_bogus;
+		total->svr.rrset_bogus += a->svr.rrset_bogus;
+		total->svr.unwanted_replies += a->svr.unwanted_replies;
+		total->svr.unwanted_queries += a->svr.unwanted_queries;
+		for(i=0; i<STATS_QTYPE_NUM; i++)
+			total->svr.qtype[i] += a->svr.qtype[i];
+		for(i=0; i<STATS_QCLASS_NUM; i++)
+			total->svr.qclass[i] += a->svr.qclass[i];
+		for(i=0; i<STATS_OPCODE_NUM; i++)
+			total->svr.qopcode[i] += a->svr.qopcode[i];
+		for(i=0; i<STATS_RCODE_NUM; i++)
+			total->svr.ans_rcode[i] += a->svr.ans_rcode[i];
+		for(i=0; i<NUM_BUCKETS_HIST; i++)
+			total->svr.hist[i] += a->svr.hist[i];
+	}
+
+	total->mesh_num_states += a->mesh_num_states;
+	total->mesh_num_reply_states += a->mesh_num_reply_states;
+	total->mesh_jostled += a->mesh_jostled;
+	total->mesh_dropped += a->mesh_dropped;
+	total->mesh_replies_sent += a->mesh_replies_sent;
+	timeval_add(&total->mesh_replies_sum_wait, &a->mesh_replies_sum_wait);
+	/* the medians are averaged together, this is not as accurate as
+	 * taking the median over all of the data, but is good and fast
+	 * added up here, division later*/
+	total->mesh_time_median += a->mesh_time_median;
+}
+
+void server_stats_insquery(struct server_stats* stats, struct comm_point* c,
+	uint16_t qtype, uint16_t qclass, struct edns_data* edns,
+	struct comm_reply* repinfo)
+{
+	uint16_t flags = ldns_buffer_read_u16_at(c->buffer, 2);
+	if(qtype < STATS_QTYPE_NUM)
+		stats->qtype[qtype]++;
+	else	stats->qtype_big++;
+	if(qclass < STATS_QCLASS_NUM)
+		stats->qclass[qclass]++;
+	else	stats->qclass_big++;
+	stats->qopcode[ LDNS_OPCODE_WIRE(ldns_buffer_begin(c->buffer)) ]++;
+	if(c->type != comm_udp)
+		stats->qtcp++;
+	if(repinfo && addr_is_ip6(&repinfo->addr, repinfo->addrlen))
+		stats->qipv6++;
+	if( (flags&BIT_QR) )
+		stats->qbit_QR++;
+	if( (flags&BIT_AA) )
+		stats->qbit_AA++;
+	if( (flags&BIT_TC) )
+		stats->qbit_TC++;
+	if( (flags&BIT_RD) )
+		stats->qbit_RD++;
+	if( (flags&BIT_RA) )
+		stats->qbit_RA++;
+	if( (flags&BIT_Z) )
+		stats->qbit_Z++;
+	if( (flags&BIT_AD) )
+		stats->qbit_AD++;
+	if( (flags&BIT_CD) )
+		stats->qbit_CD++;
+	if(edns->edns_present) {
+		stats->qEDNS++;
+		if( (edns->bits & EDNS_DO) )
+			stats->qEDNS_DO++;
+	}
+}
+
+void server_stats_insrcode(struct server_stats* stats, ldns_buffer* buf)
+{
+	if(stats->extended && ldns_buffer_limit(buf) != 0) {
+		int r = (int)LDNS_RCODE_WIRE( ldns_buffer_begin(buf) );
+		stats->ans_rcode[r] ++;
+		if(r == 0 && LDNS_ANCOUNT( ldns_buffer_begin(buf) ) == 0)
+			stats->ans_rcode_nodata ++;
+	}
+}
diff --git a/3rdParty/Unbound/src/src/daemon/stats.h b/3rdParty/Unbound/src/src/daemon/stats.h
new file mode 100644
index 0000000..c0fc1cc
--- /dev/null
+++ b/3rdParty/Unbound/src/src/daemon/stats.h
@@ -0,0 +1,235 @@
+/*
+ * daemon/stats.h - collect runtime performance indicators.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file describes the data structure used to collect runtime performance
+ * numbers. These 'statistics' may be of interest to the operator.
+ */
+
+#ifndef DAEMON_STATS_H
+#define DAEMON_STATS_H
+#include "util/timehist.h"
+#include <ldns/buffer.h>
+struct worker;
+struct config_file;
+struct comm_point;
+struct comm_reply;
+struct edns_data;
+
+/** number of qtype that is stored for in array */
+#define STATS_QTYPE_NUM 256
+/** number of qclass that is stored for in array */
+#define STATS_QCLASS_NUM 256
+/** number of rcodes in stats */
+#define STATS_RCODE_NUM 16
+/** number of opcodes in stats */
+#define STATS_OPCODE_NUM 16
+
+/** per worker statistics */
+struct server_stats {
+	/** number of queries from clients received. */
+	size_t num_queries;
+	/** number of queries that had a cache-miss. */
+	size_t num_queries_missed_cache;
+	/** number of prefetch queries - cachehits with prefetch */
+	size_t num_queries_prefetch;
+
+	/**
+	 * Sum of the querylistsize of the worker for 
+	 * every query that missed cache. To calculate average.
+	 */
+	size_t sum_query_list_size;
+	/** max value of query list size reached. */
+	size_t max_query_list_size;
+
+	/** Extended stats below (bool) */
+	int extended;
+
+	/** qtype stats */
+	size_t qtype[STATS_QTYPE_NUM];
+	/** bigger qtype values not in array */
+	size_t qtype_big;
+	/** qclass stats */
+	size_t qclass[STATS_QCLASS_NUM];
+	/** bigger qclass values not in array */
+	size_t qclass_big;
+	/** query opcodes */
+	size_t qopcode[STATS_OPCODE_NUM];
+	/** number of queries over TCP */
+	size_t qtcp;
+	/** number of queries over IPv6 */
+	size_t qipv6;
+	/** number of queries with QR bit */
+	size_t qbit_QR;
+	/** number of queries with AA bit */
+	size_t qbit_AA;
+	/** number of queries with TC bit */
+	size_t qbit_TC;
+	/** number of queries with RD bit */
+	size_t qbit_RD;
+	/** number of queries with RA bit */
+	size_t qbit_RA;
+	/** number of queries with Z bit */
+	size_t qbit_Z;
+	/** number of queries with AD bit */
+	size_t qbit_AD;
+	/** number of queries with CD bit */
+	size_t qbit_CD;
+	/** number of queries with EDNS OPT record */
+	size_t qEDNS;
+	/** number of queries with EDNS with DO flag */
+	size_t qEDNS_DO;
+	/** answer rcodes */
+	size_t ans_rcode[STATS_RCODE_NUM];
+	/** answers with pseudo rcode 'nodata' */
+	size_t ans_rcode_nodata;
+	/** answers that were secure (AD) */
+	size_t ans_secure;
+	/** answers that were bogus (withheld as SERVFAIL) */
+	size_t ans_bogus;
+	/** rrsets marked bogus by validator */
+	size_t rrset_bogus;
+	/** unwanted traffic received on server-facing ports */
+	size_t unwanted_replies;
+	/** unwanted traffic received on client-facing ports */
+	size_t unwanted_queries;
+
+	/** histogram data exported to array 
+	 * if the array is the same size, no data is lost, and
+	 * if all histograms are same size (is so by default) then
+	 * adding up works well. */
+	size_t hist[NUM_BUCKETS_HIST];
+};
+
+/** 
+ * Statistics to send over the control pipe when asked
+ * This struct is made to be memcpied, sent in binary.
+ */
+struct stats_info {
+	/** the thread stats */
+	struct server_stats svr;
+
+	/** mesh stats: current number of states */
+	size_t mesh_num_states;
+	/** mesh stats: current number of reply (user) states */
+	size_t mesh_num_reply_states;
+	/** mesh stats: number of reply states overwritten with a new one */
+	size_t mesh_jostled;
+	/** mesh stats: number of incoming queries dropped */
+	size_t mesh_dropped;
+	/** mesh stats: replies sent */
+	size_t mesh_replies_sent;
+	/** mesh stats: sum of waiting times for the replies */
+	struct timeval mesh_replies_sum_wait;
+	/** mesh stats: median of waiting times for replies (in sec) */
+	double mesh_time_median;
+};
+
+/** 
+ * Initialize server stats to 0.
+ * @param stats: what to init (this is alloced by the caller).
+ * @param cfg: with extended statistics option.
+ */
+void server_stats_init(struct server_stats* stats, struct config_file* cfg);
+
+/** add query if it missed the cache */
+void server_stats_querymiss(struct server_stats* stats, struct worker* worker);
+
+/** add query if was cached and also resulted in a prefetch */
+void server_stats_prefetch(struct server_stats* stats, struct worker* worker);
+
+/** display the stats to the log */
+void server_stats_log(struct server_stats* stats, struct worker* worker,
+	int threadnum);
+
+/**
+ * Obtain the stats info for a given thread. Uses pipe to communicate.
+ * @param worker: the worker that is executing (the first worker).
+ * @param who: on who to get the statistics info.
+ * @param s: the stats block to fill in.
+ * @param reset: if stats can be reset.
+ */
+void server_stats_obtain(struct worker* worker, struct worker* who,
+	struct stats_info* s, int reset);
+
+/**
+ * Compile stats into structure for this thread worker.
+ * Also clears the statistics counters (if that is set by config file).
+ * @param worker: the worker to compile stats for, also the executing worker.
+ * @param s: stats block.
+ * @param reset: if true, depending on config stats are reset.
+ * 	if false, statistics are not reset.
+ */
+void server_stats_compile(struct worker* worker, struct stats_info* s, 
+	int reset);
+
+/**
+ * Send stats over comm tube in reply to query cmd
+ * @param worker: this worker.
+ * @param reset: if true, depending on config stats are reset.
+ * 	if false, statistics are not reset.
+ */
+void server_stats_reply(struct worker* worker, int reset);
+
+/**
+ * Addup stat blocks.
+ * @param total: sum of the two entries.
+ * @param a: to add to it.
+ */
+void server_stats_add(struct stats_info* total, struct stats_info* a);
+
+/**
+ * Add stats for this query
+ * @param stats: the stats
+ * @param c: commpoint with type and buffer.
+ * @param qtype: query type
+ * @param qclass: query class
+ * @param edns: edns record
+ * @param repinfo: reply info with remote address
+ */
+void server_stats_insquery(struct server_stats* stats, struct comm_point* c,
+	uint16_t qtype, uint16_t qclass, struct edns_data* edns, 
+	struct comm_reply* repinfo);
+
+/**
+ * Add rcode for this query.
+ * @param stats: the stats
+ * @param buf: buffer with rcode. If buffer is length0: not counted.
+ */
+void server_stats_insrcode(struct server_stats* stats, ldns_buffer* buf);
+
+#endif /* DAEMON_STATS_H */
diff --git a/3rdParty/Unbound/src/src/daemon/unbound.c b/3rdParty/Unbound/src/src/daemon/unbound.c
new file mode 100644
index 0000000..685277d
--- /dev/null
+++ b/3rdParty/Unbound/src/src/daemon/unbound.c
@@ -0,0 +1,742 @@
+/*
+ * daemon/unbound.c - main program for unbound DNS resolver daemon.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ *
+ */
+
+/**
+ * \file
+ *
+ * Main program to start the DNS resolver daemon.
+ */
+
+#include "config.h"
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+#include <sys/time.h>
+#include "util/log.h"
+#include "daemon/daemon.h"
+#include "daemon/remote.h"
+#include "util/config_file.h"
+#include "util/storage/slabhash.h"
+#include "services/listen_dnsport.h"
+#include "services/cache/rrset.h"
+#include "services/cache/infra.h"
+#include "util/data/msgreply.h"
+#include "util/module.h"
+#include "util/net_help.h"
+#include <signal.h>
+#include <fcntl.h>
+#include <openssl/crypto.h>
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+#ifdef HAVE_LOGIN_CAP_H
+#include <login_cap.h>
+#endif
+
+#ifdef USE_MINI_EVENT
+#  ifdef USE_WINSOCK
+#    include "util/winsock_event.h"
+#  else
+#    include "util/mini_event.h"
+#  endif
+#else
+#  include <event.h>
+#endif
+
+#ifdef UB_ON_WINDOWS
+#  include "winrc/win_svc.h"
+#endif
+
+/** global debug value to keep track of heap memory allocation */
+void* unbound_start_brk = 0;
+
+#if !defined(HAVE_EVENT_BASE_GET_METHOD) && (defined(HAVE_EV_LOOP) || defined(HAVE_EV_DEFAULT_LOOP))
+static const char* ev_backend2str(int b)
+{
+	switch(b) {
+	case EVBACKEND_SELECT:	return "select";
+	case EVBACKEND_POLL:	return "poll";
+	case EVBACKEND_EPOLL:	return "epoll";
+	case EVBACKEND_KQUEUE:	return "kqueue";
+	case EVBACKEND_DEVPOLL: return "devpoll";
+	case EVBACKEND_PORT:	return "evport";
+	}
+	return "unknown";
+}
+#endif
+
+/** get the event system in use */
+static void get_event_sys(const char** n, const char** s, const char** m)
+{
+#ifdef USE_WINSOCK
+	*n = "event";
+	*s = "winsock";
+	*m = "WSAWaitForMultipleEvents";
+#elif defined(USE_MINI_EVENT)
+	*n = "mini-event";
+	*s = "internal";
+	*m = "select";
+#else
+	struct event_base* b;
+	*s = event_get_version();
+#  ifdef HAVE_EVENT_BASE_GET_METHOD
+	*n = "libevent";
+	b = event_base_new();
+	*m = event_base_get_method(b);
+#  elif defined(HAVE_EV_LOOP) || defined(HAVE_EV_DEFAULT_LOOP)
+	*n = "libev";
+	b = (struct event_base*)ev_default_loop(EVFLAG_AUTO);
+	*m = ev_backend2str(ev_backend((struct ev_loop*)b));
+#  else
+	*n = "unknown";
+	*m = "not obtainable";
+	b = NULL;
+#  endif
+#  ifdef HAVE_EVENT_BASE_FREE
+	event_base_free(b);
+#  endif
+#endif
+}
+
+/** print usage. */
+static void usage()
+{
+	const char** m;
+	const char *evnm="event", *evsys="", *evmethod="";
+	printf("usage:  unbound [options]\n");
+	printf("	start unbound daemon DNS resolver.\n");
+	printf("-h	this help\n");
+	printf("-c file	config file to read instead of %s\n", CONFIGFILE);
+	printf("	file format is described in unbound.conf(5).\n");
+	printf("-d	do not fork into the background.\n");
+	printf("-v	verbose (more times to increase verbosity)\n");
+#ifdef UB_ON_WINDOWS
+	printf("-w opt	windows option: \n");
+	printf("   	install, remove - manage the services entry\n");
+	printf("   	service - used to start from services control panel\n");
+#endif
+	printf("Version %s\n", PACKAGE_VERSION);
+	get_event_sys(&evnm, &evsys, &evmethod);
+	printf("linked libs: %s %s (it uses %s), ldns %s, %s\n", 
+		evnm, evsys, evmethod, ldns_version(), 
+		SSLeay_version(SSLEAY_VERSION));
+	printf("linked modules:");
+	for(m = module_list_avail(); *m; m++)
+		printf(" %s", *m);
+	printf("\n");
+	printf("configured for %s on %s with options:%s\n",
+		CONFIGURE_TARGET, CONFIGURE_DATE, CONFIGURE_BUILD_WITH);
+	printf("BSD licensed, see LICENSE in source package for details.\n");
+	printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
+}
+
+#ifndef unbound_testbound
+int replay_var_compare(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
+{
+        log_assert(0);
+        return 0;
+}
+#endif
+
+/** check file descriptor count */
+static void
+checkrlimits(struct config_file* cfg)
+{
+#ifdef HAVE_GETRLIMIT
+	/* list has number of ports to listen to, ifs number addresses */
+	int list = ((cfg->do_udp?1:0) + (cfg->do_tcp?1 + 
+			(int)cfg->incoming_num_tcp:0));
+	size_t listen_ifs = (size_t)(cfg->num_ifs==0?
+		((cfg->do_ip4 && !cfg->if_automatic?1:0) + 
+		 (cfg->do_ip6?1:0)):cfg->num_ifs);
+	size_t listen_num = list*listen_ifs;
+	size_t outudpnum = (size_t)cfg->outgoing_num_ports;
+	size_t outtcpnum = cfg->outgoing_num_tcp;
+	size_t misc = 4; /* logfile, pidfile, stdout... */
+	size_t perthread_noudp = listen_num + outtcpnum + 
+		2/*cmdpipe*/ + 2/*libevent*/ + misc; 
+	size_t perthread = perthread_noudp + outudpnum;
+
+#if !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS)
+	int numthread = 1; /* it forks */
+#else
+	int numthread = (cfg->num_threads?cfg->num_threads:1);
+#endif
+	size_t total = numthread * perthread + misc;
+	size_t avail;
+	struct rlimit rlim;
+
+	if(total > 1024 && 
+		strncmp(event_get_version(), "mini-event", 10) == 0) {
+		log_warn("too many file descriptors requested. The builtin"
+			"mini-event cannot handle more than 1024. Config "
+			"for less fds or compile with libevent");
+		if(numthread*perthread_noudp+15 > 1024)
+			fatal_exit("too much tcp. not enough fds.");
+		cfg->outgoing_num_ports = (int)((1024 
+			- numthread*perthread_noudp 
+			- 10 /* safety margin */) /numthread);
+		log_warn("continuing with less udp ports: %u",
+			cfg->outgoing_num_ports);
+		total = 1024;
+	}
+	if(perthread > 64 && 
+		strncmp(event_get_version(), "winsock-event", 13) == 0) {
+		log_err("too many file descriptors requested. The winsock"
+			" event handler cannot handle more than 64 per "
+			" thread. Config for less fds");
+		if(perthread_noudp+2 > 64)
+			fatal_exit("too much tcp. not enough fds.");
+		cfg->outgoing_num_ports = (int)((64 
+			- perthread_noudp 
+			- 2/* safety margin */));
+		log_warn("continuing with less udp ports: %u",
+			cfg->outgoing_num_ports);
+		total = numthread*(perthread_noudp+
+			(size_t)cfg->outgoing_num_ports)+misc;
+	}
+	if(getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
+		log_warn("getrlimit: %s", strerror(errno));
+		return;
+	}
+	if(rlim.rlim_cur == (rlim_t)RLIM_INFINITY)
+		return;
+	if((size_t)rlim.rlim_cur < total) {
+		avail = (size_t)rlim.rlim_cur;
+		rlim.rlim_cur = (rlim_t)(total + 10);
+		rlim.rlim_max = (rlim_t)(total + 10);
+#ifdef HAVE_SETRLIMIT
+		if(setrlimit(RLIMIT_NOFILE, &rlim) < 0) {
+			log_warn("setrlimit: %s", strerror(errno));
+#else
+		if(1) {
+#endif
+			log_warn("cannot increase max open fds from %u to %u",
+				(unsigned)avail, (unsigned)total+10);
+			/* check that calculation below does not underflow,
+			 * with 15 as margin */
+			if(numthread*perthread_noudp+15 > avail)
+				fatal_exit("too much tcp. not enough fds.");
+			cfg->outgoing_num_ports = (int)((avail 
+				- numthread*perthread_noudp 
+				- 10 /* safety margin */) /numthread);
+			log_warn("continuing with less udp ports: %u",
+				cfg->outgoing_num_ports);
+			log_warn("increase ulimit or decrease threads, "
+				"ports in config to remove this warning");
+			return;
+		}
+		log_warn("increased limit(open files) from %u to %u",
+			(unsigned)avail, (unsigned)total+10);
+	}
+#else	
+	(void)cfg;
+#endif /* HAVE_GETRLIMIT */
+}
+
+/** set verbosity, check rlimits, cache settings */
+static void
+apply_settings(struct daemon* daemon, struct config_file* cfg, 
+	int cmdline_verbose)
+{
+	/* apply if they have changed */
+	verbosity = cmdline_verbose + cfg->verbosity;
+	daemon_apply_cfg(daemon, cfg);
+	checkrlimits(cfg);
+}
+
+#ifdef HAVE_KILL
+/** Read existing pid from pidfile. 
+ * @param file: file name of pid file.
+ * @return: the pid from the file or -1 if none.
+ */
+static pid_t
+readpid (const char* file)
+{
+	int fd;
+	pid_t pid;
+	char pidbuf[32];
+	char* t;
+	ssize_t l;
+
+	if ((fd = open(file, O_RDONLY)) == -1) {
+		if(errno != ENOENT)
+			log_err("Could not read pidfile %s: %s",
+				file, strerror(errno));
+		return -1;
+	}
+
+	if (((l = read(fd, pidbuf, sizeof(pidbuf)))) == -1) {
+		if(errno != ENOENT)
+			log_err("Could not read pidfile %s: %s",
+				file, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+
+	/* Empty pidfile means no pidfile... */
+	if (l == 0) {
+		return -1;
+	}
+
+	pidbuf[sizeof(pidbuf)-1] = 0;
+	pid = (pid_t)strtol(pidbuf, &t, 10);
+	
+	if (*t && *t != '\n') {
+		return -1;
+	}
+	return pid;
+}
+
+/** write pid to file. 
+ * @param pidfile: file name of pid file.
+ * @param pid: pid to write to file.
+ */
+static void
+writepid (const char* pidfile, pid_t pid)
+{
+	FILE* f;
+
+	if ((f = fopen(pidfile, "w")) ==  NULL ) {
+		log_err("cannot open pidfile %s: %s", 
+			pidfile, strerror(errno));
+		return;
+	}
+	if(fprintf(f, "%lu\n", (unsigned long)pid) < 0) {
+		log_err("cannot write to pidfile %s: %s", 
+			pidfile, strerror(errno));
+	}
+	fclose(f);
+}
+
+/**
+ * check old pid file.
+ * @param pidfile: the file name of the pid file.
+ * @param inchroot: if pidfile is inchroot and we can thus expect to
+ *	be able to delete it.
+ */
+static void
+checkoldpid(char* pidfile, int inchroot)
+{
+	pid_t old;
+	if((old = readpid(pidfile)) != -1) {
+		/* see if it is still alive */
+		if(kill(old, 0) == 0 || errno == EPERM)
+			log_warn("unbound is already running as pid %u.", 
+				(unsigned)old);
+		else	if(inchroot)
+			log_warn("did not exit gracefully last time (%u)", 
+				(unsigned)old);
+	}
+}
+#endif /* HAVE_KILL */
+
+/** detach from command line */
+static void
+detach(void)
+{
+#if defined(HAVE_DAEMON) && !defined(DEPRECATED_DAEMON)
+	/* use POSIX daemon(3) function */
+	if(daemon(1, 0) != 0)
+		fatal_exit("daemon failed: %s", strerror(errno));
+#else /* no HAVE_DAEMON */
+#ifdef HAVE_FORK
+	int fd;
+	/* Take off... */
+	switch (fork()) {
+		case 0:
+			break;
+		case -1:
+			fatal_exit("fork failed: %s", strerror(errno));
+		default:
+			/* exit interactive session */
+			exit(0);
+	}
+	/* detach */
+#ifdef HAVE_SETSID
+	if(setsid() == -1)
+		fatal_exit("setsid() failed: %s", strerror(errno));
+#endif
+	if ((fd = open("/dev/null", O_RDWR, 0)) != -1) {
+		(void)dup2(fd, STDIN_FILENO);
+		(void)dup2(fd, STDOUT_FILENO);
+		(void)dup2(fd, STDERR_FILENO);
+		if (fd > 2)
+			(void)close(fd);
+	}
+#endif /* HAVE_FORK */
+#endif /* HAVE_DAEMON */
+}
+
+/** daemonize, drop user priviliges and chroot if needed */
+static void
+perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
+	const char** cfgfile)
+{
+#ifdef HAVE_GETPWNAM
+	struct passwd *pwd = NULL;
+	uid_t uid;
+	gid_t gid;
+	/* initialize, but not to 0 (root) */
+	memset(&uid, 112, sizeof(uid));
+	memset(&gid, 112, sizeof(gid));
+	log_assert(cfg);
+
+	if(cfg->username && cfg->username[0]) {
+		if((pwd = getpwnam(cfg->username)) == NULL)
+			fatal_exit("user '%s' does not exist.", cfg->username);
+		uid = pwd->pw_uid;
+		gid = pwd->pw_gid;
+		/* endpwent below, in case we need pwd for setusercontext */
+	}
+#endif
+
+	/* init syslog (as root) if needed, before daemonize, otherwise
+	 * a fork error could not be printed since daemonize closed stderr.*/
+	if(cfg->use_syslog) {
+		log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir);
+	}
+	/* if using a logfile, we cannot open it because the logfile would
+	 * be created with the wrong permissions, we cannot chown it because
+	 * we cannot chown system logfiles, so we do not open at all.
+	 * So, using a logfile, the user does not see errors unless -d is
+	 * given to unbound on the commandline. */
+
+	/* read ssl keys while superuser and outside chroot */
+	if(!(daemon->rc = daemon_remote_create(cfg)))
+		fatal_exit("could not set up remote-control");
+	if(cfg->ssl_service_key && cfg->ssl_service_key[0]) {
+		if(!(daemon->listen_sslctx = listen_sslctx_create(
+			cfg->ssl_service_key, cfg->ssl_service_pem, NULL)))
+			fatal_exit("could not set up listen SSL_CTX");
+	}
+	if(!(daemon->connect_sslctx = connect_sslctx_create(NULL, NULL, NULL)))
+		fatal_exit("could not set up connect SSL_CTX");
+
+#ifdef HAVE_KILL
+	/* check old pid file before forking */
+	if(cfg->pidfile && cfg->pidfile[0]) {
+		/* calculate position of pidfile */
+		if(cfg->pidfile[0] == '/')
+			daemon->pidfile = strdup(cfg->pidfile);
+		else	daemon->pidfile = fname_after_chroot(cfg->pidfile, 
+				cfg, 1);
+		if(!daemon->pidfile)
+			fatal_exit("pidfile alloc: out of memory");
+		checkoldpid(daemon->pidfile,
+			/* true if pidfile is inside chrootdir, or nochroot */
+			!(cfg->chrootdir && cfg->chrootdir[0]) ||
+			(cfg->chrootdir && cfg->chrootdir[0] &&
+			strncmp(daemon->pidfile, cfg->chrootdir,
+				strlen(cfg->chrootdir))==0));
+	}
+#endif
+
+	/* daemonize because pid is needed by the writepid func */
+	if(!debug_mode && cfg->do_daemonize) {
+		detach();
+	}
+
+	/* write new pidfile (while still root, so can be outside chroot) */
+#ifdef HAVE_KILL
+	if(cfg->pidfile && cfg->pidfile[0]) {
+		writepid(daemon->pidfile, getpid());
+		if(!(cfg->chrootdir && cfg->chrootdir[0]) || 
+			(cfg->chrootdir && cfg->chrootdir[0] && 
+			strncmp(daemon->pidfile, cfg->chrootdir, 
+			strlen(cfg->chrootdir))==0)) {
+			/* delete of pidfile could potentially work,
+			 * chown to get permissions */
+			if(cfg->username && cfg->username[0]) {
+			  if(chown(daemon->pidfile, uid, gid) == -1) {
+				log_err("cannot chown %u.%u %s: %s",
+					(unsigned)uid, (unsigned)gid,
+					daemon->pidfile, strerror(errno));
+			  }
+			}
+		}
+	}
+#else
+	(void)daemon;
+#endif
+
+	/* box into the chroot */
+#ifdef HAVE_CHROOT
+	if(cfg->chrootdir && cfg->chrootdir[0]) {
+		if(chdir(cfg->chrootdir)) {
+			fatal_exit("unable to chdir to chroot %s: %s",
+				cfg->chrootdir, strerror(errno));
+		}
+		verbose(VERB_QUERY, "chdir to %s", cfg->chrootdir);
+		if(chroot(cfg->chrootdir))
+			fatal_exit("unable to chroot to %s: %s", 
+				cfg->chrootdir, strerror(errno));
+		verbose(VERB_QUERY, "chroot to %s", cfg->chrootdir);
+		if(strncmp(*cfgfile, cfg->chrootdir, 
+			strlen(cfg->chrootdir)) == 0) 
+			(*cfgfile) += strlen(cfg->chrootdir);
+
+		/* adjust stored pidfile for chroot */
+		if(daemon->pidfile && daemon->pidfile[0] && 
+			strncmp(daemon->pidfile, cfg->chrootdir,
+			strlen(cfg->chrootdir))==0) {
+			char* old = daemon->pidfile;
+			daemon->pidfile = strdup(old+strlen(cfg->chrootdir));
+			free(old);
+			if(!daemon->pidfile)
+				log_err("out of memory in pidfile adjust");
+		}
+		daemon->chroot = strdup(cfg->chrootdir);
+		if(!daemon->chroot)
+			log_err("out of memory in daemon chroot dir storage");
+	}
+#else
+	(void)cfgfile;
+#endif
+	/* change to working directory inside chroot */
+	if(cfg->directory && cfg->directory[0]) {
+		char* dir = cfg->directory;
+		if(cfg->chrootdir && cfg->chrootdir[0] &&
+			strncmp(dir, cfg->chrootdir, 
+			strlen(cfg->chrootdir)) == 0)
+			dir += strlen(cfg->chrootdir);
+		if(dir[0]) {
+			if(chdir(dir)) {
+				fatal_exit("Could not chdir to %s: %s",
+					dir, strerror(errno));
+			}
+			verbose(VERB_QUERY, "chdir to %s", dir);
+		}
+	}
+
+	/* drop permissions after chroot, getpwnam, pidfile, syslog done*/
+#ifdef HAVE_GETPWNAM
+	if(cfg->username && cfg->username[0]) {
+#ifdef HAVE_SETUSERCONTEXT
+		/* setusercontext does initgroups, setuid, setgid, and
+		 * also resource limits from login config, but we
+		 * still call setresuid, setresgid to be sure to set all uid*/
+		if(setusercontext(NULL, pwd, uid, LOGIN_SETALL) != 0)
+			log_warn("unable to setusercontext %s: %s",
+				cfg->username, strerror(errno));
+#else /* !HAVE_SETUSERCONTEXT */
+#  ifdef HAVE_INITGROUPS
+		if(initgroups(cfg->username, gid) != 0)
+			log_warn("unable to initgroups %s: %s",
+				cfg->username, strerror(errno));
+#  endif /* HAVE_INITGROUPS */
+#endif /* HAVE_SETUSERCONTEXT */
+		endpwent();
+
+#ifdef HAVE_SETRESGID
+		if(setresgid(gid,gid,gid) != 0)
+#elif defined(HAVE_SETREGID) && !defined(DARWIN_BROKEN_SETREUID)
+		if(setregid(gid,gid) != 0)
+#else /* use setgid */
+		if(setgid(gid) != 0)
+#endif /* HAVE_SETRESGID */
+			fatal_exit("unable to set group id of %s: %s", 
+				cfg->username, strerror(errno));
+#ifdef HAVE_SETRESUID
+		if(setresuid(uid,uid,uid) != 0)
+#elif defined(HAVE_SETREUID) && !defined(DARWIN_BROKEN_SETREUID)
+		if(setreuid(uid,uid) != 0)
+#else /* use setuid */
+		if(setuid(uid) != 0)
+#endif /* HAVE_SETRESUID */
+			fatal_exit("unable to set user id of %s: %s", 
+				cfg->username, strerror(errno));
+		verbose(VERB_QUERY, "drop user privileges, run as %s", 
+			cfg->username);
+	}
+#endif /* HAVE_GETPWNAM */
+	/* file logging inited after chroot,chdir,setuid is done so that 
+	 * it would succeed on SIGHUP as well */
+	if(!cfg->use_syslog)
+		log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir);
+}
+
+/**
+ * Run the daemon. 
+ * @param cfgfile: the config file name.
+ * @param cmdline_verbose: verbosity resulting from commandline -v.
+ *    These increase verbosity as specified in the config file.
+ * @param debug_mode: if set, do not daemonize.
+ */
+static void 
+run_daemon(const char* cfgfile, int cmdline_verbose, int debug_mode)
+{
+	struct config_file* cfg = NULL;
+	struct daemon* daemon = NULL;
+	int done_setup = 0;
+
+	if(!(daemon = daemon_init()))
+		fatal_exit("alloc failure");
+	while(!daemon->need_to_exit) {
+		if(done_setup)
+			verbose(VERB_OPS, "Restart of %s.", PACKAGE_STRING);
+		else	verbose(VERB_OPS, "Start of %s.", PACKAGE_STRING);
+
+		/* config stuff */
+		if(!(cfg = config_create()))
+			fatal_exit("Could not alloc config defaults");
+		if(!config_read(cfg, cfgfile, daemon->chroot)) {
+			if(errno != ENOENT)
+				fatal_exit("Could not read config file: %s",
+					cfgfile);
+			log_warn("Continuing with default config settings");
+		}
+		apply_settings(daemon, cfg, cmdline_verbose);
+	
+		/* prepare */
+		if(!daemon_open_shared_ports(daemon))
+			fatal_exit("could not open ports");
+		if(!done_setup) { 
+			perform_setup(daemon, cfg, debug_mode, &cfgfile); 
+			done_setup = 1; 
+		} else {
+			/* reopen log after HUP to facilitate log rotation */
+			if(!cfg->use_syslog)
+				log_init(cfg->logfile, 0, cfg->chrootdir);
+		}
+		/* work */
+		daemon_fork(daemon);
+
+		/* clean up for restart */
+		verbose(VERB_ALGO, "cleanup.");
+		daemon_cleanup(daemon);
+		config_delete(cfg);
+	}
+	verbose(VERB_ALGO, "Exit cleanup.");
+	/* this unlink may not work if the pidfile is located outside
+	 * of the chroot/workdir or we no longer have permissions */
+	if(daemon->pidfile) {
+		int fd;
+		/* truncate pidfile */
+		fd = open(daemon->pidfile, O_WRONLY | O_TRUNC, 0644);
+		if(fd != -1)
+			close(fd);
+		/* delete pidfile */
+		unlink(daemon->pidfile);
+	}
+	daemon_delete(daemon);
+}
+
+/** getopt global, in case header files fail to declare it. */
+extern int optind;
+/** getopt global, in case header files fail to declare it. */
+extern char* optarg;
+
+/**
+ * main program. Set options given commandline arguments.
+ * @param argc: number of commandline arguments.
+ * @param argv: array of commandline arguments.
+ * @return: exit status of the program.
+ */
+int 
+main(int argc, char* argv[])
+{
+	int c;
+	const char* cfgfile = CONFIGFILE;
+	const char* winopt = NULL;
+	int cmdline_verbose = 0;
+	int debug_mode = 0;
+#ifdef UB_ON_WINDOWS
+	int cmdline_cfg = 0;
+#endif
+
+#ifdef HAVE_SBRK
+	/* take debug snapshot of heap */
+	unbound_start_brk = sbrk(0);
+#endif
+
+	log_init(NULL, 0, NULL);
+	/* parse the options */
+	while( (c=getopt(argc, argv, "c:dhvw:")) != -1) {
+		switch(c) {
+		case 'c':
+			cfgfile = optarg;
+#ifdef UB_ON_WINDOWS
+			cmdline_cfg = 1;
+#endif
+			break;
+		case 'v':
+			cmdline_verbose ++;
+			verbosity++;
+			break;
+		case 'd':
+			debug_mode = 1;
+			break;
+		case 'w':
+			winopt = optarg;
+			break;
+		case '?':
+		case 'h':
+		default:
+			usage();
+			return 1;
+		}
+	}
+	argc -= optind;
+	argv += optind;
+
+	if(winopt) {
+#ifdef UB_ON_WINDOWS
+		wsvc_command_option(winopt, cfgfile, cmdline_verbose, 
+			cmdline_cfg);
+#else
+		fatal_exit("option not supported");
+#endif
+	}
+
+	if(argc != 0) {
+		usage();
+		return 1;
+	}
+
+	run_daemon(cfgfile, cmdline_verbose, debug_mode);
+	log_init(NULL, 0, NULL); /* close logfile */
+	return 0;
+}
diff --git a/3rdParty/Unbound/src/src/daemon/worker.c b/3rdParty/Unbound/src/src/daemon/worker.c
new file mode 100644
index 0000000..11d7c81
--- /dev/null
+++ b/3rdParty/Unbound/src/src/daemon/worker.c
@@ -0,0 +1,1348 @@
+/*
+ * daemon/worker.c - worker that handles a pending list of requests.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file implements the worker that handles callbacks on events, for
+ * pending requests.
+ */
+#include "config.h"
+#include <ldns/wire2host.h>
+#include "util/log.h"
+#include "util/net_help.h"
+#include "util/random.h"
+#include "daemon/worker.h"
+#include "daemon/daemon.h"
+#include "daemon/remote.h"
+#include "daemon/acl_list.h"
+#include "util/netevent.h"
+#include "util/config_file.h"
+#include "util/module.h"
+#include "util/regional.h"
+#include "util/storage/slabhash.h"
+#include "services/listen_dnsport.h"
+#include "services/outside_network.h"
+#include "services/outbound_list.h"
+#include "services/cache/rrset.h"
+#include "services/cache/infra.h"
+#include "services/cache/dns.h"
+#include "services/mesh.h"
+#include "services/localzone.h"
+#include "util/data/msgparse.h"
+#include "util/data/msgencode.h"
+#include "util/data/dname.h"
+#include "util/fptr_wlist.h"
+#include "util/tube.h"
+#include "iterator/iter_fwd.h"
+#include "validator/autotrust.h"
+
+#ifdef HAVE_SYS_TYPES_H
+#  include <sys/types.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#include <signal.h>
+#ifdef UB_ON_WINDOWS
+#include "winrc/win_svc.h"
+#endif
+
+/** Size of an UDP datagram */
+#define NORMAL_UDP_SIZE	512 /* bytes */
+
+/** 
+ * seconds to add to prefetch leeway.  This is a TTL that expires old rrsets
+ * earlier than they should in order to put the new update into the cache.
+ * This additional value is to make sure that if not all TTLs are equal in
+ * the message to be updated(and replaced), that rrsets with up to this much
+ * extra TTL are also replaced.  This means that the resulting new message
+ * will have (most likely) this TTL at least, avoiding very small 'split
+ * second' TTLs due to operators choosing relative primes for TTLs (or so).
+ * Also has to be at least one to break ties (and overwrite cached entry).
+ */
+#define PREFETCH_EXPIRY_ADD 60
+
+#ifdef UNBOUND_ALLOC_STATS
+/** measure memory leakage */
+static void
+debug_memleak(size_t accounted, size_t heap, 
+	size_t total_alloc, size_t total_free)
+{
+	static int init = 0;
+	static size_t base_heap, base_accounted, base_alloc, base_free;
+	size_t base_af, cur_af, grow_af, grow_acc;
+	if(!init) {
+		init = 1;
+		base_heap = heap;
+		base_accounted = accounted;
+		base_alloc = total_alloc;
+		base_free = total_free;
+	}
+	base_af = base_alloc - base_free;
+	cur_af = total_alloc - total_free;
+	grow_af = cur_af - base_af;
+	grow_acc = accounted - base_accounted;
+	log_info("Leakage: %d leaked. growth: %u use, %u acc, %u heap",
+		(int)(grow_af - grow_acc), (unsigned)grow_af, 
+		(unsigned)grow_acc, (unsigned)(heap - base_heap));
+}
+
+/** give debug heap size indication */
+static void
+debug_total_mem(size_t calctotal)
+{
+#ifdef HAVE_SBRK
+	extern void* unbound_start_brk;
+	extern size_t unbound_mem_alloc, unbound_mem_freed;
+	void* cur = sbrk(0);
+	int total = cur-unbound_start_brk;
+	log_info("Total heap memory estimate: %u  total-alloc: %u  "
+		"total-free: %u", (unsigned)total, 
+		(unsigned)unbound_mem_alloc, (unsigned)unbound_mem_freed);
+	debug_memleak(calctotal, (size_t)total, 
+		unbound_mem_alloc, unbound_mem_freed);
+#else
+	(void)calctotal;
+#endif /* HAVE_SBRK */
+}
+#endif /* UNBOUND_ALLOC_STATS */
+
+/** Report on memory usage by this thread and global */
+static void
+worker_mem_report(struct worker* ATTR_UNUSED(worker), 
+	struct serviced_query* ATTR_UNUSED(cur_serv))
+{
+#ifdef UNBOUND_ALLOC_STATS
+	/* debug func in validator module */
+	size_t total, front, back, mesh, msg, rrset, infra, ac, superac;
+	size_t me, iter, val;
+	int i;
+	if(verbosity < VERB_ALGO) 
+		return;
+	front = listen_get_mem(worker->front);
+	back = outnet_get_mem(worker->back);
+	msg = slabhash_get_mem(worker->env.msg_cache);
+	rrset = slabhash_get_mem(&worker->env.rrset_cache->table);
+	infra = infra_get_mem(worker->env.infra_cache);
+	mesh = mesh_get_mem(worker->env.mesh);
+	ac = alloc_get_mem(&worker->alloc);
+	superac = alloc_get_mem(&worker->daemon->superalloc);
+	iter = 0;
+	val = 0;
+	for(i=0; i<worker->env.mesh->mods.num; i++) {
+		fptr_ok(fptr_whitelist_mod_get_mem(worker->env.mesh->
+			mods.mod[i]->get_mem));
+		if(strcmp(worker->env.mesh->mods.mod[i]->name, "validator")==0)
+			val += (*worker->env.mesh->mods.mod[i]->get_mem)
+				(&worker->env, i);
+		else	iter += (*worker->env.mesh->mods.mod[i]->get_mem)
+				(&worker->env, i);
+	}
+	me = sizeof(*worker) + sizeof(*worker->base) + sizeof(*worker->comsig)
+		+ comm_point_get_mem(worker->cmd_com) 
+		+ sizeof(worker->rndstate) 
+		+ regional_get_mem(worker->scratchpad) 
+		+ sizeof(*worker->env.scratch_buffer) 
+		+ ldns_buffer_capacity(worker->env.scratch_buffer)
+		+ forwards_get_mem(worker->env.fwds);
+	if(worker->thread_num == 0)
+		me += acl_list_get_mem(worker->daemon->acl);
+	if(cur_serv) {
+		me += serviced_get_mem(cur_serv);
+	}
+	total = front+back+mesh+msg+rrset+infra+iter+val+ac+superac+me;
+	log_info("Memory conditions: %u front=%u back=%u mesh=%u msg=%u "
+		"rrset=%u infra=%u iter=%u val=%u "
+		"alloccache=%u globalalloccache=%u me=%u",
+		(unsigned)total, (unsigned)front, (unsigned)back, 
+		(unsigned)mesh, (unsigned)msg, (unsigned)rrset, 
+		(unsigned)infra, (unsigned)iter, (unsigned)val, (unsigned)ac, 
+		(unsigned)superac, (unsigned)me);
+	debug_total_mem(total);
+#else /* no UNBOUND_ALLOC_STATS */
+	size_t val = 0;
+	int i;
+	if(verbosity < VERB_QUERY)
+		return;
+	for(i=0; i<worker->env.mesh->mods.num; i++) {
+		fptr_ok(fptr_whitelist_mod_get_mem(worker->env.mesh->
+			mods.mod[i]->get_mem));
+		if(strcmp(worker->env.mesh->mods.mod[i]->name, "validator")==0)
+			val += (*worker->env.mesh->mods.mod[i]->get_mem)
+				(&worker->env, i);
+	}
+	verbose(VERB_QUERY, "cache memory msg=%u rrset=%u infra=%u val=%u",
+		(unsigned)slabhash_get_mem(worker->env.msg_cache),
+		(unsigned)slabhash_get_mem(&worker->env.rrset_cache->table),
+		(unsigned)infra_get_mem(worker->env.infra_cache),
+		(unsigned)val);
+#endif /* UNBOUND_ALLOC_STATS */
+}
+
+void 
+worker_send_cmd(struct worker* worker, enum worker_commands cmd)
+{
+	uint32_t c = (uint32_t)htonl(cmd);
+	if(!tube_write_msg(worker->cmd, (uint8_t*)&c, sizeof(c), 0)) {
+		log_err("worker send cmd %d failed", (int)cmd);
+	}
+}
+
+int 
+worker_handle_reply(struct comm_point* c, void* arg, int error, 
+	struct comm_reply* reply_info)
+{
+	struct module_qstate* q = (struct module_qstate*)arg;
+	struct worker* worker = q->env->worker;
+	struct outbound_entry e;
+	e.qstate = q;
+	e.qsent = NULL;
+
+	if(error != 0) {
+		mesh_report_reply(worker->env.mesh, &e, reply_info, error);
+		worker_mem_report(worker, NULL);
+		return 0;
+	}
+	/* sanity check. */
+	if(!LDNS_QR_WIRE(ldns_buffer_begin(c->buffer))
+		|| LDNS_OPCODE_WIRE(ldns_buffer_begin(c->buffer)) != 
+			LDNS_PACKET_QUERY
+		|| LDNS_QDCOUNT(ldns_buffer_begin(c->buffer)) > 1) {
+		/* error becomes timeout for the module as if this reply
+		 * never arrived. */
+		mesh_report_reply(worker->env.mesh, &e, reply_info, 
+			NETEVENT_TIMEOUT);
+		worker_mem_report(worker, NULL);
+		return 0;
+	}
+	mesh_report_reply(worker->env.mesh, &e, reply_info, NETEVENT_NOERROR);
+	worker_mem_report(worker, NULL);
+	return 0;
+}
+
+int 
+worker_handle_service_reply(struct comm_point* c, void* arg, int error, 
+	struct comm_reply* reply_info)
+{
+	struct outbound_entry* e = (struct outbound_entry*)arg;
+	struct worker* worker = e->qstate->env->worker;
+	struct serviced_query *sq = e->qsent;
+
+	verbose(VERB_ALGO, "worker svcd callback for qstate %p", e->qstate);
+	if(error != 0) {
+		mesh_report_reply(worker->env.mesh, e, reply_info, error);
+		worker_mem_report(worker, sq);
+		return 0;
+	}
+	/* sanity check. */
+	if(!LDNS_QR_WIRE(ldns_buffer_begin(c->buffer))
+		|| LDNS_OPCODE_WIRE(ldns_buffer_begin(c->buffer)) != 
+			LDNS_PACKET_QUERY
+		|| LDNS_QDCOUNT(ldns_buffer_begin(c->buffer)) > 1) {
+		/* error becomes timeout for the module as if this reply
+		 * never arrived. */
+		verbose(VERB_ALGO, "worker: bad reply handled as timeout");
+		mesh_report_reply(worker->env.mesh, e, reply_info, 
+			NETEVENT_TIMEOUT);
+		worker_mem_report(worker, sq);
+		return 0;
+	}
+	mesh_report_reply(worker->env.mesh, e, reply_info, NETEVENT_NOERROR);
+	worker_mem_report(worker, sq);
+	return 0;
+}
+
+/** check request sanity.
+ * @param pkt: the wire packet to examine for sanity.
+ * @param worker: parameters for checking.
+ * @return error code, 0 OK, or -1 discard.
+*/
+static int 
+worker_check_request(ldns_buffer* pkt, struct worker* worker)
+{
+	if(ldns_buffer_limit(pkt) < LDNS_HEADER_SIZE) {
+		verbose(VERB_QUERY, "request too short, discarded");
+		return -1;
+	}
+	if(ldns_buffer_limit(pkt) > NORMAL_UDP_SIZE && 
+		worker->daemon->cfg->harden_large_queries) {
+		verbose(VERB_QUERY, "request too large, discarded");
+		return -1;
+	}
+	if(LDNS_QR_WIRE(ldns_buffer_begin(pkt))) {
+		verbose(VERB_QUERY, "request has QR bit on, discarded");
+		return -1;
+	}
+	if(LDNS_TC_WIRE(ldns_buffer_begin(pkt))) {
+		LDNS_TC_CLR(ldns_buffer_begin(pkt));
+		verbose(VERB_QUERY, "request bad, has TC bit on");
+		return LDNS_RCODE_FORMERR;
+	}
+	if(LDNS_OPCODE_WIRE(ldns_buffer_begin(pkt)) != LDNS_PACKET_QUERY) {
+		verbose(VERB_QUERY, "request unknown opcode %d", 
+			LDNS_OPCODE_WIRE(ldns_buffer_begin(pkt)));
+		return LDNS_RCODE_NOTIMPL;
+	}
+	if(LDNS_QDCOUNT(ldns_buffer_begin(pkt)) != 1) {
+		verbose(VERB_QUERY, "request wrong nr qd=%d", 
+			LDNS_QDCOUNT(ldns_buffer_begin(pkt)));
+		return LDNS_RCODE_FORMERR;
+	}
+	if(LDNS_ANCOUNT(ldns_buffer_begin(pkt)) != 0) {
+		verbose(VERB_QUERY, "request wrong nr an=%d", 
+			LDNS_ANCOUNT(ldns_buffer_begin(pkt)));
+		return LDNS_RCODE_FORMERR;
+	}
+	if(LDNS_NSCOUNT(ldns_buffer_begin(pkt)) != 0) {
+		verbose(VERB_QUERY, "request wrong nr ns=%d", 
+			LDNS_NSCOUNT(ldns_buffer_begin(pkt)));
+		return LDNS_RCODE_FORMERR;
+	}
+	if(LDNS_ARCOUNT(ldns_buffer_begin(pkt)) > 1) {
+		verbose(VERB_QUERY, "request wrong nr ar=%d", 
+			LDNS_ARCOUNT(ldns_buffer_begin(pkt)));
+		return LDNS_RCODE_FORMERR;
+	}
+	return 0;
+}
+
+void 
+worker_handle_control_cmd(struct tube* ATTR_UNUSED(tube), uint8_t* msg,
+	size_t len, int error, void* arg)
+{
+	struct worker* worker = (struct worker*)arg;
+	enum worker_commands cmd;
+	if(error != NETEVENT_NOERROR) {
+		free(msg);
+		if(error == NETEVENT_CLOSED)
+			comm_base_exit(worker->base);
+		else	log_info("control event: %d", error);
+		return;
+	}
+	if(len != sizeof(uint32_t)) {
+		fatal_exit("bad control msg length %d", (int)len);
+	}
+	cmd = ldns_read_uint32(msg);
+	free(msg);
+	switch(cmd) {
+	case worker_cmd_quit:
+		verbose(VERB_ALGO, "got control cmd quit");
+		comm_base_exit(worker->base);
+		break;
+	case worker_cmd_stats:
+		verbose(VERB_ALGO, "got control cmd stats");
+		server_stats_reply(worker, 1);
+		break;
+	case worker_cmd_stats_noreset:
+		verbose(VERB_ALGO, "got control cmd stats_noreset");
+		server_stats_reply(worker, 0);
+		break;
+	case worker_cmd_remote:
+		verbose(VERB_ALGO, "got control cmd remote");
+		daemon_remote_exec(worker);
+		break;
+	default:
+		log_err("bad command %d", (int)cmd);
+		break;
+	}
+}
+
+/** check if a delegation is secure */
+static enum sec_status
+check_delegation_secure(struct reply_info *rep) 
+{
+	/* return smallest security status */
+	size_t i;
+	enum sec_status sec = sec_status_secure;
+	enum sec_status s;
+	size_t num = rep->an_numrrsets + rep->ns_numrrsets;
+	/* check if answer and authority are OK */
+	for(i=0; i<num; i++) {
+		s = ((struct packed_rrset_data*)rep->rrsets[i]->entry.data)
+			->security;
+		if(s < sec)
+			sec = s;
+	}
+	/* in additional, only unchecked triggers revalidation */
+	for(i=num; i<rep->rrset_count; i++) {
+		s = ((struct packed_rrset_data*)rep->rrsets[i]->entry.data)
+			->security;
+		if(s == sec_status_unchecked)
+			return s;
+	}
+	return sec;
+}
+
+/** remove nonsecure from a delegation referral additional section */
+static void
+deleg_remove_nonsecure_additional(struct reply_info* rep)
+{
+	/* we can simply edit it, since we are working in the scratch region */
+	size_t i;
+	enum sec_status s;
+
+	for(i = rep->an_numrrsets+rep->ns_numrrsets; i<rep->rrset_count; i++) {
+		s = ((struct packed_rrset_data*)rep->rrsets[i]->entry.data)
+			->security;
+		if(s != sec_status_secure) {
+			memmove(rep->rrsets+i, rep->rrsets+i+1, 
+				sizeof(struct ub_packed_rrset_key*)* 
+				(rep->rrset_count - i - 1));
+			rep->ar_numrrsets--; 
+			rep->rrset_count--;
+			i--;
+		}
+	}
+}
+
+/** answer nonrecursive query from the cache */
+static int
+answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
+	uint16_t id, uint16_t flags, struct comm_reply* repinfo, 
+	struct edns_data* edns)
+{
+	/* for a nonrecursive query return either:
+	 * 	o an error (servfail; we try to avoid this)
+	 * 	o a delegation (closest we have; this routine tries that)
+	 * 	o the answer (checked by answer_from_cache) 
+	 *
+	 * So, grab a delegation from the rrset cache. 
+	 * Then check if it needs validation, if so, this routine fails,
+	 * so that iterator can prime and validator can verify rrsets.
+	 */
+	uint16_t udpsize = edns->udp_size;
+	int secure = 0;
+	uint32_t timenow = *worker->env.now;
+	int must_validate = (!(flags&BIT_CD) || worker->env.cfg->ignore_cd)
+		&& worker->env.need_to_validate;
+	struct dns_msg *msg = NULL;
+	struct delegpt *dp;
+
+	dp = dns_cache_find_delegation(&worker->env, qinfo->qname, 
+		qinfo->qname_len, qinfo->qtype, qinfo->qclass,
+		worker->scratchpad, &msg, timenow);
+	if(!dp) { /* no delegation, need to reprime */
+		regional_free_all(worker->scratchpad);
+		return 0;
+	}
+	if(must_validate) {
+		switch(check_delegation_secure(msg->rep)) {
+		case sec_status_unchecked:
+			/* some rrsets have not been verified yet, go and 
+			 * let validator do that */
+			regional_free_all(worker->scratchpad);
+			return 0;
+		case sec_status_bogus:
+			/* some rrsets are bogus, reply servfail */
+			edns->edns_version = EDNS_ADVERTISED_VERSION;
+			edns->udp_size = EDNS_ADVERTISED_SIZE;
+			edns->ext_rcode = 0;
+			edns->bits &= EDNS_DO;
+			error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, 
+				&msg->qinfo, id, flags, edns);
+			regional_free_all(worker->scratchpad);
+			if(worker->stats.extended) {
+				worker->stats.ans_bogus++;
+				worker->stats.ans_rcode[LDNS_RCODE_SERVFAIL]++;
+			}
+			return 1;
+		case sec_status_secure:
+			/* all rrsets are secure */
+			/* remove non-secure rrsets from the add. section*/
+			if(worker->env.cfg->val_clean_additional)
+				deleg_remove_nonsecure_additional(msg->rep);
+			secure = 1;
+			break;
+		case sec_status_indeterminate:
+		case sec_status_insecure:
+		default:
+			/* not secure */
+			secure = 0;
+			break;
+		}
+	}
+	/* return this delegation from the cache */
+	edns->edns_version = EDNS_ADVERTISED_VERSION;
+	edns->udp_size = EDNS_ADVERTISED_SIZE;
+	edns->ext_rcode = 0;
+	edns->bits &= EDNS_DO;
+	msg->rep->flags |= BIT_QR|BIT_RA;
+	if(!reply_info_answer_encode(&msg->qinfo, msg->rep, id, flags, 
+		repinfo->c->buffer, 0, 1, worker->scratchpad,
+		udpsize, edns, (int)(edns->bits & EDNS_DO), secure)) {
+		error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, 
+			&msg->qinfo, id, flags, edns);
+	}
+	regional_free_all(worker->scratchpad);
+	if(worker->stats.extended) {
+		if(secure) worker->stats.ans_secure++;
+		server_stats_insrcode(&worker->stats, repinfo->c->buffer);
+	}
+	return 1;
+}
+
+/** answer query from the cache */
+static int
+answer_from_cache(struct worker* worker, struct query_info* qinfo,
+	struct reply_info* rep, uint16_t id, uint16_t flags, 
+	struct comm_reply* repinfo, struct edns_data* edns)
+{
+	uint32_t timenow = *worker->env.now;
+	uint16_t udpsize = edns->udp_size;
+	int secure;
+	int must_validate = (!(flags&BIT_CD) || worker->env.cfg->ignore_cd)
+		&& worker->env.need_to_validate;
+	/* see if it is possible */
+	if(rep->ttl < timenow) {
+		/* the rrsets may have been updated in the meantime.
+		 * we will refetch the message format from the
+		 * authoritative server 
+		 */
+		return 0;
+	}
+	if(!rrset_array_lock(rep->ref, rep->rrset_count, timenow))
+		return 0;
+	/* locked and ids and ttls are OK. */
+	/* check CNAME chain (if any) */
+	if(rep->an_numrrsets > 0 && (rep->rrsets[0]->rk.type == 
+		htons(LDNS_RR_TYPE_CNAME) || rep->rrsets[0]->rk.type == 
+		htons(LDNS_RR_TYPE_DNAME))) {
+		if(!reply_check_cname_chain(rep)) {
+			/* cname chain invalid, redo iterator steps */
+			verbose(VERB_ALGO, "Cache reply: cname chain broken");
+		bail_out:
+			rrset_array_unlock_touch(worker->env.rrset_cache, 
+				worker->scratchpad, rep->ref, rep->rrset_count);
+			regional_free_all(worker->scratchpad);
+			return 0;
+		}
+	}
+	/* check security status of the cached answer */
+	if( rep->security == sec_status_bogus && must_validate) {
+		/* BAD cached */
+		edns->edns_version = EDNS_ADVERTISED_VERSION;
+		edns->udp_size = EDNS_ADVERTISED_SIZE;
+		edns->ext_rcode = 0;
+		edns->bits &= EDNS_DO;
+		error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, 
+			qinfo, id, flags, edns);
+		rrset_array_unlock_touch(worker->env.rrset_cache, 
+			worker->scratchpad, rep->ref, rep->rrset_count);
+		regional_free_all(worker->scratchpad);
+		if(worker->stats.extended) {
+			worker->stats.ans_bogus ++;
+			worker->stats.ans_rcode[LDNS_RCODE_SERVFAIL] ++;
+		}
+		return 1;
+	} else if( rep->security == sec_status_unchecked && must_validate) {
+		verbose(VERB_ALGO, "Cache reply: unchecked entry needs "
+			"validation");
+		goto bail_out; /* need to validate cache entry first */
+	} else if(rep->security == sec_status_secure) {
+		if(reply_all_rrsets_secure(rep))
+			secure = 1;
+		else	{
+			if(must_validate) {
+				verbose(VERB_ALGO, "Cache reply: secure entry"
+					" changed status");
+				goto bail_out; /* rrset changed, re-verify */
+			}
+			secure = 0;
+		}
+	} else	secure = 0;
+
+	edns->edns_version = EDNS_ADVERTISED_VERSION;
+	edns->udp_size = EDNS_ADVERTISED_SIZE;
+	edns->ext_rcode = 0;
+	edns->bits &= EDNS_DO;
+	if(!reply_info_answer_encode(qinfo, rep, id, flags, 
+		repinfo->c->buffer, timenow, 1, worker->scratchpad,
+		udpsize, edns, (int)(edns->bits & EDNS_DO), secure)) {
+		error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, 
+			qinfo, id, flags, edns);
+	}
+	/* cannot send the reply right now, because blocking network syscall
+	 * is bad while holding locks. */
+	rrset_array_unlock_touch(worker->env.rrset_cache, worker->scratchpad,
+		rep->ref, rep->rrset_count);
+	regional_free_all(worker->scratchpad);
+	if(worker->stats.extended) {
+		if(secure) worker->stats.ans_secure++;
+		server_stats_insrcode(&worker->stats, repinfo->c->buffer);
+	}
+	/* go and return this buffer to the client */
+	return 1;
+}
+
+/** Reply to client and perform prefetch to keep cache up to date */
+static void
+reply_and_prefetch(struct worker* worker, struct query_info* qinfo, 
+	uint16_t flags, struct comm_reply* repinfo, uint32_t leeway)
+{
+	/* first send answer to client to keep its latency 
+	 * as small as a cachereply */
+	comm_point_send_reply(repinfo);
+	server_stats_prefetch(&worker->stats, worker);
+	
+	/* create the prefetch in the mesh as a normal lookup without
+	 * client addrs waiting, which has the cache blacklisted (to bypass
+	 * the cache and go to the network for the data). */
+	/* this (potentially) runs the mesh for the new query */
+	mesh_new_prefetch(worker->env.mesh, qinfo, flags, leeway + 
+		PREFETCH_EXPIRY_ADD);
+}
+
+/**
+ * Fill CH class answer into buffer. Keeps query.
+ * @param pkt: buffer
+ * @param str: string to put into text record (<255).
+ * @param edns: edns reply information.
+ */
+static void
+chaos_replystr(ldns_buffer* pkt, const char* str, struct edns_data* edns)
+{
+	size_t len = strlen(str);
+	unsigned int rd = LDNS_RD_WIRE(ldns_buffer_begin(pkt));
+	unsigned int cd = LDNS_CD_WIRE(ldns_buffer_begin(pkt));
+	if(len>255) len=255; /* cap size of TXT record */
+	ldns_buffer_clear(pkt);
+	ldns_buffer_skip(pkt, (ssize_t)sizeof(uint16_t)); /* skip id */
+	ldns_buffer_write_u16(pkt, (uint16_t)(BIT_QR|BIT_RA));
+	if(rd) LDNS_RD_SET(ldns_buffer_begin(pkt));
+	if(cd) LDNS_CD_SET(ldns_buffer_begin(pkt));
+	ldns_buffer_write_u16(pkt, 1); /* qdcount */
+	ldns_buffer_write_u16(pkt, 1); /* ancount */
+	ldns_buffer_write_u16(pkt, 0); /* nscount */
+	ldns_buffer_write_u16(pkt, 0); /* arcount */
+	(void)query_dname_len(pkt); /* skip qname */
+	ldns_buffer_skip(pkt, (ssize_t)sizeof(uint16_t)); /* skip qtype */
+	ldns_buffer_skip(pkt, (ssize_t)sizeof(uint16_t)); /* skip qclass */
+	ldns_buffer_write_u16(pkt, 0xc00c); /* compr ptr to query */
+	ldns_buffer_write_u16(pkt, LDNS_RR_TYPE_TXT);
+	ldns_buffer_write_u16(pkt, LDNS_RR_CLASS_CH);
+	ldns_buffer_write_u32(pkt, 0); /* TTL */
+	ldns_buffer_write_u16(pkt, sizeof(uint8_t) + len);
+	ldns_buffer_write_u8(pkt, len);
+	ldns_buffer_write(pkt, str, len);
+	ldns_buffer_flip(pkt);
+	edns->edns_version = EDNS_ADVERTISED_VERSION;
+	edns->udp_size = EDNS_ADVERTISED_SIZE;
+	edns->bits &= EDNS_DO;
+	attach_edns_record(pkt, edns);
+}
+
+/**
+ * Answer CH class queries.
+ * @param w: worker
+ * @param qinfo: query info. Pointer into packet buffer.
+ * @param edns: edns info from query.
+ * @param pkt: packet buffer.
+ * @return: true if a reply is to be sent.
+ */
+static int
+answer_chaos(struct worker* w, struct query_info* qinfo, 
+	struct edns_data* edns, ldns_buffer* pkt)
+{
+	struct config_file* cfg = w->env.cfg;
+	if(qinfo->qtype != LDNS_RR_TYPE_ANY && qinfo->qtype != LDNS_RR_TYPE_TXT)
+		return 0;
+	if(query_dname_compare(qinfo->qname, 
+		(uint8_t*)"\002id\006server") == 0 ||
+		query_dname_compare(qinfo->qname, 
+		(uint8_t*)"\010hostname\004bind") == 0)
+	{
+		if(cfg->hide_identity) 
+			return 0;
+		if(cfg->identity==NULL || cfg->identity[0]==0) {
+			char buf[MAXHOSTNAMELEN+1];
+			if (gethostname(buf, MAXHOSTNAMELEN) == 0) {
+				buf[MAXHOSTNAMELEN] = 0;
+				chaos_replystr(pkt, buf, edns);
+			} else 	{
+				log_err("gethostname: %s", strerror(errno));
+				chaos_replystr(pkt, "no hostname", edns);
+			}
+		}
+		else 	chaos_replystr(pkt, cfg->identity, edns);
+		return 1;
+	}
+	if(query_dname_compare(qinfo->qname, 
+		(uint8_t*)"\007version\006server") == 0 ||
+		query_dname_compare(qinfo->qname, 
+		(uint8_t*)"\007version\004bind") == 0)
+	{
+		if(cfg->hide_version) 
+			return 0;
+		if(cfg->version==NULL || cfg->version[0]==0)
+			chaos_replystr(pkt, PACKAGE_STRING, edns);
+		else 	chaos_replystr(pkt, cfg->version, edns);
+		return 1;
+	}
+	return 0;
+}
+
+int 
+worker_handle_request(struct comm_point* c, void* arg, int error,
+	struct comm_reply* repinfo)
+{
+	struct worker* worker = (struct worker*)arg;
+	int ret;
+	hashvalue_t h;
+	struct lruhash_entry* e;
+	struct query_info qinfo;
+	struct edns_data edns;
+	enum acl_access acl;
+
+	if(error != NETEVENT_NOERROR) {
+		/* some bad tcp query DNS formats give these error calls */
+		verbose(VERB_ALGO, "handle request called with err=%d", error);
+		return 0;
+	}
+	acl = acl_list_lookup(worker->daemon->acl, &repinfo->addr, 
+		repinfo->addrlen);
+	if(acl == acl_deny) {
+		comm_point_drop_reply(repinfo);
+		if(worker->stats.extended)
+			worker->stats.unwanted_queries++;
+		return 0;
+	} else if(acl == acl_refuse) {
+		log_addr(VERB_ALGO, "refused query from",
+			&repinfo->addr, repinfo->addrlen);
+		log_buf(VERB_ALGO, "refuse", c->buffer);
+		if(worker->stats.extended)
+			worker->stats.unwanted_queries++;
+		if(worker_check_request(c->buffer, worker) == -1) {
+			comm_point_drop_reply(repinfo);
+			return 0; /* discard this */
+		}
+		ldns_buffer_set_limit(c->buffer, LDNS_HEADER_SIZE);
+		ldns_buffer_write_at(c->buffer, 4, 
+			(uint8_t*)"\0\0\0\0\0\0\0\0", 8);
+		LDNS_QR_SET(ldns_buffer_begin(c->buffer));
+		LDNS_RCODE_SET(ldns_buffer_begin(c->buffer), 
+			LDNS_RCODE_REFUSED);
+		return 1;
+	}
+	if((ret=worker_check_request(c->buffer, worker)) != 0) {
+		verbose(VERB_ALGO, "worker check request: bad query.");
+		log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
+		if(ret != -1) {
+			LDNS_QR_SET(ldns_buffer_begin(c->buffer));
+			LDNS_RCODE_SET(ldns_buffer_begin(c->buffer), ret);
+			return 1;
+		}
+		comm_point_drop_reply(repinfo);
+		return 0;
+	}
+	worker->stats.num_queries++;
+	/* see if query is in the cache */
+	if(!query_info_parse(&qinfo, c->buffer)) {
+		verbose(VERB_ALGO, "worker parse request: formerror.");
+		log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
+		ldns_buffer_rewind(c->buffer);
+		LDNS_QR_SET(ldns_buffer_begin(c->buffer));
+		LDNS_RCODE_SET(ldns_buffer_begin(c->buffer), 
+			LDNS_RCODE_FORMERR);
+		server_stats_insrcode(&worker->stats, c->buffer);
+		return 1;
+	}
+	if(worker->env.cfg->log_queries) {
+		char ip[128];
+		addr_to_str(&repinfo->addr, repinfo->addrlen, ip, sizeof(ip));
+		log_nametypeclass(0, ip, qinfo.qname, qinfo.qtype, qinfo.qclass);
+	}
+	if(qinfo.qtype == LDNS_RR_TYPE_AXFR || 
+		qinfo.qtype == LDNS_RR_TYPE_IXFR) {
+		verbose(VERB_ALGO, "worker request: refused zone transfer.");
+		log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
+		ldns_buffer_rewind(c->buffer);
+		LDNS_QR_SET(ldns_buffer_begin(c->buffer));
+		LDNS_RCODE_SET(ldns_buffer_begin(c->buffer), 
+			LDNS_RCODE_REFUSED);
+		if(worker->stats.extended) {
+			worker->stats.qtype[qinfo.qtype]++;
+			server_stats_insrcode(&worker->stats, c->buffer);
+		}
+		return 1;
+	}
+	if((ret=parse_edns_from_pkt(c->buffer, &edns)) != 0) {
+		verbose(VERB_ALGO, "worker parse edns: formerror.");
+		log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
+		ldns_buffer_rewind(c->buffer);
+		LDNS_QR_SET(ldns_buffer_begin(c->buffer));
+		LDNS_RCODE_SET(ldns_buffer_begin(c->buffer), ret);
+		server_stats_insrcode(&worker->stats, c->buffer);
+		return 1;
+	}
+	if(edns.edns_present && edns.edns_version != 0) {
+		edns.ext_rcode = (uint8_t)(EDNS_RCODE_BADVERS>>4);
+		edns.edns_version = EDNS_ADVERTISED_VERSION;
+		edns.udp_size = EDNS_ADVERTISED_SIZE;
+		edns.bits &= EDNS_DO;
+		verbose(VERB_ALGO, "query with bad edns version.");
+		log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
+		error_encode(c->buffer, EDNS_RCODE_BADVERS&0xf, &qinfo,
+			*(uint16_t*)ldns_buffer_begin(c->buffer),
+			ldns_buffer_read_u16_at(c->buffer, 2), NULL);
+		attach_edns_record(c->buffer, &edns);
+		return 1;
+	}
+	if(edns.edns_present && edns.udp_size < NORMAL_UDP_SIZE &&
+		worker->daemon->cfg->harden_short_bufsize) {
+		verbose(VERB_QUERY, "worker request: EDNS bufsize %d ignored",
+			(int)edns.udp_size);
+		log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
+		edns.udp_size = NORMAL_UDP_SIZE;
+	}
+	if(edns.edns_present && edns.udp_size < LDNS_HEADER_SIZE) {
+		verbose(VERB_ALGO, "worker request: edns is too small.");
+		log_addr(VERB_CLIENT, "from", &repinfo->addr, repinfo->addrlen);
+		LDNS_QR_SET(ldns_buffer_begin(c->buffer));
+		LDNS_TC_SET(ldns_buffer_begin(c->buffer));
+		LDNS_RCODE_SET(ldns_buffer_begin(c->buffer), 
+			LDNS_RCODE_SERVFAIL);
+		ldns_buffer_set_position(c->buffer, LDNS_HEADER_SIZE);
+		ldns_buffer_write_at(c->buffer, 4, 
+			(uint8_t*)"\0\0\0\0\0\0\0\0", 8);
+		ldns_buffer_flip(c->buffer);
+		return 1;
+	}
+	if(worker->stats.extended)
+		server_stats_insquery(&worker->stats, c, qinfo.qtype,
+			qinfo.qclass, &edns, repinfo);
+	if(c->type != comm_udp)
+		edns.udp_size = 65535; /* max size for TCP replies */
+	if(qinfo.qclass == LDNS_RR_CLASS_CH && answer_chaos(worker, &qinfo,
+		&edns, c->buffer)) {
+		server_stats_insrcode(&worker->stats, c->buffer);
+		return 1;
+	}
+	if(local_zones_answer(worker->daemon->local_zones, &qinfo, &edns, 
+		c->buffer, worker->scratchpad)) {
+		regional_free_all(worker->scratchpad);
+		if(ldns_buffer_limit(c->buffer) == 0) {
+			comm_point_drop_reply(repinfo);
+			return 0;
+		}
+		server_stats_insrcode(&worker->stats, c->buffer);
+		return 1;
+	}
+	if(!(LDNS_RD_WIRE(ldns_buffer_begin(c->buffer))) &&
+		acl != acl_allow_snoop ) {
+		ldns_buffer_set_limit(c->buffer, LDNS_HEADER_SIZE);
+		ldns_buffer_write_at(c->buffer, 4, 
+			(uint8_t*)"\0\0\0\0\0\0\0\0", 8);
+		LDNS_QR_SET(ldns_buffer_begin(c->buffer));
+		LDNS_RCODE_SET(ldns_buffer_begin(c->buffer), 
+			LDNS_RCODE_REFUSED);
+		ldns_buffer_flip(c->buffer);
+		server_stats_insrcode(&worker->stats, c->buffer);
+		log_addr(VERB_ALGO, "refused nonrec (cache snoop) query from",
+			&repinfo->addr, repinfo->addrlen);
+		return 1;
+	}
+	h = query_info_hash(&qinfo);
+	if((e=slabhash_lookup(worker->env.msg_cache, h, &qinfo, 0))) {
+		/* answer from cache - we have acquired a readlock on it */
+		if(answer_from_cache(worker, &qinfo, 
+			(struct reply_info*)e->data, 
+			*(uint16_t*)ldns_buffer_begin(c->buffer), 
+			ldns_buffer_read_u16_at(c->buffer, 2), repinfo, 
+			&edns)) {
+			/* prefetch it if the prefetch TTL expired */
+			if(worker->env.cfg->prefetch && *worker->env.now >=
+				((struct reply_info*)e->data)->prefetch_ttl) {
+				uint32_t leeway = ((struct reply_info*)e->
+					data)->ttl - *worker->env.now;
+				lock_rw_unlock(&e->lock);
+				reply_and_prefetch(worker, &qinfo, 
+					ldns_buffer_read_u16_at(c->buffer, 2),
+					repinfo, leeway);
+				return 0;
+			}
+			lock_rw_unlock(&e->lock);
+			return 1;
+		}
+		verbose(VERB_ALGO, "answer from the cache failed");
+		lock_rw_unlock(&e->lock);
+	}
+	if(!LDNS_RD_WIRE(ldns_buffer_begin(c->buffer))) {
+		if(answer_norec_from_cache(worker, &qinfo,
+			*(uint16_t*)ldns_buffer_begin(c->buffer), 
+			ldns_buffer_read_u16_at(c->buffer, 2), repinfo, 
+			&edns)) {
+			return 1;
+		}
+		verbose(VERB_ALGO, "answer norec from cache -- "
+			"need to validate or not primed");
+	}
+	ldns_buffer_rewind(c->buffer);
+	server_stats_querymiss(&worker->stats, worker);
+
+	if(verbosity >= VERB_CLIENT) {
+		if(c->type == comm_udp)
+			log_addr(VERB_CLIENT, "udp request from",
+				&repinfo->addr, repinfo->addrlen);
+		else	log_addr(VERB_CLIENT, "tcp request from",
+				&repinfo->addr, repinfo->addrlen);
+	}
+
+	/* grab a work request structure for this new request */
+	mesh_new_client(worker->env.mesh, &qinfo, 
+		ldns_buffer_read_u16_at(c->buffer, 2),
+		&edns, repinfo, *(uint16_t*)ldns_buffer_begin(c->buffer));
+	worker_mem_report(worker, NULL);
+	return 0;
+}
+
+void 
+worker_sighandler(int sig, void* arg)
+{
+	/* note that log, print, syscalls here give race conditions. */
+	/* we still print DETAIL logs, because this is extensive per message
+	 * logging anyway, and the operator may then have an interest
+	 * in the cause for unbound to exit */
+	struct worker* worker = (struct worker*)arg;
+	switch(sig) {
+#ifdef SIGHUP
+		case SIGHUP:
+			verbose(VERB_QUERY, "caught signal SIGHUP");
+			comm_base_exit(worker->base);
+			break;
+#endif
+		case SIGINT:
+			verbose(VERB_QUERY, "caught signal SIGINT");
+			worker->need_to_exit = 1;
+			comm_base_exit(worker->base);
+			break;
+#ifdef SIGQUIT
+		case SIGQUIT:
+			verbose(VERB_QUERY, "caught signal SIGQUIT");
+			worker->need_to_exit = 1;
+			comm_base_exit(worker->base);
+			break;
+#endif
+		case SIGTERM:
+			verbose(VERB_QUERY, "caught signal SIGTERM");
+			worker->need_to_exit = 1;
+			comm_base_exit(worker->base);
+			break;
+		default:
+			log_err("unknown signal: %d, ignored", sig);
+			break;
+	}
+}
+
+/** restart statistics timer for worker, if enabled */
+static void
+worker_restart_timer(struct worker* worker)
+{
+	if(worker->env.cfg->stat_interval > 0) {
+		struct timeval tv;
+#ifndef S_SPLINT_S
+		tv.tv_sec = worker->env.cfg->stat_interval;
+		tv.tv_usec = 0;
+#endif
+		comm_timer_set(worker->stat_timer, &tv);
+	}
+}
+
+void worker_stat_timer_cb(void* arg)
+{
+	struct worker* worker = (struct worker*)arg;
+	server_stats_log(&worker->stats, worker, worker->thread_num);
+	mesh_stats(worker->env.mesh, "mesh has");
+	worker_mem_report(worker, NULL);
+	if(!worker->daemon->cfg->stat_cumulative) {
+		worker_stats_clear(worker);
+	}
+	/* start next timer */
+	worker_restart_timer(worker);
+}
+
+void worker_probe_timer_cb(void* arg)
+{
+	struct worker* worker = (struct worker*)arg;
+	struct timeval tv;
+#ifndef S_SPLINT_S
+	tv.tv_sec = (time_t)autr_probe_timer(&worker->env);
+	tv.tv_usec = 0;
+#endif
+	if(tv.tv_sec != 0)
+		comm_timer_set(worker->env.probe_timer, &tv);
+}
+
+struct worker* 
+worker_create(struct daemon* daemon, int id, int* ports, int n)
+{
+	unsigned int seed;
+	struct worker* worker = (struct worker*)calloc(1, 
+		sizeof(struct worker));
+	if(!worker) 
+		return NULL;
+	worker->numports = n;
+	worker->ports = (int*)memdup(ports, sizeof(int)*n);
+	if(!worker->ports) {
+		free(worker);
+		return NULL;
+	}
+	worker->daemon = daemon;
+	worker->thread_num = id;
+	if(!(worker->cmd = tube_create())) {
+		free(worker->ports);
+		free(worker);
+		return NULL;
+	}
+	/* create random state here to avoid locking trouble in RAND_bytes */
+	seed = (unsigned int)time(NULL) ^ (unsigned int)getpid() ^
+		(((unsigned int)worker->thread_num)<<17);
+		/* shift thread_num so it does not match out pid bits */
+	if(!(worker->rndstate = ub_initstate(seed, daemon->rand))) {
+		seed = 0;
+		log_err("could not init random numbers.");
+		tube_delete(worker->cmd);
+		free(worker->ports);
+		free(worker);
+		return NULL;
+	}
+	seed = 0;
+	return worker;
+}
+
+int
+worker_init(struct worker* worker, struct config_file *cfg, 
+	struct listen_port* ports, int do_sigs)
+{
+	worker->need_to_exit = 0;
+	worker->base = comm_base_create(do_sigs);
+	if(!worker->base) {
+		log_err("could not create event handling base");
+		worker_delete(worker);
+		return 0;
+	}
+	if(do_sigs) {
+#ifdef SIGHUP
+		ub_thread_sig_unblock(SIGHUP);
+#endif
+		ub_thread_sig_unblock(SIGINT);
+#ifdef SIGQUIT
+		ub_thread_sig_unblock(SIGQUIT);
+#endif
+		ub_thread_sig_unblock(SIGTERM);
+#ifndef LIBEVENT_SIGNAL_PROBLEM
+		worker->comsig = comm_signal_create(worker->base, 
+			worker_sighandler, worker);
+		if(!worker->comsig 
+#ifdef SIGHUP
+			|| !comm_signal_bind(worker->comsig, SIGHUP)
+#endif
+#ifdef SIGQUIT
+			|| !comm_signal_bind(worker->comsig, SIGQUIT)
+#endif
+			|| !comm_signal_bind(worker->comsig, SIGTERM)
+			|| !comm_signal_bind(worker->comsig, SIGINT)) {
+			log_err("could not create signal handlers");
+			worker_delete(worker);
+			return 0;
+		}
+#endif /* LIBEVENT_SIGNAL_PROBLEM */
+		if(!daemon_remote_open_accept(worker->daemon->rc, 
+			worker->daemon->rc_ports, worker)) {
+			worker_delete(worker);
+			return 0;
+		}
+#ifdef UB_ON_WINDOWS
+		wsvc_setup_worker(worker);
+#endif /* UB_ON_WINDOWS */
+	} else { /* !do_sigs */
+		worker->comsig = NULL;
+	}
+	worker->front = listen_create(worker->base, ports,
+		cfg->msg_buffer_size, (int)cfg->incoming_num_tcp, 
+		worker->daemon->listen_sslctx, worker_handle_request, worker);
+	if(!worker->front) {
+		log_err("could not create listening sockets");
+		worker_delete(worker);
+		return 0;
+	}
+	worker->back = outside_network_create(worker->base,
+		cfg->msg_buffer_size, (size_t)cfg->outgoing_num_ports, 
+		cfg->out_ifs, cfg->num_out_ifs, cfg->do_ip4, cfg->do_ip6, 
+		cfg->do_tcp?cfg->outgoing_num_tcp:0, 
+		worker->daemon->env->infra_cache, worker->rndstate,
+		cfg->use_caps_bits_for_id, worker->ports, worker->numports,
+		cfg->unwanted_threshold, &worker_alloc_cleanup, worker,
+		cfg->do_udp, worker->daemon->connect_sslctx);
+	if(!worker->back) {
+		log_err("could not create outgoing sockets");
+		worker_delete(worker);
+		return 0;
+	}
+	/* start listening to commands */
+	if(!tube_setup_bg_listen(worker->cmd, worker->base,
+		&worker_handle_control_cmd, worker)) {
+		log_err("could not create control compt.");
+		worker_delete(worker);
+		return 0;
+	}
+	worker->stat_timer = comm_timer_create(worker->base, 
+		worker_stat_timer_cb, worker);
+	if(!worker->stat_timer) {
+		log_err("could not create statistics timer");
+	}
+
+	/* we use the msg_buffer_size as a good estimate for what the 
+	 * user wants for memory usage sizes */
+	worker->scratchpad = regional_create_custom(cfg->msg_buffer_size);
+	if(!worker->scratchpad) {
+		log_err("malloc failure");
+		worker_delete(worker);
+		return 0;
+	}
+
+	server_stats_init(&worker->stats, cfg);
+	alloc_init(&worker->alloc, &worker->daemon->superalloc, 
+		worker->thread_num);
+	alloc_set_id_cleanup(&worker->alloc, &worker_alloc_cleanup, worker);
+	worker->env = *worker->daemon->env;
+	comm_base_timept(worker->base, &worker->env.now, &worker->env.now_tv);
+	if(worker->thread_num == 0)
+		log_set_time(worker->env.now);
+	worker->env.worker = worker;
+	worker->env.send_query = &worker_send_query;
+	worker->env.alloc = &worker->alloc;
+	worker->env.rnd = worker->rndstate;
+	worker->env.scratch = worker->scratchpad;
+	worker->env.mesh = mesh_create(&worker->daemon->mods, &worker->env);
+	worker->env.detach_subs = &mesh_detach_subs;
+	worker->env.attach_sub = &mesh_attach_sub;
+	worker->env.kill_sub = &mesh_state_delete;
+	worker->env.detect_cycle = &mesh_detect_cycle;
+	worker->env.scratch_buffer = ldns_buffer_new(cfg->msg_buffer_size);
+	if(!(worker->env.fwds = forwards_create()) ||
+		!forwards_apply_cfg(worker->env.fwds, cfg)) {
+		log_err("Could not set forward zones");
+		worker_delete(worker);
+		return 0;
+	}
+	/* one probe timer per process -- if we have 5011 anchors */
+	if(autr_get_num_anchors(worker->env.anchors) > 0
+#ifndef THREADS_DISABLED
+		&& worker->thread_num == 0
+#endif
+		) {
+		struct timeval tv;
+		tv.tv_sec = 0;
+		tv.tv_usec = 0;
+		worker->env.probe_timer = comm_timer_create(worker->base,
+			worker_probe_timer_cb, worker);
+		if(!worker->env.probe_timer) {
+			log_err("could not create 5011-probe timer");
+		} else {
+			/* let timer fire, then it can reset itself */
+			comm_timer_set(worker->env.probe_timer, &tv);
+		}
+	}
+	if(!worker->env.mesh || !worker->env.scratch_buffer) {
+		worker_delete(worker);
+		return 0;
+	}
+	worker_mem_report(worker, NULL);
+	/* if statistics enabled start timer */
+	if(worker->env.cfg->stat_interval > 0) {
+		verbose(VERB_ALGO, "set statistics interval %d secs", 
+			worker->env.cfg->stat_interval);
+		worker_restart_timer(worker);
+	}
+	return 1;
+}
+
+void 
+worker_work(struct worker* worker)
+{
+	comm_base_dispatch(worker->base);
+}
+
+void 
+worker_delete(struct worker* worker)
+{
+	if(!worker) 
+		return;
+	if(worker->env.mesh && verbosity >= VERB_OPS) {
+		server_stats_log(&worker->stats, worker, worker->thread_num);
+		mesh_stats(worker->env.mesh, "mesh has");
+		worker_mem_report(worker, NULL);
+	}
+	outside_network_quit_prepare(worker->back);
+	mesh_delete(worker->env.mesh);
+	ldns_buffer_free(worker->env.scratch_buffer);
+	forwards_delete(worker->env.fwds);
+	listen_delete(worker->front);
+	outside_network_delete(worker->back);
+	comm_signal_delete(worker->comsig);
+	tube_delete(worker->cmd);
+	comm_timer_delete(worker->stat_timer);
+	comm_timer_delete(worker->env.probe_timer);
+	free(worker->ports);
+	if(worker->thread_num == 0) {
+		log_set_time(NULL);
+#ifdef UB_ON_WINDOWS
+		wsvc_desetup_worker(worker);
+#endif /* UB_ON_WINDOWS */
+	}
+	comm_base_delete(worker->base);
+	ub_randfree(worker->rndstate);
+	alloc_clear(&worker->alloc);
+	regional_destroy(worker->scratchpad);
+	free(worker);
+}
+
+/** compare outbound entry qstates */
+static int
+outbound_entry_compare(void* a, void* b)
+{
+	struct outbound_entry* e1 = (struct outbound_entry*)a;
+	struct outbound_entry* e2 = (struct outbound_entry*)b;
+	if(e1->qstate == e2->qstate)
+		return 1;
+	return 0;
+}
+
+struct outbound_entry*
+worker_send_query(uint8_t* qname, size_t qnamelen, uint16_t qtype,
+	uint16_t qclass, uint16_t flags, int dnssec, int want_dnssec,
+	struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
+	size_t zonelen, struct module_qstate* q)
+{
+	struct worker* worker = q->env->worker;
+	struct outbound_entry* e = (struct outbound_entry*)regional_alloc(
+		q->region, sizeof(*e));
+	if(!e) 
+		return NULL;
+	e->qstate = q;
+	e->qsent = outnet_serviced_query(worker->back, qname,
+		qnamelen, qtype, qclass, flags, dnssec, want_dnssec,
+		q->env->cfg->tcp_upstream, q->env->cfg->ssl_upstream, addr,
+		addrlen, zone, zonelen, worker_handle_service_reply, e,
+		worker->back->udp_buff, &outbound_entry_compare);
+	if(!e->qsent) {
+		return NULL;
+	}
+	return e;
+}
+
+void 
+worker_alloc_cleanup(void* arg)
+{
+	struct worker* worker = (struct worker*)arg;
+	slabhash_clear(&worker->env.rrset_cache->table);
+	slabhash_clear(worker->env.msg_cache);
+}
+
+void worker_stats_clear(struct worker* worker)
+{
+	server_stats_init(&worker->stats, worker->env.cfg);
+	mesh_stats_clear(worker->env.mesh);
+	worker->back->unwanted_replies = 0;
+}
+
+/* --- fake callbacks for fptr_wlist to work --- */
+struct outbound_entry* libworker_send_query(uint8_t* ATTR_UNUSED(qname), 
+	size_t ATTR_UNUSED(qnamelen), uint16_t ATTR_UNUSED(qtype), 
+	uint16_t ATTR_UNUSED(qclass), uint16_t ATTR_UNUSED(flags), 
+	int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec),
+	struct sockaddr_storage* ATTR_UNUSED(addr), 
+	socklen_t ATTR_UNUSED(addrlen), struct module_qstate* ATTR_UNUSED(q))
+{
+	log_assert(0);
+	return 0;
+}
+
+int libworker_handle_reply(struct comm_point* ATTR_UNUSED(c), 
+	void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
+        struct comm_reply* ATTR_UNUSED(reply_info))
+{
+	log_assert(0);
+	return 0;
+}
+
+int libworker_handle_service_reply(struct comm_point* ATTR_UNUSED(c), 
+	void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
+        struct comm_reply* ATTR_UNUSED(reply_info))
+{
+	log_assert(0);
+	return 0;
+}
+
+void libworker_handle_control_cmd(struct tube* ATTR_UNUSED(tube),
+        uint8_t* ATTR_UNUSED(buffer), size_t ATTR_UNUSED(len),
+        int ATTR_UNUSED(error), void* ATTR_UNUSED(arg))
+{
+	log_assert(0);
+}
+
+void libworker_fg_done_cb(void* ATTR_UNUSED(arg), int ATTR_UNUSED(rcode),
+        ldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s),
+	char* ATTR_UNUSED(why_bogus))
+{
+	log_assert(0);
+}
+
+void libworker_bg_done_cb(void* ATTR_UNUSED(arg), int ATTR_UNUSED(rcode),
+        ldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s),
+	char* ATTR_UNUSED(why_bogus))
+{
+	log_assert(0);
+}
+
+int context_query_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
+{
+	log_assert(0);
+	return 0;
+}
+
+int order_lock_cmp(const void* ATTR_UNUSED(e1), const void* ATTR_UNUSED(e2))
+{
+        log_assert(0);
+        return 0;
+}
+
+int codeline_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
+{
+        log_assert(0);
+        return 0;
+}
+
diff --git a/3rdParty/Unbound/src/src/daemon/worker.h b/3rdParty/Unbound/src/src/daemon/worker.h
new file mode 100644
index 0000000..96c0467
--- /dev/null
+++ b/3rdParty/Unbound/src/src/daemon/worker.h
@@ -0,0 +1,228 @@
+/*
+ * daemon/worker.h - worker that handles a pending list of requests.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file describes the worker structure that holds a list of 
+ * pending requests and handles them.
+ */
+
+#ifndef DAEMON_WORKER_H
+#define DAEMON_WORKER_H
+
+#include "util/netevent.h"
+#include "util/locks.h"
+#include "util/alloc.h"
+#include "util/data/msgreply.h"
+#include "util/data/msgparse.h"
+#include "daemon/stats.h"
+#include "util/module.h"
+struct listen_dnsport;
+struct outside_network;
+struct config_file;
+struct daemon;
+struct listen_port;
+struct ub_randstate;
+struct regional;
+struct tube;
+struct daemon_remote;
+
+/** worker commands */
+enum worker_commands {
+	/** make the worker quit */
+	worker_cmd_quit,
+	/** obtain statistics */
+	worker_cmd_stats,
+	/** obtain statistics without statsclear */
+	worker_cmd_stats_noreset,
+	/** execute remote control command */
+	worker_cmd_remote
+};
+
+/**
+ * Structure holding working information for unbound.
+ * Holds globally visible information.
+ */
+struct worker {
+	/** the thread number (in daemon array). First in struct for debug. */
+	int thread_num;
+	/** global shared daemon structure */
+	struct daemon* daemon;
+	/** thread id */
+	ub_thread_t thr_id;
+	/** pipe, for commands for this worker */
+	struct tube* cmd;
+	/** the event base this worker works with */
+	struct comm_base* base;
+	/** the frontside listening interface where request events come in */
+	struct listen_dnsport* front;
+	/** the backside outside network interface to the auth servers */
+	struct outside_network* back;
+	/** ports to be used by this worker. */
+	int* ports;
+	/** number of ports for this worker */
+	int numports;
+	/** the signal handler */
+	struct comm_signal* comsig;
+	/** commpoint to listen to commands. */
+	struct comm_point* cmd_com;
+	/** timer for statistics */
+	struct comm_timer* stat_timer;
+
+	/** random() table for this worker. */
+	struct ub_randstate* rndstate;
+	/** do we need to restart or quit (on signal) */
+	int need_to_exit;
+	/** allocation cache for this thread */
+	struct alloc_cache alloc;
+	/** per thread statistics */
+	struct server_stats stats;
+	/** thread scratch regional */
+	struct regional* scratchpad;
+
+	/** module environment passed to modules, changed for this thread */
+	struct module_env env;
+};
+
+/**
+ * Create the worker structure. Bare bones version, zeroed struct,
+ * with backpointers only. Use worker_init on it later.
+ * @param daemon: the daemon that this worker thread is part of.
+ * @param id: the thread number from 0.. numthreads-1.
+ * @param ports: the ports it is allowed to use, array.
+ * @param n: the number of ports.
+ * @return: the new worker or NULL on alloc failure.
+ */
+struct worker* worker_create(struct daemon* daemon, int id, int* ports, int n);
+
+/**
+ * Initialize worker.
+ * Allocates event base, listens to ports
+ * @param worker: worker to initialize, created with worker_create.
+ * @param cfg: configuration settings.
+ * @param ports: list of shared query ports.
+ * @param do_sigs: if true, worker installs signal handlers.
+ * @return: false on error.
+ */
+int worker_init(struct worker* worker, struct config_file *cfg, 
+	struct listen_port* ports, int do_sigs);
+
+/**
+ * Make worker work.
+ */
+void worker_work(struct worker* worker);
+
+/**
+ * Delete worker.
+ */
+void worker_delete(struct worker* worker);
+
+/**
+ * Send a command to a worker. Uses blocking writes.
+ * @param worker: worker to send command to.
+ * @param cmd: command to send.
+ */
+void worker_send_cmd(struct worker* worker, enum worker_commands cmd);
+
+/**
+ * Worker signal handler function. User argument is the worker itself.
+ * @param sig: signal number.
+ * @param arg: the worker (main worker) that handles signals.
+ */
+void worker_sighandler(int sig, void* arg);
+
+/**
+ * Worker service routine to send serviced queries to authoritative servers.
+ * @param qname: query name. (host order)
+ * @param qnamelen: length in bytes of qname, including trailing 0.
+ * @param qtype: query type. (host order)
+ * @param qclass: query class. (host order)
+ * @param flags: host order flags word, with opcode and CD bit.
+ * @param dnssec: if set, EDNS record will have DO bit set.
+ * @param want_dnssec: signatures needed.
+ * @param addr: where to.
+ * @param addrlen: length of addr.
+ * @param zone: wireformat dname of the zone.
+ * @param zonelen: length of zone name.
+ * @param q: wich query state to reactivate upon return.
+ * @return: false on failure (memory or socket related). no query was
+ *      sent.
+ */
+struct outbound_entry* worker_send_query(uint8_t* qname, size_t qnamelen, 
+	uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec, 
+	int want_dnssec, struct sockaddr_storage* addr, socklen_t addrlen,
+	uint8_t* zone, size_t zonelen, struct module_qstate* q);
+
+/** 
+ * process control messages from the main thread. Frees the control 
+ * command message.
+ * @param tube: tube control message came on.
+ * @param msg: message contents.  Is freed.
+ * @param len: length of message.
+ * @param error: if error (NETEVENT_*) happened.
+ * @param arg: user argument
+ */
+void worker_handle_control_cmd(struct tube* tube, uint8_t* msg, size_t len,
+	int error, void* arg);
+
+/** handles callbacks from listening event interface */
+int worker_handle_request(struct comm_point* c, void* arg, int error,
+	struct comm_reply* repinfo);
+
+/** process incoming replies from the network */
+int worker_handle_reply(struct comm_point* c, void* arg, int error, 
+	struct comm_reply* reply_info);
+
+/** process incoming serviced query replies from the network */
+int worker_handle_service_reply(struct comm_point* c, void* arg, int error, 
+	struct comm_reply* reply_info);
+
+/** cleanup the cache to remove all rrset IDs from it, arg is worker */
+void worker_alloc_cleanup(void* arg);
+
+/**
+ * Init worker stats - includes server_stats_init, outside network and mesh.
+ * @param worker: the worker to init
+ */
+void worker_stats_clear(struct worker* worker);
+
+/** statistics timer callback handler */
+void worker_stat_timer_cb(void* arg);
+
+/** probe timer callback handler */
+void worker_probe_timer_cb(void* arg);
+
+#endif /* DAEMON_WORKER_H */
diff --git a/3rdParty/Unbound/src/src/iterator/iter_delegpt.c b/3rdParty/Unbound/src/src/iterator/iter_delegpt.c
new file mode 100644
index 0000000..f49048d
--- /dev/null
+++ b/3rdParty/Unbound/src/src/iterator/iter_delegpt.c
@@ -0,0 +1,494 @@
+/*
+ * iterator/iter_delegpt.c - delegation point with NS and address information.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file implements the Delegation Point. It contains a list of name servers
+ * and their addresses if known.
+ */
+#include "config.h"
+#include "iterator/iter_delegpt.h"
+#include "services/cache/dns.h"
+#include "util/regional.h"
+#include "util/data/dname.h"
+#include "util/data/packed_rrset.h"
+#include "util/data/msgreply.h"
+#include "util/net_help.h"
+
+struct delegpt* 
+delegpt_create(struct regional* region)
+{
+	struct delegpt* dp=(struct delegpt*)regional_alloc(
+		region, sizeof(*dp));
+	if(!dp)
+		return NULL;
+	memset(dp, 0, sizeof(*dp));
+	return dp;
+}
+
+struct delegpt* delegpt_copy(struct delegpt* dp, struct regional* region)
+{
+	struct delegpt* copy = delegpt_create(region);
+	struct delegpt_ns* ns;
+	struct delegpt_addr* a;
+	if(!copy) 
+		return NULL;
+	if(!delegpt_set_name(copy, region, dp->name))
+		return NULL;
+	copy->bogus = dp->bogus;
+	copy->has_parent_side_NS = dp->has_parent_side_NS;
+	for(ns = dp->nslist; ns; ns = ns->next) {
+		if(!delegpt_add_ns(copy, region, ns->name, (int)ns->lame))
+			return NULL;
+		copy->nslist->resolved = ns->resolved;
+		copy->nslist->got4 = ns->got4;
+		copy->nslist->got6 = ns->got6;
+		copy->nslist->done_pside4 = ns->done_pside4;
+		copy->nslist->done_pside6 = ns->done_pside6;
+	}
+	for(a = dp->target_list; a; a = a->next_target) {
+		if(!delegpt_add_addr(copy, region, &a->addr, a->addrlen, 
+			a->bogus, a->lame))
+			return NULL;
+	}
+	return copy;
+}
+
+int 
+delegpt_set_name(struct delegpt* dp, struct regional* region, uint8_t* name)
+{
+	dp->namelabs = dname_count_size_labels(name, &dp->namelen);
+	dp->name = regional_alloc_init(region, name, dp->namelen);
+	return dp->name != 0;
+}
+
+int 
+delegpt_add_ns(struct delegpt* dp, struct regional* region, uint8_t* name,
+	int lame)
+{
+	struct delegpt_ns* ns;
+	size_t len;
+	(void)dname_count_size_labels(name, &len);
+	/* slow check for duplicates to avoid counting failures when
+	 * adding the same server as a dependency twice */
+	if(delegpt_find_ns(dp, name, len))
+		return 1;
+	ns = (struct delegpt_ns*)regional_alloc(region,
+		sizeof(struct delegpt_ns));
+	if(!ns)
+		return 0;
+	ns->next = dp->nslist;
+	ns->namelen = len;
+	dp->nslist = ns;
+	ns->name = regional_alloc_init(region, name, ns->namelen);
+	ns->resolved = 0;
+	ns->got4 = 0;
+	ns->got6 = 0;
+	ns->lame = (uint8_t)lame;
+	ns->done_pside4 = 0;
+	ns->done_pside6 = 0;
+	return 1;
+}
+
+struct delegpt_ns*
+delegpt_find_ns(struct delegpt* dp, uint8_t* name, size_t namelen)
+{
+	struct delegpt_ns* p = dp->nslist;
+	while(p) {
+		if(namelen == p->namelen && 
+			query_dname_compare(name, p->name) == 0) {
+			return p;
+		}
+		p = p->next;
+	}
+	return NULL;
+}
+
+struct delegpt_addr*
+delegpt_find_addr(struct delegpt* dp, struct sockaddr_storage* addr, 
+	socklen_t addrlen)
+{
+	struct delegpt_addr* p = dp->target_list;
+	while(p) {
+		if(sockaddr_cmp_addr(addr, addrlen, &p->addr, p->addrlen)==0) {
+			return p;
+		}
+		p = p->next_target;
+	}
+	return NULL;
+}
+
+int 
+delegpt_add_target(struct delegpt* dp, struct regional* region, 
+	uint8_t* name, size_t namelen, struct sockaddr_storage* addr, 
+	socklen_t addrlen, int bogus, int lame)
+{
+	struct delegpt_ns* ns = delegpt_find_ns(dp, name, namelen);
+	if(!ns) {
+		/* ignore it */
+		return 1;
+	}
+	if(!lame) {
+		if(addr_is_ip6(addr, addrlen))
+			ns->got6 = 1;
+		else	ns->got4 = 1;
+		if(ns->got4 && ns->got6)
+			ns->resolved = 1;
+	}
+	return delegpt_add_addr(dp, region, addr, addrlen, bogus, lame);
+}
+
+int 
+delegpt_add_addr(struct delegpt* dp, struct regional* region, 
+	struct sockaddr_storage* addr, socklen_t addrlen, int bogus, 
+	int lame)
+{
+	struct delegpt_addr* a;
+	/* check for duplicates */
+	if((a = delegpt_find_addr(dp, addr, addrlen))) {
+		if(bogus)
+			a->bogus = bogus;
+		if(!lame)
+			a->lame = 0;
+		return 1;
+	}
+
+	a = (struct delegpt_addr*)regional_alloc(region,
+		sizeof(struct delegpt_addr));
+	if(!a)
+		return 0;
+	a->next_target = dp->target_list;
+	dp->target_list = a;
+	a->next_result = 0;
+	a->next_usable = dp->usable_list;
+	dp->usable_list = a;
+	memcpy(&a->addr, addr, addrlen);
+	a->addrlen = addrlen;
+	a->attempts = 0;
+	a->bogus = bogus;
+	a->lame = lame;
+	return 1;
+}
+
+void
+delegpt_count_ns(struct delegpt* dp, size_t* numns, size_t* missing)
+{
+	struct delegpt_ns* ns;
+	*numns = 0;
+	*missing = 0;
+	for(ns = dp->nslist; ns; ns = ns->next) {
+		(*numns)++;
+		if(!ns->resolved)
+			(*missing)++;
+	}
+}
+
+void
+delegpt_count_addr(struct delegpt* dp, size_t* numaddr, size_t* numres, 
+	size_t* numavail)
+{
+	struct delegpt_addr* a;
+	*numaddr = 0;
+	*numres = 0;
+	*numavail = 0;
+	for(a = dp->target_list; a; a = a->next_target) {
+		(*numaddr)++;
+	}
+	for(a = dp->result_list; a; a = a->next_result) {
+		(*numres)++;
+	}
+	for(a = dp->usable_list; a; a = a->next_usable) {
+		(*numavail)++;
+	}
+}
+
+void delegpt_log(enum verbosity_value v, struct delegpt* dp)
+{
+	char buf[LDNS_MAX_DOMAINLEN+1];
+	struct delegpt_ns* ns;
+	struct delegpt_addr* a;
+	size_t missing=0, numns=0, numaddr=0, numres=0, numavail=0;
+	if(verbosity < v)
+		return;
+	dname_str(dp->name, buf);
+	if(dp->nslist == NULL && dp->target_list == NULL) {
+		log_info("DelegationPoint<%s>: empty", buf);
+		return;
+	}
+	delegpt_count_ns(dp, &numns, &missing);
+	delegpt_count_addr(dp, &numaddr, &numres, &numavail);
+	log_info("DelegationPoint<%s>: %u names (%u missing), "
+		"%u addrs (%u result, %u avail)%s", 
+		buf, (unsigned)numns, (unsigned)missing, 
+		(unsigned)numaddr, (unsigned)numres, (unsigned)numavail,
+		(dp->has_parent_side_NS?" parentNS":" cacheNS"));
+	if(verbosity >= VERB_ALGO) {
+		for(ns = dp->nslist; ns; ns = ns->next) {
+			dname_str(ns->name, buf);
+			log_info("  %s %s%s%s%s%s%s%s", buf, 
+			(ns->resolved?"*":""),
+			(ns->got4?" A":""), (ns->got6?" AAAA":""),
+			(dp->bogus?" BOGUS":""), (ns->lame?" PARENTSIDE":""),
+			(ns->done_pside4?" PSIDE_A":""),
+			(ns->done_pside6?" PSIDE_AAAA":""));
+		}
+		for(a = dp->target_list; a; a = a->next_target) {
+			const char* str = "  ";
+			if(a->bogus && a->lame) str = "  BOGUS ADDR_LAME ";
+			else if(a->bogus) str = "  BOGUS ";
+			else if(a->lame) str = "  ADDR_LAME ";
+			log_addr(VERB_ALGO, str, &a->addr, a->addrlen);
+		}
+	}
+}
+
+void 
+delegpt_add_unused_targets(struct delegpt* dp)
+{
+	struct delegpt_addr* usa = dp->usable_list;
+	dp->usable_list = NULL;
+	while(usa) {
+		usa->next_result = dp->result_list;
+		dp->result_list = usa;
+		usa = usa->next_usable;
+	}
+}
+
+size_t
+delegpt_count_targets(struct delegpt* dp)
+{
+	struct delegpt_addr* a;
+	size_t n = 0;
+	for(a = dp->target_list; a; a = a->next_target)
+		n++;
+	return n;
+}
+
+size_t 
+delegpt_count_missing_targets(struct delegpt* dp)
+{
+	struct delegpt_ns* ns;
+	size_t n = 0;
+	for(ns = dp->nslist; ns; ns = ns->next)
+		if(!ns->resolved)
+			n++;
+	return n;
+}
+
+/** find NS rrset in given list */
+static struct ub_packed_rrset_key*
+find_NS(struct reply_info* rep, size_t from, size_t to)
+{
+	size_t i;
+	for(i=from; i<to; i++) {
+		if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS)
+			return rep->rrsets[i];
+	}
+	return NULL;
+}
+
+struct delegpt* 
+delegpt_from_message(struct dns_msg* msg, struct regional* region)
+{
+	struct ub_packed_rrset_key* ns_rrset = NULL;
+	struct delegpt* dp;
+	size_t i;
+	/* look for NS records in the authority section... */
+	ns_rrset = find_NS(msg->rep, msg->rep->an_numrrsets, 
+		msg->rep->an_numrrsets+msg->rep->ns_numrrsets);
+
+	/* In some cases (even legitimate, perfectly legal cases), the 
+	 * NS set for the "referral" might be in the answer section. */
+	if(!ns_rrset)
+		ns_rrset = find_NS(msg->rep, 0, msg->rep->an_numrrsets);
+	
+	/* If there was no NS rrset in the authority section, then this 
+	 * wasn't a referral message. (It might not actually be a 
+	 * referral message anyway) */
+	if(!ns_rrset)
+		return NULL;
+	
+	/* If we found any, then Yay! we have a delegation point. */
+	dp = delegpt_create(region);
+	if(!dp)
+		return NULL;
+	dp->has_parent_side_NS = 1; /* created from message */
+	if(!delegpt_set_name(dp, region, ns_rrset->rk.dname))
+		return NULL;
+	if(!delegpt_rrset_add_ns(dp, region, ns_rrset, 0))
+		return NULL;
+
+	/* add glue, A and AAAA in answer and additional section */
+	for(i=0; i<msg->rep->rrset_count; i++) {
+		struct ub_packed_rrset_key* s = msg->rep->rrsets[i];
+		/* skip auth section. FIXME really needed?*/
+		if(msg->rep->an_numrrsets <= i && 
+			i < (msg->rep->an_numrrsets+msg->rep->ns_numrrsets))
+			continue;
+
+		if(ntohs(s->rk.type) == LDNS_RR_TYPE_A) {
+			if(!delegpt_add_rrset_A(dp, region, s, 0))
+				return NULL;
+		} else if(ntohs(s->rk.type) == LDNS_RR_TYPE_AAAA) {
+			if(!delegpt_add_rrset_AAAA(dp, region, s, 0))
+				return NULL;
+		}
+	}
+	return dp;
+}
+
+int 
+delegpt_rrset_add_ns(struct delegpt* dp, struct regional* region,
+        struct ub_packed_rrset_key* ns_rrset, int lame)
+{
+	struct packed_rrset_data* nsdata = (struct packed_rrset_data*)
+		ns_rrset->entry.data;
+	size_t i;
+	if(nsdata->security == sec_status_bogus)
+		dp->bogus = 1;
+	for(i=0; i<nsdata->count; i++) {
+		if(nsdata->rr_len[i] < 2+1) continue; /* len + root label */
+		if(dname_valid(nsdata->rr_data[i]+2, nsdata->rr_len[i]-2) !=
+			(size_t)ldns_read_uint16(nsdata->rr_data[i]))
+			continue; /* bad format */
+		/* add rdata of NS (= wirefmt dname), skip rdatalen bytes */
+		if(!delegpt_add_ns(dp, region, nsdata->rr_data[i]+2, lame))
+			return 0;
+	}
+	return 1;
+}
+
+int 
+delegpt_add_rrset_A(struct delegpt* dp, struct regional* region,
+	struct ub_packed_rrset_key* ak, int lame)
+{
+        struct packed_rrset_data* d=(struct packed_rrset_data*)ak->entry.data;
+        size_t i;
+        struct sockaddr_in sa;
+        socklen_t len = (socklen_t)sizeof(sa);
+        memset(&sa, 0, len);
+        sa.sin_family = AF_INET;
+        sa.sin_port = (in_port_t)htons(UNBOUND_DNS_PORT);
+        for(i=0; i<d->count; i++) {
+                if(d->rr_len[i] != 2 + INET_SIZE)
+                        continue;
+                memmove(&sa.sin_addr, d->rr_data[i]+2, INET_SIZE);
+                if(!delegpt_add_target(dp, region, ak->rk.dname,
+                        ak->rk.dname_len, (struct sockaddr_storage*)&sa,
+                        len, (d->security==sec_status_bogus), lame))
+                        return 0;
+        }
+        return 1;
+}
+
+int 
+delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* region,
+	struct ub_packed_rrset_key* ak, int lame)
+{
+        struct packed_rrset_data* d=(struct packed_rrset_data*)ak->entry.data;
+        size_t i;
+        struct sockaddr_in6 sa;
+        socklen_t len = (socklen_t)sizeof(sa);
+        memset(&sa, 0, len);
+        sa.sin6_family = AF_INET6;
+        sa.sin6_port = (in_port_t)htons(UNBOUND_DNS_PORT);
+        for(i=0; i<d->count; i++) {
+                if(d->rr_len[i] != 2 + INET6_SIZE) /* rdatalen + len of IP6 */
+                        continue;
+                memmove(&sa.sin6_addr, d->rr_data[i]+2, INET6_SIZE);
+                if(!delegpt_add_target(dp, region, ak->rk.dname,
+                        ak->rk.dname_len, (struct sockaddr_storage*)&sa,
+                        len, (d->security==sec_status_bogus), lame))
+                        return 0;
+        }
+        return 1;
+}
+
+int 
+delegpt_add_rrset(struct delegpt* dp, struct regional* region,
+        struct ub_packed_rrset_key* rrset, int lame)
+{
+	if(!rrset)
+		return 1;
+	if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_NS)
+		return delegpt_rrset_add_ns(dp, region, rrset, lame);
+	else if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_A)
+		return delegpt_add_rrset_A(dp, region, rrset, lame);
+	else if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_AAAA)
+		return delegpt_add_rrset_AAAA(dp, region, rrset, lame);
+	log_warn("Unknown rrset type added to delegpt");
+	return 1;
+}
+
+void delegpt_add_neg_msg(struct delegpt* dp, struct msgreply_entry* msg)
+{
+	struct reply_info* rep = (struct reply_info*)msg->entry.data;
+	if(!rep) return;
+
+	/* if error or no answers */
+	if(FLAGS_GET_RCODE(rep->flags) != 0 || rep->an_numrrsets == 0) {
+		struct delegpt_ns* ns = delegpt_find_ns(dp, msg->key.qname, 
+			msg->key.qname_len);
+		if(ns) {
+			if(msg->key.qtype == LDNS_RR_TYPE_A)
+				ns->got4 = 1;
+			else if(msg->key.qtype == LDNS_RR_TYPE_AAAA)
+				ns->got6 = 1;
+			if(ns->got4 && ns->got6)
+				ns->resolved = 1;
+		}
+	}
+}
+
+void delegpt_no_ipv6(struct delegpt* dp)
+{
+	struct delegpt_ns* ns;
+	for(ns = dp->nslist; ns; ns = ns->next) {
+		/* no ipv6, so only ipv4 is enough to resolve a nameserver */
+		if(ns->got4)
+			ns->resolved = 1;
+	}
+}
+
+void delegpt_no_ipv4(struct delegpt* dp)
+{
+	struct delegpt_ns* ns;
+	for(ns = dp->nslist; ns; ns = ns->next) {
+		/* no ipv4, so only ipv6 is enough to resolve a nameserver */
+		if(ns->got6)
+			ns->resolved = 1;
+	}
+}
diff --git a/3rdParty/Unbound/src/src/iterator/iter_delegpt.h b/3rdParty/Unbound/src/src/iterator/iter_delegpt.h
new file mode 100644
index 0000000..c4ca62d
--- /dev/null
+++ b/3rdParty/Unbound/src/src/iterator/iter_delegpt.h
@@ -0,0 +1,349 @@
+/*
+ * iterator/iter_delegpt.h - delegation point with NS and address information.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file implements the Delegation Point. It contains a list of name servers
+ * and their addresses if known.
+ */
+
+#ifndef ITERATOR_ITER_DELEGPT_H
+#define ITERATOR_ITER_DELEGPT_H
+#include "util/log.h"
+struct regional;
+struct delegpt_ns;
+struct delegpt_addr;
+struct dns_msg;
+struct ub_packed_rrset_key;
+struct msgreply_entry;
+
+/**
+ * Delegation Point.
+ * For a domain name, the NS rrset, and the A and AAAA records for those.
+ */
+struct delegpt {
+	/** the domain name of the delegation point. */
+	uint8_t* name;
+	/** length of the delegation point name */
+	size_t namelen;
+	/** number of labels in delegation point */
+	int namelabs;
+
+	/** the nameservers, names from the NS RRset rdata. */
+	struct delegpt_ns* nslist;
+	/** the target addresses for delegation */
+	struct delegpt_addr* target_list;
+	/** the list of usable targets; subset of target_list 
+	 * the items in this list are not part of the result list.  */
+	struct delegpt_addr* usable_list;
+	/** the list of returned targets; subset of target_list */
+	struct delegpt_addr* result_list;
+
+	/** if true, the NS RRset was bogus. All info is bad. */
+	int bogus;
+	/** if true, the parent-side NS record has been applied:
+	 * its names have been added and their addresses can follow later.
+	 * Also true if the delegationpoint was created from a delegation
+	 * message and thus contains the parent-side-info already. */
+	uint8_t has_parent_side_NS;
+};
+
+/**
+ * Nameservers for a delegation point.
+ */
+struct delegpt_ns {
+	/** next in list */
+	struct delegpt_ns* next;
+	/** name of nameserver */
+	uint8_t* name;
+	/** length of name */
+	size_t namelen;
+	/** 
+	 * If the name has been resolved. false if not queried for yet.
+	 * true if the A, AAAA queries have been generated.
+	 * marked true if those queries fail.
+	 * and marked true if got4 and got6 are both true.
+	 */
+	int resolved;
+	/** if the ipv4 address is in the delegpt */
+	uint8_t got4;
+	/** if the ipv6 address is in the delegpt */
+	uint8_t got6;
+	/**
+	 * If the name is parent-side only and thus dispreferred.
+	 * Its addresses become dispreferred as well
+	 */
+	uint8_t lame;
+	/** if the parent-side ipv4 address has been looked up (last resort).
+	 * Also enabled if a parent-side cache entry exists, or a parent-side
+	 * negative-cache entry exists. */
+	uint8_t done_pside4;
+	/** if the parent-side ipv6 address has been looked up (last resort).
+	 * Also enabled if a parent-side cache entry exists, or a parent-side
+	 * negative-cache entry exists. */
+	uint8_t done_pside6;
+};
+
+/**
+ * Address of target nameserver in delegation point.
+ */
+struct delegpt_addr {
+	/** next delegation point in results */
+	struct delegpt_addr* next_result;
+	/** next delegation point in usable list */
+	struct delegpt_addr* next_usable;
+	/** next delegation point in all targets list */
+	struct delegpt_addr* next_target;
+
+	/** delegation point address */
+	struct sockaddr_storage addr;
+	/** length of addr */
+	socklen_t addrlen;
+	/** number of attempts for this addr */
+	int attempts;
+	/** rtt stored here in the selection algorithm */
+	int sel_rtt;
+	/** if true, the A or AAAA RR was bogus, so this address is bad.
+	 * Also check the dp->bogus to see if everything is bogus. */
+	int bogus;
+	/** if true, this address is dispreferred: it is a lame IP address */
+	int lame;
+};
+
+/**
+ * Create new delegation point.
+ * @param regional: where to allocate it.
+ * @return new delegation point or NULL on error.
+ */
+struct delegpt* delegpt_create(struct regional* regional);
+
+/**
+ * Create a copy of a delegation point.
+ * @param dp: delegation point to copy.
+ * @param regional: where to allocate it.
+ * @return new delegation point or NULL on error.
+ */
+struct delegpt* delegpt_copy(struct delegpt* dp, struct regional* regional);
+
+/**
+ * Set name of delegation point.
+ * @param dp: delegation point.
+ * @param regional: where to allocate the name copy.
+ * @param name: name to use.
+ * @return false on error.
+ */
+int delegpt_set_name(struct delegpt* dp, struct regional* regional, 
+	uint8_t* name);
+
+/**
+ * Add a name to the delegation point.
+ * @param dp: delegation point.
+ * @param regional: where to allocate the info.
+ * @param name: domain name in wire format.
+ * @param lame: name is lame, disprefer it.
+ * @return false on error.
+ */
+int delegpt_add_ns(struct delegpt* dp, struct regional* regional, 
+	uint8_t* name, int lame);
+
+/**
+ * Add NS rrset; calls add_ns repeatedly.
+ * @param dp: delegation point.
+ * @param regional: where to allocate the info.
+ * @param ns_rrset: NS rrset.
+ * @param lame: rrset is lame, disprefer it.
+ * @return 0 on alloc error.
+ */
+int delegpt_rrset_add_ns(struct delegpt* dp, struct regional* regional,
+	struct ub_packed_rrset_key* ns_rrset, int lame);
+
+/**
+ * Add target address to the delegation point.
+ * @param dp: delegation point.
+ * @param regional: where to allocate the info.
+ * @param name: name for which target was found (must be in nslist).
+ *	This name is marked resolved.
+ * @param namelen: length of name.
+ * @param addr: the address.
+ * @param addrlen: the length of addr.
+ * @param bogus: security status for the address, pass true if bogus.
+ * @param lame: address is lame.
+ * @return false on error.
+ */
+int delegpt_add_target(struct delegpt* dp, struct regional* regional, 
+	uint8_t* name, size_t namelen, struct sockaddr_storage* addr, 
+	socklen_t addrlen, int bogus, int lame);
+
+/**
+ * Add A RRset to delegpt.
+ * @param dp: delegation point.
+ * @param regional: where to allocate the info.
+ * @param rrset: RRset A to add.
+ * @param lame: rrset is lame, disprefer it.
+ * @return 0 on alloc error.
+ */
+int delegpt_add_rrset_A(struct delegpt* dp, struct regional* regional, 
+	struct ub_packed_rrset_key* rrset, int lame);
+
+/**
+ * Add AAAA RRset to delegpt.
+ * @param dp: delegation point.
+ * @param regional: where to allocate the info.
+ * @param rrset: RRset AAAA to add.
+ * @param lame: rrset is lame, disprefer it.
+ * @return 0 on alloc error.
+ */
+int delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* regional, 
+	struct ub_packed_rrset_key* rrset, int lame);
+
+/**
+ * Add any RRset to delegpt.
+ * Does not check for duplicates added.
+ * @param dp: delegation point.
+ * @param regional: where to allocate the info.
+ * @param rrset: RRset to add, NS, A, AAAA.
+ * @param lame: rrset is lame, disprefer it.
+ * @return 0 on alloc error.
+ */
+int delegpt_add_rrset(struct delegpt* dp, struct regional* regional, 
+	struct ub_packed_rrset_key* rrset, int lame);
+
+/**
+ * Add address to the delegation point. No servername is associated or checked.
+ * @param dp: delegation point.
+ * @param regional: where to allocate the info.
+ * @param addr: the address.
+ * @param addrlen: the length of addr.
+ * @param bogus: if address is bogus.
+ * @param lame: if address is lame.
+ * @return false on error.
+ */
+int delegpt_add_addr(struct delegpt* dp, struct regional* regional, 
+	struct sockaddr_storage* addr, socklen_t addrlen, int bogus, int lame);
+
+/** 
+ * Find NS record in name list of delegation point.
+ * @param dp: delegation point.
+ * @param name: name of nameserver to look for, uncompressed wireformat.
+ * @param namelen: length of name.
+ * @return the ns structure or NULL if not found.
+ */
+struct delegpt_ns* delegpt_find_ns(struct delegpt* dp, uint8_t* name, 
+	size_t namelen);
+
+/** 
+ * Find address record in total list of delegation point.
+ * @param dp: delegation point.
+ * @param addr: address
+ * @param addrlen: length of addr
+ * @return the addr structure or NULL if not found.
+ */
+struct delegpt_addr* delegpt_find_addr(struct delegpt* dp, 
+	struct sockaddr_storage* addr, socklen_t addrlen);
+
+/**
+ * Print the delegation point to the log. For debugging.
+ * @param v: verbosity value that is needed to emit to log.
+ * @param dp: delegation point.
+ */
+void delegpt_log(enum verbosity_value v, struct delegpt* dp);
+
+/** count NS and number missing for logging */
+void delegpt_count_ns(struct delegpt* dp, size_t* numns, size_t* missing);
+
+/** count addresses, and number in result and available lists, for logging */
+void delegpt_count_addr(struct delegpt* dp, size_t* numaddr, size_t* numres, 
+	size_t* numavail);
+
+/**
+ * Add all usable targets to the result list.
+ * @param dp: delegation point.
+ */
+void delegpt_add_unused_targets(struct delegpt* dp);
+
+/**
+ * Count number of missing targets. These are ns names with no resolved flag.
+ * @param dp: delegation point.
+ * @return number of missing targets (or 0).
+ */
+size_t delegpt_count_missing_targets(struct delegpt* dp);
+
+/** count total number of targets in dp */
+size_t delegpt_count_targets(struct delegpt* dp);
+
+/**
+ * Create new delegation point from a dns message
+ *
+ * Note that this method does not actually test to see if the message is an
+ * actual referral. It really is just checking to see if it can construct a
+ * delegation point, so the message could be of some other type (some ANSWER
+ * messages, some CNAME messages, generally.) Note that the resulting
+ * DelegationPoint will contain targets for all "relevant" glue (i.e.,
+ * address records whose ownernames match the target of one of the NS
+ * records), so if policy dictates that some glue should be discarded beyond
+ * that, discard it before calling this method. Note that this method will
+ * find "glue" in either the ADDITIONAL section or the ANSWER section.
+ *
+ * @param msg: the dns message, referral.
+ * @param regional: where to allocate delegation point.
+ * @return new delegation point or NULL on alloc error, or if the
+ *         message was not appropriate.
+ */
+struct delegpt* delegpt_from_message(struct dns_msg* msg, 
+	struct regional* regional);
+
+/**
+ * Add negative message to delegation point.
+ * @param dp: delegation point.
+ * @param msg: the message added, marks off A or AAAA from an NS entry.
+ */
+void delegpt_add_neg_msg(struct delegpt* dp, struct msgreply_entry* msg);
+
+/**
+ * Register the fact that there is no ipv6 and thus AAAAs are not going 
+ * to be queried for or be useful.
+ * @param dp: the delegation point. Updated to reflect no ipv6.
+ */
+void delegpt_no_ipv6(struct delegpt* dp);
+
+/**
+ * Register the fact that there is no ipv4 and thus As are not going 
+ * to be queried for or be useful.
+ * @param dp: the delegation point. Updated to reflect no ipv4.
+ */
+void delegpt_no_ipv4(struct delegpt* dp);
+
+#endif /* ITERATOR_ITER_DELEGPT_H */
diff --git a/3rdParty/Unbound/src/src/iterator/iter_donotq.c b/3rdParty/Unbound/src/src/iterator/iter_donotq.c
new file mode 100644
index 0000000..bd60633
--- /dev/null
+++ b/3rdParty/Unbound/src/src/iterator/iter_donotq.c
@@ -0,0 +1,153 @@
+/*
+ * iterator/iter_donotq.c - iterative resolver donotqueryaddresses storage.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to assist the iterator module.
+ * The donotqueryaddresses are stored and looked up. These addresses
+ * (like 127.0.0.1) must not be used to send queries to, and can be
+ * discarded immediately from the server selection.
+ */
+#include "config.h"
+#include "iterator/iter_donotq.h"
+#include "util/regional.h"
+#include "util/log.h"
+#include "util/config_file.h"
+#include "util/net_help.h"
+
+struct iter_donotq* 
+donotq_create(void)
+{
+	struct iter_donotq* dq = (struct iter_donotq*)calloc(1,
+		sizeof(struct iter_donotq));
+	if(!dq)
+		return NULL;
+	dq->region = regional_create();
+	if(!dq->region) {
+		donotq_delete(dq);
+		return NULL;
+	}
+	return dq;
+}
+
+void 
+donotq_delete(struct iter_donotq* dq)
+{
+	if(!dq) 
+		return;
+	regional_destroy(dq->region);
+	free(dq);
+}
+
+/** insert new address into donotq structure */
+static int
+donotq_insert(struct iter_donotq* dq, struct sockaddr_storage* addr, 
+	socklen_t addrlen, int net)
+{
+	struct addr_tree_node* node = (struct addr_tree_node*)regional_alloc(
+		dq->region, sizeof(*node));
+	if(!node)
+		return 0;
+	if(!addr_tree_insert(&dq->tree, node, addr, addrlen, net)) {
+		verbose(VERB_QUERY, "duplicate donotquery address ignored.");
+	}
+	return 1;
+}
+
+/** apply donotq string */
+static int
+donotq_str_cfg(struct iter_donotq* dq, const char* str)
+{
+	struct sockaddr_storage addr;
+	int net;
+	socklen_t addrlen;
+	verbose(VERB_ALGO, "donotq: %s", str);
+	if(!netblockstrtoaddr(str, UNBOUND_DNS_PORT, &addr, &addrlen, &net)) {
+		log_err("cannot parse donotquery netblock: %s", str);
+		return 0;
+	}
+	if(!donotq_insert(dq, &addr, addrlen, net)) {
+		log_err("out of memory");
+		return 0;
+	}
+	return 1;
+}
+
+/** read donotq config */
+static int 
+read_donotq(struct iter_donotq* dq, struct config_file* cfg)
+{
+	struct config_strlist* p;
+	for(p = cfg->donotqueryaddrs; p; p = p->next) {
+		log_assert(p->str);
+		if(!donotq_str_cfg(dq, p->str))
+			return 0;
+	}
+	return 1;
+}
+
+int 
+donotq_apply_cfg(struct iter_donotq* dq, struct config_file* cfg)
+{
+	regional_free_all(dq->region);
+	addr_tree_init(&dq->tree);
+	if(!read_donotq(dq, cfg))
+		return 0;
+	if(cfg->donotquery_localhost) {
+		if(!donotq_str_cfg(dq, "127.0.0.0/8"))
+			return 0;
+		if(cfg->do_ip6) {
+			if(!donotq_str_cfg(dq, "::1"))
+				return 0;
+		}
+	}
+	addr_tree_init_parents(&dq->tree);
+	return 1;
+}
+
+int 
+donotq_lookup(struct iter_donotq* donotq, struct sockaddr_storage* addr,
+        socklen_t addrlen)
+{
+	return addr_tree_lookup(&donotq->tree, addr, addrlen) != NULL;
+}
+
+size_t 
+donotq_get_mem(struct iter_donotq* donotq)
+{
+	if(!donotq) return 0;
+	return sizeof(*donotq) + regional_get_mem(donotq->region);
+}
diff --git a/3rdParty/Unbound/src/src/iterator/iter_donotq.h b/3rdParty/Unbound/src/src/iterator/iter_donotq.h
new file mode 100644
index 0000000..4c4fcb2
--- /dev/null
+++ b/3rdParty/Unbound/src/src/iterator/iter_donotq.h
@@ -0,0 +1,101 @@
+/*
+ * iterator/iter_donotq.h - iterative resolver donotqueryaddresses storage.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to assist the iterator module.
+ * Keep track of the donotquery addresses and lookup fast.
+ */
+
+#ifndef ITERATOR_ITER_DONOTQ_H
+#define ITERATOR_ITER_DONOTQ_H
+#include "util/storage/dnstree.h"
+struct iter_env;
+struct config_file;
+struct regional;
+
+/**
+ * Iterator donotqueryaddresses structure
+ */
+struct iter_donotq {
+	/** regional for allocation */
+	struct regional* region;
+	/** 
+	 * Tree of the address spans that are blocked.
+	 * contents of type addr_tree_node. Each node is an address span 
+	 * that must not be used to send queries to.
+	 */
+	rbtree_t tree;
+};
+
+/**
+ * Create donotqueryaddresses structure 
+ * @return new structure or NULL on error.
+ */
+struct iter_donotq* donotq_create(void);
+
+/**
+ * Delete donotqueryaddresses structure.
+ * @param donotq: to delete.
+ */
+void donotq_delete(struct iter_donotq* donotq);
+
+/**
+ * Process donotqueryaddresses config.
+ * @param donotq: where to store.
+ * @param cfg: config options.
+ * @return 0 on error.
+ */
+int donotq_apply_cfg(struct iter_donotq* donotq, struct config_file* cfg);
+
+/**
+ * See if an address is blocked.
+ * @param donotq: structure for address storage.
+ * @param addr: address to check
+ * @param addrlen: length of addr.
+ * @return: true if the address must not be queried. false if unlisted.
+ */
+int donotq_lookup(struct iter_donotq* donotq, struct sockaddr_storage* addr,
+	socklen_t addrlen);
+
+/**
+ * Get memory used by donotqueryaddresses structure.
+ * @param donotq: structure for address storage.
+ * @return bytes in use.
+ */
+size_t donotq_get_mem(struct iter_donotq* donotq);
+
+#endif /* ITERATOR_ITER_DONOTQ_H */
diff --git a/3rdParty/Unbound/src/src/iterator/iter_fwd.c b/3rdParty/Unbound/src/src/iterator/iter_fwd.c
new file mode 100644
index 0000000..2df1f9c
--- /dev/null
+++ b/3rdParty/Unbound/src/src/iterator/iter_fwd.c
@@ -0,0 +1,442 @@
+/*
+ * iterator/iter_fwd.c - iterative resolver module forward zones.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to assist the iterator module.
+ * Keep track of forward zones and config settings.
+ */
+#include "config.h"
+#include <ldns/rdata.h>
+#include <ldns/dname.h>
+#include <ldns/rr.h>
+#include "iterator/iter_fwd.h"
+#include "iterator/iter_delegpt.h"
+#include "util/regional.h"
+#include "util/log.h"
+#include "util/config_file.h"
+#include "util/net_help.h"
+#include "util/data/dname.h"
+
+int
+fwd_cmp(const void* k1, const void* k2)
+{
+	int m;
+	struct iter_forward_zone* n1 = (struct iter_forward_zone*)k1;
+	struct iter_forward_zone* n2 = (struct iter_forward_zone*)k2;
+	if(n1->dclass != n2->dclass) {
+		if(n1->dclass < n2->dclass)
+			return -1;
+		return 1;
+	}
+	return dname_lab_cmp(n1->name, n1->namelabs, n2->name, n2->namelabs, 
+		&m);
+}
+
+struct iter_forwards* 
+forwards_create(void)
+{
+	struct iter_forwards* fwd = (struct iter_forwards*)calloc(1,
+		sizeof(struct iter_forwards));
+	if(!fwd)
+		return NULL;
+	fwd->region = regional_create();
+	if(!fwd->region) {
+		forwards_delete(fwd);
+		return NULL;
+	}
+	return fwd;
+}
+
+void 
+forwards_delete(struct iter_forwards* fwd)
+{
+	if(!fwd) 
+		return;
+	regional_destroy(fwd->region);
+	free(fwd->tree);
+	free(fwd);
+}
+
+/** insert info into forward structure */
+static int
+forwards_insert_data(struct iter_forwards* fwd, uint16_t c, uint8_t* nm, 
+	size_t nmlen, int nmlabs, struct delegpt* dp)
+{
+	struct iter_forward_zone* node = regional_alloc(fwd->region,
+		sizeof(struct iter_forward_zone));
+	if(!node)
+		return 0;
+	node->node.key = node;
+	node->dclass = c;
+	node->name = regional_alloc_init(fwd->region, nm, nmlen);
+	if(!node->name)
+		return 0;
+	node->namelen = nmlen;
+	node->namelabs = nmlabs;
+	node->dp = dp;
+	if(!rbtree_insert(fwd->tree, &node->node)) {
+		log_err("duplicate forward zone ignored.");
+	}
+	return 1;
+}
+
+/** insert new info into forward structure given dp */
+static int
+forwards_insert(struct iter_forwards* fwd, uint16_t c, struct delegpt* dp)
+{
+	return forwards_insert_data(fwd, c, dp->name, dp->namelen,
+		dp->namelabs, dp);
+}
+
+/** initialise parent pointers in the tree */
+static void
+fwd_init_parents(struct iter_forwards* fwd)
+{
+	struct iter_forward_zone* node, *prev = NULL, *p;
+	int m;
+	RBTREE_FOR(node, struct iter_forward_zone*, fwd->tree) {
+		node->parent = NULL;
+		if(!prev || prev->dclass != node->dclass) {
+			prev = node;
+			continue;
+		}
+		(void)dname_lab_cmp(prev->name, prev->namelabs, node->name,
+			node->namelabs, &m); /* we know prev is smaller */
+		/* sort order like: . com. bla.com. zwb.com. net. */
+		/* find the previous, or parent-parent-parent */
+		for(p = prev; p; p = p->parent)
+			/* looking for name with few labels, a parent */
+			if(p->namelabs <= m) {
+				/* ==: since prev matched m, this is closest*/
+				/* <: prev matches more, but is not a parent,
+				 * this one is a (grand)parent */
+				node->parent = p;
+				break;
+			}
+		prev = node;
+	}
+}
+
+/** set zone name */
+static int 
+read_fwds_name(struct iter_forwards* fwd, struct config_stub* s, 
+	struct delegpt* dp)
+{
+	ldns_rdf* rdf;
+	if(!s->name) {
+		log_err("forward zone without a name (use name \".\" to forward everything)");
+		return 0;
+	}
+	rdf = ldns_dname_new_frm_str(s->name);
+	if(!rdf) {
+		log_err("cannot parse forward zone name %s", s->name);
+		return 0;
+	}
+	if(!delegpt_set_name(dp, fwd->region, ldns_rdf_data(rdf))) {
+		ldns_rdf_deep_free(rdf);
+		log_err("out of memory");
+		return 0;
+	}
+	ldns_rdf_deep_free(rdf);
+	return 1;
+}
+
+/** set fwd host names */
+static int 
+read_fwds_host(struct iter_forwards* fwd, struct config_stub* s, 
+	struct delegpt* dp)
+{
+	struct config_strlist* p;
+	ldns_rdf* rdf;
+	for(p = s->hosts; p; p = p->next) {
+		log_assert(p->str);
+		rdf = ldns_dname_new_frm_str(p->str);
+		if(!rdf) {
+			log_err("cannot parse forward %s server name: '%s'", 
+				s->name, p->str);
+			return 0;
+		}
+		if(!delegpt_add_ns(dp, fwd->region, ldns_rdf_data(rdf), 0)) {
+			ldns_rdf_deep_free(rdf);
+			log_err("out of memory");
+			return 0;
+		}
+		ldns_rdf_deep_free(rdf);
+	}
+	return 1;
+}
+
+/** set fwd server addresses */
+static int 
+read_fwds_addr(struct iter_forwards* fwd, struct config_stub* s, 
+	struct delegpt* dp)
+{
+	struct config_strlist* p;
+	struct sockaddr_storage addr;
+	socklen_t addrlen;
+	for(p = s->addrs; p; p = p->next) {
+		log_assert(p->str);
+		if(!extstrtoaddr(p->str, &addr, &addrlen)) {
+			log_err("cannot parse forward %s ip address: '%s'", 
+				s->name, p->str);
+			return 0;
+		}
+		if(!delegpt_add_addr(dp, fwd->region, &addr, addrlen, 0, 0)) {
+			log_err("out of memory");
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/** read forwards config */
+static int 
+read_forwards(struct iter_forwards* fwd, struct config_file* cfg)
+{
+	struct config_stub* s;
+	for(s = cfg->forwards; s; s = s->next) {
+		struct delegpt* dp = delegpt_create(fwd->region);
+		if(!dp) {
+			log_err("out of memory");
+			return 0;
+		}
+		/* set flag that parent side NS information is included.
+		 * Asking a (higher up) server on the internet is not useful */
+		dp->has_parent_side_NS = 1;
+		if(!read_fwds_name(fwd, s, dp) ||
+			!read_fwds_host(fwd, s, dp) ||
+			!read_fwds_addr(fwd, s, dp))
+			return 0;
+		if(!forwards_insert(fwd, LDNS_RR_CLASS_IN, dp))
+			return 0;
+		verbose(VERB_QUERY, "Forward zone server list:");
+		delegpt_log(VERB_QUERY, dp);
+	}
+	return 1;
+}
+
+/** see if zone needs to have a hole inserted */
+static int
+need_hole_insert(rbtree_t* tree, struct iter_forward_zone* zone)
+{
+	struct iter_forward_zone k;
+	if(rbtree_search(tree, zone))
+		return 0; /* exact match exists */
+	k = *zone;
+	k.node.key = &k;
+	/* search up the tree */
+	do {
+		dname_remove_label(&k.name, &k.namelen);
+		k.namelabs --;
+		if(rbtree_search(tree, &k))
+			return 1; /* found an upper forward zone, need hole */
+	} while(k.namelabs > 1);
+	return 0; /* no forwards above, no holes needed */
+}
+
+/** make NULL entries for stubs */
+static int
+make_stub_holes(struct iter_forwards* fwd, struct config_file* cfg)
+{
+	struct config_stub* s;
+	struct iter_forward_zone key;
+	key.node.key = &key;
+	key.dclass = LDNS_RR_CLASS_IN;
+	for(s = cfg->stubs; s; s = s->next) {
+		ldns_rdf* rdf = ldns_dname_new_frm_str(s->name);
+		if(!rdf) {
+			log_err("cannot parse stub name '%s'", s->name);
+			return 0;
+		}
+		key.name = ldns_rdf_data(rdf);
+		key.namelabs = dname_count_size_labels(key.name, &key.namelen);
+		if(!need_hole_insert(fwd->tree, &key)) {
+			ldns_rdf_deep_free(rdf);
+			continue;
+		}
+		if(!forwards_insert_data(fwd, key.dclass, key.name, 
+			key.namelen, key.namelabs, NULL)) {
+			ldns_rdf_deep_free(rdf);
+			log_err("out of memory");
+			return 0;
+		}
+		ldns_rdf_deep_free(rdf);
+	}
+	return 1;
+}
+
+int 
+forwards_apply_cfg(struct iter_forwards* fwd, struct config_file* cfg)
+{
+	free(fwd->tree);
+	regional_free_all(fwd->region);
+	fwd->tree = rbtree_create(fwd_cmp);
+	if(!fwd->tree)
+		return 0;
+
+	/* read forward zones */
+	if(!read_forwards(fwd, cfg))
+		return 0;
+	if(!make_stub_holes(fwd, cfg))
+		return 0;
+	fwd_init_parents(fwd);
+	return 1;
+}
+
+struct delegpt* 
+forwards_lookup(struct iter_forwards* fwd, uint8_t* qname, uint16_t qclass)
+{
+	/* lookup the forward zone in the tree */
+	rbnode_t* res = NULL;
+	struct iter_forward_zone *result;
+	struct iter_forward_zone key;
+	key.node.key = &key;
+	key.dclass = qclass;
+	key.name = qname;
+	key.namelabs = dname_count_size_labels(qname, &key.namelen);
+	if(rbtree_find_less_equal(fwd->tree, &key, &res)) {
+		/* exact */
+		result = (struct iter_forward_zone*)res;
+	} else {
+		/* smaller element (or no element) */
+		int m;
+		result = (struct iter_forward_zone*)res;
+		if(!result || result->dclass != qclass)
+			return NULL;
+		/* count number of labels matched */
+		(void)dname_lab_cmp(result->name, result->namelabs, key.name,
+			key.namelabs, &m);
+		while(result) { /* go up until qname is subdomain of stub */
+			if(result->namelabs <= m)
+				break;
+			result = result->parent;
+		}
+	}
+	if(result)
+		return result->dp;
+	return NULL;
+}
+
+struct delegpt* 
+forwards_lookup_root(struct iter_forwards* fwd, uint16_t qclass)
+{
+	uint8_t root = 0;
+	return forwards_lookup(fwd, &root, qclass);
+}
+
+int
+forwards_next_root(struct iter_forwards* fwd, uint16_t* dclass)
+{
+	struct iter_forward_zone key;
+	rbnode_t* n;
+	struct iter_forward_zone* p;
+	if(*dclass == 0) {
+		/* first root item is first item in tree */
+		n = rbtree_first(fwd->tree);
+		if(n == RBTREE_NULL)
+			return 0;
+		p = (struct iter_forward_zone*)n;
+		if(dname_is_root(p->name)) {
+			*dclass = p->dclass;
+			return 1;
+		}
+		/* root not first item? search for higher items */
+		*dclass = p->dclass + 1;
+		return forwards_next_root(fwd, dclass);
+	}
+	/* find class n in tree, we may get a direct hit, or if we don't
+	 * this is the last item of the previous class so rbtree_next() takes
+	 * us to the next root (if any) */
+	key.node.key = &key;
+	key.name = (uint8_t*)"\000";
+	key.namelen = 1;
+	key.namelabs = 0;
+	key.dclass = *dclass;
+	n = NULL;
+	if(rbtree_find_less_equal(fwd->tree, &key, &n)) {
+		/* exact */
+		return 1;
+	} else {
+		/* smaller element */
+		if(!n || n == RBTREE_NULL)
+			return 0; /* nothing found */
+		n = rbtree_next(n);
+		if(n == RBTREE_NULL)
+			return 0; /* no higher */
+		p = (struct iter_forward_zone*)n;
+		if(dname_is_root(p->name)) {
+			*dclass = p->dclass;
+			return 1;
+		}
+		/* not a root node, return next higher item */
+		*dclass = p->dclass+1;
+		return forwards_next_root(fwd, dclass);
+	}
+}
+
+size_t 
+forwards_get_mem(struct iter_forwards* fwd)
+{
+	if(!fwd)
+		return 0;
+	return sizeof(*fwd) + sizeof(*fwd->tree) + 
+		regional_get_mem(fwd->region);
+}
+
+int 
+forwards_add_zone(struct iter_forwards* fwd, uint16_t c, struct delegpt* dp)
+{
+	if(!forwards_insert(fwd, c, dp))
+		return 0;
+	fwd_init_parents(fwd);
+	return 1;
+}
+
+void 
+forwards_delete_zone(struct iter_forwards* fwd, uint16_t c, uint8_t* nm)
+{
+	struct iter_forward_zone key;
+	key.node.key = &key;
+	key.dclass = c;
+	key.name = nm;
+	key.namelabs = dname_count_size_labels(nm, &key.namelen);
+	if(!rbtree_search(fwd->tree, &key))
+		return; /* nothing to do */
+	(void)rbtree_delete(fwd->tree, &key);
+	fwd_init_parents(fwd);
+}
+
diff --git a/3rdParty/Unbound/src/src/iterator/iter_fwd.h b/3rdParty/Unbound/src/src/iterator/iter_fwd.h
new file mode 100644
index 0000000..8f3bc1f
--- /dev/null
+++ b/3rdParty/Unbound/src/src/iterator/iter_fwd.h
@@ -0,0 +1,173 @@
+/*
+ * iterator/iter_fwd.h - iterative resolver module forward zones.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to assist the iterator module.
+ * Keep track of forward zones, and read those from config.
+ */
+
+#ifndef ITERATOR_ITER_FWD_H
+#define ITERATOR_ITER_FWD_H
+#include "util/rbtree.h"
+struct config_file;
+struct delegpt;
+struct regional;
+
+/**
+ * Iterator forward zones structure
+ */
+struct iter_forwards {
+	/** regional where forward zone server addresses are allocated */
+	struct regional* region;
+	/** 
+	 * Zones are stored in this tree. Sort order is specially chosen.
+	 * first sorted on qclass. Then on dname in nsec-like order, so that
+	 * a lookup on class, name will return an exact match or the closest
+	 * match which gives the ancestor needed.
+	 * contents of type iter_forward_zone.
+	 */
+	rbtree_t* tree;
+};
+
+/**
+ * Iterator forward servers for a particular zone.
+ */
+struct iter_forward_zone {
+	/** redblacktree node, key is this structure: class and name */
+	rbnode_t node;
+	/** name */
+	uint8_t* name;
+	/** length of name */
+	size_t namelen;
+	/** number of labels in name */
+	int namelabs;
+	/** delegation point with forward server information for this zone. 
+	 * If NULL then this forward entry is used to indicate that a
+	 * stub-zone with the same name exists, and should be used. */
+	struct delegpt* dp;
+	/** pointer to parent in tree (or NULL if none) */
+	struct iter_forward_zone* parent;
+	/** class. host order. */
+	uint16_t dclass;
+};
+
+/**
+ * Create forwards 
+ * @return new forwards or NULL on error.
+ */
+struct iter_forwards* forwards_create(void);
+
+/**
+ * Delete forwards.
+ * @param fwd: to delete.
+ */
+void forwards_delete(struct iter_forwards* fwd);
+
+/**
+ * Process forwards config.
+ * @param fwd: where to store.
+ * @param cfg: config options.
+ * @return 0 on error.
+ */
+int forwards_apply_cfg(struct iter_forwards* fwd, struct config_file* cfg);
+
+/**
+ * Find forward zone information
+ * For this qname/qclass find forward zone information, returns delegation
+ * point with server names and addresses, or NULL if no forwarding is needed.
+ *
+ * @param fwd: forward storage.
+ * @param qname: The qname of the query.
+ * @param qclass: The qclass of the query.
+ * @return: A delegation point if the query has to be forwarded to that list,
+ *         otherwise null.
+ */
+struct delegpt* forwards_lookup(struct iter_forwards* fwd, 
+	uint8_t* qname, uint16_t qclass);
+
+/**
+ * Same as forwards_lookup, but for the root only
+ * @param fwd: forward storage.
+ * @param qclass: The qclass of the query.
+ * @return: A delegation point if root forward exists, otherwise null.
+ */
+struct delegpt* forwards_lookup_root(struct iter_forwards* fwd, 
+	uint16_t qclass);
+
+/**
+ * Find next root item in forwards lookup tree.
+ * @param fwd: the forward storage
+ * @param qclass: class to look at next, or higher.
+ * @return false if none found, or if true stored in qclass.
+ */
+int forwards_next_root(struct iter_forwards* fwd, uint16_t* qclass);
+
+/**
+ * Get memory in use by forward storage
+ * @param fwd: forward storage.
+ * @return bytes in use
+ */
+size_t forwards_get_mem(struct iter_forwards* fwd);
+
+/** compare two fwd entries */
+int fwd_cmp(const void* k1, const void* k2);
+
+/**
+ * Add zone to forward structure. For external use since it recalcs 
+ * the tree parents.
+ * @param fwd: the forward data structure
+ * @param c: class of zone
+ * @param dp: delegation point with name and target nameservers for new
+ *	forward zone. This delegation point and all its data must be
+ *	malloced in the fwd->region. (then it is freed when the fwd is
+ *	deleted).
+ * @return false on failure (out of memory);
+ */
+int forwards_add_zone(struct iter_forwards* fwd, uint16_t c, 
+	struct delegpt* dp);
+
+/**
+ * Remove zone from forward structure. For external use since it 
+ * recalcs the tree parents. Does not actually release any memory, the region 
+ * is unchanged.
+ * @param fwd: the forward data structure
+ * @param c: class of zone
+ * @param nm: name of zone (in uncompressed wireformat).
+ */
+void forwards_delete_zone(struct iter_forwards* fwd, uint16_t c, uint8_t* nm);
+
+#endif /* ITERATOR_ITER_FWD_H */
diff --git a/3rdParty/Unbound/src/src/iterator/iter_hints.c b/3rdParty/Unbound/src/src/iterator/iter_hints.c
new file mode 100644
index 0000000..01c50b9
--- /dev/null
+++ b/3rdParty/Unbound/src/src/iterator/iter_hints.c
@@ -0,0 +1,488 @@
+/*
+ * iterator/iter_hints.c - iterative resolver module stub and root hints.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to assist the iterator module.
+ * Keep track of stub and root hints, and read those from config.
+ */
+#include "config.h"
+#include <ldns/dname.h>
+#include <ldns/rr.h>
+#include "iterator/iter_hints.h"
+#include "iterator/iter_delegpt.h"
+#include "util/regional.h"
+#include "util/log.h"
+#include "util/config_file.h"
+#include "util/net_help.h"
+#include "util/data/dname.h"
+
+struct iter_hints* 
+hints_create(void)
+{
+	struct iter_hints* hints = (struct iter_hints*)calloc(1,
+		sizeof(struct iter_hints));
+	if(!hints)
+		return NULL;
+	hints->region = regional_create();
+	if(!hints->region) {
+		hints_delete(hints);
+		return NULL;
+	}
+	return hints;
+}
+
+void 
+hints_delete(struct iter_hints* hints)
+{
+	if(!hints) 
+		return;
+	regional_destroy(hints->region);
+	free(hints);
+}
+
+/** add hint to delegation hints */
+static int
+ah(struct delegpt* dp, struct regional* r, const char* sv, const char* ip)
+{
+	struct sockaddr_storage addr;
+	socklen_t addrlen;
+	ldns_rdf* rdf = ldns_dname_new_frm_str(sv);
+	if(!rdf) {
+		log_err("could not parse %s", sv);
+		return 0;
+	}
+	if(!delegpt_add_ns(dp, r, ldns_rdf_data(rdf), 0) ||
+	   !extstrtoaddr(ip, &addr, &addrlen) ||
+	   !delegpt_add_target(dp, r, ldns_rdf_data(rdf), ldns_rdf_size(rdf),
+		&addr, addrlen, 0, 0)) {
+		ldns_rdf_deep_free(rdf);
+		return 0;
+	}
+	ldns_rdf_deep_free(rdf);
+	return 1;
+}
+
+/** obtain compiletime provided root hints */
+static struct delegpt* 
+compile_time_root_prime(struct regional* r, int do_ip4, int do_ip6)
+{
+	/* from:
+	 ;       This file is made available by InterNIC
+	 ;       under anonymous FTP as
+	 ;           file                /domain/named.cache
+	 ;           on server           FTP.INTERNIC.NET
+	 ;       -OR-                    RS.INTERNIC.NET
+	 ;
+	 ;       related version of root zone:   2010061700
+	 */
+	struct delegpt* dp = delegpt_create(r);
+	if(!dp)
+		return NULL;
+	dp->has_parent_side_NS = 1;
+	if(!delegpt_set_name(dp, r, (uint8_t*)"\000"))
+		return NULL;
+      if(do_ip4) {
+	if(!ah(dp, r, "A.ROOT-SERVERS.NET.", "198.41.0.4"))	return 0;
+	if(!ah(dp, r, "B.ROOT-SERVERS.NET.", "192.228.79.201")) return 0;
+	if(!ah(dp, r, "C.ROOT-SERVERS.NET.", "192.33.4.12"))	return 0;
+	if(!ah(dp, r, "D.ROOT-SERVERS.NET.", "128.8.10.90"))	return 0;
+	if(!ah(dp, r, "E.ROOT-SERVERS.NET.", "192.203.230.10")) return 0;
+	if(!ah(dp, r, "F.ROOT-SERVERS.NET.", "192.5.5.241"))	return 0;
+	if(!ah(dp, r, "G.ROOT-SERVERS.NET.", "192.112.36.4"))	return 0;
+	if(!ah(dp, r, "H.ROOT-SERVERS.NET.", "128.63.2.53"))	return 0;
+	if(!ah(dp, r, "I.ROOT-SERVERS.NET.", "192.36.148.17"))	return 0;
+	if(!ah(dp, r, "J.ROOT-SERVERS.NET.", "192.58.128.30"))	return 0;
+	if(!ah(dp, r, "K.ROOT-SERVERS.NET.", "193.0.14.129"))	return 0;
+	if(!ah(dp, r, "L.ROOT-SERVERS.NET.", "199.7.83.42"))	return 0;
+	if(!ah(dp, r, "M.ROOT-SERVERS.NET.", "202.12.27.33"))	return 0;
+      }
+      if(do_ip6) {
+	if(!ah(dp, r, "A.ROOT-SERVERS.NET.", "2001:503:ba3e::2:30")) return 0;
+	if(!ah(dp, r, "D.ROOT-SERVERS.NET.", "2001:500:2d::d")) return 0;
+	if(!ah(dp, r, "F.ROOT-SERVERS.NET.", "2001:500:2f::f")) return 0;
+	if(!ah(dp, r, "H.ROOT-SERVERS.NET.", "2001:500:1::803f:235")) return 0;
+	if(!ah(dp, r, "I.ROOT-SERVERS.NET.", "2001:7fe::53")) return 0;
+	if(!ah(dp, r, "J.ROOT-SERVERS.NET.", "2001:503:c27::2:30")) return 0;
+	if(!ah(dp, r, "K.ROOT-SERVERS.NET.", "2001:7fd::1")) return 0;
+	if(!ah(dp, r, "L.ROOT-SERVERS.NET.", "2001:500:3::42")) return 0;
+	if(!ah(dp, r, "M.ROOT-SERVERS.NET.", "2001:dc3::35")) return 0;
+      }
+	return dp;
+}
+
+/** insert new hint info into hint structure */
+static int
+hints_insert(struct iter_hints* hints, uint16_t c, struct delegpt* dp,
+	int noprime)
+{
+	struct iter_hints_stub* node = regional_alloc(hints->region,
+		sizeof(struct iter_hints_stub));
+	uint8_t* nm;
+	if(!node)
+		return 0;
+	nm = regional_alloc_init(hints->region, dp->name, dp->namelen);
+	if(!nm)
+		return 0;
+	node->dp = dp;
+	node->noprime = (uint8_t)noprime;
+	if(!name_tree_insert(&hints->tree, &node->node, nm, dp->namelen,
+		dp->namelabs, c)) {
+		log_err("second hints ignored.");
+	}
+	return 1;
+}
+
+/** set stub name */
+static int 
+read_stubs_name(struct iter_hints* hints, struct config_stub* s, 
+	struct delegpt* dp)
+{
+	ldns_rdf* rdf;
+	if(!s->name) {
+		log_err("stub zone without a name");
+		return 0;
+	}
+	rdf = ldns_dname_new_frm_str(s->name);
+	if(!rdf) {
+		log_err("cannot parse stub zone name %s", s->name);
+		return 0;
+	}
+	if(!delegpt_set_name(dp, hints->region, ldns_rdf_data(rdf))) {
+		ldns_rdf_deep_free(rdf);
+		log_err("out of memory");
+		return 0;
+	}
+	ldns_rdf_deep_free(rdf);
+	return 1;
+}
+
+/** set stub host names */
+static int 
+read_stubs_host(struct iter_hints* hints, struct config_stub* s, 
+	struct delegpt* dp)
+{
+	struct config_strlist* p;
+	ldns_rdf* rdf;
+	for(p = s->hosts; p; p = p->next) {
+		log_assert(p->str);
+		rdf = ldns_dname_new_frm_str(p->str);
+		if(!rdf) {
+			log_err("cannot parse stub %s nameserver name: '%s'", 
+				s->name, p->str);
+			return 0;
+		}
+		if(!delegpt_add_ns(dp, hints->region, ldns_rdf_data(rdf), 0)) {
+			ldns_rdf_deep_free(rdf);
+			log_err("out of memory");
+			return 0;
+		}
+		ldns_rdf_deep_free(rdf);
+	}
+	return 1;
+}
+
+/** set stub server addresses */
+static int 
+read_stubs_addr(struct iter_hints* hints, struct config_stub* s, 
+	struct delegpt* dp)
+{
+	struct config_strlist* p;
+	struct sockaddr_storage addr;
+	socklen_t addrlen;
+	for(p = s->addrs; p; p = p->next) {
+		log_assert(p->str);
+		if(!extstrtoaddr(p->str, &addr, &addrlen)) {
+			log_err("cannot parse stub %s ip address: '%s'", 
+				s->name, p->str);
+			return 0;
+		}
+		if(!delegpt_add_addr(dp, hints->region, &addr, addrlen, 0, 0)) {
+			log_err("out of memory");
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/** read stubs config */
+static int 
+read_stubs(struct iter_hints* hints, struct config_file* cfg)
+{
+	struct config_stub* s;
+	for(s = cfg->stubs; s; s = s->next) {
+		struct delegpt* dp = delegpt_create(hints->region);
+		if(!dp) {
+			log_err("out of memory");
+			return 0;
+		}
+		dp->has_parent_side_NS = 1;
+		if(!read_stubs_name(hints, s, dp) ||
+			!read_stubs_host(hints, s, dp) ||
+			!read_stubs_addr(hints, s, dp))
+			return 0;
+		if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp, !s->isprime))
+			return 0;
+		delegpt_log(VERB_QUERY, dp);
+	}
+	return 1;
+}
+
+/** read root hints from file */
+static int 
+read_root_hints(struct iter_hints* hints, char* fname)
+{
+	int lineno = 0;
+	uint32_t default_ttl = 0;
+	ldns_rdf* origin = NULL;
+	ldns_rdf* prev_rr = NULL;
+	struct delegpt* dp;
+	ldns_rr* rr = NULL;
+	ldns_status status;
+	uint16_t c = LDNS_RR_CLASS_IN;
+	FILE* f = fopen(fname, "r");
+	if(!f) {
+		log_err("could not read root hints %s: %s",
+			fname, strerror(errno));
+		return 0;
+	}
+	dp = delegpt_create(hints->region);
+	if(!dp) {
+		log_err("out of memory reading root hints");
+		fclose(f);
+		return 0;
+	}
+	verbose(VERB_QUERY, "Reading root hints from %s", fname);
+	dp->has_parent_side_NS = 1;
+	while(!feof(f)) {
+		status = ldns_rr_new_frm_fp_l(&rr, f, 
+			&default_ttl, &origin, &prev_rr, &lineno);
+		if(status == LDNS_STATUS_SYNTAX_EMPTY ||
+			status == LDNS_STATUS_SYNTAX_TTL ||
+			status == LDNS_STATUS_SYNTAX_ORIGIN)
+			continue;
+		if(status != LDNS_STATUS_OK) {
+			log_err("reading root hints %s %d: %s", fname,
+				lineno, ldns_get_errorstr_by_id(status));
+			goto stop_read;
+		}
+		if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_NS) {
+			if(!delegpt_add_ns(dp, hints->region,
+				ldns_rdf_data(ldns_rr_rdf(rr, 0)), 0)) {
+				log_err("out of memory reading root hints");
+				goto stop_read;
+			}
+			c = ldns_rr_get_class(rr);
+			if(!dp->name) {
+				if(!delegpt_set_name(dp, hints->region, 
+					ldns_rdf_data(ldns_rr_owner(rr)))){
+					log_err("out of memory.");
+					goto stop_read;
+				}
+			}
+		} else if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_A) {
+			struct sockaddr_in sa;
+			socklen_t len = (socklen_t)sizeof(sa);
+			memset(&sa, 0, len);
+			sa.sin_family = AF_INET;
+			sa.sin_port = (in_port_t)htons(UNBOUND_DNS_PORT);
+			memmove(&sa.sin_addr, 
+				ldns_rdf_data(ldns_rr_rdf(rr, 0)), INET_SIZE);
+			if(!delegpt_add_target(dp, hints->region,
+					ldns_rdf_data(ldns_rr_owner(rr)),
+					ldns_rdf_size(ldns_rr_owner(rr)),
+					(struct sockaddr_storage*)&sa, len, 
+					0, 0)) {
+				log_err("out of memory reading root hints");
+				goto stop_read;
+			}
+		} else if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_AAAA) {
+			struct sockaddr_in6 sa;
+			socklen_t len = (socklen_t)sizeof(sa);
+			memset(&sa, 0, len);
+			sa.sin6_family = AF_INET6;
+			sa.sin6_port = (in_port_t)htons(UNBOUND_DNS_PORT);
+			memmove(&sa.sin6_addr, 
+				ldns_rdf_data(ldns_rr_rdf(rr, 0)), INET6_SIZE);
+			if(!delegpt_add_target(dp, hints->region,
+					ldns_rdf_data(ldns_rr_owner(rr)),
+					ldns_rdf_size(ldns_rr_owner(rr)),
+					(struct sockaddr_storage*)&sa, len,
+					0, 0)) {
+				log_err("out of memory reading root hints");
+				goto stop_read;
+			}
+		} else {
+			log_warn("root hints %s:%d skipping type %d",
+				fname, lineno, ldns_rr_get_type(rr));
+		}
+
+		ldns_rr_free(rr);
+	}
+
+	if (origin)
+		ldns_rdf_deep_free(origin);
+	if (prev_rr)
+		ldns_rdf_deep_free(prev_rr);
+	fclose(f);
+	if(!dp->name) {
+		log_warn("root hints %s: no NS content", fname);
+		return 1;
+	}
+	if(!hints_insert(hints, c, dp, 0)) {
+		return 0;
+	}
+	delegpt_log(VERB_QUERY, dp);
+	return 1;
+
+stop_read:
+	if (origin)
+		ldns_rdf_deep_free(origin);
+	if (prev_rr)
+		ldns_rdf_deep_free(prev_rr);
+	fclose(f);
+	return 0;
+}
+
+/** read root hints list */
+static int 
+read_root_hints_list(struct iter_hints* hints, struct config_file* cfg)
+{
+	struct config_strlist* p;
+	for(p = cfg->root_hints; p; p = p->next) {
+		log_assert(p->str);
+		if(p->str && p->str[0]) {
+			char* f = p->str;
+			if(cfg->chrootdir && cfg->chrootdir[0] &&
+				strncmp(p->str, cfg->chrootdir, 
+				strlen(cfg->chrootdir)) == 0)
+				f += strlen(cfg->chrootdir);
+			if(!read_root_hints(hints, f))
+				return 0;
+		}
+	}
+	return 1;
+}
+
+int 
+hints_apply_cfg(struct iter_hints* hints, struct config_file* cfg)
+{
+	regional_free_all(hints->region);
+	name_tree_init(&hints->tree);
+	
+	/* read root hints */
+	if(!read_root_hints_list(hints, cfg))
+		return 0;
+
+	/* read stub hints */
+	if(!read_stubs(hints, cfg))
+		return 0;
+
+	/* use fallback compiletime root hints */
+	if(!hints_lookup_root(hints, LDNS_RR_CLASS_IN)) {
+		struct delegpt* dp = compile_time_root_prime(hints->region,
+			cfg->do_ip4, cfg->do_ip6);
+		verbose(VERB_ALGO, "no config, using builtin root hints.");
+		if(!dp) 
+			return 0;
+		if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp, 0))
+			return 0;
+	}
+
+	name_tree_init_parents(&hints->tree);
+	return 1;
+}
+
+struct delegpt* 
+hints_lookup_root(struct iter_hints* hints, uint16_t qclass)
+{
+	uint8_t rootlab = 0;
+	struct iter_hints_stub *stub;
+	stub = (struct iter_hints_stub*)name_tree_find(&hints->tree,
+		&rootlab, 1, 1, qclass);
+	if(!stub)
+		return NULL;
+	return stub->dp;
+}
+
+struct iter_hints_stub* 
+hints_lookup_stub(struct iter_hints* hints, uint8_t* qname, 
+	uint16_t qclass, struct delegpt* cache_dp)
+{
+	size_t len;
+	int labs;
+	struct iter_hints_stub *r;
+
+	/* first lookup the stub */
+	labs = dname_count_size_labels(qname, &len);
+	r = (struct iter_hints_stub*)name_tree_lookup(&hints->tree, qname,
+		len, labs, qclass);
+	if(!r) return NULL;
+
+	/* If there is no cache (root prime situation) */
+	if(cache_dp == NULL) {
+		if(r->dp->namelabs != 1)
+			return r; /* no cache dp, use any non-root stub */
+		return NULL;
+	}
+
+	/*
+	 * If the stub is same as the delegation we got
+	 * And has noprime set, we need to 'prime' to use this stub instead.
+	 */
+	if(r->noprime && query_dname_compare(cache_dp->name, r->dp->name)==0)
+		return r; /* use this stub instead of cached dp */
+	
+	/* 
+	 * If our cached delegation point is above the hint, we need to prime.
+	 */
+	if(dname_strict_subdomain(r->dp->name, r->dp->namelabs,
+		cache_dp->name, cache_dp->namelabs))
+		return r; /* need to prime this stub */
+	return NULL;
+}
+
+int hints_next_root(struct iter_hints* hints, uint16_t* qclass)
+{
+	return name_tree_next_root(&hints->tree, qclass);
+}
+
+size_t 
+hints_get_mem(struct iter_hints* hints)
+{
+	if(!hints) return 0;
+	return sizeof(*hints) + regional_get_mem(hints->region);
+}
diff --git a/3rdParty/Unbound/src/src/iterator/iter_hints.h b/3rdParty/Unbound/src/src/iterator/iter_hints.h
new file mode 100644
index 0000000..4540971
--- /dev/null
+++ b/3rdParty/Unbound/src/src/iterator/iter_hints.h
@@ -0,0 +1,142 @@
+/*
+ * iterator/iter_hints.h - iterative resolver module stub and root hints.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to assist the iterator module.
+ * Keep track of stub and root hints, and read those from config.
+ */
+
+#ifndef ITERATOR_ITER_HINTS_H
+#define ITERATOR_ITER_HINTS_H
+#include "util/storage/dnstree.h"
+struct iter_env;
+struct config_file;
+struct delegpt;
+struct regional;
+
+/**
+ * Iterator hints structure
+ */
+struct iter_hints {
+	/** regional where hints are allocated */
+	struct regional* region;
+	/** 
+	 * Hints are stored in this tree. Sort order is specially chosen.
+	 * first sorted on qclass. Then on dname in nsec-like order, so that
+	 * a lookup on class, name will return an exact match or the closest
+	 * match which gives the ancestor needed.
+	 * contents of type iter_hints_stub. The class IN root is in here.
+	 * uses name_tree_node from dnstree.h.
+	 */
+	rbtree_t tree;
+};
+
+/**
+ * Iterator hints for a particular stub.
+ */
+struct iter_hints_stub {
+	/** tree sorted by name, class */
+	struct name_tree_node node;
+	/** delegation point with hint information for this stub. */
+	struct delegpt* dp;
+	/** does the stub need to forego priming (like on other ports) */
+	uint8_t noprime;
+};
+
+/**
+ * Create hints 
+ * @return new hints or NULL on error.
+ */
+struct iter_hints* hints_create(void);
+
+/**
+ * Delete hints.
+ * @param hints: to delete.
+ */
+void hints_delete(struct iter_hints* hints);
+
+/**
+ * Process hints config. Sets default values for root hints if no config.
+ * @param hints: where to store.
+ * @param cfg: config options.
+ * @return 0 on error.
+ */
+int hints_apply_cfg(struct iter_hints* hints, struct config_file* cfg);
+
+/**
+ * Find root hints for the given class.
+ * @param hints: hint storage.
+ * @param qclass: class for which root hints are requested. host order.
+ * @return: NULL if no hints, or a ptr to stored hints.
+ */
+struct delegpt* hints_lookup_root(struct iter_hints* hints, uint16_t qclass);
+
+/**
+ * Find next root hints (to cycle through all root hints).
+ * @param hints: hint storage
+ * @param qclass: class for which root hints are sought.
+ * 	0 means give the first available root hints class.
+ * 	x means, give class x or a higher class if any.
+ * 	returns the found class in this variable.
+ * @return true if a root hint class is found.
+ * 	false if not root hint class is found (qclass may have been changed).
+ */
+int hints_next_root(struct iter_hints* hints, uint16_t* qclass);
+
+/**
+ * Given a qname/qclass combination, and the delegation point from the cache
+ * for this qname/qclass, determine if this combination indicates that a
+ * stub hint exists and must be primed.
+ *
+ * @param hints: hint storage.
+ * @param qname: The qname that generated the delegation point.
+ * @param qclass: The qclass that generated the delegation point.
+ * @param dp: The cache generated delegation point.
+ * @return: A priming delegation point if there is a stub hint that must
+ *         be primed, otherwise null.
+ */
+struct iter_hints_stub* hints_lookup_stub(struct iter_hints* hints, 
+	uint8_t* qname, uint16_t qclass, struct delegpt* dp);
+
+/**
+ * Get memory in use by hints
+ * @param hints: hint storage.
+ * @return bytes in use
+ */
+size_t hints_get_mem(struct iter_hints* hints);
+
+#endif /* ITERATOR_ITER_HINTS_H */
diff --git a/3rdParty/Unbound/src/src/iterator/iter_priv.c b/3rdParty/Unbound/src/src/iterator/iter_priv.c
new file mode 100644
index 0000000..db7dbe5
--- /dev/null
+++ b/3rdParty/Unbound/src/src/iterator/iter_priv.c
@@ -0,0 +1,263 @@
+/*
+ * iterator/iter_priv.c - iterative resolver private address and domain store
+ *
+ * Copyright (c) 2008, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to assist the iterator module.
+ * Keep track of the private addresses and lookup fast.
+ */
+
+#include "config.h"
+#include <ldns/dname.h>
+#include "iterator/iter_priv.h"
+#include "util/regional.h"
+#include "util/log.h"
+#include "util/config_file.h"
+#include "util/data/dname.h"
+#include "util/data/msgparse.h"
+#include "util/net_help.h"
+#include "util/storage/dnstree.h"
+
+struct iter_priv* priv_create(void)
+{
+	struct iter_priv* priv = (struct iter_priv*)calloc(1, sizeof(*priv));
+	if(!priv)
+		return NULL;
+	priv->region = regional_create();
+	if(!priv->region) {
+		priv_delete(priv);
+		return NULL;
+	}
+	addr_tree_init(&priv->a);
+	name_tree_init(&priv->n);
+	return priv;
+}
+
+void priv_delete(struct iter_priv* priv)
+{
+	if(!priv) return;
+	regional_destroy(priv->region);
+	free(priv);
+}
+
+/** Read private-addr declarations from config */
+static int read_addrs(struct iter_priv* priv, struct config_file* cfg)
+{
+	/* parse addresses, report errors, insert into tree */
+	struct config_strlist* p;
+	struct addr_tree_node* n;
+	struct sockaddr_storage addr;
+	int net;
+	socklen_t addrlen;
+
+	for(p = cfg->private_address; p; p = p->next) {
+		log_assert(p->str);
+		if(!netblockstrtoaddr(p->str, UNBOUND_DNS_PORT, &addr, 
+			&addrlen, &net)) {
+			log_err("cannot parse private-address: %s", p->str);
+			return 0;
+		}
+		n = (struct addr_tree_node*)regional_alloc(priv->region,
+			sizeof(*n));
+		if(!n) {
+			log_err("out of memory");
+			return 0;
+		}
+		if(!addr_tree_insert(&priv->a, n, &addr, addrlen, net)) {
+			verbose(VERB_QUERY, "ignoring duplicate "
+				"private-address: %s", p->str);
+		}
+	}
+	return 1;
+}
+
+/** Read private-domain declarations from config */
+static int read_names(struct iter_priv* priv, struct config_file* cfg)
+{
+	/* parse names, report errors, insert into tree */
+	struct config_strlist* p;
+	struct name_tree_node* n;
+	uint8_t* nm;
+	size_t nm_len;
+	int nm_labs;
+	ldns_rdf* rdf;
+
+	for(p = cfg->private_domain; p; p = p->next) {
+		log_assert(p->str);
+		rdf = ldns_dname_new_frm_str(p->str);
+		if(!rdf) {
+			log_err("cannot parse private-domain: %s", p->str);
+			return 0;
+		}
+		nm = ldns_rdf_data(rdf);
+		nm_labs = dname_count_size_labels(nm, &nm_len);
+		nm = (uint8_t*)regional_alloc_init(priv->region, nm, nm_len);
+		ldns_rdf_deep_free(rdf);
+		if(!nm) {
+			log_err("out of memory");
+			return 0;
+		}
+		n = (struct name_tree_node*)regional_alloc(priv->region,
+			sizeof(*n));
+		if(!n) {
+			log_err("out of memory");
+			return 0;
+		}
+		if(!name_tree_insert(&priv->n, n, nm, nm_len, nm_labs,
+			LDNS_RR_CLASS_IN)) {
+			verbose(VERB_QUERY, "ignoring duplicate "
+				"private-domain: %s", p->str);
+		}
+	}
+	return 1;
+}
+
+int priv_apply_cfg(struct iter_priv* priv, struct config_file* cfg)
+{
+	/* empty the current contents */
+	regional_free_all(priv->region);
+	addr_tree_init(&priv->a);
+	name_tree_init(&priv->n);
+
+	/* read new contents */
+	if(!read_addrs(priv, cfg))
+		return 0;
+	if(!read_names(priv, cfg))
+		return 0;
+
+	/* prepare for lookups */
+	addr_tree_init_parents(&priv->a);
+	name_tree_init_parents(&priv->n);
+	return 1;
+}
+
+/**
+ * See if an address is blocked.
+ * @param priv: structure for address storage.
+ * @param addr: address to check
+ * @param addrlen: length of addr.
+ * @return: true if the address must not be queried. false if unlisted.
+ */
+static int 
+priv_lookup_addr(struct iter_priv* priv, struct sockaddr_storage* addr,
+	socklen_t addrlen)
+{
+	return addr_tree_lookup(&priv->a, addr, addrlen) != NULL;
+}
+
+/**
+ * See if a name is whitelisted.
+ * @param priv: structure for address storage.
+ * @param pkt: the packet (for compression ptrs).
+ * @param name: name to check.
+ * @param name_len: uncompressed length of the name to check.
+ * @param dclass: class to check.
+ * @return: true if the name is OK. false if unlisted.
+ */
+static int 
+priv_lookup_name(struct iter_priv* priv, ldns_buffer* pkt,
+	uint8_t* name, size_t name_len, uint16_t dclass)
+{
+	size_t len;
+	uint8_t decomp[256];
+	int labs;
+	if(name_len >= sizeof(decomp))
+		return 0;
+	dname_pkt_copy(pkt, decomp, name);
+	labs = dname_count_size_labels(decomp, &len);
+	log_assert(name_len == len);
+	return name_tree_lookup(&priv->n, decomp, len, labs, dclass) != NULL;
+}
+
+size_t priv_get_mem(struct iter_priv* priv)
+{
+	if(!priv) return 0;
+	return sizeof(*priv) + regional_get_mem(priv->region);
+}
+
+int priv_rrset_bad(struct iter_priv* priv, ldns_buffer* pkt,
+	struct rrset_parse* rrset)
+{
+	if(priv->a.count == 0) 
+		return 0; /* there are no blocked addresses */
+
+	/* see if it is a private name, that is allowed to have any */
+	if(priv_lookup_name(priv, pkt, rrset->dname, rrset->dname_len,
+		ntohs(rrset->rrset_class))) {
+		return 0;
+	} else {
+		/* so its a public name, check the address */
+		socklen_t len;
+		struct rr_parse* rr;
+		if(rrset->type == LDNS_RR_TYPE_A) {
+			struct sockaddr_storage addr;
+			struct sockaddr_in sa;
+
+			len = (socklen_t)sizeof(sa);
+			memset(&sa, 0, len);
+			sa.sin_family = AF_INET;
+			sa.sin_port = (in_port_t)htons(UNBOUND_DNS_PORT);
+			for(rr = rrset->rr_first; rr; rr = rr->next) {
+				if(ldns_read_uint16(rr->ttl_data+4) 
+					!= INET_SIZE)
+					continue;
+				memmove(&sa.sin_addr, rr->ttl_data+4+2, 
+					INET_SIZE);
+				memmove(&addr, &sa, len);
+				if(priv_lookup_addr(priv, &addr, len))
+					return 1;
+			}
+		} else if(rrset->type == LDNS_RR_TYPE_AAAA) {
+			struct sockaddr_storage addr;
+			struct sockaddr_in6 sa;
+			len = (socklen_t)sizeof(sa);
+			memset(&sa, 0, len);
+			sa.sin6_family = AF_INET6;
+			sa.sin6_port = (in_port_t)htons(UNBOUND_DNS_PORT);
+			for(rr = rrset->rr_first; rr; rr = rr->next) {
+				if(ldns_read_uint16(rr->ttl_data+4) 
+					!= INET6_SIZE)
+					continue;
+				memmove(&sa.sin6_addr, rr->ttl_data+4+2, 
+					INET6_SIZE);
+				memmove(&addr, &sa, len);
+				if(priv_lookup_addr(priv, &addr, len)) 
+					return 1;
+			}
+		} 
+	}
+	return 0;
+}
diff --git a/3rdParty/Unbound/src/src/iterator/iter_priv.h b/3rdParty/Unbound/src/src/iterator/iter_priv.h
new file mode 100644
index 0000000..f6264f8
--- /dev/null
+++ b/3rdParty/Unbound/src/src/iterator/iter_priv.h
@@ -0,0 +1,110 @@
+/*
+ * iterator/iter_priv.h - iterative resolver private address and domain store
+ *
+ * Copyright (c) 2008, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to assist the iterator module.
+ * Keep track of the private addresses and lookup fast.
+ */
+
+#ifndef ITERATOR_ITER_PRIV_H
+#define ITERATOR_ITER_PRIV_H
+#include "util/rbtree.h"
+#include <ldns/buffer.h>
+struct iter_env;
+struct config_file;
+struct regional;
+struct rrset_parse;
+
+/**
+ * Iterator priv structure
+ */
+struct iter_priv {
+	/** regional for allocation */
+	struct regional* region;
+	/** 
+	 * Tree of the address spans that are blocked.
+	 * contents of type addr_tree_node.
+	 * No further data need, only presence or absence.
+	 */
+	rbtree_t a;
+	/** 
+	 * Tree of the domains spans that are allowed to contain
+	 * the blocked address spans.
+	 * contents of type name_tree_node.
+	 * No further data need, only presence or absence.
+	 */
+	rbtree_t n;
+};
+
+/**
+ * Create priv structure 
+ * @return new structure or NULL on error.
+ */
+struct iter_priv* priv_create(void);
+
+/**
+ * Delete priv structure.
+ * @param priv: to delete.
+ */
+void priv_delete(struct iter_priv* priv);
+
+/**
+ * Process priv config.
+ * @param priv: where to store.
+ * @param cfg: config options.
+ * @return 0 on error.
+ */
+int priv_apply_cfg(struct iter_priv* priv, struct config_file* cfg);
+
+/**
+ * See if rrset is bad.
+ * @param priv: structure for private address storage.
+ * @param pkt: packet to decompress rrset name in.
+ * @param rrset: the rrset to examine, A or AAAA.
+ * @return true if the rrset is bad and should be removed.
+ */
+int priv_rrset_bad(struct iter_priv* priv, ldns_buffer* pkt, 
+	struct rrset_parse* rrset);
+
+/**
+ * Get memory used by priv structure.
+ * @param priv: structure for address storage.
+ * @return bytes in use.
+ */
+size_t priv_get_mem(struct iter_priv* priv);
+
+#endif /* ITERATOR_ITER_PRIV_H */
diff --git a/3rdParty/Unbound/src/src/iterator/iter_resptype.c b/3rdParty/Unbound/src/src/iterator/iter_resptype.c
new file mode 100644
index 0000000..2cdc5fc
--- /dev/null
+++ b/3rdParty/Unbound/src/src/iterator/iter_resptype.c
@@ -0,0 +1,286 @@
+/*
+ * iterator/iter_resptype.c - response type information and classification.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file defines the response type. DNS Responses can be classified as
+ * one of the response types.
+ */
+#include "config.h"
+#include <ldns/packet.h>
+#include "iterator/iter_resptype.h"
+#include "iterator/iter_delegpt.h"
+#include "services/cache/dns.h"
+#include "util/net_help.h"
+#include "util/data/dname.h"
+
+enum response_type 
+response_type_from_cache(struct dns_msg* msg, 
+	struct query_info* request)
+{
+	/* If the message is NXDOMAIN, then it is an ANSWER. */
+	if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_NXDOMAIN)
+		return RESPONSE_TYPE_ANSWER;
+	if(request->qtype == LDNS_RR_TYPE_ANY)
+		return RESPONSE_TYPE_ANSWER;
+	
+	/* First we look at the answer section. This can tell us if this is
+	 * CNAME or positive ANSWER. */
+	if(msg->rep->an_numrrsets > 0) {
+		/* Now look at the answer section first. 3 states: 
+		 *	o our answer is there directly,
+		 *	o our answer is there after a cname,
+		 *	o or there is just a cname. */
+		uint8_t* mname = request->qname;
+		size_t mname_len = request->qname_len;
+		size_t i;
+		for(i=0; i<msg->rep->an_numrrsets; i++) {
+			struct ub_packed_rrset_key* s = msg->rep->rrsets[i];
+
+			/* If we have encountered an answer (before or 
+			 * after a CNAME), then we are done! Note that 
+			 * if qtype == CNAME then this will be noted as 
+			 * an ANSWER before it gets treated as a CNAME, 
+			 * as it should */
+			if(ntohs(s->rk.type) == request->qtype &&
+				ntohs(s->rk.rrset_class) == request->qclass &&
+				query_dname_compare(mname, s->rk.dname) == 0) {
+				return RESPONSE_TYPE_ANSWER;
+			}
+
+			/* If we have encountered a CNAME, make sure that 
+			 * it is relevant. */
+			if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME &&
+				query_dname_compare(mname, s->rk.dname) == 0) {
+				get_cname_target(s, &mname, &mname_len);
+			}
+		}
+
+		/* if we encountered a CNAME (or a bunch of CNAMEs), and 
+		 * still got to here, then it is a CNAME response. (i.e., 
+		 * the CNAME chain didn't terminate in an answer rrset.) */
+		if(mname != request->qname) {
+			return RESPONSE_TYPE_CNAME;
+		}
+	}
+
+	/* At this point, since we don't need to detect REFERRAL or LAME 
+	 * messages, it can only be an ANSWER. */
+	return RESPONSE_TYPE_ANSWER;
+}
+
+enum response_type 
+response_type_from_server(int rdset,
+	struct dns_msg* msg, struct query_info* request, struct delegpt* dp)
+{
+	uint8_t* origzone = (uint8_t*)"\000"; /* the default */
+	struct ub_packed_rrset_key* s;
+	size_t i;
+
+	if(!msg || !request)
+		return RESPONSE_TYPE_THROWAWAY;
+	
+	/* If the message is NXDOMAIN, then it answers the question. */
+	if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_NXDOMAIN) {
+		/* make sure its not recursive when we don't want it to */
+		if( (msg->rep->flags&BIT_RA) &&
+			!(msg->rep->flags&BIT_AA) && !rdset)
+				return RESPONSE_TYPE_REC_LAME;
+		/* it could be a CNAME with NXDOMAIN rcode */
+		for(i=0; i<msg->rep->an_numrrsets; i++) {
+			s = msg->rep->rrsets[i];
+			if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME &&
+				query_dname_compare(request->qname,
+				s->rk.dname) == 0) {
+				return RESPONSE_TYPE_CNAME;
+			}
+		}
+		return RESPONSE_TYPE_ANSWER;
+	}
+	
+	/* Other response codes mean (so far) to throw the response away as
+	 * meaningless and move on to the next nameserver. */
+	if(FLAGS_GET_RCODE(msg->rep->flags) != LDNS_RCODE_NOERROR)
+		return RESPONSE_TYPE_THROWAWAY;
+
+	/* Note: TC bit has already been handled */
+
+	if(dp) {
+		origzone = dp->name;
+	}
+
+	/* First we look at the answer section. This can tell us if this is a
+	 * CNAME or ANSWER or (provisional) ANSWER. */
+	if(msg->rep->an_numrrsets > 0) {
+		uint8_t* mname = request->qname;
+		size_t mname_len = request->qname_len;
+
+		/* Now look at the answer section first. 3 states: our 
+		 * answer is there directly, our answer is there after 
+		 * a cname, or there is just a cname. */
+		for(i=0; i<msg->rep->an_numrrsets; i++) {
+			s = msg->rep->rrsets[i];
+			
+			/* if the answer section has NS rrset, and qtype ANY 
+		 	 * and the delegation is lower, and no CNAMEs followed,
+		 	 * this is a referral where the NS went to AN section */
+			if((request->qtype == LDNS_RR_TYPE_ANY ||
+				request->qtype == LDNS_RR_TYPE_NS) &&
+				ntohs(s->rk.type) == LDNS_RR_TYPE_NS &&
+				ntohs(s->rk.rrset_class) == request->qclass &&
+				dname_strict_subdomain_c(s->rk.dname, 
+				origzone)) {
+				if((msg->rep->flags&BIT_AA))
+					return RESPONSE_TYPE_ANSWER;
+				return RESPONSE_TYPE_REFERRAL;
+			}
+
+			/* If we have encountered an answer (before or 
+			 * after a CNAME), then we are done! Note that 
+			 * if qtype == CNAME then this will be noted as an
+			 * ANSWER before it gets treated as a CNAME, as 
+			 * it should. */
+			if(ntohs(s->rk.type) == request->qtype &&
+				ntohs(s->rk.rrset_class) == request->qclass &&
+				query_dname_compare(mname, s->rk.dname) == 0) {
+				if((msg->rep->flags&BIT_AA))
+					return RESPONSE_TYPE_ANSWER;
+				/* If the AA bit isn't on, and we've seen 
+				 * the answer, we only provisionally say 
+				 * 'ANSWER' -- it very well could be a 
+				 * REFERRAL. */
+				break;
+			}
+
+			/* If we have encountered a CNAME, make sure that 
+			 * it is relevant. */
+			if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME &&
+				query_dname_compare(mname, s->rk.dname) == 0) {
+				get_cname_target(s, &mname, &mname_len);
+			}
+		}
+		/* not a referral, and qtype any, thus an answer */
+		if(request->qtype == LDNS_RR_TYPE_ANY)
+			return RESPONSE_TYPE_ANSWER;
+		/* if we encountered a CNAME (or a bunch of CNAMEs), and 
+		 * still got to here, then it is a CNAME response. 
+		 * (This is regardless of the AA bit at this point) */
+		if(mname != request->qname) {
+			return RESPONSE_TYPE_CNAME;
+		}
+	}
+
+	/* Looking at the authority section, we just look and see if 
+	 * there is a SOA record, that means a NOERROR/NODATA */
+	for(i = msg->rep->an_numrrsets; i < (msg->rep->an_numrrsets +
+		msg->rep->ns_numrrsets); i++) {
+		s = msg->rep->rrsets[i];
+
+		/* The normal way of detecting NOERROR/NODATA. */
+		if(ntohs(s->rk.type) == LDNS_RR_TYPE_SOA &&
+			dname_subdomain_c(request->qname, s->rk.dname)) {
+			/* we do our own recursion, thank you */
+			if( (msg->rep->flags&BIT_RA) &&
+				!(msg->rep->flags&BIT_AA) && !rdset)
+				return RESPONSE_TYPE_REC_LAME;
+			return RESPONSE_TYPE_ANSWER;
+		}
+	}
+	/* Looking at the authority section, we just look and see if 
+	 * there is a delegation NS set, turning it into a delegation. 
+	 * Otherwise, we will have to conclude ANSWER (either it is 
+	 * NOERROR/NODATA, or an non-authoritative answer). */
+	for(i = msg->rep->an_numrrsets; i < (msg->rep->an_numrrsets +
+		msg->rep->ns_numrrsets); i++) {
+		s = msg->rep->rrsets[i];
+
+		/* Detect REFERRAL/LAME/ANSWER based on the relationship 
+		 * of the NS set to the originating zone name. */
+		if(ntohs(s->rk.type) == LDNS_RR_TYPE_NS) {
+			/* If we are getting an NS set for the zone we 
+			 * thought we were contacting, then it is an answer.*/
+			if(query_dname_compare(s->rk.dname, origzone) == 0) {
+				/* see if mistakenly a recursive server was
+				 * deployed and is responding nonAA */
+				if( (msg->rep->flags&BIT_RA) &&
+					!(msg->rep->flags&BIT_AA) && !rdset)
+					return RESPONSE_TYPE_REC_LAME;
+				/* Or if a lame server is deployed,
+				 * which gives ns==zone delegation from cache 
+				 * without AA bit as well, with nodata nosoa*/
+				/* real answer must be +AA and SOA RFC(2308),
+				 * so this is wrong, and we SERVFAIL it if
+				 * this is the only possible reply, if it
+				 * is misdeployed the THROWAWAY makes us pick
+				 * the next server from the selection */
+				if(msg->rep->an_numrrsets==0 &&
+					!(msg->rep->flags&BIT_AA) && !rdset)
+					return RESPONSE_TYPE_THROWAWAY;
+				return RESPONSE_TYPE_ANSWER;
+			}
+			/* If we are getting a referral upwards (or to 
+			 * the same zone), then the server is 'lame'. */
+			if(dname_subdomain_c(origzone, s->rk.dname)) {
+				if(rdset) /* forward or reclame not LAME */
+					return RESPONSE_TYPE_THROWAWAY;
+				return RESPONSE_TYPE_LAME;
+			}
+			/* If the NS set is below the delegation point we 
+			 * are on, and it is non-authoritative, then it is 
+			 * a referral, otherwise it is an answer. */
+			if(dname_subdomain_c(s->rk.dname, origzone)) {
+				/* NOTE: I no longer remember in what case 
+				 * we would like this to be an answer. 
+				 * NODATA should have a SOA or nothing, 
+				 * not an NS rrset. 
+				 * True, referrals should not have the AA 
+				 * bit set, but... */
+				 
+				/* if((msg->rep->flags&BIT_AA))
+					return RESPONSE_TYPE_ANSWER; */
+				return RESPONSE_TYPE_REFERRAL;
+			}
+			/* Otherwise, the NS set is irrelevant. */
+		}
+	}
+
+	/* If we've gotten this far, this is NOERROR/NODATA (which could 
+	 * be an entirely empty message) */
+	/* check if recursive answer; saying it has empty cache */
+	if( (msg->rep->flags&BIT_RA) && !(msg->rep->flags&BIT_AA) && !rdset)
+		return RESPONSE_TYPE_REC_LAME;
+	return RESPONSE_TYPE_ANSWER;
+}
diff --git a/3rdParty/Unbound/src/src/iterator/iter_resptype.h b/3rdParty/Unbound/src/src/iterator/iter_resptype.h
new file mode 100644
index 0000000..3bb3eed
--- /dev/null
+++ b/3rdParty/Unbound/src/src/iterator/iter_resptype.h
@@ -0,0 +1,127 @@
+/*
+ * iterator/iter_resptype.h - response type information and classification.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file defines the response type. DNS Responses can be classified as
+ * one of the response types.
+ */
+
+#ifndef ITERATOR_ITER_RESPTYPE_H
+#define ITERATOR_ITER_RESPTYPE_H
+struct dns_msg;
+struct query_info;
+struct delegpt;
+
+/**
+ * The response type is used to interpret the response.
+ */
+enum response_type {
+	/** 
+	 * 'untyped' means that the type of this response hasn't been 
+	 * assigned. 
+	 */
+	RESPONSE_TYPE_UNTYPED   = 0,
+
+	/** 
+	 * 'answer' means that the response terminates the resolution 
+	 * process. 
+	 */
+	RESPONSE_TYPE_ANSWER,
+
+	/** 'delegation' means that the response is a delegation. */
+	RESPONSE_TYPE_REFERRAL,
+
+	/**
+	 * 'cname' means that the response is a cname without the final 
+	 * answer, and thus must be restarted.
+	 */
+	RESPONSE_TYPE_CNAME,
+
+	/**
+	 * 'throwaway' means that this particular response should be 
+	 * discarded and the next nameserver should be contacted
+	 */
+	RESPONSE_TYPE_THROWAWAY,
+
+	/**
+	 * 'lame' means that this particular response indicates that 
+	 * the nameserver knew nothing about the question.
+	 */
+	RESPONSE_TYPE_LAME,
+
+	/**
+	 * Recursion lame means that the nameserver is some sort of
+	 * open recursor, and not authoritative for the question.
+	 * It may know something, but not authoritatively.
+	 */
+	RESPONSE_TYPE_REC_LAME
+};
+
+/**
+ * Classifies a response message from cache based on the current request.
+ * Note that this routine assumes that THROWAWAY or LAME responses will not
+ * occur. Also, it will not detect REFERRAL type messages, since those are
+ * (currently) automatically classified based on how they came from the
+ * cache (findDelegation() instead of lookup()).
+ *
+ * @param msg: the message from the cache.
+ * @param request: the request that generated the response.
+ * @return the response type (CNAME or ANSWER).
+ */
+enum response_type response_type_from_cache(struct dns_msg* msg, 
+	struct query_info* request);
+
+/**
+ * Classifies a response message (from the wire) based on the current
+ * request.
+ *
+ * NOTE: currently this routine uses the AA bit in the response to help
+ * distinguish between some non-standard referrals and answers. It also
+ * relies somewhat on the originating zone to be accurate (for lameness
+ * detection, mostly).
+ *
+ * @param rdset: if RD bit was sent in query sent by unbound.
+ * @param msg: the message from the cache.
+ * @param request: the request that generated the response.
+ * @param dp: The delegation point that was being queried
+ *          when the response was returned.
+ * @return the response type (CNAME or ANSWER).
+ */
+enum response_type response_type_from_server(int rdset, 
+	struct dns_msg* msg, struct query_info* request, struct delegpt* dp);
+
+#endif /* ITERATOR_ITER_RESPTYPE_H */
diff --git a/3rdParty/Unbound/src/src/iterator/iter_scrub.c b/3rdParty/Unbound/src/src/iterator/iter_scrub.c
new file mode 100644
index 0000000..6147c96
--- /dev/null
+++ b/3rdParty/Unbound/src/src/iterator/iter_scrub.c
@@ -0,0 +1,751 @@
+/*
+ * iterator/iter_scrub.c - scrubbing, normalization, sanitization of DNS msgs.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file has routine(s) for cleaning up incoming DNS messages from 
+ * possible useless or malicious junk in it.
+ */
+#include "config.h"
+#include "iterator/iter_scrub.h"
+#include "iterator/iterator.h"
+#include "iterator/iter_priv.h"
+#include "services/cache/rrset.h"
+#include "util/log.h"
+#include "util/net_help.h"
+#include "util/regional.h"
+#include "util/config_file.h"
+#include "util/module.h"
+#include "util/data/msgparse.h"
+#include "util/data/dname.h"
+#include "util/data/msgreply.h"
+#include "util/alloc.h"
+
+/** RRset flag used during scrubbing. The RRset is OK. */
+#define RRSET_SCRUB_OK	0x80
+
+/** remove rrset, update loop variables */
+static void
+remove_rrset(const char* str, ldns_buffer* pkt, struct msg_parse* msg, 
+	struct rrset_parse* prev, struct rrset_parse** rrset)
+{
+	if(verbosity >= VERB_QUERY 
+		&& (*rrset)->dname_len <= LDNS_MAX_DOMAINLEN) {
+		uint8_t buf[LDNS_MAX_DOMAINLEN+1];
+		dname_pkt_copy(pkt, buf, (*rrset)->dname);
+		log_nametypeclass(VERB_QUERY, str, buf, 
+			(*rrset)->type, ntohs((*rrset)->rrset_class));
+	}
+	if(prev)
+		prev->rrset_all_next = (*rrset)->rrset_all_next;
+	else	msg->rrset_first = (*rrset)->rrset_all_next;
+	if(msg->rrset_last == *rrset)
+		msg->rrset_last = prev;
+	msg->rrset_count --;
+	switch((*rrset)->section) {
+		case LDNS_SECTION_ANSWER: msg->an_rrsets--; break;
+		case LDNS_SECTION_AUTHORITY: msg->ns_rrsets--; break;
+		case LDNS_SECTION_ADDITIONAL: msg->ar_rrsets--; break;
+		default: log_assert(0);
+	}
+	msgparse_bucket_remove(msg, *rrset);
+	*rrset = (*rrset)->rrset_all_next;
+}
+
+/** return true if rr type has additional names in it */
+static int
+has_additional(uint16_t t)
+{
+	switch(t) {
+		case LDNS_RR_TYPE_MB:
+		case LDNS_RR_TYPE_MD:
+		case LDNS_RR_TYPE_MF:
+		case LDNS_RR_TYPE_NS:
+		case LDNS_RR_TYPE_MX:
+		case LDNS_RR_TYPE_KX:
+		case LDNS_RR_TYPE_SRV:
+			return 1;
+		case LDNS_RR_TYPE_NAPTR:
+			/* TODO: NAPTR not supported, glue stripped off */
+			return 0;
+	}
+	return 0;
+}
+
+/** get additional name from rrset RR, return false if no name present */
+static int
+get_additional_name(struct rrset_parse* rrset, struct rr_parse* rr, 
+	uint8_t** nm, size_t* nmlen, ldns_buffer* pkt) 
+{
+	size_t offset = 0;
+	size_t len, oldpos;
+	switch(rrset->type) {
+		case LDNS_RR_TYPE_MB:
+		case LDNS_RR_TYPE_MD:
+		case LDNS_RR_TYPE_MF:
+		case LDNS_RR_TYPE_NS:
+			offset = 0;
+			break;
+		case LDNS_RR_TYPE_MX:
+		case LDNS_RR_TYPE_KX:
+			offset = 2;
+			break;
+		case LDNS_RR_TYPE_SRV:
+			offset = 6;
+			break;
+		case LDNS_RR_TYPE_NAPTR:
+			/* TODO: NAPTR not supported, glue stripped off */
+			return 0;
+		default:
+			return 0;
+	}
+	len = ldns_read_uint16(rr->ttl_data+sizeof(uint32_t));
+	if(len < offset+1)
+		return 0; /* rdata field too small */
+	*nm = rr->ttl_data+sizeof(uint32_t)+sizeof(uint16_t)+offset;
+	oldpos = ldns_buffer_position(pkt);
+	ldns_buffer_set_position(pkt, (size_t)(*nm - ldns_buffer_begin(pkt)));
+	*nmlen = pkt_dname_len(pkt);
+	ldns_buffer_set_position(pkt, oldpos);
+	if(*nmlen == 0)
+		return 0;
+	return 1;
+}
+
+/** Place mark on rrsets in additional section they are OK */
+static void
+mark_additional_rrset(ldns_buffer* pkt, struct msg_parse* msg, 
+	struct rrset_parse* rrset)
+{
+	/* Mark A and AAAA for NS as appropriate additional section info. */
+	uint8_t* nm = NULL;
+	size_t nmlen = 0;
+	struct rr_parse* rr;
+
+	if(!has_additional(rrset->type))
+		return;
+	for(rr = rrset->rr_first; rr; rr = rr->next) {
+		if(get_additional_name(rrset, rr, &nm, &nmlen, pkt)) {
+			/* mark A */
+			hashvalue_t h = pkt_hash_rrset(pkt, nm, LDNS_RR_TYPE_A, 
+				rrset->rrset_class, 0);
+			struct rrset_parse* r = msgparse_hashtable_lookup(
+				msg, pkt, h, 0, nm, nmlen, 
+				LDNS_RR_TYPE_A, rrset->rrset_class);
+			if(r && r->section == LDNS_SECTION_ADDITIONAL) {
+				r->flags |= RRSET_SCRUB_OK;
+			}
+			
+			/* mark AAAA */
+			h = pkt_hash_rrset(pkt, nm, LDNS_RR_TYPE_AAAA, 
+				rrset->rrset_class, 0);
+			r = msgparse_hashtable_lookup(msg, pkt, h, 0, nm, 
+				nmlen, LDNS_RR_TYPE_AAAA, rrset->rrset_class);
+			if(r && r->section == LDNS_SECTION_ADDITIONAL) {
+				r->flags |= RRSET_SCRUB_OK;
+			}
+		}
+	}
+}
+
+/** Get target name of a CNAME */
+static int
+parse_get_cname_target(struct rrset_parse* rrset, uint8_t** sname, 
+	size_t* snamelen)
+{
+	if(rrset->rr_count != 1) {
+		struct rr_parse* sig;
+		verbose(VERB_ALGO, "Found CNAME rrset with "
+			"size > 1: %u", (unsigned)rrset->rr_count);
+		/* use the first CNAME! */
+		rrset->rr_count = 1;
+		rrset->size = rrset->rr_first->size;
+		for(sig=rrset->rrsig_first; sig; sig=sig->next)
+			rrset->size += sig->size;
+		rrset->rr_last = rrset->rr_first;
+		rrset->rr_first->next = NULL;
+	}
+	if(rrset->rr_first->size < sizeof(uint16_t)+1)
+		return 0; /* CNAME rdata too small */
+	*sname = rrset->rr_first->ttl_data + sizeof(uint32_t)
+		+ sizeof(uint16_t); /* skip ttl, rdatalen */
+	*snamelen = rrset->rr_first->size - sizeof(uint16_t);
+	return 1;
+}
+
+/** Synthesize CNAME from DNAME, false if too long */
+static int 
+synth_cname(uint8_t* qname, size_t qnamelen, struct rrset_parse* dname_rrset, 
+	uint8_t* alias, size_t* aliaslen, ldns_buffer* pkt)
+{
+	/* we already know that sname is a strict subdomain of DNAME owner */
+	uint8_t* dtarg = NULL;
+	size_t dtarglen;
+	if(!parse_get_cname_target(dname_rrset, &dtarg, &dtarglen))
+		return 0; 
+	log_assert(qnamelen > dname_rrset->dname_len);
+	/* DNAME from com. to net. with qname example.com. -> example.net. */
+	/* so: \3com\0 to \3net\0 and qname \7example\3com\0 */
+	*aliaslen = qnamelen + dtarglen - dname_rrset->dname_len;
+	if(*aliaslen > LDNS_MAX_DOMAINLEN)
+		return 0; /* should have been RCODE YXDOMAIN */
+	/* decompress dnames into buffer, we know it fits */
+	dname_pkt_copy(pkt, alias, qname);
+	dname_pkt_copy(pkt, alias+(qnamelen-dname_rrset->dname_len), dtarg);
+	return 1;
+}
+
+/** synthesize a CNAME rrset */
+static struct rrset_parse*
+synth_cname_rrset(uint8_t** sname, size_t* snamelen, uint8_t* alias, 
+	size_t aliaslen, struct regional* region, struct msg_parse* msg, 
+	struct rrset_parse* rrset, struct rrset_parse* prev,
+	struct rrset_parse* nx, ldns_buffer* pkt)
+{
+	struct rrset_parse* cn = (struct rrset_parse*)regional_alloc(region,
+		sizeof(struct rrset_parse));
+	if(!cn)
+		return NULL;
+	memset(cn, 0, sizeof(*cn));
+	cn->rr_first = (struct rr_parse*)regional_alloc(region, 
+		sizeof(struct rr_parse));
+	if(!cn->rr_first)
+		return NULL;
+	cn->rr_last = cn->rr_first;
+	/* CNAME from sname to alias */
+	cn->dname = (uint8_t*)regional_alloc(region, *snamelen);
+	if(!cn->dname)
+		return NULL;
+	dname_pkt_copy(pkt, cn->dname, *sname);
+	cn->dname_len = *snamelen;
+	cn->type = LDNS_RR_TYPE_CNAME;
+	cn->section = rrset->section;
+	cn->rrset_class = rrset->rrset_class;
+	cn->rr_count = 1;
+	cn->size = sizeof(uint16_t) + aliaslen;
+	cn->hash=pkt_hash_rrset(pkt, cn->dname, cn->type, cn->rrset_class, 0);
+	/* allocate TTL + rdatalen + uncompressed dname */
+	memset(cn->rr_first, 0, sizeof(struct rr_parse));
+	cn->rr_first->outside_packet = 1;
+	cn->rr_first->ttl_data = (uint8_t*)regional_alloc(region, 
+		sizeof(uint32_t)+sizeof(uint16_t)+aliaslen);
+	if(!cn->rr_first->ttl_data)
+		return NULL;
+	ldns_write_uint32(cn->rr_first->ttl_data, 0); /* TTL = 0 */
+	ldns_write_uint16(cn->rr_first->ttl_data+4, aliaslen);
+	memmove(cn->rr_first->ttl_data+6, alias, aliaslen);
+	cn->rr_first->size = sizeof(uint16_t)+aliaslen;
+
+	/* link it in */
+	cn->rrset_all_next = nx;
+	if(prev)
+		prev->rrset_all_next = cn;
+	else	msg->rrset_first = cn;
+	if(nx == NULL)
+		msg->rrset_last = cn;
+	msg->rrset_count ++;
+	msg->an_rrsets++;
+	/* it is not inserted in the msg hashtable. */
+
+	*sname = cn->rr_first->ttl_data + sizeof(uint32_t)+sizeof(uint16_t);
+	*snamelen = aliaslen;
+	return cn;
+}
+
+/** check if DNAME applies to a name */
+static int
+pkt_strict_sub(ldns_buffer* pkt, uint8_t* sname, uint8_t* dr)
+{
+	uint8_t buf1[LDNS_MAX_DOMAINLEN+1];
+	uint8_t buf2[LDNS_MAX_DOMAINLEN+1];
+	/* decompress names */
+	dname_pkt_copy(pkt, buf1, sname);
+	dname_pkt_copy(pkt, buf2, dr);
+	return dname_strict_subdomain_c(buf1, buf2);
+}
+
+/** check subdomain with decompression */
+static int
+pkt_sub(ldns_buffer* pkt, uint8_t* comprname, uint8_t* zone)
+{
+	uint8_t buf[LDNS_MAX_DOMAINLEN+1];
+	dname_pkt_copy(pkt, buf, comprname);
+	return dname_subdomain_c(buf, zone);
+}
+
+/** check subdomain with decompression, compressed is parent */
+static int
+sub_of_pkt(ldns_buffer* pkt, uint8_t* zone, uint8_t* comprname)
+{
+	uint8_t buf[LDNS_MAX_DOMAINLEN+1];
+	dname_pkt_copy(pkt, buf, comprname);
+	return dname_subdomain_c(zone, buf);
+}
+
+/**
+ * This routine normalizes a response. This includes removing "irrelevant"
+ * records from the answer and additional sections and (re)synthesizing
+ * CNAMEs from DNAMEs, if present.
+ *
+ * @param pkt: packet.
+ * @param msg: msg to normalize.
+ * @param qinfo: original query.
+ * @param region: where to allocate synthesized CNAMEs.
+ * @return 0 on error.
+ */
+static int
+scrub_normalize(ldns_buffer* pkt, struct msg_parse* msg, 
+	struct query_info* qinfo, struct regional* region)
+{
+	uint8_t* sname = qinfo->qname;
+	size_t snamelen = qinfo->qname_len;
+	struct rrset_parse* rrset, *prev, *nsset=NULL;
+
+	if(FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NOERROR &&
+		FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NXDOMAIN)
+		return 1;
+
+	/* For the ANSWER section, remove all "irrelevant" records and add
+	 * synthesized CNAMEs from DNAMEs
+	 * This will strip out-of-order CNAMEs as well. */
+
+	/* walk through the parse packet rrset list, keep track of previous
+	 * for insert and delete ease, and examine every RRset */
+	prev = NULL;
+	rrset = msg->rrset_first;
+	while(rrset && rrset->section == LDNS_SECTION_ANSWER) {
+		if(rrset->type == LDNS_RR_TYPE_DNAME && 
+			pkt_strict_sub(pkt, sname, rrset->dname)) {
+			/* check if next rrset is correct CNAME. else,
+			 * synthesize a CNAME */
+			struct rrset_parse* nx = rrset->rrset_all_next;
+			uint8_t alias[LDNS_MAX_DOMAINLEN+1];
+			size_t aliaslen = 0;
+			if(rrset->rr_count != 1) {
+				verbose(VERB_ALGO, "Found DNAME rrset with "
+					"size > 1: %u", 
+					(unsigned)rrset->rr_count);
+				return 0;
+			}
+			if(!synth_cname(sname, snamelen, rrset, alias, 
+				&aliaslen, pkt)) {
+				verbose(VERB_ALGO, "synthesized CNAME "
+					"too long");
+				return 0;
+			}
+			if(nx && nx->type == LDNS_RR_TYPE_CNAME && 
+			   dname_pkt_compare(pkt, sname, nx->dname) == 0) {
+				/* check next cname */
+				uint8_t* t = NULL;
+				size_t tlen = 0;
+				if(!parse_get_cname_target(rrset, &t, &tlen))
+					return 0;
+				if(dname_pkt_compare(pkt, alias, t) == 0) {
+					/* it's OK and better capitalized */
+					prev = rrset;
+					rrset = nx;
+					continue;
+				}
+				/* synth ourselves */
+			}
+			/* synth a CNAME rrset */
+			prev = synth_cname_rrset(&sname, &snamelen, alias, 
+				aliaslen, region, msg, rrset, rrset, nx, pkt);
+			if(!prev) {
+				log_err("out of memory synthesizing CNAME");
+				return 0;
+			}
+			/* FIXME: resolve the conflict between synthesized 
+			 * CNAME ttls and the cache. */
+			rrset = nx;
+			continue;
+
+		}
+
+		/* The only records in the ANSWER section not allowed to */
+		if(dname_pkt_compare(pkt, sname, rrset->dname) != 0) {
+			remove_rrset("normalize: removing irrelevant RRset:", 
+				pkt, msg, prev, &rrset);
+			continue;
+		}
+
+		/* Follow the CNAME chain. */
+		if(rrset->type == LDNS_RR_TYPE_CNAME) {
+			uint8_t* oldsname = sname;
+			if(!parse_get_cname_target(rrset, &sname, &snamelen))
+				return 0;
+			prev = rrset;
+			rrset = rrset->rrset_all_next;
+			/* in CNAME ANY response, can have data after CNAME */
+			if(qinfo->qtype == LDNS_RR_TYPE_ANY) {
+				while(rrset && rrset->section ==
+					LDNS_SECTION_ANSWER &&
+					dname_pkt_compare(pkt, oldsname,
+					rrset->dname) == 0) {
+					prev = rrset;
+					rrset = rrset->rrset_all_next;
+				}
+			}
+			continue;
+		}
+
+		/* Otherwise, make sure that the RRset matches the qtype. */
+		if(qinfo->qtype != LDNS_RR_TYPE_ANY && 
+			qinfo->qtype != rrset->type) {
+			remove_rrset("normalize: removing irrelevant RRset:", 
+				pkt, msg, prev, &rrset);
+			continue;
+		}
+
+		/* Mark the additional names from relevant rrset as OK. */
+		/* only for RRsets that match the query name, other ones
+		 * will be removed by sanitize, so no additional for them */
+		if(dname_pkt_compare(pkt, qinfo->qname, rrset->dname) == 0)
+			mark_additional_rrset(pkt, msg, rrset);
+		
+		prev = rrset;
+		rrset = rrset->rrset_all_next;
+	}
+
+	/* Mark additional names from AUTHORITY */
+	while(rrset && rrset->section == LDNS_SECTION_AUTHORITY) {
+		if(rrset->type==LDNS_RR_TYPE_DNAME ||
+			rrset->type==LDNS_RR_TYPE_CNAME ||
+			rrset->type==LDNS_RR_TYPE_A ||
+			rrset->type==LDNS_RR_TYPE_AAAA) {
+			remove_rrset("normalize: removing irrelevant "
+				"RRset:", pkt, msg, prev, &rrset);
+			continue;
+		}
+		/* only one NS set allowed in authority section */
+		if(rrset->type==LDNS_RR_TYPE_NS) {
+			/* NS set must be pertinent to the query */
+			if(!sub_of_pkt(pkt, qinfo->qname, rrset->dname)) {
+				remove_rrset("normalize: removing irrelevant "
+					"RRset:", pkt, msg, prev, &rrset);
+				continue;
+			}
+			if(nsset == NULL) {
+				nsset = rrset;
+			} else {
+				remove_rrset("normalize: removing irrelevant "
+					"RRset:", pkt, msg, prev, &rrset);
+				continue;
+			}
+		}
+		mark_additional_rrset(pkt, msg, rrset);
+		prev = rrset;
+		rrset = rrset->rrset_all_next;
+	}
+
+	/* For each record in the additional section, remove it if it is an
+	 * address record and not in the collection of additional names 
+	 * found in ANSWER and AUTHORITY. */
+	/* These records have not been marked OK previously */
+	while(rrset && rrset->section == LDNS_SECTION_ADDITIONAL) {
+		/* FIXME: what about other types? */
+		if(rrset->type==LDNS_RR_TYPE_A || 
+			rrset->type==LDNS_RR_TYPE_AAAA) 
+		{
+			if((rrset->flags & RRSET_SCRUB_OK)) {
+				/* remove flag to clean up flags variable */
+				rrset->flags &= ~RRSET_SCRUB_OK;
+			} else {
+				remove_rrset("normalize: removing irrelevant "
+					"RRset:", pkt, msg, prev, &rrset);
+				continue;
+			}
+		}
+		if(rrset->type==LDNS_RR_TYPE_DNAME || 
+			rrset->type==LDNS_RR_TYPE_CNAME ||
+			rrset->type==LDNS_RR_TYPE_NS) {
+			remove_rrset("normalize: removing irrelevant "
+				"RRset:", pkt, msg, prev, &rrset);
+			continue;
+		}
+		prev = rrset;
+		rrset = rrset->rrset_all_next;
+	}
+	
+	return 1;
+}
+
+/**
+ * Store potential poison in the cache (only if hardening disabled).
+ * The rrset is stored in the cache but removed from the message.
+ * So that it will be used for infrastructure purposes, but not be 
+ * returned to the client.
+ * @param pkt: packet
+ * @param msg: message parsed
+ * @param env: environment with cache
+ * @param rrset: to store.
+ */
+static void
+store_rrset(ldns_buffer* pkt, struct msg_parse* msg, struct module_env* env,
+	struct rrset_parse* rrset)
+{
+	struct ub_packed_rrset_key* k;
+	struct packed_rrset_data* d;
+	struct rrset_ref ref;
+	uint32_t now = *env->now;
+
+	k = alloc_special_obtain(env->alloc);
+	if(!k)
+		return;
+	k->entry.data = NULL;
+	if(!parse_copy_decompress_rrset(pkt, msg, rrset, NULL, k)) {
+		alloc_special_release(env->alloc, k);
+		return;
+	}
+	d = (struct packed_rrset_data*)k->entry.data;
+	packed_rrset_ttl_add(d, now);
+	ref.key = k;
+	ref.id = k->id;
+	/*ignore ret: it was in the cache, ref updated */
+	(void)rrset_cache_update(env->rrset_cache, &ref, env->alloc, now);
+}
+
+/** Check if there are SOA records in the authority section (negative) */
+static int
+soa_in_auth(struct msg_parse* msg)
+{
+	struct rrset_parse* rrset;
+	for(rrset = msg->rrset_first; rrset; rrset = rrset->rrset_all_next)
+		if(rrset->type == LDNS_RR_TYPE_SOA &&
+			rrset->section == LDNS_SECTION_AUTHORITY) 
+			return 1;
+	return 0;
+}
+ 
+/**
+ * Check if right hand name in NSEC is within zone
+ * @param rrset: the NSEC rrset
+ * @param zonename: the zone name.
+ * @return true if BAD.
+ */
+static int sanitize_nsec_is_overreach(struct rrset_parse* rrset, 
+	uint8_t* zonename)
+{
+	struct rr_parse* rr;
+	uint8_t* rhs;
+	size_t len;
+	log_assert(rrset->type == LDNS_RR_TYPE_NSEC);
+	for(rr = rrset->rr_first; rr; rr = rr->next) {
+		rhs = rr->ttl_data+4+2;
+		len = ldns_read_uint16(rr->ttl_data+4);
+		if(!dname_valid(rhs, len)) {
+			/* malformed domain name in rdata */
+			return 1;
+		}
+		if(!dname_subdomain_c(rhs, zonename)) {
+			/* overreaching */
+			return 1;
+		}
+	}
+	/* all NSEC RRs OK */
+	return 0;
+}
+
+/**
+ * Given a response event, remove suspect RRsets from the response.
+ * "Suspect" rrsets are potentially poison. Note that this routine expects
+ * the response to be in a "normalized" state -- that is, all "irrelevant"
+ * RRsets have already been removed, CNAMEs are in order, etc.
+ *
+ * @param pkt: packet.
+ * @param msg: msg to normalize.
+ * @param qinfo: the question originally asked.
+ * @param zonename: name of server zone.
+ * @param env: module environment with config and cache.
+ * @param ie: iterator environment with private address data.
+ * @return 0 on error.
+ */
+static int
+scrub_sanitize(ldns_buffer* pkt, struct msg_parse* msg, 
+	struct query_info* qinfo, uint8_t* zonename, struct module_env* env,
+	struct iter_env* ie)
+{
+	int del_addi = 0; /* if additional-holding rrsets are deleted, we
+		do not trust the normalized additional-A-AAAA any more */
+	struct rrset_parse* rrset, *prev;
+	prev = NULL;
+	rrset = msg->rrset_first;
+
+	/* the first DNAME is allowed to stay. It needs checking before
+	 * it can be used from the cache. After normalization, an initial 
+	 * DNAME will have a correctly synthesized CNAME after it. */
+	if(rrset && rrset->type == LDNS_RR_TYPE_DNAME && 
+		rrset->section == LDNS_SECTION_ANSWER &&
+		pkt_strict_sub(pkt, qinfo->qname, rrset->dname) &&
+		pkt_sub(pkt, rrset->dname, zonename)) {
+		prev = rrset; /* DNAME allowed to stay in answer section */
+		rrset = rrset->rrset_all_next;
+	}
+	
+	/* remove all records from the answer section that are 
+	 * not the same domain name as the query domain name.
+	 * The answer section should contain rrsets with the same name
+	 * as the question. For DNAMEs a CNAME has been synthesized.
+	 * Wildcards have the query name in answer section.
+	 * ANY queries get query name in answer section.
+	 * Remainders of CNAME chains are cut off and resolved by iterator. */
+	while(rrset && rrset->section == LDNS_SECTION_ANSWER) {
+		if(dname_pkt_compare(pkt, qinfo->qname, rrset->dname) != 0) {
+			if(has_additional(rrset->type)) del_addi = 1;
+			remove_rrset("sanitize: removing extraneous answer "
+				"RRset:", pkt, msg, prev, &rrset);
+			continue;
+		}
+		prev = rrset;
+		rrset = rrset->rrset_all_next;
+	}
+
+	/* At this point, we brutally remove ALL rrsets that aren't 
+	 * children of the originating zone. The idea here is that, 
+	 * as far as we know, the server that we contacted is ONLY 
+	 * authoritative for the originating zone. It, of course, MAY 
+	 * be authoriative for any other zones, and of course, MAY 
+	 * NOT be authoritative for some subdomains of the originating 
+	 * zone. */
+	prev = NULL;
+	rrset = msg->rrset_first;
+	while(rrset) {
+
+		/* remove private addresses */
+		if( (rrset->type == LDNS_RR_TYPE_A || 
+			rrset->type == LDNS_RR_TYPE_AAAA) &&
+			priv_rrset_bad(ie->priv, pkt, rrset)) {
+
+			/* do not set servfail since this leads to too
+			 * many drops of other people using rfc1918 space */
+			remove_rrset("sanitize: removing public name with "
+				"private address", pkt, msg, prev, &rrset);
+			continue;
+		}
+		
+		/* skip DNAME records -- they will always be followed by a 
+		 * synthesized CNAME, which will be relevant.
+		 * FIXME: should this do something differently with DNAME 
+		 * rrsets NOT in Section.ANSWER? */
+		/* But since DNAME records are also subdomains of the zone,
+		 * same check can be used */
+
+		if(!pkt_sub(pkt, rrset->dname, zonename)) {
+			if(msg->an_rrsets == 0 && 
+				rrset->type == LDNS_RR_TYPE_NS && 
+				rrset->section == LDNS_SECTION_AUTHORITY &&
+				FLAGS_GET_RCODE(msg->flags) == 
+				LDNS_RCODE_NOERROR && !soa_in_auth(msg) &&
+				sub_of_pkt(pkt, zonename, rrset->dname)) {
+				/* noerror, nodata and this NS rrset is above
+				 * the zone. This is LAME! 
+				 * Leave in the NS for lame classification. */
+				/* remove everything from the additional
+				 * (we dont want its glue that was approved
+				 * during the normalize action) */
+				del_addi = 1;
+			} else if(!env->cfg->harden_glue) {
+				/* store in cache! Since it is relevant
+				 * (from normalize) it will be picked up 
+				 * from the cache to be used later */
+				store_rrset(pkt, msg, env, rrset);
+				remove_rrset("sanitize: storing potential "
+				"poison RRset:", pkt, msg, prev, &rrset);
+				continue;
+			} else {
+				if(has_additional(rrset->type)) del_addi = 1;
+				remove_rrset("sanitize: removing potential "
+				"poison RRset:", pkt, msg, prev, &rrset);
+				continue;
+			}
+		}
+		if(del_addi && rrset->section == LDNS_SECTION_ADDITIONAL) {
+			remove_rrset("sanitize: removing potential "
+			"poison reference RRset:", pkt, msg, prev, &rrset);
+			continue;
+		}
+		/* check if right hand side of NSEC is within zone */
+		if(rrset->type == LDNS_RR_TYPE_NSEC &&
+			sanitize_nsec_is_overreach(rrset, zonename)) {
+			remove_rrset("sanitize: removing overreaching NSEC "
+				"RRset:", pkt, msg, prev, &rrset);
+			continue;
+		}
+		prev = rrset;
+		rrset = rrset->rrset_all_next;
+	}
+	return 1;
+}
+
+int 
+scrub_message(ldns_buffer* pkt, struct msg_parse* msg, 
+	struct query_info* qinfo, uint8_t* zonename, struct regional* region,
+	struct module_env* env, struct iter_env* ie)
+{
+	/* basic sanity checks */
+	log_nametypeclass(VERB_ALGO, "scrub for", zonename, LDNS_RR_TYPE_NS, 
+		qinfo->qclass);
+	if(msg->qdcount > 1)
+		return 0;
+	if( !(msg->flags&BIT_QR) )
+		return 0;
+	msg->flags &= ~(BIT_AD|BIT_Z); /* force off bit AD and Z */
+	
+	/* make sure that a query is echoed back when NOERROR or NXDOMAIN */
+	/* this is not required for basic operation but is a forgery 
+	 * resistance (security) feature */
+	if((FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NOERROR ||
+		FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NXDOMAIN) &&
+		msg->qdcount == 0)
+		return 0;
+
+	/* if a query is echoed back, make sure it is correct. Otherwise,
+	 * this may be not a reply to our query. */
+	if(msg->qdcount == 1) {
+		if(dname_pkt_compare(pkt, msg->qname, qinfo->qname) != 0)
+			return 0;
+		if(msg->qtype != qinfo->qtype || msg->qclass != qinfo->qclass)
+			return 0;
+	}
+
+	/* normalize the response, this cleans up the additional.  */
+	if(!scrub_normalize(pkt, msg, qinfo, region))
+		return 0;
+	/* delete all out-of-zone information */
+	if(!scrub_sanitize(pkt, msg, qinfo, zonename, env, ie))
+		return 0;
+	return 1;
+}
diff --git a/3rdParty/Unbound/src/src/iterator/iter_scrub.h b/3rdParty/Unbound/src/src/iterator/iter_scrub.h
new file mode 100644
index 0000000..6b7274e
--- /dev/null
+++ b/3rdParty/Unbound/src/src/iterator/iter_scrub.h
@@ -0,0 +1,69 @@
+/*
+ * iterator/iter_scrub.h - scrubbing, normalization, sanitization of DNS msgs.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file has routine(s) for cleaning up incoming DNS messages from 
+ * possible useless or malicious junk in it.
+ */
+
+#ifndef ITERATOR_ITER_SCRUB_H
+#define ITERATOR_ITER_SCRUB_H
+#include <ldns/buffer.h>
+struct msg_parse;
+struct query_info;
+struct regional;
+struct module_env;
+struct iter_env;
+
+/**
+ * Cleanup the passed dns message.
+ * @param pkt: the packet itself, for resolving name compression pointers.
+ *	the packet buffer is unaltered.
+ * @param msg: the parsed packet, this structure is cleaned up.
+ * @param qinfo: the query info that was sent to the server. Checked.
+ * @param zonename: the name of the last delegation point.
+ *	Used to determine out of bailiwick information.
+ * @param regional: where to allocate (new) parts of the message.
+ * @param env: module environment with config settings and cache. 
+ * @param ie: iterator module environment data.
+ * @return: false if the message is total waste. true if scrubbed with success.
+ */
+int scrub_message(ldns_buffer* pkt, struct msg_parse* msg, 
+	struct query_info* qinfo, uint8_t* zonename, struct regional* regional,
+	struct module_env* env, struct iter_env* ie);
+
+#endif /* ITERATOR_ITER_SCRUB_H */
diff --git a/3rdParty/Unbound/src/src/iterator/iter_utils.c b/3rdParty/Unbound/src/src/iterator/iter_utils.c
new file mode 100644
index 0000000..25fda5e
--- /dev/null
+++ b/3rdParty/Unbound/src/src/iterator/iter_utils.c
@@ -0,0 +1,976 @@
+/*
+ * iterator/iter_utils.c - iterative resolver module utility functions.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to assist the iterator module.
+ * Configuration options. Forward zones. 
+ */
+#include "config.h"
+#include "iterator/iter_utils.h"
+#include "iterator/iterator.h"
+#include "iterator/iter_hints.h"
+#include "iterator/iter_fwd.h"
+#include "iterator/iter_donotq.h"
+#include "iterator/iter_delegpt.h"
+#include "iterator/iter_priv.h"
+#include "services/cache/infra.h"
+#include "services/cache/dns.h"
+#include "services/cache/rrset.h"
+#include "util/net_help.h"
+#include "util/module.h"
+#include "util/log.h"
+#include "util/config_file.h"
+#include "util/regional.h"
+#include "util/data/msgparse.h"
+#include "util/data/dname.h"
+#include "util/random.h"
+#include "util/fptr_wlist.h"
+#include "validator/val_anchor.h"
+#include "validator/val_kcache.h"
+#include "validator/val_kentry.h"
+
+/** time when nameserver glue is said to be 'recent' */
+#define SUSPICION_RECENT_EXPIRY 86400
+/** penalty to validation failed blacklisted IPs */
+#define BLACKLIST_PENALTY (USEFUL_SERVER_TOP_TIMEOUT*4)
+
+/** fillup fetch policy array */
+static void
+fetch_fill(struct iter_env* ie, const char* str)
+{
+	char* s = (char*)str, *e;
+	int i;
+	for(i=0; i<ie->max_dependency_depth+1; i++) {
+		ie->target_fetch_policy[i] = strtol(s, &e, 10);
+		if(s == e)
+			fatal_exit("cannot parse fetch policy number %s", s);
+		s = e;
+	}
+}
+
+/** Read config string that represents the target fetch policy */
+static int
+read_fetch_policy(struct iter_env* ie, const char* str)
+{
+	int count = cfg_count_numbers(str);
+	if(count < 1) {
+		log_err("Cannot parse target fetch policy: \"%s\"", str);
+		return 0;
+	}
+	ie->max_dependency_depth = count - 1;
+	ie->target_fetch_policy = (int*)calloc(
+		(size_t)ie->max_dependency_depth+1, sizeof(int));
+	if(!ie->target_fetch_policy) {
+		log_err("alloc fetch policy: out of memory");
+		return 0;
+	}
+	fetch_fill(ie, str);
+	return 1;
+}
+
+int 
+iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg)
+{
+	int i;
+	/* target fetch policy */
+	if(!read_fetch_policy(iter_env, cfg->target_fetch_policy))
+		return 0;
+	for(i=0; i<iter_env->max_dependency_depth+1; i++)
+		verbose(VERB_QUERY, "target fetch policy for level %d is %d",
+			i, iter_env->target_fetch_policy[i]);
+	
+	if(!iter_env->hints)
+		iter_env->hints = hints_create();
+	if(!iter_env->hints || !hints_apply_cfg(iter_env->hints, cfg)) {
+		log_err("Could not set root or stub hints");
+		return 0;
+	}
+	if(!iter_env->donotq)
+		iter_env->donotq = donotq_create();
+	if(!iter_env->donotq || !donotq_apply_cfg(iter_env->donotq, cfg)) {
+		log_err("Could not set donotqueryaddresses");
+		return 0;
+	}
+	if(!iter_env->priv)
+		iter_env->priv = priv_create();
+	if(!iter_env->priv || !priv_apply_cfg(iter_env->priv, cfg)) {
+		log_err("Could not set private addresses");
+		return 0;
+	}
+	iter_env->supports_ipv6 = cfg->do_ip6;
+	iter_env->supports_ipv4 = cfg->do_ip4;
+	return 1;
+}
+
+/** filter out unsuitable targets
+ * @param iter_env: iterator environment with ipv6-support flag.
+ * @param env: module environment with infra cache.
+ * @param name: zone name
+ * @param namelen: length of name
+ * @param qtype: query type (host order).
+ * @param now: current time
+ * @param a: address in delegation point we are examining.
+ * @return an integer that signals the target suitability.
+ *	as follows:
+ *	-1: The address should be omitted from the list.
+ *	    Because:
+ *		o The address is bogus (DNSSEC validation failure).
+ *		o Listed as donotquery
+ *		o is ipv6 but no ipv6 support (in operating system).
+ *		o is ipv4 but no ipv4 support (in operating system).
+ *		o is lame
+ *	Otherwise, an rtt in milliseconds.
+ *	0 .. USEFUL_SERVER_TOP_TIMEOUT-1
+ *		The roundtrip time timeout estimate. less than 2 minutes.
+ *		Note that util/rtt.c has a MIN_TIMEOUT of 50 msec, thus
+ *		values 0 .. 49 are not used, unless that is changed.
+ *	USEFUL_SERVER_TOP_TIMEOUT
+ *		This value exactly is given for unresponsive blacklisted.
+ *	USEFUL_SERVER_TOP_TIMEOUT+1
+ *		For non-blacklisted servers: huge timeout, but has traffic.
+ *	USEFUL_SERVER_TOP_TIMEOUT*1 ..
+ *		parent-side lame servers get this penalty. A dispreferential
+ *		server. (lame in delegpt).
+ *	USEFUL_SERVER_TOP_TIMEOUT*2 ..
+ *		dnsseclame servers get penalty
+ *	USEFUL_SERVER_TOP_TIMEOUT*3 ..
+ *		recursion lame servers get penalty
+ *	UNKNOWN_SERVER_NICENESS 
+ *		If no information is known about the server, this is
+ *		returned. 376 msec or so.
+ *	+BLACKLIST_PENALTY (of USEFUL_TOP_TIMEOUT*4) for dnssec failed IPs.
+ *
+ * When a final value is chosen that is dnsseclame ; dnsseclameness checking
+ * is turned off (so we do not discard the reply).
+ * When a final value is chosen that is recursionlame; RD bit is set on query.
+ * Because of the numbers this means recursionlame also have dnssec lameness
+ * checking turned off. 
+ */
+static int
+iter_filter_unsuitable(struct iter_env* iter_env, struct module_env* env,
+	uint8_t* name, size_t namelen, uint16_t qtype, uint32_t now, 
+	struct delegpt_addr* a)
+{
+	int rtt, lame, reclame, dnsseclame;
+	if(a->bogus)
+		return -1; /* address of server is bogus */
+	if(donotq_lookup(iter_env->donotq, &a->addr, a->addrlen)) {
+		log_addr(VERB_ALGO, "skip addr on the donotquery list",
+			&a->addr, a->addrlen);
+		return -1; /* server is on the donotquery list */
+	}
+	if(!iter_env->supports_ipv6 && addr_is_ip6(&a->addr, a->addrlen)) {
+		return -1; /* there is no ip6 available */
+	}
+	if(!iter_env->supports_ipv4 && !addr_is_ip6(&a->addr, a->addrlen)) {
+		return -1; /* there is no ip4 available */
+	}
+	/* check lameness - need zone , class info */
+	if(infra_get_lame_rtt(env->infra_cache, &a->addr, a->addrlen, 
+		name, namelen, qtype, &lame, &dnsseclame, &reclame, 
+		&rtt, now)) {
+		log_addr(VERB_ALGO, "servselect", &a->addr, a->addrlen);
+		verbose(VERB_ALGO, "   rtt=%d%s%s%s%s", rtt,
+			lame?" LAME":"",
+			dnsseclame?" DNSSEC_LAME":"",
+			reclame?" REC_LAME":"",
+			a->lame?" ADDR_LAME":"");
+		if(lame)
+			return -1; /* server is lame */
+		else if(rtt >= USEFUL_SERVER_TOP_TIMEOUT)
+			/* server is unresponsive,
+			 * we used to return TOP_TIMOUT, but fairly useless,
+			 * because if == TOP_TIMEOUT is dropped because
+			 * blacklisted later, instead, remove it here, so
+			 * other choices (that are not blacklisted) can be
+			 * tried */
+			return -1;
+		/* select remainder from worst to best */
+		else if(reclame)
+			return rtt+USEFUL_SERVER_TOP_TIMEOUT*3; /* nonpref */
+		else if(dnsseclame )
+			return rtt+USEFUL_SERVER_TOP_TIMEOUT*2; /* nonpref */
+		else if(a->lame)
+			return rtt+USEFUL_SERVER_TOP_TIMEOUT+1; /* nonpref */
+		else	return rtt;
+	}
+	/* no server information present */
+	if(a->lame)
+		return USEFUL_SERVER_TOP_TIMEOUT+1+UNKNOWN_SERVER_NICENESS; /* nonpref */
+	return UNKNOWN_SERVER_NICENESS;
+}
+
+/** lookup RTT information, and also store fastest rtt (if any) */
+static int
+iter_fill_rtt(struct iter_env* iter_env, struct module_env* env,
+	uint8_t* name, size_t namelen, uint16_t qtype, uint32_t now, 
+	struct delegpt* dp, int* best_rtt, struct sock_list* blacklist)
+{
+	int got_it = 0;
+	struct delegpt_addr* a;
+	if(dp->bogus)
+		return 0; /* NS bogus, all bogus, nothing found */
+	for(a=dp->result_list; a; a = a->next_result) {
+		a->sel_rtt = iter_filter_unsuitable(iter_env, env, 
+			name, namelen, qtype, now, a);
+		if(a->sel_rtt != -1) {
+			if(sock_list_find(blacklist, &a->addr, a->addrlen))
+				a->sel_rtt += BLACKLIST_PENALTY;
+
+			if(!got_it) {
+				*best_rtt = a->sel_rtt;
+				got_it = 1;
+			} else if(a->sel_rtt < *best_rtt) {
+				*best_rtt = a->sel_rtt;
+			}
+		}
+	}
+	return got_it;
+}
+
+/** filter the addres list, putting best targets at front,
+ * returns number of best targets (or 0, no suitable targets) */
+static int
+iter_filter_order(struct iter_env* iter_env, struct module_env* env,
+	uint8_t* name, size_t namelen, uint16_t qtype, uint32_t now, 
+	struct delegpt* dp, int* selected_rtt, int open_target, 
+	struct sock_list* blacklist)
+{
+	int got_num = 0, low_rtt = 0, swap_to_front;
+	struct delegpt_addr* a, *n, *prev=NULL;
+
+	/* fillup sel_rtt and find best rtt in the bunch */
+	got_num = iter_fill_rtt(iter_env, env, name, namelen, qtype, now, dp, 
+		&low_rtt, blacklist);
+	if(got_num == 0) 
+		return 0;
+	if(low_rtt >= USEFUL_SERVER_TOP_TIMEOUT &&
+		(delegpt_count_missing_targets(dp) > 0 || open_target > 0)) {
+		verbose(VERB_ALGO, "Bad choices, trying to get more choice");
+		return 0; /* we want more choice. The best choice is a bad one.
+			     return 0 to force the caller to fetch more */
+	}
+
+	got_num = 0;
+	a = dp->result_list;
+	while(a) {
+		/* skip unsuitable targets */
+		if(a->sel_rtt == -1) {
+			prev = a;
+			a = a->next_result;
+			continue;
+		}
+		/* classify the server address and determine what to do */
+		swap_to_front = 0;
+		if(a->sel_rtt >= low_rtt && a->sel_rtt - low_rtt <= RTT_BAND) {
+			got_num++;
+			swap_to_front = 1;
+		} else if(a->sel_rtt<low_rtt && low_rtt-a->sel_rtt<=RTT_BAND) {
+			got_num++;
+			swap_to_front = 1;
+		}
+		/* swap to front if necessary, or move to next result */
+		if(swap_to_front && prev) {
+			n = a->next_result;
+			prev->next_result = n;
+			a->next_result = dp->result_list;
+			dp->result_list = a;
+			a = n;
+		} else {
+			prev = a;
+			a = a->next_result;
+		}
+	}
+	*selected_rtt = low_rtt;
+	return got_num;
+}
+
+struct delegpt_addr* 
+iter_server_selection(struct iter_env* iter_env, 
+	struct module_env* env, struct delegpt* dp, 
+	uint8_t* name, size_t namelen, uint16_t qtype, int* dnssec_lame,
+	int* chase_to_rd, int open_target, struct sock_list* blacklist)
+{
+	int sel;
+	int selrtt;
+	struct delegpt_addr* a, *prev;
+	int num = iter_filter_order(iter_env, env, name, namelen, qtype,
+		*env->now, dp, &selrtt, open_target, blacklist);
+
+	if(num == 0)
+		return NULL;
+	verbose(VERB_ALGO, "selrtt %d", selrtt);
+	if(selrtt > BLACKLIST_PENALTY) {
+		if(selrtt-BLACKLIST_PENALTY > USEFUL_SERVER_TOP_TIMEOUT*3) {
+			verbose(VERB_ALGO, "chase to "
+				"blacklisted recursion lame server");
+			*chase_to_rd = 1;
+		}
+		if(selrtt-BLACKLIST_PENALTY > USEFUL_SERVER_TOP_TIMEOUT*2) {
+			verbose(VERB_ALGO, "chase to "
+				"blacklisted dnssec lame server");
+			*dnssec_lame = 1;
+		}
+	} else {
+		if(selrtt > USEFUL_SERVER_TOP_TIMEOUT*3) {
+			verbose(VERB_ALGO, "chase to recursion lame server");
+			*chase_to_rd = 1;
+		}
+		if(selrtt > USEFUL_SERVER_TOP_TIMEOUT*2) {
+			verbose(VERB_ALGO, "chase to dnssec lame server");
+			*dnssec_lame = 1;
+		}
+		if(selrtt == USEFUL_SERVER_TOP_TIMEOUT) {
+			verbose(VERB_ALGO, "chase to blacklisted lame server");
+			return NULL;
+		}
+	}
+
+	if(num == 1) {
+		a = dp->result_list;
+		if(++a->attempts < OUTBOUND_MSG_RETRY)
+			return a;
+		dp->result_list = a->next_result;
+		return a;
+	}
+
+	/* randomly select a target from the list */
+	log_assert(num > 1);
+	/* grab secure random number, to pick unexpected server.
+	 * also we need it to be threadsafe. */
+	sel = ub_random_max(env->rnd, num); 
+	a = dp->result_list;
+	prev = NULL;
+	while(sel > 0 && a) {
+		prev = a;
+		a = a->next_result;
+		sel--;
+	}
+	if(!a)  /* robustness */
+		return NULL;
+	if(++a->attempts < OUTBOUND_MSG_RETRY)
+		return a;
+	/* remove it from the delegation point result list */
+	if(prev)
+		prev->next_result = a->next_result;
+	else	dp->result_list = a->next_result;
+	return a;
+}
+
+struct dns_msg* 
+dns_alloc_msg(ldns_buffer* pkt, struct msg_parse* msg, 
+	struct regional* region)
+{
+	struct dns_msg* m = (struct dns_msg*)regional_alloc(region,
+		sizeof(struct dns_msg));
+	if(!m)
+		return NULL;
+	memset(m, 0, sizeof(*m));
+	if(!parse_create_msg(pkt, msg, NULL, &m->qinfo, &m->rep, region)) {
+		log_err("malloc failure: allocating incoming dns_msg");
+		return NULL;
+	}
+	return m;
+}
+
+struct dns_msg* 
+dns_copy_msg(struct dns_msg* from, struct regional* region)
+{
+	struct dns_msg* m = (struct dns_msg*)regional_alloc(region,
+		sizeof(struct dns_msg));
+	if(!m)
+		return NULL;
+	m->qinfo = from->qinfo;
+	if(!(m->qinfo.qname = regional_alloc_init(region, from->qinfo.qname,
+		from->qinfo.qname_len)))
+		return NULL;
+	if(!(m->rep = reply_info_copy(from->rep, NULL, region)))
+		return NULL;
+	return m;
+}
+
+int 
+iter_dns_store(struct module_env* env, struct query_info* msgqinf,
+	struct reply_info* msgrep, int is_referral, uint32_t leeway,
+	struct regional* region)
+{
+	return dns_cache_store(env, msgqinf, msgrep, is_referral, leeway,
+		region);
+}
+
+int 
+iter_ns_probability(struct ub_randstate* rnd, int n, int m)
+{
+	int sel;
+	if(n == m) /* 100% chance */
+		return 1;
+	/* we do not need secure random numbers here, but
+	 * we do need it to be threadsafe, so we use this */
+	sel = ub_random_max(rnd, m); 
+	return (sel < n);
+}
+
+/** detect dependency cycle for query and target */
+static int
+causes_cycle(struct module_qstate* qstate, uint8_t* name, size_t namelen,
+	uint16_t t, uint16_t c)
+{
+	struct query_info qinf;
+	qinf.qname = name;
+	qinf.qname_len = namelen;
+	qinf.qtype = t;
+	qinf.qclass = c;
+	fptr_ok(fptr_whitelist_modenv_detect_cycle(
+		qstate->env->detect_cycle));
+	return (*qstate->env->detect_cycle)(qstate, &qinf, 
+		(uint16_t)(BIT_RD|BIT_CD), qstate->is_priming);
+}
+
+void 
+iter_mark_cycle_targets(struct module_qstate* qstate, struct delegpt* dp)
+{
+	struct delegpt_ns* ns;
+	for(ns = dp->nslist; ns; ns = ns->next) {
+		if(ns->resolved)
+			continue;
+		/* see if this ns as target causes dependency cycle */
+		if(causes_cycle(qstate, ns->name, ns->namelen, 
+			LDNS_RR_TYPE_AAAA, qstate->qinfo.qclass) ||
+		   causes_cycle(qstate, ns->name, ns->namelen, 
+			LDNS_RR_TYPE_A, qstate->qinfo.qclass)) {
+			log_nametypeclass(VERB_QUERY, "skipping target due "
+			 	"to dependency cycle (harden-glue: no may "
+				"fix some of the cycles)", 
+				ns->name, LDNS_RR_TYPE_A, 
+				qstate->qinfo.qclass);
+			ns->resolved = 1;
+		}
+	}
+}
+
+void 
+iter_mark_pside_cycle_targets(struct module_qstate* qstate, struct delegpt* dp)
+{
+	struct delegpt_ns* ns;
+	for(ns = dp->nslist; ns; ns = ns->next) {
+		if(ns->done_pside4 && ns->done_pside6)
+			continue;
+		/* see if this ns as target causes dependency cycle */
+		if(causes_cycle(qstate, ns->name, ns->namelen, 
+			LDNS_RR_TYPE_A, qstate->qinfo.qclass)) {
+			log_nametypeclass(VERB_QUERY, "skipping target due "
+			 	"to dependency cycle", ns->name,
+				LDNS_RR_TYPE_A, qstate->qinfo.qclass);
+			ns->done_pside4 = 1;
+		}
+		if(causes_cycle(qstate, ns->name, ns->namelen, 
+			LDNS_RR_TYPE_AAAA, qstate->qinfo.qclass)) {
+			log_nametypeclass(VERB_QUERY, "skipping target due "
+			 	"to dependency cycle", ns->name,
+				LDNS_RR_TYPE_AAAA, qstate->qinfo.qclass);
+			ns->done_pside6 = 1;
+		}
+	}
+}
+
+int 
+iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags, 
+	struct delegpt* dp)
+{
+	struct delegpt_ns* ns;
+	/* check:
+	 *      o RD qflag is on.
+	 *      o no addresses are provided.
+	 *      o all NS items are required glue.
+	 * OR
+	 *      o RD qflag is on.
+	 *      o no addresses are provided.
+	 *      o the query is for one of the nameservers in dp,
+	 *        and that nameserver is a glue-name for this dp.
+	 */
+	if(!(qflags&BIT_RD))
+		return 0;
+	/* either available or unused targets */
+	if(dp->usable_list || dp->result_list)
+		return 0;
+	
+	/* see if query is for one of the nameservers, which is glue */
+	if( (qinfo->qtype == LDNS_RR_TYPE_A ||
+		qinfo->qtype == LDNS_RR_TYPE_AAAA) &&
+		dname_subdomain_c(qinfo->qname, dp->name) &&
+		delegpt_find_ns(dp, qinfo->qname, qinfo->qname_len))
+		return 1;
+	
+	for(ns = dp->nslist; ns; ns = ns->next) {
+		if(ns->resolved) /* skip failed targets */
+			continue;
+		if(!dname_subdomain_c(ns->name, dp->name))
+			return 0; /* one address is not required glue */
+	}
+	return 1;
+}
+
+int 
+iter_indicates_dnssec(struct module_env* env, struct delegpt* dp,
+        struct dns_msg* msg, uint16_t dclass)
+{
+	struct trust_anchor* a;
+	/* information not available, !env->anchors can be common */
+	if(!env || !env->anchors || !dp || !dp->name)
+		return 0;
+	/* a trust anchor exists with this name, RRSIGs expected */
+	if((a=anchor_find(env->anchors, dp->name, dp->namelabs, dp->namelen,
+		dclass))) {
+		lock_basic_unlock(&a->lock);
+		return 1;
+	}
+	/* see if DS rrset was given, in AUTH section */
+	if(msg && msg->rep &&
+		reply_find_rrset_section_ns(msg->rep, dp->name, dp->namelen,
+		LDNS_RR_TYPE_DS, dclass))
+		return 1;
+	/* look in key cache */
+	if(env->key_cache) {
+		struct key_entry_key* kk = key_cache_obtain(env->key_cache,
+			dp->name, dp->namelen, dclass, env->scratch, *env->now);
+		if(kk) {
+			if(query_dname_compare(kk->name, dp->name) == 0) {
+			  if(key_entry_isgood(kk) || key_entry_isbad(kk)) {
+				regional_free_all(env->scratch);
+				return 1;
+			  } else if(key_entry_isnull(kk)) {
+				regional_free_all(env->scratch);
+				return 0;
+			  }
+			}
+			regional_free_all(env->scratch);
+		}
+	}
+	return 0;
+}
+
+int 
+iter_msg_has_dnssec(struct dns_msg* msg)
+{
+	size_t i;
+	if(!msg || !msg->rep)
+		return 0;
+	for(i=0; i<msg->rep->an_numrrsets + msg->rep->ns_numrrsets; i++) {
+		if(((struct packed_rrset_data*)msg->rep->rrsets[i]->
+			entry.data)->rrsig_count > 0)
+			return 1;
+	}
+	/* empty message has no DNSSEC info, with DNSSEC the reply is
+	 * not empty (NSEC) */
+	return 0;
+}
+
+int iter_msg_from_zone(struct dns_msg* msg, struct delegpt* dp,
+        enum response_type type, uint16_t dclass)
+{
+	if(!msg || !dp || !msg->rep || !dp->name)
+		return 0;
+	/* SOA RRset - always from reply zone */
+	if(reply_find_rrset_section_an(msg->rep, dp->name, dp->namelen,
+		LDNS_RR_TYPE_SOA, dclass) ||
+	   reply_find_rrset_section_ns(msg->rep, dp->name, dp->namelen,
+		LDNS_RR_TYPE_SOA, dclass))
+		return 1;
+	if(type == RESPONSE_TYPE_REFERRAL) {
+		size_t i;
+		/* if it adds a single label, i.e. we expect .com,
+		 * and referral to example.com. NS ... , then origin zone
+		 * is .com. For a referral to sub.example.com. NS ... then
+		 * we do not know, since example.com. may be in between. */
+		for(i=0; i<msg->rep->an_numrrsets+msg->rep->ns_numrrsets; 
+			i++) {
+			struct ub_packed_rrset_key* s = msg->rep->rrsets[i];
+			if(ntohs(s->rk.type) == LDNS_RR_TYPE_NS &&
+				ntohs(s->rk.rrset_class) == dclass) {
+				int l = dname_count_labels(s->rk.dname);
+				if(l == dp->namelabs + 1 &&
+					dname_strict_subdomain(s->rk.dname,
+					l, dp->name, dp->namelabs))
+					return 1;
+			}
+		}
+		return 0;
+	}
+	log_assert(type==RESPONSE_TYPE_ANSWER || type==RESPONSE_TYPE_CNAME);
+	/* not a referral, and not lame delegation (upwards), so, 
+	 * any NS rrset must be from the zone itself */
+	if(reply_find_rrset_section_an(msg->rep, dp->name, dp->namelen,
+		LDNS_RR_TYPE_NS, dclass) ||
+	   reply_find_rrset_section_ns(msg->rep, dp->name, dp->namelen,
+		LDNS_RR_TYPE_NS, dclass))
+		return 1;
+	/* a DNSKEY set is expected at the zone apex as well */
+	/* this is for 'minimal responses' for DNSKEYs */
+	if(reply_find_rrset_section_an(msg->rep, dp->name, dp->namelen,
+		LDNS_RR_TYPE_DNSKEY, dclass))
+		return 1;
+	return 0;
+}
+
+/**
+ * check equality of two rrsets 
+ * @param k1: rrset
+ * @param k2: rrset
+ * @return true if equal
+ */
+static int
+rrset_equal(struct ub_packed_rrset_key* k1, struct ub_packed_rrset_key* k2)
+{
+	struct packed_rrset_data* d1 = (struct packed_rrset_data*)
+		k1->entry.data;
+	struct packed_rrset_data* d2 = (struct packed_rrset_data*)
+		k2->entry.data;
+	size_t i, t;
+	if(k1->rk.dname_len != k2->rk.dname_len ||
+		k1->rk.flags != k2->rk.flags ||
+		k1->rk.type != k2->rk.type ||
+		k1->rk.rrset_class != k2->rk.rrset_class ||
+		query_dname_compare(k1->rk.dname, k2->rk.dname) != 0)
+		return 0;
+	if(d1->ttl != d2->ttl ||
+		d1->count != d2->count ||
+		d1->rrsig_count != d2->rrsig_count ||
+		d1->trust != d2->trust ||
+		d1->security != d2->security)
+		return 0;
+	t = d1->count + d1->rrsig_count;
+	for(i=0; i<t; i++) {
+		if(d1->rr_len[i] != d2->rr_len[i] ||
+			d1->rr_ttl[i] != d2->rr_ttl[i] ||
+			memcmp(d1->rr_data[i], d2->rr_data[i], 
+				d1->rr_len[i]) != 0)
+			return 0;
+	}
+	return 1;
+}
+
+int 
+reply_equal(struct reply_info* p, struct reply_info* q, ldns_buffer* scratch)
+{
+	size_t i;
+	if(p->flags != q->flags ||
+		p->qdcount != q->qdcount ||
+		p->ttl != q->ttl ||
+		p->prefetch_ttl != q->prefetch_ttl ||
+		p->security != q->security ||
+		p->an_numrrsets != q->an_numrrsets ||
+		p->ns_numrrsets != q->ns_numrrsets ||
+		p->ar_numrrsets != q->ar_numrrsets ||
+		p->rrset_count != q->rrset_count)
+		return 0;
+	for(i=0; i<p->rrset_count; i++) {
+		if(!rrset_equal(p->rrsets[i], q->rrsets[i])) {
+			/* fallback procedure: try to sort and canonicalize */
+			ldns_rr_list* pl, *ql;
+			pl = packed_rrset_to_rr_list(p->rrsets[i], scratch);
+			ql = packed_rrset_to_rr_list(q->rrsets[i], scratch);
+			if(!pl || !ql) {
+				ldns_rr_list_deep_free(pl);
+				ldns_rr_list_deep_free(ql);
+				return 0;
+			}
+			ldns_rr_list2canonical(pl);
+			ldns_rr_list2canonical(ql);
+			ldns_rr_list_sort(pl);
+			ldns_rr_list_sort(ql);
+			if(ldns_rr_list_compare(pl, ql) != 0) {
+				ldns_rr_list_deep_free(pl);
+				ldns_rr_list_deep_free(ql);
+				return 0;
+			}
+			ldns_rr_list_deep_free(pl);
+			ldns_rr_list_deep_free(ql);
+			continue;
+		}
+	}
+	return 1;
+}
+
+void 
+iter_store_parentside_rrset(struct module_env* env, 
+	struct ub_packed_rrset_key* rrset)
+{
+	struct rrset_ref ref;
+	rrset = packed_rrset_copy_alloc(rrset, env->alloc, *env->now);
+	if(!rrset) {
+		log_err("malloc failure in store_parentside_rrset");
+		return;
+	}
+	rrset->rk.flags |= PACKED_RRSET_PARENT_SIDE;
+	rrset->entry.hash = rrset_key_hash(&rrset->rk);
+	ref.key = rrset;
+	ref.id = rrset->id;
+	/* ignore ret: if it was in the cache, ref updated */
+	(void)rrset_cache_update(env->rrset_cache, &ref, env->alloc, *env->now);
+}
+
+/** fetch NS record from reply, if any */
+static struct ub_packed_rrset_key*
+reply_get_NS_rrset(struct reply_info* rep)
+{
+	size_t i;
+	for(i=0; i<rep->rrset_count; i++) {
+		if(rep->rrsets[i]->rk.type == htons(LDNS_RR_TYPE_NS)) {
+			return rep->rrsets[i];
+		}
+	}
+	return NULL;
+}
+
+void
+iter_store_parentside_NS(struct module_env* env, struct reply_info* rep)
+{
+	struct ub_packed_rrset_key* rrset = reply_get_NS_rrset(rep);
+	if(rrset) {
+		log_rrset_key(VERB_ALGO, "store parent-side NS", rrset);
+		iter_store_parentside_rrset(env, rrset);
+	}
+}
+
+void iter_store_parentside_neg(struct module_env* env, 
+        struct query_info* qinfo, struct reply_info* rep)
+{
+	/* TTL: NS from referral in iq->deleg_msg,
+	 *      or first RR from iq->response,
+	 *      or servfail5secs if !iq->response */ 
+	uint32_t ttl = NORR_TTL;
+	struct ub_packed_rrset_key* neg;
+	struct packed_rrset_data* newd;
+	if(rep) {
+		struct ub_packed_rrset_key* rrset = reply_get_NS_rrset(rep);
+		if(!rrset && rep->rrset_count != 0) rrset = rep->rrsets[0];
+		if(rrset) ttl = ub_packed_rrset_ttl(rrset);
+	}
+	/* create empty rrset to store */
+	neg = (struct ub_packed_rrset_key*)regional_alloc(env->scratch,
+	                sizeof(struct ub_packed_rrset_key));
+	if(!neg) {
+		log_err("out of memory in store_parentside_neg");
+		return;
+	}
+	memset(&neg->entry, 0, sizeof(neg->entry));
+	neg->entry.key = neg;
+	neg->rk.type = htons(qinfo->qtype);
+	neg->rk.rrset_class = htons(qinfo->qclass);
+	neg->rk.flags = 0;
+	neg->rk.dname = regional_alloc_init(env->scratch, qinfo->qname, 
+		qinfo->qname_len);
+	if(!neg->rk.dname) {
+		log_err("out of memory in store_parentside_neg");
+		return;
+	}
+	neg->rk.dname_len = qinfo->qname_len;
+	neg->entry.hash = rrset_key_hash(&neg->rk);
+	newd = (struct packed_rrset_data*)regional_alloc_zero(env->scratch, 
+		sizeof(struct packed_rrset_data) + sizeof(size_t) +
+		sizeof(uint8_t*) + sizeof(uint32_t) + sizeof(uint16_t));
+	if(!newd) {
+		log_err("out of memory in store_parentside_neg");
+		return;
+	}
+	neg->entry.data = newd;
+	newd->ttl = ttl;
+	/* entry must have one RR, otherwise not valid in cache.
+	 * put in one RR with empty rdata: those are ignored as nameserver */
+	newd->count = 1;
+	newd->rrsig_count = 0;
+	newd->trust = rrset_trust_ans_noAA;
+	newd->rr_len = (size_t*)((uint8_t*)newd +
+		sizeof(struct packed_rrset_data));
+	newd->rr_len[0] = 0 /* zero len rdata */ + sizeof(uint16_t);
+	packed_rrset_ptr_fixup(newd);
+	newd->rr_ttl[0] = newd->ttl;
+	ldns_write_uint16(newd->rr_data[0], 0 /* zero len rdata */);
+	/* store it */
+	log_rrset_key(VERB_ALGO, "store parent-side negative", neg);
+	iter_store_parentside_rrset(env, neg);
+}
+
+int 
+iter_lookup_parent_NS_from_cache(struct module_env* env, struct delegpt* dp,
+	struct regional* region, struct query_info* qinfo)
+{
+	struct ub_packed_rrset_key* akey;
+	akey = rrset_cache_lookup(env->rrset_cache, dp->name, 
+		dp->namelen, LDNS_RR_TYPE_NS, qinfo->qclass, 
+		PACKED_RRSET_PARENT_SIDE, *env->now, 0);
+	if(akey) {
+		log_rrset_key(VERB_ALGO, "found parent-side NS in cache", akey);
+		dp->has_parent_side_NS = 1;
+		/* and mark the new names as lame */
+		if(!delegpt_rrset_add_ns(dp, region, akey, 1)) {
+			lock_rw_unlock(&akey->entry.lock);
+			return 0;
+		}
+		lock_rw_unlock(&akey->entry.lock);
+	}
+	return 1;
+}
+
+int iter_lookup_parent_glue_from_cache(struct module_env* env,
+        struct delegpt* dp, struct regional* region, struct query_info* qinfo)
+{
+	struct ub_packed_rrset_key* akey;
+	struct delegpt_ns* ns;
+	size_t num = delegpt_count_targets(dp);
+	for(ns = dp->nslist; ns; ns = ns->next) {
+		/* get cached parentside A */
+		akey = rrset_cache_lookup(env->rrset_cache, ns->name, 
+			ns->namelen, LDNS_RR_TYPE_A, qinfo->qclass, 
+			PACKED_RRSET_PARENT_SIDE, *env->now, 0);
+		if(akey) {
+			log_rrset_key(VERB_ALGO, "found parent-side", akey);
+			ns->done_pside4 = 1;
+			/* a negative-cache-element has no addresses it adds */
+			if(!delegpt_add_rrset_A(dp, region, akey, 1))
+				log_err("malloc failure in lookup_parent_glue");
+			lock_rw_unlock(&akey->entry.lock);
+		}
+		/* get cached parentside AAAA */
+		akey = rrset_cache_lookup(env->rrset_cache, ns->name, 
+			ns->namelen, LDNS_RR_TYPE_AAAA, qinfo->qclass, 
+			PACKED_RRSET_PARENT_SIDE, *env->now, 0);
+		if(akey) {
+			log_rrset_key(VERB_ALGO, "found parent-side", akey);
+			ns->done_pside6 = 1;
+			/* a negative-cache-element has no addresses it adds */
+			if(!delegpt_add_rrset_AAAA(dp, region, akey, 1))
+				log_err("malloc failure in lookup_parent_glue");
+			lock_rw_unlock(&akey->entry.lock);
+		}
+	}
+	/* see if new (but lame) addresses have become available */
+	return delegpt_count_targets(dp) != num;
+}
+
+int 
+iter_get_next_root(struct iter_hints* hints, struct iter_forwards* fwd, 
+	uint16_t* c)
+{
+	uint16_t c1 = *c, c2 = *c;
+	int r1 = hints_next_root(hints, &c1);
+	int r2 = forwards_next_root(fwd, &c2);
+	if(!r1 && !r2) /* got none, end of list */
+		return 0;
+	else if(!r1) /* got one, return that */
+		*c = c2;
+	else if(!r2)
+		*c = c1;
+	else if(c1 < c2) /* got both take smallest */
+		*c = c1;
+	else	*c = c2;
+	return 1;
+}
+
+void
+iter_scrub_ds(struct dns_msg* msg, struct ub_packed_rrset_key* ns, uint8_t* z)
+{
+	/* Only the DS record for the delegation itself is expected.
+	 * We allow DS for everything between the bailiwick and the 
+	 * zonecut, thus DS records must be at or above the zonecut.
+	 * And the DS records must be below the server authority zone.
+	 * The answer section is already scrubbed. */
+	size_t i = msg->rep->an_numrrsets;
+	while(i < (msg->rep->an_numrrsets + msg->rep->ns_numrrsets)) {
+		struct ub_packed_rrset_key* s = msg->rep->rrsets[i];
+		if(ntohs(s->rk.type) == LDNS_RR_TYPE_DS &&
+			(!ns || !dname_subdomain_c(ns->rk.dname, s->rk.dname)
+			|| query_dname_compare(z, s->rk.dname) == 0)) {
+			log_nametypeclass(VERB_ALGO, "removing irrelevant DS",
+				s->rk.dname, ntohs(s->rk.type),
+				ntohs(s->rk.rrset_class));
+			memmove(msg->rep->rrsets+i, msg->rep->rrsets+i+1,
+				sizeof(struct ub_packed_rrset_key*) * 
+				(msg->rep->rrset_count-i-1));
+			msg->rep->ns_numrrsets--;
+			msg->rep->rrset_count--;
+			/* stay at same i, but new record */
+			continue;
+		}
+		i++;
+	}
+}
+
+void iter_dec_attempts(struct delegpt* dp, int d)
+{
+	struct delegpt_addr* a;
+	for(a=dp->target_list; a; a = a->next_target) {
+		if(a->attempts >= OUTBOUND_MSG_RETRY) {
+			/* add back to result list */
+			a->next_result = dp->result_list;
+			dp->result_list = a;
+		}
+		if(a->attempts > d)
+			a->attempts -= d;
+		else a->attempts = 0;
+	}
+}
+
+void iter_merge_retry_counts(struct delegpt* dp, struct delegpt* old)
+{
+	struct delegpt_addr* a, *o, *prev;
+	for(a=dp->target_list; a; a = a->next_target) {
+		o = delegpt_find_addr(old, &a->addr, a->addrlen);
+		if(o) {
+			log_addr(VERB_ALGO, "copy attempt count previous dp",
+				&a->addr, a->addrlen);
+			a->attempts = o->attempts;
+		}
+	}
+	prev = NULL;
+	a = dp->usable_list;
+	while(a) {
+		if(a->attempts >= OUTBOUND_MSG_RETRY) {
+			log_addr(VERB_ALGO, "remove from usable list dp",
+				&a->addr, a->addrlen);
+			/* remove from result list */
+			if(prev)
+				prev->next_usable = a->next_usable;
+			else	dp->usable_list = a->next_usable;
+			/* prev stays the same */
+			a = a->next_usable;
+			continue;
+		}
+		prev = a;
+		a = a->next_usable;
+	}
+}
diff --git a/3rdParty/Unbound/src/src/iterator/iter_utils.h b/3rdParty/Unbound/src/src/iterator/iter_utils.h
new file mode 100644
index 0000000..6d2f862
--- /dev/null
+++ b/3rdParty/Unbound/src/src/iterator/iter_utils.h
@@ -0,0 +1,312 @@
+/*
+ * iterator/iter_utils.h - iterative resolver module utility functions.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to assist the iterator module.
+ * Configuration options. Forward zones. 
+ */
+
+#ifndef ITERATOR_ITER_UTILS_H
+#define ITERATOR_ITER_UTILS_H
+#include "iterator/iter_resptype.h"
+#include <ldns/buffer.h>
+struct iter_env;
+struct iter_hints;
+struct iter_forwards;
+struct config_file;
+struct module_env;
+struct delegpt_addr;
+struct delegpt;
+struct regional;
+struct msg_parse;
+struct ub_randstate;
+struct query_info;
+struct reply_info;
+struct module_qstate;
+struct sock_list;
+struct ub_packed_rrset_key;
+
+/**
+ * Process config options and set iterator module state.
+ * Sets default values if no config is found.
+ * @param iter_env: iterator module state.
+ * @param cfg: config options.
+ * @return 0 on error.
+ */
+int iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg);
+
+/**
+ * Select a valid, nice target to send query to.
+ * Sorting and removing unsuitable targets is combined.
+ *
+ * @param iter_env: iterator module global state, with ip6 enabled and 
+ *	do-not-query-addresses.
+ * @param env: environment with infra cache (lameness, rtt info).
+ * @param dp: delegation point with result list.
+ * @param name: zone name (for lameness check).
+ * @param namelen: length of name.
+ * @param qtype: query type that we want to send.
+ * @param dnssec_lame: set to 1, if a known dnssec-lame server is selected
+ *	these are not preferred, but are used as a last resort.
+ * @param chase_to_rd: set to 1 if a known recursion lame server is selected
+ * 	these are not preferred, but are used as a last resort.
+ * @param open_target: number of currently outstanding target queries.
+ * 	If we wait for these, perhaps more server addresses become available.
+ * @param blacklist: the IP blacklist to use.
+ * @return best target or NULL if no target.
+ *	if not null, that target is removed from the result list in the dp.
+ */
+struct delegpt_addr* iter_server_selection(struct iter_env* iter_env, 
+	struct module_env* env, struct delegpt* dp, uint8_t* name, 
+	size_t namelen, uint16_t qtype, int* dnssec_lame,
+	int* chase_to_rd, int open_target, struct sock_list* blacklist);
+
+/**
+ * Allocate dns_msg from parsed msg, in regional.
+ * @param pkt: packet.
+ * @param msg: parsed message (cleaned and ready for regional allocation).
+ * @param regional: regional to use for allocation.
+ * @return newly allocated dns_msg, or NULL on memory error.
+ */
+struct dns_msg* dns_alloc_msg(ldns_buffer* pkt, struct msg_parse* msg, 
+	struct regional* regional);
+
+/**
+ * Copy a dns_msg to this regional.
+ * @param from: dns message, also in regional.
+ * @param regional: regional to use for allocation.
+ * @return newly allocated dns_msg, or NULL on memory error.
+ */
+struct dns_msg* dns_copy_msg(struct dns_msg* from, struct regional* regional);
+
+/**
+ * Allocate a dns_msg with malloc/alloc structure and store in dns cache.
+ * @param env: environment, with alloc structure and dns cache.
+ * @param qinf: query info, the query for which answer is stored.
+ * @param rep: reply in dns_msg from dns_alloc_msg for example.
+ * @param is_referral: If true, then the given message to be stored is a
+ *	referral. The cache implementation may use this as a hint.
+ * @param leeway: prefetch TTL leeway to expire old rrsets quicker.
+ * @param region: to copy modified (cache is better) rrs back to.
+ * @return 0 on alloc error (out of memory).
+ */
+int iter_dns_store(struct module_env* env, struct query_info* qinf,
+	struct reply_info* rep, int is_referral, uint32_t leeway,
+	struct regional* region);
+
+/**
+ * Select randomly with n/m probability.
+ * For shuffle NS records for address fetching.
+ * @param rnd: random table
+ * @param n: probability.
+ * @param m: divisor for probability.
+ * @return true with n/m probability.
+ */
+int iter_ns_probability(struct ub_randstate* rnd, int n, int m);
+
+/**
+ * Mark targets that result in a dependency cycle as done, so they
+ * will not get selected as targets.
+ * @param qstate: query state.
+ * @param dp: delegpt to mark ns in.
+ */
+void iter_mark_cycle_targets(struct module_qstate* qstate, struct delegpt* dp);
+
+/**
+ * Mark targets that result in a dependency cycle as done, so they
+ * will not get selected as targets.  For the parent-side lookups.
+ * @param qstate: query state.
+ * @param dp: delegpt to mark ns in.
+ */
+void iter_mark_pside_cycle_targets(struct module_qstate* qstate,
+	struct delegpt* dp);
+
+/**
+ * See if delegation is useful or offers immediately no targets for 
+ * further recursion.
+ * @param qinfo: query name and type
+ * @param qflags: query flags with RD flag
+ * @param dp: delegpt to check.
+ * @return true if dp is useless.
+ */
+int iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags, 
+	struct delegpt* dp);
+
+/**
+ * See if delegation is expected to have DNSSEC information (RRSIGs) in 
+ * its answers, or not. Inspects delegation point (name), trust anchors,
+ * and delegation message (DS RRset) to determine this.
+ * @param env: module env with trust anchors.
+ * @param dp: delegation point.
+ * @param msg: delegation message, with DS if a secure referral.
+ * @param dclass: class of query.
+ * @return 1 if dnssec is expected, 0 if not.
+ */
+int iter_indicates_dnssec(struct module_env* env, struct delegpt* dp,
+	struct dns_msg* msg, uint16_t dclass);
+
+/**
+ * See if a message contains DNSSEC.
+ * This is examined by looking for RRSIGs. With DNSSEC a valid answer, 
+ * nxdomain, nodata, referral or cname reply has RRSIGs in answer or auth 
+ * sections, sigs on answer data, SOA, DS, or NSEC/NSEC3 records.
+ * @param msg: message to examine.
+ * @return true if DNSSEC information was found.
+ */
+int iter_msg_has_dnssec(struct dns_msg* msg);
+
+/**
+ * See if a message is known to be from a certain zone.
+ * This looks for SOA or NS rrsets, for answers.
+ * For referrals, when one label is delegated, the zone is detected.
+ * Does not look at signatures.
+ * @param msg: the message to inspect.
+ * @param dp: delegation point with zone name to look for.
+ * @param type: type of message.
+ * @param dclass: class of query.
+ * @return true if message is certain to be from zone in dp->name.
+ *	false if not sure (empty msg), or not from the zone.
+ */
+int iter_msg_from_zone(struct dns_msg* msg, struct delegpt* dp, 
+	enum response_type type, uint16_t dclass);
+
+/**
+ * Check if two replies are equal
+ * For fallback procedures
+ * @param p: reply one. The reply has rrset data pointers in region.
+ * 	Does not check rrset-IDs
+ * @param q: reply two
+ * @param buf: scratch buffer.
+ * @return if one and two are equal.
+ */
+int reply_equal(struct reply_info* p, struct reply_info* q, ldns_buffer* buf);
+
+/**
+ * Store parent-side rrset in seperate rrset cache entries for later 
+ * last-resort * lookups in case the child-side versions of this information 
+ * fails.
+ * @param env: environment with cache, time, ...
+ * @param rrset: the rrset to store (copied).
+ * Failure to store is logged, but otherwise ignored.
+ */
+void iter_store_parentside_rrset(struct module_env* env, 
+	struct ub_packed_rrset_key* rrset);
+
+/**
+ * Store parent-side NS records from a referral message
+ * @param env: environment with cache, time, ...
+ * @param rep: response with NS rrset.
+ * Failure to store is logged, but otherwise ignored.
+ */
+void iter_store_parentside_NS(struct module_env* env, struct reply_info* rep);
+
+/**
+ * Store parent-side negative element, the parentside rrset does not exist,
+ * creates an rrset with empty rdata in the rrset cache with PARENTSIDE flag.
+ * @param env: environment with cache, time, ...
+ * @param qinfo: the identity of the rrset that is missing.
+ * @param rep: delegation response or answer response, to glean TTL from.
+ * (malloc) failure is logged but otherwise ignored.
+ */
+void iter_store_parentside_neg(struct module_env* env, 
+	struct query_info* qinfo, struct reply_info* rep);
+
+/**
+ * Add parent NS record if that exists in the cache.  This is both new
+ * information and acts like a timeout throttle on retries.
+ * @param env: query env with rrset cache and time.
+ * @param dp: delegation point to store result in.  Also this dp is used to
+ *	see which NS name is needed.
+ * @param region: region to alloc result in.
+ * @param qinfo: pertinent information, the qclass.
+ * @return false on malloc failure.
+ *	if true, the routine worked and if such cached information 
+ *	existed dp->has_parent_side_NS is set true.
+ */
+int iter_lookup_parent_NS_from_cache(struct module_env* env,
+	struct delegpt* dp, struct regional* region, struct query_info* qinfo);
+
+/**
+ * Add parent-side glue if that exists in the cache.  This is both new
+ * information and acts like a timeout throttle on retries to fetch them.
+ * @param env: query env with rrset cache and time.
+ * @param dp: delegation point to store result in.  Also this dp is used to
+ *	see which NS name is needed.
+ * @param region: region to alloc result in.
+ * @param qinfo: pertinent information, the qclass.
+ * @return: true, it worked, no malloc failures, and new addresses (lame)
+ *	have been added, giving extra options as query targets.
+ */
+int iter_lookup_parent_glue_from_cache(struct module_env* env,
+	struct delegpt* dp, struct regional* region, struct query_info* qinfo);
+
+/**
+ * Lookup next root-hint or root-forward entry.
+ * @param hints: the hints.
+ * @param fwd: the forwards.
+ * @param c: the class to start searching at. 0 means find first one.
+ * @return false if no classes found, true if found and returned in c.
+ */
+int iter_get_next_root(struct iter_hints* hints, struct iter_forwards* fwd,
+	uint16_t* c);
+
+/**
+ * Remove DS records that are inappropriate before they are cached.
+ * @param msg: the response to scrub.
+ * @param ns: RRSET that is the NS record for the referral.
+ * 	if NULL, then all DS records are removed from the authority section.
+ * @param z: zone name that the response is from.
+ */
+void iter_scrub_ds(struct dns_msg* msg, struct ub_packed_rrset_key* ns,
+	uint8_t* z);
+
+/**
+ * Remove query attempts from all available ips. For 0x20.
+ * @param dp: delegpt.
+ * @param d: decrease.
+ */
+void iter_dec_attempts(struct delegpt* dp, int d);
+
+/**
+ * Add retry counts from older delegpt to newer delegpt.
+ * Does not waste time on timeout'd (or other failing) addresses.
+ * @param dp: new delegationpoint.
+ * @param old: old delegationpoint.
+ */
+void iter_merge_retry_counts(struct delegpt* dp, struct delegpt* old);
+
+#endif /* ITERATOR_ITER_UTILS_H */
diff --git a/3rdParty/Unbound/src/src/iterator/iterator.c b/3rdParty/Unbound/src/src/iterator/iterator.c
new file mode 100644
index 0000000..432fdc4
--- /dev/null
+++ b/3rdParty/Unbound/src/src/iterator/iterator.c
@@ -0,0 +1,2767 @@
+/*
+ * iterator/iterator.c - iterative resolver DNS query response module
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains a module that performs recusive iterative DNS query
+ * processing.
+ */
+
+#include "config.h"
+#include <ldns/ldns.h>
+#include "iterator/iterator.h"
+#include "iterator/iter_utils.h"
+#include "iterator/iter_hints.h"
+#include "iterator/iter_fwd.h"
+#include "iterator/iter_donotq.h"
+#include "iterator/iter_delegpt.h"
+#include "iterator/iter_resptype.h"
+#include "iterator/iter_scrub.h"
+#include "iterator/iter_priv.h"
+#include "validator/val_neg.h"
+#include "services/cache/dns.h"
+#include "services/cache/infra.h"
+#include "util/module.h"
+#include "util/netevent.h"
+#include "util/net_help.h"
+#include "util/regional.h"
+#include "util/data/dname.h"
+#include "util/data/msgencode.h"
+#include "util/fptr_wlist.h"
+#include "util/config_file.h"
+
+int 
+iter_init(struct module_env* env, int id)
+{
+	struct iter_env* iter_env = (struct iter_env*)calloc(1,
+		sizeof(struct iter_env));
+	if(!iter_env) {
+		log_err("malloc failure");
+		return 0;
+	}
+	env->modinfo[id] = (void*)iter_env;
+	if(!iter_apply_cfg(iter_env, env->cfg)) {
+		log_err("iterator: could not apply configuration settings.");
+		return 0;
+	}
+	return 1;
+}
+
+void 
+iter_deinit(struct module_env* env, int id)
+{
+	struct iter_env* iter_env;
+	if(!env || !env->modinfo[id])
+		return;
+	iter_env = (struct iter_env*)env->modinfo[id];
+	free(iter_env->target_fetch_policy);
+	priv_delete(iter_env->priv);
+	hints_delete(iter_env->hints);
+	donotq_delete(iter_env->donotq);
+	free(iter_env);
+	env->modinfo[id] = NULL;
+}
+
+/** new query for iterator */
+static int
+iter_new(struct module_qstate* qstate, int id)
+{
+	struct iter_qstate* iq = (struct iter_qstate*)regional_alloc(
+		qstate->region, sizeof(struct iter_qstate));
+	qstate->minfo[id] = iq;
+	if(!iq) 
+		return 0;
+	memset(iq, 0, sizeof(*iq));
+	iq->state = INIT_REQUEST_STATE;
+	iq->final_state = FINISHED_STATE;
+	iq->an_prepend_list = NULL;
+	iq->an_prepend_last = NULL;
+	iq->ns_prepend_list = NULL;
+	iq->ns_prepend_last = NULL;
+	iq->dp = NULL;
+	iq->depth = 0;
+	iq->num_target_queries = 0;
+	iq->num_current_queries = 0;
+	iq->query_restart_count = 0;
+	iq->referral_count = 0;
+	iq->sent_count = 0;
+	iq->wait_priming_stub = 0;
+	iq->refetch_glue = 0;
+	iq->dnssec_expected = 0;
+	iq->dnssec_lame_query = 0;
+	iq->chase_flags = qstate->query_flags;
+	/* Start with the (current) qname. */
+	iq->qchase = qstate->qinfo;
+	outbound_list_init(&iq->outlist);
+	return 1;
+}
+
+/**
+ * Transition to the next state. This can be used to advance a currently
+ * processing event. It cannot be used to reactivate a forEvent.
+ *
+ * @param iq: iterator query state
+ * @param nextstate The state to transition to.
+ * @return true. This is so this can be called as the return value for the
+ *         actual process*State() methods. (Transitioning to the next state
+ *         implies further processing).
+ */
+static int
+next_state(struct iter_qstate* iq, enum iter_state nextstate)
+{
+	/* If transitioning to a "response" state, make sure that there is a
+	 * response */
+	if(iter_state_is_responsestate(nextstate)) {
+		if(iq->response == NULL) {
+			log_err("transitioning to response state sans "
+				"response.");
+		}
+	}
+	iq->state = nextstate;
+	return 1;
+}
+
+/**
+ * Transition an event to its final state. Final states always either return
+ * a result up the module chain, or reactivate a dependent event. Which
+ * final state to transtion to is set in the module state for the event when
+ * it was created, and depends on the original purpose of the event.
+ *
+ * The response is stored in the qstate->buf buffer.
+ *
+ * @param iq: iterator query state
+ * @return false. This is so this method can be used as the return value for
+ *         the processState methods. (Transitioning to the final state
+ */
+static int
+final_state(struct iter_qstate* iq)
+{
+	return next_state(iq, iq->final_state);
+}
+
+/**
+ * Callback routine to handle errors in parent query states
+ * @param qstate: query state that failed.
+ * @param id: module id.
+ * @param super: super state.
+ */
+static void
+error_supers(struct module_qstate* qstate, int id, struct module_qstate* super)
+{
+	struct iter_qstate* super_iq = (struct iter_qstate*)super->minfo[id];
+
+	if(qstate->qinfo.qtype == LDNS_RR_TYPE_A ||
+		qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA) {
+		/* mark address as failed. */
+		struct delegpt_ns* dpns = NULL;
+		if(super_iq->dp)
+			dpns = delegpt_find_ns(super_iq->dp, 
+				qstate->qinfo.qname, qstate->qinfo.qname_len);
+		if(!dpns) {
+			/* not interested */
+			verbose(VERB_ALGO, "subq error, but not interested");
+			log_query_info(VERB_ALGO, "superq", &super->qinfo);
+			if(super_iq->dp)
+				delegpt_log(VERB_ALGO, super_iq->dp);
+			log_assert(0);
+			return;
+		} else {
+			/* see if the failure did get (parent-lame) info */
+			if(!cache_fill_missing(super->env, 
+				super_iq->qchase.qclass, super->region, 
+				super_iq->dp))
+				log_err("out of memory adding missing");
+		}
+		dpns->resolved = 1; /* mark as failed */
+		super_iq->num_target_queries--; 
+	}
+	if(qstate->qinfo.qtype == LDNS_RR_TYPE_NS) {
+		/* prime failed to get delegation */
+		super_iq->dp = NULL;
+	}
+	/* evaluate targets again */
+	super_iq->state = QUERYTARGETS_STATE; 
+	/* super becomes runnable, and will process this change */
+}
+
+/**
+ * Return an error to the client
+ * @param qstate: our query state
+ * @param id: module id
+ * @param rcode: error code (DNS errcode).
+ * @return: 0 for use by caller, to make notation easy, like:
+ * 	return error_response(..). 
+ */
+static int
+error_response(struct module_qstate* qstate, int id, int rcode)
+{
+	verbose(VERB_QUERY, "return error response %s", 
+		ldns_lookup_by_id(ldns_rcodes, rcode)?
+		ldns_lookup_by_id(ldns_rcodes, rcode)->name:"??");
+	qstate->return_rcode = rcode;
+	qstate->return_msg = NULL;
+	qstate->ext_state[id] = module_finished;
+	return 0;
+}
+
+/**
+ * Return an error to the client and cache the error code in the
+ * message cache (so per qname, qtype, qclass).
+ * @param qstate: our query state
+ * @param id: module id
+ * @param rcode: error code (DNS errcode).
+ * @return: 0 for use by caller, to make notation easy, like:
+ * 	return error_response(..). 
+ */
+static int
+error_response_cache(struct module_qstate* qstate, int id, int rcode)
+{
+	/* store in cache */
+	struct reply_info err;
+	memset(&err, 0, sizeof(err));
+	err.flags = (uint16_t)(BIT_QR | BIT_RA);
+	FLAGS_SET_RCODE(err.flags, rcode);
+	err.qdcount = 1;
+	err.ttl = NORR_TTL;
+	err.prefetch_ttl = PREFETCH_TTL_CALC(err.ttl);
+	/* do not waste time trying to validate this servfail */
+	err.security = sec_status_indeterminate;
+	verbose(VERB_ALGO, "store error response in message cache");
+	if(!iter_dns_store(qstate->env, &qstate->qinfo, &err, 0, 0, NULL)) {
+		log_err("error_response_cache: could not store error (nomem)");
+	}
+	return error_response(qstate, id, rcode);
+}
+
+/** check if prepend item is duplicate item */
+static int
+prepend_is_duplicate(struct ub_packed_rrset_key** sets, size_t to,
+	struct ub_packed_rrset_key* dup)
+{
+	size_t i;
+	for(i=0; i<to; i++) {
+		if(sets[i]->rk.type == dup->rk.type &&
+			sets[i]->rk.rrset_class == dup->rk.rrset_class &&
+			sets[i]->rk.dname_len == dup->rk.dname_len &&
+			query_dname_compare(sets[i]->rk.dname, dup->rk.dname)
+			== 0)
+			return 1;
+	}
+	return 0;
+}
+
+/** prepend the prepend list in the answer and authority section of dns_msg */
+static int
+iter_prepend(struct iter_qstate* iq, struct dns_msg* msg, 
+	struct regional* region)
+{
+	struct iter_prep_list* p;
+	struct ub_packed_rrset_key** sets;
+	size_t num_an = 0, num_ns = 0;;
+	for(p = iq->an_prepend_list; p; p = p->next)
+		num_an++;
+	for(p = iq->ns_prepend_list; p; p = p->next)
+		num_ns++;
+	if(num_an + num_ns == 0)
+		return 1;
+	verbose(VERB_ALGO, "prepending %d rrsets", (int)num_an + (int)num_ns);
+	sets = regional_alloc(region, (num_an+num_ns+msg->rep->rrset_count) *
+		sizeof(struct ub_packed_rrset_key*));
+	if(!sets) 
+		return 0;
+	/* ANSWER section */
+	num_an = 0;
+	for(p = iq->an_prepend_list; p; p = p->next) {
+		sets[num_an++] = p->rrset;
+	}
+	memcpy(sets+num_an, msg->rep->rrsets, msg->rep->an_numrrsets *
+		sizeof(struct ub_packed_rrset_key*));
+	/* AUTH section */
+	num_ns = 0;
+	for(p = iq->ns_prepend_list; p; p = p->next) {
+		if(prepend_is_duplicate(sets+msg->rep->an_numrrsets+num_an,
+			num_ns, p->rrset) || prepend_is_duplicate(
+			msg->rep->rrsets+msg->rep->an_numrrsets, 
+			msg->rep->ns_numrrsets, p->rrset))
+			continue;
+		sets[msg->rep->an_numrrsets + num_an + num_ns++] = p->rrset;
+	}
+	memcpy(sets + num_an + msg->rep->an_numrrsets + num_ns, 
+		msg->rep->rrsets + msg->rep->an_numrrsets, 
+		(msg->rep->ns_numrrsets + msg->rep->ar_numrrsets) *
+		sizeof(struct ub_packed_rrset_key*));
+
+	/* NXDOMAIN rcode can stay if we prepended DNAME/CNAMEs, because
+	 * this is what recursors should give. */
+	msg->rep->rrset_count += num_an + num_ns;
+	msg->rep->an_numrrsets += num_an;
+	msg->rep->ns_numrrsets += num_ns;
+	msg->rep->rrsets = sets;
+	return 1;
+}
+
+/**
+ * Add rrset to ANSWER prepend list
+ * @param qstate: query state.
+ * @param iq: iterator query state.
+ * @param rrset: rrset to add.
+ * @return false on failure (malloc).
+ */
+static int
+iter_add_prepend_answer(struct module_qstate* qstate, struct iter_qstate* iq,
+	struct ub_packed_rrset_key* rrset)
+{
+	struct iter_prep_list* p = (struct iter_prep_list*)regional_alloc(
+		qstate->region, sizeof(struct iter_prep_list));
+	if(!p)
+		return 0;
+	p->rrset = rrset;
+	p->next = NULL;
+	/* add at end */
+	if(iq->an_prepend_last)
+		iq->an_prepend_last->next = p;
+	else	iq->an_prepend_list = p;
+	iq->an_prepend_last = p;
+	return 1;
+}
+
+/**
+ * Add rrset to AUTHORITY prepend list
+ * @param qstate: query state.
+ * @param iq: iterator query state.
+ * @param rrset: rrset to add.
+ * @return false on failure (malloc).
+ */
+static int
+iter_add_prepend_auth(struct module_qstate* qstate, struct iter_qstate* iq,
+	struct ub_packed_rrset_key* rrset)
+{
+	struct iter_prep_list* p = (struct iter_prep_list*)regional_alloc(
+		qstate->region, sizeof(struct iter_prep_list));
+	if(!p)
+		return 0;
+	p->rrset = rrset;
+	p->next = NULL;
+	/* add at end */
+	if(iq->ns_prepend_last)
+		iq->ns_prepend_last->next = p;
+	else	iq->ns_prepend_list = p;
+	iq->ns_prepend_last = p;
+	return 1;
+}
+
+/**
+ * Given a CNAME response (defined as a response containing a CNAME or DNAME
+ * that does not answer the request), process the response, modifying the
+ * state as necessary. This follows the CNAME/DNAME chain and returns the
+ * final query name.
+ *
+ * sets the new query name, after following the CNAME/DNAME chain.
+ * @param qstate: query state.
+ * @param iq: iterator query state.
+ * @param msg: the response.
+ * @param mname: returned target new query name.
+ * @param mname_len: length of mname.
+ * @return false on (malloc) error.
+ */
+static int
+handle_cname_response(struct module_qstate* qstate, struct iter_qstate* iq,
+        struct dns_msg* msg, uint8_t** mname, size_t* mname_len)
+{
+	size_t i;
+	/* Start with the (current) qname. */
+	*mname = iq->qchase.qname;
+	*mname_len = iq->qchase.qname_len;
+
+	/* Iterate over the ANSWER rrsets in order, looking for CNAMEs and 
+	 * DNAMES. */
+	for(i=0; i<msg->rep->an_numrrsets; i++) {
+		struct ub_packed_rrset_key* r = msg->rep->rrsets[i];
+		/* If there is a (relevant) DNAME, add it to the list.
+		 * We always expect there to be CNAME that was generated 
+		 * by this DNAME following, so we don't process the DNAME 
+		 * directly.  */
+		if(ntohs(r->rk.type) == LDNS_RR_TYPE_DNAME &&
+			dname_strict_subdomain_c(*mname, r->rk.dname)) {
+			if(!iter_add_prepend_answer(qstate, iq, r))
+				return 0;
+			continue;
+		}
+
+		if(ntohs(r->rk.type) == LDNS_RR_TYPE_CNAME &&
+			query_dname_compare(*mname, r->rk.dname) == 0) {
+			/* Add this relevant CNAME rrset to the prepend list.*/
+			if(!iter_add_prepend_answer(qstate, iq, r))
+				return 0;
+			get_cname_target(r, mname, mname_len);
+		}
+
+		/* Other rrsets in the section are ignored. */
+	}
+	/* add authority rrsets to authority prepend, for wildcarded CNAMEs */
+	for(i=msg->rep->an_numrrsets; i<msg->rep->an_numrrsets +
+		msg->rep->ns_numrrsets; i++) {
+		struct ub_packed_rrset_key* r = msg->rep->rrsets[i];
+		/* only add NSEC/NSEC3, as they may be needed for validation */
+		if(ntohs(r->rk.type) == LDNS_RR_TYPE_NSEC ||
+			ntohs(r->rk.type) == LDNS_RR_TYPE_NSEC3) {
+			if(!iter_add_prepend_auth(qstate, iq, r))
+				return 0;
+		}
+	}
+	return 1;
+}
+
+/**
+ * Generate a subrequest.
+ * Generate a local request event. Local events are tied to this module, and
+ * have a correponding (first tier) event that is waiting for this event to
+ * resolve to continue.
+ *
+ * @param qname The query name for this request.
+ * @param qnamelen length of qname
+ * @param qtype The query type for this request.
+ * @param qclass The query class for this request.
+ * @param qstate The event that is generating this event.
+ * @param id: module id.
+ * @param iq: The iterator state that is generating this event.
+ * @param initial_state The initial response state (normally this
+ *          is QUERY_RESP_STATE, unless it is known that the request won't
+ *          need iterative processing
+ * @param finalstate The final state for the response to this request.
+ * @param subq_ret: if newly allocated, the subquerystate, or NULL if it does
+ * 	not need initialisation.
+ * @param v: if true, validation is done on the subquery.
+ * @return false on error (malloc).
+ */
+static int
+generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype, 
+	uint16_t qclass, struct module_qstate* qstate, int id,
+	struct iter_qstate* iq, enum iter_state initial_state, 
+	enum iter_state finalstate, struct module_qstate** subq_ret, int v)
+{
+	struct module_qstate* subq = NULL;
+	struct iter_qstate* subiq = NULL;
+	uint16_t qflags = 0; /* OPCODE QUERY, no flags */
+	struct query_info qinf;
+	int prime = (finalstate == PRIME_RESP_STATE)?1:0;
+	qinf.qname = qname;
+	qinf.qname_len = qnamelen;
+	qinf.qtype = qtype;
+	qinf.qclass = qclass;
+
+	/* RD should be set only when sending the query back through the INIT
+	 * state. */
+	if(initial_state == INIT_REQUEST_STATE)
+		qflags |= BIT_RD;
+	/* We set the CD flag so we can send this through the "head" of 
+	 * the resolution chain, which might have a validator. We are 
+	 * uninterested in validating things not on the direct resolution 
+	 * path.  */
+	if(!v)
+		qflags |= BIT_CD;
+	
+	/* attach subquery, lookup existing or make a new one */
+	fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
+	if(!(*qstate->env->attach_sub)(qstate, &qinf, qflags, prime, &subq)) {
+		return 0;
+	}
+	*subq_ret = subq;
+	if(subq) {
+		/* initialise the new subquery */
+		subq->curmod = id;
+		subq->ext_state[id] = module_state_initial;
+		subq->minfo[id] = regional_alloc(subq->region, 
+			sizeof(struct iter_qstate));
+		if(!subq->minfo[id]) {
+			log_err("init subq: out of memory");
+			fptr_ok(fptr_whitelist_modenv_kill_sub(
+				qstate->env->kill_sub));
+			(*qstate->env->kill_sub)(subq);
+			return 0;
+		}
+		subiq = (struct iter_qstate*)subq->minfo[id];
+		memset(subiq, 0, sizeof(*subiq));
+		subiq->num_target_queries = 0;
+		subiq->num_current_queries = 0;
+		subiq->depth = iq->depth+1;
+		outbound_list_init(&subiq->outlist);
+		subiq->state = initial_state;
+		subiq->final_state = finalstate;
+		subiq->qchase = subq->qinfo;
+		subiq->chase_flags = subq->query_flags;
+		subiq->refetch_glue = 0;
+	}
+	return 1;
+}
+
+/**
+ * Generate and send a root priming request.
+ * @param qstate: the qtstate that triggered the need to prime.
+ * @param iq: iterator query state.
+ * @param ie: iterator global state.
+ * @param id: module id.
+ * @param qclass: the class to prime.
+ * @return 0 on failure
+ */
+static int
+prime_root(struct module_qstate* qstate, struct iter_qstate* iq, 
+	struct iter_env* ie, int id, uint16_t qclass)
+{
+	struct delegpt* dp;
+	struct module_qstate* subq;
+	verbose(VERB_DETAIL, "priming . %s NS", 
+		ldns_lookup_by_id(ldns_rr_classes, (int)qclass)?
+		ldns_lookup_by_id(ldns_rr_classes, (int)qclass)->name:"??");
+	dp = hints_lookup_root(ie->hints, qclass);
+	if(!dp) {
+		verbose(VERB_ALGO, "Cannot prime due to lack of hints");
+		return 0;
+	}
+	/* Priming requests start at the QUERYTARGETS state, skipping 
+	 * the normal INIT state logic (which would cause an infloop). */
+	if(!generate_sub_request((uint8_t*)"\000", 1, LDNS_RR_TYPE_NS, 
+		qclass, qstate, id, iq, QUERYTARGETS_STATE, PRIME_RESP_STATE,
+		&subq, 0)) {
+		verbose(VERB_ALGO, "could not prime root");
+		return 0;
+	}
+	if(subq) {
+		struct iter_qstate* subiq = 
+			(struct iter_qstate*)subq->minfo[id];
+		/* Set the initial delegation point to the hint.
+		 * copy dp, it is now part of the root prime query. 
+		 * dp was part of in the fixed hints structure. */
+		subiq->dp = delegpt_copy(dp, subq->region);
+		if(!subiq->dp) {
+			log_err("out of memory priming root, copydp");
+			fptr_ok(fptr_whitelist_modenv_kill_sub(
+				qstate->env->kill_sub));
+			(*qstate->env->kill_sub)(subq);
+			return 0;
+		}
+		/* there should not be any target queries. */
+		subiq->num_target_queries = 0; 
+		subiq->dnssec_expected = iter_indicates_dnssec(
+			qstate->env, subiq->dp, NULL, subq->qinfo.qclass);
+	}
+	
+	/* this module stops, our submodule starts, and does the query. */
+	qstate->ext_state[id] = module_wait_subquery;
+	return 1;
+}
+
+/**
+ * Generate and process a stub priming request. This method tests for the
+ * need to prime a stub zone, so it is safe to call for every request.
+ *
+ * @param qstate: the qtstate that triggered the need to prime.
+ * @param iq: iterator query state.
+ * @param ie: iterator global state.
+ * @param id: module id.
+ * @param q: request name.
+ * @return true if a priming subrequest was made, false if not. The will only
+ *         issue a priming request if it detects an unprimed stub.
+ *         Uses value of 2 to signal during stub-prime in root-prime situation
+ *         that a noprime-stub is available and resolution can continue.
+ */
+static int
+prime_stub(struct module_qstate* qstate, struct iter_qstate* iq, 
+	struct iter_env* ie, int id, struct query_info* q)
+{
+	/* Lookup the stub hint. This will return null if the stub doesn't 
+	 * need to be re-primed. */
+	struct iter_hints_stub* stub;
+	struct delegpt* stub_dp;
+	struct module_qstate* subq;
+	uint8_t* delname = q->qname;
+	size_t delnamelen = q->qname_len;
+
+	if(q->qtype == LDNS_RR_TYPE_DS && !dname_is_root(q->qname))
+		/* remove first label, but not for root */
+		dname_remove_label(&delname, &delnamelen);
+
+	stub = hints_lookup_stub(ie->hints, delname, q->qclass, iq->dp);
+	/* The stub (if there is one) does not need priming. */
+	if(!stub)
+		return 0;
+	stub_dp = stub->dp;
+
+	/* is it a noprime stub (always use) */
+	if(stub->noprime) {
+		int r = 0;
+		if(iq->dp == NULL) r = 2;
+		/* copy the dp out of the fixed hints structure, so that
+		 * it can be changed when servicing this query */
+		iq->dp = delegpt_copy(stub_dp, qstate->region);
+		if(!iq->dp) {
+			log_err("out of memory priming stub");
+			(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+			return 1; /* return 1 to make module stop, with error */
+		}
+		log_nametypeclass(VERB_DETAIL, "use stub", stub_dp->name, 
+			LDNS_RR_TYPE_NS, q->qclass);
+		return r;
+	}
+
+	/* Otherwise, we need to (re)prime the stub. */
+	log_nametypeclass(VERB_DETAIL, "priming stub", stub_dp->name, 
+		LDNS_RR_TYPE_NS, q->qclass);
+
+	/* Stub priming events start at the QUERYTARGETS state to avoid the
+	 * redundant INIT state processing. */
+	if(!generate_sub_request(stub_dp->name, stub_dp->namelen, 
+		LDNS_RR_TYPE_NS, q->qclass, qstate, id, iq,
+		QUERYTARGETS_STATE, PRIME_RESP_STATE, &subq, 0)) {
+		verbose(VERB_ALGO, "could not prime stub");
+		(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+		return 1; /* return 1 to make module stop, with error */
+	}
+	if(subq) {
+		struct iter_qstate* subiq = 
+			(struct iter_qstate*)subq->minfo[id];
+
+		/* Set the initial delegation point to the hint. */
+		/* make copy to avoid use of stub dp by different qs/threads */
+		subiq->dp = delegpt_copy(stub_dp, subq->region);
+		if(!subiq->dp) {
+			log_err("out of memory priming stub, copydp");
+			fptr_ok(fptr_whitelist_modenv_kill_sub(
+				qstate->env->kill_sub));
+			(*qstate->env->kill_sub)(subq);
+			(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+			return 1; /* return 1 to make module stop, with error */
+		}
+		/* there should not be any target queries -- although there 
+		 * wouldn't be anyway, since stub hints never have 
+		 * missing targets. */
+		subiq->num_target_queries = 0; 
+		subiq->wait_priming_stub = 1;
+		subiq->dnssec_expected = iter_indicates_dnssec(
+			qstate->env, subiq->dp, NULL, subq->qinfo.qclass);
+	}
+	
+	/* this module stops, our submodule starts, and does the query. */
+	qstate->ext_state[id] = module_wait_subquery;
+	return 1;
+}
+
+/**
+ * Generate A and AAAA checks for glue that is in-zone for the referral
+ * we just got to obtain authoritative information on the adresses.
+ *
+ * @param qstate: the qtstate that triggered the need to prime.
+ * @param iq: iterator query state.
+ * @param id: module id.
+ */
+static void
+generate_a_aaaa_check(struct module_qstate* qstate, struct iter_qstate* iq, 
+	int id)
+{
+	struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id];
+	struct module_qstate* subq;
+	size_t i;
+	struct reply_info* rep = iq->response->rep;
+	struct ub_packed_rrset_key* s;
+	log_assert(iq->dp);
+
+	if(iq->depth == ie->max_dependency_depth)
+		return;
+	/* walk through additional, and check if in-zone,
+	 * only relevant A, AAAA are left after scrub anyway */
+	for(i=rep->an_numrrsets+rep->ns_numrrsets; i<rep->rrset_count; i++) {
+		s = rep->rrsets[i];
+		/* check *ALL* addresses that are transmitted in additional*/
+		/* is it an address ? */
+		if( !(ntohs(s->rk.type)==LDNS_RR_TYPE_A ||
+			ntohs(s->rk.type)==LDNS_RR_TYPE_AAAA)) {
+			continue;
+		}
+		/* is this query the same as the A/AAAA check for it */
+		if(qstate->qinfo.qtype == ntohs(s->rk.type) &&
+			qstate->qinfo.qclass == ntohs(s->rk.rrset_class) &&
+			query_dname_compare(qstate->qinfo.qname, 
+				s->rk.dname)==0 &&
+			(qstate->query_flags&BIT_RD) && 
+			!(qstate->query_flags&BIT_CD))
+			continue;
+
+		/* generate subrequest for it */
+		log_nametypeclass(VERB_ALGO, "schedule addr fetch", 
+			s->rk.dname, ntohs(s->rk.type), 
+			ntohs(s->rk.rrset_class));
+		if(!generate_sub_request(s->rk.dname, s->rk.dname_len, 
+			ntohs(s->rk.type), ntohs(s->rk.rrset_class),
+			qstate, id, iq,
+			INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1)) {
+			verbose(VERB_ALGO, "could not generate addr check");
+			return;
+		}
+		/* ignore subq - not need for more init */
+	}
+}
+
+/**
+ * Generate a NS check request to obtain authoritative information
+ * on an NS rrset.
+ *
+ * @param qstate: the qtstate that triggered the need to prime.
+ * @param iq: iterator query state.
+ * @param id: module id.
+ */
+static void
+generate_ns_check(struct module_qstate* qstate, struct iter_qstate* iq, int id)
+{
+	struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id];
+	struct module_qstate* subq;
+	log_assert(iq->dp);
+
+	if(iq->depth == ie->max_dependency_depth)
+		return;
+	/* is this query the same as the nscheck? */
+	if(qstate->qinfo.qtype == LDNS_RR_TYPE_NS &&
+		query_dname_compare(iq->dp->name, qstate->qinfo.qname)==0 &&
+		(qstate->query_flags&BIT_RD) && !(qstate->query_flags&BIT_CD)){
+		/* spawn off A, AAAA queries for in-zone glue to check */
+		generate_a_aaaa_check(qstate, iq, id);
+		return;
+	}
+
+	log_nametypeclass(VERB_ALGO, "schedule ns fetch", 
+		iq->dp->name, LDNS_RR_TYPE_NS, iq->qchase.qclass);
+	if(!generate_sub_request(iq->dp->name, iq->dp->namelen, 
+		LDNS_RR_TYPE_NS, iq->qchase.qclass, qstate, id, iq,
+		INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1)) {
+		verbose(VERB_ALGO, "could not generate ns check");
+		return;
+	}
+	if(subq) {
+		struct iter_qstate* subiq = 
+			(struct iter_qstate*)subq->minfo[id];
+
+		/* make copy to avoid use of stub dp by different qs/threads */
+		/* refetch glue to start higher up the tree */
+		subiq->refetch_glue = 1;
+		subiq->dp = delegpt_copy(iq->dp, subq->region);
+		if(!subiq->dp) {
+			log_err("out of memory generating ns check, copydp");
+			fptr_ok(fptr_whitelist_modenv_kill_sub(
+				qstate->env->kill_sub));
+			(*qstate->env->kill_sub)(subq);
+			return;
+		}
+	}
+}
+
+/**
+ * Generate a DNSKEY prefetch query to get the DNSKEY for the DS record we
+ * just got in a referral (where we have dnssec_expected, thus have trust
+ * anchors above it).  Note that right after calling this routine the
+ * iterator detached subqueries (because of following the referral), and thus
+ * the DNSKEY query becomes detached, its return stored in the cache for
+ * later lookup by the validator.  This cache lookup by the validator avoids
+ * the roundtrip incurred by the DNSKEY query.  The DNSKEY query is now
+ * performed at about the same time the original query is sent to the domain,
+ * thus the two answers are likely to be returned at about the same time,
+ * saving a roundtrip from the validated lookup.
+ *
+ * @param qstate: the qtstate that triggered the need to prime.
+ * @param iq: iterator query state.
+ * @param id: module id.
+ */
+static void
+generate_dnskey_prefetch(struct module_qstate* qstate, 
+	struct iter_qstate* iq, int id)
+{
+	struct module_qstate* subq;
+	log_assert(iq->dp);
+
+	/* is this query the same as the prefetch? */
+	if(qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY &&
+		query_dname_compare(iq->dp->name, qstate->qinfo.qname)==0 &&
+		(qstate->query_flags&BIT_RD) && !(qstate->query_flags&BIT_CD)){
+		return;
+	}
+
+	/* if the DNSKEY is in the cache this lookup will stop quickly */
+	log_nametypeclass(VERB_ALGO, "schedule dnskey prefetch", 
+		iq->dp->name, LDNS_RR_TYPE_DNSKEY, iq->qchase.qclass);
+	if(!generate_sub_request(iq->dp->name, iq->dp->namelen, 
+		LDNS_RR_TYPE_DNSKEY, iq->qchase.qclass, qstate, id, iq,
+		INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0)) {
+		/* we'll be slower, but it'll work */
+		verbose(VERB_ALGO, "could not generate dnskey prefetch");
+		return;
+	}
+	if(subq) {
+		struct iter_qstate* subiq = 
+			(struct iter_qstate*)subq->minfo[id];
+		/* this qstate has the right delegation for the dnskey lookup*/
+		/* make copy to avoid use of stub dp by different qs/threads */
+		subiq->dp = delegpt_copy(iq->dp, subq->region);
+		/* if !subiq->dp, it'll start from the cache, no problem */
+	}
+}
+
+/**
+ * See if the query needs forwarding.
+ * 
+ * @param qstate: query state.
+ * @param iq: iterator query state.
+ * @return true if the request is forwarded, false if not.
+ * 	If returns true but, iq->dp is NULL then a malloc failure occurred.
+ */
+static int
+forward_request(struct module_qstate* qstate, struct iter_qstate* iq)
+{
+	struct delegpt* dp;
+	uint8_t* delname = iq->qchase.qname;
+	size_t delnamelen = iq->qchase.qname_len;
+	/* strip one label off of DS query to lookup higher for it */
+	if(iq->qchase.qtype == LDNS_RR_TYPE_DS 
+		&& !dname_is_root(iq->qchase.qname))
+		dname_remove_label(&delname, &delnamelen);
+	dp = forwards_lookup(qstate->env->fwds, delname, iq->qchase.qclass);
+	if(!dp) 
+		return 0;
+	/* send recursion desired to forward addr */
+	iq->chase_flags |= BIT_RD; 
+	iq->dp = delegpt_copy(dp, qstate->region);
+	/* iq->dp checked by caller */
+	verbose(VERB_ALGO, "forwarding request");
+	return 1;
+}
+
+/** 
+ * Process the initial part of the request handling. This state roughly
+ * corresponds to resolver algorithms steps 1 (find answer in cache) and 2
+ * (find the best servers to ask).
+ *
+ * Note that all requests start here, and query restarts revisit this state.
+ *
+ * This state either generates: 1) a response, from cache or error, 2) a
+ * priming event, or 3) forwards the request to the next state (init2,
+ * generally).
+ *
+ * @param qstate: query state.
+ * @param iq: iterator query state.
+ * @param ie: iterator shared global environment.
+ * @param id: module id.
+ * @return true if the event needs more request processing immediately,
+ *         false if not.
+ */
+static int
+processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
+	struct iter_env* ie, int id)
+{
+	uint8_t* delname;
+	size_t delnamelen;
+	struct dns_msg* msg;
+
+	log_query_info(VERB_DETAIL, "resolving", &qstate->qinfo);
+	/* check effort */
+
+	/* We enforce a maximum number of query restarts. This is primarily a
+	 * cheap way to prevent CNAME loops. */
+	if(iq->query_restart_count > MAX_RESTART_COUNT) {
+		verbose(VERB_QUERY, "request has exceeded the maximum number"
+			" of query restarts with %d", iq->query_restart_count);
+		return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+	}
+
+	/* We enforce a maximum recursion/dependency depth -- in general, 
+	 * this is unnecessary for dependency loops (although it will 
+	 * catch those), but it provides a sensible limit to the amount 
+	 * of work required to answer a given query. */
+	verbose(VERB_ALGO, "request has dependency depth of %d", iq->depth);
+	if(iq->depth > ie->max_dependency_depth) {
+		verbose(VERB_QUERY, "request has exceeded the maximum "
+			"dependency depth with depth of %d", iq->depth);
+		return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+	}
+
+	/* If the request is qclass=ANY, setup to generate each class */
+	if(qstate->qinfo.qclass == LDNS_RR_CLASS_ANY) {
+		iq->qchase.qclass = 0;
+		return next_state(iq, COLLECT_CLASS_STATE);
+	}
+
+	/* Resolver Algorithm Step 1 -- Look for the answer in local data. */
+
+	/* This either results in a query restart (CNAME cache response), a
+	 * terminating response (ANSWER), or a cache miss (null). */
+	
+	if(qstate->blacklist) {
+		/* if cache, or anything else, was blacklisted then
+		 * getting older results from cache is a bad idea, no cache */
+		verbose(VERB_ALGO, "cache blacklisted, going to the network");
+		msg = NULL;
+	} else {
+		msg = dns_cache_lookup(qstate->env, iq->qchase.qname, 
+			iq->qchase.qname_len, iq->qchase.qtype, 
+			iq->qchase.qclass, qstate->region, qstate->env->scratch);
+		if(!msg && qstate->env->neg_cache) {
+			/* lookup in negative cache; may result in 
+			 * NOERROR/NODATA or NXDOMAIN answers that need validation */
+			msg = val_neg_getmsg(qstate->env->neg_cache, &iq->qchase,
+				qstate->region, qstate->env->rrset_cache,
+				qstate->env->scratch_buffer, 
+				*qstate->env->now, 1/*add SOA*/, NULL);
+		}
+		/* item taken from cache does not match our query name, thus
+		 * security needs to be re-examined later */
+		if(msg && query_dname_compare(qstate->qinfo.qname,
+			iq->qchase.qname) != 0)
+			msg->rep->security = sec_status_unchecked;
+	}
+	if(msg) {
+		/* handle positive cache response */
+		enum response_type type = response_type_from_cache(msg, 
+			&iq->qchase);
+		if(verbosity >= VERB_ALGO) {
+			log_dns_msg("msg from cache lookup", &msg->qinfo, 
+				msg->rep);
+			verbose(VERB_ALGO, "msg ttl is %d, prefetch ttl %d", 
+				(int)msg->rep->ttl, 
+				(int)msg->rep->prefetch_ttl);
+		}
+
+		if(type == RESPONSE_TYPE_CNAME) {
+			uint8_t* sname = 0;
+			size_t slen = 0;
+			verbose(VERB_ALGO, "returning CNAME response from "
+				"cache");
+			if(!handle_cname_response(qstate, iq, msg, 
+				&sname, &slen))
+				return error_response(qstate, id, 
+					LDNS_RCODE_SERVFAIL);
+			iq->qchase.qname = sname;
+			iq->qchase.qname_len = slen;
+			/* This *is* a query restart, even if it is a cheap 
+			 * one. */
+			iq->dp = NULL;
+			iq->refetch_glue = 0;
+			iq->query_restart_count++;
+			iq->sent_count = 0;
+			sock_list_insert(&qstate->reply_origin, NULL, 0, qstate->region);
+			return next_state(iq, INIT_REQUEST_STATE);
+		}
+
+		/* if from cache, NULL, else insert 'cache IP' len=0 */
+		if(qstate->reply_origin)
+			sock_list_insert(&qstate->reply_origin, NULL, 0, qstate->region);
+		/* it is an answer, response, to final state */
+		verbose(VERB_ALGO, "returning answer from cache.");
+		iq->response = msg;
+		return final_state(iq);
+	}
+	
+	/* attempt to forward the request */
+	if(forward_request(qstate, iq))
+	{
+		if(!iq->dp) {
+			log_err("alloc failure for forward dp");
+			return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+		}
+		iq->refetch_glue = 0;
+		/* the request has been forwarded.
+		 * forwarded requests need to be immediately sent to the 
+		 * next state, QUERYTARGETS. */
+		return next_state(iq, QUERYTARGETS_STATE);
+	}
+
+	/* Resolver Algorithm Step 2 -- find the "best" servers. */
+
+	/* first, adjust for DS queries. To avoid the grandparent problem, 
+	 * we just look for the closest set of server to the parent of qname.
+	 * When re-fetching glue we also need to ask the parent.
+	 */
+	if(iq->refetch_glue) {
+		if(!iq->dp) {
+			log_err("internal or malloc fail: no dp for refetch");
+			return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+		}
+		delname = iq->dp->name;
+		delnamelen = iq->dp->namelen;
+	} else {
+		delname = iq->qchase.qname;
+		delnamelen = iq->qchase.qname_len;
+	}
+	if(iq->qchase.qtype == LDNS_RR_TYPE_DS || iq->refetch_glue ||
+	   (iq->qchase.qtype == LDNS_RR_TYPE_NS && qstate->prefetch_leeway)) {
+		/* remove first label from delname, root goes to hints,
+		 * but only to fetch glue, not for qtype=DS. */
+		/* also when prefetching an NS record, fetch it again from
+		 * its parent, just as if it expired, so that you do not
+		 * get stuck on an older nameserver that gives old NSrecords */
+		if(dname_is_root(delname) && (iq->refetch_glue ||
+			(iq->qchase.qtype == LDNS_RR_TYPE_NS &&
+			qstate->prefetch_leeway)))
+			delname = NULL; /* go to root priming */
+		else 	dname_remove_label(&delname, &delnamelen);
+		iq->refetch_glue = 0; /* if CNAME causes restart, no refetch */
+	}
+	/* delname is the name to lookup a delegation for. If NULL rootprime */
+	while(1) {
+		
+		/* Lookup the delegation in the cache. If null, then the 
+		 * cache needs to be primed for the qclass. */
+		if(delname)
+		     iq->dp = dns_cache_find_delegation(qstate->env, delname, 
+			delnamelen, iq->qchase.qtype, iq->qchase.qclass, 
+			qstate->region, &iq->deleg_msg, *qstate->env->now);
+		else iq->dp = NULL;
+
+		/* If the cache has returned nothing, then we have a 
+		 * root priming situation. */
+		if(iq->dp == NULL) {
+			/* if there is a stub, then no root prime needed */
+			int r = prime_stub(qstate, iq, ie, id, &iq->qchase);
+			if(r == 2)
+				break; /* got noprime-stub-zone, continue */
+			else if(r)
+				return 0; /* stub prime request made */
+			if(forwards_lookup_root(qstate->env->fwds, 
+				iq->qchase.qclass)) {
+				/* forward zone root, no root prime needed */
+				/* fill in some dp - safety belt */
+				iq->dp = hints_lookup_root(ie->hints, 
+					iq->qchase.qclass);
+				if(!iq->dp) {
+					log_err("internal error: no hints dp");
+					return error_response(qstate, id, 
+						LDNS_RCODE_SERVFAIL);
+				}
+				iq->dp = delegpt_copy(iq->dp, qstate->region);
+				if(!iq->dp) {
+					log_err("out of memory in safety belt");
+					return error_response(qstate, id, 
+						LDNS_RCODE_SERVFAIL);
+				}
+				return next_state(iq, INIT_REQUEST_2_STATE);
+			}
+			/* Note that the result of this will set a new
+			 * DelegationPoint based on the result of priming. */
+			if(!prime_root(qstate, iq, ie, id, iq->qchase.qclass))
+				return error_response(qstate, id, 
+					LDNS_RCODE_REFUSED);
+
+			/* priming creates and sends a subordinate query, with 
+			 * this query as the parent. So further processing for 
+			 * this event will stop until reactivated by the 
+			 * results of priming. */
+			return 0;
+		}
+
+		/* see if this dp not useless.
+		 * It is useless if:
+		 *	o all NS items are required glue. 
+		 *	  or the query is for NS item that is required glue.
+		 *	o no addresses are provided.
+		 *	o RD qflag is on.
+		 * Instead, go up one level, and try to get even further
+		 * If the root was useless, use safety belt information. 
+		 * Only check cache returns, because replies for servers
+		 * could be useless but lead to loops (bumping into the
+		 * same server reply) if useless-checked.
+		 */
+		if(iter_dp_is_useless(&qstate->qinfo, qstate->query_flags, 
+			iq->dp)) {
+			if(dname_is_root(iq->dp->name)) {
+				/* use safety belt */
+				verbose(VERB_QUERY, "Cache has root NS but "
+				"no addresses. Fallback to the safety belt.");
+				iq->dp = hints_lookup_root(ie->hints, 
+					iq->qchase.qclass);
+				/* note deleg_msg is from previous lookup,
+				 * but RD is on, so it is not used */
+				if(!iq->dp) {
+					log_err("internal error: no hints dp");
+					return error_response(qstate, id, 
+						LDNS_RCODE_REFUSED);
+				}
+				iq->dp = delegpt_copy(iq->dp, qstate->region);
+				if(!iq->dp) {
+					log_err("out of memory in safety belt");
+					return error_response(qstate, id, 
+						LDNS_RCODE_SERVFAIL);
+				}
+				break;
+			} else {
+				verbose(VERB_ALGO, 
+					"cache delegation was useless:");
+				delegpt_log(VERB_ALGO, iq->dp);
+				/* go up */
+				delname = iq->dp->name;
+				delnamelen = iq->dp->namelen;
+				dname_remove_label(&delname, &delnamelen);
+			}
+		} else break;
+	}
+
+	verbose(VERB_ALGO, "cache delegation returns delegpt");
+	delegpt_log(VERB_ALGO, iq->dp);
+
+	/* Otherwise, set the current delegation point and move on to the 
+	 * next state. */
+	return next_state(iq, INIT_REQUEST_2_STATE);
+}
+
+/** 
+ * Process the second part of the initial request handling. This state
+ * basically exists so that queries that generate root priming events have
+ * the same init processing as ones that do not. Request events that reach
+ * this state must have a valid currentDelegationPoint set.
+ *
+ * This part is primarly handling stub zone priming. Events that reach this
+ * state must have a current delegation point.
+ *
+ * @param qstate: query state.
+ * @param iq: iterator query state.
+ * @param ie: iterator shared global environment.
+ * @param id: module id.
+ * @return true if the event needs more request processing immediately,
+ *         false if not.
+ */
+static int
+processInitRequest2(struct module_qstate* qstate, struct iter_qstate* iq,
+	struct iter_env* ie, int id)
+{
+	log_query_info(VERB_QUERY, "resolving (init part 2): ", 
+		&qstate->qinfo);
+
+	/* Check to see if we need to prime a stub zone. */
+	if(prime_stub(qstate, iq, ie, id, &iq->qchase)) {
+		/* A priming sub request was made */
+		return 0;
+	}
+
+	/* most events just get forwarded to the next state. */
+	return next_state(iq, INIT_REQUEST_3_STATE);
+}
+
+/** 
+ * Process the third part of the initial request handling. This state exists
+ * as a separate state so that queries that generate stub priming events
+ * will get the tail end of the init process but not repeat the stub priming
+ * check.
+ *
+ * @param qstate: query state.
+ * @param iq: iterator query state.
+ * @param id: module id.
+ * @return true, advancing the event to the QUERYTARGETS_STATE.
+ */
+static int
+processInitRequest3(struct module_qstate* qstate, struct iter_qstate* iq, 
+	int id)
+{
+	log_query_info(VERB_QUERY, "resolving (init part 3): ", 
+		&qstate->qinfo);
+	/* if the cache reply dp equals a validation anchor or msg has DS,
+	 * then DNSSEC RRSIGs are expected in the reply */
+	iq->dnssec_expected = iter_indicates_dnssec(qstate->env, iq->dp, 
+		iq->deleg_msg, iq->qchase.qclass);
+
+	/* If the RD flag wasn't set, then we just finish with the 
+	 * cached referral as the response. */
+	if(!(qstate->query_flags & BIT_RD)) {
+		iq->response = iq->deleg_msg;
+		if(verbosity >= VERB_ALGO)
+			log_dns_msg("no RD requested, using delegation msg", 
+				&iq->response->qinfo, iq->response->rep);
+		if(qstate->reply_origin)
+			sock_list_insert(&qstate->reply_origin, NULL, 0, qstate->region);
+		return final_state(iq);
+	}
+	/* After this point, unset the RD flag -- this query is going to 
+	 * be sent to an auth. server. */
+	iq->chase_flags &= ~BIT_RD;
+
+	/* if dnssec expected, fetch key for the trust-anchor or cached-DS */
+	if(iq->dnssec_expected && qstate->env->cfg->prefetch_key &&
+		!(qstate->query_flags&BIT_CD)) {
+		generate_dnskey_prefetch(qstate, iq, id);
+		fptr_ok(fptr_whitelist_modenv_detach_subs(
+			qstate->env->detach_subs));
+		(*qstate->env->detach_subs)(qstate);
+	}
+
+	/* Jump to the next state. */
+	return next_state(iq, QUERYTARGETS_STATE);
+}
+
+/**
+ * Given a basic query, generate a parent-side "target" query. 
+ * These are subordinate queries for missing delegation point target addresses,
+ * for which only the parent of the delegation provides correct IP addresses.
+ *
+ * @param qstate: query state.
+ * @param iq: iterator query state.
+ * @param id: module id.
+ * @param name: target qname.
+ * @param namelen: target qname length.
+ * @param qtype: target qtype (either A or AAAA).
+ * @param qclass: target qclass.
+ * @return true on success, false on failure.
+ */
+static int
+generate_parentside_target_query(struct module_qstate* qstate, 
+	struct iter_qstate* iq, int id, uint8_t* name, size_t namelen, 
+	uint16_t qtype, uint16_t qclass)
+{
+	struct module_qstate* subq;
+	if(!generate_sub_request(name, namelen, qtype, qclass, qstate, 
+		id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0))
+		return 0;
+	if(subq) {
+		struct iter_qstate* subiq = 
+			(struct iter_qstate*)subq->minfo[id];
+		/* blacklist the cache - we want to fetch parent stuff */
+		sock_list_insert(&subq->blacklist, NULL, 0, subq->region);
+		subiq->query_for_pside_glue = 1;
+		if(dname_subdomain_c(name, iq->dp->name)) {
+			subiq->dp = delegpt_copy(iq->dp, subq->region);
+			subiq->dnssec_expected = iter_indicates_dnssec(
+				qstate->env, subiq->dp, NULL, 
+				subq->qinfo.qclass);
+			subiq->refetch_glue = 1;
+		} else {
+			subiq->dp = dns_cache_find_delegation(qstate->env, 
+				name, namelen, qtype, qclass, subq->region,
+				&subiq->deleg_msg, *qstate->env->now); 
+			/* if no dp, then it's from root, refetch unneeded */
+			if(subiq->dp) { 
+				subiq->dnssec_expected = iter_indicates_dnssec(
+					qstate->env, subiq->dp, NULL, 
+					subq->qinfo.qclass);
+				subiq->refetch_glue = 1;
+			}
+		}
+	}
+	log_nametypeclass(VERB_QUERY, "new pside target", name, qtype, qclass);
+	return 1;
+}
+
+/**
+ * Given a basic query, generate a "target" query. These are subordinate
+ * queries for missing delegation point target addresses.
+ *
+ * @param qstate: query state.
+ * @param iq: iterator query state.
+ * @param id: module id.
+ * @param name: target qname.
+ * @param namelen: target qname length.
+ * @param qtype: target qtype (either A or AAAA).
+ * @param qclass: target qclass.
+ * @return true on success, false on failure.
+ */
+static int
+generate_target_query(struct module_qstate* qstate, struct iter_qstate* iq,
+        int id, uint8_t* name, size_t namelen, uint16_t qtype, uint16_t qclass)
+{
+	struct module_qstate* subq;
+	if(!generate_sub_request(name, namelen, qtype, qclass, qstate, 
+		id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0))
+		return 0;
+	log_nametypeclass(VERB_QUERY, "new target", name, qtype, qclass);
+	return 1;
+}
+
+/**
+ * Given an event at a certain state, generate zero or more target queries
+ * for it's current delegation point.
+ *
+ * @param qstate: query state.
+ * @param iq: iterator query state.
+ * @param ie: iterator shared global environment.
+ * @param id: module id.
+ * @param maxtargets: The maximum number of targets to query for.
+ *	if it is negative, there is no maximum number of targets.
+ * @param num: returns the number of queries generated and processed, 
+ *	which may be zero if there were no missing targets.
+ * @return false on error.
+ */
+static int
+query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
+        struct iter_env* ie, int id, int maxtargets, int* num)
+{
+	int query_count = 0;
+	struct delegpt_ns* ns;
+	int missing;
+	int toget = 0;
+
+	if(iq->depth == ie->max_dependency_depth)
+		return 0;
+
+	iter_mark_cycle_targets(qstate, iq->dp);
+	missing = (int)delegpt_count_missing_targets(iq->dp);
+	log_assert(maxtargets != 0); /* that would not be useful */
+
+	/* Generate target requests. Basically, any missing targets 
+	 * are queried for here, regardless if it is necessary to do 
+	 * so to continue processing. */
+	if(maxtargets < 0 || maxtargets > missing)
+		toget = missing;
+	else	toget = maxtargets;
+	if(toget == 0) {
+		*num = 0;
+		return 1;
+	}
+	/* select 'toget' items from the total of 'missing' items */
+	log_assert(toget <= missing);
+
+	/* loop over missing targets */
+	for(ns = iq->dp->nslist; ns; ns = ns->next) {
+		if(ns->resolved)
+			continue;
+
+		/* randomly select this item with probability toget/missing */
+		if(!iter_ns_probability(qstate->env->rnd, toget, missing)) {
+			/* do not select this one, next; select toget number
+			 * of items from a list one less in size */
+			missing --;
+			continue;
+		}
+
+		if(ie->supports_ipv6 && !ns->got6) {
+			/* Send the AAAA request. */
+			if(!generate_target_query(qstate, iq, id, 
+				ns->name, ns->namelen,
+				LDNS_RR_TYPE_AAAA, iq->qchase.qclass)) {
+				*num = query_count;
+				if(query_count > 0)
+					qstate->ext_state[id] = module_wait_subquery;
+				return 0;
+			}
+			query_count++;
+		}
+		/* Send the A request. */
+		if(ie->supports_ipv4 && !ns->got4) {
+			if(!generate_target_query(qstate, iq, id, 
+				ns->name, ns->namelen, 
+				LDNS_RR_TYPE_A, iq->qchase.qclass)) {
+				*num = query_count;
+				if(query_count > 0)
+					qstate->ext_state[id] = module_wait_subquery;
+				return 0;
+			}
+			query_count++;
+		}
+
+		/* mark this target as in progress. */
+		ns->resolved = 1;
+		missing--;
+		toget--;
+		if(toget == 0)
+			break;
+	}
+	*num = query_count;
+	if(query_count > 0)
+		qstate->ext_state[id] = module_wait_subquery;
+
+	return 1;
+}
+
+/**
+ * Called by processQueryTargets when it would like extra targets to query
+ * but it seems to be out of options.  At last resort some less appealing
+ * options are explored.  If there are no more options, the result is SERVFAIL
+ *
+ * @param qstate: query state.
+ * @param iq: iterator query state.
+ * @param ie: iterator shared global environment.
+ * @param id: module id.
+ * @return true if the event requires more request processing immediately,
+ *         false if not. 
+ */
+static int
+processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
+	struct iter_env* ie, int id)
+{
+	struct delegpt_ns* ns;
+	int query_count = 0;
+	verbose(VERB_ALGO, "No more query targets, attempting last resort");
+	log_assert(iq->dp);
+
+	if(!iq->dp->has_parent_side_NS) {
+		if(!iter_lookup_parent_NS_from_cache(qstate->env, iq->dp,
+			qstate->region, &qstate->qinfo) 
+			|| !iq->dp->has_parent_side_NS) {
+			/* if: malloc failure in lookup go up to try */
+			/* if: no parent NS in cache - go up one level */
+			verbose(VERB_ALGO, "try to grab parent NS");
+			iq->store_parent_NS = iq->dp;
+			iq->deleg_msg = NULL;
+			iq->refetch_glue = 1;
+			iq->query_restart_count++;
+			iq->sent_count = 0;
+			return next_state(iq, INIT_REQUEST_STATE);
+		}
+	}
+	/* see if that makes new names available */
+	if(!cache_fill_missing(qstate->env, iq->qchase.qclass, 
+		qstate->region, iq->dp))
+		log_err("out of memory in cache_fill_missing");
+	if(iq->dp->usable_list) {
+		verbose(VERB_ALGO, "try parent-side-name, w. glue from cache");
+		return next_state(iq, QUERYTARGETS_STATE);
+	}
+	/* try to fill out parent glue from cache */
+	if(iter_lookup_parent_glue_from_cache(qstate->env, iq->dp,
+		qstate->region, &qstate->qinfo)) {
+		/* got parent stuff from cache, see if we can continue */
+		verbose(VERB_ALGO, "try parent-side glue from cache");
+		return next_state(iq, QUERYTARGETS_STATE);
+	}
+	/* query for an extra name added by the parent-NS record */
+	if(delegpt_count_missing_targets(iq->dp) > 0) {
+		int qs = 0;
+		verbose(VERB_ALGO, "try parent-side target name");
+		if(!query_for_targets(qstate, iq, ie, id, 1, &qs)) {
+			return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+		}
+		iq->num_target_queries += qs;
+		if(qs != 0) {
+			qstate->ext_state[id] = module_wait_subquery;
+			return 0; /* and wait for them */
+		}
+	}
+	if(iq->depth == ie->max_dependency_depth) {
+		verbose(VERB_QUERY, "maxdepth and need more nameservers, fail");
+		return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
+	}
+	/* mark cycle targets for parent-side lookups */
+	iter_mark_pside_cycle_targets(qstate, iq->dp);
+	/* see if we can issue queries to get nameserver addresses */
+	/* this lookup is not randomized, but sequential. */
+	for(ns = iq->dp->nslist; ns; ns = ns->next) {
+		/* query for parent-side A and AAAA for nameservers */
+		if(ie->supports_ipv6 && !ns->done_pside6) {
+			/* Send the AAAA request. */
+			if(!generate_parentside_target_query(qstate, iq, id, 
+				ns->name, ns->namelen,
+				LDNS_RR_TYPE_AAAA, iq->qchase.qclass))
+				return error_response(qstate, id,
+					LDNS_RCODE_SERVFAIL);
+			ns->done_pside6 = 1;
+			query_count++;
+		}
+		if(ie->supports_ipv4 && !ns->done_pside4) {
+			/* Send the A request. */
+			if(!generate_parentside_target_query(qstate, iq, id, 
+				ns->name, ns->namelen, 
+				LDNS_RR_TYPE_A, iq->qchase.qclass))
+				return error_response(qstate, id,
+					LDNS_RCODE_SERVFAIL);
+			ns->done_pside4 = 1;
+			query_count++;
+		}
+		if(query_count != 0) { /* suspend to await results */
+			verbose(VERB_ALGO, "try parent-side glue lookup");
+			iq->num_target_queries += query_count;
+			qstate->ext_state[id] = module_wait_subquery;
+			return 0;
+		}
+	}
+
+	/* if this was a parent-side glue query itself, then store that
+	 * failure in cache. */
+	if(iq->query_for_pside_glue && !iq->pside_glue)
+		iter_store_parentside_neg(qstate->env, &qstate->qinfo,
+			iq->deleg_msg?iq->deleg_msg->rep:
+			(iq->response?iq->response->rep:NULL));
+
+	verbose(VERB_QUERY, "out of query targets -- returning SERVFAIL");
+	/* fail -- no more targets, no more hope of targets, no hope 
+	 * of a response. */
+	return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
+}
+
+/** 
+ * This is the request event state where the request will be sent to one of
+ * its current query targets. This state also handles issuing target lookup
+ * queries for missing target IP addresses. Queries typically iterate on
+ * this state, both when they are just trying different targets for a given
+ * delegation point, and when they change delegation points. This state
+ * roughly corresponds to RFC 1034 algorithm steps 3 and 4.
+ *
+ * @param qstate: query state.
+ * @param iq: iterator query state.
+ * @param ie: iterator shared global environment.
+ * @param id: module id.
+ * @return true if the event requires more request processing immediately,
+ *         false if not. This state only returns true when it is generating
+ *         a SERVFAIL response because the query has hit a dead end.
+ */
+static int
+processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
+	struct iter_env* ie, int id)
+{
+	int tf_policy;
+	struct delegpt_addr* target;
+	struct outbound_entry* outq;
+
+	/* NOTE: a request will encounter this state for each target it 
+	 * needs to send a query to. That is, at least one per referral, 
+	 * more if some targets timeout or return throwaway answers. */
+
+	log_query_info(VERB_QUERY, "processQueryTargets:", &qstate->qinfo);
+	verbose(VERB_ALGO, "processQueryTargets: targetqueries %d, "
+		"currentqueries %d sentcount %d", iq->num_target_queries, 
+		iq->num_current_queries, iq->sent_count);
+
+	/* Make sure that we haven't run away */
+	/* FIXME: is this check even necessary? */
+	if(iq->referral_count > MAX_REFERRAL_COUNT) {
+		verbose(VERB_QUERY, "request has exceeded the maximum "
+			"number of referrrals with %d", iq->referral_count);
+		return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+	}
+	if(iq->sent_count > MAX_SENT_COUNT) {
+		verbose(VERB_QUERY, "request has exceeded the maximum "
+			"number of sends with %d", iq->sent_count);
+		return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+	}
+	
+	/* Make sure we have a delegation point, otherwise priming failed
+	 * or another failure occurred */
+	if(!iq->dp) {
+		verbose(VERB_QUERY, "Failed to get a delegation, giving up");
+		return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+	}
+	if(!ie->supports_ipv6)
+		delegpt_no_ipv6(iq->dp);
+	if(!ie->supports_ipv4)
+		delegpt_no_ipv4(iq->dp);
+	delegpt_log(VERB_ALGO, iq->dp);
+
+	if(iq->num_current_queries>0) {
+		/* already busy answering a query, this restart is because
+		 * more delegpt addrs became available, wait for existing
+		 * query. */
+		verbose(VERB_ALGO, "woke up, but wait for outstanding query");
+		qstate->ext_state[id] = module_wait_reply;
+		return 0;
+	}
+
+	tf_policy = 0;
+	/* < not <=, because although the array is large enough for <=, the
+	 * generated query will immediately be discarded due to depth and
+	 * that servfail is cached, which is not good as opportunism goes. */
+	if(iq->depth < ie->max_dependency_depth
+		&& iq->sent_count < TARGET_FETCH_STOP) {
+		tf_policy = ie->target_fetch_policy[iq->depth];
+	}
+
+	/* if in 0x20 fallback get as many targets as possible */
+	if(iq->caps_fallback) {
+		int extra = 0;
+		size_t naddr, nres, navail;
+		if(!query_for_targets(qstate, iq, ie, id, -1, &extra)) {
+			return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+		}
+		iq->num_target_queries += extra;
+		if(iq->num_target_queries > 0) {
+			/* wait to get all targets, we want to try em */
+			verbose(VERB_ALGO, "wait for all targets for fallback");
+			qstate->ext_state[id] = module_wait_reply;
+			return 0;
+		}
+		/* did we do enough fallback queries already? */
+		delegpt_count_addr(iq->dp, &naddr, &nres, &navail);
+		/* the current caps_server is the number of fallbacks sent.
+		 * the original query is one that matched too, so we have
+		 * caps_server+1 number of matching queries now */
+		if(iq->caps_server+1 >= naddr*3 ||
+			iq->caps_server+1 >= MAX_SENT_COUNT) {
+			/* we're done, process the response */
+			verbose(VERB_ALGO, "0x20 fallback had %d responses "
+				"match for %d wanted, done.", 
+				(int)iq->caps_server+1, (int)naddr*3);
+			iq->caps_fallback = 0;
+			iter_dec_attempts(iq->dp, 3); /* space for fallback */
+			iq->num_current_queries++; /* RespState decrements it*/
+			iq->referral_count++; /* make sure we don't loop */
+			iq->sent_count = 0;
+			iq->state = QUERY_RESP_STATE;
+			return 1;
+		}
+		verbose(VERB_ALGO, "0x20 fallback number %d", 
+			(int)iq->caps_server);
+
+	/* if there is a policy to fetch missing targets 
+	 * opportunistically, do it. we rely on the fact that once a 
+	 * query (or queries) for a missing name have been issued, 
+	 * they will not show up again. */
+	} else if(tf_policy != 0) {
+		int extra = 0;
+		verbose(VERB_ALGO, "attempt to get extra %d targets", 
+			tf_policy);
+		(void)query_for_targets(qstate, iq, ie, id, tf_policy, &extra);
+		/* errors ignored, these targets are not strictly necessary for
+		 * this result, we do not have to reply with SERVFAIL */
+		iq->num_target_queries += extra;
+	}
+
+	/* Add the current set of unused targets to our queue. */
+	delegpt_add_unused_targets(iq->dp);
+
+	/* Select the next usable target, filtering out unsuitable targets. */
+	target = iter_server_selection(ie, qstate->env, iq->dp, 
+		iq->dp->name, iq->dp->namelen, iq->qchase.qtype,
+		&iq->dnssec_lame_query, &iq->chase_to_rd, 
+		iq->num_target_queries, qstate->blacklist);
+
+	/* If no usable target was selected... */
+	if(!target) {
+		/* Here we distinguish between three states: generate a new 
+		 * target query, just wait, or quit (with a SERVFAIL).
+		 * We have the following information: number of active 
+		 * target queries, number of active current queries, 
+		 * the presence of missing targets at this delegation 
+		 * point, and the given query target policy. */
+		
+		/* Check for the wait condition. If this is true, then 
+		 * an action must be taken. */
+		if(iq->num_target_queries==0 && iq->num_current_queries==0) {
+			/* If there is nothing to wait for, then we need 
+			 * to distinguish between generating (a) new target 
+			 * query, or failing. */
+			if(delegpt_count_missing_targets(iq->dp) > 0) {
+				int qs = 0;
+				verbose(VERB_ALGO, "querying for next "
+					"missing target");
+				if(!query_for_targets(qstate, iq, ie, id, 
+					1, &qs)) {
+					return error_response(qstate, id,
+						LDNS_RCODE_SERVFAIL);
+				}
+				if(qs == 0 && 
+				   delegpt_count_missing_targets(iq->dp) == 0){
+					/* it looked like there were missing
+					 * targets, but they did not turn up.
+					 * Try the bad choices again (if any),
+					 * when we get back here missing==0,
+					 * so this is not a loop. */
+					return 1;
+				}
+				iq->num_target_queries += qs;
+			}
+			/* Since a target query might have been made, we 
+			 * need to check again. */
+			if(iq->num_target_queries == 0) {
+				return processLastResort(qstate, iq, ie, id);
+			}
+		}
+
+		/* otherwise, we have no current targets, so submerge 
+		 * until one of the target or direct queries return. */
+		if(iq->num_target_queries>0 && iq->num_current_queries>0) {
+			verbose(VERB_ALGO, "no current targets -- waiting "
+				"for %d targets to resolve or %d outstanding"
+				" queries to respond", iq->num_target_queries, 
+				iq->num_current_queries);
+			qstate->ext_state[id] = module_wait_reply;
+		} else if(iq->num_target_queries>0) {
+			verbose(VERB_ALGO, "no current targets -- waiting "
+				"for %d targets to resolve.",
+				iq->num_target_queries);
+			qstate->ext_state[id] = module_wait_subquery;
+		} else {
+			verbose(VERB_ALGO, "no current targets -- waiting "
+				"for %d outstanding queries to respond.",
+				iq->num_current_queries);
+			qstate->ext_state[id] = module_wait_reply;
+		}
+		return 0;
+	}
+
+	/* We have a valid target. */
+	if(verbosity >= VERB_QUERY) {
+		log_query_info(VERB_QUERY, "sending query:", &iq->qchase);
+		log_name_addr(VERB_QUERY, "sending to target:", iq->dp->name, 
+			&target->addr, target->addrlen);
+		verbose(VERB_ALGO, "dnssec status: %s%s",
+			iq->dnssec_expected?"expected": "not expected",
+			iq->dnssec_lame_query?" but lame_query anyway": "");
+	}
+	fptr_ok(fptr_whitelist_modenv_send_query(qstate->env->send_query));
+	outq = (*qstate->env->send_query)(
+		iq->qchase.qname, iq->qchase.qname_len, 
+		iq->qchase.qtype, iq->qchase.qclass, 
+		iq->chase_flags | (iq->chase_to_rd?BIT_RD:0), EDNS_DO|BIT_CD, 
+		iq->dnssec_expected, &target->addr, target->addrlen,
+		iq->dp->name, iq->dp->namelen, qstate);
+	if(!outq) {
+		log_addr(VERB_DETAIL, "error sending query to auth server", 
+			&target->addr, target->addrlen);
+		return next_state(iq, QUERYTARGETS_STATE);
+	}
+	outbound_list_insert(&iq->outlist, outq);
+	iq->num_current_queries++;
+	iq->sent_count++;
+	qstate->ext_state[id] = module_wait_reply;
+
+	return 0;
+}
+
+/** find NS rrset in given list */
+static struct ub_packed_rrset_key*
+find_NS(struct reply_info* rep, size_t from, size_t to)
+{
+	size_t i;
+	for(i=from; i<to; i++) {
+		if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS)
+			return rep->rrsets[i];
+	}
+	return NULL;
+}
+
+
+/** 
+ * Process the query response. All queries end up at this state first. This
+ * process generally consists of analyzing the response and routing the
+ * event to the next state (either bouncing it back to a request state, or
+ * terminating the processing for this event).
+ * 
+ * @param qstate: query state.
+ * @param iq: iterator query state.
+ * @param id: module id.
+ * @return true if the event requires more immediate processing, false if
+ *         not. This is generally only true when forwarding the request to
+ *         the final state (i.e., on answer).
+ */
+static int
+processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
+	int id)
+{
+	int dnsseclame = 0;
+	enum response_type type;
+	iq->num_current_queries--;
+	if(iq->response == NULL) {
+		iq->chase_to_rd = 0;
+		iq->dnssec_lame_query = 0;
+		verbose(VERB_ALGO, "query response was timeout");
+		return next_state(iq, QUERYTARGETS_STATE);
+	}
+	type = response_type_from_server(
+		(int)((iq->chase_flags&BIT_RD) || iq->chase_to_rd),
+		iq->response, &iq->qchase, iq->dp);
+	iq->chase_to_rd = 0;
+	if(type == RESPONSE_TYPE_REFERRAL && (iq->chase_flags&BIT_RD)) {
+		/* When forwarding (RD bit is set), we handle referrals 
+		 * differently. No queries should be sent elsewhere */
+		type = RESPONSE_TYPE_ANSWER;
+	}
+	if(iq->dnssec_expected && !iq->dnssec_lame_query &&
+		!(iq->chase_flags&BIT_RD) 
+		&& type != RESPONSE_TYPE_LAME 
+		&& type != RESPONSE_TYPE_REC_LAME 
+		&& type != RESPONSE_TYPE_THROWAWAY 
+		&& type != RESPONSE_TYPE_UNTYPED) {
+		/* a possible answer, see if it is missing DNSSEC */
+		/* but not when forwarding, so we dont mark fwder lame */
+		/* also make sure the answer is from the zone we expected,
+		 * otherwise, (due to parent,child on same server), we
+		 * might mark the server,zone lame inappropriately */
+		if(!iter_msg_has_dnssec(iq->response) &&
+			iter_msg_from_zone(iq->response, iq->dp, type,
+				iq->qchase.qclass)) {
+			type = RESPONSE_TYPE_LAME;
+			dnsseclame = 1;
+		}
+	} else iq->dnssec_lame_query = 0;
+	/* see if referral brings us close to the target */
+	if(type == RESPONSE_TYPE_REFERRAL) {
+		struct ub_packed_rrset_key* ns = find_NS(
+			iq->response->rep, iq->response->rep->an_numrrsets,
+			iq->response->rep->an_numrrsets 
+			+ iq->response->rep->ns_numrrsets);
+		if(!ns) ns = find_NS(iq->response->rep, 0, 
+				iq->response->rep->an_numrrsets);
+		if(!ns || !dname_strict_subdomain_c(ns->rk.dname, iq->dp->name) 
+			|| !dname_subdomain_c(iq->qchase.qname, ns->rk.dname)){
+			verbose(VERB_ALGO, "bad referral, throwaway");
+			type = RESPONSE_TYPE_THROWAWAY;
+		} else
+			iter_scrub_ds(iq->response, ns, iq->dp->name);
+	} else iter_scrub_ds(iq->response, NULL, NULL);
+
+	/* handle each of the type cases */
+	if(type == RESPONSE_TYPE_ANSWER) {
+		/* ANSWER type responses terminate the query algorithm, 
+		 * so they sent on their */
+		if(verbosity >= VERB_DETAIL) {
+			verbose(VERB_DETAIL, "query response was %s",
+				FLAGS_GET_RCODE(iq->response->rep->flags)
+				==LDNS_RCODE_NXDOMAIN?"NXDOMAIN ANSWER":
+				(iq->response->rep->an_numrrsets?"ANSWER":
+				"nodata ANSWER"));
+		}
+		if(!iter_dns_store(qstate->env, &iq->response->qinfo,
+			iq->response->rep, 0, qstate->prefetch_leeway,
+			qstate->region))
+			return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+		/* close down outstanding requests to be discarded */
+		outbound_list_clear(&iq->outlist);
+		iq->num_current_queries = 0;
+		fptr_ok(fptr_whitelist_modenv_detach_subs(
+			qstate->env->detach_subs));
+		(*qstate->env->detach_subs)(qstate);
+		iq->num_target_queries = 0;
+		if(qstate->reply)
+			sock_list_insert(&qstate->reply_origin, 
+				&qstate->reply->addr, qstate->reply->addrlen, 
+				qstate->region);
+		return final_state(iq);
+	} else if(type == RESPONSE_TYPE_REFERRAL) {
+		/* REFERRAL type responses get a reset of the 
+		 * delegation point, and back to the QUERYTARGETS_STATE. */
+		verbose(VERB_DETAIL, "query response was REFERRAL");
+
+		/* if hardened, only store referral if we asked for it */
+		if(!qstate->env->cfg->harden_referral_path ||
+		    (  qstate->qinfo.qtype == LDNS_RR_TYPE_NS 
+			&& (qstate->query_flags&BIT_RD) 
+			&& !(qstate->query_flags&BIT_CD)
+			   /* we know that all other NS rrsets are scrubbed
+			    * away, thus on referral only one is left.
+			    * see if that equals the query name... */
+			&& ( /* auth section, but sometimes in answer section*/
+			  reply_find_rrset_section_ns(iq->response->rep,
+				iq->qchase.qname, iq->qchase.qname_len,
+				LDNS_RR_TYPE_NS, iq->qchase.qclass)
+			  || reply_find_rrset_section_an(iq->response->rep,
+				iq->qchase.qname, iq->qchase.qname_len,
+				LDNS_RR_TYPE_NS, iq->qchase.qclass)
+			  )
+		    )) {
+			/* Store the referral under the current query */
+			/* no prefetch-leeway, since its not the answer */
+			if(!iter_dns_store(qstate->env, &iq->response->qinfo,
+				iq->response->rep, 1, 0, NULL))
+				return error_response(qstate, id, 
+					LDNS_RCODE_SERVFAIL);
+			if(iq->store_parent_NS)
+				iter_store_parentside_NS(qstate->env, 
+					iq->response->rep);
+			if(qstate->env->neg_cache)
+				val_neg_addreferral(qstate->env->neg_cache, 
+					iq->response->rep, iq->dp->name);
+		}
+		/* store parent-side-in-zone-glue, if directly queried for */
+		if(iq->query_for_pside_glue && !iq->pside_glue) {
+			iq->pside_glue = reply_find_rrset(iq->response->rep, 
+				iq->qchase.qname, iq->qchase.qname_len, 
+				iq->qchase.qtype, iq->qchase.qclass);
+			if(iq->pside_glue) {
+				log_rrset_key(VERB_ALGO, "found parent-side "
+					"glue", iq->pside_glue);
+				iter_store_parentside_rrset(qstate->env,
+					iq->pside_glue);
+			}
+		}
+
+		/* Reset the event state, setting the current delegation 
+		 * point to the referral. */
+		iq->deleg_msg = iq->response;
+		iq->dp = delegpt_from_message(iq->response, qstate->region);
+		if(!iq->dp)
+			return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+		if(!cache_fill_missing(qstate->env, iq->qchase.qclass, 
+			qstate->region, iq->dp))
+			return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+		if(iq->store_parent_NS && query_dname_compare(iq->dp->name,
+			iq->store_parent_NS->name) == 0)
+			iter_merge_retry_counts(iq->dp, iq->store_parent_NS);
+		delegpt_log(VERB_ALGO, iq->dp);
+		/* Count this as a referral. */
+		iq->referral_count++;
+		iq->sent_count = 0;
+		/* see if the next dp is a trust anchor, or a DS was sent
+		 * along, indicating dnssec is expected for next zone */
+		iq->dnssec_expected = iter_indicates_dnssec(qstate->env, 
+			iq->dp, iq->response, iq->qchase.qclass);
+		/* if dnssec, validating then also fetch the key for the DS */
+		if(iq->dnssec_expected && qstate->env->cfg->prefetch_key &&
+			!(qstate->query_flags&BIT_CD))
+			generate_dnskey_prefetch(qstate, iq, id);
+
+		/* spawn off NS and addr to auth servers for the NS we just
+		 * got in the referral. This gets authoritative answer
+		 * (answer section trust level) rrset. 
+		 * right after, we detach the subs, answer goes to cache. */
+		if(qstate->env->cfg->harden_referral_path)
+			generate_ns_check(qstate, iq, id);
+
+		/* stop current outstanding queries. 
+		 * FIXME: should the outstanding queries be waited for and
+		 * handled? Say by a subquery that inherits the outbound_entry.
+		 */
+		outbound_list_clear(&iq->outlist);
+		iq->num_current_queries = 0;
+		fptr_ok(fptr_whitelist_modenv_detach_subs(
+			qstate->env->detach_subs));
+		(*qstate->env->detach_subs)(qstate);
+		iq->num_target_queries = 0;
+		verbose(VERB_ALGO, "cleared outbound list for next round");
+		return next_state(iq, QUERYTARGETS_STATE);
+	} else if(type == RESPONSE_TYPE_CNAME) {
+		uint8_t* sname = NULL;
+		size_t snamelen = 0;
+		/* CNAME type responses get a query restart (i.e., get a 
+		 * reset of the query state and go back to INIT_REQUEST_STATE).
+		 */
+		verbose(VERB_DETAIL, "query response was CNAME");
+		if(verbosity >= VERB_ALGO)
+			log_dns_msg("cname msg", &iq->response->qinfo, 
+				iq->response->rep);
+		/* Process the CNAME response. */
+		if(!handle_cname_response(qstate, iq, iq->response, 
+			&sname, &snamelen))
+			return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+		/* cache the CNAME response under the current query */
+		/* NOTE : set referral=1, so that rrsets get stored but not 
+		 * the partial query answer (CNAME only). */
+		/* prefetchleeway applied because this updates answer parts */
+		if(!iter_dns_store(qstate->env, &iq->response->qinfo,
+			iq->response->rep, 1, qstate->prefetch_leeway, NULL))
+			return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+		/* set the current request's qname to the new value. */
+		iq->qchase.qname = sname;
+		iq->qchase.qname_len = snamelen;
+		/* Clear the query state, since this is a query restart. */
+		iq->deleg_msg = NULL;
+		iq->dp = NULL;
+		/* Note the query restart. */
+		iq->query_restart_count++;
+		iq->sent_count = 0;
+
+		/* stop current outstanding queries. 
+		 * FIXME: should the outstanding queries be waited for and
+		 * handled? Say by a subquery that inherits the outbound_entry.
+		 */
+		outbound_list_clear(&iq->outlist);
+		iq->num_current_queries = 0;
+		fptr_ok(fptr_whitelist_modenv_detach_subs(
+			qstate->env->detach_subs));
+		(*qstate->env->detach_subs)(qstate);
+		iq->num_target_queries = 0;
+		if(qstate->reply)
+			sock_list_insert(&qstate->reply_origin, 
+				&qstate->reply->addr, qstate->reply->addrlen, 
+				qstate->region);
+		verbose(VERB_ALGO, "cleared outbound list for query restart");
+		/* go to INIT_REQUEST_STATE for new qname. */
+		return next_state(iq, INIT_REQUEST_STATE);
+	} else if(type == RESPONSE_TYPE_LAME) {
+		/* Cache the LAMEness. */
+		verbose(VERB_DETAIL, "query response was %sLAME",
+			dnsseclame?"DNSSEC ":"");
+		if(!dname_subdomain_c(iq->qchase.qname, iq->dp->name)) {
+			log_err("mark lame: mismatch in qname and dpname");
+			/* throwaway this reply below */
+		} else if(qstate->reply) {
+			/* need addr for lameness cache, but we may have
+			 * gotten this from cache, so test to be sure */
+			if(!infra_set_lame(qstate->env->infra_cache, 
+				&qstate->reply->addr, qstate->reply->addrlen, 
+				iq->dp->name, iq->dp->namelen, 
+				*qstate->env->now, dnsseclame, 0,
+				iq->qchase.qtype))
+				log_err("mark host lame: out of memory");
+		} else log_err("%slame response from cache",
+			dnsseclame?"DNSSEC ":"");
+	} else if(type == RESPONSE_TYPE_REC_LAME) {
+		/* Cache the LAMEness. */
+		verbose(VERB_DETAIL, "query response REC_LAME: "
+			"recursive but not authoritative server");
+		if(!dname_subdomain_c(iq->qchase.qname, iq->dp->name)) {
+			log_err("mark rec_lame: mismatch in qname and dpname");
+			/* throwaway this reply below */
+		} else if(qstate->reply) {
+			/* need addr for lameness cache, but we may have
+			 * gotten this from cache, so test to be sure */
+			verbose(VERB_DETAIL, "mark as REC_LAME");
+			if(!infra_set_lame(qstate->env->infra_cache, 
+				&qstate->reply->addr, qstate->reply->addrlen, 
+				iq->dp->name, iq->dp->namelen, 
+				*qstate->env->now, 0, 1, iq->qchase.qtype))
+				log_err("mark host lame: out of memory");
+		} 
+	} else if(type == RESPONSE_TYPE_THROWAWAY) {
+		/* LAME and THROWAWAY responses are handled the same way. 
+		 * In this case, the event is just sent directly back to 
+		 * the QUERYTARGETS_STATE without resetting anything, 
+		 * because, clearly, the next target must be tried. */
+		verbose(VERB_DETAIL, "query response was THROWAWAY");
+	} else {
+		log_warn("A query response came back with an unknown type: %d",
+			(int)type);
+	}
+
+	/* LAME, THROWAWAY and "unknown" all end up here.
+	 * Recycle to the QUERYTARGETS state to hopefully try a 
+	 * different target. */
+	return next_state(iq, QUERYTARGETS_STATE);
+}
+
+/**
+ * Return priming query results to interestes super querystates.
+ * 
+ * Sets the delegation point and delegation message (not nonRD queries).
+ * This is a callback from walk_supers.
+ *
+ * @param qstate: priming query state that finished.
+ * @param id: module id.
+ * @param forq: the qstate for which priming has been done.
+ */
+static void
+prime_supers(struct module_qstate* qstate, int id, struct module_qstate* forq)
+{
+	struct iter_qstate* foriq = (struct iter_qstate*)forq->minfo[id];
+	struct delegpt* dp = NULL;
+
+	log_assert(qstate->is_priming || foriq->wait_priming_stub);
+	log_assert(qstate->return_rcode == LDNS_RCODE_NOERROR);
+	/* Convert our response to a delegation point */
+	dp = delegpt_from_message(qstate->return_msg, forq->region);
+	if(!dp) {
+		/* if there is no convertable delegation point, then 
+		 * the ANSWER type was (presumably) a negative answer. */
+		verbose(VERB_ALGO, "prime response was not a positive "
+			"ANSWER; failing");
+		foriq->dp = NULL;
+		foriq->state = QUERYTARGETS_STATE;
+		return;
+	}
+
+	log_query_info(VERB_DETAIL, "priming successful for", &qstate->qinfo);
+	delegpt_log(VERB_ALGO, dp);
+	foriq->dp = dp;
+	foriq->deleg_msg = dns_copy_msg(qstate->return_msg, forq->region);
+	if(!foriq->deleg_msg) {
+		log_err("copy prime response: out of memory");
+		foriq->dp = NULL;
+		foriq->state = QUERYTARGETS_STATE;
+		return;
+	}
+
+	/* root priming responses go to init stage 2, priming stub 
+	 * responses to to stage 3. */
+	if(foriq->wait_priming_stub) {
+		foriq->state = INIT_REQUEST_3_STATE;
+		foriq->wait_priming_stub = 0;
+	} else	foriq->state = INIT_REQUEST_2_STATE;
+	/* because we are finished, the parent will be reactivated */
+}
+
+/** 
+ * This handles the response to a priming query. This is used to handle both
+ * root and stub priming responses. This is basically the equivalent of the
+ * QUERY_RESP_STATE, but will not handle CNAME responses and will treat
+ * REFERRALs as ANSWERS. It will also update and reactivate the originating
+ * event.
+ *
+ * @param qstate: query state.
+ * @param id: module id.
+ * @return true if the event needs more immediate processing, false if not.
+ *         This state always returns false.
+ */
+static int
+processPrimeResponse(struct module_qstate* qstate, int id)
+{
+	struct iter_qstate* iq = (struct iter_qstate*)qstate->minfo[id];
+	enum response_type type;
+	iq->response->rep->flags &= ~(BIT_RD|BIT_RA); /* ignore rec-lame */
+	type = response_type_from_server(
+		(int)((iq->chase_flags&BIT_RD) || iq->chase_to_rd), 
+		iq->response, &iq->qchase, iq->dp);
+	if(type == RESPONSE_TYPE_ANSWER) {
+		qstate->return_rcode = LDNS_RCODE_NOERROR;
+		qstate->return_msg = iq->response;
+	} else {
+		qstate->return_rcode = LDNS_RCODE_SERVFAIL;
+		qstate->return_msg = NULL;
+	}
+
+	/* validate the root or stub after priming (if enabled).
+	 * This is the same query as the prime query, but with validation.
+	 * Now that we are primed, the additional queries that validation
+	 * may need can be resolved, such as DLV. */
+	if(qstate->env->cfg->harden_referral_path) {
+		struct module_qstate* subq = NULL;
+		log_nametypeclass(VERB_ALGO, "schedule prime validation", 
+			qstate->qinfo.qname, qstate->qinfo.qtype,
+			qstate->qinfo.qclass);
+		if(!generate_sub_request(qstate->qinfo.qname, 
+			qstate->qinfo.qname_len, qstate->qinfo.qtype,
+			qstate->qinfo.qclass, qstate, id, iq,
+			INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1)) {
+			verbose(VERB_ALGO, "could not generate prime check");
+		}
+		generate_a_aaaa_check(qstate, iq, id);
+	}
+
+	/* This event is finished. */
+	qstate->ext_state[id] = module_finished;
+	return 0;
+}
+
+/** 
+ * Do final processing on responses to target queries. Events reach this
+ * state after the iterative resolution algorithm terminates. This state is
+ * responsible for reactiving the original event, and housekeeping related
+ * to received target responses (caching, updating the current delegation
+ * point, etc).
+ * Callback from walk_supers for every super state that is interested in 
+ * the results from this query.
+ *
+ * @param qstate: query state.
+ * @param id: module id.
+ * @param forq: super query state.
+ */
+static void
+processTargetResponse(struct module_qstate* qstate, int id,
+	struct module_qstate* forq)
+{
+	struct iter_qstate* iq = (struct iter_qstate*)qstate->minfo[id];
+	struct iter_qstate* foriq = (struct iter_qstate*)forq->minfo[id];
+	struct ub_packed_rrset_key* rrset;
+	struct delegpt_ns* dpns;
+	log_assert(qstate->return_rcode == LDNS_RCODE_NOERROR);
+
+	foriq->state = QUERYTARGETS_STATE;
+	log_query_info(VERB_ALGO, "processTargetResponse", &qstate->qinfo);
+	log_query_info(VERB_ALGO, "processTargetResponse super", &forq->qinfo);
+
+	/* check to see if parent event is still interested (in orig name).  */
+	if(!foriq->dp) {
+		verbose(VERB_ALGO, "subq: parent not interested, was reset");
+		return; /* not interested anymore */
+	}
+	dpns = delegpt_find_ns(foriq->dp, qstate->qinfo.qname,
+			qstate->qinfo.qname_len);
+	if(!dpns) {
+		/* If not interested, just stop processing this event */
+		verbose(VERB_ALGO, "subq: parent not interested anymore");
+		/* could be because parent was jostled out of the cache,
+		   and a new identical query arrived, that does not want it*/
+		return;
+	}
+
+	/* Tell the originating event that this target query has finished
+	 * (regardless if it succeeded or not). */
+	foriq->num_target_queries--;
+
+	/* if iq->query_for_pside_glue then add the pside_glue (marked lame) */
+	if(iq->pside_glue) {
+		/* if the pside_glue is NULL, then it could not be found,
+		 * the done_pside is already set when created and a cache
+		 * entry created in processFinished so nothing to do here */
+		log_rrset_key(VERB_ALGO, "add parentside glue to dp", 
+			iq->pside_glue);
+		if(!delegpt_add_rrset(foriq->dp, forq->region, 
+			iq->pside_glue, 1))
+			log_err("out of memory adding pside glue");
+	}
+
+	/* This response is relevant to the current query, so we 
+	 * add (attempt to add, anyway) this target(s) and reactivate 
+	 * the original event. 
+	 * NOTE: we could only look for the AnswerRRset if the 
+	 * response type was ANSWER. */
+	rrset = reply_find_answer_rrset(&iq->qchase, qstate->return_msg->rep);
+	if(rrset) {
+		/* if CNAMEs have been followed - add new NS to delegpt. */
+		/* BTW. RFC 1918 says NS should not have got CNAMEs. Robust. */
+		if(!delegpt_find_ns(foriq->dp, rrset->rk.dname, 
+			rrset->rk.dname_len)) {
+			/* if dpns->lame then set newcname ns lame too */
+			if(!delegpt_add_ns(foriq->dp, forq->region, 
+				rrset->rk.dname, (int)dpns->lame))
+				log_err("out of memory adding cnamed-ns");
+		}
+		/* if dpns->lame then set the address(es) lame too */
+		if(!delegpt_add_rrset(foriq->dp, forq->region, rrset, 
+			(int)dpns->lame))
+			log_err("out of memory adding targets");
+		verbose(VERB_ALGO, "added target response");
+		delegpt_log(VERB_ALGO, foriq->dp);
+	} else {
+		verbose(VERB_ALGO, "iterator TargetResponse failed");
+		dpns->resolved = 1; /* fail the target */
+	}
+}
+
+/**
+ * Process response for qclass=ANY queries for a particular class.
+ * Append to result or error-exit.
+ *
+ * @param qstate: query state.
+ * @param id: module id.
+ * @param forq: super query state.
+ */
+static void
+processClassResponse(struct module_qstate* qstate, int id,
+	struct module_qstate* forq)
+{
+	struct iter_qstate* foriq = (struct iter_qstate*)forq->minfo[id];
+	struct dns_msg* from = qstate->return_msg;
+	log_query_info(VERB_ALGO, "processClassResponse", &qstate->qinfo);
+	log_query_info(VERB_ALGO, "processClassResponse super", &forq->qinfo);
+	if(qstate->return_rcode != LDNS_RCODE_NOERROR) {
+		/* cause servfail for qclass ANY query */
+		foriq->response = NULL;
+		foriq->state = FINISHED_STATE;
+		return;
+	}
+	/* append result */
+	if(!foriq->response) {
+		/* allocate the response: copy RCODE, sec_state */
+		foriq->response = dns_copy_msg(from, forq->region);
+		if(!foriq->response) {
+			log_err("malloc failed for qclass ANY response"); 
+			foriq->state = FINISHED_STATE;
+			return;
+		}
+		foriq->response->qinfo.qclass = forq->qinfo.qclass;
+		/* qclass ANY does not receive the AA flag on replies */
+		foriq->response->rep->authoritative = 0; 
+	} else {
+		struct dns_msg* to = foriq->response;
+		/* add _from_ this response _to_ existing collection */
+		/* if there are records, copy RCODE */
+		/* lower sec_state if this message is lower */
+		if(from->rep->rrset_count != 0) {
+			size_t n = from->rep->rrset_count+to->rep->rrset_count;
+			struct ub_packed_rrset_key** dest, **d;
+			/* copy appropriate rcode */
+			to->rep->flags = from->rep->flags;
+			/* copy rrsets */
+			dest = regional_alloc(forq->region, sizeof(dest[0])*n);
+			if(!dest) {
+				log_err("malloc failed in collect ANY"); 
+				foriq->state = FINISHED_STATE;
+				return;
+			}
+			d = dest;
+			/* copy AN */
+			memcpy(dest, to->rep->rrsets, to->rep->an_numrrsets
+				* sizeof(dest[0]));
+			dest += to->rep->an_numrrsets;
+			memcpy(dest, from->rep->rrsets, from->rep->an_numrrsets
+				* sizeof(dest[0]));
+			dest += from->rep->an_numrrsets;
+			/* copy NS */
+			memcpy(dest, to->rep->rrsets+to->rep->an_numrrsets,
+				to->rep->ns_numrrsets * sizeof(dest[0]));
+			dest += to->rep->ns_numrrsets;
+			memcpy(dest, from->rep->rrsets+from->rep->an_numrrsets,
+				from->rep->ns_numrrsets * sizeof(dest[0]));
+			dest += from->rep->ns_numrrsets;
+			/* copy AR */
+			memcpy(dest, to->rep->rrsets+to->rep->an_numrrsets+
+				to->rep->ns_numrrsets,
+				to->rep->ar_numrrsets * sizeof(dest[0]));
+			dest += to->rep->ar_numrrsets;
+			memcpy(dest, from->rep->rrsets+from->rep->an_numrrsets+
+				from->rep->ns_numrrsets,
+				from->rep->ar_numrrsets * sizeof(dest[0]));
+			/* update counts */
+			to->rep->rrsets = d;
+			to->rep->an_numrrsets += from->rep->an_numrrsets;
+			to->rep->ns_numrrsets += from->rep->ns_numrrsets;
+			to->rep->ar_numrrsets += from->rep->ar_numrrsets;
+			to->rep->rrset_count = n;
+		}
+		if(from->rep->security < to->rep->security) /* lowest sec */
+			to->rep->security = from->rep->security;
+		if(from->rep->qdcount != 0) /* insert qd if appropriate */
+			to->rep->qdcount = from->rep->qdcount;
+		if(from->rep->ttl < to->rep->ttl) /* use smallest TTL */
+			to->rep->ttl = from->rep->ttl;
+		if(from->rep->prefetch_ttl < to->rep->prefetch_ttl)
+			to->rep->prefetch_ttl = from->rep->prefetch_ttl;
+	}
+	/* are we done? */
+	foriq->num_current_queries --;
+	if(foriq->num_current_queries == 0)
+		foriq->state = FINISHED_STATE;
+}
+	
+/** 
+ * Collect class ANY responses and make them into one response.  This
+ * state is started and it creates queries for all classes (that have
+ * root hints).  The answers are then collected.
+ *
+ * @param qstate: query state.
+ * @param id: module id.
+ * @return true if the event needs more immediate processing, false if not.
+ */
+static int
+processCollectClass(struct module_qstate* qstate, int id)
+{
+	struct iter_qstate* iq = (struct iter_qstate*)qstate->minfo[id];
+	struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id];
+	struct module_qstate* subq;
+	/* If qchase.qclass == 0 then send out queries for all classes.
+	 * Otherwise, do nothing (wait for all answers to arrive and the
+	 * processClassResponse to put them together, and that moves us
+	 * towards the Finished state when done. */
+	if(iq->qchase.qclass == 0) {
+		uint16_t c = 0;
+		iq->qchase.qclass = LDNS_RR_CLASS_ANY;
+		while(iter_get_next_root(ie->hints, qstate->env->fwds, &c)) {
+			/* generate query for this class */
+			log_nametypeclass(VERB_ALGO, "spawn collect query",
+				qstate->qinfo.qname, qstate->qinfo.qtype, c);
+			if(!generate_sub_request(qstate->qinfo.qname,
+				qstate->qinfo.qname_len, qstate->qinfo.qtype,
+				c, qstate, id, iq, INIT_REQUEST_STATE,
+				FINISHED_STATE, &subq, 
+				(int)!(qstate->query_flags&BIT_CD))) {
+				return error_response(qstate, id, 
+					LDNS_RCODE_SERVFAIL);
+			}
+			/* ignore subq, no special init required */
+			iq->num_current_queries ++;
+			if(c == 0xffff)
+				break;
+			else c++;
+		}
+		/* if no roots are configured at all, return */
+		if(iq->num_current_queries == 0) {
+			verbose(VERB_ALGO, "No root hints or fwds, giving up "
+				"on qclass ANY");
+			return error_response(qstate, id, LDNS_RCODE_REFUSED);
+		}
+		/* return false, wait for queries to return */
+	}
+	/* if woke up here because of an answer, wait for more answers */
+	return 0;
+}
+
+/** 
+ * This handles the final state for first-tier responses (i.e., responses to
+ * externally generated queries).
+ *
+ * @param qstate: query state.
+ * @param iq: iterator query state.
+ * @param id: module id.
+ * @return true if the event needs more processing, false if not. Since this
+ *         is the final state for an event, it always returns false.
+ */
+static int
+processFinished(struct module_qstate* qstate, struct iter_qstate* iq,
+	int id)
+{
+	log_query_info(VERB_QUERY, "finishing processing for", 
+		&qstate->qinfo);
+
+	/* store negative cache element for parent side glue. */
+	if(iq->query_for_pside_glue && !iq->pside_glue)
+		iter_store_parentside_neg(qstate->env, &qstate->qinfo,
+			iq->deleg_msg?iq->deleg_msg->rep:
+			(iq->response?iq->response->rep:NULL));
+	if(!iq->response) {
+		verbose(VERB_ALGO, "No response is set, servfail");
+		return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+	}
+
+	/* Make sure that the RA flag is set (since the presence of 
+	 * this module means that recursion is available) */
+	iq->response->rep->flags |= BIT_RA;
+
+	/* Clear the AA flag */
+	/* FIXME: does this action go here or in some other module? */
+	iq->response->rep->flags &= ~BIT_AA;
+
+	/* make sure QR flag is on */
+	iq->response->rep->flags |= BIT_QR;
+
+	/* we have finished processing this query */
+	qstate->ext_state[id] = module_finished;
+
+	/* TODO:  we are using a private TTL, trim the response. */
+	/* if (mPrivateTTL > 0){IterUtils.setPrivateTTL(resp, mPrivateTTL); } */
+
+	/* prepend any items we have accumulated */
+	if(iq->an_prepend_list || iq->ns_prepend_list) {
+		if(!iter_prepend(iq, iq->response, qstate->region)) {
+			log_err("prepend rrsets: out of memory");
+			return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+		}
+		/* reset the query name back */
+		iq->response->qinfo = qstate->qinfo;
+		/* the security state depends on the combination */
+		iq->response->rep->security = sec_status_unchecked;
+		/* store message with the finished prepended items,
+		 * but only if we did recursion. The nonrecursion referral
+		 * from cache does not need to be stored in the msg cache. */
+		if(qstate->query_flags&BIT_RD) {
+			if(!iter_dns_store(qstate->env, &qstate->qinfo, 
+				iq->response->rep, 0, qstate->prefetch_leeway,
+				qstate->region))
+				return error_response(qstate, id, 
+					LDNS_RCODE_SERVFAIL);
+		}
+	}
+	qstate->return_rcode = LDNS_RCODE_NOERROR;
+	qstate->return_msg = iq->response;
+	return 0;
+}
+
+/*
+ * Return priming query results to interestes super querystates.
+ * 
+ * Sets the delegation point and delegation message (not nonRD queries).
+ * This is a callback from walk_supers.
+ *
+ * @param qstate: query state that finished.
+ * @param id: module id.
+ * @param super: the qstate to inform.
+ */
+void
+iter_inform_super(struct module_qstate* qstate, int id, 
+	struct module_qstate* super)
+{
+	if(!qstate->is_priming && super->qinfo.qclass == LDNS_RR_CLASS_ANY)
+		processClassResponse(qstate, id, super);
+	else if(qstate->return_rcode != LDNS_RCODE_NOERROR)
+		error_supers(qstate, id, super);
+	else if(qstate->is_priming)
+		prime_supers(qstate, id, super);
+	else	processTargetResponse(qstate, id, super);
+}
+
+/**
+ * Handle iterator state.
+ * Handle events. This is the real processing loop for events, responsible
+ * for moving events through the various states. If a processing method
+ * returns true, then it will be advanced to the next state. If false, then
+ * processing will stop.
+ *
+ * @param qstate: query state.
+ * @param ie: iterator shared global environment.
+ * @param iq: iterator query state.
+ * @param id: module id.
+ */
+static void
+iter_handle(struct module_qstate* qstate, struct iter_qstate* iq,
+	struct iter_env* ie, int id)
+{
+	int cont = 1;
+	while(cont) {
+		verbose(VERB_ALGO, "iter_handle processing q with state %s",
+			iter_state_to_string(iq->state));
+		switch(iq->state) {
+			case INIT_REQUEST_STATE:
+				cont = processInitRequest(qstate, iq, ie, id);
+				break;
+			case INIT_REQUEST_2_STATE:
+				cont = processInitRequest2(qstate, iq, ie, id);
+				break;
+			case INIT_REQUEST_3_STATE:
+				cont = processInitRequest3(qstate, iq, id);
+				break;
+			case QUERYTARGETS_STATE:
+				cont = processQueryTargets(qstate, iq, ie, id);
+				break;
+			case QUERY_RESP_STATE:
+				cont = processQueryResponse(qstate, iq, id);
+				break;
+			case PRIME_RESP_STATE:
+				cont = processPrimeResponse(qstate, id);
+				break;
+			case COLLECT_CLASS_STATE:
+				cont = processCollectClass(qstate, id);
+				break;
+			case FINISHED_STATE:
+				cont = processFinished(qstate, iq, id);
+				break;
+			default:
+				log_warn("iterator: invalid state: %d",
+					iq->state);
+				cont = 0;
+				break;
+		}
+	}
+}
+
+/** 
+ * This is the primary entry point for processing request events. Note that
+ * this method should only be used by external modules.
+ * @param qstate: query state.
+ * @param ie: iterator shared global environment.
+ * @param iq: iterator query state.
+ * @param id: module id.
+ */
+static void
+process_request(struct module_qstate* qstate, struct iter_qstate* iq,
+	struct iter_env* ie, int id)
+{
+	/* external requests start in the INIT state, and finish using the
+	 * FINISHED state. */
+	iq->state = INIT_REQUEST_STATE;
+	iq->final_state = FINISHED_STATE;
+	verbose(VERB_ALGO, "process_request: new external request event");
+	iter_handle(qstate, iq, ie, id);
+}
+
+/** process authoritative server reply */
+static void
+process_response(struct module_qstate* qstate, struct iter_qstate* iq, 
+	struct iter_env* ie, int id, struct outbound_entry* outbound,
+	enum module_ev event)
+{
+	struct msg_parse* prs;
+	struct edns_data edns;
+	ldns_buffer* pkt;
+
+	verbose(VERB_ALGO, "process_response: new external response event");
+	iq->response = NULL;
+	iq->state = QUERY_RESP_STATE;
+	if(event == module_event_noreply || event == module_event_error) {
+		goto handle_it;
+	}
+	if( (event != module_event_reply && event != module_event_capsfail)
+		|| !qstate->reply) {
+		log_err("Bad event combined with response");
+		outbound_list_remove(&iq->outlist, outbound);
+		(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+		return;
+	}
+
+	/* parse message */
+	prs = (struct msg_parse*)regional_alloc(qstate->env->scratch, 
+		sizeof(struct msg_parse));
+	if(!prs) {
+		log_err("out of memory on incoming message");
+		/* like packet got dropped */
+		goto handle_it;
+	}
+	memset(prs, 0, sizeof(*prs));
+	memset(&edns, 0, sizeof(edns));
+	pkt = qstate->reply->c->buffer;
+	ldns_buffer_set_position(pkt, 0);
+	if(parse_packet(pkt, prs, qstate->env->scratch) != LDNS_RCODE_NOERROR) {
+		verbose(VERB_ALGO, "parse error on reply packet");
+		goto handle_it;
+	}
+	/* edns is not examined, but removed from message to help cache */
+	if(parse_extract_edns(prs, &edns) != LDNS_RCODE_NOERROR)
+		goto handle_it;
+	/* remove CD-bit, we asked for in case we handle validation ourself */
+	prs->flags &= ~BIT_CD;
+
+	/* normalize and sanitize: easy to delete items from linked lists */
+	if(!scrub_message(pkt, prs, &iq->qchase, iq->dp->name, 
+		qstate->env->scratch, qstate->env, ie))
+		goto handle_it;
+
+	/* allocate response dns_msg in region */
+	iq->response = dns_alloc_msg(pkt, prs, qstate->region);
+	if(!iq->response)
+		goto handle_it;
+	log_query_info(VERB_DETAIL, "response for", &qstate->qinfo);
+	log_name_addr(VERB_DETAIL, "reply from", iq->dp->name, 
+		&qstate->reply->addr, qstate->reply->addrlen);
+	if(verbosity >= VERB_ALGO)
+		log_dns_msg("incoming scrubbed packet:", &iq->response->qinfo, 
+			iq->response->rep);
+	
+	if(event == module_event_capsfail) {
+		if(!iq->caps_fallback) {
+			/* start fallback */
+			iq->caps_fallback = 1;
+			iq->caps_server = 0;
+			iq->caps_reply = iq->response->rep;
+			iq->state = QUERYTARGETS_STATE;
+			iq->num_current_queries--;
+			verbose(VERB_DETAIL, "Capsforid: starting fallback");
+			goto handle_it;
+		} else {
+			/* check if reply is the same, otherwise, fail */
+			if(!reply_equal(iq->response->rep, iq->caps_reply,
+				qstate->env->scratch_buffer)) {
+				verbose(VERB_DETAIL, "Capsforid fallback: "
+					"getting different replies, failed");
+				outbound_list_remove(&iq->outlist, outbound);
+				(void)error_response(qstate, id, 
+					LDNS_RCODE_SERVFAIL);
+				return;
+			}
+			/* continue the fallback procedure at next server */
+			iq->caps_server++;
+			iq->state = QUERYTARGETS_STATE;
+			iq->num_current_queries--;
+			verbose(VERB_DETAIL, "Capsforid: reply is equal. "
+				"go to next fallback");
+			goto handle_it;
+		}
+	}
+	iq->caps_fallback = 0; /* if we were in fallback, 0x20 is OK now */
+
+handle_it:
+	outbound_list_remove(&iq->outlist, outbound);
+	iter_handle(qstate, iq, ie, id);
+}
+
+void 
+iter_operate(struct module_qstate* qstate, enum module_ev event, int id,
+	struct outbound_entry* outbound)
+{
+	struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id];
+	struct iter_qstate* iq = (struct iter_qstate*)qstate->minfo[id];
+	verbose(VERB_QUERY, "iterator[module %d] operate: extstate:%s event:%s", 
+		id, strextstate(qstate->ext_state[id]), strmodulevent(event));
+	if(iq) log_query_info(VERB_QUERY, "iterator operate: query", 
+		&qstate->qinfo);
+	if(iq && qstate->qinfo.qname != iq->qchase.qname)
+		log_query_info(VERB_QUERY, "iterator operate: chased to", 
+			&iq->qchase);
+
+	/* perform iterator state machine */
+	if((event == module_event_new || event == module_event_pass) && 
+		iq == NULL) {
+		if(!iter_new(qstate, id)) {
+			(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+			return;
+		}
+		iq = (struct iter_qstate*)qstate->minfo[id];
+		process_request(qstate, iq, ie, id);
+		return;
+	}
+	if(iq && event == module_event_pass) {
+		iter_handle(qstate, iq, ie, id);
+		return;
+	}
+	if(iq && outbound) {
+		process_response(qstate, iq, ie, id, outbound, event);
+		return;
+	}
+	if(event == module_event_error) {
+		verbose(VERB_ALGO, "got called with event error, giving up");
+		(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+		return;
+	}
+
+	log_err("bad event for iterator");
+	(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+}
+
+void 
+iter_clear(struct module_qstate* qstate, int id)
+{
+	struct iter_qstate* iq;
+	if(!qstate)
+		return;
+	iq = (struct iter_qstate*)qstate->minfo[id];
+	if(iq) {
+		outbound_list_clear(&iq->outlist);
+		iq->num_current_queries = 0;
+	}
+	qstate->minfo[id] = NULL;
+}
+
+size_t 
+iter_get_mem(struct module_env* env, int id)
+{
+	struct iter_env* ie = (struct iter_env*)env->modinfo[id];
+	if(!ie)
+		return 0;
+	return sizeof(*ie) + sizeof(int)*((size_t)ie->max_dependency_depth+1)
+		+ hints_get_mem(ie->hints) + donotq_get_mem(ie->donotq)
+		+ priv_get_mem(ie->priv);
+}
+
+/**
+ * The iterator function block 
+ */
+static struct module_func_block iter_block = {
+	"iterator",
+	&iter_init, &iter_deinit, &iter_operate, &iter_inform_super, 
+	&iter_clear, &iter_get_mem
+};
+
+struct module_func_block* 
+iter_get_funcblock(void)
+{
+	return &iter_block;
+}
+
+const char* 
+iter_state_to_string(enum iter_state state)
+{
+	switch (state)
+	{
+	case INIT_REQUEST_STATE :
+		return "INIT REQUEST STATE";
+	case INIT_REQUEST_2_STATE :
+		return "INIT REQUEST STATE (stage 2)";
+	case INIT_REQUEST_3_STATE:
+		return "INIT REQUEST STATE (stage 3)";
+	case QUERYTARGETS_STATE :
+		return "QUERY TARGETS STATE";
+	case PRIME_RESP_STATE :
+		return "PRIME RESPONSE STATE";
+	case COLLECT_CLASS_STATE :
+		return "COLLECT CLASS STATE";
+	case QUERY_RESP_STATE :
+		return "QUERY RESPONSE STATE";
+	case FINISHED_STATE :
+		return "FINISHED RESPONSE STATE";
+	default :
+		return "UNKNOWN ITER STATE";
+	}
+}
+
+int 
+iter_state_is_responsestate(enum iter_state s)
+{
+	switch(s) {
+		case INIT_REQUEST_STATE :
+		case INIT_REQUEST_2_STATE :
+		case INIT_REQUEST_3_STATE :
+		case QUERYTARGETS_STATE :
+		case COLLECT_CLASS_STATE :
+			return 0;
+		default:
+			break;
+	}
+	return 1;
+}
diff --git a/3rdParty/Unbound/src/src/iterator/iterator.h b/3rdParty/Unbound/src/src/iterator/iterator.h
new file mode 100644
index 0000000..0272fe1
--- /dev/null
+++ b/3rdParty/Unbound/src/src/iterator/iterator.h
@@ -0,0 +1,374 @@
+/*
+ * iterator/iterator.h - iterative resolver DNS query response module
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains a module that performs recusive iterative DNS query
+ * processing.
+ */
+
+#ifndef ITERATOR_ITERATOR_H
+#define ITERATOR_ITERATOR_H
+#include "services/outbound_list.h"
+#include "util/data/msgreply.h"
+#include "util/module.h"
+struct delegpt;
+struct iter_hints;
+struct iter_forwards;
+struct iter_donotq;
+struct iter_prep_list;
+struct iter_priv;
+
+/** max number of query restarts. Determines max number of CNAME chain. */
+#define MAX_RESTART_COUNT       8
+/** max number of referrals. Makes sure resolver does not run away */
+#define MAX_REFERRAL_COUNT	130
+/** max number of queries-sent-out.  Make sure large NS set does not loop */
+#define MAX_SENT_COUNT		16
+/** at what query-sent-count to stop target fetch policy */
+#define TARGET_FETCH_STOP	3
+/** how nice is a server without further information, in msec 
+ * Equals rtt initial timeout value.
+ */
+#define UNKNOWN_SERVER_NICENESS 376
+/** maximum timeout before a host is deemed unsuitable, in msec. 
+ * After host_ttl this will be timed out and the host will be tried again. 
+ * Equals RTT_MAX_TIMEOUT
+ */
+#define USEFUL_SERVER_TOP_TIMEOUT	120000
+/** Number of lost messages in a row that get a host blacklisted.
+ * With 16, a couple different queries have to time out and no working
+ * queries are happening */
+#define USEFUL_SERVER_MAX_LOST	16
+/** number of retries on outgoing queries */
+#define OUTBOUND_MSG_RETRY 5
+/** RTT band, within this amount from the best, servers are chosen randomly.
+ * Chosen so that the UNKNOWN_SERVER_NICENESS falls within the band of a 
+ * fast server, this causes server exploration as a side benefit. msec. */
+#define RTT_BAND 400
+/** Start value for blacklisting a host, 2*USEFUL_SERVER_TOP_TIMEOUT in sec */
+#define INFRA_BACKOFF_INITIAL 240
+
+/**
+ * Global state for the iterator. 
+ */
+struct iter_env {
+	/** 
+	 * The hints -- these aren't stored in the cache because they don't 
+	 * expire. The hints are always used to "prime" the cache. Note 
+	 * that both root hints and stub zone "hints" are stored in this 
+	 * data structure.
+	 */
+	struct iter_hints* hints;
+
+	/** A flag to indicate whether or not we have an IPv6 route */
+	int supports_ipv6;
+
+	/** A flag to indicate whether or not we have an IPv4 route */
+	int supports_ipv4;
+
+	/** A set of inetaddrs that should never be queried. */
+	struct iter_donotq* donotq;
+
+	/** private address space and private domains */
+	struct iter_priv* priv;
+
+	/** The maximum dependency depth that this resolver will pursue. */
+	int max_dependency_depth;
+
+	/**
+	 * The target fetch policy for each dependency level. This is 
+	 * described as a simple number (per dependency level): 
+	 *	negative numbers (usually just -1) mean fetch-all, 
+	 *	0 means only fetch on demand, and 
+	 *	positive numbers mean to fetch at most that many targets.
+	 * array of max_dependency_depth+1 size.
+	 */
+	int* target_fetch_policy;
+};
+
+/**
+ * State of the iterator for a query.
+ */
+enum iter_state {
+	/**
+	 * Externally generated queries start at this state. Query restarts are
+	 * reset to this state.
+	 */
+	INIT_REQUEST_STATE = 0,
+
+	/**
+	 * Root priming events reactivate here, most other events pass 
+	 * through this naturally as the 2nd part of the INIT_REQUEST_STATE.
+	 */
+	INIT_REQUEST_2_STATE,
+
+	/**
+	 * Stub priming events reactivate here, most other events pass 
+	 * through this naturally as the 3rd part of the INIT_REQUEST_STATE.
+	 */
+	INIT_REQUEST_3_STATE,
+
+	/**
+	 * Each time a delegation point changes for a given query or a 
+	 * query times out and/or wakes up, this state is (re)visited. 
+	 * This state is reponsible for iterating through a list of 
+	 * nameserver targets.
+	 */
+	QUERYTARGETS_STATE,
+
+	/**
+	 * Responses to queries start at this state. This state handles 
+	 * the decision tree associated with handling responses.
+	 */
+	QUERY_RESP_STATE,
+
+	/** Responses to priming queries finish at this state. */
+	PRIME_RESP_STATE,
+
+	/** Collecting query class information, for qclass=ANY, when
+	 * it spawns off queries for every class, it returns here. */
+	COLLECT_CLASS_STATE,
+
+	/** Responses that are to be returned upstream end at this state. 
+	 * As well as responses to target queries. */
+	FINISHED_STATE
+};
+
+/**
+ * Per query state for the iterator module.
+ */
+struct iter_qstate {
+	/** 
+	 * State of the iterator module.
+	 * This is the state that event is in or should sent to -- all 
+	 * requests should start with the INIT_REQUEST_STATE. All 
+	 * responses should start with QUERY_RESP_STATE. Subsequent 
+	 * processing of the event will change this state.
+	 */
+	enum iter_state state;
+
+	/** 
+	 * Final state for the iterator module.
+	 * This is the state that responses should be routed to once the 
+	 * response is final. For externally initiated queries, this 
+	 * will be FINISHED_STATE, locally initiated queries will have 
+	 * different final states.
+	 */
+	enum iter_state final_state;
+
+	/** 
+	 * The depth of this query, this means the depth of recursion.
+	 * This address is needed for another query, which is an address
+	 * needed for another query, etc. Original client query has depth 0.
+	 */
+	int depth;
+
+	/**
+	 * The response
+	 */
+	struct dns_msg* response;
+
+	/** 
+	 * This is a list of RRsets that must be prepended to the 
+	 * ANSWER section of a response before being sent upstream.
+	 */
+	struct iter_prep_list* an_prepend_list;
+	/** Last element of the prepend list */
+	struct iter_prep_list* an_prepend_last;
+
+	/**
+	 * This is the list of RRsets that must be prepended to the
+	 * AUTHORITY section of the response before being sent upstream.
+	 */
+	struct iter_prep_list* ns_prepend_list;
+	/** Last element of the authority prepend list */
+	struct iter_prep_list* ns_prepend_last;
+
+	/** query name used for chasing the results. Initially the same as
+	 * the state qinfo, but after CNAMEs this will be different. 
+	 * The query info used to elicit the results needed. */
+	struct query_info qchase;
+	/** query flags to use when chasing the answer (i.e. RD flag) */
+	uint16_t chase_flags;
+	/** true if we set RD bit because of last resort recursion lame query*/
+	int chase_to_rd;
+
+	/** 
+	 * This is the current delegation point for an in-progress query. This
+	 * object retains state as to which delegation targets need to be
+	 * (sub)queried for vs which ones have already been visited.
+	 */
+	struct delegpt* dp;
+
+	/** state for 0x20 fallback when capsfail happens, 0 not a fallback */
+	int caps_fallback;
+	/** state for capsfail: current server number to try */
+	size_t caps_server;
+	/** state for capsfail: stored query for comparisons */
+	struct reply_info* caps_reply;
+
+	/** Current delegation message - returned for non-RD queries */
+	struct dns_msg* deleg_msg;
+
+	/** number of outstanding target sub queries */
+	int num_target_queries;
+
+	/** outstanding direct queries */
+	int num_current_queries;
+
+	/** the number of times this query has been restarted. */
+	int query_restart_count;
+
+	/** the number of times this query as followed a referral. */
+	int referral_count;
+
+	/** number of queries fired off */
+	int sent_count;
+
+	/**
+	 * The query must store NS records from referrals as parentside RRs
+	 * Enabled once it hits resolution problems, to throttle retries.
+	 * If enabled it is the pointer to the old delegation point with
+	 * the old retry counts for bad-nameserver-addresses.
+	 */
+	struct delegpt* store_parent_NS;
+
+	/**
+	 * The query is for parent-side glue(A or AAAA) for a nameserver.
+	 * If the item is seen as glue in a referral, and pside_glue is NULL,
+	 * then it is stored in pside_glue for later.
+	 * If it was never seen, at the end, then a negative caching element 
+	 * must be created.  
+	 * The (data or negative) RR cache element then throttles retries.
+	 */
+	int query_for_pside_glue;
+	/** the parent-side-glue element (NULL if none, its first match) */
+	struct ub_packed_rrset_key* pside_glue;
+
+	/** 
+	 * expected dnssec information for this iteration step. 
+	 * If dnssec rrsigs are expected and not given, the server is marked
+	 * lame (dnssec-lame).
+	 */
+	int dnssec_expected;
+
+	/**
+	 * We are expecting dnssec information, but we also know the server
+	 * is DNSSEC lame.  The response need not be marked dnssec-lame again.
+	 */
+	int dnssec_lame_query;
+
+	/**
+	 * This is flag that, if true, means that this event is 
+	 * waiting for a stub priming query. 
+	 */
+	int wait_priming_stub;
+
+	/**
+	 * This is a flag that, if true, means that this query is
+	 * for (re)fetching glue from a zone. Since the address should
+	 * have been glue, query again to the servers that should have
+	 * been returning it as glue.
+	 * The delegation point must be set to the one that should *not*
+	 * be used when creating the state. A higher one will be attempted.
+	 */
+	int refetch_glue;
+
+	/** list of pending queries to authoritative servers. */
+	struct outbound_list outlist;
+};
+
+/**
+ * List of prepend items
+ */
+struct iter_prep_list {
+	/** next in list */
+	struct iter_prep_list* next;
+	/** rrset */
+	struct ub_packed_rrset_key* rrset;
+};
+
+/**
+ * Get the iterator function block.
+ * @return: function block with function pointers to iterator methods.
+ */
+struct module_func_block* iter_get_funcblock(void);
+
+/**
+ * Get iterator state as a string
+ * @param state: to convert
+ * @return constant string that is printable.
+ */
+const char* iter_state_to_string(enum iter_state state);
+
+/**
+ * See if iterator state is a response state
+ * @param s: to inspect
+ * @return true if response state.
+ */
+int iter_state_is_responsestate(enum iter_state s);
+
+/** iterator init */
+int iter_init(struct module_env* env, int id);
+
+/** iterator deinit */
+void iter_deinit(struct module_env* env, int id);
+
+/** iterator operate on a query */
+void iter_operate(struct module_qstate* qstate, enum module_ev event, int id,
+	struct outbound_entry* outbound);
+
+/**
+ * Return priming query results to interestes super querystates.
+ * 
+ * Sets the delegation point and delegation message (not nonRD queries).
+ * This is a callback from walk_supers.
+ *
+ * @param qstate: query state that finished.
+ * @param id: module id.
+ * @param super: the qstate to inform.
+ */
+void iter_inform_super(struct module_qstate* qstate, int id, 
+	struct module_qstate* super);
+
+/** iterator cleanup query state */
+void iter_clear(struct module_qstate* qstate, int id);
+
+/** iterator alloc size routine */
+size_t iter_get_mem(struct module_env* env, int id);
+
+#endif /* ITERATOR_ITERATOR_H */
diff --git a/3rdParty/Unbound/src/src/libunbound/context.c b/3rdParty/Unbound/src/src/libunbound/context.c
new file mode 100644
index 0000000..f283079
--- /dev/null
+++ b/3rdParty/Unbound/src/src/libunbound/context.c
@@ -0,0 +1,400 @@
+/*
+ * libunbound/context.c - validating context for unbound internal use
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains the validator context structure.
+ */
+#include "config.h"
+#include "libunbound/context.h"
+#include "util/module.h"
+#include "util/config_file.h"
+#include "util/net_help.h"
+#include "services/modstack.h"
+#include "services/localzone.h"
+#include "services/cache/rrset.h"
+#include "services/cache/infra.h"
+#include "util/data/msgreply.h"
+#include "util/storage/slabhash.h"
+
+int 
+context_finalize(struct ub_ctx* ctx)
+{
+	struct config_file* cfg = ctx->env->cfg;
+	verbosity = cfg->verbosity;
+	if(ctx->logfile_override)
+		log_file(ctx->log_out);
+	else	log_init(cfg->logfile, cfg->use_syslog, NULL);
+	config_apply(cfg);
+	if(!modstack_setup(&ctx->mods, cfg->module_conf, ctx->env))
+		return UB_INITFAIL;
+	ctx->local_zones = local_zones_create();
+	if(!ctx->local_zones)
+		return UB_NOMEM;
+	if(!local_zones_apply_cfg(ctx->local_zones, cfg))
+		return UB_INITFAIL;
+	if(!ctx->env->msg_cache ||
+	   cfg->msg_cache_size != slabhash_get_size(ctx->env->msg_cache) || 
+	   cfg->msg_cache_slabs != ctx->env->msg_cache->size) {
+		slabhash_delete(ctx->env->msg_cache);
+		ctx->env->msg_cache = slabhash_create(cfg->msg_cache_slabs,
+			HASH_DEFAULT_STARTARRAY, cfg->msg_cache_size,
+			msgreply_sizefunc, query_info_compare,
+			query_entry_delete, reply_info_delete, NULL);
+		if(!ctx->env->msg_cache)
+			return UB_NOMEM;
+	}
+	ctx->env->rrset_cache = rrset_cache_adjust(ctx->env->rrset_cache,
+		ctx->env->cfg, ctx->env->alloc);
+	if(!ctx->env->rrset_cache)
+		return UB_NOMEM;
+	ctx->env->infra_cache = infra_adjust(ctx->env->infra_cache, cfg);
+	if(!ctx->env->infra_cache)
+		return UB_NOMEM;
+	ctx->finalized = 1;
+	return UB_NOERROR;
+}
+
+int context_query_cmp(const void* a, const void* b)
+{
+	if( *(int*)a < *(int*)b )
+		return -1;
+	if( *(int*)a > *(int*)b )
+		return 1;
+	return 0;
+}
+
+void
+context_query_delete(struct ctx_query* q) 
+{
+	if(!q) return;
+	ub_resolve_free(q->res);
+	free(q->msg);
+	free(q);
+}
+
+/** How many times to try to find an unused query-id-number for async */
+#define NUM_ID_TRIES 100000
+/** find next useful id number of 0 on error */
+static int
+find_id(struct ub_ctx* ctx, int* id)
+{
+	size_t tries = 0;
+	ctx->next_querynum++;
+	while(rbtree_search(&ctx->queries, &ctx->next_querynum)) {
+		ctx->next_querynum++; /* numerical wraparound is fine */
+		if(tries++ > NUM_ID_TRIES)
+			return 0;
+	}
+	*id = ctx->next_querynum;
+	return 1;
+}
+
+struct ctx_query* 
+context_new(struct ub_ctx* ctx, char* name, int rrtype, int rrclass, 
+	ub_callback_t cb, void* cbarg)
+{
+	struct ctx_query* q = (struct ctx_query*)calloc(1, sizeof(*q));
+	if(!q) return NULL;
+	lock_basic_lock(&ctx->cfglock);
+	if(!find_id(ctx, &q->querynum)) {
+		lock_basic_unlock(&ctx->cfglock);
+		free(q);
+		return NULL;
+	}
+	lock_basic_unlock(&ctx->cfglock);
+	q->node.key = &q->querynum;
+	q->async = (cb != NULL);
+	q->cb = cb;
+	q->cb_arg = cbarg;
+	q->res = (struct ub_result*)calloc(1, sizeof(*q->res));
+	if(!q->res) {
+		free(q);
+		return NULL;
+	}
+	q->res->qname = strdup(name);
+	if(!q->res->qname) {
+		free(q->res);
+		free(q);
+		return NULL;
+	}
+	q->res->qtype = rrtype;
+	q->res->qclass = rrclass;
+
+	/* add to query list */
+	lock_basic_lock(&ctx->cfglock);
+	if(q->async)
+		ctx->num_async ++;
+	(void)rbtree_insert(&ctx->queries, &q->node);
+	lock_basic_unlock(&ctx->cfglock);
+	return q;
+}
+
+struct alloc_cache* 
+context_obtain_alloc(struct ub_ctx* ctx, int locking)
+{
+	struct alloc_cache* a;
+	int tnum = 0;
+	if(locking) {
+		lock_basic_lock(&ctx->cfglock);
+	}
+	a = ctx->alloc_list;
+	if(a)
+		ctx->alloc_list = a->super; /* snip off list */
+	else	tnum = ctx->thr_next_num++;
+	if(locking) {
+		lock_basic_unlock(&ctx->cfglock);
+	}
+	if(a) {
+		a->super = &ctx->superalloc;
+		return a;
+	}
+	a = (struct alloc_cache*)calloc(1, sizeof(*a));
+	if(!a)
+		return NULL;
+	alloc_init(a, &ctx->superalloc, tnum);
+	return a;
+}
+
+void 
+context_release_alloc(struct ub_ctx* ctx, struct alloc_cache* alloc,
+	int locking)
+{
+	if(!ctx || !alloc)
+		return;
+	if(locking) {
+		lock_basic_lock(&ctx->cfglock);
+	}
+	alloc->super = ctx->alloc_list;
+	ctx->alloc_list = alloc;
+	if(locking) {
+		lock_basic_unlock(&ctx->cfglock);
+	}
+}
+
+uint8_t* 
+context_serialize_new_query(struct ctx_query* q, uint32_t* len)
+{
+	/* format for new query is
+	 * 	o uint32 cmd
+	 * 	o uint32 id
+	 * 	o uint32 type
+	 * 	o uint32 class
+	 * 	o rest queryname (string)
+	 */
+	uint8_t* p;
+	size_t slen = strlen(q->res->qname) + 1/*end of string*/;
+	*len = sizeof(uint32_t)*4 + slen;
+	p = (uint8_t*)malloc(*len);
+	if(!p) return NULL;
+	ldns_write_uint32(p, UB_LIBCMD_NEWQUERY);
+	ldns_write_uint32(p+sizeof(uint32_t), (uint32_t)q->querynum);
+	ldns_write_uint32(p+2*sizeof(uint32_t), (uint32_t)q->res->qtype);
+	ldns_write_uint32(p+3*sizeof(uint32_t), (uint32_t)q->res->qclass);
+	memmove(p+4*sizeof(uint32_t), q->res->qname, slen);
+	return p;
+}
+
+struct ctx_query* 
+context_deserialize_new_query(struct ub_ctx* ctx, uint8_t* p, uint32_t len)
+{
+	struct ctx_query* q = (struct ctx_query*)calloc(1, sizeof(*q));
+	if(!q) return NULL;
+	if(len < 4*sizeof(uint32_t)+1) {
+		free(q);
+		return NULL;
+	}
+	log_assert( ldns_read_uint32(p) == UB_LIBCMD_NEWQUERY);
+	q->querynum = (int)ldns_read_uint32(p+sizeof(uint32_t));
+	q->node.key = &q->querynum;
+	q->async = 1;
+	q->res = (struct ub_result*)calloc(1, sizeof(*q->res));
+	if(!q->res) {
+		free(q);
+		return NULL;
+	}
+	q->res->qtype = (int)ldns_read_uint32(p+2*sizeof(uint32_t));
+	q->res->qclass = (int)ldns_read_uint32(p+3*sizeof(uint32_t));
+	q->res->qname = strdup((char*)(p+4*sizeof(uint32_t)));
+	if(!q->res->qname) {
+		free(q->res);
+		free(q);
+		return NULL;
+	}
+
+	/** add to query list */
+	ctx->num_async++;
+	(void)rbtree_insert(&ctx->queries, &q->node);
+	return q;
+}
+
+struct ctx_query* 
+context_lookup_new_query(struct ub_ctx* ctx, uint8_t* p, uint32_t len)
+{
+	struct ctx_query* q;
+	int querynum;
+	if(len < 4*sizeof(uint32_t)+1) {
+		return NULL;
+	}
+	log_assert( ldns_read_uint32(p) == UB_LIBCMD_NEWQUERY);
+	querynum = (int)ldns_read_uint32(p+sizeof(uint32_t));
+	q = (struct ctx_query*)rbtree_search(&ctx->queries, &querynum);
+	if(!q) {
+		return NULL;
+	}
+	log_assert(q->async);
+	return q;
+}
+
+uint8_t* 
+context_serialize_answer(struct ctx_query* q, int err, ldns_buffer* pkt,
+	uint32_t* len)
+{
+	/* answer format
+	 * 	o uint32 cmd
+	 * 	o uint32 id
+	 * 	o uint32 error_code
+	 * 	o uint32 msg_security
+	 * 	o uint32 length of why_bogus string (+1 for eos); 0 absent.
+	 * 	o why_bogus_string
+	 * 	o the remainder is the answer msg from resolver lookup.
+	 * 	  remainder can be length 0.
+	 */
+	size_t pkt_len = pkt?ldns_buffer_remaining(pkt):0;
+	size_t wlen = (pkt&&q->res->why_bogus)?strlen(q->res->why_bogus)+1:0;
+	uint8_t* p;
+	*len = sizeof(uint32_t)*5 + pkt_len + wlen;
+	p = (uint8_t*)malloc(*len);
+	if(!p) return NULL;
+	ldns_write_uint32(p, UB_LIBCMD_ANSWER);
+	ldns_write_uint32(p+sizeof(uint32_t), (uint32_t)q->querynum);
+	ldns_write_uint32(p+2*sizeof(uint32_t), (uint32_t)err);
+	ldns_write_uint32(p+3*sizeof(uint32_t), (uint32_t)q->msg_security);
+	ldns_write_uint32(p+4*sizeof(uint32_t), (uint32_t)wlen);
+	if(wlen > 0)
+		memmove(p+5*sizeof(uint32_t), q->res->why_bogus, wlen);
+	if(pkt_len > 0)
+		memmove(p+5*sizeof(uint32_t)+wlen, 
+			ldns_buffer_begin(pkt), pkt_len);
+	return p;
+}
+
+struct ctx_query* 
+context_deserialize_answer(struct ub_ctx* ctx,
+        uint8_t* p, uint32_t len, int* err)
+{
+	struct ctx_query* q = NULL ;
+	int id;
+	size_t wlen;
+	if(len < 5*sizeof(uint32_t)) return NULL;
+	log_assert( ldns_read_uint32(p) == UB_LIBCMD_ANSWER);
+	id = (int)ldns_read_uint32(p+sizeof(uint32_t));
+	q = (struct ctx_query*)rbtree_search(&ctx->queries, &id);
+	if(!q) return NULL; 
+	*err = (int)ldns_read_uint32(p+2*sizeof(uint32_t));
+	q->msg_security = ldns_read_uint32(p+3*sizeof(uint32_t));
+	wlen = (size_t)ldns_read_uint32(p+4*sizeof(uint32_t));
+	if(len > 5*sizeof(uint32_t) && wlen > 0) {
+		if(len >= 5*sizeof(uint32_t)+wlen)
+			q->res->why_bogus = (char*)memdup(
+				p+5*sizeof(uint32_t), wlen);
+		if(!q->res->why_bogus) {
+			/* pass malloc failure to the user callback */
+			q->msg_len = 0;
+			*err = UB_NOMEM;
+			return q;
+		}
+		q->res->why_bogus[wlen-1] = 0; /* zero terminated for sure */
+	}
+	if(len > 5*sizeof(uint32_t)+wlen) {
+		q->msg_len = len - 5*sizeof(uint32_t) - wlen;
+		q->msg = (uint8_t*)memdup(p+5*sizeof(uint32_t)+wlen, 
+			q->msg_len);
+		if(!q->msg) {
+			/* pass malloc failure to the user callback */
+			q->msg_len = 0;
+			*err = UB_NOMEM;
+			return q;
+		}
+	} 
+	return q;
+}
+
+uint8_t* 
+context_serialize_cancel(struct ctx_query* q, uint32_t* len)
+{
+	/* format of cancel:
+	 * 	o uint32 cmd
+	 * 	o uint32 async-id */
+	uint8_t* p = (uint8_t*)malloc(2*sizeof(uint32_t));
+	if(!p) return NULL;
+	*len = 2*sizeof(uint32_t);
+	ldns_write_uint32(p, UB_LIBCMD_CANCEL);
+	ldns_write_uint32(p+sizeof(uint32_t), (uint32_t)q->querynum);
+	return p;
+}
+
+struct ctx_query* context_deserialize_cancel(struct ub_ctx* ctx,
+        uint8_t* p, uint32_t len)
+{
+	struct ctx_query* q;
+	int id;
+	if(len != 2*sizeof(uint32_t)) return NULL;
+	log_assert( ldns_read_uint32(p) == UB_LIBCMD_CANCEL);
+	id = (int)ldns_read_uint32(p+sizeof(uint32_t));
+	q = (struct ctx_query*)rbtree_search(&ctx->queries, &id);
+	return q;
+}
+
+uint8_t* 
+context_serialize_quit(uint32_t* len)
+{
+	uint8_t* p = (uint8_t*)malloc(sizeof(uint32_t));
+	if(!p)
+		return NULL;
+	*len = sizeof(uint32_t);
+	ldns_write_uint32(p, UB_LIBCMD_QUIT);
+	return p;
+}
+
+enum ub_ctx_cmd context_serial_getcmd(uint8_t* p, uint32_t len)
+{
+	uint32_t v;
+	if((size_t)len < sizeof(v))
+		return UB_LIBCMD_QUIT;
+	v = ldns_read_uint32(p);
+	return v;
+}
diff --git a/3rdParty/Unbound/src/src/libunbound/context.h b/3rdParty/Unbound/src/src/libunbound/context.h
new file mode 100644
index 0000000..8898f3e
--- /dev/null
+++ b/3rdParty/Unbound/src/src/libunbound/context.h
@@ -0,0 +1,345 @@
+/*
+ * libunbound/context.h - validating context for unbound internal use
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains the validator context structure.
+ */
+#ifndef LIBUNBOUND_CONTEXT_H
+#define LIBUNBOUND_CONTEXT_H
+#include "util/locks.h"
+#include "util/alloc.h"
+#include "util/rbtree.h"
+#include "services/modstack.h"
+#include "libunbound/unbound.h"
+#include "util/data/packed_rrset.h"
+struct libworker;
+struct tube;
+
+/**
+ * The context structure
+ *
+ * Contains two pipes for async service
+ *	qq : write queries to the async service pid/tid.
+ *	rr : read results from the async service pid/tid.
+ */
+struct ub_ctx {
+	/* --- pipes --- */
+	/** mutex on query write pipe */
+	lock_basic_t qqpipe_lock;
+	/** the query write pipe */
+	struct tube* qq_pipe;
+	/** mutex on result read pipe */
+	lock_basic_t rrpipe_lock;
+	/** the result read pipe */
+	struct tube* rr_pipe;
+
+	/* --- shared data --- */
+	/** mutex for access to env.cfg, finalized and dothread */
+	lock_basic_t cfglock;
+	/** 
+	 * The context has been finalized 
+	 * This is after config when the first resolve is done.
+	 * The modules are inited (module-init()) and shared caches created.
+	 */
+	int finalized;
+
+	/** is bg worker created yet ? */
+	int created_bg;
+	/** pid of bg worker process */
+	pid_t bg_pid;
+	/** tid of bg worker thread */
+	ub_thread_t bg_tid;
+
+	/** do threading (instead of forking) for async resolution */
+	int dothread;
+	/** next thread number for new threads */
+	int thr_next_num;
+	/** if logfile is overriden */
+	int logfile_override;
+	/** what logfile to use instead */
+	FILE* log_out;
+	/** 
+	 * List of alloc-cache-id points per threadnum for notinuse threads.
+	 * Simply the entire struct alloc_cache with the 'super' member used
+	 * to link a simply linked list. Reset super member to the superalloc
+	 * before use.
+	 */
+	struct alloc_cache* alloc_list;
+
+	/** shared caches, and so on */
+	struct alloc_cache superalloc;
+	/** module env master value */
+	struct module_env* env;
+	/** module stack */
+	struct module_stack mods;
+	/** local authority zones */
+	struct local_zones* local_zones;
+	/** random state used to seed new random state structures */
+	struct ub_randstate* seed_rnd;
+
+	/** next query number (to try) to use */
+	int next_querynum;
+	/** number of async queries outstanding */
+	size_t num_async;
+	/** 
+	 * Tree of outstanding queries. Indexed by querynum 
+	 * Used when results come in for async to lookup.
+	 * Used when cancel is done for lookup (and delete).
+	 * Used to see if querynum is free for use.
+	 * Content of type ctx_query.
+	 */ 
+	rbtree_t queries;
+};
+
+/**
+ * The queries outstanding for the libunbound resolver.
+ * These are outstanding for async resolution.
+ * But also, outstanding for sync resolution by one of the threads that
+ * has joined the threadpool.
+ */
+struct ctx_query {
+	/** node in rbtree, must be first entry, key is ptr to the querynum */
+	struct rbnode_t node;
+	/** query id number, key for node */
+	int querynum;
+	/** was this an async query? */
+	int async;
+	/** was this query cancelled (for bg worker) */
+	int cancelled;
+
+	/** for async query, the callback function */
+	ub_callback_t cb;
+	/** for async query, the callback user arg */
+	void* cb_arg;
+
+	/** answer message, result from resolver lookup. */
+	uint8_t* msg;
+	/** resulting message length. */
+	size_t msg_len;
+	/** validation status on security */
+	enum sec_status msg_security;
+	/** store libworker that is handling this query */
+	struct libworker* w;
+
+	/** result structure, also contains original query, type, class.
+	 * malloced ptr ready to hand to the client. */
+	struct ub_result* res;
+};
+
+/**
+ * The error constants
+ */
+enum ub_ctx_err {
+	/** no error */
+	UB_NOERROR = 0,
+	/** socket operation. Set to -1, so that if an error from _fd() is
+	 * passed (-1) it gives a socket error. */
+	UB_SOCKET = -1,
+	/** alloc failure */
+	UB_NOMEM = -2,
+	/** syntax error */
+	UB_SYNTAX = -3,
+	/** DNS service failed */
+	UB_SERVFAIL = -4,
+	/** fork() failed */
+	UB_FORKFAIL = -5,
+	/** cfg change after finalize() */
+	UB_AFTERFINAL = -6,
+	/** initialization failed (bad settings) */
+	UB_INITFAIL = -7,
+	/** error in pipe communication with async bg worker */
+	UB_PIPE = -8,
+	/** error reading from file (resolv.conf) */
+	UB_READFILE = -9,
+	/** error async_id does not exist or result already been delivered */
+	UB_NOID = -10
+};
+
+/**
+ * Command codes for libunbound pipe.
+ *
+ * Serialization looks like this:
+ * 	o length (of remainder) uint32.
+ * 	o uint32 command code.
+ * 	o per command format.
+ */
+enum ub_ctx_cmd {
+	/** QUIT */
+	UB_LIBCMD_QUIT = 0,
+	/** New query, sent to bg worker */
+	UB_LIBCMD_NEWQUERY,
+	/** Cancel query, sent to bg worker */
+	UB_LIBCMD_CANCEL,
+	/** Query result, originates from bg worker */
+	UB_LIBCMD_ANSWER
+};
+
+/** 
+ * finalize a context.
+ * @param ctx: context to finalize. creates shared data.
+ * @return 0 if OK, or errcode.
+ */
+int context_finalize(struct ub_ctx* ctx);
+
+/** compare two ctx_query elements */
+int context_query_cmp(const void* a, const void* b);
+
+/** 
+ * delete context query
+ * @param q: query to delete, including message packet and prealloc result
+ */
+void context_query_delete(struct ctx_query* q);
+
+/**
+ * Create new query in context, add to querynum list.
+ * @param ctx: context
+ * @param name: query name
+ * @param rrtype: type
+ * @param rrclass: class
+ * @param cb: callback for async, or NULL for sync.
+ * @param cbarg: user arg for async queries.
+ * @return new ctx_query or NULL for malloc failure.
+ */
+struct ctx_query* context_new(struct ub_ctx* ctx, char* name, int rrtype,
+        int rrclass, ub_callback_t cb, void* cbarg);
+
+/**
+ * Get a new alloc. Creates a new one or uses a cached one.
+ * @param ctx: context
+ * @param locking: if true, cfglock is locked while getting alloc.
+ * @return an alloc, or NULL on mem error.
+ */
+struct alloc_cache* context_obtain_alloc(struct ub_ctx* ctx, int locking);
+
+/**
+ * Release an alloc. Puts it into the cache.
+ * @param ctx: context
+ * @param locking: if true, cfglock is locked while releasing alloc.
+ * @param alloc: alloc to relinquish.
+ */
+void context_release_alloc(struct ub_ctx* ctx, struct alloc_cache* alloc,
+	int locking);
+
+/**
+ * Serialize a context query that questions data.
+ * This serializes the query name, type, ...
+ * As well as command code 'new_query'.
+ * @param q: context query
+ * @param len: the length of the allocation is returned.
+ * @return: an alloc, or NULL on mem error.
+ */
+uint8_t* context_serialize_new_query(struct ctx_query* q, uint32_t* len);
+
+/**
+ * Serialize a context_query result to hand back to user.
+ * This serializes the query name, type, ..., and result.
+ * As well as command code 'answer'.
+ * @param q: context query
+ * @param err: error code to pass to client.
+ * @param pkt: the packet to add, can be NULL.
+ * @param len: the length of the allocation is returned.
+ * @return: an alloc, or NULL on mem error.
+ */
+uint8_t* context_serialize_answer(struct ctx_query* q, int err, 
+	ldns_buffer* pkt, uint32_t* len);
+
+/**
+ * Serialize a query cancellation. Serializes query async id
+ * as well as command code 'cancel'
+ * @param q: context query
+ * @param len: the length of the allocation is returned.
+ * @return: an alloc, or NULL on mem error.
+ */
+uint8_t* context_serialize_cancel(struct ctx_query* q, uint32_t* len);
+
+/**
+ * Serialize a 'quit' command.
+ * @param len: the length of the allocation is returned.
+ * @return: an alloc, or NULL on mem error.
+ */
+uint8_t* context_serialize_quit(uint32_t* len);
+
+/**
+ * Obtain command code from serialized buffer
+ * @param p: buffer serialized.
+ * @param len: length of buffer.
+ * @return command code or QUIT on error.
+ */
+enum ub_ctx_cmd context_serial_getcmd(uint8_t* p, uint32_t len);
+
+/**
+ * Lookup query from new_query buffer.
+ * @param ctx: context
+ * @param p: buffer serialized.
+ * @param len: length of buffer.
+ * @return looked up ctx_query or NULL for malloc failure.
+ */
+struct ctx_query* context_lookup_new_query(struct ub_ctx* ctx, 
+	uint8_t* p, uint32_t len);
+
+/**
+ * Deserialize a new_query buffer.
+ * @param ctx: context
+ * @param p: buffer serialized.
+ * @param len: length of buffer.
+ * @return new ctx_query or NULL for malloc failure.
+ */
+struct ctx_query* context_deserialize_new_query(struct ub_ctx* ctx, 
+	uint8_t* p, uint32_t len);
+
+/**
+ * Deserialize an answer buffer.
+ * @param ctx: context
+ * @param p: buffer serialized.
+ * @param len: length of buffer.
+ * @param err: error code to be returned to client is passed.
+ * @return ctx_query with answer added or NULL for malloc failure.
+ */
+struct ctx_query* context_deserialize_answer(struct ub_ctx* ctx, 
+	uint8_t* p, uint32_t len, int* err);
+
+/**
+ * Deserialize a cancel buffer.
+ * @param ctx: context
+ * @param p: buffer serialized.
+ * @param len: length of buffer.
+ * @return ctx_query to cancel or NULL for failure.
+ */
+struct ctx_query* context_deserialize_cancel(struct ub_ctx* ctx, 
+	uint8_t* p, uint32_t len);
+
+#endif /* LIBUNBOUND_CONTEXT_H */
diff --git a/3rdParty/Unbound/src/src/libunbound/libunbound.c b/3rdParty/Unbound/src/src/libunbound/libunbound.c
new file mode 100644
index 0000000..10d00dd
--- /dev/null
+++ b/3rdParty/Unbound/src/src/libunbound/libunbound.c
@@ -0,0 +1,1124 @@
+/*
+ * unbound.c - unbound validating resolver public API implementation
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to resolve DNS queries and 
+ * validate the answers. Synchonously and asynchronously.
+ *
+ */
+
+/* include the public api first, it should be able to stand alone */
+#include "libunbound/unbound.h"
+#include "config.h"
+#include <ctype.h>
+#include "libunbound/context.h"
+#include "libunbound/libworker.h"
+#include "util/locks.h"
+#include "util/config_file.h"
+#include "util/alloc.h"
+#include "util/module.h"
+#include "util/regional.h"
+#include "util/log.h"
+#include "util/random.h"
+#include "util/net_help.h"
+#include "util/tube.h"
+#include "services/modstack.h"
+#include "services/localzone.h"
+#include "services/cache/infra.h"
+#include "services/cache/rrset.h"
+
+#if defined(UB_ON_WINDOWS) && defined (HAVE_WINDOWS_H)
+#include <windows.h>
+#include <iphlpapi.h>
+#endif /* UB_ON_WINDOWS */
+
+struct ub_ctx* 
+ub_ctx_create(void)
+{
+	struct ub_ctx* ctx;
+	unsigned int seed;
+#ifdef USE_WINSOCK
+	int r;
+	WSADATA wsa_data;
+#endif
+	
+	log_init(NULL, 0, NULL); /* logs to stderr */
+	log_ident_set("libunbound");
+#ifdef USE_WINSOCK
+	if((r = WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0) {
+		log_err("could not init winsock. WSAStartup: %s",
+			wsa_strerror(r));
+		return NULL;
+	}
+#endif
+	verbosity = 0; /* errors only */
+	checklock_start();
+	ctx = (struct ub_ctx*)calloc(1, sizeof(*ctx));
+	if(!ctx) {
+		errno = ENOMEM;
+		return NULL;
+	}
+	alloc_init(&ctx->superalloc, NULL, 0);
+	seed = (unsigned int)time(NULL) ^ (unsigned int)getpid();
+	if(!(ctx->seed_rnd = ub_initstate(seed, NULL))) {
+		seed = 0;
+		ub_randfree(ctx->seed_rnd);
+		free(ctx);
+		errno = ENOMEM;
+		return NULL;
+	}
+	seed = 0;
+	if((ctx->qq_pipe = tube_create()) == NULL) {
+		int e = errno;
+		ub_randfree(ctx->seed_rnd);
+		free(ctx);
+		errno = e;
+		return NULL;
+	}
+	if((ctx->rr_pipe = tube_create()) == NULL) {
+		int e = errno;
+		tube_delete(ctx->qq_pipe);
+		ub_randfree(ctx->seed_rnd);
+		free(ctx);
+		errno = e;
+		return NULL;
+	}
+	lock_basic_init(&ctx->qqpipe_lock);
+	lock_basic_init(&ctx->rrpipe_lock);
+	lock_basic_init(&ctx->cfglock);
+	ctx->env = (struct module_env*)calloc(1, sizeof(*ctx->env));
+	if(!ctx->env) {
+		tube_delete(ctx->qq_pipe);
+		tube_delete(ctx->rr_pipe);
+		ub_randfree(ctx->seed_rnd);
+		free(ctx);
+		errno = ENOMEM;
+		return NULL;
+	}
+	ctx->env->cfg = config_create_forlib();
+	if(!ctx->env->cfg) {
+		tube_delete(ctx->qq_pipe);
+		tube_delete(ctx->rr_pipe);
+		free(ctx->env);
+		ub_randfree(ctx->seed_rnd);
+		free(ctx);
+		errno = ENOMEM;
+		return NULL;
+	}
+	ctx->env->alloc = &ctx->superalloc;
+	ctx->env->worker = NULL;
+	ctx->env->need_to_validate = 0;
+	modstack_init(&ctx->mods);
+	rbtree_init(&ctx->queries, &context_query_cmp);
+	return ctx;
+}
+
+/** delete q */
+static void
+delq(rbnode_t* n, void* ATTR_UNUSED(arg))
+{
+	struct ctx_query* q = (struct ctx_query*)n;
+	context_query_delete(q);
+}
+
+void 
+ub_ctx_delete(struct ub_ctx* ctx)
+{
+	struct alloc_cache* a, *na;
+	if(!ctx) return;
+	/* stop the bg thread */
+	lock_basic_lock(&ctx->cfglock);
+	if(ctx->created_bg) {
+		uint8_t* msg;
+		uint32_t len;
+		uint32_t cmd = UB_LIBCMD_QUIT;
+		lock_basic_unlock(&ctx->cfglock);
+		lock_basic_lock(&ctx->qqpipe_lock);
+		(void)tube_write_msg(ctx->qq_pipe, (uint8_t*)&cmd, 
+			(uint32_t)sizeof(cmd), 0);
+		lock_basic_unlock(&ctx->qqpipe_lock);
+		lock_basic_lock(&ctx->rrpipe_lock);
+		while(tube_read_msg(ctx->rr_pipe, &msg, &len, 0)) {
+			/* discard all results except a quit confirm */
+			if(context_serial_getcmd(msg, len) == UB_LIBCMD_QUIT) {
+				free(msg);
+				break;
+			}
+			free(msg);
+		}
+		lock_basic_unlock(&ctx->rrpipe_lock);
+
+		/* if bg worker is a thread, wait for it to exit, so that all
+	 	 * resources are really gone. */
+		lock_basic_lock(&ctx->cfglock);
+		if(ctx->dothread) {
+			lock_basic_unlock(&ctx->cfglock);
+			ub_thread_join(ctx->bg_tid);
+		} else {
+			lock_basic_unlock(&ctx->cfglock);
+		}
+	}
+	else {
+		lock_basic_unlock(&ctx->cfglock);
+	}
+
+
+	modstack_desetup(&ctx->mods, ctx->env);
+	a = ctx->alloc_list;
+	while(a) {
+		na = a->super;
+		a->super = &ctx->superalloc;
+		alloc_clear(a);
+		free(a);
+		a = na;
+	}
+	local_zones_delete(ctx->local_zones);
+	lock_basic_destroy(&ctx->qqpipe_lock);
+	lock_basic_destroy(&ctx->rrpipe_lock);
+	lock_basic_destroy(&ctx->cfglock);
+	tube_delete(ctx->qq_pipe);
+	tube_delete(ctx->rr_pipe);
+	if(ctx->env) {
+		slabhash_delete(ctx->env->msg_cache);
+		rrset_cache_delete(ctx->env->rrset_cache);
+		infra_delete(ctx->env->infra_cache);
+		config_delete(ctx->env->cfg);
+		free(ctx->env);
+	}
+	ub_randfree(ctx->seed_rnd);
+	alloc_clear(&ctx->superalloc);
+	traverse_postorder(&ctx->queries, delq, NULL);
+	free(ctx);
+#ifdef USE_WINSOCK
+	WSACleanup();
+#endif
+}
+
+int 
+ub_ctx_set_option(struct ub_ctx* ctx, char* opt, char* val)
+{
+	lock_basic_lock(&ctx->cfglock);
+	if(ctx->finalized) {
+		lock_basic_unlock(&ctx->cfglock);
+		return UB_AFTERFINAL;
+	}
+	if(!config_set_option(ctx->env->cfg, opt, val)) {
+		lock_basic_unlock(&ctx->cfglock);
+		return UB_SYNTAX;
+	}
+	lock_basic_unlock(&ctx->cfglock);
+	return UB_NOERROR;
+}
+
+int
+ub_ctx_get_option(struct ub_ctx* ctx, char* opt, char** str)
+{
+	int r;
+	lock_basic_lock(&ctx->cfglock);
+	r = config_get_option_collate(ctx->env->cfg, opt, str);
+	lock_basic_unlock(&ctx->cfglock);
+	if(r == 0) r = UB_NOERROR;
+	else if(r == 1) r = UB_SYNTAX;
+	else if(r == 2) r = UB_NOMEM;
+	return r;
+}
+
+int 
+ub_ctx_config(struct ub_ctx* ctx, char* fname)
+{
+	lock_basic_lock(&ctx->cfglock);
+	if(ctx->finalized) {
+		lock_basic_unlock(&ctx->cfglock);
+		return UB_AFTERFINAL;
+	}
+	if(!config_read(ctx->env->cfg, fname, NULL)) {
+		lock_basic_unlock(&ctx->cfglock);
+		return UB_SYNTAX;
+	}
+	lock_basic_unlock(&ctx->cfglock);
+	return UB_NOERROR;
+}
+
+int 
+ub_ctx_add_ta(struct ub_ctx* ctx, char* ta)
+{
+	char* dup = strdup(ta);
+	if(!dup) return UB_NOMEM;
+	lock_basic_lock(&ctx->cfglock);
+	if(ctx->finalized) {
+		lock_basic_unlock(&ctx->cfglock);
+		free(dup);
+		return UB_AFTERFINAL;
+	}
+	if(!cfg_strlist_insert(&ctx->env->cfg->trust_anchor_list, dup)) {
+		lock_basic_unlock(&ctx->cfglock);
+		free(dup);
+		return UB_NOMEM;
+	}
+	lock_basic_unlock(&ctx->cfglock);
+	return UB_NOERROR;
+}
+
+int 
+ub_ctx_add_ta_file(struct ub_ctx* ctx, char* fname)
+{
+	char* dup = strdup(fname);
+	if(!dup) return UB_NOMEM;
+	lock_basic_lock(&ctx->cfglock);
+	if(ctx->finalized) {
+		lock_basic_unlock(&ctx->cfglock);
+		free(dup);
+		return UB_AFTERFINAL;
+	}
+	if(!cfg_strlist_insert(&ctx->env->cfg->trust_anchor_file_list, dup)) {
+		lock_basic_unlock(&ctx->cfglock);
+		free(dup);
+		return UB_NOMEM;
+	}
+	lock_basic_unlock(&ctx->cfglock);
+	return UB_NOERROR;
+}
+
+int 
+ub_ctx_trustedkeys(struct ub_ctx* ctx, char* fname)
+{
+	char* dup = strdup(fname);
+	if(!dup) return UB_NOMEM;
+	lock_basic_lock(&ctx->cfglock);
+	if(ctx->finalized) {
+		lock_basic_unlock(&ctx->cfglock);
+		free(dup);
+		return UB_AFTERFINAL;
+	}
+	if(!cfg_strlist_insert(&ctx->env->cfg->trusted_keys_file_list, dup)) {
+		lock_basic_unlock(&ctx->cfglock);
+		free(dup);
+		return UB_NOMEM;
+	}
+	lock_basic_unlock(&ctx->cfglock);
+	return UB_NOERROR;
+}
+
+int
+ub_ctx_debuglevel(struct ub_ctx* ctx, int d)
+{
+	lock_basic_lock(&ctx->cfglock);
+	verbosity = d;
+	ctx->env->cfg->verbosity = d;
+	lock_basic_unlock(&ctx->cfglock);
+	return UB_NOERROR;
+}
+
+int ub_ctx_debugout(struct ub_ctx* ctx, void* out)
+{
+	lock_basic_lock(&ctx->cfglock);
+	log_file((FILE*)out);
+	ctx->logfile_override = 1;
+	ctx->log_out = out;
+	lock_basic_unlock(&ctx->cfglock);
+	return UB_NOERROR;
+}
+
+int 
+ub_ctx_async(struct ub_ctx* ctx, int dothread)
+{
+#ifdef THREADS_DISABLED
+	if(dothread) /* cannot do threading */
+		return UB_NOERROR;
+#endif
+	lock_basic_lock(&ctx->cfglock);
+	if(ctx->finalized) {
+		lock_basic_unlock(&ctx->cfglock);
+		return UB_AFTERFINAL;
+	}
+	ctx->dothread = dothread;
+	lock_basic_unlock(&ctx->cfglock);
+	return UB_NOERROR;
+}
+
+int 
+ub_poll(struct ub_ctx* ctx)
+{
+	/* no need to hold lock while testing for readability. */
+	return tube_poll(ctx->rr_pipe);
+}
+
+int 
+ub_fd(struct ub_ctx* ctx)
+{
+	return tube_read_fd(ctx->rr_pipe);
+}
+
+/** process answer from bg worker */
+static int
+process_answer_detail(struct ub_ctx* ctx, uint8_t* msg, uint32_t len,
+	ub_callback_t* cb, void** cbarg, int* err,
+	struct ub_result** res)
+{
+	struct ctx_query* q;
+	if(context_serial_getcmd(msg, len) != UB_LIBCMD_ANSWER) {
+		log_err("error: bad data from bg worker %d",
+			(int)context_serial_getcmd(msg, len));
+		return 0;
+	}
+
+	lock_basic_lock(&ctx->cfglock);
+	q = context_deserialize_answer(ctx, msg, len, err);
+	if(!q) {
+		lock_basic_unlock(&ctx->cfglock);
+		/* probably simply the lookup that failed, i.e.
+		 * response returned before cancel was sent out, so noerror */
+		return 1;
+	}
+	log_assert(q->async);
+
+	/* grab cb while locked */
+	if(q->cancelled) {
+		*cb = NULL;
+		*cbarg = NULL;
+	} else {
+		*cb = q->cb;
+		*cbarg = q->cb_arg;
+	}
+	if(*err) {
+		*res = NULL;
+		ub_resolve_free(q->res);
+	} else {
+		/* parse the message, extract rcode, fill result */
+		ldns_buffer* buf = ldns_buffer_new(q->msg_len);
+		struct regional* region = regional_create();
+		*res = q->res;
+		(*res)->rcode = LDNS_RCODE_SERVFAIL;
+		if(region && buf) {
+			ldns_buffer_clear(buf);
+			ldns_buffer_write(buf, q->msg, q->msg_len);
+			ldns_buffer_flip(buf);
+			libworker_enter_result(*res, buf, region,
+				q->msg_security);
+		}
+		(*res)->answer_packet = q->msg;
+		(*res)->answer_len = (int)q->msg_len;
+		q->msg = NULL;
+		ldns_buffer_free(buf);
+		regional_destroy(region);
+	}
+	q->res = NULL;
+	/* delete the q from list */
+	(void)rbtree_delete(&ctx->queries, q->node.key);
+	ctx->num_async--;
+	context_query_delete(q);
+	lock_basic_unlock(&ctx->cfglock);
+
+	if(*cb) return 2;
+	ub_resolve_free(*res);
+	return 1;
+}
+
+/** process answer from bg worker */
+static int
+process_answer(struct ub_ctx* ctx, uint8_t* msg, uint32_t len)
+{
+	int err;
+	ub_callback_t cb;
+	void* cbarg;
+	struct ub_result* res;
+	int r;
+
+	r = process_answer_detail(ctx, msg, len, &cb, &cbarg, &err, &res);
+
+	/* no locks held while calling callback, so that library is
+	 * re-entrant. */
+	if(r == 2)
+		(*cb)(cbarg, err, res);
+
+	return r;
+}
+
+int 
+ub_process(struct ub_ctx* ctx)
+{
+	int r;
+	uint8_t* msg;
+	uint32_t len;
+	while(1) {
+		msg = NULL;
+		lock_basic_lock(&ctx->rrpipe_lock);
+		r = tube_read_msg(ctx->rr_pipe, &msg, &len, 1);
+		lock_basic_unlock(&ctx->rrpipe_lock);
+		if(r == 0)
+			return UB_PIPE;
+		else if(r == -1)
+			break;
+		if(!process_answer(ctx, msg, len)) {
+			free(msg);
+			return UB_PIPE;
+		}
+		free(msg);
+	}
+	return UB_NOERROR;
+}
+
+int 
+ub_wait(struct ub_ctx* ctx)
+{
+	int err;
+	ub_callback_t cb;
+	void* cbarg;
+	struct ub_result* res;
+	int r;
+	uint8_t* msg;
+	uint32_t len;
+	/* this is basically the same loop as _process(), but with changes.
+	 * holds the rrpipe lock and waits with tube_wait */
+	while(1) {
+		lock_basic_lock(&ctx->rrpipe_lock);
+		lock_basic_lock(&ctx->cfglock);
+		if(ctx->num_async == 0) {
+			lock_basic_unlock(&ctx->cfglock);
+			lock_basic_unlock(&ctx->rrpipe_lock);
+			break;
+		}
+		lock_basic_unlock(&ctx->cfglock);
+
+		/* keep rrpipe locked, while
+		 * 	o waiting for pipe readable
+		 * 	o parsing message
+		 * 	o possibly decrementing num_async
+		 * do callback without lock
+		 */
+		r = tube_wait(ctx->rr_pipe);
+		if(r) {
+			r = tube_read_msg(ctx->rr_pipe, &msg, &len, 1);
+			if(r == 0) {
+				lock_basic_unlock(&ctx->rrpipe_lock);
+				return UB_PIPE;
+			}
+			if(r == -1) {
+				lock_basic_unlock(&ctx->rrpipe_lock);
+				continue;
+			}
+			r = process_answer_detail(ctx, msg, len, 
+				&cb, &cbarg, &err, &res);
+			lock_basic_unlock(&ctx->rrpipe_lock);
+			free(msg);
+			if(r == 0)
+				return UB_PIPE;
+			if(r == 2)
+				(*cb)(cbarg, err, res);
+		} else {
+			lock_basic_unlock(&ctx->rrpipe_lock);
+		}
+	}
+	return UB_NOERROR;
+}
+
+int 
+ub_resolve(struct ub_ctx* ctx, char* name, int rrtype, 
+	int rrclass, struct ub_result** result)
+{
+	struct ctx_query* q;
+	int r;
+	*result = NULL;
+
+	lock_basic_lock(&ctx->cfglock);
+	if(!ctx->finalized) {
+		r = context_finalize(ctx);
+		if(r) {
+			lock_basic_unlock(&ctx->cfglock);
+			return r;
+		}
+	}
+	/* create new ctx_query and attempt to add to the list */
+	lock_basic_unlock(&ctx->cfglock);
+	q = context_new(ctx, name, rrtype, rrclass, NULL, NULL);
+	if(!q)
+		return UB_NOMEM;
+	/* become a resolver thread for a bit */
+
+	r = libworker_fg(ctx, q);
+	if(r) {
+		lock_basic_lock(&ctx->cfglock);
+		(void)rbtree_delete(&ctx->queries, q->node.key);
+		context_query_delete(q);
+		lock_basic_unlock(&ctx->cfglock);
+		return r;
+	}
+	q->res->answer_packet = q->msg;
+	q->res->answer_len = (int)q->msg_len;
+	q->msg = NULL;
+	*result = q->res;
+	q->res = NULL;
+
+	lock_basic_lock(&ctx->cfglock);
+	(void)rbtree_delete(&ctx->queries, q->node.key);
+	context_query_delete(q);
+	lock_basic_unlock(&ctx->cfglock);
+	return UB_NOERROR;
+}
+
+int 
+ub_resolve_async(struct ub_ctx* ctx, char* name, int rrtype, 
+	int rrclass, void* mydata, ub_callback_t callback, int* async_id)
+{
+	struct ctx_query* q;
+	uint8_t* msg = NULL;
+	uint32_t len = 0;
+
+	if(async_id)
+		*async_id = 0;
+	lock_basic_lock(&ctx->cfglock);
+	if(!ctx->finalized) {
+		int r = context_finalize(ctx);
+		if(r) {
+			lock_basic_unlock(&ctx->cfglock);
+			return r;
+		}
+	}
+	if(!ctx->created_bg) {
+		int r;
+		ctx->created_bg = 1;
+		lock_basic_unlock(&ctx->cfglock);
+		r = libworker_bg(ctx);
+		if(r) {
+			lock_basic_lock(&ctx->cfglock);
+			ctx->created_bg = 0;
+			lock_basic_unlock(&ctx->cfglock);
+			return r;
+		}
+	} else {
+		lock_basic_unlock(&ctx->cfglock);
+	}
+
+	/* create new ctx_query and attempt to add to the list */
+	q = context_new(ctx, name, rrtype, rrclass, callback, mydata);
+	if(!q)
+		return UB_NOMEM;
+
+	/* write over pipe to background worker */
+	lock_basic_lock(&ctx->cfglock);
+	msg = context_serialize_new_query(q, &len);
+	if(!msg) {
+		(void)rbtree_delete(&ctx->queries, q->node.key);
+		ctx->num_async--;
+		context_query_delete(q);
+		lock_basic_unlock(&ctx->cfglock);
+		return UB_NOMEM;
+	}
+	if(async_id)
+		*async_id = q->querynum;
+	lock_basic_unlock(&ctx->cfglock);
+	
+	lock_basic_lock(&ctx->qqpipe_lock);
+	if(!tube_write_msg(ctx->qq_pipe, msg, len, 0)) {
+		lock_basic_unlock(&ctx->qqpipe_lock);
+		free(msg);
+		return UB_PIPE;
+	}
+	lock_basic_unlock(&ctx->qqpipe_lock);
+	free(msg);
+	return UB_NOERROR;
+}
+
+int 
+ub_cancel(struct ub_ctx* ctx, int async_id)
+{
+	struct ctx_query* q;
+	uint8_t* msg = NULL;
+	uint32_t len = 0;
+	lock_basic_lock(&ctx->cfglock);
+	q = (struct ctx_query*)rbtree_search(&ctx->queries, &async_id);
+	if(!q || !q->async) {
+		/* it is not there, so nothing to do */
+		lock_basic_unlock(&ctx->cfglock);
+		return UB_NOID;
+	}
+	log_assert(q->async);
+	q->cancelled = 1;
+	
+	/* delete it */
+	if(!ctx->dothread) { /* if forked */
+		(void)rbtree_delete(&ctx->queries, q->node.key);
+		ctx->num_async--;
+		msg = context_serialize_cancel(q, &len);
+		context_query_delete(q);
+		lock_basic_unlock(&ctx->cfglock);
+		if(!msg) {
+			return UB_NOMEM;
+		}
+		/* send cancel to background worker */
+		lock_basic_lock(&ctx->qqpipe_lock);
+		if(!tube_write_msg(ctx->qq_pipe, msg, len, 0)) {
+			lock_basic_unlock(&ctx->qqpipe_lock);
+			free(msg);
+			return UB_PIPE;
+		}
+		lock_basic_unlock(&ctx->qqpipe_lock);
+		free(msg);
+	} else {
+		lock_basic_unlock(&ctx->cfglock);
+	}
+	return UB_NOERROR;
+}
+
+void 
+ub_resolve_free(struct ub_result* result)
+{
+	char** p;
+	if(!result) return;
+	free(result->qname);
+	if(result->canonname != result->qname)
+		free(result->canonname);
+	if(result->data)
+		for(p = result->data; *p; p++)
+			free(*p);
+	free(result->data);
+	free(result->len);
+	free(result->answer_packet);
+	free(result->why_bogus);
+	free(result);
+}
+
+const char* 
+ub_strerror(int err)
+{
+	switch(err) {
+		case UB_NOERROR: return "no error";
+		case UB_SOCKET: return "socket io error";
+		case UB_NOMEM: return "out of memory";
+		case UB_SYNTAX: return "syntax error";
+		case UB_SERVFAIL: return "server failure";
+		case UB_FORKFAIL: return "could not fork";
+		case UB_INITFAIL: return "initialization failure";
+		case UB_AFTERFINAL: return "setting change after finalize";
+		case UB_PIPE: return "error in pipe communication with async";
+		case UB_READFILE: return "error reading file";
+		case UB_NOID: return "error async_id does not exist";
+		default: return "unknown error";
+	}
+}
+
+int 
+ub_ctx_set_fwd(struct ub_ctx* ctx, char* addr)
+{
+	struct sockaddr_storage storage;
+	socklen_t stlen;
+	struct config_stub* s;
+	char* dupl;
+	lock_basic_lock(&ctx->cfglock);
+	if(ctx->finalized) {
+		lock_basic_unlock(&ctx->cfglock);
+		errno=EINVAL;
+		return UB_AFTERFINAL;
+	}
+	if(!addr) {
+		/* disable fwd mode - the root stub should be first. */
+		if(ctx->env->cfg->forwards &&
+			strcmp(ctx->env->cfg->forwards->name, ".") == 0) {
+			s = ctx->env->cfg->forwards;
+			ctx->env->cfg->forwards = s->next;
+			s->next = NULL;
+			config_delstubs(s);
+		}
+		lock_basic_unlock(&ctx->cfglock);
+		return UB_NOERROR;
+	}
+	lock_basic_unlock(&ctx->cfglock);
+
+	/* check syntax for addr */
+	if(!extstrtoaddr(addr, &storage, &stlen)) {
+		errno=EINVAL;
+		return UB_SYNTAX;
+	}
+	
+	/* it parses, add root stub in front of list */
+	lock_basic_lock(&ctx->cfglock);
+	if(!ctx->env->cfg->forwards ||
+		strcmp(ctx->env->cfg->forwards->name, ".") != 0) {
+		s = calloc(1, sizeof(*s));
+		if(!s) {
+			lock_basic_unlock(&ctx->cfglock);
+			errno=ENOMEM;
+			return UB_NOMEM;
+		}
+		s->name = strdup(".");
+		if(!s->name) {
+			free(s);
+			lock_basic_unlock(&ctx->cfglock);
+			errno=ENOMEM;
+			return UB_NOMEM;
+		}
+		s->next = ctx->env->cfg->forwards;
+		ctx->env->cfg->forwards = s;
+	} else {
+		log_assert(ctx->env->cfg->forwards);
+		s = ctx->env->cfg->forwards;
+	}
+	dupl = strdup(addr);
+	if(!dupl) {
+		lock_basic_unlock(&ctx->cfglock);
+		errno=ENOMEM;
+		return UB_NOMEM;
+	}
+	if(!cfg_strlist_insert(&s->addrs, dupl)) {
+		free(dupl);
+		lock_basic_unlock(&ctx->cfglock);
+		errno=ENOMEM;
+		return UB_NOMEM;
+	}
+	lock_basic_unlock(&ctx->cfglock);
+	return UB_NOERROR;
+}
+
+int 
+ub_ctx_resolvconf(struct ub_ctx* ctx, char* fname)
+{
+	FILE* in;
+	int numserv = 0;
+	char buf[1024];
+	char* parse, *addr;
+	int r;
+
+	if(fname == NULL) {
+#if !defined(UB_ON_WINDOWS) || !defined(HAVE_WINDOWS_H)
+		fname = "/etc/resolv.conf";
+#else
+		FIXED_INFO *info;
+		ULONG buflen = sizeof(*info);
+		IP_ADDR_STRING *ptr;
+
+		info = (FIXED_INFO *) malloc(sizeof (FIXED_INFO));
+		if (info == NULL) 
+			return UB_READFILE;
+
+		if (GetNetworkParams(info, &buflen) == ERROR_BUFFER_OVERFLOW) {
+			free(info);
+			info = (FIXED_INFO *) malloc(buflen);
+			if (info == NULL)
+				return UB_READFILE;
+		}
+
+		if (GetNetworkParams(info, &buflen) == NO_ERROR) {
+			int retval=0;
+			ptr = &(info->DnsServerList);
+			while (ptr) {
+				numserv++;
+				if((retval=ub_ctx_set_fwd(ctx, 
+					ptr->IpAddress.String)!=0)) {
+					free(info);
+					return retval;
+				}
+				ptr = ptr->Next;
+			}
+			free(info);
+			if (numserv==0)
+				return UB_READFILE;
+			return UB_NOERROR;
+		}
+		free(info);
+		return UB_READFILE;
+#endif /* WINDOWS */
+	}
+	in = fopen(fname, "r");
+	if(!in) {
+		/* error in errno! perror(fname) */
+		return UB_READFILE;
+	}
+	while(fgets(buf, (int)sizeof(buf), in)) {
+		buf[sizeof(buf)-1] = 0;
+		parse=buf;
+		while(*parse == ' ' || *parse == '\t')
+			parse++;
+		if(strncmp(parse, "nameserver", 10) == 0) {
+			numserv++;
+			parse += 10; /* skip 'nameserver' */
+			/* skip whitespace */
+			while(*parse == ' ' || *parse == '\t')
+				parse++;
+			addr = parse;
+			/* skip [0-9a-fA-F.:]*, i.e. IP4 and IP6 address */
+			while(isxdigit(*parse) || *parse=='.' || *parse==':')
+				parse++;
+			/* terminate after the address, remove newline */
+			*parse = 0;
+			
+			if((r = ub_ctx_set_fwd(ctx, addr)) != UB_NOERROR) {
+				fclose(in);
+				return r;
+			}
+		}
+	}
+	fclose(in);
+	if(numserv == 0) {
+		/* from resolv.conf(5) if none given, use localhost */
+		return ub_ctx_set_fwd(ctx, "127.0.0.1");
+	}
+	return UB_NOERROR;
+}
+
+int
+ub_ctx_hosts(struct ub_ctx* ctx, char* fname)
+{
+	FILE* in;
+	char buf[1024], ldata[1024];
+	char* parse, *addr, *name, *ins;
+	lock_basic_lock(&ctx->cfglock);
+	if(ctx->finalized) {
+		lock_basic_unlock(&ctx->cfglock);
+		errno=EINVAL;
+		return UB_AFTERFINAL;
+	}
+	lock_basic_unlock(&ctx->cfglock);
+	if(fname == NULL) {
+#if defined(UB_ON_WINDOWS) && defined(HAVE_WINDOWS_H)
+		/*
+		 * If this is Windows NT/XP/2K it's in
+		 * %WINDIR%\system32\drivers\etc\hosts.
+		 * If this is Windows 95/98/Me it's in %WINDIR%\hosts.
+		 */
+		name = getenv("WINDIR");
+		if (name != NULL) {
+			int retval=0;
+			snprintf(buf, sizeof(buf), "%s%s", name, 
+				"\\system32\\drivers\\etc\\hosts");
+			if((retval=ub_ctx_hosts(ctx, buf)) !=0 ) {
+				snprintf(buf, sizeof(buf), "%s%s", name, 
+					"\\hosts");
+				retval=ub_ctx_hosts(ctx, buf);
+			}
+			free(name);
+			return retval;
+		}
+		return UB_READFILE;
+#else
+		fname = "/etc/hosts";
+#endif /* WIN32 */
+	}
+	in = fopen(fname, "r");
+	if(!in) {
+		/* error in errno! perror(fname) */
+		return UB_READFILE;
+	}
+	while(fgets(buf, (int)sizeof(buf), in)) {
+		buf[sizeof(buf)-1] = 0;
+		parse=buf;
+		while(*parse == ' ' || *parse == '\t')
+			parse++;
+		if(*parse == '#')
+			continue; /* skip comment */
+		/* format: <addr> spaces <name> spaces <name> ... */
+		addr = parse;
+		/* skip addr */
+		while(isxdigit(*parse) || *parse == '.' || *parse == ':')
+			parse++;
+		if(*parse == '\n' || *parse == 0)
+			continue;
+		if(*parse == '%') 
+			continue; /* ignore macOSX fe80::1%lo0 localhost */
+		if(*parse != ' ' && *parse != '\t') {
+			/* must have whitespace after address */
+			fclose(in);
+			errno=EINVAL;
+			return UB_SYNTAX;
+		}
+		*parse++ = 0; /* end delimiter for addr ... */
+		/* go to names and add them */
+		while(*parse) {
+			while(*parse == ' ' || *parse == '\t' || *parse=='\n')
+				parse++;
+			if(*parse == 0 || *parse == '#')
+				break;
+			/* skip name, allows (too) many printable characters */
+			name = parse;
+			while('!' <= *parse && *parse <= '~')
+				parse++;
+			if(*parse)
+				*parse++ = 0; /* end delimiter for name */
+			snprintf(ldata, sizeof(ldata), "%s %s %s",
+				name, str_is_ip6(addr)?"AAAA":"A", addr);
+			ins = strdup(ldata);
+			if(!ins) {
+				/* out of memory */
+				fclose(in);
+				errno=ENOMEM;
+				return UB_NOMEM;
+			}
+			lock_basic_lock(&ctx->cfglock);
+			if(!cfg_strlist_insert(&ctx->env->cfg->local_data, 
+				ins)) {
+				lock_basic_unlock(&ctx->cfglock);
+				fclose(in);
+				free(ins);
+				errno=ENOMEM;
+				return UB_NOMEM;
+			}
+			lock_basic_unlock(&ctx->cfglock);
+		}
+	}
+	fclose(in);
+	return UB_NOERROR;
+}
+
+/** finalize the context, if not already finalized */
+static int ub_ctx_finalize(struct ub_ctx* ctx)
+{
+	int res = 0;
+	lock_basic_lock(&ctx->cfglock);
+	if (!ctx->finalized) {
+		res = context_finalize(ctx);
+	}
+	lock_basic_unlock(&ctx->cfglock);
+	return res;
+}
+
+/* Print local zones and RR data */
+int ub_ctx_print_local_zones(struct ub_ctx* ctx)
+{   
+	int res = ub_ctx_finalize(ctx);
+	if (res) return res;
+
+	local_zones_print(ctx->local_zones);
+
+	return UB_NOERROR;
+}
+
+/* Add a new zone */
+int ub_ctx_zone_add(struct ub_ctx* ctx, char *zone_name, char *zone_type)
+{
+	enum localzone_type t;
+	struct local_zone* z;
+	uint8_t* nm;
+	int nmlabs;
+	size_t nmlen;
+
+	int res = ub_ctx_finalize(ctx);
+	if (res) return res;
+
+	if(!local_zone_str2type(zone_type, &t)) {
+		return UB_SYNTAX;
+	}
+
+	if(!parse_dname(zone_name, &nm, &nmlen, &nmlabs)) {
+		return UB_SYNTAX;
+	}
+
+	lock_quick_lock(&ctx->local_zones->lock);
+	if((z=local_zones_find(ctx->local_zones, nm, nmlen, nmlabs, 
+		LDNS_RR_CLASS_IN))) {
+		/* already present in tree */
+		lock_rw_wrlock(&z->lock);
+		z->type = t; /* update type anyway */
+		lock_rw_unlock(&z->lock);
+		lock_quick_unlock(&ctx->local_zones->lock);
+		free(nm);
+		return UB_NOERROR;
+	}
+	if(!local_zones_add_zone(ctx->local_zones, nm, nmlen, nmlabs, 
+		LDNS_RR_CLASS_IN, t)) {
+		lock_quick_unlock(&ctx->local_zones->lock);
+		return UB_NOMEM;
+	}
+	lock_quick_unlock(&ctx->local_zones->lock);
+	return UB_NOERROR;
+}
+
+/* Remove zone */
+int ub_ctx_zone_remove(struct ub_ctx* ctx, char *zone_name)
+{   
+	struct local_zone* z;
+	uint8_t* nm;
+	int nmlabs;
+	size_t nmlen;
+
+	int res = ub_ctx_finalize(ctx);
+	if (res) return res;
+
+	if(!parse_dname(zone_name, &nm, &nmlen, &nmlabs)) {
+		return UB_SYNTAX;
+	}
+
+	lock_quick_lock(&ctx->local_zones->lock);
+	if((z=local_zones_find(ctx->local_zones, nm, nmlen, nmlabs, 
+		LDNS_RR_CLASS_IN))) {
+		/* present in tree */
+		local_zones_del_zone(ctx->local_zones, z);
+	}
+	lock_quick_unlock(&ctx->local_zones->lock);
+	free(nm);
+	return UB_NOERROR;
+}
+
+/* Add new RR data */
+int ub_ctx_data_add(struct ub_ctx* ctx, char *data)
+{
+	ldns_buffer* buf;
+	int res = ub_ctx_finalize(ctx);
+	if (res) return res;
+
+	lock_basic_lock(&ctx->cfglock);
+	buf = ldns_buffer_new(ctx->env->cfg->msg_buffer_size);
+	lock_basic_unlock(&ctx->cfglock);
+	if(!buf) return UB_NOMEM;
+
+	res = local_zones_add_RR(ctx->local_zones, data, buf);
+
+	ldns_buffer_free(buf);
+	return (!res) ? UB_NOMEM : UB_NOERROR;
+}
+
+/* Remove RR data */
+int ub_ctx_data_remove(struct ub_ctx* ctx, char *data)
+{
+	uint8_t* nm;
+	int nmlabs;
+	size_t nmlen;
+	int res = ub_ctx_finalize(ctx);
+	if (res) return res;
+
+	if(!parse_dname(data, &nm, &nmlen, &nmlabs)) 
+		return UB_SYNTAX;
+
+	local_zones_del_data(ctx->local_zones, nm, nmlen, nmlabs, 
+		LDNS_RR_CLASS_IN);
+
+	free(nm);
+	return UB_NOERROR;
+}
+
+const char* ub_version(void)
+{
+	return PACKAGE_VERSION;
+}
diff --git a/3rdParty/Unbound/src/src/libunbound/libworker.c b/3rdParty/Unbound/src/src/libunbound/libworker.c
new file mode 100644
index 0000000..0a734a2
--- /dev/null
+++ b/3rdParty/Unbound/src/src/libunbound/libworker.c
@@ -0,0 +1,901 @@
+/*
+ * libunbound/worker.c - worker thread or process that resolves
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains the worker process or thread that performs
+ * the DNS resolving and validation. The worker is called by a procedure
+ * and if in the background continues until exit, if in the foreground
+ * returns from the procedure when done.
+ */
+#include "config.h"
+#include <ldns/dname.h>
+#include <ldns/wire2host.h>
+#include <openssl/ssl.h>
+#include "libunbound/libworker.h"
+#include "libunbound/context.h"
+#include "libunbound/unbound.h"
+#include "services/outside_network.h"
+#include "services/mesh.h"
+#include "services/localzone.h"
+#include "services/cache/rrset.h"
+#include "services/outbound_list.h"
+#include "util/module.h"
+#include "util/regional.h"
+#include "util/random.h"
+#include "util/config_file.h"
+#include "util/netevent.h"
+#include "util/storage/lookup3.h"
+#include "util/storage/slabhash.h"
+#include "util/net_help.h"
+#include "util/data/dname.h"
+#include "util/data/msgreply.h"
+#include "util/data/msgencode.h"
+#include "util/tube.h"
+#include "iterator/iter_fwd.h"
+
+/** handle new query command for bg worker */
+static void handle_newq(struct libworker* w, uint8_t* buf, uint32_t len);
+
+/** delete libworker struct */
+static void
+libworker_delete(struct libworker* w)
+{
+	if(!w) return;
+	if(w->env) {
+		outside_network_quit_prepare(w->back);
+		mesh_delete(w->env->mesh);
+		context_release_alloc(w->ctx, w->env->alloc, 
+			!w->is_bg || w->is_bg_thread);
+		ldns_buffer_free(w->env->scratch_buffer);
+		regional_destroy(w->env->scratch);
+		forwards_delete(w->env->fwds);
+		ub_randfree(w->env->rnd);
+		free(w->env);
+	}
+	SSL_CTX_free(w->sslctx);
+	outside_network_delete(w->back);
+	comm_base_delete(w->base);
+	free(w);
+}
+
+/** setup fresh libworker struct */
+static struct libworker*
+libworker_setup(struct ub_ctx* ctx, int is_bg)
+{
+	unsigned int seed;
+	struct libworker* w = (struct libworker*)calloc(1, sizeof(*w));
+	struct config_file* cfg = ctx->env->cfg;
+	int* ports;
+	int numports;
+	if(!w) return NULL;
+	w->is_bg = is_bg;
+	w->ctx = ctx;
+	w->env = (struct module_env*)malloc(sizeof(*w->env));
+	if(!w->env) {
+		free(w);
+		return NULL;
+	}
+	*w->env = *ctx->env;
+	w->env->alloc = context_obtain_alloc(ctx, !w->is_bg || w->is_bg_thread);
+	if(!w->env->alloc) {
+		libworker_delete(w);
+		return NULL;
+	}
+	w->thread_num = w->env->alloc->thread_num;
+	alloc_set_id_cleanup(w->env->alloc, &libworker_alloc_cleanup, w);
+	if(!w->is_bg || w->is_bg_thread) {
+		lock_basic_lock(&ctx->cfglock);
+	}
+	w->env->scratch = regional_create_custom(cfg->msg_buffer_size);
+	w->env->scratch_buffer = ldns_buffer_new(cfg->msg_buffer_size);
+	w->env->fwds = forwards_create();
+	if(w->env->fwds && !forwards_apply_cfg(w->env->fwds, cfg)) { 
+		forwards_delete(w->env->fwds);
+		w->env->fwds = NULL;
+	}
+	if(cfg->ssl_upstream) {
+		w->sslctx = connect_sslctx_create(NULL, NULL, NULL);
+		if(!w->sslctx) {
+			libworker_delete(w);
+			return NULL;
+		}
+	}
+	if(!w->is_bg || w->is_bg_thread) {
+		lock_basic_unlock(&ctx->cfglock);
+	}
+	if(!w->env->scratch || !w->env->scratch_buffer || !w->env->fwds) {
+		libworker_delete(w);
+		return NULL;
+	}
+	w->env->worker = (struct worker*)w;
+	w->env->probe_timer = NULL;
+	seed = (unsigned int)time(NULL) ^ (unsigned int)getpid() ^
+		(((unsigned int)w->thread_num)<<17);
+	seed ^= (unsigned int)w->env->alloc->next_id;
+	if(!w->is_bg || w->is_bg_thread) {
+		lock_basic_lock(&ctx->cfglock);
+	}
+	if(!(w->env->rnd = ub_initstate(seed, ctx->seed_rnd))) {
+		if(!w->is_bg || w->is_bg_thread) {
+			lock_basic_unlock(&ctx->cfglock);
+		}
+		seed = 0;
+		libworker_delete(w);
+		return NULL;
+	}
+	if(!w->is_bg || w->is_bg_thread) {
+		lock_basic_unlock(&ctx->cfglock);
+	}
+	if(1) {
+		/* primitive lockout for threading: if it overwrites another
+		 * thread it is like wiping the cache (which is likely empty
+		 * at the start) */
+		/* note we are holding the ctx lock in normal threaded
+		 * cases so that is solved properly, it is only for many ctx
+		 * in different threads that this may clash */
+		static int done_raninit = 0;
+		if(!done_raninit) {
+			done_raninit = 1;
+			hash_set_raninit((uint32_t)ub_random(w->env->rnd));
+		}
+	}
+	seed = 0;
+
+	w->base = comm_base_create(0);
+	if(!w->base) {
+		libworker_delete(w);
+		return NULL;
+	}
+	if(!w->is_bg || w->is_bg_thread) {
+		lock_basic_lock(&ctx->cfglock);
+	}
+	numports = cfg_condense_ports(cfg, &ports);
+	if(numports == 0) {
+		libworker_delete(w);
+		return NULL;
+	}
+	w->back = outside_network_create(w->base, cfg->msg_buffer_size,
+		(size_t)cfg->outgoing_num_ports, cfg->out_ifs,
+		cfg->num_out_ifs, cfg->do_ip4, cfg->do_ip6, 
+		cfg->do_tcp?cfg->outgoing_num_tcp:0,
+		w->env->infra_cache, w->env->rnd, cfg->use_caps_bits_for_id,
+		ports, numports, cfg->unwanted_threshold,
+		&libworker_alloc_cleanup, w, cfg->do_udp, w->sslctx);
+	if(!w->is_bg || w->is_bg_thread) {
+		lock_basic_unlock(&ctx->cfglock);
+	}
+	free(ports);
+	if(!w->back) {
+		libworker_delete(w);
+		return NULL;
+	}
+	w->env->mesh = mesh_create(&ctx->mods, w->env);
+	if(!w->env->mesh) {
+		libworker_delete(w);
+		return NULL;
+	}
+	w->env->send_query = &libworker_send_query;
+	w->env->detach_subs = &mesh_detach_subs;
+	w->env->attach_sub = &mesh_attach_sub;
+	w->env->kill_sub = &mesh_state_delete;
+	w->env->detect_cycle = &mesh_detect_cycle;
+	comm_base_timept(w->base, &w->env->now, &w->env->now_tv);
+	return w;
+}
+
+/** handle cancel command for bg worker */
+static void
+handle_cancel(struct libworker* w, uint8_t* buf, uint32_t len)
+{
+	struct ctx_query* q;
+	if(w->is_bg_thread) {
+		lock_basic_lock(&w->ctx->cfglock);
+		q = context_deserialize_cancel(w->ctx, buf, len);
+		lock_basic_unlock(&w->ctx->cfglock);
+	} else {
+		q = context_deserialize_cancel(w->ctx, buf, len);
+	}
+	if(!q) {
+		/* probably simply lookup failed, i.e. the message had been
+		 * processed and answered before the cancel arrived */
+		return;
+	}
+	q->cancelled = 1;
+	free(buf);
+}
+
+/** do control command coming into bg server */
+static void
+libworker_do_cmd(struct libworker* w, uint8_t* msg, uint32_t len)
+{
+	switch(context_serial_getcmd(msg, len)) {
+		default:
+		case UB_LIBCMD_ANSWER:
+			log_err("unknown command for bg worker %d", 
+				(int)context_serial_getcmd(msg, len));
+			/* and fall through to quit */
+		case UB_LIBCMD_QUIT:
+			free(msg);
+			comm_base_exit(w->base);
+			break;
+		case UB_LIBCMD_NEWQUERY:
+			handle_newq(w, msg, len);
+			break;
+		case UB_LIBCMD_CANCEL:
+			handle_cancel(w, msg, len);
+			break;
+	}
+}
+
+/** handle control command coming into server */
+void 
+libworker_handle_control_cmd(struct tube* ATTR_UNUSED(tube), 
+	uint8_t* msg, size_t len, int err, void* arg)
+{
+	struct libworker* w = (struct libworker*)arg;
+
+	if(err != 0) {
+		free(msg);
+		/* it is of no use to go on, exit */
+		comm_base_exit(w->base);
+		return;
+	}
+	libworker_do_cmd(w, msg, len); /* also frees the buf */
+}
+
+/** the background thread func */
+static void*
+libworker_dobg(void* arg)
+{
+	/* setup */
+	uint32_t m;
+	struct libworker* w = (struct libworker*)arg;
+	struct ub_ctx* ctx;
+	if(!w) {
+		log_err("libunbound bg worker init failed, nomem");
+		return NULL;
+	}
+	ctx = w->ctx;
+	log_thread_set(&w->thread_num);
+#ifdef THREADS_DISABLED
+	/* we are forked */
+	w->is_bg_thread = 0;
+	/* close non-used parts of the pipes */
+	tube_close_write(ctx->qq_pipe);
+	tube_close_read(ctx->rr_pipe);
+#endif
+	if(!tube_setup_bg_listen(ctx->qq_pipe, w->base, 
+		libworker_handle_control_cmd, w)) {
+		log_err("libunbound bg worker init failed, no bglisten");
+		return NULL;
+	}
+	if(!tube_setup_bg_write(ctx->rr_pipe, w->base)) {
+		log_err("libunbound bg worker init failed, no bgwrite");
+		return NULL;
+	}
+
+	/* do the work */
+	comm_base_dispatch(w->base);
+
+	/* cleanup */
+	m = UB_LIBCMD_QUIT;
+	tube_remove_bg_listen(w->ctx->qq_pipe);
+	tube_remove_bg_write(w->ctx->rr_pipe);
+	libworker_delete(w);
+	(void)tube_write_msg(ctx->rr_pipe, (uint8_t*)&m, 
+		(uint32_t)sizeof(m), 0);
+#ifdef THREADS_DISABLED
+	/* close pipes from forked process before exit */
+	tube_close_read(ctx->qq_pipe);
+	tube_close_write(ctx->rr_pipe);
+#endif
+	return NULL;
+}
+
+int libworker_bg(struct ub_ctx* ctx)
+{
+	struct libworker* w;
+	/* fork or threadcreate */
+	lock_basic_lock(&ctx->cfglock);
+	if(ctx->dothread) {
+		lock_basic_unlock(&ctx->cfglock);
+		w = libworker_setup(ctx, 1);
+		if(!w) return UB_NOMEM;
+		w->is_bg_thread = 1;
+#ifdef ENABLE_LOCK_CHECKS
+		w->thread_num = 1; /* for nicer DEBUG checklocks */
+#endif
+		ub_thread_create(&ctx->bg_tid, libworker_dobg, w);
+	} else {
+		lock_basic_unlock(&ctx->cfglock);
+#ifndef HAVE_FORK
+		/* no fork on windows */
+		return UB_FORKFAIL;
+#else /* HAVE_FORK */
+		switch((ctx->bg_pid=fork())) {
+			case 0:
+				w = libworker_setup(ctx, 1);
+				if(!w) fatal_exit("out of memory");
+				/* close non-used parts of the pipes */
+				tube_close_write(ctx->qq_pipe);
+				tube_close_read(ctx->rr_pipe);
+				(void)libworker_dobg(w);
+				exit(0);
+				break;
+			case -1:
+				return UB_FORKFAIL;
+			default:
+				break;
+		}
+#endif /* HAVE_FORK */ 
+	}
+	return UB_NOERROR;
+}
+
+/** get msg reply struct (in temp region) */
+static struct reply_info*
+parse_reply(ldns_buffer* pkt, struct regional* region, struct query_info* qi)
+{
+	struct reply_info* rep;
+	struct msg_parse* msg;
+	if(!(msg = regional_alloc(region, sizeof(*msg)))) {
+		return NULL;
+	}
+	memset(msg, 0, sizeof(*msg));
+	ldns_buffer_set_position(pkt, 0);
+	if(parse_packet(pkt, msg, region) != 0)
+		return 0;
+	if(!parse_create_msg(pkt, msg, NULL, qi, &rep, region)) {
+		return 0;
+	}
+	return rep;
+}
+
+/** insert canonname */
+static int
+fill_canon(struct ub_result* res, uint8_t* s)
+{
+	char buf[255+2];
+	dname_str(s, buf);
+	res->canonname = strdup(buf);
+	return res->canonname != 0;
+}
+
+/** fill data into result */
+static int
+fill_res(struct ub_result* res, struct ub_packed_rrset_key* answer,
+	uint8_t* finalcname, struct query_info* rq)
+{
+	size_t i;
+	struct packed_rrset_data* data;
+	if(!answer) {
+		if(finalcname) {
+			if(!fill_canon(res, finalcname))
+				return 0; /* out of memory */
+		}
+		res->data = (char**)calloc(1, sizeof(char*));
+		res->len = (int*)calloc(1, sizeof(int));
+		return (res->data && res->len);
+	}
+	data = (struct packed_rrset_data*)answer->entry.data;
+	if(query_dname_compare(rq->qname, answer->rk.dname) != 0) {
+		if(!fill_canon(res, answer->rk.dname))
+			return 0; /* out of memory */
+	} else	res->canonname = NULL;
+	res->data = (char**)calloc(data->count+1, sizeof(char*));
+	res->len = (int*)calloc(data->count+1, sizeof(int));
+	if(!res->data || !res->len)
+		return 0; /* out of memory */
+	for(i=0; i<data->count; i++) {
+		/* remove rdlength from rdata */
+		res->len[i] = (int)(data->rr_len[i] - 2);
+		res->data[i] = memdup(data->rr_data[i]+2, (size_t)res->len[i]);
+		if(!res->data[i])
+			return 0; /* out of memory */
+	}
+	res->data[data->count] = NULL;
+	res->len[data->count] = 0;
+	return 1;
+}
+
+/** fill result from parsed message, on error fills servfail */
+void
+libworker_enter_result(struct ub_result* res, ldns_buffer* buf,
+	struct regional* temp, enum sec_status msg_security)
+{
+	struct query_info rq;
+	struct reply_info* rep;
+	res->rcode = LDNS_RCODE_SERVFAIL;
+	rep = parse_reply(buf, temp, &rq);
+	if(!rep) {
+		log_err("cannot parse buf");
+		return; /* error parsing buf, or out of memory */
+	}
+	if(!fill_res(res, reply_find_answer_rrset(&rq, rep), 
+		reply_find_final_cname_target(&rq, rep), &rq))
+		return; /* out of memory */
+	/* rcode, havedata, nxdomain, secure, bogus */
+	res->rcode = (int)FLAGS_GET_RCODE(rep->flags);
+	if(res->data && res->data[0])
+		res->havedata = 1;
+	if(res->rcode == LDNS_RCODE_NXDOMAIN)
+		res->nxdomain = 1;
+	if(msg_security == sec_status_secure)
+		res->secure = 1;
+	if(msg_security == sec_status_bogus)
+		res->bogus = 1;
+}
+
+/** fillup fg results */
+static void
+libworker_fillup_fg(struct ctx_query* q, int rcode, ldns_buffer* buf, 
+	enum sec_status s, char* why_bogus)
+{
+	if(why_bogus)
+		q->res->why_bogus = strdup(why_bogus);
+	if(rcode != 0) {
+		q->res->rcode = rcode;
+		q->msg_security = s;
+		return;
+	}
+
+	q->res->rcode = LDNS_RCODE_SERVFAIL;
+	q->msg_security = 0;
+	q->msg = memdup(ldns_buffer_begin(buf), ldns_buffer_limit(buf));
+	q->msg_len = ldns_buffer_limit(buf);
+	if(!q->msg) {
+		return; /* the error is in the rcode */
+	}
+
+	/* canonname and results */
+	q->msg_security = s;
+	libworker_enter_result(q->res, buf, q->w->env->scratch, s);
+}
+
+void
+libworker_fg_done_cb(void* arg, int rcode, ldns_buffer* buf, enum sec_status s,
+	char* why_bogus)
+{
+	struct ctx_query* q = (struct ctx_query*)arg;
+	/* fg query is done; exit comm base */
+	comm_base_exit(q->w->base);
+
+	libworker_fillup_fg(q, rcode, buf, s, why_bogus);
+}
+
+/** setup qinfo and edns */
+static int
+setup_qinfo_edns(struct libworker* w, struct ctx_query* q, 
+	struct query_info* qinfo, struct edns_data* edns)
+{
+	ldns_rdf* rdf;
+	qinfo->qtype = (uint16_t)q->res->qtype;
+	qinfo->qclass = (uint16_t)q->res->qclass;
+	rdf = ldns_dname_new_frm_str(q->res->qname);
+	if(!rdf) {
+		return 0;
+	}
+#ifdef UNBOUND_ALLOC_LITE
+	qinfo->qname = memdup(ldns_rdf_data(rdf), ldns_rdf_size(rdf));
+	qinfo->qname_len = ldns_rdf_size(rdf);
+	ldns_rdf_deep_free(rdf);
+	rdf = 0;
+#else
+	qinfo->qname = ldns_rdf_data(rdf);
+	qinfo->qname_len = ldns_rdf_size(rdf);
+#endif
+	edns->edns_present = 1;
+	edns->ext_rcode = 0;
+	edns->edns_version = 0;
+	edns->bits = EDNS_DO;
+	if(ldns_buffer_capacity(w->back->udp_buff) < 65535)
+		edns->udp_size = (uint16_t)ldns_buffer_capacity(
+			w->back->udp_buff);
+	else	edns->udp_size = 65535;
+	ldns_rdf_free(rdf);
+	return 1;
+}
+
+int libworker_fg(struct ub_ctx* ctx, struct ctx_query* q)
+{
+	struct libworker* w = libworker_setup(ctx, 0);
+	uint16_t qflags, qid;
+	struct query_info qinfo;
+	struct edns_data edns;
+	if(!w)
+		return UB_INITFAIL;
+	if(!setup_qinfo_edns(w, q, &qinfo, &edns)) {
+		libworker_delete(w);
+		return UB_SYNTAX;
+	}
+	qid = 0;
+	qflags = BIT_RD;
+	q->w = w;
+	/* see if there is a fixed answer */
+	ldns_buffer_write_u16_at(w->back->udp_buff, 0, qid);
+	ldns_buffer_write_u16_at(w->back->udp_buff, 2, qflags);
+	if(local_zones_answer(ctx->local_zones, &qinfo, &edns, 
+		w->back->udp_buff, w->env->scratch)) {
+		regional_free_all(w->env->scratch);
+		libworker_fillup_fg(q, LDNS_RCODE_NOERROR, 
+			w->back->udp_buff, sec_status_insecure, NULL);
+		libworker_delete(w);
+		free(qinfo.qname);
+		return UB_NOERROR;
+	}
+	/* process new query */
+	if(!mesh_new_callback(w->env->mesh, &qinfo, qflags, &edns, 
+		w->back->udp_buff, qid, libworker_fg_done_cb, q)) {
+		free(qinfo.qname);
+		return UB_NOMEM;
+	}
+	free(qinfo.qname);
+
+	/* wait for reply */
+	comm_base_dispatch(w->base);
+
+	libworker_delete(w);
+	return UB_NOERROR;
+}
+
+/** add result to the bg worker result queue */
+static void
+add_bg_result(struct libworker* w, struct ctx_query* q, ldns_buffer* pkt, 
+	int err, char* reason)
+{
+	uint8_t* msg = NULL;
+	uint32_t len = 0;
+
+	/* serialize and delete unneeded q */
+	if(w->is_bg_thread) {
+		lock_basic_lock(&w->ctx->cfglock);
+		if(reason)
+			q->res->why_bogus = strdup(reason);
+		if(pkt) {
+			q->msg_len = ldns_buffer_remaining(pkt);
+			q->msg = memdup(ldns_buffer_begin(pkt), q->msg_len);
+			if(!q->msg)
+				msg = context_serialize_answer(q, UB_NOMEM, 
+				NULL, &len);
+			else	msg = context_serialize_answer(q, err, 
+				NULL, &len);
+		} else msg = context_serialize_answer(q, err, NULL, &len);
+		lock_basic_unlock(&w->ctx->cfglock);
+	} else {
+		if(reason)
+			q->res->why_bogus = strdup(reason);
+		msg = context_serialize_answer(q, err, pkt, &len);
+		(void)rbtree_delete(&w->ctx->queries, q->node.key);
+		w->ctx->num_async--;
+		context_query_delete(q);
+	}
+
+	if(!msg) {
+		log_err("out of memory for async answer");
+		return;
+	}
+	if(!tube_queue_item(w->ctx->rr_pipe, msg, len)) {
+		log_err("out of memory for async answer");
+		return;
+	}
+}
+
+void
+libworker_bg_done_cb(void* arg, int rcode, ldns_buffer* buf, enum sec_status s,
+	char* why_bogus)
+{
+	struct ctx_query* q = (struct ctx_query*)arg;
+
+	if(q->cancelled) {
+		if(q->w->is_bg_thread) {
+			/* delete it now */
+			struct ub_ctx* ctx = q->w->ctx;
+			lock_basic_lock(&ctx->cfglock);
+			(void)rbtree_delete(&ctx->queries, q->node.key);
+			ctx->num_async--;
+			context_query_delete(q);
+			lock_basic_unlock(&ctx->cfglock);
+		}
+		/* cancelled, do not give answer */
+		return;
+	}
+	q->msg_security = s;
+	if(rcode != 0) {
+		error_encode(buf, rcode, NULL, 0, BIT_RD, NULL);
+	}
+	add_bg_result(q->w, q, buf, UB_NOERROR, why_bogus);
+}
+
+
+/** handle new query command for bg worker */
+static void
+handle_newq(struct libworker* w, uint8_t* buf, uint32_t len)
+{
+	uint16_t qflags, qid;
+	struct query_info qinfo;
+	struct edns_data edns;
+	struct ctx_query* q;
+	if(w->is_bg_thread) {
+		lock_basic_lock(&w->ctx->cfglock);
+		q = context_lookup_new_query(w->ctx, buf, len);
+		lock_basic_unlock(&w->ctx->cfglock);
+	} else {
+		q = context_deserialize_new_query(w->ctx, buf, len);
+	}
+	free(buf);
+	if(!q) {
+		log_err("failed to deserialize newq");
+		return;
+	}
+	if(!setup_qinfo_edns(w, q, &qinfo, &edns)) {
+		add_bg_result(w, q, NULL, UB_SYNTAX, NULL);
+		return;
+	}
+	qid = 0;
+	qflags = BIT_RD;
+	/* see if there is a fixed answer */
+	ldns_buffer_write_u16_at(w->back->udp_buff, 0, qid);
+	ldns_buffer_write_u16_at(w->back->udp_buff, 2, qflags);
+	if(local_zones_answer(w->ctx->local_zones, &qinfo, &edns, 
+		w->back->udp_buff, w->env->scratch)) {
+		regional_free_all(w->env->scratch);
+		q->msg_security = sec_status_insecure;
+		add_bg_result(w, q, w->back->udp_buff, UB_NOERROR, NULL);
+		free(qinfo.qname);
+		return;
+	}
+	q->w = w;
+	/* process new query */
+	if(!mesh_new_callback(w->env->mesh, &qinfo, qflags, &edns, 
+		w->back->udp_buff, qid, libworker_bg_done_cb, q)) {
+		add_bg_result(w, q, NULL, UB_NOMEM, NULL);
+	}
+	free(qinfo.qname);
+}
+
+void libworker_alloc_cleanup(void* arg)
+{
+	struct libworker* w = (struct libworker*)arg;
+	slabhash_clear(&w->env->rrset_cache->table);
+        slabhash_clear(w->env->msg_cache);
+}
+
+/** compare outbound entry qstates */
+static int
+outbound_entry_compare(void* a, void* b)
+{
+        struct outbound_entry* e1 = (struct outbound_entry*)a;
+        struct outbound_entry* e2 = (struct outbound_entry*)b;
+        if(e1->qstate == e2->qstate)
+                return 1;
+        return 0;
+}
+
+struct outbound_entry* libworker_send_query(uint8_t* qname, size_t qnamelen,
+        uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec,
+	int want_dnssec, struct sockaddr_storage* addr, socklen_t addrlen,
+	uint8_t* zone, size_t zonelen, struct module_qstate* q)
+{
+	struct libworker* w = (struct libworker*)q->env->worker;
+	struct outbound_entry* e = (struct outbound_entry*)regional_alloc(
+		q->region, sizeof(*e));
+	if(!e)
+		return NULL;
+	e->qstate = q;
+	e->qsent = outnet_serviced_query(w->back, qname,
+		qnamelen, qtype, qclass, flags, dnssec, want_dnssec,
+		q->env->cfg->tcp_upstream, q->env->cfg->ssl_upstream, addr,
+		addrlen, zone, zonelen, libworker_handle_service_reply, e,
+		w->back->udp_buff, &outbound_entry_compare);
+	if(!e->qsent) {
+		return NULL;
+	}
+	return e;
+}
+
+int 
+libworker_handle_reply(struct comm_point* c, void* arg, int error,
+        struct comm_reply* reply_info)
+{
+	struct module_qstate* q = (struct module_qstate*)arg;
+	struct libworker* lw = (struct libworker*)q->env->worker;
+	struct outbound_entry e;
+	e.qstate = q;
+	e.qsent = NULL;
+
+	if(error != 0) {
+		mesh_report_reply(lw->env->mesh, &e, reply_info, error);
+		return 0;
+	}
+	/* sanity check. */
+	if(!LDNS_QR_WIRE(ldns_buffer_begin(c->buffer))
+		|| LDNS_OPCODE_WIRE(ldns_buffer_begin(c->buffer)) !=
+			LDNS_PACKET_QUERY
+		|| LDNS_QDCOUNT(ldns_buffer_begin(c->buffer)) > 1) {
+		/* error becomes timeout for the module as if this reply
+		 * never arrived. */
+		mesh_report_reply(lw->env->mesh, &e, reply_info, 
+			NETEVENT_TIMEOUT);
+		return 0;
+	}
+	mesh_report_reply(lw->env->mesh, &e, reply_info, NETEVENT_NOERROR);
+	return 0;
+}
+
+int 
+libworker_handle_service_reply(struct comm_point* c, void* arg, int error,
+        struct comm_reply* reply_info)
+{
+	struct outbound_entry* e = (struct outbound_entry*)arg;
+	struct libworker* lw = (struct libworker*)e->qstate->env->worker;
+
+	if(error != 0) {
+		mesh_report_reply(lw->env->mesh, e, reply_info, error);
+		return 0;
+	}
+	/* sanity check. */
+	if(!LDNS_QR_WIRE(ldns_buffer_begin(c->buffer))
+		|| LDNS_OPCODE_WIRE(ldns_buffer_begin(c->buffer)) !=
+			LDNS_PACKET_QUERY
+		|| LDNS_QDCOUNT(ldns_buffer_begin(c->buffer)) > 1) {
+		/* error becomes timeout for the module as if this reply
+		 * never arrived. */
+		mesh_report_reply(lw->env->mesh, e, reply_info, 
+			NETEVENT_TIMEOUT);
+		return 0;
+	}
+	mesh_report_reply(lw->env->mesh,  e, reply_info, NETEVENT_NOERROR);
+	return 0;
+}
+
+/* --- fake callbacks for fptr_wlist to work --- */
+void worker_handle_control_cmd(struct tube* ATTR_UNUSED(tube), 
+	uint8_t* ATTR_UNUSED(buffer), size_t ATTR_UNUSED(len),
+	int ATTR_UNUSED(error), void* ATTR_UNUSED(arg))
+{
+	log_assert(0);
+}
+
+int worker_handle_request(struct comm_point* ATTR_UNUSED(c), 
+	void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
+        struct comm_reply* ATTR_UNUSED(repinfo))
+{
+	log_assert(0);
+	return 0;
+}
+
+int worker_handle_reply(struct comm_point* ATTR_UNUSED(c), 
+	void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
+        struct comm_reply* ATTR_UNUSED(reply_info))
+{
+	log_assert(0);
+	return 0;
+}
+
+int worker_handle_service_reply(struct comm_point* ATTR_UNUSED(c), 
+	void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
+        struct comm_reply* ATTR_UNUSED(reply_info))
+{
+	log_assert(0);
+	return 0;
+}
+
+int remote_accept_callback(struct comm_point* ATTR_UNUSED(c), 
+	void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
+        struct comm_reply* ATTR_UNUSED(repinfo))
+{
+	log_assert(0);
+	return 0;
+}
+
+int remote_control_callback(struct comm_point* ATTR_UNUSED(c), 
+	void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
+        struct comm_reply* ATTR_UNUSED(repinfo))
+{
+	log_assert(0);
+	return 0;
+}
+
+void worker_sighandler(int ATTR_UNUSED(sig), void* ATTR_UNUSED(arg))
+{
+	log_assert(0);
+}
+
+struct outbound_entry* worker_send_query(uint8_t* ATTR_UNUSED(qname), 
+	size_t ATTR_UNUSED(qnamelen), uint16_t ATTR_UNUSED(qtype), 
+	uint16_t ATTR_UNUSED(qclass), uint16_t ATTR_UNUSED(flags), 
+	int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec),
+	struct sockaddr_storage* ATTR_UNUSED(addr), 
+	socklen_t ATTR_UNUSED(addrlen), struct module_qstate* ATTR_UNUSED(q))
+{
+	log_assert(0);
+	return 0;
+}
+
+void 
+worker_alloc_cleanup(void* ATTR_UNUSED(arg))
+{
+	log_assert(0);
+}
+
+void worker_stat_timer_cb(void* ATTR_UNUSED(arg))
+{
+	log_assert(0);
+}
+
+void worker_probe_timer_cb(void* ATTR_UNUSED(arg))
+{
+	log_assert(0);
+}
+
+int order_lock_cmp(const void* ATTR_UNUSED(e1), const void* ATTR_UNUSED(e2))
+{
+	log_assert(0);
+	return 0;
+}
+
+int
+codeline_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
+{
+	log_assert(0);
+	return 0;
+}
+
+int replay_var_compare(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
+{
+        log_assert(0);
+        return 0;
+}
+
+void remote_get_opt_ssl(char* ATTR_UNUSED(str), void* ATTR_UNUSED(arg))
+{
+        log_assert(0);
+}
+
+#ifdef UB_ON_WINDOWS
+void
+worker_win_stop_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev), void* 
+        ATTR_UNUSED(arg)) {
+        log_assert(0);
+}
+
+void
+wsvc_cron_cb(void* ATTR_UNUSED(arg))
+{
+        log_assert(0);
+}
+#endif /* UB_ON_WINDOWS */
diff --git a/3rdParty/Unbound/src/src/libunbound/libworker.h b/3rdParty/Unbound/src/src/libunbound/libworker.h
new file mode 100644
index 0000000..c3896fc
--- /dev/null
+++ b/3rdParty/Unbound/src/src/libunbound/libworker.h
@@ -0,0 +1,170 @@
+/*
+ * libunbound/worker.h - worker thread or process that resolves
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains the worker process or thread that performs
+ * the DNS resolving and validation. The worker is called by a procedure
+ * and if in the background continues until exit, if in the foreground
+ * returns from the procedure when done.
+ */
+#ifndef LIBUNBOUND_WORKER_H
+#define LIBUNBOUND_WORKER_H
+#include "util/data/packed_rrset.h"
+struct ub_ctx;
+struct ub_result;
+struct module_env;
+struct comm_base;
+struct outside_network;
+struct ub_randstate;
+struct ctx_query;
+struct outbound_entry;
+struct module_qstate;
+struct comm_point;
+struct comm_reply;
+struct regional;
+struct tube;
+
+/** 
+ * The library-worker status structure
+ * Internal to the worker.
+ */
+struct libworker {
+	/** every worker has a unique thread_num. (first in struct) */
+	int thread_num;
+	/** context we are operating under */
+	struct ub_ctx* ctx;
+
+	/** is this the bg worker? */
+	int is_bg;
+	/** is this a bg worker that is threaded (not forked)? */
+	int is_bg_thread;
+
+	/** copy of the module environment with worker local entries. */
+	struct module_env* env;
+	/** the event base this worker works with */
+	struct comm_base* base;
+	/** the backside outside network interface to the auth servers */
+	struct outside_network* back;
+	/** random() table for this worker. */
+	struct ub_randstate* rndstate;
+	/** sslcontext for SSL wrapped DNS over TCP queries */
+	void* sslctx;
+};
+
+/**
+ * Create a background worker
+ * @param ctx: is updated with pid/tid of the background worker.
+ *	a new allocation cache is obtained from ctx. It contains the
+ *	threadnumber and unique id for further (shared) cache insertions.
+ * @return 0 if OK, else error.
+ *	Further communication is done via the pipes in ctx. 
+ */
+int libworker_bg(struct ub_ctx* ctx);
+
+/**
+ * Create a foreground worker.
+ * This worker will join the threadpool of resolver threads.
+ * It exits when the query answer has been obtained (or error).
+ * This routine blocks until the worker is finished.
+ * @param ctx: new allocation cache obtained and returned to it.
+ * @param q: query (result is stored in here).
+ * @return 0 if finished OK, else error.
+ */
+int libworker_fg(struct ub_ctx* ctx, struct ctx_query* q);
+
+/** cleanup the cache to remove all rrset IDs from it, arg is libworker */
+void libworker_alloc_cleanup(void* arg);
+
+/**
+ * Worker service routine to send serviced queries to authoritative servers.
+ * @param qname: query name. (host order)
+ * @param qnamelen: length in bytes of qname, including trailing 0.
+ * @param qtype: query type. (host order)
+ * @param qclass: query class. (host order)
+ * @param flags: host order flags word, with opcode and CD bit.
+ * @param dnssec: if set, EDNS record will have DO bit set.
+ * @param want_dnssec: signatures needed.
+ * @param addr: where to.
+ * @param addrlen: length of addr.
+ * @param zone: delegation point name.
+ * @param zonelen: length of zone name wireformat dname.
+ * @param q: wich query state to reactivate upon return.
+ * @return: false on failure (memory or socket related). no query was
+ *      sent.
+ */
+struct outbound_entry* libworker_send_query(uint8_t* qname, size_t qnamelen,
+        uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec,
+	int want_dnssec, struct sockaddr_storage* addr, socklen_t addrlen,
+	uint8_t* zone, size_t zonelen, struct module_qstate* q);
+
+/** process incoming replies from the network */
+int libworker_handle_reply(struct comm_point* c, void* arg, int error,
+        struct comm_reply* reply_info);
+
+/** process incoming serviced query replies from the network */
+int libworker_handle_service_reply(struct comm_point* c, void* arg, int error,
+        struct comm_reply* reply_info);
+
+/** handle control command coming into server */
+void libworker_handle_control_cmd(struct tube* tube, uint8_t* msg, size_t len,
+	int err, void* arg);
+
+/** handle opportunity to write result back */
+void libworker_handle_result_write(struct tube* tube, uint8_t* msg, size_t len,
+	int err, void* arg);
+
+/** mesh callback with fg results */
+void libworker_fg_done_cb(void* arg, int rcode, ldns_buffer* buf, 
+	enum sec_status s, char* why_bogus);
+
+/** mesh callback with bg results */
+void libworker_bg_done_cb(void* arg, int rcode, ldns_buffer* buf, 
+	enum sec_status s, char* why_bogus);
+
+/** 
+ * fill result from parsed message, on error fills servfail 
+ * @param res: is clear at start, filled in at end.
+ * @param buf: contains DNS message.
+ * @param temp: temporary buffer for parse.
+ * @param msg_security: security status of the DNS message.
+ *   On error, the res may contain a different status 
+ *   (out of memory is not secure, not bogus).
+ */
+void libworker_enter_result(struct ub_result* res, ldns_buffer* buf,
+	struct regional* temp, enum sec_status msg_security);
+
+#endif /* LIBUNBOUND_WORKER_H */
diff --git a/3rdParty/Unbound/src/src/libunbound/unbound.h b/3rdParty/Unbound/src/src/libunbound/unbound.h
new file mode 100644
index 0000000..085f9f5
--- /dev/null
+++ b/3rdParty/Unbound/src/src/libunbound/unbound.h
@@ -0,0 +1,556 @@
+/*
+ * unbound.h - unbound validating resolver public API
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to resolve DNS queries and 
+ * validate the answers. Synchonously and asynchronously.
+ *
+ * Several ways to use this interface from an application wishing
+ * to perform (validated) DNS lookups.
+ *
+ * All start with
+ *	ctx = ub_ctx_create();
+ *	err = ub_ctx_add_ta(ctx, "...");
+ *	err = ub_ctx_add_ta(ctx, "...");
+ *	... some lookups
+ *	... call ub_ctx_delete(ctx); when you want to stop.
+ *
+ * Application not threaded. Blocking.
+ *	int err = ub_resolve(ctx, "www.example.com", ...
+ *	if(err) fprintf(stderr, "lookup error: %s\n", ub_strerror(err));
+ *	... use the answer
+ *
+ * Application not threaded. Non-blocking ('asynchronous').
+ *      err = ub_resolve_async(ctx, "www.example.com", ... my_callback);
+ *	... application resumes processing ...
+ *	... and when either ub_poll(ctx) is true
+ *	... or when the file descriptor ub_fd(ctx) is readable,
+ *	... or whenever, the app calls ...
+ *	ub_process(ctx);
+ *	... if no result is ready, the app resumes processing above,
+ *	... or process() calls my_callback() with results.
+ *
+ *      ... if the application has nothing more to do, wait for answer
+ *      ub_wait(ctx); 
+ *
+ * Application threaded. Blocking.
+ *	Blocking, same as above. The current thread does the work.
+ *	Multiple threads can use the *same context*, each does work and uses
+ *	shared cache data from the context.
+ *
+ * Application threaded. Non-blocking ('asynchronous').
+ *	... setup threaded-asynchronous config option
+ *	err = ub_ctx_async(ctx, 1);
+ *	... same as async for non-threaded
+ *	... the callbacks are called in the thread that calls process(ctx)
+ *
+ * If no threading is compiled in, the above async example uses fork(2) to
+ * create a process to perform the work. The forked process exits when the 
+ * calling process exits, or ctx_delete() is called.
+ * Otherwise, for asynchronous with threading, a worker thread is created.
+ *
+ * The blocking calls use shared ctx-cache when threaded. Thus
+ * ub_resolve() and ub_resolve_async() && ub_wait() are
+ * not the same. The first makes the current thread do the work, setting
+ * up buffers, etc, to perform the work (but using shared cache data).
+ * The second calls another worker thread (or process) to perform the work.
+ * And no buffers need to be set up, but a context-switch happens.
+ */
+#ifndef _UB_UNBOUND_H
+#define _UB_UNBOUND_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The validation context is created to hold the resolver status,
+ * validation keys and a small cache (containing messages, rrsets,
+ * roundtrip times, trusted keys, lameness information).
+ *
+ * Its contents are internally defined.
+ */
+struct ub_ctx;
+
+/**
+ * The validation and resolution results.
+ * Allocated by the resolver, and need to be freed by the application
+ * with ub_resolve_free().
+ */
+struct ub_result {
+	/** The original question, name text string. */
+	char* qname;
+	/** the type asked for */
+	int qtype;
+	/** the class asked for */
+	int qclass;
+
+	/** 
+	 * a list of network order DNS rdata items, terminated with a 
+	 * NULL pointer, so that data[0] is the first result entry,
+	 * data[1] the second, and the last entry is NULL. 
+	 * If there was no data, data[0] is NULL.
+	 */
+	char** data;
+
+	/** the length in bytes of the data items, len[i] for data[i] */
+	int* len;
+
+	/** 
+	 * canonical name for the result (the final cname). 
+	 * zero terminated string.
+	 * May be NULL if no canonical name exists.
+	 */
+	char* canonname;
+
+	/**
+	 * DNS RCODE for the result. May contain additional error code if
+	 * there was no data due to an error. 0 (NOERROR) if okay.
+	 */
+	int rcode;
+
+	/**
+	 * The DNS answer packet. Network formatted. Can contain DNSSEC types.
+	 */
+	void* answer_packet;
+	/** length of the answer packet in octets. */
+	int answer_len;
+
+	/**
+	 * If there is any data, this is true.
+	 * If false, there was no data (nxdomain may be true, rcode can be set).
+	 */
+	int havedata;
+
+	/** 
+	 * If there was no data, and the domain did not exist, this is true.
+	 * If it is false, and there was no data, then the domain name 
+	 * is purported to exist, but the requested data type is not available.
+	 */
+	int nxdomain;
+
+	/**
+	 * True, if the result is validated securely.
+	 * False, if validation failed or domain queried has no security info.
+	 *
+	 * It is possible to get a result with no data (havedata is false),
+	 * and secure is true. This means that the non-existance of the data
+	 * was cryptographically proven (with signatures).
+	 */
+	int secure;
+
+	/** 
+	 * If the result was not secure (secure==0), and this result is due 
+	 * to a security failure, bogus is true.
+	 * This means the data has been actively tampered with, signatures
+	 * failed, expected signatures were not present, timestamps on 
+	 * signatures were out of date and so on.
+	 *
+	 * If !secure and !bogus, this can happen if the data is not secure 
+	 * because security is disabled for that domain name. 
+	 * This means the data is from a domain where data is not signed.
+	 */
+	int bogus;
+	
+	/**
+	 * If the result is bogus this contains a string (zero terminated)
+	 * that describes the failure.  There may be other errors as well
+	 * as the one described, the description may not be perfectly accurate.
+	 * Is NULL if the result is not bogus.
+	 */
+	char* why_bogus;
+};
+
+/**
+ * Callback for results of async queries.
+ * The readable function definition looks like:
+ * void my_callback(void* my_arg, int err, struct ub_result* result);
+ * It is called with
+ *	void* my_arg: your pointer to a (struct of) data of your choice, 
+ *		or NULL.
+ *	int err: if 0 all is OK, otherwise an error occured and no results
+ *	     are forthcoming.
+ *	struct result: pointer to more detailed result structure.
+ *		This structure is allocated on the heap and needs to be
+ *		freed with ub_resolve_free(result);
+ */
+typedef void (*ub_callback_t)(void*, int, struct ub_result*);
+
+/**
+ * Create a resolving and validation context.
+ * The information from /etc/resolv.conf and /etc/hosts is not utilised by
+ * default. Use ub_ctx_resolvconf and ub_ctx_hosts to read them.
+ * @return a new context. default initialisation.
+ * 	returns NULL on error.
+ */
+struct ub_ctx* ub_ctx_create(void);
+
+/**
+ * Destroy a validation context and free all its resources.
+ * Outstanding async queries are killed and callbacks are not called for them.
+ * @param ctx: context to delete.
+ */
+void ub_ctx_delete(struct ub_ctx* ctx);
+
+/**
+ * Set an option for the context.
+ * @param ctx: context.
+ * @param opt: option name from the unbound.conf config file format.
+ *	(not all settings applicable). The name includes the trailing ':'
+ *	for example ub_ctx_set_option(ctx, "logfile:", "mylog.txt");
+ * 	This is a power-users interface that lets you specify all sorts
+ * 	of options.
+ * 	For some specific options, such as adding trust anchors, special
+ * 	routines exist.
+ * @param val: value of the option.
+ * @return: 0 if OK, else error.
+ */
+int ub_ctx_set_option(struct ub_ctx* ctx, char* opt, char* val);
+
+/**
+ * Get an option from the context.
+ * @param ctx: context.
+ * @param opt: option name from the unbound.conf config file format.
+ *	(not all settings applicable). The name excludes the trailing ':'
+ *	for example ub_ctx_get_option(ctx, "logfile", &result);
+ * 	This is a power-users interface that lets you specify all sorts
+ * 	of options.
+ * @param str: the string is malloced and returned here. NULL on error.
+ * 	The caller must free() the string.  In cases with multiple 
+ * 	entries (auto-trust-anchor-file), a newline delimited list is 
+ * 	returned in the string.
+ * @return 0 if OK else an error code (malloc failure, syntax error).
+ */
+int ub_ctx_get_option(struct ub_ctx* ctx, char* opt, char** str);
+
+/**
+ * setup configuration for the given context.
+ * @param ctx: context.
+ * @param fname: unbound config file (not all settings applicable).
+ * 	This is a power-users interface that lets you specify all sorts
+ * 	of options.
+ * 	For some specific options, such as adding trust anchors, special
+ * 	routines exist.
+ * @return: 0 if OK, else error.
+ */
+int ub_ctx_config(struct ub_ctx* ctx, char* fname);
+
+/**
+ * Set machine to forward DNS queries to, the caching resolver to use. 
+ * IP4 or IP6 address. Forwards all DNS requests to that machine, which 
+ * is expected to run a recursive resolver. If the proxy is not 
+ * DNSSEC-capable, validation may fail. Can be called several times, in 
+ * that case the addresses are used as backup servers.
+ *
+ * To read the list of nameservers from /etc/resolv.conf (from DHCP or so),
+ * use the call ub_ctx_resolvconf.
+ *
+ * @param ctx: context.
+ *	At this time it is only possible to set configuration before the
+ *	first resolve is done.
+ * @param addr: address, IP4 or IP6 in string format.
+ * 	If the addr is NULL, forwarding is disabled.
+ * @return 0 if OK, else error.
+ */
+int ub_ctx_set_fwd(struct ub_ctx* ctx, char* addr);
+
+/**
+ * Read list of nameservers to use from the filename given.
+ * Usually "/etc/resolv.conf". Uses those nameservers as caching proxies.
+ * If they do not support DNSSEC, validation may fail.
+ *
+ * Only nameservers are picked up, the searchdomain, ndots and other
+ * settings from resolv.conf(5) are ignored.
+ *
+ * @param ctx: context.
+ *	At this time it is only possible to set configuration before the
+ *	first resolve is done.
+ * @param fname: file name string. If NULL "/etc/resolv.conf" is used.
+ * @return 0 if OK, else error.
+ */
+int ub_ctx_resolvconf(struct ub_ctx* ctx, char* fname);
+
+/**
+ * Read list of hosts from the filename given.
+ * Usually "/etc/hosts". 
+ * These addresses are not flagged as DNSSEC secure when queried for.
+ *
+ * @param ctx: context.
+ *	At this time it is only possible to set configuration before the
+ *	first resolve is done.
+ * @param fname: file name string. If NULL "/etc/hosts" is used.
+ * @return 0 if OK, else error.
+ */
+int ub_ctx_hosts(struct ub_ctx* ctx, char* fname);
+
+/**
+ * Add a trust anchor to the given context.
+ * The trust anchor is a string, on one line, that holds a valid DNSKEY or
+ * DS RR. 
+ * @param ctx: context.
+ *	At this time it is only possible to add trusted keys before the
+ *	first resolve is done.
+ * @param ta: string, with zone-format RR on one line.
+ * 	[domainname] [TTL optional] [type] [class optional] [rdata contents]
+ * @return 0 if OK, else error.
+ */
+int ub_ctx_add_ta(struct ub_ctx* ctx, char* ta);
+
+/**
+ * Add trust anchors to the given context.
+ * Pass name of a file with DS and DNSKEY records (like from dig or drill).
+ * @param ctx: context.
+ *	At this time it is only possible to add trusted keys before the
+ *	first resolve is done.
+ * @param fname: filename of file with keyfile with trust anchors.
+ * @return 0 if OK, else error.
+ */
+int ub_ctx_add_ta_file(struct ub_ctx* ctx, char* fname);
+
+/**
+ * Add trust anchors to the given context.
+ * Pass the name of a bind-style config file with trusted-keys{}.
+ * @param ctx: context.
+ *	At this time it is only possible to add trusted keys before the
+ *	first resolve is done.
+ * @param fname: filename of file with bind-style config entries with trust
+ * 	anchors.
+ * @return 0 if OK, else error.
+ */
+int ub_ctx_trustedkeys(struct ub_ctx* ctx, char* fname);
+
+/**
+ * Set debug output (and error output) to the specified stream.
+ * Pass NULL to disable. Default is stderr.
+ * @param ctx: context.
+ * @param out: FILE* out file stream to log to.
+ * 	Type void* to avoid stdio dependency of this header file.
+ * @return 0 if OK, else error.
+ */
+int ub_ctx_debugout(struct ub_ctx* ctx, void* out);
+
+/**
+ * Set debug verbosity for the context
+ * Output is directed to stderr.
+ * @param ctx: context.
+ * @param d: debug level, 0 is off, 1 is very minimal, 2 is detailed, 
+ *	and 3 is lots.
+ * @return 0 if OK, else error.
+ */
+int ub_ctx_debuglevel(struct ub_ctx* ctx, int d);
+
+/**
+ * Set a context behaviour for asynchronous action.
+ * @param ctx: context.
+ * @param dothread: if true, enables threading and a call to resolve_async() 
+ *	creates a thread to handle work in the background.
+ *	If false, a process is forked to handle work in the background.
+ *	Changes to this setting after async() calls have been made have 
+ *	no effect (delete and re-create the context to change).
+ * @return 0 if OK, else error.
+ */
+int ub_ctx_async(struct ub_ctx* ctx, int dothread);
+
+/**
+ * Poll a context to see if it has any new results
+ * Do not poll in a loop, instead extract the fd below to poll for readiness,
+ * and then check, or wait using the wait routine.
+ * @param ctx: context.
+ * @return: 0 if nothing to read, or nonzero if a result is available.
+ * 	If nonzero, call ctx_process() to do callbacks.
+ */
+int ub_poll(struct ub_ctx* ctx);
+
+/**
+ * Wait for a context to finish with results. Calls ub_process() after
+ * the wait for you. After the wait, there are no more outstanding 
+ * asynchronous queries.
+ * @param ctx: context.
+ * @return: 0 if OK, else error.
+ */
+int ub_wait(struct ub_ctx* ctx);
+
+/**
+ * Get file descriptor. Wait for it to become readable, at this point
+ * answers are returned from the asynchronous validating resolver.
+ * Then call the ub_process to continue processing.
+ * This routine works immediately after context creation, the fd
+ * does not change.
+ * @param ctx: context.
+ * @return: -1 on error, or file descriptor to use select(2) with.
+ */
+int ub_fd(struct ub_ctx* ctx);
+
+/**
+ * Call this routine to continue processing results from the validating
+ * resolver (when the fd becomes readable).
+ * Will perform necessary callbacks.
+ * @param ctx: context
+ * @return: 0 if OK, else error.
+ */
+int ub_process(struct ub_ctx* ctx);
+
+/**
+ * Perform resolution and validation of the target name.
+ * @param ctx: context.
+ *	The context is finalized, and can no longer accept config changes.
+ * @param name: domain name in text format (a zero terminated text string).
+ * @param rrtype: type of RR in host order, 1 is A (address).
+ * @param rrclass: class of RR in host order, 1 is IN (for internet).
+ * @param result: the result data is returned in a newly allocated result
+ * 	structure. May be NULL on return, return value is set to an error 
+ * 	in that case (out of memory).
+ * @return 0 if OK, else error.
+ */
+int ub_resolve(struct ub_ctx* ctx, char* name, int rrtype, 
+	int rrclass, struct ub_result** result);
+
+/**
+ * Perform resolution and validation of the target name.
+ * Asynchronous, after a while, the callback will be called with your
+ * data and the result.
+ * @param ctx: context.
+ *	If no thread or process has been created yet to perform the
+ *	work in the background, it is created now.
+ *	The context is finalized, and can no longer accept config changes.
+ * @param name: domain name in text format (a string).
+ * @param rrtype: type of RR in host order, 1 is A.
+ * @param rrclass: class of RR in host order, 1 is IN (for internet).
+ * @param mydata: this data is your own data (you can pass NULL),
+ * 	and is passed on to the callback function.
+ * @param callback: this is called on completion of the resolution.
+ * 	It is called as:
+ * 	void callback(void* mydata, int err, struct ub_result* result)
+ * 	with mydata: the same as passed here, you may pass NULL,
+ * 	with err: is 0 when a result has been found.
+ * 	with result: a newly allocated result structure.
+ *		The result may be NULL, in that case err is set.
+ *
+ * 	If an error happens during processing, your callback will be called
+ * 	with error set to a nonzero value (and result==NULL).
+ * @param async_id: if you pass a non-NULL value, an identifier number is
+ *	returned for the query as it is in progress. It can be used to 
+ *	cancel the query.
+ * @return 0 if OK, else error.
+ */
+int ub_resolve_async(struct ub_ctx* ctx, char* name, int rrtype, 
+	int rrclass, void* mydata, ub_callback_t callback, int* async_id);
+
+/**
+ * Cancel an async query in progress.
+ * Its callback will not be called.
+ *
+ * @param ctx: context.
+ * @param async_id: which query to cancel.
+ * @return 0 if OK, else error.
+ * This routine can return an error if the async_id passed does not exist
+ * or has already been delivered. If another thread is processing results
+ * at the same time, the result may be delivered at the same time and the
+ * cancel fails with an error.  Also the cancel can fail due to a system
+ * error, no memory or socket failures.
+ */
+int ub_cancel(struct ub_ctx* ctx, int async_id);
+
+/**
+ * Free storage associated with a result structure.
+ * @param result: to free
+ */
+void ub_resolve_free(struct ub_result* result);
+
+/** 
+ * Convert error value to a human readable string.
+ * @param err: error code from one of the ub_val* functions.
+ * @return pointer to constant text string, zero terminated.
+ */
+const char* ub_strerror(int err);
+
+/**
+ * Debug routine.  Print the local zone information to debug output.
+ * @param ctx: context.  Is finalized by the routine.
+ * @return 0 if OK, else error.
+ */
+int ub_ctx_print_local_zones(struct ub_ctx* ctx);
+
+/**
+ * Add a new zone with the zonetype to the local authority info of the 
+ * library.
+ * @param ctx: context.  Is finalized by the routine.
+ * @param zone_name: name of the zone in text, "example.com"
+ *	If it already exists, the type is updated.
+ * @param zone_type: type of the zone (like for unbound.conf) in text.
+ * @return 0 if OK, else error.
+ */
+int ub_ctx_zone_add(struct ub_ctx* ctx, char *zone_name, char *zone_type);
+
+/**
+ * Remove zone from local authority info of the library.
+ * @param ctx: context.  Is finalized by the routine.
+ * @param zone_name: name of the zone in text, "example.com"
+ *	If it does not exist, nothing happens.
+ * @return 0 if OK, else error.
+ */
+int ub_ctx_zone_remove(struct ub_ctx* ctx, char *zone_name);
+
+/**
+ * Add localdata to the library local authority info.
+ * Similar to local-data config statement.
+ * @param ctx: context.  Is finalized by the routine.
+ * @param data: the resource record in text format, for example
+ *	"www.example.com IN A 127.0.0.1"
+ * @return 0 if OK, else error.
+ */
+int ub_ctx_data_add(struct ub_ctx* ctx, char *data);
+
+/**
+ * Remove localdata from the library local authority info.
+ * @param ctx: context.  Is finalized by the routine.
+ * @param data: the name to delete all data from, like "www.example.com".
+ * @return 0 if OK, else error.
+ */
+int ub_ctx_data_remove(struct ub_ctx* ctx, char *data);
+
+/**
+ * Get a version string from the libunbound implementation.
+ * @return a static constant string with the version number.
+ */
+const char* ub_version(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _UB_UNBOUND_H */
diff --git a/3rdParty/Unbound/src/src/services/cache/dns.c b/3rdParty/Unbound/src/src/services/cache/dns.c
new file mode 100644
index 0000000..49234ab
--- /dev/null
+++ b/3rdParty/Unbound/src/src/services/cache/dns.c
@@ -0,0 +1,783 @@
+/*
+ * services/cache/dns.c - Cache services for DNS using msg and rrset caches.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains the DNS cache.
+ */
+#include "config.h"
+#include "iterator/iter_delegpt.h"
+#include "validator/val_nsec.h"
+#include "services/cache/dns.h"
+#include "services/cache/rrset.h"
+#include "util/data/msgreply.h"
+#include "util/data/packed_rrset.h"
+#include "util/data/dname.h"
+#include "util/module.h"
+#include "util/net_help.h"
+#include "util/regional.h"
+#include "util/config_file.h"
+
+/** store rrsets in the rrset cache. 
+ * @param env: module environment with caches.
+ * @param rep: contains list of rrsets to store.
+ * @param now: current time.
+ * @param qrep: update rrsets here if cache is better
+ * @param region: for qrep allocs.
+ */
+static void
+store_rrsets(struct module_env* env, struct reply_info* rep, uint32_t now,
+	struct reply_info* qrep, struct regional* region)
+{
+        size_t i;
+        /* see if rrset already exists in cache, if not insert it. */
+        for(i=0; i<rep->rrset_count; i++) {
+                rep->ref[i].key = rep->rrsets[i];
+                rep->ref[i].id = rep->rrsets[i]->id;
+		/* update ref if it was in the cache */ 
+		switch(rrset_cache_update(env->rrset_cache, &rep->ref[i],
+                        env->alloc, now)) {
+		case 0: /* ref unchanged, item inserted */
+			break;
+		case 2: /* ref updated, cache is superior */
+			if(region) {
+				struct ub_packed_rrset_key* ck;
+				lock_rw_rdlock(&rep->ref[i].key->entry.lock);
+				/* if deleted rrset, do not copy it */
+				if(rep->ref[i].key->id == 0)
+					ck = NULL;
+				else 	ck = packed_rrset_copy_region(
+					rep->ref[i].key, region, now);
+				lock_rw_unlock(&rep->ref[i].key->entry.lock);
+				if(ck) {
+					/* use cached copy if memory allows */
+					qrep->rrsets[i] = ck;
+				}
+			}
+			/* no break: also copy key item */
+		case 1: /* ref updated, item inserted */
+                        rep->rrsets[i] = rep->ref[i].key;
+		}
+        }
+}
+
+void 
+dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
+	hashvalue_t hash, struct reply_info* rep, uint32_t leeway,
+	struct reply_info* qrep, struct regional* region)
+{
+	struct msgreply_entry* e;
+	uint32_t ttl = rep->ttl;
+	size_t i;
+
+	/* store RRsets */
+        for(i=0; i<rep->rrset_count; i++) {
+		rep->ref[i].key = rep->rrsets[i];
+		rep->ref[i].id = rep->rrsets[i]->id;
+	}
+
+	/* there was a reply_info_sortref(rep) here but it seems to be
+	 * unnecessary, because the cache gets locked per rrset. */
+	reply_info_set_ttls(rep, *env->now);
+	store_rrsets(env, rep, *env->now+leeway, qrep, region);
+	if(ttl == 0) {
+		/* we do not store the message, but we did store the RRs,
+		 * which could be useful for delegation information */
+		verbose(VERB_ALGO, "TTL 0: dropped msg from cache");
+		free(rep);
+		return;
+	}
+
+	/* store msg in the cache */
+	reply_info_sortref(rep);
+	if(!(e = query_info_entrysetup(qinfo, rep, hash))) {
+		log_err("store_msg: malloc failed");
+		return;
+	}
+	slabhash_insert(env->msg_cache, hash, &e->entry, rep, env->alloc);
+}
+
+/** find closest NS or DNAME and returns the rrset (locked) */
+static struct ub_packed_rrset_key*
+find_closest_of_type(struct module_env* env, uint8_t* qname, size_t qnamelen, 
+	uint16_t qclass, uint32_t now, uint16_t searchtype, int stripfront)
+{
+	struct ub_packed_rrset_key *rrset;
+	uint8_t lablen;
+
+	if(stripfront) {
+		/* strip off so that DNAMEs have strict subdomain match */
+		lablen = *qname;
+		qname += lablen + 1;
+		qnamelen -= lablen + 1;
+	}
+
+	/* snip off front part of qname until the type is found */
+	while(qnamelen > 0) {
+		if((rrset = rrset_cache_lookup(env->rrset_cache, qname, 
+			qnamelen, searchtype, qclass, 0, now, 0)))
+			return rrset;
+
+		/* snip off front label */
+		lablen = *qname;
+		qname += lablen + 1;
+		qnamelen -= lablen + 1;
+	}
+	return NULL;
+}
+
+/** add addr to additional section */
+static void
+addr_to_additional(struct ub_packed_rrset_key* rrset, struct regional* region,
+	struct dns_msg* msg, uint32_t now)
+{
+	if((msg->rep->rrsets[msg->rep->rrset_count] = 
+		packed_rrset_copy_region(rrset, region, now))) {
+		msg->rep->ar_numrrsets++;
+		msg->rep->rrset_count++;
+	}
+}
+
+/** lookup message in message cache */
+static struct msgreply_entry* 
+msg_cache_lookup(struct module_env* env, uint8_t* qname, size_t qnamelen, 
+	uint16_t qtype, uint16_t qclass, uint32_t now, int wr)
+{
+	struct lruhash_entry* e;
+	struct query_info k;
+	hashvalue_t h;
+
+	k.qname = qname;
+	k.qname_len = qnamelen;
+	k.qtype = qtype;
+	k.qclass = qclass;
+	h = query_info_hash(&k);
+	e = slabhash_lookup(env->msg_cache, h, &k, wr);
+
+	if(!e) return NULL;
+	if( now > ((struct reply_info*)e->data)->ttl ) {
+		lock_rw_unlock(&e->lock);
+		return NULL;
+	}
+	return (struct msgreply_entry*)e->key;
+}
+
+/** find and add A and AAAA records for nameservers in delegpt */
+static int
+find_add_addrs(struct module_env* env, uint16_t qclass, 
+	struct regional* region, struct delegpt* dp, uint32_t now, 
+	struct dns_msg** msg)
+{
+	struct delegpt_ns* ns;
+	struct msgreply_entry* neg;
+	struct ub_packed_rrset_key* akey;
+	for(ns = dp->nslist; ns; ns = ns->next) {
+		akey = rrset_cache_lookup(env->rrset_cache, ns->name, 
+			ns->namelen, LDNS_RR_TYPE_A, qclass, 0, now, 0);
+		if(akey) {
+			if(!delegpt_add_rrset_A(dp, region, akey, 0)) {
+				lock_rw_unlock(&akey->entry.lock);
+				return 0;
+			}
+			if(msg)
+				addr_to_additional(akey, region, *msg, now);
+			lock_rw_unlock(&akey->entry.lock);
+		} else {
+			neg = msg_cache_lookup(env, ns->name, ns->namelen,
+				LDNS_RR_TYPE_A, qclass, now, 0);
+			if(neg) {
+				delegpt_add_neg_msg(dp, neg);
+				lock_rw_unlock(&neg->entry.lock);
+			}
+		}
+		akey = rrset_cache_lookup(env->rrset_cache, ns->name, 
+			ns->namelen, LDNS_RR_TYPE_AAAA, qclass, 0, now, 0);
+		if(akey) {
+			if(!delegpt_add_rrset_AAAA(dp, region, akey, 0)) {
+				lock_rw_unlock(&akey->entry.lock);
+				return 0;
+			}
+			if(msg)
+				addr_to_additional(akey, region, *msg, now);
+			lock_rw_unlock(&akey->entry.lock);
+		} else {
+			neg = msg_cache_lookup(env, ns->name, ns->namelen,
+				LDNS_RR_TYPE_AAAA, qclass, now, 0);
+			if(neg) {
+				delegpt_add_neg_msg(dp, neg);
+				lock_rw_unlock(&neg->entry.lock);
+			}
+		}
+	}
+	return 1;
+}
+
+/** find and add A and AAAA records for missing nameservers in delegpt */
+int
+cache_fill_missing(struct module_env* env, uint16_t qclass, 
+	struct regional* region, struct delegpt* dp)
+{
+	struct delegpt_ns* ns;
+	struct msgreply_entry* neg;
+	struct ub_packed_rrset_key* akey;
+	uint32_t now = *env->now;
+	for(ns = dp->nslist; ns; ns = ns->next) {
+		akey = rrset_cache_lookup(env->rrset_cache, ns->name, 
+			ns->namelen, LDNS_RR_TYPE_A, qclass, 0, now, 0);
+		if(akey) {
+			if(!delegpt_add_rrset_A(dp, region, akey, (int)ns->lame)) {
+				lock_rw_unlock(&akey->entry.lock);
+				return 0;
+			}
+			log_nametypeclass(VERB_ALGO, "found in cache",
+				ns->name, LDNS_RR_TYPE_A, qclass);
+			lock_rw_unlock(&akey->entry.lock);
+		} else {
+			neg = msg_cache_lookup(env, ns->name, ns->namelen,
+				LDNS_RR_TYPE_A, qclass, now, 0);
+			if(neg) {
+				delegpt_add_neg_msg(dp, neg);
+				lock_rw_unlock(&neg->entry.lock);
+			}
+		}
+		akey = rrset_cache_lookup(env->rrset_cache, ns->name, 
+			ns->namelen, LDNS_RR_TYPE_AAAA, qclass, 0, now, 0);
+		if(akey) {
+			if(!delegpt_add_rrset_AAAA(dp, region, akey, (int)ns->lame)) {
+				lock_rw_unlock(&akey->entry.lock);
+				return 0;
+			}
+			log_nametypeclass(VERB_ALGO, "found in cache",
+				ns->name, LDNS_RR_TYPE_AAAA, qclass);
+			lock_rw_unlock(&akey->entry.lock);
+		} else {
+			neg = msg_cache_lookup(env, ns->name, ns->namelen,
+				LDNS_RR_TYPE_AAAA, qclass, now, 0);
+			if(neg) {
+				delegpt_add_neg_msg(dp, neg);
+				lock_rw_unlock(&neg->entry.lock);
+			}
+		}
+	}
+	return 1;
+}
+
+/** find and add DS or NSEC to delegation msg */
+static void
+find_add_ds(struct module_env* env, struct regional* region, 
+	struct dns_msg* msg, struct delegpt* dp, uint32_t now)
+{
+	/* Lookup the DS or NSEC at the delegation point. */
+	struct ub_packed_rrset_key* rrset = rrset_cache_lookup(
+		env->rrset_cache, dp->name, dp->namelen, LDNS_RR_TYPE_DS, 
+		msg->qinfo.qclass, 0, now, 0);
+	if(!rrset) {
+		/* NOTE: this won't work for alternate NSEC schemes 
+		 *	(opt-in, NSEC3) */
+		rrset = rrset_cache_lookup(env->rrset_cache, dp->name, 
+			dp->namelen, LDNS_RR_TYPE_NSEC, msg->qinfo.qclass, 
+			0, now, 0);
+		/* Note: the PACKED_RRSET_NSEC_AT_APEX flag is not used.
+		 * since this is a referral, we need the NSEC at the parent
+		 * side of the zone cut, not the NSEC at apex side. */
+		if(rrset && nsec_has_type(rrset, LDNS_RR_TYPE_DS)) {
+			lock_rw_unlock(&rrset->entry.lock);
+			rrset = NULL; /* discard wrong NSEC */
+		}
+	}
+	if(rrset) {
+		/* add it to auth section. This is the second rrset. */
+		if((msg->rep->rrsets[msg->rep->rrset_count] = 
+			packed_rrset_copy_region(rrset, region, now))) {
+			msg->rep->ns_numrrsets++;
+			msg->rep->rrset_count++;
+		}
+		lock_rw_unlock(&rrset->entry.lock);
+	}
+}
+
+struct dns_msg*
+dns_msg_create(uint8_t* qname, size_t qnamelen, uint16_t qtype, 
+	uint16_t qclass, struct regional* region, size_t capacity)
+{
+	struct dns_msg* msg = (struct dns_msg*)regional_alloc(region,
+		sizeof(struct dns_msg));
+	if(!msg)
+		return NULL;
+	msg->qinfo.qname = regional_alloc_init(region, qname, qnamelen);
+	if(!msg->qinfo.qname)
+		return NULL;
+	msg->qinfo.qname_len = qnamelen;
+	msg->qinfo.qtype = qtype;
+	msg->qinfo.qclass = qclass;
+	/* non-packed reply_info, because it needs to grow the array */
+	msg->rep = (struct reply_info*)regional_alloc_zero(region, 
+		sizeof(struct reply_info)-sizeof(struct rrset_ref));
+	if(!msg->rep)
+		return NULL;
+	msg->rep->flags = BIT_QR; /* with QR, no AA */
+	msg->rep->qdcount = 1;
+	msg->rep->rrsets = (struct ub_packed_rrset_key**)
+		regional_alloc(region, 
+		capacity*sizeof(struct ub_packed_rrset_key*));
+	if(!msg->rep->rrsets)
+		return NULL;
+	return msg;
+}
+
+int
+dns_msg_authadd(struct dns_msg* msg, struct regional* region, 
+	struct ub_packed_rrset_key* rrset, uint32_t now)
+{
+	if(!(msg->rep->rrsets[msg->rep->rrset_count++] = 
+		packed_rrset_copy_region(rrset, region, now)))
+		return 0;
+	msg->rep->ns_numrrsets++;
+	return 1;
+}
+
+struct delegpt* 
+dns_cache_find_delegation(struct module_env* env, uint8_t* qname, 
+	size_t qnamelen, uint16_t qtype, uint16_t qclass, 
+	struct regional* region, struct dns_msg** msg, uint32_t now)
+{
+	/* try to find closest NS rrset */
+	struct ub_packed_rrset_key* nskey;
+	struct packed_rrset_data* nsdata;
+	struct delegpt* dp;
+
+	nskey = find_closest_of_type(env, qname, qnamelen, qclass, now,
+		LDNS_RR_TYPE_NS, 0);
+	if(!nskey) /* hope the caller has hints to prime or something */
+		return NULL;
+	nsdata = (struct packed_rrset_data*)nskey->entry.data;
+	/* got the NS key, create delegation point */
+	dp = delegpt_create(region);
+	if(!dp || !delegpt_set_name(dp, region, nskey->rk.dname)) {
+		lock_rw_unlock(&nskey->entry.lock);
+		log_err("find_delegation: out of memory");
+		return NULL;
+	}
+	/* create referral message */
+	if(msg) {
+		/* allocate the array to as much as we could need:
+		 *	NS rrset + DS/NSEC rrset +
+		 *	A rrset for every NS RR
+		 *	AAAA rrset for every NS RR
+		 */
+		*msg = dns_msg_create(qname, qnamelen, qtype, qclass, region, 
+			2 + nsdata->count*2);
+		if(!*msg || !dns_msg_authadd(*msg, region, nskey, now)) {
+			lock_rw_unlock(&nskey->entry.lock);
+			log_err("find_delegation: out of memory");
+			return NULL;
+		}
+	}
+	if(!delegpt_rrset_add_ns(dp, region, nskey, 0))
+		log_err("find_delegation: addns out of memory");
+	lock_rw_unlock(&nskey->entry.lock); /* first unlock before next lookup*/
+	/* find and add DS/NSEC (if any) */
+	if(msg)
+		find_add_ds(env, region, *msg, dp, now);
+	/* find and add A entries */
+	if(!find_add_addrs(env, qclass, region, dp, now, msg))
+		log_err("find_delegation: addrs out of memory");
+	return dp;
+}
+
+/** allocate dns_msg from query_info and reply_info */
+static struct dns_msg*
+gen_dns_msg(struct regional* region, struct query_info* q, size_t num)
+{
+	struct dns_msg* msg = (struct dns_msg*)regional_alloc(region, 
+		sizeof(struct dns_msg));
+	if(!msg)
+		return NULL;
+	memcpy(&msg->qinfo, q, sizeof(struct query_info));
+	msg->qinfo.qname = regional_alloc_init(region, q->qname, q->qname_len);
+	if(!msg->qinfo.qname)
+		return NULL;
+	/* allocate replyinfo struct and rrset key array separately */
+	msg->rep = (struct reply_info*)regional_alloc(region,
+		sizeof(struct reply_info) - sizeof(struct rrset_ref));
+	if(!msg->rep)
+		return NULL;
+	msg->rep->rrsets = (struct ub_packed_rrset_key**)
+		regional_alloc(region,
+		num * sizeof(struct ub_packed_rrset_key*));
+	if(!msg->rep->rrsets)
+		return NULL;
+	return msg;
+}
+
+/** generate dns_msg from cached message */
+static struct dns_msg*
+tomsg(struct module_env* env, struct query_info* q, struct reply_info* r, 
+	struct regional* region, uint32_t now, struct regional* scratch)
+{
+	struct dns_msg* msg;
+	size_t i;
+	if(now > r->ttl)
+		return NULL;
+	msg = gen_dns_msg(region, q, r->rrset_count);
+	if(!msg)
+		return NULL;
+	msg->rep->flags = r->flags;
+	msg->rep->qdcount = r->qdcount;
+	msg->rep->ttl = r->ttl - now;
+	if(r->prefetch_ttl > now)
+		msg->rep->prefetch_ttl = r->prefetch_ttl - now;
+	else	msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl);
+	msg->rep->security = r->security;
+	msg->rep->an_numrrsets = r->an_numrrsets;
+	msg->rep->ns_numrrsets = r->ns_numrrsets;
+	msg->rep->ar_numrrsets = r->ar_numrrsets;
+	msg->rep->rrset_count = r->rrset_count;
+        msg->rep->authoritative = r->authoritative;
+	if(!rrset_array_lock(r->ref, r->rrset_count, now))
+		return NULL;
+	if(r->an_numrrsets > 0 && (r->rrsets[0]->rk.type == htons(
+		LDNS_RR_TYPE_CNAME) || r->rrsets[0]->rk.type == htons(
+		LDNS_RR_TYPE_DNAME)) && !reply_check_cname_chain(r)) {
+		/* cname chain is now invalid, reconstruct msg */
+		rrset_array_unlock(r->ref, r->rrset_count);
+		return NULL;
+	}
+	if(r->security == sec_status_secure && !reply_all_rrsets_secure(r)) {
+		/* message rrsets have changed status, revalidate */
+		rrset_array_unlock(r->ref, r->rrset_count);
+		return NULL;
+	}
+	for(i=0; i<msg->rep->rrset_count; i++) {
+		msg->rep->rrsets[i] = packed_rrset_copy_region(r->rrsets[i], 
+			region, now);
+		if(!msg->rep->rrsets[i]) {
+			rrset_array_unlock(r->ref, r->rrset_count);
+			return NULL;
+		}
+	}
+	rrset_array_unlock_touch(env->rrset_cache, scratch, r->ref, 
+		r->rrset_count);
+	return msg;
+}
+
+/** synthesize RRset-only response from cached RRset item */
+static struct dns_msg*
+rrset_msg(struct ub_packed_rrset_key* rrset, struct regional* region, 
+	uint32_t now, struct query_info* q)
+{
+	struct dns_msg* msg;
+	struct packed_rrset_data* d = (struct packed_rrset_data*)
+		rrset->entry.data;
+	if(now > d->ttl)
+		return NULL;
+	msg = gen_dns_msg(region, q, 1); /* only the CNAME (or other) RRset */
+	if(!msg)
+		return NULL;
+	msg->rep->flags = BIT_QR; /* reply, no AA, no error */
+        msg->rep->authoritative = 0; /* reply stored in cache can't be authoritative */
+	msg->rep->qdcount = 1;
+	msg->rep->ttl = d->ttl - now;
+	msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl);
+	msg->rep->security = sec_status_unchecked;
+	msg->rep->an_numrrsets = 1;
+	msg->rep->ns_numrrsets = 0;
+	msg->rep->ar_numrrsets = 0;
+	msg->rep->rrset_count = 1;
+	msg->rep->rrsets[0] = packed_rrset_copy_region(rrset, region, now);
+	if(!msg->rep->rrsets[0]) /* copy CNAME */
+		return NULL;
+	return msg;
+}
+
+/** synthesize DNAME+CNAME response from cached DNAME item */
+static struct dns_msg*
+synth_dname_msg(struct ub_packed_rrset_key* rrset, struct regional* region, 
+	uint32_t now, struct query_info* q)
+{
+	struct dns_msg* msg;
+	struct ub_packed_rrset_key* ck;
+	struct packed_rrset_data* newd, *d = (struct packed_rrset_data*)
+		rrset->entry.data;
+	uint8_t* newname, *dtarg = NULL;
+	size_t newlen, dtarglen;
+	if(now > d->ttl)
+		return NULL;
+	/* only allow validated (with DNSSEC) DNAMEs used from cache 
+	 * for insecure DNAMEs, query again. */
+	if(d->security != sec_status_secure)
+		return NULL;
+	msg = gen_dns_msg(region, q, 2); /* DNAME + CNAME RRset */
+	if(!msg)
+		return NULL;
+	msg->rep->flags = BIT_QR; /* reply, no AA, no error */
+        msg->rep->authoritative = 0; /* reply stored in cache can't be authoritative */
+	msg->rep->qdcount = 1;
+	msg->rep->ttl = d->ttl - now;
+	msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl);
+	msg->rep->security = sec_status_unchecked;
+	msg->rep->an_numrrsets = 1;
+	msg->rep->ns_numrrsets = 0;
+	msg->rep->ar_numrrsets = 0;
+	msg->rep->rrset_count = 1;
+	msg->rep->rrsets[0] = packed_rrset_copy_region(rrset, region, now);
+	if(!msg->rep->rrsets[0]) /* copy DNAME */
+		return NULL;
+	/* synth CNAME rrset */
+	get_cname_target(rrset, &dtarg, &dtarglen);
+	if(!dtarg)
+		return NULL;
+	newlen = q->qname_len + dtarglen - rrset->rk.dname_len;
+	if(newlen > LDNS_MAX_DOMAINLEN) {
+		msg->rep->flags |= LDNS_RCODE_YXDOMAIN;
+		return msg;
+	}
+	newname = (uint8_t*)regional_alloc(region, newlen);
+	if(!newname)
+		return NULL;
+	/* new name is concatenation of qname front (without DNAME owner)
+	 * and DNAME target name */
+	memcpy(newname, q->qname, q->qname_len-rrset->rk.dname_len);
+	memmove(newname+(q->qname_len-rrset->rk.dname_len), dtarg, dtarglen);
+	/* create rest of CNAME rrset */
+	ck = (struct ub_packed_rrset_key*)regional_alloc(region, 
+		sizeof(struct ub_packed_rrset_key));
+	if(!ck)
+		return NULL;
+	memset(&ck->entry, 0, sizeof(ck->entry));
+	msg->rep->rrsets[1] = ck;
+	ck->entry.key = ck;
+	ck->rk.type = htons(LDNS_RR_TYPE_CNAME);
+	ck->rk.rrset_class = rrset->rk.rrset_class;
+	ck->rk.flags = 0;
+	ck->rk.dname = regional_alloc_init(region, q->qname, q->qname_len);
+	if(!ck->rk.dname)
+		return NULL;
+	ck->rk.dname_len = q->qname_len;
+	ck->entry.hash = rrset_key_hash(&ck->rk);
+	newd = (struct packed_rrset_data*)regional_alloc_zero(region,
+		sizeof(struct packed_rrset_data) + sizeof(size_t) + 
+		sizeof(uint8_t*) + sizeof(uint32_t) + sizeof(uint16_t) 
+		+ newlen);
+	if(!newd)
+		return NULL;
+	ck->entry.data = newd;
+	newd->ttl = 0; /* 0 for synthesized CNAME TTL */
+	newd->count = 1;
+	newd->rrsig_count = 0;
+	newd->trust = rrset_trust_ans_noAA;
+	newd->rr_len = (size_t*)((uint8_t*)newd + 
+		sizeof(struct packed_rrset_data));
+	newd->rr_len[0] = newlen + sizeof(uint16_t);
+	packed_rrset_ptr_fixup(newd);
+	newd->rr_ttl[0] = newd->ttl;
+	msg->rep->ttl = newd->ttl;
+	msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(newd->ttl);
+	ldns_write_uint16(newd->rr_data[0], newlen);
+	memmove(newd->rr_data[0] + sizeof(uint16_t), newname, newlen);
+	msg->rep->an_numrrsets ++;
+	msg->rep->rrset_count ++;
+	return msg;
+}
+
+struct dns_msg* 
+dns_cache_lookup(struct module_env* env,
+	uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
+	struct regional* region, struct regional* scratch)
+{
+	struct lruhash_entry* e;
+	struct query_info k;
+	hashvalue_t h;
+	uint32_t now = *env->now;
+	struct ub_packed_rrset_key* rrset;
+
+	/* lookup first, this has both NXdomains and ANSWER responses */
+	k.qname = qname;
+	k.qname_len = qnamelen;
+	k.qtype = qtype;
+	k.qclass = qclass;
+	h = query_info_hash(&k);
+	e = slabhash_lookup(env->msg_cache, h, &k, 0);
+	if(e) {
+		struct msgreply_entry* key = (struct msgreply_entry*)e->key;
+		struct reply_info* data = (struct reply_info*)e->data;
+		struct dns_msg* msg = tomsg(env, &key->key, data, region, now, 
+			scratch);
+		if(msg) {
+			lock_rw_unlock(&e->lock);
+			return msg;
+		}
+		/* could be msg==NULL; due to TTL or not all rrsets available */
+		lock_rw_unlock(&e->lock);
+	}
+
+	/* see if a DNAME exists. Checked for first, to enforce that DNAMEs
+	 * are more important, the CNAME is resynthesized and thus 
+	 * consistent with the DNAME */
+	if( (rrset=find_closest_of_type(env, qname, qnamelen, qclass, now,
+		LDNS_RR_TYPE_DNAME, 1))) {
+		/* synthesize a DNAME+CNAME message based on this */
+		struct dns_msg* msg = synth_dname_msg(rrset, region, now, &k);
+		if(msg) {
+			lock_rw_unlock(&rrset->entry.lock);
+			return msg;
+		}
+		lock_rw_unlock(&rrset->entry.lock);
+	}
+
+	/* see if we have CNAME for this domain,
+	 * but not for DS records (which are part of the parent) */
+	if( qtype != LDNS_RR_TYPE_DS &&
+	   (rrset=rrset_cache_lookup(env->rrset_cache, qname, qnamelen, 
+		LDNS_RR_TYPE_CNAME, qclass, 0, now, 0))) {
+		struct dns_msg* msg = rrset_msg(rrset, region, now, &k);
+		if(msg) {
+			lock_rw_unlock(&rrset->entry.lock);
+			return msg;
+		}
+		lock_rw_unlock(&rrset->entry.lock);
+	}
+
+	/* construct DS, DNSKEY, DLV messages from rrset cache. */
+	if((qtype == LDNS_RR_TYPE_DS || qtype == LDNS_RR_TYPE_DNSKEY ||
+		qtype == LDNS_RR_TYPE_DLV) &&
+		(rrset=rrset_cache_lookup(env->rrset_cache, qname, qnamelen, 
+		qtype, qclass, 0, now, 0))) {
+		/* if the rrset is from the additional section, and the
+		 * signatures have fallen off, then do not synthesize a msg
+		 * instead, allow a full query for signed results to happen.
+		 * Forego all rrset data from additional section, because
+		 * some signatures may not be present and cause validation
+		 * failure.
+		 */
+		struct packed_rrset_data *d = (struct packed_rrset_data*)
+			rrset->entry.data;
+		if(d->trust != rrset_trust_add_noAA && 
+			d->trust != rrset_trust_add_AA && 
+			(qtype == LDNS_RR_TYPE_DS || 
+				(d->trust != rrset_trust_auth_noAA 
+				&& d->trust != rrset_trust_auth_AA) )) {
+			struct dns_msg* msg = rrset_msg(rrset, region, now, &k);
+			if(msg) {
+				lock_rw_unlock(&rrset->entry.lock);
+				return msg;
+			}
+		}
+		lock_rw_unlock(&rrset->entry.lock);
+	}
+
+	/* stop downwards cache search on NXDOMAIN.
+	 * Empty nonterminals are NOERROR, so an NXDOMAIN for foo
+	 * means bla.foo also does not exist.  The DNSSEC proofs are
+	 * the same.  We search upwards for NXDOMAINs. */
+	if(env->cfg->harden_below_nxdomain)
+	    while(!dname_is_root(k.qname)) {
+		dname_remove_label(&k.qname, &k.qname_len);
+		h = query_info_hash(&k);
+		e = slabhash_lookup(env->msg_cache, h, &k, 0);
+		if(e) {
+			struct reply_info* data = (struct reply_info*)e->data;
+			struct dns_msg* msg;
+			if(FLAGS_GET_RCODE(data->flags) == LDNS_RCODE_NXDOMAIN
+			  && data->security == sec_status_secure
+			  && (msg=tomsg(env, &k, data, region, now, scratch))){
+				lock_rw_unlock(&e->lock);
+				msg->qinfo.qname=qname;
+				msg->qinfo.qname_len=qnamelen;
+				/* check that DNSSEC really works out */
+				msg->rep->security = sec_status_unchecked;
+				return msg;
+			}
+			lock_rw_unlock(&e->lock);
+		}
+	}
+
+	return NULL;
+}
+
+int 
+dns_cache_store(struct module_env* env, struct query_info* msgqinf,
+        struct reply_info* msgrep, int is_referral, uint32_t leeway,
+	struct regional* region)
+{
+	struct reply_info* rep = NULL;
+	/* alloc, malloc properly (not in region, like msg is) */
+	rep = reply_info_copy(msgrep, env->alloc, NULL);
+	if(!rep)
+		return 0;
+	/* ttl must be relative ;i.e. 0..86400 not  time(0)+86400. 
+	 * the env->now is added to message and RRsets in this routine. */
+	/* the leeway is used to invalidate other rrsets earlier */
+
+	if(is_referral) {
+		/* store rrsets */
+		struct rrset_ref ref;
+		size_t i;
+		for(i=0; i<rep->rrset_count; i++) {
+			packed_rrset_ttl_add((struct packed_rrset_data*)
+				rep->rrsets[i]->entry.data, *env->now);
+			ref.key = rep->rrsets[i];
+			ref.id = rep->rrsets[i]->id;
+			/*ignore ret: it was in the cache, ref updated */
+			(void)rrset_cache_update(env->rrset_cache, &ref, 
+				env->alloc, *env->now + leeway);
+		}
+		free(rep);
+		return 1;
+	} else {
+		/* store msg, and rrsets */
+		struct query_info qinf;
+		hashvalue_t h;
+
+		qinf = *msgqinf;
+		qinf.qname = memdup(msgqinf->qname, msgqinf->qname_len);
+		if(!qinf.qname) {
+			reply_info_parsedelete(rep, env->alloc);
+			return 0;
+		}
+		/* fixup flags to be sensible for a reply based on the cache */
+		/* this module means that RA is available. It is an answer QR. 
+		 * Not AA from cache. Not CD in cache (depends on client bit). */
+		rep->flags |= (BIT_RA | BIT_QR);
+		rep->flags &= ~(BIT_AA | BIT_CD);
+		h = query_info_hash(&qinf);
+		dns_cache_store_msg(env, &qinf, h, rep, leeway, msgrep, region);
+		/* qname is used inside query_info_entrysetup, and set to 
+		 * NULL. If it has not been used, free it. free(0) is safe. */
+		free(qinf.qname);
+	}
+	return 1;
+}
diff --git a/3rdParty/Unbound/src/src/services/cache/dns.h b/3rdParty/Unbound/src/src/services/cache/dns.h
new file mode 100644
index 0000000..76705ab
--- /dev/null
+++ b/3rdParty/Unbound/src/src/services/cache/dns.h
@@ -0,0 +1,176 @@
+/*
+ * services/cache/dns.h - Cache services for DNS using msg and rrset caches.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains the DNS cache.
+ */
+
+#ifndef SERVICES_CACHE_DNS_H
+#define SERVICES_CACHE_DNS_H
+#include "util/storage/lruhash.h"
+#include "util/data/msgreply.h"
+struct module_env;
+struct query_info;
+struct reply_info;
+struct regional;
+struct delegpt;
+
+/**
+ * Region allocated message reply
+ */
+struct dns_msg {
+	/** query info */
+	struct query_info qinfo;
+	/** reply info - ptr to packed repinfo structure */
+	struct reply_info *rep;
+};
+
+/**
+ * Allocate a dns_msg with malloc/alloc structure and store in dns cache.
+ *
+ * @param env: environment, with alloc structure and dns cache.
+ * @param qinf: query info, the query for which answer is stored.
+ * 	this is allocated in a region, and will be copied to malloc area
+ * 	before insertion.
+ * @param rep: reply in dns_msg from dns_alloc_msg for example.
+ * 	this is allocated in a region, and will be copied to malloc area
+ * 	before insertion.
+ * @param is_referral: If true, then the given message to be stored is a
+ *      referral. The cache implementation may use this as a hint.
+ *      It will store only the RRsets, not the message.
+ * @param leeway: TTL value, if not 0, other rrsets are considered expired
+ *	that many seconds before actual TTL expiry.
+ * @param region: region to allocate better entries from cache into.
+ *   (used when is_referral is false).
+ * @return 0 on alloc error (out of memory).
+ */
+int dns_cache_store(struct module_env* env, struct query_info* qinf,
+        struct reply_info* rep, int is_referral, uint32_t leeway,
+	struct regional* region); 
+
+/**
+ * Store message in the cache. Stores in message cache and rrset cache.
+ * Both qinfo and rep should be malloced and are put in the cache.
+ * They should not be used after this call, as they are then in shared cache.
+ * Does not return errors, they are logged and only lead to less cache.
+ *
+ * @param env: module environment with the DNS cache.
+ * @param qinfo: query info
+ * @param hash: hash over qinfo.
+ * @param rep: reply info, together with qinfo makes up the message.
+ *	Adjusts the reply info TTLs to absolute time.
+ * @param leeway: TTL value, if not 0, other rrsets are considered expired
+ *	that many seconds before actual TTL expiry.
+ * @param qrep: message that can be altered with better rrs from cache.
+ * @param region: to allocate into for qmsg.
+ */
+void dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
+	hashvalue_t hash, struct reply_info* rep, uint32_t leeway,
+	struct reply_info* qrep, struct regional* region);
+
+/**
+ * Find a delegation from the cache.
+ * @param env: module environment with the DNS cache.
+ * @param qname: query name.
+ * @param qnamelen: length of qname.
+ * @param qtype: query type.
+ * @param qclass: query class.
+ * @param region: where to allocate result delegation.
+ * @param msg: if not NULL, delegation message is returned here, synthesized
+ *	from the cache.
+ * @param timenow: the time now, for checking if TTL on cache entries is OK.
+ * @return new delegation or NULL on error or if not found in cache.
+ */
+struct delegpt* dns_cache_find_delegation(struct module_env* env, 
+	uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, 
+	struct regional* region, struct dns_msg** msg, uint32_t timenow);
+
+/** 
+ * Find cached message 
+ * @param env: module environment with the DNS cache.
+ * @param qname: query name.
+ * @param qnamelen: length of qname.
+ * @param qtype: query type.
+ * @param qclass: query class.
+ * @param region: where to allocate result.
+ * @param scratch: where to allocate temporary data.
+ * @return new response message (alloced in region, rrsets do not have IDs).
+ * 	or NULL on error or if not found in cache.
+ *	TTLs are made relative to the current time.
+ */
+struct dns_msg* dns_cache_lookup(struct module_env* env,
+	uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
+	struct regional* region, struct regional* scratch);
+
+/** 
+ * find and add A and AAAA records for missing nameservers in delegpt 
+ * @param env: module environment with rrset cache
+ * @param qclass: which class to look in.
+ * @param region: where to store new dp info.
+ * @param dp: delegation point to fill missing entries.
+ * @return false on alloc failure.
+ */
+int cache_fill_missing(struct module_env* env, uint16_t qclass, 
+	struct regional* region, struct delegpt* dp);
+
+/**
+ * Utility, create new, unpacked data structure for cache response.
+ * QR bit set, no AA. Query set as indicated. Space for number of rrsets.
+ * @param qname: query section name
+ * @param qnamelen: len of qname
+ * @param qtype: query section type
+ * @param qclass: query section class
+ * @param region: where to alloc.
+ * @param capacity: number of rrsets space to create in the array.
+ * @return new dns_msg struct or NULL on mem fail.
+ */
+struct dns_msg* dns_msg_create(uint8_t* qname, size_t qnamelen, uint16_t qtype, 
+	uint16_t qclass, struct regional* region, size_t capacity);
+
+/**
+ * Add rrset to authority section in unpacked dns_msg message. Must have enough
+ * space left, does not grow the array.
+ * @param msg: msg to put it in.
+ * @param region: region to alloc in
+ * @param rrset: to add in authority section
+ * @param now: now.
+ * @return true if worked, false on fail
+ */
+int dns_msg_authadd(struct dns_msg* msg, struct regional* region, 
+	struct ub_packed_rrset_key* rrset, uint32_t now);
+
+#endif /* SERVICES_CACHE_DNS_H */
diff --git a/3rdParty/Unbound/src/src/services/cache/infra.c b/3rdParty/Unbound/src/src/services/cache/infra.c
new file mode 100644
index 0000000..8a83528
--- /dev/null
+++ b/3rdParty/Unbound/src/src/services/cache/infra.c
@@ -0,0 +1,516 @@
+/*
+ * services/cache/infra.c - infrastructure cache, server rtt and capabilities
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains the infrastructure cache.
+ */
+#include "config.h"
+#include <ldns/rr.h>
+#include "services/cache/infra.h"
+#include "util/storage/slabhash.h"
+#include "util/storage/lookup3.h"
+#include "util/data/dname.h"
+#include "util/log.h"
+#include "util/net_help.h"
+#include "util/config_file.h"
+#include "iterator/iterator.h"
+
+/** Timeout when only a single probe query per IP is allowed. */
+#define PROBE_MAXRTO 12000 /* in msec */
+
+size_t 
+infra_sizefunc(void* k, void* ATTR_UNUSED(d))
+{
+	struct infra_key* key = (struct infra_key*)k;
+	return sizeof(*key) + sizeof(struct infra_data) + key->namelen
+		+ lock_get_mem(&key->entry.lock);
+}
+
+int 
+infra_compfunc(void* key1, void* key2)
+{
+	struct infra_key* k1 = (struct infra_key*)key1;
+	struct infra_key* k2 = (struct infra_key*)key2;
+	int r = sockaddr_cmp(&k1->addr, k1->addrlen, &k2->addr, k2->addrlen);
+	if(r != 0)
+		return r;
+	if(k1->namelen != k2->namelen) {
+		if(k1->namelen < k2->namelen)
+			return -1;
+		return 1;
+	}
+	return query_dname_compare(k1->zonename, k2->zonename);
+}
+
+void 
+infra_delkeyfunc(void* k, void* ATTR_UNUSED(arg))
+{
+	struct infra_key* key = (struct infra_key*)k;
+	if(!key)
+		return;
+	lock_rw_destroy(&key->entry.lock);
+	free(key->zonename);
+	free(key);
+}
+
+void 
+infra_deldatafunc(void* d, void* ATTR_UNUSED(arg))
+{
+	struct infra_data* data = (struct infra_data*)d;
+	free(data);
+}
+
+struct infra_cache* 
+infra_create(struct config_file* cfg)
+{
+	struct infra_cache* infra = (struct infra_cache*)calloc(1, 
+		sizeof(struct infra_cache));
+	size_t maxmem = cfg->infra_cache_numhosts * (sizeof(struct infra_key)+
+		sizeof(struct infra_data)+INFRA_BYTES_NAME);
+	infra->hosts = slabhash_create(cfg->infra_cache_slabs,
+		INFRA_HOST_STARTSIZE, maxmem, &infra_sizefunc, &infra_compfunc,
+		&infra_delkeyfunc, &infra_deldatafunc, NULL);
+	if(!infra->hosts) {
+		free(infra);
+		return NULL;
+	}
+	infra->host_ttl = cfg->host_ttl;
+	return infra;
+}
+
+void 
+infra_delete(struct infra_cache* infra)
+{
+	if(!infra)
+		return;
+	slabhash_delete(infra->hosts);
+	free(infra);
+}
+
+struct infra_cache* 
+infra_adjust(struct infra_cache* infra, struct config_file* cfg)
+{
+	size_t maxmem;
+	if(!infra)
+		return infra_create(cfg);
+	infra->host_ttl = cfg->host_ttl;
+	maxmem = cfg->infra_cache_numhosts * (sizeof(struct infra_key)+
+		sizeof(struct infra_data)+INFRA_BYTES_NAME);
+	if(maxmem != slabhash_get_size(infra->hosts) ||
+		cfg->infra_cache_slabs != infra->hosts->size) {
+		infra_delete(infra);
+		infra = infra_create(cfg);
+	}
+	return infra;
+}
+
+/** calculate the hash value for a host key */
+static hashvalue_t
+hash_addr(struct sockaddr_storage* addr, socklen_t addrlen)
+{
+	hashvalue_t h = 0xab;
+	/* select the pieces to hash, some OS have changing data inside */
+	if(addr_is_ip6(addr, addrlen)) {
+		struct sockaddr_in6* in6 = (struct sockaddr_in6*)addr;
+		h = hashlittle(&in6->sin6_family, sizeof(in6->sin6_family), h);
+		h = hashlittle(&in6->sin6_port, sizeof(in6->sin6_port), h);
+		h = hashlittle(&in6->sin6_addr, INET6_SIZE, h);
+	} else {
+		struct sockaddr_in* in = (struct sockaddr_in*)addr;
+		h = hashlittle(&in->sin_family, sizeof(in->sin_family), h);
+		h = hashlittle(&in->sin_port, sizeof(in->sin_port), h);
+		h = hashlittle(&in->sin_addr, INET_SIZE, h);
+	}
+	return h;
+}
+
+/** calculate infra hash for a key */
+static hashvalue_t
+hash_infra(struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* name)
+{
+	return dname_query_hash(name, hash_addr(addr, addrlen));
+}
+
+/** lookup version that does not check host ttl (you check it) */
+struct lruhash_entry* 
+infra_lookup_nottl(struct infra_cache* infra, struct sockaddr_storage* addr,
+	socklen_t addrlen, uint8_t* name, size_t namelen, int wr)
+{
+	struct infra_key k;
+	k.addrlen = addrlen;
+	memcpy(&k.addr, addr, addrlen);
+	k.namelen = namelen;
+	k.zonename = name;
+	k.entry.hash = hash_infra(addr, addrlen, name);
+	k.entry.key = (void*)&k;
+	k.entry.data = NULL;
+	return slabhash_lookup(infra->hosts, k.entry.hash, &k, wr);
+}
+
+/** init the data elements */
+static void
+data_entry_init(struct infra_cache* infra, struct lruhash_entry* e, 
+	uint32_t timenow)
+{
+	struct infra_data* data = (struct infra_data*)e->data;
+	data->ttl = timenow + infra->host_ttl;
+	rtt_init(&data->rtt);
+	data->edns_version = 0;
+	data->edns_lame_known = 0;
+	data->probedelay = 0;
+	data->isdnsseclame = 0;
+	data->rec_lame = 0;
+	data->lame_type_A = 0;
+	data->lame_other = 0;
+}
+
+/** 
+ * Create and init a new entry for a host 
+ * @param infra: infra structure with config parameters.
+ * @param addr: host address.
+ * @param addrlen: length of addr.
+ * @param name: name of zone
+ * @param namelen: length of name.
+ * @param tm: time now.
+ * @return: the new entry or NULL on malloc failure.
+ */
+static struct lruhash_entry*
+new_entry(struct infra_cache* infra, struct sockaddr_storage* addr, 
+	socklen_t addrlen, uint8_t* name, size_t namelen, uint32_t tm)
+{
+	struct infra_data* data;
+	struct infra_key* key = (struct infra_key*)malloc(sizeof(*key));
+	if(!key)
+		return NULL;
+	data = (struct infra_data*)malloc(sizeof(struct infra_data));
+	if(!data) {
+		free(key);
+		return NULL;
+	}
+	key->zonename = memdup(name, namelen);
+	if(!key->zonename) {
+		free(key);
+		free(data);
+		return NULL;
+	}
+	key->namelen = namelen;
+	lock_rw_init(&key->entry.lock);
+	key->entry.hash = hash_infra(addr, addrlen, name);
+	key->entry.key = (void*)key;
+	key->entry.data = (void*)data;
+	key->addrlen = addrlen;
+	memcpy(&key->addr, addr, addrlen);
+	data_entry_init(infra, &key->entry, tm);
+	return &key->entry;
+}
+
+int 
+infra_host(struct infra_cache* infra, struct sockaddr_storage* addr,
+        socklen_t addrlen, uint8_t* nm, size_t nmlen, uint32_t timenow,
+	int* edns_vs, uint8_t* edns_lame_known, int* to)
+{
+	struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen,
+		nm, nmlen, 0);
+	struct infra_data* data;
+	int wr = 0;
+	if(e && ((struct infra_data*)e->data)->ttl < timenow) {
+		/* it expired, try to reuse existing entry */
+		int old = ((struct infra_data*)e->data)->rtt.rto;
+		lock_rw_unlock(&e->lock);
+		e = infra_lookup_nottl(infra, addr, addrlen, nm, nmlen, 1);
+		if(e) {
+			/* if its still there we have a writelock, init */
+			/* re-initialise */
+			/* do not touch lameness, it may be valid still */
+			data_entry_init(infra, e, timenow);
+			wr = 1;
+			/* TOP_TIMEOUT remains on reuse */
+			if(old >= USEFUL_SERVER_TOP_TIMEOUT)
+				((struct infra_data*)e->data)->rtt.rto
+					= USEFUL_SERVER_TOP_TIMEOUT;
+		}
+	}
+	if(!e) {
+		/* insert new entry */
+		if(!(e = new_entry(infra, addr, addrlen, nm, nmlen, timenow)))
+			return 0;
+		data = (struct infra_data*)e->data;
+		*edns_vs = data->edns_version;
+		*edns_lame_known = data->edns_lame_known;
+		*to = rtt_timeout(&data->rtt);
+		slabhash_insert(infra->hosts, e->hash, e, data, NULL);
+		return 1;
+	}
+	/* use existing entry */
+	data = (struct infra_data*)e->data;
+	*edns_vs = data->edns_version;
+	*edns_lame_known = data->edns_lame_known;
+	*to = rtt_timeout(&data->rtt);
+	if(*to >= PROBE_MAXRTO && rtt_notimeout(&data->rtt)*4 <= *to) {
+		/* delay other queries, this is the probe query */
+		if(!wr) {
+			lock_rw_unlock(&e->lock);
+			e = infra_lookup_nottl(infra, addr,addrlen,nm,nmlen, 1);
+			if(!e) { /* flushed from cache real fast, no use to
+				allocate just for the probedelay */
+				return 1;
+			}
+			data = (struct infra_data*)e->data;
+		}
+		/* add 999 to round up the timeout value from msec to sec,
+		 * then add a whole second so it is certain that this probe
+		 * has timed out before the next is allowed */
+		data->probedelay = timenow + ((*to)+1999)/1000;
+	}
+	lock_rw_unlock(&e->lock);
+	return 1;
+}
+
+int 
+infra_set_lame(struct infra_cache* infra, struct sockaddr_storage* addr,
+	socklen_t addrlen, uint8_t* nm, size_t nmlen, uint32_t timenow,
+	int dnsseclame, int reclame, uint16_t qtype)
+{
+	struct infra_data* data;
+	struct lruhash_entry* e;
+	int needtoinsert = 0;
+	e = infra_lookup_nottl(infra, addr, addrlen, nm, nmlen, 1);
+	if(!e) {
+		/* insert it */
+		if(!(e = new_entry(infra, addr, addrlen, nm, nmlen, timenow))) {
+			log_err("set_lame: malloc failure");
+			return 0;
+		}
+		needtoinsert = 1;
+	} else if( ((struct infra_data*)e->data)->ttl < timenow) {
+		/* expired, reuse existing entry */
+		data_entry_init(infra, e, timenow);
+	}
+	/* got an entry, now set the zone lame */
+	data = (struct infra_data*)e->data;
+	/* merge data (if any) */
+	if(dnsseclame)
+		data->isdnsseclame = 1;
+	if(reclame)
+		data->rec_lame = 1;
+	if(!dnsseclame && !reclame && qtype == LDNS_RR_TYPE_A)
+		data->lame_type_A = 1;
+	if(!dnsseclame  && !reclame && qtype != LDNS_RR_TYPE_A)
+		data->lame_other = 1;
+	/* done */
+	if(needtoinsert)
+		slabhash_insert(infra->hosts, e->hash, e, e->data, NULL);
+	else 	{ lock_rw_unlock(&e->lock); }
+	return 1;
+}
+
+void 
+infra_update_tcp_works(struct infra_cache* infra,
+        struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* nm,
+	size_t nmlen)
+{
+	struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen,
+		nm, nmlen, 1);
+	struct infra_data* data;
+	if(!e)
+		return; /* doesn't exist */
+	data = (struct infra_data*)e->data;
+	if(data->rtt.rto >= RTT_MAX_TIMEOUT)
+		/* do not disqualify this server altogether, it is better
+		 * than nothing */
+		data->rtt.rto = RTT_MAX_TIMEOUT-1000;
+	lock_rw_unlock(&e->lock);
+}
+
+int 
+infra_rtt_update(struct infra_cache* infra, struct sockaddr_storage* addr,
+	socklen_t addrlen, uint8_t* nm, size_t nmlen, int roundtrip,
+	int orig_rtt, uint32_t timenow)
+{
+	struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen,
+		nm, nmlen, 1);
+	struct infra_data* data;
+	int needtoinsert = 0;
+	int rto = 1;
+	if(!e) {
+		if(!(e = new_entry(infra, addr, addrlen, nm, nmlen, timenow)))
+			return 0;
+		needtoinsert = 1;
+	} else if(((struct infra_data*)e->data)->ttl < timenow) {
+		data_entry_init(infra, e, timenow);
+	}
+	/* have an entry, update the rtt */
+	data = (struct infra_data*)e->data;
+	if(roundtrip == -1) {
+		rtt_lost(&data->rtt, orig_rtt);
+	} else {
+		rtt_update(&data->rtt, roundtrip);
+		data->probedelay = 0;
+	}
+	if(data->rtt.rto > 0)
+		rto = data->rtt.rto;
+
+	if(needtoinsert)
+		slabhash_insert(infra->hosts, e->hash, e, e->data, NULL);
+	else 	{ lock_rw_unlock(&e->lock); }
+	return rto;
+}
+
+int infra_get_host_rto(struct infra_cache* infra,
+        struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* nm,
+	size_t nmlen, struct rtt_info* rtt, int* delay, uint32_t timenow)
+{
+	struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen,
+		nm, nmlen, 0);
+	struct infra_data* data;
+	int ttl = -2;
+	if(!e) return -1;
+	data = (struct infra_data*)e->data;
+	if(data->ttl >= timenow) {
+		ttl = (int)(data->ttl - timenow);
+		memmove(rtt, &data->rtt, sizeof(*rtt));
+		if(timenow < data->probedelay)
+			*delay = (int)(data->probedelay - timenow);
+		else	*delay = 0;
+	}
+	lock_rw_unlock(&e->lock);
+	return ttl;
+}
+
+int 
+infra_edns_update(struct infra_cache* infra, struct sockaddr_storage* addr,
+	socklen_t addrlen, uint8_t* nm, size_t nmlen, int edns_version,
+	uint32_t timenow)
+{
+	struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen,
+		nm, nmlen, 1);
+	struct infra_data* data;
+	int needtoinsert = 0;
+	if(!e) {
+		if(!(e = new_entry(infra, addr, addrlen, nm, nmlen, timenow)))
+			return 0;
+		needtoinsert = 1;
+	} else if(((struct infra_data*)e->data)->ttl < timenow) {
+		data_entry_init(infra, e, timenow);
+	}
+	/* have an entry, update the rtt, and the ttl */
+	data = (struct infra_data*)e->data;
+	/* do not update if noEDNS and stored is yesEDNS */
+	if(!(edns_version == -1 && (data->edns_version != -1 &&
+		data->edns_lame_known))) {
+		data->edns_version = edns_version;
+		data->edns_lame_known = 1;
+	}
+
+	if(needtoinsert)
+		slabhash_insert(infra->hosts, e->hash, e, e->data, NULL);
+	else 	{ lock_rw_unlock(&e->lock); }
+	return 1;
+}
+
+int
+infra_get_lame_rtt(struct infra_cache* infra,
+        struct sockaddr_storage* addr, socklen_t addrlen,
+        uint8_t* name, size_t namelen, uint16_t qtype, 
+	int* lame, int* dnsseclame, int* reclame, int* rtt, uint32_t timenow)
+{
+	struct infra_data* host;
+	struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen,
+		name, namelen, 0);
+	if(!e) 
+		return 0;
+	host = (struct infra_data*)e->data;
+	*rtt = rtt_unclamped(&host->rtt);
+	if(host->rtt.rto >= PROBE_MAXRTO && timenow < host->probedelay
+		&& rtt_notimeout(&host->rtt)*4 <= host->rtt.rto)
+		/* single probe for this domain, and we are not probing */
+		*rtt = USEFUL_SERVER_TOP_TIMEOUT;
+	if(timenow > host->ttl) {
+		/* expired entry */
+		/* see if this can be a re-probe of an unresponsive server */
+		/* minus 1000 because that is outside of the RTTBAND, so
+		 * blacklisted servers stay blacklisted if this is chosen */
+		if(host->rtt.rto >= USEFUL_SERVER_TOP_TIMEOUT) {
+			*rtt = USEFUL_SERVER_TOP_TIMEOUT-1000;
+			*lame = 0;
+			*dnsseclame = 0;
+			*reclame = 0;
+			lock_rw_unlock(&e->lock);
+			return 1;
+		}
+		lock_rw_unlock(&e->lock);
+		return 0;
+	}
+	/* check lameness first */
+	if(host->lame_type_A && qtype == LDNS_RR_TYPE_A) {
+		lock_rw_unlock(&e->lock);
+		*lame = 1;
+		*dnsseclame = 0;
+		*reclame = 0;
+		return 1;
+	} else if(host->lame_other && qtype != LDNS_RR_TYPE_A) {
+		lock_rw_unlock(&e->lock);
+		*lame = 1;
+		*dnsseclame = 0;
+		*reclame = 0;
+		return 1;
+	} else if(host->isdnsseclame) {
+		lock_rw_unlock(&e->lock);
+		*lame = 0;
+		*dnsseclame = 1;
+		*reclame = 0;
+		return 1;
+	} else if(host->rec_lame) {
+		lock_rw_unlock(&e->lock);
+		*lame = 0;
+		*dnsseclame = 0;
+		*reclame = 1;
+		return 1;
+	}
+	/* no lameness for this type of query */
+	lock_rw_unlock(&e->lock);
+	*lame = 0;
+	*dnsseclame = 0;
+	*reclame = 0;
+	return 1;
+}
+
+size_t 
+infra_get_mem(struct infra_cache* infra)
+{
+	return sizeof(*infra) + slabhash_get_mem(infra->hosts);
+}
diff --git a/3rdParty/Unbound/src/src/services/cache/infra.h b/3rdParty/Unbound/src/src/services/cache/infra.h
new file mode 100644
index 0000000..3a3508e
--- /dev/null
+++ b/3rdParty/Unbound/src/src/services/cache/infra.h
@@ -0,0 +1,297 @@
+/*
+ * services/cache/infra.h - infrastructure cache, server rtt and capabilities
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains the infrastructure cache.
+ */
+
+#ifndef SERVICES_CACHE_INFRA_H
+#define SERVICES_CACHE_INFRA_H
+#include "util/storage/lruhash.h"
+#include "util/rtt.h"
+struct slabhash;
+struct config_file;
+
+/**
+ * Host information kept for every server, per zone.
+ */
+struct infra_key {
+	/** the host address. */
+	struct sockaddr_storage addr;
+	/** length of addr. */
+	socklen_t addrlen;
+	/** zone name in wireformat */
+	uint8_t* zonename;
+	/** length of zonename */
+	size_t namelen;
+	/** hash table entry, data of type infra_data. */
+	struct lruhash_entry entry;
+};
+
+/**
+ * Host information encompasses host capabilities and retransmission timeouts.
+ * And lameness information (notAuthoritative, noEDNS, Recursive)
+ */
+struct infra_data {
+	/** TTL value for this entry. absolute time. */
+	uint32_t ttl;
+
+	/** time in seconds (absolute) when probing re-commences, 0 disabled */
+	uint32_t probedelay;
+	/** round trip times for timeout calculation */
+	struct rtt_info rtt;
+
+	/** edns version that the host supports, -1 means no EDNS */
+	int edns_version;
+	/** if the EDNS lameness is already known or not.
+	 * EDNS lame is when EDNS queries or replies are dropped, 
+	 * and cause a timeout */
+	uint8_t edns_lame_known;
+
+	/** is the host lame (does not serve the zone authoritatively),
+	 * or is the host dnssec lame (does not serve DNSSEC data) */
+	uint8_t isdnsseclame;
+	/** is the host recursion lame (not AA, but RA) */
+	uint8_t rec_lame;
+	/** the host is lame (not authoritative) for A records */
+	uint8_t lame_type_A;
+	/** the host is lame (not authoritative) for other query types */
+	uint8_t lame_other;
+};
+
+/**
+ * Infra cache 
+ */
+struct infra_cache {
+	/** The hash table with hosts */
+	struct slabhash* hosts;
+	/** TTL value for host information, in seconds */
+	int host_ttl;
+};
+
+/** infra host cache default hash lookup size */
+#define INFRA_HOST_STARTSIZE 32
+/** bytes per zonename reserved in the hostcache, dnamelen(zonename.com.) */
+#define INFRA_BYTES_NAME 14
+
+/**
+ * Create infra cache.
+ * @param cfg: config parameters or NULL for defaults.
+ * @return: new infra cache, or NULL.
+ */
+struct infra_cache* infra_create(struct config_file* cfg);
+
+/**
+ * Delete infra cache.
+ * @param infra: infrastructure cache to delete.
+ */
+void infra_delete(struct infra_cache* infra);
+
+/**
+ * Adjust infra cache to use updated configuration settings.
+ * This may clean the cache. Operates a bit like realloc.
+ * There may be no threading or use by other threads.
+ * @param infra: existing cache. If NULL a new infra cache is returned.
+ * @param cfg: config options.
+ * @return the new infra cache pointer or NULL on error.
+ */
+struct infra_cache* infra_adjust(struct infra_cache* infra, 
+	struct config_file* cfg);
+
+/**
+ * Plain find infra data function (used by the the other functions)
+ * @param infra: infrastructure cache.
+ * @param addr: host address.
+ * @param addrlen: length of addr.
+ * @param name: domain name of zone.
+ * @param namelen: length of domain name.
+ * @param wr: if true, writelock, else readlock.
+ * @return the entry, could be expired (this is not checked) or NULL.
+ */
+struct lruhash_entry* infra_lookup_nottl(struct infra_cache* infra,
+	struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* name,
+	size_t namelen, int wr);
+
+/**
+ * Find host information to send a packet. Creates new entry if not found.
+ * Lameness is empty. EDNS is 0 (try with first), and rtt is returned for 
+ * the first message to it.
+ * Use this to send a packet only, because it also locks out others when
+ * probing is restricted.
+ * @param infra: infrastructure cache.
+ * @param addr: host address.
+ * @param addrlen: length of addr.
+ * @param name: domain name of zone.
+ * @param namelen: length of domain name.
+ * @param timenow: what time it is now.
+ * @param edns_vs: edns version it supports, is returned.
+ * @param edns_lame_known: if EDNS lame (EDNS is dropped in transit) has
+ * 	already been probed, is returned.
+ * @param to: timeout to use, is returned.
+ * @return: 0 on error.
+ */
+int infra_host(struct infra_cache* infra, struct sockaddr_storage* addr, 
+	socklen_t addrlen, uint8_t* name, size_t namelen,
+	uint32_t timenow, int* edns_vs, uint8_t* edns_lame_known, int* to);
+
+/**
+ * Set a host to be lame for the given zone.
+ * @param infra: infrastructure cache.
+ * @param addr: host address.
+ * @param addrlen: length of addr.
+ * @param name: domain name of zone apex.
+ * @param namelen: length of domain name.
+ * @param timenow: what time it is now.
+ * @param dnsseclame: if true the host is set dnssec lame.
+ *	if false, the host is marked lame (not serving the zone).
+ * @param reclame: if true host is a recursor not AA server.
+ *      if false, dnsseclame or marked lame.
+ * @param qtype: the query type for which it is lame.
+ * @return: 0 on error.
+ */
+int infra_set_lame(struct infra_cache* infra,
+        struct sockaddr_storage* addr, socklen_t addrlen,
+	uint8_t* name, size_t namelen, uint32_t timenow, int dnsseclame,
+	int reclame, uint16_t qtype);
+
+/**
+ * Update rtt information for the host.
+ * @param infra: infrastructure cache.
+ * @param addr: host address.
+ * @param addrlen: length of addr.
+ * @param name: zone name
+ * @param namelen: zone name length
+ * @param roundtrip: estimate of roundtrip time in milliseconds or -1 for 
+ * 	timeout.
+ * @param orig_rtt: original rtt for the query that timed out (roundtrip==-1).
+ * 	ignored if roundtrip != -1.
+ * @param timenow: what time it is now.
+ * @return: 0 on error. new rto otherwise.
+ */
+int infra_rtt_update(struct infra_cache* infra, struct sockaddr_storage* addr,
+	socklen_t addrlen, uint8_t* name, size_t namelen,
+	int roundtrip, int orig_rtt, uint32_t timenow);
+
+/**
+ * Update information for the host, store that a TCP transaction works.
+ * @param infra: infrastructure cache.
+ * @param addr: host address.
+ * @param addrlen: length of addr.
+ * @param name: name of zone
+ * @param namelen: length of name
+ */
+void infra_update_tcp_works(struct infra_cache* infra,
+        struct sockaddr_storage* addr, socklen_t addrlen,
+	uint8_t* name, size_t namelen);
+
+/**
+ * Update edns information for the host.
+ * @param infra: infrastructure cache.
+ * @param addr: host address.
+ * @param addrlen: length of addr.
+ * @param name: name of zone
+ * @param namelen: length of name
+ * @param edns_version: the version that it publishes.
+ * 	If it is known to support EDNS then no-EDNS is not stored over it.
+ * @param timenow: what time it is now.
+ * @return: 0 on error.
+ */
+int infra_edns_update(struct infra_cache* infra,
+        struct sockaddr_storage* addr, socklen_t addrlen,
+	uint8_t* name, size_t namelen, int edns_version, uint32_t timenow);
+
+/**
+ * Get Lameness information and average RTT if host is in the cache.
+ * This information is to be used for server selection.
+ * @param infra: infrastructure cache.
+ * @param addr: host address.
+ * @param addrlen: length of addr.
+ * @param name: zone name.
+ * @param namelen: zone name length.
+ * @param qtype: the query to be made.
+ * @param lame: if function returns true, this returns lameness of the zone.
+ * @param dnsseclame: if function returns true, this returns if the zone
+ *	is dnssec-lame.
+ * @param reclame: if function returns true, this is if it is recursion lame.
+ * @param rtt: if function returns true, this returns avg rtt of the server.
+ * 	The rtt value is unclamped and reflects recent timeouts.
+ * @param timenow: what time it is now.
+ * @return if found in cache, or false if not (or TTL bad).
+ */
+int infra_get_lame_rtt(struct infra_cache* infra,
+        struct sockaddr_storage* addr, socklen_t addrlen, 
+	uint8_t* name, size_t namelen, uint16_t qtype, 
+	int* lame, int* dnsseclame, int* reclame, int* rtt, uint32_t timenow);
+
+/**
+ * Get additional (debug) info on timing.
+ * @param infra: infra cache.
+ * @param addr: host address.
+ * @param addrlen: length of addr.
+ * @param name: zone name
+ * @param namelen: zone name length
+ * @param rtt: the rtt_info is copied into here (caller alloced return struct).
+ * @param delay: probe delay (if any).
+ * @param timenow: what time it is now.
+ * @return TTL the infra host element is valid for. If -1: not found in cache.
+ *	TTL -2: found but expired.
+ */
+int infra_get_host_rto(struct infra_cache* infra,
+        struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* name,
+	size_t namelen, struct rtt_info* rtt, int* delay, uint32_t timenow);
+
+/**
+ * Get memory used by the infra cache.
+ * @param infra: infrastructure cache.
+ * @return memory in use in bytes.
+ */
+size_t infra_get_mem(struct infra_cache* infra);
+
+/** calculate size for the hashtable, does not count size of lameness,
+ * so the hashtable is a fixed number of items */
+size_t infra_sizefunc(void* k, void* d);
+
+/** compare two addresses, returns -1, 0, or +1 */
+int infra_compfunc(void* key1, void* key2);
+
+/** delete key, and destroy the lock */
+void infra_delkeyfunc(void* k, void* arg);
+
+/** delete data and destroy the lameness hashtable */
+void infra_deldatafunc(void* d, void* arg);
+
+#endif /* SERVICES_CACHE_INFRA_H */
diff --git a/3rdParty/Unbound/src/src/services/cache/rrset.c b/3rdParty/Unbound/src/src/services/cache/rrset.c
new file mode 100644
index 0000000..b9d20db
--- /dev/null
+++ b/3rdParty/Unbound/src/src/services/cache/rrset.c
@@ -0,0 +1,416 @@
+/*
+ * services/cache/rrset.c - Resource record set cache.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains the rrset cache.
+ */
+#include "config.h"
+#include "services/cache/rrset.h"
+#include "util/storage/slabhash.h"
+#include "util/config_file.h"
+#include "util/data/packed_rrset.h"
+#include "util/data/msgreply.h"
+#include "util/regional.h"
+#include "util/alloc.h"
+
+void
+rrset_markdel(void* key)
+{
+	struct ub_packed_rrset_key* r = (struct ub_packed_rrset_key*)key;
+	r->id = 0;
+}
+
+struct rrset_cache* rrset_cache_create(struct config_file* cfg, 
+	struct alloc_cache* alloc)
+{
+	size_t slabs = (cfg?cfg->rrset_cache_slabs:HASH_DEFAULT_SLABS);
+	size_t startarray = HASH_DEFAULT_STARTARRAY;
+	size_t maxmem = (cfg?cfg->rrset_cache_size:HASH_DEFAULT_MAXMEM);
+
+	struct rrset_cache *r = (struct rrset_cache*)slabhash_create(slabs,
+		startarray, maxmem, ub_rrset_sizefunc, ub_rrset_compare,
+		ub_rrset_key_delete, rrset_data_delete, alloc);
+	slabhash_setmarkdel(&r->table, &rrset_markdel);
+	return r;
+}
+
+void rrset_cache_delete(struct rrset_cache* r)
+{
+	if(!r) 
+		return;
+	slabhash_delete(&r->table);
+	/* slabhash delete also does free(r), since table is first in struct*/
+}
+
+struct rrset_cache* rrset_cache_adjust(struct rrset_cache *r, 
+	struct config_file* cfg, struct alloc_cache* alloc)
+{
+	if(!r || !cfg || cfg->rrset_cache_slabs != r->table.size ||
+		cfg->rrset_cache_size != slabhash_get_size(&r->table))
+	{
+		rrset_cache_delete(r);
+		r = rrset_cache_create(cfg, alloc);
+	}
+	return r;
+}
+
+void 
+rrset_cache_touch(struct rrset_cache* r, struct ub_packed_rrset_key* key,
+        hashvalue_t hash, rrset_id_t id)
+{
+	struct lruhash* table = slabhash_gettable(&r->table, hash);
+	/* 
+	 * This leads to locking problems, deadlocks, if the caller is 
+	 * holding any other rrset lock.
+	 * Because a lookup through the hashtable does:
+	 *	tablelock -> entrylock  (for that entry caller holds)
+	 * And this would do
+	 *	entrylock(already held) -> tablelock
+	 * And if two threads do this, it results in deadlock.
+	 * So, the caller must not hold entrylock.
+	 */
+	lock_quick_lock(&table->lock);
+	/* we have locked the hash table, the item can still be deleted.
+	 * because it could already have been reclaimed, but not yet set id=0.
+	 * This is because some lruhash routines have lazy deletion.
+	 * so, we must acquire a lock on the item to verify the id != 0.
+	 * also, with hash not changed, we are using the right slab.
+	 */
+	lock_rw_rdlock(&key->entry.lock);
+	if(key->id == id && key->entry.hash == hash) {
+		lru_touch(table, &key->entry);
+	}
+	lock_rw_unlock(&key->entry.lock);
+	lock_quick_unlock(&table->lock);
+}
+
+/** see if rrset needs to be updated in the cache */
+static int
+need_to_update_rrset(void* nd, void* cd, uint32_t timenow, int equal, int ns)
+{
+	struct packed_rrset_data* newd = (struct packed_rrset_data*)nd;
+	struct packed_rrset_data* cached = (struct packed_rrset_data*)cd;
+	/* 	o store if rrset has been validated 
+	 *  		everything better than bogus data 
+	 *  		secure is preferred */
+	if( newd->security == sec_status_secure &&
+		cached->security != sec_status_secure)
+		return 1;
+	if( cached->security == sec_status_bogus && 
+		newd->security != sec_status_bogus && !equal)
+		return 1;
+        /*      o if current RRset is more trustworthy - insert it */
+        if( newd->trust > cached->trust ) {
+		/* if the cached rrset is bogus, and this one equal,
+		 * do not update the TTL - let it expire. */
+		if(equal && cached->ttl >= timenow && 
+			cached->security == sec_status_bogus)
+			return 0;
+                return 1;
+	}
+	/*	o item in cache has expired */
+	if( cached->ttl < timenow )
+		return 1;
+	/*  o same trust, but different in data - insert it */
+	if( newd->trust == cached->trust && !equal ) {
+		/* if this is type NS, do not 'stick' to owner that changes
+		 * the NS RRset, but use the old TTL for the new data, and
+		 * update to fetch the latest data. ttl is not expired, because
+		 * that check was before this one. */
+		if(ns) {
+			size_t i;
+			newd->ttl = cached->ttl;
+			for(i=0; i<(newd->count+newd->rrsig_count); i++)
+				if(newd->rr_ttl[i] > newd->ttl)
+					newd->rr_ttl[i] = newd->ttl;
+		}
+		return 1;
+	}
+	return 0;
+}
+
+/** Update RRSet special key ID */
+static void
+rrset_update_id(struct rrset_ref* ref, struct alloc_cache* alloc)
+{
+	/* this may clear the cache and invalidate lock below */
+	uint64_t newid = alloc_get_id(alloc);
+	/* obtain writelock */
+	lock_rw_wrlock(&ref->key->entry.lock);
+	/* check if it was deleted in the meantime, if so, skip update */
+	if(ref->key->id == ref->id) {
+		ref->key->id = newid;
+		ref->id = newid;
+	}
+	lock_rw_unlock(&ref->key->entry.lock);
+}
+
+int 
+rrset_cache_update(struct rrset_cache* r, struct rrset_ref* ref,
+	struct alloc_cache* alloc, uint32_t timenow)
+{
+	struct lruhash_entry* e;
+	struct ub_packed_rrset_key* k = ref->key;
+	hashvalue_t h = k->entry.hash;
+	uint16_t rrset_type = ntohs(k->rk.type);
+	int equal = 0;
+	log_assert(ref->id != 0 && k->id != 0);
+	/* looks up item with a readlock - no editing! */
+	if((e=slabhash_lookup(&r->table, h, k, 0)) != 0) {
+		/* return id and key as they will be used in the cache
+		 * since the lruhash_insert, if item already exists, deallocs
+		 * the passed key in favor of the already stored key.
+		 * because of the small gap (see below) this key ptr and id
+		 * may prove later to be already deleted, which is no problem
+		 * as it only makes a cache miss. 
+		 */
+		ref->key = (struct ub_packed_rrset_key*)e->key;
+		ref->id = ref->key->id;
+		equal = rrsetdata_equal((struct packed_rrset_data*)k->entry.
+			data, (struct packed_rrset_data*)e->data);
+		if(!need_to_update_rrset(k->entry.data, e->data, timenow,
+			equal, (rrset_type==LDNS_RR_TYPE_NS))) {
+			/* cache is superior, return that value */
+			lock_rw_unlock(&e->lock);
+			ub_packed_rrset_parsedelete(k, alloc);
+			if(equal) return 2;
+			return 1;
+		}
+		lock_rw_unlock(&e->lock);
+		/* Go on and insert the passed item.
+		 * small gap here, where entry is not locked.
+		 * possibly entry is updated with something else.
+		 * we then overwrite that with our data.
+		 * this is just too bad, its cache anyway. */
+		/* use insert to update entry to manage lruhash
+		 * cache size values nicely. */
+	}
+	log_assert(ref->key->id != 0);
+	slabhash_insert(&r->table, h, &k->entry, k->entry.data, alloc);
+	if(e) {
+		/* For NSEC, NSEC3, DNAME, when rdata is updated, update 
+		 * the ID number so that proofs in message cache are 
+		 * invalidated */
+		if((rrset_type == LDNS_RR_TYPE_NSEC 
+			|| rrset_type == LDNS_RR_TYPE_NSEC3
+			|| rrset_type == LDNS_RR_TYPE_DNAME) && !equal) {
+			rrset_update_id(ref, alloc);
+		}
+		return 1;
+	}
+	return 0;
+}
+
+struct ub_packed_rrset_key* 
+rrset_cache_lookup(struct rrset_cache* r, uint8_t* qname, size_t qnamelen, 
+	uint16_t qtype, uint16_t qclass, uint32_t flags, uint32_t timenow,
+	int wr)
+{
+	struct lruhash_entry* e;
+	struct ub_packed_rrset_key key;
+	
+	key.entry.key = &key;
+	key.entry.data = NULL;
+	key.rk.dname = qname;
+	key.rk.dname_len = qnamelen;
+	key.rk.type = htons(qtype);
+	key.rk.rrset_class = htons(qclass);
+	key.rk.flags = flags;
+
+	key.entry.hash = rrset_key_hash(&key.rk);
+
+	if((e = slabhash_lookup(&r->table, key.entry.hash, &key, wr))) {
+		/* check TTL */
+		struct packed_rrset_data* data = 
+			(struct packed_rrset_data*)e->data;
+		if(timenow > data->ttl) {
+			lock_rw_unlock(&e->lock);
+			return NULL;
+		}
+		/* we're done */
+		return (struct ub_packed_rrset_key*)e->key;
+	}
+	return NULL;
+}
+
+int 
+rrset_array_lock(struct rrset_ref* ref, size_t count, uint32_t timenow)
+{
+	size_t i;
+	for(i=0; i<count; i++) {
+		if(i>0 && ref[i].key == ref[i-1].key)
+			continue; /* only lock items once */
+		lock_rw_rdlock(&ref[i].key->entry.lock);
+		if(ref[i].id != ref[i].key->id || timenow >
+			((struct packed_rrset_data*)(ref[i].key->entry.data))
+			->ttl) {
+			/* failure! rollback our readlocks */
+			rrset_array_unlock(ref, i+1);
+			return 0;
+		}
+	}
+	return 1;
+}
+
+void 
+rrset_array_unlock(struct rrset_ref* ref, size_t count)
+{
+	size_t i;
+	for(i=0; i<count; i++) {
+		if(i>0 && ref[i].key == ref[i-1].key)
+			continue; /* only unlock items once */
+		lock_rw_unlock(&ref[i].key->entry.lock);
+	}
+}
+
+void 
+rrset_array_unlock_touch(struct rrset_cache* r, struct regional* scratch,
+	struct rrset_ref* ref, size_t count)
+{
+	hashvalue_t* h;
+	size_t i;
+	if(!(h = (hashvalue_t*)regional_alloc(scratch, 
+		sizeof(hashvalue_t)*count)))
+		log_warn("rrset LRU: memory allocation failed");
+	else 	/* store hash values */
+		for(i=0; i<count; i++)
+			h[i] = ref[i].key->entry.hash;
+	/* unlock */
+	for(i=0; i<count; i++) {
+		if(i>0 && ref[i].key == ref[i-1].key)
+			continue; /* only unlock items once */
+		lock_rw_unlock(&ref[i].key->entry.lock);
+	}
+	if(h) {
+		/* LRU touch, with no rrset locks held */
+		for(i=0; i<count; i++) {
+			if(i>0 && ref[i].key == ref[i-1].key)
+				continue; /* only touch items once */
+			rrset_cache_touch(r, ref[i].key, h[i], ref[i].id);
+		}
+	}
+}
+
+void 
+rrset_update_sec_status(struct rrset_cache* r, 
+	struct ub_packed_rrset_key* rrset, uint32_t now)
+{
+	struct packed_rrset_data* updata = 
+		(struct packed_rrset_data*)rrset->entry.data;
+	struct lruhash_entry* e;
+	struct packed_rrset_data* cachedata;
+
+	/* hash it again to make sure it has a hash */
+	rrset->entry.hash = rrset_key_hash(&rrset->rk);
+
+	e = slabhash_lookup(&r->table, rrset->entry.hash, rrset, 1);
+	if(!e)
+		return; /* not in the cache anymore */
+	cachedata = (struct packed_rrset_data*)e->data;
+	if(!rrsetdata_equal(updata, cachedata)) {
+		lock_rw_unlock(&e->lock);
+		return; /* rrset has changed in the meantime */
+	}
+	/* update the cached rrset */
+	if(updata->security > cachedata->security) {
+		size_t i;
+		if(updata->trust > cachedata->trust)
+			cachedata->trust = updata->trust;
+		cachedata->security = updata->security;
+		/* for NS records only shorter TTLs, other types: update it */
+		if(ntohs(rrset->rk.type) != LDNS_RR_TYPE_NS ||
+			updata->ttl+now < cachedata->ttl ||
+			cachedata->ttl < now ||
+			updata->security == sec_status_bogus) {
+			cachedata->ttl = updata->ttl + now;
+			for(i=0; i<cachedata->count+cachedata->rrsig_count; i++)
+				cachedata->rr_ttl[i] = updata->rr_ttl[i]+now;
+		}
+	}
+	lock_rw_unlock(&e->lock);
+}
+
+void 
+rrset_check_sec_status(struct rrset_cache* r, 
+	struct ub_packed_rrset_key* rrset, uint32_t now)
+{
+	struct packed_rrset_data* updata = 
+		(struct packed_rrset_data*)rrset->entry.data;
+	struct lruhash_entry* e;
+	struct packed_rrset_data* cachedata;
+
+	/* hash it again to make sure it has a hash */
+	rrset->entry.hash = rrset_key_hash(&rrset->rk);
+
+	e = slabhash_lookup(&r->table, rrset->entry.hash, rrset, 0);
+	if(!e)
+		return; /* not in the cache anymore */
+	cachedata = (struct packed_rrset_data*)e->data;
+	if(now > cachedata->ttl || !rrsetdata_equal(updata, cachedata)) {
+		lock_rw_unlock(&e->lock);
+		return; /* expired, or rrset has changed in the meantime */
+	}
+	if(cachedata->security > updata->security) {
+		updata->security = cachedata->security;
+		if(cachedata->security == sec_status_bogus) {
+			size_t i;
+			updata->ttl = cachedata->ttl - now;
+			for(i=0; i<cachedata->count+cachedata->rrsig_count; i++)
+				if(cachedata->rr_ttl[i] < now)
+					updata->rr_ttl[i] = 0;
+				else updata->rr_ttl[i] = 
+					cachedata->rr_ttl[i]-now;
+		}
+		if(cachedata->trust > updata->trust)
+			updata->trust = cachedata->trust;
+	}
+	lock_rw_unlock(&e->lock);
+}
+
+void rrset_cache_remove(struct rrset_cache* r, uint8_t* nm, size_t nmlen,
+	uint16_t type, uint16_t dclass, uint32_t flags)
+{
+	struct ub_packed_rrset_key key;
+	key.entry.key = &key;
+	key.rk.dname = nm;
+	key.rk.dname_len = nmlen;
+	key.rk.rrset_class = htons(dclass);
+	key.rk.type = htons(type);
+	key.rk.flags = flags;
+	key.entry.hash = rrset_key_hash(&key.rk);
+	slabhash_remove(&r->table, key.entry.hash, &key);
+}
diff --git a/3rdParty/Unbound/src/src/services/cache/rrset.h b/3rdParty/Unbound/src/src/services/cache/rrset.h
new file mode 100644
index 0000000..22d3671
--- /dev/null
+++ b/3rdParty/Unbound/src/src/services/cache/rrset.h
@@ -0,0 +1,231 @@
+/*
+ * services/cache/rrset.h - Resource record set cache.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains the rrset cache.
+ */
+
+#ifndef SERVICES_CACHE_RRSET_H
+#define SERVICES_CACHE_RRSET_H
+#include "util/storage/lruhash.h"
+#include "util/storage/slabhash.h"
+#include "util/data/packed_rrset.h"
+struct config_file;
+struct alloc_cache;
+struct rrset_ref;
+struct regional;
+
+/**
+ * The rrset cache
+ * Thin wrapper around hashtable, like a typedef.
+ */
+struct rrset_cache {
+	/** uses partitioned hash table */
+	struct slabhash table;
+};
+
+/**
+ * Create rrset cache
+ * @param cfg: config settings or NULL for defaults.
+ * @param alloc: initial default rrset key allocation.
+ * @return: NULL on error.
+ */
+struct rrset_cache* rrset_cache_create(struct config_file* cfg, 
+	struct alloc_cache* alloc);
+
+/**
+ * Delete rrset cache
+ * @param r: rrset cache to delete.
+ */
+void rrset_cache_delete(struct rrset_cache* r);
+
+/**
+ * Adjust settings of the cache to settings from the config file.
+ * May purge the cache. May recreate the cache.
+ * There may be no threading or use by other threads.
+ * @param r: rrset cache to adjust (like realloc).
+ * @param cfg: config settings or NULL for defaults.
+ * @param alloc: initial default rrset key allocation.
+ * @return 0 on error, or new rrset cache pointer on success.
+ */
+struct rrset_cache* rrset_cache_adjust(struct rrset_cache* r, 
+	struct config_file* cfg, struct alloc_cache* alloc);
+
+/**
+ * Touch rrset, with given pointer and id.
+ * Caller may not hold a lock on ANY rrset, this could give deadlock.
+ *
+ * This routine is faster than a hashtable lookup:
+ *	o no bin_lock is acquired.
+ *	o no walk through the bin-overflow-list. 
+ *	o no comparison of the entry key to find it.
+ *
+ * @param r: rrset cache.
+ * @param key: rrset key. Marked recently used (if it was not deleted
+ *	before the lock is acquired, in that case nothing happens).
+ * @param hash: hash value of the item. Please read it from the key when
+ *	you have it locked. Used to find slab from slabhash.
+ * @param id: used to check that the item is unchanged and not deleted.
+ */
+void rrset_cache_touch(struct rrset_cache* r, struct ub_packed_rrset_key* key,
+	hashvalue_t hash, rrset_id_t id);
+
+/**
+ * Update an rrset in the rrset cache. Stores the information for later use.
+ * Will lookup if the rrset is in the cache and perform an update if necessary.
+ * If the item was present, and superior, references are returned to that.
+ * The passed item is then deallocated with rrset_parsedelete.
+ *
+ * A superior rrset is:
+ *	o rrset with better trust value.
+ *	o same trust value, different rdata, newly passed rrset is inserted.
+ * If rdata is the same, TTL in the cache is updated.
+ *
+ * @param r: the rrset cache.
+ * @param ref: reference (ptr and id) to the rrset. Pass reference setup for
+ *	the new rrset. The reference may be changed if the cached rrset is
+ *	superior.
+ *	Before calling the rrset is presumed newly allocated and changeable.
+ *	Afer calling you do not hold a lock, and the rrset is inserted in
+ *	the hashtable so you need a lock to change it.
+ * @param alloc: how to allocate (and deallocate) the special rrset key.
+ * @param timenow: current time (to see if ttl in cache is expired).
+ * @return: true if the passed reference is updated, false if it is unchanged.
+ * 	0: reference unchanged, inserted in cache.
+ * 	1: reference updated, item is inserted in cache.
+ * 	2: reference updated, item in cache is considered superior.
+ *	   also the rdata is equal (but other parameters in cache are superior).
+ */
+int rrset_cache_update(struct rrset_cache* r, struct rrset_ref* ref, 
+	struct alloc_cache* alloc, uint32_t timenow);
+
+/**
+ * Lookup rrset. You obtain read/write lock. You must unlock before lookup
+ * anything of else.
+ * @param r: the rrset cache.
+ * @param qname: name of rrset to lookup.
+ * @param qnamelen: length of name of rrset to lookup.
+ * @param qtype: type of rrset to lookup (host order).
+ * @param qclass: class of rrset to lookup (host order).
+ * @param flags: rrset flags, or 0.
+ * @param timenow: used to compare with TTL.
+ * @param wr: set true to get writelock.
+ * @return packed rrset key pointer. Remember to unlock the key.entry.lock.
+ * 	or NULL if could not be found or it was timed out.
+ */
+struct ub_packed_rrset_key* rrset_cache_lookup(struct rrset_cache* r,
+	uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
+	uint32_t flags, uint32_t timenow, int wr);
+
+/**
+ * Obtain readlock on a (sorted) list of rrset references.
+ * Checks TTLs and IDs of the rrsets and rollbacks locking if not Ok.
+ * @param ref: array of rrset references (key pointer and ID value).
+ *	duplicate references are allowed and handled.
+ * @param count: size of array.
+ * @param timenow: used to compare with TTL.
+ * @return true on success, false on a failure, which can be that some
+ * 	RRsets have timed out, or that they do not exist any more, the
+ *	RRsets have been purged from the cache.
+ *	If true, you hold readlocks on all the ref items. 
+ */
+int rrset_array_lock(struct rrset_ref* ref, size_t count, uint32_t timenow);
+
+/**
+ * Unlock array (sorted) of rrset references.
+ * @param ref: array of rrset references (key pointer and ID value).
+ *	duplicate references are allowed and handled.
+ * @param count: size of array.
+ */
+void rrset_array_unlock(struct rrset_ref* ref, size_t count);
+
+/**
+ * Unlock array (sorted) of rrset references and at the same time
+ * touch LRU on the rrsets. It needs the scratch region for temporary
+ * storage as it uses the initial locks to obtain hash values.
+ * @param r: the rrset cache. In this cache LRU is updated.
+ * @param scratch: region for temporary storage of hash values.
+ *	if memory allocation fails, the lru touch fails silently,
+ *	but locks are released. memory errors are logged.
+ * @param ref: array of rrset references (key pointer and ID value).
+ *	duplicate references are allowed and handled.
+ * @param count: size of array.
+ */
+void rrset_array_unlock_touch(struct rrset_cache* r, struct regional* scratch,
+	struct rrset_ref* ref, size_t count);
+
+/**
+ * Update security status of an rrset. Looks up the rrset.
+ * If found, checks if rdata is equal.
+ * If so, it will update the security, trust and rrset-ttl values.
+ * The values are only updated if security is increased (towards secure).
+ * @param r: the rrset cache. 
+ * @param rrset: which rrset to attempt to update. This rrset is left 
+ * 	untouched. The rrset in the cache is updated in-place.
+ * @param now: current time.
+ */
+void rrset_update_sec_status(struct rrset_cache* r, 
+	struct ub_packed_rrset_key* rrset, uint32_t now);
+
+/**
+ * Looks up security status of an rrset. Looks up the rrset.
+ * If found, checks if rdata is equal, and entry did not expire.
+ * If so, it will update the security, trust and rrset-ttl values.
+ * @param r: the rrset cache. 
+ * @param rrset: This rrset may change security status due to the cache.
+ * 	But its status will only improve, towards secure.
+ * @param now: current time.
+ */
+void rrset_check_sec_status(struct rrset_cache* r, 
+	struct ub_packed_rrset_key* rrset, uint32_t now);
+
+/**
+ * Remove an rrset from the cache, by name and type and flags
+ * @param r: rrset cache
+ * @param nm: name of rrset
+ * @param nmlen: length of name
+ * @param type: type of rrset
+ * @param dclass: class of rrset, host order
+ * @param flags: flags of rrset, host order
+ */
+void rrset_cache_remove(struct rrset_cache* r, uint8_t* nm, size_t nmlen,
+	uint16_t type, uint16_t dclass, uint32_t flags);
+
+/** mark rrset to be deleted, set id=0 */
+void rrset_markdel(void* key);
+
+#endif /* SERVICES_CACHE_RRSET_H */
diff --git a/3rdParty/Unbound/src/src/services/listen_dnsport.c b/3rdParty/Unbound/src/src/services/listen_dnsport.c
new file mode 100644
index 0000000..ea7ec3a
--- /dev/null
+++ b/3rdParty/Unbound/src/src/services/listen_dnsport.c
@@ -0,0 +1,917 @@
+/*
+ * services/listen_dnsport.c - listen on port 53 for incoming DNS queries.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file has functions to get queries from clients.
+ */
+#include "config.h"
+#ifdef HAVE_SYS_TYPES_H
+#  include <sys/types.h>
+#endif
+#include <sys/time.h>
+#include "services/listen_dnsport.h"
+#include "services/outside_network.h"
+#include "util/netevent.h"
+#include "util/log.h"
+#include "util/config_file.h"
+#include "util/net_help.h"
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#include <fcntl.h>
+
+/** number of queued TCP connections for listen() */
+#define TCP_BACKLOG 5 
+
+/**
+ * Debug print of the getaddrinfo returned address.
+ * @param addr: the address returned.
+ */
+static void
+verbose_print_addr(struct addrinfo *addr)
+{
+	if(verbosity >= VERB_ALGO) {
+		char buf[100];
+		void* sinaddr = &((struct sockaddr_in*)addr->ai_addr)->sin_addr;
+#ifdef INET6
+		if(addr->ai_family == AF_INET6)
+			sinaddr = &((struct sockaddr_in6*)addr->ai_addr)->
+				sin6_addr;
+#endif /* INET6 */
+		if(inet_ntop(addr->ai_family, sinaddr, buf,
+			(socklen_t)sizeof(buf)) == 0) {
+			strncpy(buf, "(null)", sizeof(buf));
+		}
+		buf[sizeof(buf)-1] = 0;
+		verbose(VERB_ALGO, "creating %s%s socket %s %d", 
+			addr->ai_socktype==SOCK_DGRAM?"udp":
+			addr->ai_socktype==SOCK_STREAM?"tcp":"otherproto",
+			addr->ai_family==AF_INET?"4":
+			addr->ai_family==AF_INET6?"6":
+			"_otherfam", buf, 
+			ntohs(((struct sockaddr_in*)addr->ai_addr)->sin_port));
+	}
+}
+
+int
+create_udp_sock(int family, int socktype, struct sockaddr* addr,
+        socklen_t addrlen, int v6only, int* inuse, int* noproto,
+	int rcv, int snd)
+{
+	int s;
+#if defined(IPV6_USE_MIN_MTU)
+	int on=1;
+#endif
+#ifdef IPV6_MTU
+	int mtu = IPV6_MIN_MTU;
+#endif
+#if !defined(SO_RCVBUFFORCE) && !defined(SO_RCVBUF)
+	(void)rcv;
+#endif
+#if !defined(SO_SNDBUFFORCE) && !defined(SO_SNDBUF)
+	(void)snd;
+#endif
+#ifndef IPV6_V6ONLY
+	(void)v6only;
+#endif
+	if((s = socket(family, socktype, 0)) == -1) {
+		*inuse = 0;
+#ifndef USE_WINSOCK
+		if(errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
+			*noproto = 1;
+			return -1;
+		}
+		log_err("can't create socket: %s", strerror(errno));
+#else
+		if(WSAGetLastError() == WSAEAFNOSUPPORT || 
+			WSAGetLastError() == WSAEPROTONOSUPPORT) {
+			*noproto = 1;
+			return -1;
+		}
+		log_err("can't create socket: %s", 
+			wsa_strerror(WSAGetLastError()));
+#endif
+		*noproto = 0;
+		return -1;
+	}
+	if(rcv) {
+#ifdef SO_RCVBUF
+		int got;
+		socklen_t slen = (socklen_t)sizeof(got);
+#  ifdef SO_RCVBUFFORCE
+		/* Linux specific: try to use root permission to override
+		 * system limits on rcvbuf. The limit is stored in 
+		 * /proc/sys/net/core/rmem_max or sysctl net.core.rmem_max */
+		if(setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, (void*)&rcv, 
+			(socklen_t)sizeof(rcv)) < 0) {
+			if(errno != EPERM) {
+#    ifndef USE_WINSOCK
+				log_err("setsockopt(..., SO_RCVBUFFORCE, "
+					"...) failed: %s", strerror(errno));
+				close(s);
+#    else
+				log_err("setsockopt(..., SO_RCVBUFFORCE, "
+					"...) failed: %s", 
+					wsa_strerror(WSAGetLastError()));
+				closesocket(s);
+#    endif
+				*noproto = 0;
+				*inuse = 0;
+				return -1;
+			}
+#  endif /* SO_RCVBUFFORCE */
+			if(setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&rcv, 
+				(socklen_t)sizeof(rcv)) < 0) {
+#  ifndef USE_WINSOCK
+				log_err("setsockopt(..., SO_RCVBUF, "
+					"...) failed: %s", strerror(errno));
+				close(s);
+#  else
+				log_err("setsockopt(..., SO_RCVBUF, "
+					"...) failed: %s", 
+					wsa_strerror(WSAGetLastError()));
+				closesocket(s);
+#  endif
+				*noproto = 0;
+				*inuse = 0;
+				return -1;
+			}
+			/* check if we got the right thing or if system
+			 * reduced to some system max.  Warn if so */
+			if(getsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&got, 
+				&slen) >= 0 && got < rcv/2) {
+				log_warn("so-rcvbuf %u was not granted. "
+					"Got %u. To fix: start with "
+					"root permissions(linux) or sysctl "
+					"bigger net.core.rmem_max(linux) or "
+					"kern.ipc.maxsockbuf(bsd) values.",
+					(unsigned)rcv, (unsigned)got);
+			}
+#  ifdef SO_RCVBUFFORCE
+		}
+#  endif
+#endif /* SO_RCVBUF */
+	}
+	/* first do RCVBUF as the receive buffer is more important */
+	if(snd) {
+#ifdef SO_SNDBUF
+		int got;
+		socklen_t slen = (socklen_t)sizeof(got);
+#  ifdef SO_SNDBUFFORCE
+		/* Linux specific: try to use root permission to override
+		 * system limits on sndbuf. The limit is stored in 
+		 * /proc/sys/net/core/wmem_max or sysctl net.core.wmem_max */
+		if(setsockopt(s, SOL_SOCKET, SO_SNDBUFFORCE, (void*)&snd, 
+			(socklen_t)sizeof(snd)) < 0) {
+			if(errno != EPERM) {
+#    ifndef USE_WINSOCK
+				log_err("setsockopt(..., SO_SNDBUFFORCE, "
+					"...) failed: %s", strerror(errno));
+				close(s);
+#    else
+				log_err("setsockopt(..., SO_SNDBUFFORCE, "
+					"...) failed: %s", 
+					wsa_strerror(WSAGetLastError()));
+				closesocket(s);
+#    endif
+				*noproto = 0;
+				*inuse = 0;
+				return -1;
+			}
+#  endif /* SO_SNDBUFFORCE */
+			if(setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&snd, 
+				(socklen_t)sizeof(snd)) < 0) {
+#  ifndef USE_WINSOCK
+				log_err("setsockopt(..., SO_SNDBUF, "
+					"...) failed: %s", strerror(errno));
+				close(s);
+#  else
+				log_err("setsockopt(..., SO_SNDBUF, "
+					"...) failed: %s", 
+					wsa_strerror(WSAGetLastError()));
+				closesocket(s);
+#  endif
+				*noproto = 0;
+				*inuse = 0;
+				return -1;
+			}
+			/* check if we got the right thing or if system
+			 * reduced to some system max.  Warn if so */
+			if(getsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&got, 
+				&slen) >= 0 && got < snd/2) {
+				log_warn("so-sndbuf %u was not granted. "
+					"Got %u. To fix: start with "
+					"root permissions(linux) or sysctl "
+					"bigger net.core.wmem_max(linux) or "
+					"kern.ipc.maxsockbuf(bsd) values.",
+					(unsigned)snd, (unsigned)got);
+			}
+#  ifdef SO_SNDBUFFORCE
+		}
+#  endif
+#endif /* SO_SNDBUF */
+	}
+	if(family == AF_INET6) {
+# if defined(IPV6_V6ONLY)
+		if(v6only) {
+			int val=(v6only==2)?0:1;
+			if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, 
+				(void*)&val, (socklen_t)sizeof(val)) < 0) {
+#ifndef USE_WINSOCK
+				log_err("setsockopt(..., IPV6_V6ONLY"
+					", ...) failed: %s", strerror(errno));
+				close(s);
+#else
+				log_err("setsockopt(..., IPV6_V6ONLY"
+					", ...) failed: %s", 
+					wsa_strerror(WSAGetLastError()));
+				closesocket(s);
+#endif
+				*noproto = 0;
+				*inuse = 0;
+				return -1;
+			}
+		}
+# endif
+# if defined(IPV6_USE_MIN_MTU)
+		/*
+		 * There is no fragmentation of IPv6 datagrams
+		 * during forwarding in the network. Therefore
+		 * we do not send UDP datagrams larger than
+		 * the minimum IPv6 MTU of 1280 octets. The
+		 * EDNS0 message length can be larger if the
+		 * network stack supports IPV6_USE_MIN_MTU.
+		 */
+		if (setsockopt(s, IPPROTO_IPV6, IPV6_USE_MIN_MTU,
+			(void*)&on, (socklen_t)sizeof(on)) < 0) {
+#  ifndef USE_WINSOCK
+			log_err("setsockopt(..., IPV6_USE_MIN_MTU, "
+				"...) failed: %s", strerror(errno));
+			close(s);
+#  else
+			log_err("setsockopt(..., IPV6_USE_MIN_MTU, "
+				"...) failed: %s", 
+				wsa_strerror(WSAGetLastError()));
+			closesocket(s);
+#  endif
+			*noproto = 0;
+			*inuse = 0;
+			return -1;
+		}
+# elif defined(IPV6_MTU)
+		/*
+		 * On Linux, to send no larger than 1280, the PMTUD is
+		 * disabled by default for datagrams anyway, so we set
+		 * the MTU to use.
+		 */
+		if (setsockopt(s, IPPROTO_IPV6, IPV6_MTU,
+			(void*)&mtu, (socklen_t)sizeof(mtu)) < 0) {
+#  ifndef USE_WINSOCK
+			log_err("setsockopt(..., IPV6_MTU, ...) failed: %s", 
+				strerror(errno));
+			close(s);
+#  else
+			log_err("setsockopt(..., IPV6_MTU, ...) failed: %s", 
+				wsa_strerror(WSAGetLastError()));
+			closesocket(s);
+#  endif
+			*noproto = 0;
+			*inuse = 0;
+			return -1;
+		}
+# endif /* IPv6 MTU */
+	} else if(family == AF_INET) {
+#  if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
+		int action = IP_PMTUDISC_DONT;
+		if (setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, 
+			&action, (socklen_t)sizeof(action)) < 0) {
+			log_err("setsockopt(..., IP_MTU_DISCOVER, "
+				"IP_PMTUDISC_DONT...) failed: %s",
+				strerror(errno));
+			return -1;
+		}
+#  elif defined(IP_DONTFRAG)
+		int off = 0;
+		if (setsockopt(s, IPPROTO_IP, IP_DONTFRAG, 
+			&off, (socklen_t)sizeof(off)) < 0) {
+			log_err("setsockopt(..., IP_DONTFRAG, ...) failed: %s",
+				strerror(errno));
+			return -1;
+		}
+#  endif /* IPv4 MTU */
+	}
+	if(bind(s, (struct sockaddr*)addr, addrlen) != 0) {
+		*noproto = 0;
+#ifndef USE_WINSOCK
+#ifdef EADDRINUSE
+		*inuse = (errno == EADDRINUSE);
+		/* detect freebsd jail with no ipv6 permission */
+		if(family==AF_INET6 && errno==EINVAL)
+			*noproto = 1;
+		else if(errno != EADDRINUSE) {
+			log_err("can't bind socket: %s", strerror(errno));
+			log_addr(0, "failed address",
+				(struct sockaddr_storage*)addr, addrlen);
+		}
+#endif /* EADDRINUSE */
+		close(s);
+#else /* USE_WINSOCK */
+		if(WSAGetLastError() != WSAEADDRINUSE &&
+			WSAGetLastError() != WSAEADDRNOTAVAIL) {
+			log_err("can't bind socket: %s", 
+				wsa_strerror(WSAGetLastError()));
+			log_addr(0, "failed address",
+				(struct sockaddr_storage*)addr, addrlen);
+		}
+		closesocket(s);
+#endif
+		return -1;
+	}
+	if(!fd_set_nonblock(s)) {
+		*noproto = 0;
+		*inuse = 0;
+#ifndef USE_WINSOCK
+		close(s);
+#else
+		closesocket(s);
+#endif
+		return -1;
+	}
+	return s;
+}
+
+int
+create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto)
+{
+	int s;
+#if defined(SO_REUSEADDR) || defined(IPV6_V6ONLY)
+	int on = 1;
+#endif /* SO_REUSEADDR || IPV6_V6ONLY */
+	verbose_print_addr(addr);
+	*noproto = 0;
+	if((s = socket(addr->ai_family, addr->ai_socktype, 0)) == -1) {
+#ifndef USE_WINSOCK
+		if(errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
+			*noproto = 1;
+			return -1;
+		}
+		log_err("can't create socket: %s", strerror(errno));
+#else
+		if(WSAGetLastError() == WSAEAFNOSUPPORT ||
+			WSAGetLastError() == WSAEPROTONOSUPPORT) {
+			*noproto = 1;
+			return -1;
+		}
+		log_err("can't create socket: %s", 
+			wsa_strerror(WSAGetLastError()));
+#endif
+		return -1;
+	}
+#ifdef SO_REUSEADDR
+	if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&on, 
+		(socklen_t)sizeof(on)) < 0) {
+#ifndef USE_WINSOCK
+		log_err("setsockopt(.. SO_REUSEADDR ..) failed: %s",
+			strerror(errno));
+#else
+		log_err("setsockopt(.. SO_REUSEADDR ..) failed: %s",
+			wsa_strerror(WSAGetLastError()));
+#endif
+		return -1;
+	}
+#endif /* SO_REUSEADDR */
+#if defined(IPV6_V6ONLY)
+	if(addr->ai_family == AF_INET6 && v6only) {
+		if(setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, 
+			(void*)&on, (socklen_t)sizeof(on)) < 0) {
+#ifndef USE_WINSOCK
+			log_err("setsockopt(..., IPV6_V6ONLY, ...) failed: %s",
+				strerror(errno));
+#else
+			log_err("setsockopt(..., IPV6_V6ONLY, ...) failed: %s",
+				wsa_strerror(WSAGetLastError()));
+#endif
+			return -1;
+		}
+	}
+#else
+	(void)v6only;
+#endif /* IPV6_V6ONLY */
+	if(bind(s, addr->ai_addr, addr->ai_addrlen) != 0) {
+#ifndef USE_WINSOCK
+		/* detect freebsd jail with no ipv6 permission */
+		if(addr->ai_family==AF_INET6 && errno==EINVAL)
+			*noproto = 1;
+		else {
+			log_err("can't bind socket: %s", strerror(errno));
+			log_addr(0, "failed address",
+				(struct sockaddr_storage*)addr->ai_addr,
+				addr->ai_addrlen);
+		}
+#else
+		log_err("can't bind socket: %s", 
+			wsa_strerror(WSAGetLastError()));
+		log_addr(0, "failed address",
+			(struct sockaddr_storage*)addr->ai_addr,
+			addr->ai_addrlen);
+#endif
+		return -1;
+	}
+	if(!fd_set_nonblock(s)) {
+		return -1;
+	}
+	if(listen(s, TCP_BACKLOG) == -1) {
+#ifndef USE_WINSOCK
+		log_err("can't listen: %s", strerror(errno));
+#else
+		log_err("can't listen: %s", wsa_strerror(WSAGetLastError()));
+#endif
+		return -1;
+	}
+	return s;
+}
+
+/**
+ * Create socket from getaddrinfo results
+ */
+static int
+make_sock(int stype, const char* ifname, const char* port, 
+	struct addrinfo *hints, int v6only, int* noip6, size_t rcv, size_t snd)
+{
+	struct addrinfo *res = NULL;
+	int r, s, inuse, noproto;
+	hints->ai_socktype = stype;
+	*noip6 = 0;
+	if((r=getaddrinfo(ifname, port, hints, &res)) != 0 || !res) {
+#ifdef USE_WINSOCK
+		if(r == EAI_NONAME && hints->ai_family == AF_INET6){
+			*noip6 = 1; /* 'Host not found' for IP6 on winXP */
+			return -1;
+		}
+#endif
+		log_err("node %s:%s getaddrinfo: %s %s", 
+			ifname?ifname:"default", port, gai_strerror(r),
+#ifdef EAI_SYSTEM
+			r==EAI_SYSTEM?(char*)strerror(errno):""
+#else
+			""
+#endif
+		);
+		return -1;
+	}
+	if(stype == SOCK_DGRAM) {
+		verbose_print_addr(res);
+		s = create_udp_sock(res->ai_family, res->ai_socktype,
+			(struct sockaddr*)res->ai_addr, res->ai_addrlen,
+			v6only, &inuse, &noproto, (int)rcv, (int)snd);
+		if(s == -1 && inuse) {
+			log_err("bind: address already in use");
+		} else if(s == -1 && noproto && hints->ai_family == AF_INET6){
+			*noip6 = 1;
+		}
+	} else	{
+		s = create_tcp_accept_sock(res, v6only, &noproto);
+		if(s == -1 && noproto && hints->ai_family == AF_INET6){
+			*noip6 = 1;
+		}
+	}
+	freeaddrinfo(res);
+	return s;
+}
+
+/** make socket and first see if ifname contains port override info */
+static int
+make_sock_port(int stype, const char* ifname, const char* port, 
+	struct addrinfo *hints, int v6only, int* noip6, size_t rcv, size_t snd)
+{
+	char* s = strchr(ifname, '@');
+	if(s) {
+		/* override port with ifspec@port */
+		char p[16];
+		char newif[128];
+		if((size_t)(s-ifname) >= sizeof(newif)) {
+			log_err("ifname too long: %s", ifname);
+			*noip6 = 0;
+			return -1;
+		}
+		if(strlen(s+1) >= sizeof(p)) {
+			log_err("portnumber too long: %s", ifname);
+			*noip6 = 0;
+			return -1;
+		}
+		strncpy(newif, ifname, sizeof(newif));
+		newif[s-ifname] = 0;
+		strncpy(p, s+1, sizeof(p));
+		p[strlen(s+1)]=0;
+		return make_sock(stype, newif, p, hints, v6only, noip6,
+			rcv, snd);
+	}
+	return make_sock(stype, ifname, port, hints, v6only, noip6, rcv, snd);
+}
+
+/**
+ * Add port to open ports list.
+ * @param list: list head. changed.
+ * @param s: fd.
+ * @param ftype: if fd is UDP.
+ * @return false on failure. list in unchanged then.
+ */
+static int
+port_insert(struct listen_port** list, int s, enum listen_type ftype)
+{
+	struct listen_port* item = (struct listen_port*)malloc(
+		sizeof(struct listen_port));
+	if(!item)
+		return 0;
+	item->next = *list;
+	item->fd = s;
+	item->ftype = ftype;
+	*list = item;
+	return 1;
+}
+
+/** set fd to receive source address packet info */
+static int
+set_recvpktinfo(int s, int family) 
+{
+#if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO) || (defined(IP_RECVDSTADDR) && defined(IP_SENDSRCADDR)) || defined(IP_PKTINFO)
+	int on = 1;
+#else
+	(void)s;
+#endif
+	if(family == AF_INET6) {
+#           ifdef IPV6_RECVPKTINFO
+		if(setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO,
+			(void*)&on, (socklen_t)sizeof(on)) < 0) {
+			log_err("setsockopt(..., IPV6_RECVPKTINFO, ...) failed: %s",
+				strerror(errno));
+			return 0;
+		}
+#           elif defined(IPV6_PKTINFO)
+		if(setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO,
+			(void*)&on, (socklen_t)sizeof(on)) < 0) {
+			log_err("setsockopt(..., IPV6_PKTINFO, ...) failed: %s",
+				strerror(errno));
+			return 0;
+		}
+#           else
+		log_err("no IPV6_RECVPKTINFO and no IPV6_PKTINFO option, please "
+			"disable interface-automatic in config");
+		return 0;
+#           endif /* defined IPV6_RECVPKTINFO */
+
+	} else if(family == AF_INET) {
+#           ifdef IP_PKTINFO
+		if(setsockopt(s, IPPROTO_IP, IP_PKTINFO,
+			(void*)&on, (socklen_t)sizeof(on)) < 0) {
+			log_err("setsockopt(..., IP_PKTINFO, ...) failed: %s",
+				strerror(errno));
+			return 0;
+		}
+#           elif defined(IP_RECVDSTADDR) && defined(IP_SENDSRCADDR)
+		if(setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR,
+			(void*)&on, (socklen_t)sizeof(on)) < 0) {
+			log_err("setsockopt(..., IP_RECVDSTADDR, ...) failed: %s",
+				strerror(errno));
+			return 0;
+		}
+#           else
+		log_err("no IP_SENDSRCADDR or IP_PKTINFO option, please disable "
+			"interface-automatic in config");
+		return 0;
+#           endif /* IP_PKTINFO */
+
+	}
+	return 1;
+}
+
+/**
+ * Helper for ports_open. Creates one interface (or NULL for default).
+ * @param ifname: The interface ip address.
+ * @param do_auto: use automatic interface detection.
+ * 	If enabled, then ifname must be the wildcard name.
+ * @param do_udp: if udp should be used.
+ * @param do_tcp: if udp should be used.
+ * @param hints: for getaddrinfo. family and flags have to be set by caller.
+ * @param port: Port number to use (as string).
+ * @param list: list of open ports, appended to, changed to point to list head.
+ * @param rcv: receive buffer size for UDP
+ * @param snd: send buffer size for UDP
+ * @param ssl_port: ssl service port number
+ * @return: returns false on error.
+ */
+static int
+ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp, 
+	struct addrinfo *hints, const char* port, struct listen_port** list,
+	size_t rcv, size_t snd, int ssl_port)
+{
+	int s, noip6=0;
+	if(!do_udp && !do_tcp)
+		return 0;
+	if(do_auto) {
+		if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1, 
+			&noip6, rcv, snd)) == -1) {
+			if(noip6) {
+				log_warn("IPv6 protocol not available");
+				return 1;
+			}
+			return 0;
+		}
+		/* getting source addr packet info is highly non-portable */
+		if(!set_recvpktinfo(s, hints->ai_family))
+			return 0;
+		if(!port_insert(list, s, listen_type_udpancil)) {
+#ifndef USE_WINSOCK
+			close(s);
+#else
+			closesocket(s);
+#endif
+			return 0;
+		}
+	} else if(do_udp) {
+		/* regular udp socket */
+		if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1, 
+			&noip6, rcv, snd)) == -1) {
+			if(noip6) {
+				log_warn("IPv6 protocol not available");
+				return 1;
+			}
+			return 0;
+		}
+		if(!port_insert(list, s, listen_type_udp)) {
+#ifndef USE_WINSOCK
+			close(s);
+#else
+			closesocket(s);
+#endif
+			return 0;
+		}
+	}
+	if(do_tcp) {
+		int is_ssl = ((strchr(ifname, '@') && 
+			atoi(strchr(ifname, '@')+1) == ssl_port) ||
+			(!strchr(ifname, '@') && atoi(port) == ssl_port));
+		if((s = make_sock_port(SOCK_STREAM, ifname, port, hints, 1, 
+			&noip6, 0, 0)) == -1) {
+			if(noip6) {
+				/*log_warn("IPv6 protocol not available");*/
+				return 1;
+			}
+			return 0;
+		}
+		if(is_ssl)
+			verbose(VERB_ALGO, "setup TCP for SSL service");
+		if(!port_insert(list, s, is_ssl?listen_type_ssl:
+			listen_type_tcp)) {
+#ifndef USE_WINSOCK
+			close(s);
+#else
+			closesocket(s);
+#endif
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/** 
+ * Add items to commpoint list in front.
+ * @param c: commpoint to add.
+ * @param front: listen struct.
+ * @return: false on failure.
+ */
+static int
+listen_cp_insert(struct comm_point* c, struct listen_dnsport* front)
+{
+	struct listen_list* item = (struct listen_list*)malloc(
+		sizeof(struct listen_list));
+	if(!item)
+		return 0;
+	item->com = c;
+	item->next = front->cps;
+	front->cps = item;
+	return 1;
+}
+
+struct listen_dnsport* 
+listen_create(struct comm_base* base, struct listen_port* ports,
+	size_t bufsize, int tcp_accept_count, void* sslctx,
+	comm_point_callback_t* cb, void *cb_arg)
+{
+	struct listen_dnsport* front = (struct listen_dnsport*)
+		malloc(sizeof(struct listen_dnsport));
+	if(!front)
+		return NULL;
+	front->cps = NULL;
+	front->udp_buff = ldns_buffer_new(bufsize);
+	if(!front->udp_buff) {
+		free(front);
+		return NULL;
+	}
+
+	/* create comm points as needed */
+	while(ports) {
+		struct comm_point* cp = NULL;
+		if(ports->ftype == listen_type_udp) 
+			cp = comm_point_create_udp(base, ports->fd, 
+				front->udp_buff, cb, cb_arg);
+		else if(ports->ftype == listen_type_tcp)
+			cp = comm_point_create_tcp(base, ports->fd, 
+				tcp_accept_count, bufsize, cb, cb_arg);
+		else if(ports->ftype == listen_type_ssl) {
+			cp = comm_point_create_tcp(base, ports->fd, 
+				tcp_accept_count, bufsize, cb, cb_arg);
+			cp->ssl = sslctx;
+		} else if(ports->ftype == listen_type_udpancil) 
+			cp = comm_point_create_udp_ancil(base, ports->fd, 
+				front->udp_buff, cb, cb_arg);
+		if(!cp) {
+			log_err("can't create commpoint");	
+			listen_delete(front);
+			return NULL;
+		}
+		cp->do_not_close = 1;
+		if(!listen_cp_insert(cp, front)) {
+			log_err("malloc failed");
+			comm_point_delete(cp);
+			listen_delete(front);
+			return NULL;
+		}
+		ports = ports->next;
+	}
+	if(!front->cps) {
+		log_err("Could not open sockets to accept queries.");
+		listen_delete(front);
+		return NULL;
+	}
+
+	return front;
+}
+
+void
+listen_list_delete(struct listen_list* list)
+{
+	struct listen_list *p = list, *pn;
+	while(p) {
+		pn = p->next;
+		comm_point_delete(p->com);
+		free(p);
+		p = pn;
+	}
+}
+
+void 
+listen_delete(struct listen_dnsport* front)
+{
+	if(!front) 
+		return;
+	listen_list_delete(front->cps);
+	ldns_buffer_free(front->udp_buff);
+	free(front);
+}
+
+struct listen_port* 
+listening_ports_open(struct config_file* cfg)
+{
+	struct listen_port* list = NULL;
+	struct addrinfo hints;
+	int i, do_ip4, do_ip6;
+	int do_tcp, do_auto;
+	char portbuf[32];
+	snprintf(portbuf, sizeof(portbuf), "%d", cfg->port);
+	do_ip4 = cfg->do_ip4;
+	do_ip6 = cfg->do_ip6;
+	do_tcp = cfg->do_tcp;
+	do_auto = cfg->if_automatic && cfg->do_udp;
+	if(cfg->incoming_num_tcp == 0)
+		do_tcp = 0;
+
+	/* getaddrinfo */
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_flags = AI_PASSIVE;
+	/* no name lookups on our listening ports */
+	if(cfg->num_ifs > 0)
+		hints.ai_flags |= AI_NUMERICHOST;
+	hints.ai_family = AF_UNSPEC;
+#ifndef INET6
+	do_ip6 = 0;
+#endif
+	if(!do_ip4 && !do_ip6) {
+		return NULL;
+	}
+	/* create ip4 and ip6 ports so that return addresses are nice. */
+	if(do_auto || cfg->num_ifs == 0) {
+		if(do_ip6) {
+			hints.ai_family = AF_INET6;
+			if(!ports_create_if(do_auto?"::0":"::1", 
+				do_auto, cfg->do_udp, do_tcp, 
+				&hints, portbuf, &list,
+				cfg->so_rcvbuf, cfg->so_sndbuf,
+				cfg->ssl_port)) {
+				listening_ports_free(list);
+				return NULL;
+			}
+		}
+		if(do_ip4) {
+			hints.ai_family = AF_INET;
+			if(!ports_create_if(do_auto?"0.0.0.0":"127.0.0.1", 
+				do_auto, cfg->do_udp, do_tcp, 
+				&hints, portbuf, &list,
+				cfg->so_rcvbuf, cfg->so_sndbuf,
+				cfg->ssl_port)) {
+				listening_ports_free(list);
+				return NULL;
+			}
+		}
+	} else for(i = 0; i<cfg->num_ifs; i++) {
+		if(str_is_ip6(cfg->ifs[i])) {
+			if(!do_ip6)
+				continue;
+			hints.ai_family = AF_INET6;
+			if(!ports_create_if(cfg->ifs[i], 0, cfg->do_udp, 
+				do_tcp, &hints, portbuf, &list, 
+				cfg->so_rcvbuf, cfg->so_sndbuf,
+				cfg->ssl_port)) {
+				listening_ports_free(list);
+				return NULL;
+			}
+		} else {
+			if(!do_ip4)
+				continue;
+			hints.ai_family = AF_INET;
+			if(!ports_create_if(cfg->ifs[i], 0, cfg->do_udp, 
+				do_tcp, &hints, portbuf, &list, 
+				cfg->so_rcvbuf, cfg->so_sndbuf,
+				cfg->ssl_port)) {
+				listening_ports_free(list);
+				return NULL;
+			}
+		}
+	}
+	return list;
+}
+
+void listening_ports_free(struct listen_port* list)
+{
+	struct listen_port* nx;
+	while(list) {
+		nx = list->next;
+		if(list->fd != -1) {
+#ifndef USE_WINSOCK
+			close(list->fd);
+#else
+			closesocket(list->fd);
+#endif
+		}
+		free(list);
+		list = nx;
+	}
+}
+
+size_t listen_get_mem(struct listen_dnsport* listen)
+{
+	size_t s = sizeof(*listen) + sizeof(*listen->base) + 
+		sizeof(*listen->udp_buff) + 
+		ldns_buffer_capacity(listen->udp_buff);
+	struct listen_list* p;
+	for(p = listen->cps; p; p = p->next) {
+		s += sizeof(*p);
+		s += comm_point_get_mem(p->com);
+	}
+	return s;
+}
diff --git a/3rdParty/Unbound/src/src/services/listen_dnsport.h b/3rdParty/Unbound/src/src/services/listen_dnsport.h
new file mode 100644
index 0000000..22fa828
--- /dev/null
+++ b/3rdParty/Unbound/src/src/services/listen_dnsport.h
@@ -0,0 +1,184 @@
+/*
+ * services/listen_dnsport.h - listen on port 53 for incoming DNS queries.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file has functions to get queries from clients.
+ */
+
+#ifndef LISTEN_DNSPORT_H
+#define LISTEN_DNSPORT_H
+
+#include "util/netevent.h"
+struct listen_list;
+struct config_file;
+struct addrinfo;
+
+/**
+ * Listening for queries structure.
+ * Contains list of query-listen sockets.
+ */
+struct listen_dnsport {
+	/** Base for select calls */
+	struct comm_base* base;
+
+	/** buffer shared by UDP connections, since there is only one
+	    datagram at any time. */
+	ldns_buffer* udp_buff;
+
+	/** list of comm points used to get incoming events */
+	struct listen_list* cps;
+};
+
+/**
+ * Single linked list to store event points.
+ */
+struct listen_list {
+	/** next in list */
+	struct listen_list* next;
+	/** event info */
+	struct comm_point* com;
+};
+
+/**
+ * type of ports
+ */
+enum listen_type {
+	/** udp type */
+	listen_type_udp,
+	/** tcp type */
+	listen_type_tcp,
+	/** udp ipv6 (v4mapped) for use with ancillary data */
+	listen_type_udpancil,
+	/** ssl over tcp type */
+	listen_type_ssl
+};
+
+/**
+ * Single linked list to store shared ports that have been 
+ * opened for use by all threads.
+ */
+struct listen_port {
+	/** next in list */
+	struct listen_port* next;
+	/** file descriptor, open and ready for use */
+	int fd;
+	/** type of file descriptor, udp or tcp */
+	enum listen_type ftype;
+};
+
+/**
+ * Create shared listening ports
+ * Getaddrinfo, create socket, bind and listen to zero or more 
+ * interfaces for IP4 and/or IP6, for UDP and/or TCP.
+ * On the given port number. It creates the sockets.
+ * @param cfg: settings on what ports to open.
+ * @return: linked list of ports or NULL on error.
+ */
+struct listen_port* listening_ports_open(struct config_file* cfg);
+
+/**
+ * Close and delete the (list of) listening ports.
+ */
+void listening_ports_free(struct listen_port* list);
+
+/**
+ * Create commpoints with for this thread for the shared ports.
+ * @param base: the comm_base that provides event functionality.
+ *	for default all ifs.
+ * @param ports: the list of shared ports.
+ * @param bufsize: size of datagram buffer.
+ * @param tcp_accept_count: max number of simultaneous TCP connections 
+ * 	from clients.
+ * @param sslctx: nonNULL if ssl context.
+ * @param cb: callback function when a request arrives. It is passed
+ *	  the packet and user argument. Return true to send a reply.
+ * @param cb_arg: user data argument for callback function.
+ * @return: the malloced listening structure, ready for use. NULL on error.
+ */
+struct listen_dnsport* listen_create(struct comm_base* base,
+	struct listen_port* ports, size_t bufsize, int tcp_accept_count,
+	void* sslctx, comm_point_callback_t* cb, void* cb_arg);
+
+/**
+ * delete the listening structure
+ * @param listen: listening structure.
+ */
+void listen_delete(struct listen_dnsport* listen);
+
+/**
+ * delete listen_list of commpoints. Calls commpointdelete() on items.
+ * This may close the fds or not depending on flags.
+ * @param list: to delete.
+ */
+void listen_list_delete(struct listen_list* list);
+
+/**
+ * get memory size used by the listening structs
+ * @param listen: listening structure.
+ * @return: size in bytes.
+ */
+size_t listen_get_mem(struct listen_dnsport* listen);
+
+/**
+ * Create and bind nonblocking UDP socket
+ * @param family: for socket call.
+ * @param socktype: for socket call.
+ * @param addr: for bind call.
+ * @param addrlen: for bind call.
+ * @param v6only: if enabled, IP6 sockets get IP6ONLY option set.
+ * 	if enabled with value 2 IP6ONLY option is disabled.
+ * @param inuse: on error, this is set true if the port was in use.
+ * @param noproto: on error, this is set true if cause is that the
+	IPv6 proto (family) is not available.
+ * @param rcv: set size on rcvbuf with socket option, if 0 it is not set.
+ * @param snd: set size on sndbuf with socket option, if 0 it is not set.
+ * @return: the socket. -1 on error.
+ */
+int create_udp_sock(int family, int socktype, struct sockaddr* addr, 
+	socklen_t addrlen, int v6only, int* inuse, int* noproto, int rcv,
+	int snd);
+
+/**
+ * Create and bind TCP listening socket
+ * @param addr: address info ready to make socket.
+ * @param v6only: enable ip6 only flag on ip6 sockets.
+ * @param noproto: if error caused by lack of protocol support.
+ * @return: the socket. -1 on error.
+ */
+int create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto);
+
+#endif /* LISTEN_DNSPORT_H */
diff --git a/3rdParty/Unbound/src/src/services/localzone.c b/3rdParty/Unbound/src/src/services/localzone.c
new file mode 100644
index 0000000..98d6943
--- /dev/null
+++ b/3rdParty/Unbound/src/src/services/localzone.c
@@ -0,0 +1,1353 @@
+/*
+ * services/localzone.c - local zones authority service.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to enable local zone authority service.
+ */
+#include "config.h"
+#include <ldns/dname.h>
+#include <ldns/host2wire.h>
+#include "services/localzone.h"
+#include "util/regional.h"
+#include "util/config_file.h"
+#include "util/data/dname.h"
+#include "util/data/packed_rrset.h"
+#include "util/data/msgencode.h"
+#include "util/net_help.h"
+#include "util/data/msgreply.h"
+#include "util/data/msgparse.h"
+
+struct local_zones* 
+local_zones_create(void)
+{
+	struct local_zones* zones = (struct local_zones*)calloc(1, 
+		sizeof(*zones));
+	if(!zones)
+		return NULL;
+	rbtree_init(&zones->ztree, &local_zone_cmp);
+	lock_quick_init(&zones->lock);
+	lock_protect(&zones->lock, &zones->ztree, sizeof(zones->ztree));
+	/* also lock protects the rbnode's in struct local_zone */
+	return zones;
+}
+
+/** helper traverse to delete zones */
+static void 
+lzdel(rbnode_t* n, void* ATTR_UNUSED(arg))
+{
+	struct local_zone* z = (struct local_zone*)n->key;
+	local_zone_delete(z);
+}
+
+void 
+local_zones_delete(struct local_zones* zones)
+{
+	if(!zones)
+		return;
+	lock_quick_destroy(&zones->lock);
+	/* walk through zones and delete them all */
+	traverse_postorder(&zones->ztree, lzdel, NULL);
+	free(zones);
+}
+
+void 
+local_zone_delete(struct local_zone* z)
+{
+	if(!z)
+		return;
+	lock_rw_destroy(&z->lock);
+	regional_destroy(z->region);
+	free(z->name);
+	free(z);
+}
+
+int 
+local_zone_cmp(const void* z1, const void* z2)
+{
+	/* first sort on class, so that hierarchy can be maintained within
+	 * a class */
+	struct local_zone* a = (struct local_zone*)z1;
+	struct local_zone* b = (struct local_zone*)z2;
+	int m;
+	if(a->dclass != b->dclass) {
+		if(a->dclass < b->dclass)
+			return -1;
+		return 1;
+	}
+	return dname_lab_cmp(a->name, a->namelabs, b->name, b->namelabs, &m);
+}
+
+int 
+local_data_cmp(const void* d1, const void* d2)
+{
+	struct local_data* a = (struct local_data*)d1;
+	struct local_data* b = (struct local_data*)d2;
+	int m;
+	return dname_canon_lab_cmp(a->name, a->namelabs, b->name, 
+		b->namelabs, &m);
+}
+
+/* form wireformat from text format domain name */
+int
+parse_dname(const char* str, uint8_t** res, size_t* len, int* labs)
+{
+	ldns_rdf* rdf;
+	*res = NULL;
+	*len = 0;
+	*labs = 0;
+	rdf = ldns_dname_new_frm_str(str);
+	if(!rdf) {
+		log_err("cannot parse name %s", str);
+		return 0;
+	}
+	*res = memdup(ldns_rdf_data(rdf), ldns_rdf_size(rdf));
+	ldns_rdf_deep_free(rdf);
+	if(!*res) {
+		log_err("out of memory");
+		return 0;
+	}
+	*labs = dname_count_size_labels(*res, len);
+	return 1;
+}
+
+/** create a new localzone */
+static struct local_zone*
+local_zone_create(uint8_t* nm, size_t len, int labs, 
+	enum localzone_type t, uint16_t dclass)
+{
+	struct local_zone* z = (struct local_zone*)calloc(1, sizeof(*z));
+	if(!z) {
+		return NULL;
+	}
+	z->node.key = z;
+	z->dclass = dclass;
+	z->type = t;
+	z->name = nm;
+	z->namelen = len;
+	z->namelabs = labs;
+	lock_rw_init(&z->lock);
+	z->region = regional_create();
+	if(!z->region) {
+		free(z);
+		return NULL;
+	}
+	rbtree_init(&z->data, &local_data_cmp);
+	lock_protect(&z->lock, &z->parent, sizeof(*z)-sizeof(rbnode_t));
+	/* also the zones->lock protects node, parent, name*, class */
+	return z;
+}
+
+/** enter a new zone with allocated dname returns with WRlock */
+static struct local_zone*
+lz_enter_zone_dname(struct local_zones* zones, uint8_t* nm, size_t len, 
+	int labs, enum localzone_type t, uint16_t c)
+{
+	struct local_zone* z = local_zone_create(nm, len, labs, t, c);
+	if(!z) {
+		log_err("out of memory");
+		return NULL;
+	}
+
+	/* add to rbtree */
+	lock_quick_lock(&zones->lock);
+	lock_rw_wrlock(&z->lock);
+	if(!rbtree_insert(&zones->ztree, &z->node)) {
+		log_warn("duplicate local-zone");
+		lock_rw_unlock(&z->lock);
+		local_zone_delete(z);
+		lock_quick_unlock(&zones->lock);
+		return NULL;
+	}
+	lock_quick_unlock(&zones->lock);
+	return z;
+}
+
+/** enter a new zone */
+static struct local_zone*
+lz_enter_zone(struct local_zones* zones, const char* name, const char* type,
+	uint16_t dclass)
+{
+	struct local_zone* z;
+	enum localzone_type t;
+	uint8_t* nm;
+	size_t len;
+	int labs;
+	if(!parse_dname(name, &nm, &len, &labs)) {
+		log_err("bad zone name %s %s", name, type);
+		return NULL;
+	}
+	if(!local_zone_str2type(type, &t)) {
+		log_err("bad lz_enter_zone type %s %s", name, type);
+		free(nm);
+		return NULL;
+	}
+	if(!(z=lz_enter_zone_dname(zones, nm, len, labs, t, dclass))) {
+		log_err("could not enter zone %s %s", name, type);
+		return NULL;
+	}
+	return z;
+}
+
+/** return name and class and rdata of rr; parses string */
+static int
+get_rr_content(const char* str, uint8_t** nm, uint16_t* type,
+	uint16_t* dclass, uint32_t* ttl, ldns_buffer* rdata)
+{
+	ldns_rr* rr = NULL;
+	ldns_status status = ldns_rr_new_frm_str(&rr, str, 3600, NULL, NULL);
+	if(status != LDNS_STATUS_OK) {
+		log_err("error parsing local-data '%s': %s",
+			str, ldns_get_errorstr_by_id(status));
+		ldns_rr_free(rr);
+		return 0;
+	}
+	*nm = memdup(ldns_rdf_data(ldns_rr_owner(rr)), 
+		ldns_rdf_size(ldns_rr_owner(rr)));
+	if(!*nm) {
+		log_err("out of memory");
+		ldns_rr_free(rr);
+		return 0;
+	}
+	*dclass = ldns_rr_get_class(rr);
+	*type = ldns_rr_get_type(rr);
+	*ttl = (uint32_t)ldns_rr_ttl(rr);
+	ldns_buffer_clear(rdata);
+	ldns_buffer_skip(rdata, 2);
+	status = ldns_rr_rdata2buffer_wire(rdata, rr);
+	ldns_rr_free(rr);
+        if(status != LDNS_STATUS_OK) {
+                log_err("error converting RR '%s' to wireformat: %s",
+                        str, ldns_get_errorstr_by_id(status));
+		free(*nm);
+		*nm = NULL;
+                return 0;
+        }
+        ldns_buffer_flip(rdata);
+        ldns_buffer_write_u16_at(rdata, 0, ldns_buffer_limit(rdata) - 2);
+	return 1;
+}
+
+/** return name and class of rr; parses string */
+static int
+get_rr_nameclass(const char* str, uint8_t** nm, uint16_t* dclass)
+{
+	ldns_rr* rr = NULL;
+	ldns_status status = ldns_rr_new_frm_str(&rr, str, 3600, NULL, NULL);
+	if(status != LDNS_STATUS_OK) {
+		log_err("error parsing local-data '%s': %s",
+			str, ldns_get_errorstr_by_id(status));
+		ldns_rr_free(rr);
+		return 0;
+	}
+	*nm = memdup(ldns_rdf_data(ldns_rr_owner(rr)), 
+		ldns_rdf_size(ldns_rr_owner(rr)));
+	*dclass = ldns_rr_get_class(rr);
+	ldns_rr_free(rr);
+	if(!*nm) {
+		log_err("out of memory");
+		return 0;
+	}
+	return 1;
+}
+
+/**
+ * Find an rrset in local data structure.
+ * @param data: local data domain name structure.
+ * @param type: type to look for (host order).
+ * @return rrset pointer or NULL if not found.
+ */
+static struct local_rrset*
+local_data_find_type(struct local_data* data, uint16_t type)
+{
+	struct local_rrset* p;
+	type = htons(type);
+	for(p = data->rrsets; p; p = p->next) {
+		if(p->rrset->rk.type == type)
+			return p;
+	}
+	return NULL;
+}
+
+/** check for RR duplicates */
+static int
+rr_is_duplicate(struct packed_rrset_data* pd, ldns_buffer* buf)
+{
+	size_t i;
+	for(i=0; i<pd->count; i++) {
+		if(ldns_buffer_limit(buf) == pd->rr_len[i] &&
+			memcmp(ldns_buffer_begin(buf), pd->rr_data[i],
+				ldns_buffer_limit(buf)) == 0) 
+			return 1;
+	}
+	return 0;
+}
+
+/** new local_rrset */
+static struct local_rrset*
+new_local_rrset(struct regional* region, struct local_data* node,
+	uint16_t rrtype, uint16_t rrclass)
+{
+	struct packed_rrset_data* pd;
+	struct local_rrset* rrset = (struct local_rrset*)
+		regional_alloc_zero(region, sizeof(*rrset));
+	if(!rrset) {
+		log_err("out of memory");
+		return NULL;
+	}
+	rrset->next = node->rrsets;
+	node->rrsets = rrset;
+	rrset->rrset = (struct ub_packed_rrset_key*)
+		regional_alloc_zero(region, sizeof(*rrset->rrset));
+	if(!rrset->rrset) {
+		log_err("out of memory");
+		return NULL;
+	}
+	rrset->rrset->entry.key = rrset->rrset;
+	pd = (struct packed_rrset_data*)regional_alloc_zero(region,
+		sizeof(*pd));
+	if(!pd) {
+		log_err("out of memory");
+		return NULL;
+	}
+	pd->trust = rrset_trust_prim_noglue;
+	pd->security = sec_status_insecure;
+	rrset->rrset->entry.data = pd;
+	rrset->rrset->rk.dname = node->name;
+	rrset->rrset->rk.dname_len = node->namelen;
+	rrset->rrset->rk.type = htons(rrtype);
+	rrset->rrset->rk.rrset_class = htons(rrclass);
+	return rrset;
+}
+
+/** insert RR into RRset data structure; Wastes a couple of bytes */
+static int
+insert_rr(struct regional* region, struct packed_rrset_data* pd,
+	ldns_buffer* buf, uint32_t ttl)
+{
+	size_t* oldlen = pd->rr_len;
+	uint32_t* oldttl = pd->rr_ttl;
+	uint8_t** olddata = pd->rr_data;
+
+	/* add RR to rrset */
+	pd->count++;
+	pd->rr_len = regional_alloc(region, sizeof(*pd->rr_len)*pd->count);
+	pd->rr_ttl = regional_alloc(region, sizeof(*pd->rr_ttl)*pd->count);
+	pd->rr_data = regional_alloc(region, sizeof(*pd->rr_data)*pd->count);
+	if(!pd->rr_len || !pd->rr_ttl || !pd->rr_data) {
+		log_err("out of memory");
+		return 0;
+	}
+	if(pd->count > 1) {
+		memcpy(pd->rr_len+1, oldlen, 
+			sizeof(*pd->rr_len)*(pd->count-1));
+		memcpy(pd->rr_ttl+1, oldttl, 
+			sizeof(*pd->rr_ttl)*(pd->count-1));
+		memcpy(pd->rr_data+1, olddata, 
+			sizeof(*pd->rr_data)*(pd->count-1));
+	}
+	pd->rr_len[0] = ldns_buffer_limit(buf);
+	pd->rr_ttl[0] = ttl;
+	pd->rr_data[0] = regional_alloc_init(region,
+		ldns_buffer_begin(buf), ldns_buffer_limit(buf));
+	if(!pd->rr_data[0]) {
+		log_err("out of memory");
+		return 0;
+	}
+	return 1;
+}
+
+/** find a data node by exact name */
+static struct local_data* 
+lz_find_node(struct local_zone* z, uint8_t* nm, size_t nmlen, int nmlabs)
+{
+	struct local_data key;
+	key.node.key = &key;
+	key.name = nm;
+	key.namelen = nmlen;
+	key.namelabs = nmlabs;
+	return (struct local_data*)rbtree_search(&z->data, &key.node);
+}
+
+/** find a node, create it if not and all its empty nonterminal parents */
+static int
+lz_find_create_node(struct local_zone* z, uint8_t* nm, size_t nmlen, 
+	int nmlabs, struct local_data** res)
+{
+	struct local_data* ld = lz_find_node(z, nm, nmlen, nmlabs);
+	if(!ld) {
+		/* create a domain name to store rr. */
+		ld = (struct local_data*)regional_alloc_zero(z->region,
+			sizeof(*ld));
+		if(!ld) {
+			log_err("out of memory adding local data");
+			return 0;
+		}
+		ld->node.key = ld;
+		ld->name = regional_alloc_init(z->region, nm, nmlen);
+		if(!ld->name) {
+			log_err("out of memory");
+			return 0;
+		}
+		ld->namelen = nmlen;
+		ld->namelabs = nmlabs;
+		if(!rbtree_insert(&z->data, &ld->node)) {
+			log_assert(0); /* duplicate name */
+		}
+		/* see if empty nonterminals need to be created */
+		if(nmlabs > z->namelabs) {
+			dname_remove_label(&nm, &nmlen);
+			if(!lz_find_create_node(z, nm, nmlen, nmlabs-1, res))
+				return 0;
+		}
+	}
+	*res = ld;
+	return 1;
+}
+
+/** enter data RR into auth zone */
+static int
+lz_enter_rr_into_zone(struct local_zone* z, ldns_buffer* buf,
+	const char* rrstr)
+{
+	uint8_t* nm;
+	size_t nmlen;
+	int nmlabs;
+	struct local_data* node;
+	struct local_rrset* rrset;
+	struct packed_rrset_data* pd;
+	uint16_t rrtype, rrclass;
+	uint32_t ttl;
+	if(!get_rr_content(rrstr, &nm, &rrtype, &rrclass, &ttl, buf)) {
+		log_err("bad local-data: %s", rrstr);
+		return 0;
+	}
+	log_assert(z->dclass == rrclass);
+	if(z->type == local_zone_redirect &&
+		query_dname_compare(z->name, nm) != 0) {
+		log_err("local-data in redirect zone must reside at top of zone"
+			", not at %s", rrstr);
+		free(nm);
+		return 0;
+	}
+	nmlabs = dname_count_size_labels(nm, &nmlen);
+	if(!lz_find_create_node(z, nm, nmlen, nmlabs, &node)) {
+		free(nm);
+		return 0;
+	}
+	log_assert(node);
+	free(nm);
+
+	rrset = local_data_find_type(node, rrtype);
+	if(!rrset) {
+		rrset = new_local_rrset(z->region, node, rrtype, rrclass);
+		if(!rrset)
+			return 0;
+		if(query_dname_compare(node->name, z->name) == 0) {
+			if(rrtype == LDNS_RR_TYPE_NSEC)
+			  rrset->rrset->rk.flags = PACKED_RRSET_NSEC_AT_APEX;
+			if(rrtype == LDNS_RR_TYPE_SOA)
+				z->soa = rrset->rrset;
+		}
+	} 
+	pd = (struct packed_rrset_data*)rrset->rrset->entry.data;
+	log_assert(rrset && pd);
+
+	/* check for duplicate RR */
+	if(rr_is_duplicate(pd, buf)) {
+		verbose(VERB_ALGO, "ignoring duplicate RR: %s", rrstr);
+		return 1;
+	} 
+	return insert_rr(z->region, pd, buf, ttl);
+}
+
+/** enter a data RR into auth data; a zone for it must exist */
+static int
+lz_enter_rr_str(struct local_zones* zones, const char* rr, ldns_buffer* buf)
+{
+	uint8_t* rr_name;
+	uint16_t rr_class;
+	size_t len;
+	int labs;
+	struct local_zone* z;
+	int r;
+	if(!get_rr_nameclass(rr, &rr_name, &rr_class)) {
+		log_err("bad rr %s", rr);
+		return 0;
+	}
+	labs = dname_count_size_labels(rr_name, &len);
+	lock_quick_lock(&zones->lock);
+	z = local_zones_lookup(zones, rr_name, len, labs, rr_class);
+	if(!z) {
+		lock_quick_unlock(&zones->lock);
+		fatal_exit("internal error: no zone for rr %s", rr);
+	}
+	lock_rw_wrlock(&z->lock);
+	lock_quick_unlock(&zones->lock);
+	free(rr_name);
+	r = lz_enter_rr_into_zone(z, buf, rr);
+	lock_rw_unlock(&z->lock);
+	return r;
+}
+
+/** parse local-zone: statements */
+static int
+lz_enter_zones(struct local_zones* zones, struct config_file* cfg)
+{
+	struct config_str2list* p;
+	struct local_zone* z;
+	for(p = cfg->local_zones; p; p = p->next) {
+		if(!(z=lz_enter_zone(zones, p->str, p->str2, 
+			LDNS_RR_CLASS_IN)))
+			return 0;
+		lock_rw_unlock(&z->lock);
+	}
+	return 1;
+}
+
+/** lookup a zone in rbtree; exact match only; SLOW due to parse */
+static int
+lz_exists(struct local_zones* zones, const char* name)
+{
+	struct local_zone z;
+	z.node.key = &z;
+	z.dclass = LDNS_RR_CLASS_IN;
+	if(!parse_dname(name, &z.name, &z.namelen, &z.namelabs)) {
+		log_err("bad name %s", name);
+		return 0;
+	}
+	lock_quick_lock(&zones->lock);
+	if(rbtree_search(&zones->ztree, &z.node)) {
+		lock_quick_unlock(&zones->lock);
+		free(z.name);
+		return 1;
+	}
+	lock_quick_unlock(&zones->lock);
+	free(z.name);
+	return 0;
+}
+
+/** lookup a zone in cfg->nodefault list */
+static int
+lz_nodefault(struct config_file* cfg, const char* name)
+{
+	struct config_strlist* p;
+	size_t len = strlen(name);
+	if(len == 0) return 0;
+	if(name[len-1] == '.') len--;
+
+	for(p = cfg->local_zones_nodefault; p; p = p->next) {
+		/* compare zone name, lowercase, compare without ending . */
+		if(strncasecmp(p->str, name, len) == 0 && 
+			(strlen(p->str) == len || (strlen(p->str)==len+1 &&
+			p->str[len] == '.')))
+			return 1;
+	}
+	return 0;
+}
+
+/** enter AS112 default zone */
+static int
+add_as112_default(struct local_zones* zones, struct config_file* cfg,
+        ldns_buffer* buf, const char* name)
+{
+	struct local_zone* z;
+	char str[1024]; /* known long enough */
+	if(lz_exists(zones, name) || lz_nodefault(cfg, name))
+		return 1; /* do not enter default content */
+	if(!(z=lz_enter_zone(zones, name, "static", LDNS_RR_CLASS_IN)))
+		return 0;
+	snprintf(str, sizeof(str), "%s 10800 IN SOA localhost. "
+		"nobody.invalid. 1 3600 1200 604800 10800", name);
+	if(!lz_enter_rr_into_zone(z, buf, str)) {
+		lock_rw_unlock(&z->lock);
+		return 0;
+	}
+	snprintf(str, sizeof(str), "%s 10800 IN NS localhost. ", name);
+	if(!lz_enter_rr_into_zone(z, buf, str)) {
+		lock_rw_unlock(&z->lock);
+		return 0;
+	}
+	lock_rw_unlock(&z->lock);
+	return 1;
+}
+
+/** enter default zones */
+static int
+lz_enter_defaults(struct local_zones* zones, struct config_file* cfg,
+	ldns_buffer* buf)
+{
+	struct local_zone* z;
+
+	/* this list of zones is from RFC 6303 */
+
+	/* localhost. zone */
+	if(!lz_exists(zones, "localhost.") &&
+		!lz_nodefault(cfg, "localhost.")) {
+		if(!(z=lz_enter_zone(zones, "localhost.", "static", 
+			LDNS_RR_CLASS_IN)) ||
+		   !lz_enter_rr_into_zone(z, buf,
+			"localhost. 10800 IN NS localhost.") ||
+		   !lz_enter_rr_into_zone(z, buf,
+			"localhost. 10800 IN SOA localhost. nobody.invalid. "
+			"1 3600 1200 604800 10800") ||
+		   !lz_enter_rr_into_zone(z, buf,
+			"localhost. 10800 IN A 127.0.0.1") ||
+		   !lz_enter_rr_into_zone(z, buf,
+			"localhost. 10800 IN AAAA ::1")) {
+			log_err("out of memory adding default zone");
+			if(z) { lock_rw_unlock(&z->lock); }
+			return 0;
+		}
+		lock_rw_unlock(&z->lock);
+	}
+	/* reverse ip4 zone */
+	if(!lz_exists(zones, "127.in-addr.arpa.") &&
+		!lz_nodefault(cfg, "127.in-addr.arpa.")) {
+		if(!(z=lz_enter_zone(zones, "127.in-addr.arpa.", "static", 
+			LDNS_RR_CLASS_IN)) ||
+		   !lz_enter_rr_into_zone(z, buf,
+			"127.in-addr.arpa. 10800 IN NS localhost.") ||
+		   !lz_enter_rr_into_zone(z, buf,
+			"127.in-addr.arpa. 10800 IN SOA localhost. "
+			"nobody.invalid. 1 3600 1200 604800 10800") ||
+		   !lz_enter_rr_into_zone(z, buf,
+			"1.0.0.127.in-addr.arpa. 10800 IN PTR localhost.")) {
+			log_err("out of memory adding default zone");
+			if(z) { lock_rw_unlock(&z->lock); }
+			return 0;
+		}
+		lock_rw_unlock(&z->lock);
+	}
+	/* reverse ip6 zone */
+	if(!lz_exists(zones, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.") &&
+		!lz_nodefault(cfg, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.")) {
+		if(!(z=lz_enter_zone(zones, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.", "static", 
+			LDNS_RR_CLASS_IN)) ||
+		   !lz_enter_rr_into_zone(z, buf,
+			"1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN NS localhost.") ||
+		   !lz_enter_rr_into_zone(z, buf,
+			"1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN SOA localhost. "
+			"nobody.invalid. 1 3600 1200 604800 10800") ||
+		   !lz_enter_rr_into_zone(z, buf,
+			"1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN PTR localhost.")) {
+			log_err("out of memory adding default zone");
+			if(z) { lock_rw_unlock(&z->lock); }
+			return 0;
+		}
+		lock_rw_unlock(&z->lock);
+	}
+	if (	!add_as112_default(zones, cfg, buf, "10.in-addr.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "16.172.in-addr.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "17.172.in-addr.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "18.172.in-addr.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "19.172.in-addr.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "20.172.in-addr.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "21.172.in-addr.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "22.172.in-addr.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "23.172.in-addr.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "24.172.in-addr.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "25.172.in-addr.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "26.172.in-addr.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "27.172.in-addr.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "28.172.in-addr.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "29.172.in-addr.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "30.172.in-addr.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "31.172.in-addr.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "168.192.in-addr.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "0.in-addr.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "254.169.in-addr.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "2.0.192.in-addr.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "100.51.198.in-addr.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "113.0.203.in-addr.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "255.255.255.255.in-addr.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "d.f.ip6.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "8.e.f.ip6.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "9.e.f.ip6.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "a.e.f.ip6.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "b.e.f.ip6.arpa.") ||
+		!add_as112_default(zones, cfg, buf, "8.b.d.0.1.0.0.2.ip6.arpa.")) {
+		log_err("out of memory adding default zone");
+		return 0;
+	}
+	return 1;
+}
+
+/** setup parent pointers, so that a lookup can be done for closest match */
+static void
+init_parents(struct local_zones* zones)
+{
+        struct local_zone* node, *prev = NULL, *p;
+        int m;
+	lock_quick_lock(&zones->lock);
+        RBTREE_FOR(node, struct local_zone*, &zones->ztree) {
+		lock_rw_wrlock(&node->lock);
+                node->parent = NULL;
+                if(!prev || prev->dclass != node->dclass) {
+                        prev = node;
+			lock_rw_unlock(&node->lock);
+                        continue;
+                }
+                (void)dname_lab_cmp(prev->name, prev->namelabs, node->name,
+                        node->namelabs, &m); /* we know prev is smaller */
+                /* sort order like: . com. bla.com. zwb.com. net. */
+                /* find the previous, or parent-parent-parent */
+                for(p = prev; p; p = p->parent)
+                        /* looking for name with few labels, a parent */
+                        if(p->namelabs <= m) {
+                                /* ==: since prev matched m, this is closest*/
+                                /* <: prev matches more, but is not a parent,
+                                 * this one is a (grand)parent */
+                                node->parent = p;
+                                break;
+                        }
+                prev = node;
+		lock_rw_unlock(&node->lock);
+        }
+	lock_quick_unlock(&zones->lock);
+}
+
+/** enter implicit transparent zone for local-data: without local-zone: */
+static int
+lz_setup_implicit(struct local_zones* zones, struct config_file* cfg)
+{
+	/* walk over all items that have no parent zone and find
+	 * the name that covers them all (could be the root) and
+	 * add that as a transparent zone */
+	struct config_strlist* p;
+	int have_name = 0;
+	int have_other_classes = 0;
+	uint16_t dclass = 0;
+	uint8_t* nm = 0;
+	size_t nmlen = 0;
+	int nmlabs = 0;
+	int match = 0; /* number of labels match count */
+
+	init_parents(zones); /* to enable local_zones_lookup() */
+	for(p = cfg->local_data; p; p = p->next) {
+		uint8_t* rr_name;
+		uint16_t rr_class;
+		size_t len;
+		int labs;
+		if(!get_rr_nameclass(p->str, &rr_name, &rr_class)) {
+			log_err("Bad local-data RR %s", p->str);
+			return 0;
+		}
+		labs = dname_count_size_labels(rr_name, &len);
+		lock_quick_lock(&zones->lock);
+		if(!local_zones_lookup(zones, rr_name, len, labs, rr_class)) {
+			if(!have_name) {
+				dclass = rr_class;
+				nm = rr_name;
+				nmlen = len;
+				nmlabs = labs;
+				match = labs;
+				have_name = 1;
+			} else {
+				int m;
+				if(rr_class != dclass) {
+					/* process other classes later */
+					free(rr_name);
+					have_other_classes = 1;
+					lock_quick_unlock(&zones->lock);
+					continue;
+				}
+				/* find smallest shared topdomain */
+				(void)dname_lab_cmp(nm, nmlabs, 
+					rr_name, labs, &m);
+				free(rr_name);
+				if(m < match)
+					match = m;
+			}
+		} else free(rr_name);
+		lock_quick_unlock(&zones->lock);
+	}
+	if(have_name) {
+		uint8_t* n2;
+		struct local_zone* z;
+		/* allocate zone of smallest shared topdomain to contain em */
+		n2 = nm;
+		dname_remove_labels(&n2, &nmlen, nmlabs - match);
+		n2 = memdup(n2, nmlen);
+		free(nm);
+		if(!n2) {
+			log_err("out of memory");
+			return 0;
+		}
+		log_nametypeclass(VERB_ALGO, "implicit transparent local-zone", 
+			n2, 0, dclass);
+		if(!(z=lz_enter_zone_dname(zones, n2, nmlen, match, 
+			local_zone_transparent, dclass))) {
+			return 0;
+		}
+		lock_rw_unlock(&z->lock);
+	}
+	if(have_other_classes) { 
+		/* restart to setup other class */
+		return lz_setup_implicit(zones, cfg);
+	}
+	return 1;
+}
+
+/** enter auth data */
+static int
+lz_enter_data(struct local_zones* zones, struct config_file* cfg,
+	ldns_buffer* buf)
+{
+	struct config_strlist* p;
+	for(p = cfg->local_data; p; p = p->next) {
+		if(!lz_enter_rr_str(zones, p->str, buf))
+			return 0;
+	}
+	return 1;
+}
+
+/** free memory from config */
+static void
+lz_freeup_cfg(struct config_file* cfg)
+{
+	config_deldblstrlist(cfg->local_zones);
+	cfg->local_zones = NULL;
+	config_delstrlist(cfg->local_zones_nodefault);
+	cfg->local_zones_nodefault = NULL;
+	config_delstrlist(cfg->local_data);
+	cfg->local_data = NULL;
+}
+
+int 
+local_zones_apply_cfg(struct local_zones* zones, struct config_file* cfg)
+{
+	ldns_buffer* buf = ldns_buffer_new(65535);
+	if(!buf) fatal_exit("cannot create temporary buffer");
+
+	/* create zones from zone statements. */
+	if(!lz_enter_zones(zones, cfg)) {
+		ldns_buffer_free(buf);
+		return 0;
+	}
+	/* apply default zones+content (unless disabled, or overridden) */
+	if(!lz_enter_defaults(zones, cfg, buf)) {
+		ldns_buffer_free(buf);
+		return 0;
+	}
+	/* create implicit transparent zone from data. */
+	if(!lz_setup_implicit(zones, cfg)) {
+		ldns_buffer_free(buf);
+		return 0;
+	}
+
+	/* setup parent ptrs for lookup during data entry */
+	init_parents(zones);
+	/* insert local data */
+	if(!lz_enter_data(zones, cfg, buf)) {
+		ldns_buffer_free(buf);
+		return 0;
+	}
+	/* freeup memory from cfg struct. */
+	lz_freeup_cfg(cfg);
+	ldns_buffer_free(buf);
+	return 1;
+}
+
+struct local_zone* 
+local_zones_lookup(struct local_zones* zones,
+        uint8_t* name, size_t len, int labs, uint16_t dclass)
+{
+	rbnode_t* res = NULL;
+	struct local_zone *result;
+	struct local_zone key;
+	key.node.key = &key;
+	key.dclass = dclass;
+	key.name = name;
+	key.namelen = len;
+	key.namelabs = labs;
+	if(rbtree_find_less_equal(&zones->ztree, &key, &res)) {
+		/* exact */
+		return (struct local_zone*)res;
+	} else {
+	        /* smaller element (or no element) */
+                int m;
+                result = (struct local_zone*)res;
+                if(!result || result->dclass != dclass)
+                        return NULL;
+                /* count number of labels matched */
+                (void)dname_lab_cmp(result->name, result->namelabs, key.name,
+                        key.namelabs, &m);
+                while(result) { /* go up until qname is subdomain of zone */
+                        if(result->namelabs <= m)
+                                break;
+                        result = result->parent;
+                }
+		return result;
+	}
+}
+
+struct local_zone* 
+local_zones_find(struct local_zones* zones,
+        uint8_t* name, size_t len, int labs, uint16_t dclass)
+{
+	struct local_zone key;
+	key.node.key = &key;
+	key.dclass = dclass;
+	key.name = name;
+	key.namelen = len;
+	key.namelabs = labs;
+	/* exact */
+	return (struct local_zone*)rbtree_search(&zones->ztree, &key);
+}
+
+/** print all RRsets in local zone */
+static void 
+local_zone_out(struct local_zone* z)
+{
+	struct local_data* d;
+	struct local_rrset* p;
+	RBTREE_FOR(d, struct local_data*, &z->data) {
+		for(p = d->rrsets; p; p = p->next) {
+			log_nametypeclass(0, "rrset", d->name, 
+				ntohs(p->rrset->rk.type),
+				ntohs(p->rrset->rk.rrset_class));
+		}
+	}
+}
+
+void local_zones_print(struct local_zones* zones)
+{
+	struct local_zone* z;
+	lock_quick_lock(&zones->lock);
+	log_info("number of auth zones %u", (unsigned)zones->ztree.count);
+	RBTREE_FOR(z, struct local_zone*, &zones->ztree) {
+		lock_rw_rdlock(&z->lock);
+		switch(z->type) {
+		case local_zone_deny:
+			log_nametypeclass(0, "deny zone", 
+				z->name, 0, z->dclass);
+			break;
+		case local_zone_refuse:
+			log_nametypeclass(0, "refuse zone", 
+				z->name, 0, z->dclass);
+			break;
+		case local_zone_redirect:
+			log_nametypeclass(0, "redirect zone", 
+				z->name, 0, z->dclass);
+			break;
+		case local_zone_transparent:
+			log_nametypeclass(0, "transparent zone", 
+				z->name, 0, z->dclass);
+			break;
+		case local_zone_typetransparent:
+			log_nametypeclass(0, "typetransparent zone", 
+				z->name, 0, z->dclass);
+			break;
+		case local_zone_static:
+			log_nametypeclass(0, "static zone", 
+				z->name, 0, z->dclass);
+			break;
+		default:
+			log_nametypeclass(0, "badtyped zone", 
+				z->name, 0, z->dclass);
+			break;
+		}
+		local_zone_out(z);
+		lock_rw_unlock(&z->lock);
+	}
+	lock_quick_unlock(&zones->lock);
+}
+
+/** encode answer consisting of 1 rrset */
+static int
+local_encode(struct query_info* qinfo, struct edns_data* edns, 
+	ldns_buffer* buf, struct regional* temp, 
+	struct ub_packed_rrset_key* rrset, int ansec, int rcode)
+{
+	struct reply_info rep;
+	uint16_t udpsize;
+	/* make answer with time=0 for fixed TTL values */
+	memset(&rep, 0, sizeof(rep));
+	rep.flags = (uint16_t)((BIT_QR | BIT_AA | BIT_RA) | rcode);
+	rep.qdcount = 1;
+	if(ansec)
+		rep.an_numrrsets = 1;
+	else	rep.ns_numrrsets = 1;
+	rep.rrset_count = 1;
+	rep.rrsets = &rrset;
+	udpsize = edns->udp_size;
+	edns->edns_version = EDNS_ADVERTISED_VERSION;
+	edns->udp_size = EDNS_ADVERTISED_SIZE;
+	edns->ext_rcode = 0;
+	edns->bits &= EDNS_DO;
+	if(!reply_info_answer_encode(qinfo, &rep, 
+		*(uint16_t*)ldns_buffer_begin(buf),
+		ldns_buffer_read_u16_at(buf, 2),
+		buf, 0, 0, temp, udpsize, edns, 
+		(int)(edns->bits&EDNS_DO), 0))
+		error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo,
+			*(uint16_t*)ldns_buffer_begin(buf),
+		       ldns_buffer_read_u16_at(buf, 2), edns);
+	return 1;
+}
+
+/** answer local data match */
+static int
+local_data_answer(struct local_zone* z, struct query_info* qinfo,
+	struct edns_data* edns, ldns_buffer* buf, struct regional* temp,
+	int labs, struct local_data** ldp)
+{
+	struct local_data key;
+	struct local_data* ld;
+	struct local_rrset* lr;
+	key.node.key = &key;
+	key.name = qinfo->qname;
+	key.namelen = qinfo->qname_len;
+	key.namelabs = labs;
+	if(z->type == local_zone_redirect) {
+		key.name = z->name;
+		key.namelen = z->namelen;
+		key.namelabs = z->namelabs;
+	}
+	ld = (struct local_data*)rbtree_search(&z->data, &key.node);
+	*ldp = ld;
+	if(!ld) {
+		return 0;
+	}
+	lr = local_data_find_type(ld, qinfo->qtype);
+	if(!lr)
+		return 0;
+	if(z->type == local_zone_redirect) {
+		/* convert rrset name to query name; like a wildcard */
+		struct ub_packed_rrset_key r = *lr->rrset;
+		r.rk.dname = qinfo->qname;
+		r.rk.dname_len = qinfo->qname_len;
+		return local_encode(qinfo, edns, buf, temp, &r, 1, 
+			LDNS_RCODE_NOERROR);
+	}
+	return local_encode(qinfo, edns, buf, temp, lr->rrset, 1, 
+		LDNS_RCODE_NOERROR);
+}
+
+/** 
+ * answer in case where no exact match is found 
+ * @param z: zone for query
+ * @param qinfo: query
+ * @param edns: edns from query
+ * @param buf: buffer for answer.
+ * @param temp: temp region for encoding
+ * @param ld: local data, if NULL, no such name exists in localdata.
+ * @return 1 if a reply is to be sent, 0 if not.
+ */
+static int
+lz_zone_answer(struct local_zone* z, struct query_info* qinfo,
+	struct edns_data* edns, ldns_buffer* buf, struct regional* temp,
+	struct local_data* ld)
+{
+	if(z->type == local_zone_deny) {
+		/** no reply at all, signal caller by clearing buffer. */
+		ldns_buffer_clear(buf);
+		ldns_buffer_flip(buf);
+		return 1;
+	} else if(z->type == local_zone_refuse) {
+		error_encode(buf, (LDNS_RCODE_REFUSED|BIT_AA), qinfo,
+			*(uint16_t*)ldns_buffer_begin(buf),
+		       ldns_buffer_read_u16_at(buf, 2), edns);
+		return 1;
+	} else if(z->type == local_zone_static ||
+		z->type == local_zone_redirect) {
+		/* for static, reply nodata or nxdomain
+		 * for redirect, reply nodata */
+		/* no additional section processing,
+		 * cname, dname or wildcard processing,
+		 * or using closest match for NSEC.
+		 * or using closest match for returning delegation downwards
+		 */
+		int rcode = ld?LDNS_RCODE_NOERROR:LDNS_RCODE_NXDOMAIN;
+		if(z->soa)
+			return local_encode(qinfo, edns, buf, temp, 
+				z->soa, 0, rcode);
+		error_encode(buf, (rcode|BIT_AA), qinfo, 
+			*(uint16_t*)ldns_buffer_begin(buf), 
+			ldns_buffer_read_u16_at(buf, 2), edns);
+		return 1;
+	} else if(z->type == local_zone_typetransparent) {
+		/* no NODATA or NXDOMAINS for this zone type */
+		return 0;
+	}
+	/* else z->type == local_zone_transparent */
+
+	/* if the zone is transparent and the name exists, but the type
+	 * does not, then we should make this noerror/nodata */
+	if(ld && ld->rrsets) {
+		int rcode = LDNS_RCODE_NOERROR;
+		if(z->soa)
+			return local_encode(qinfo, edns, buf, temp, 
+				z->soa, 0, rcode);
+		error_encode(buf, (rcode|BIT_AA), qinfo, 
+			*(uint16_t*)ldns_buffer_begin(buf), 
+			ldns_buffer_read_u16_at(buf, 2), edns);
+		return 1;
+	}
+
+	/* stop here, and resolve further on */
+	return 0;
+}
+
+int 
+local_zones_answer(struct local_zones* zones, struct query_info* qinfo,
+	struct edns_data* edns, ldns_buffer* buf, struct regional* temp)
+{
+	/* see if query is covered by a zone,
+	 * 	if so:	- try to match (exact) local data 
+	 * 		- look at zone type for negative response. */
+	int labs = dname_count_labels(qinfo->qname);
+	struct local_data* ld;
+	struct local_zone* z;
+	int r;
+	lock_quick_lock(&zones->lock);
+	z = local_zones_lookup(zones, qinfo->qname,
+		qinfo->qname_len, labs, qinfo->qclass);
+	if(!z) {
+		lock_quick_unlock(&zones->lock);
+		return 0;
+	}
+	lock_rw_rdlock(&z->lock);
+	lock_quick_unlock(&zones->lock);
+
+	if(local_data_answer(z, qinfo, edns, buf, temp, labs, &ld)) {
+		lock_rw_unlock(&z->lock);
+		return 1;
+	}
+	r = lz_zone_answer(z, qinfo, edns, buf, temp, ld);
+	lock_rw_unlock(&z->lock);
+	return r;
+}
+
+const char* local_zone_type2str(enum localzone_type t)
+{
+	switch(t) {
+		case local_zone_deny: return "deny";
+		case local_zone_refuse: return "refuse";
+		case local_zone_redirect: return "redirect";
+		case local_zone_transparent: return "transparent";
+		case local_zone_typetransparent: return "typetransparent";
+		case local_zone_static: return "static";
+		case local_zone_nodefault: return "nodefault";
+	}
+	return "badtyped"; 
+}
+
+int local_zone_str2type(const char* type, enum localzone_type* t)
+{
+	if(strcmp(type, "deny") == 0)
+		*t = local_zone_deny;
+	else if(strcmp(type, "refuse") == 0)
+		*t = local_zone_refuse;
+	else if(strcmp(type, "static") == 0)
+		*t = local_zone_static;
+	else if(strcmp(type, "transparent") == 0)
+		*t = local_zone_transparent;
+	else if(strcmp(type, "typetransparent") == 0)
+		*t = local_zone_typetransparent;
+	else if(strcmp(type, "redirect") == 0)
+		*t = local_zone_redirect;
+	else return 0;
+	return 1;
+}
+
+/** iterate over the kiddies of the given name and set their parent ptr */
+static void
+set_kiddo_parents(struct local_zone* z, struct local_zone* match, 
+	struct local_zone* newp)
+{
+	/* both zones and z are locked already */
+	/* in the sorted rbtree, the kiddies of z are located after z */
+	/* z must be present in the tree */
+	struct local_zone* p = z;
+	p = (struct local_zone*)rbtree_next(&p->node);
+	while(p!=(struct local_zone*)RBTREE_NULL &&
+		p->dclass == z->dclass && dname_strict_subdomain(p->name,
+		p->namelabs, z->name, z->namelabs)) {
+		/* update parent ptr */
+		/* only when matches with existing parent pointer, so that
+		 * deeper child structures are not touched, i.e.
+		 * update of x, and a.x, b.x, f.b.x, g.b.x, c.x, y
+		 * gets to update a.x, b.x and c.x */
+		lock_rw_wrlock(&p->lock);
+		if(p->parent == match)
+			p->parent = newp;
+		lock_rw_unlock(&p->lock);
+		p = (struct local_zone*)rbtree_next(&p->node);
+	}
+}
+
+struct local_zone* local_zones_add_zone(struct local_zones* zones,
+	uint8_t* name, size_t len, int labs, uint16_t dclass,
+	enum localzone_type tp)
+{
+	/* create */
+	struct local_zone* z = local_zone_create(name, len, labs, tp, dclass);
+	if(!z) return NULL;
+	lock_rw_wrlock(&z->lock);
+
+	/* find the closest parent */
+	z->parent = local_zones_find(zones, name, len, labs, dclass);
+
+	/* insert into the tree */
+	if(!rbtree_insert(&zones->ztree, &z->node)) {
+		/* duplicate entry! */
+		lock_rw_unlock(&z->lock);
+		local_zone_delete(z);
+		log_err("internal: duplicate entry in local_zones_add_zone");
+		return NULL;
+	}
+
+	/* set parent pointers right */
+	set_kiddo_parents(z, z->parent, z);
+
+	lock_rw_unlock(&z->lock);
+	return z;
+}
+
+void local_zones_del_zone(struct local_zones* zones, struct local_zone* z)
+{
+	/* fix up parents in tree */
+	lock_rw_wrlock(&z->lock);
+	set_kiddo_parents(z, z, z->parent);
+
+	/* remove from tree */
+	(void)rbtree_delete(&zones->ztree, z);
+
+	/* delete the zone */
+	lock_rw_unlock(&z->lock);
+	local_zone_delete(z);
+}
+
+int
+local_zones_add_RR(struct local_zones* zones, const char* rr, ldns_buffer* buf)
+{
+	uint8_t* rr_name;
+	uint16_t rr_class;
+	size_t len;
+	int labs;
+	struct local_zone* z;
+	int r;
+	if(!get_rr_nameclass(rr, &rr_name, &rr_class)) {
+		return 0;
+	}
+	labs = dname_count_size_labels(rr_name, &len);
+	lock_quick_lock(&zones->lock);
+	z = local_zones_lookup(zones, rr_name, len, labs, rr_class);
+	if(!z) {
+		z = local_zones_add_zone(zones, rr_name, len, labs, rr_class,
+			local_zone_transparent);
+		if(!z) {
+			lock_quick_unlock(&zones->lock);
+			return 0;
+		}
+	} else {
+		free(rr_name);
+	}
+	lock_rw_wrlock(&z->lock);
+	lock_quick_unlock(&zones->lock);
+	r = lz_enter_rr_into_zone(z, buf, rr);
+	lock_rw_unlock(&z->lock);
+	return r;
+}
+
+/** returns true if the node is terminal so no deeper domain names exist */
+static int
+is_terminal(struct local_data* d)
+{
+	/* for empty nonterminals, the deeper domain names are sorted
+	 * right after them, so simply check the next name in the tree 
+	 */
+	struct local_data* n = (struct local_data*)rbtree_next(&d->node);
+	if(n == (struct local_data*)RBTREE_NULL)
+		return 1; /* last in tree, no deeper node */
+	if(dname_strict_subdomain(n->name, n->namelabs, d->name, d->namelabs))
+		return 0; /* there is a deeper node */
+	return 1;
+}
+
+/** delete empty terminals from tree when final data is deleted */
+static void 
+del_empty_term(struct local_zone* z, struct local_data* d, 
+	uint8_t* name, size_t len, int labs)
+{
+	while(d && d->rrsets == NULL && is_terminal(d)) {
+		/* is this empty nonterminal? delete */
+		/* note, no memory recycling in zone region */
+		(void)rbtree_delete(&z->data, d);
+
+		/* go up and to the next label */
+		if(dname_is_root(name))
+			return;
+		dname_remove_label(&name, &len);
+		labs--;
+		d = lz_find_node(z, name, len, labs);
+	}
+}
+
+void local_zones_del_data(struct local_zones* zones, 
+	uint8_t* name, size_t len, int labs, uint16_t dclass)
+{
+	/* find zone */
+	struct local_zone* z;
+	struct local_data* d;
+	lock_quick_lock(&zones->lock);
+	z = local_zones_lookup(zones, name, len, labs, dclass);
+	if(!z) {
+		/* no such zone, we're done */
+		lock_quick_unlock(&zones->lock);
+		return;
+	}
+	lock_rw_wrlock(&z->lock);
+	lock_quick_unlock(&zones->lock);
+
+	/* find the domain */
+	d = lz_find_node(z, name, len, labs);
+	if(d) {
+		/* no memory recycling for zone deletions ... */
+		d->rrsets = NULL;
+		/* did we delete the soa record ? */
+		if(query_dname_compare(d->name, z->name) == 0)
+			z->soa = NULL;
+
+		/* cleanup the empty nonterminals for this name */
+		del_empty_term(z, d, name, len, labs);
+	}
+
+	lock_rw_unlock(&z->lock);
+}
diff --git a/3rdParty/Unbound/src/src/services/localzone.h b/3rdParty/Unbound/src/src/services/localzone.h
new file mode 100644
index 0000000..794988e
--- /dev/null
+++ b/3rdParty/Unbound/src/src/services/localzone.h
@@ -0,0 +1,318 @@
+/*
+ * services/localzone.h - local zones authority service.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to enable local zone authority service.
+ */
+
+#ifndef SERVICES_LOCALZONE_H
+#define SERVICES_LOCALZONE_H
+#include "util/rbtree.h"
+#include "util/locks.h"
+struct ub_packed_rrset_key;
+struct regional;
+struct config_file;
+struct edns_data;
+struct query_info;
+
+/**
+ * Local zone type
+ * This type determines processing for queries that did not match
+ * local-data directly.
+ */
+enum localzone_type {
+	/** drop query */
+	local_zone_deny = 0,
+	/** answer with error */
+	local_zone_refuse,
+	/** answer nxdomain or nodata */
+	local_zone_static,
+	/** resolve normally */
+	local_zone_transparent,
+	/** do not block types at localdata names */
+	local_zone_typetransparent,
+	/** answer with data at zone apex */
+	local_zone_redirect,
+	/** remove default AS112 blocking contents for zone
+	 * nodefault is used in config not during service. */
+	local_zone_nodefault
+};
+
+/**
+ * Authoritative local zones storage, shared.
+ */
+struct local_zones {
+	/** lock on the localzone tree */
+	lock_quick_t lock;
+	/** rbtree of struct local_zone */
+	rbtree_t ztree;
+};
+
+/**
+ * Local zone. A locally served authoritative zone.
+ */
+struct local_zone {
+	/** rbtree node, key is name and class */
+	rbnode_t node;
+	/** parent zone, if any. */
+	struct local_zone* parent;
+
+	/** zone name, in uncompressed wireformat */
+	uint8_t* name;
+	/** length of zone name */
+	size_t namelen;
+	/** number of labels in zone name */
+	int namelabs;
+	/** the class of this zone. 
+	 * uses 'dclass' to not conflict with c++ keyword class. */
+	uint16_t dclass;
+
+	/** lock on the data in the structure
+	 * For the node, parent, name, namelen, namelabs, dclass, you
+	 * need to also hold the zones_tree lock to change them (or to
+	 * delete this zone) */
+	lock_rw_t lock;
+
+	/** how to process zone */
+	enum localzone_type type;
+
+	/** in this region the zone's data is allocated.
+	 * the struct local_zone itself is malloced. */
+	struct regional* region;
+	/** local data for this zone
+	 * rbtree of struct local_data */
+	rbtree_t data;
+	/** if data contains zone apex SOA data, this is a ptr to it. */
+	struct ub_packed_rrset_key* soa;
+};
+
+/**
+ * Local data. One domain name, and the RRs to go with it.
+ */
+struct local_data {
+	/** rbtree node, key is name only */
+	rbnode_t node;
+	/** domain name */
+	uint8_t* name;
+	/** length of name */
+	size_t namelen;
+	/** number of labels in name */
+	int namelabs;
+	/** the data rrsets, with different types, linked list.
+	 * If this list is NULL, the node is an empty non-terminal. */
+	struct local_rrset* rrsets;
+};
+
+/**
+ * A local data RRset
+ */
+struct local_rrset {
+	/** next in list */
+	struct local_rrset* next;
+	/** RRset data item */
+	struct ub_packed_rrset_key* rrset;
+};
+
+/**
+ * Create local zones storage
+ * @return new struct or NULL on error.
+ */
+struct local_zones* local_zones_create(void);
+
+/**
+ * Delete local zones storage
+ * @param zones: to delete.
+ */
+void local_zones_delete(struct local_zones* zones);
+
+/**
+ * Apply config settings; setup the local authoritative data. 
+ * Takes care of locking.
+ * @param zones: is set up.
+ * @param cfg: config data.
+ * @return false on error.
+ */
+int local_zones_apply_cfg(struct local_zones* zones, struct config_file* cfg);
+
+/**
+ * Compare two local_zone entries in rbtree. Sort hierarchical but not
+ * canonical
+ * @param z1: zone 1
+ * @param z2: zone 2
+ * @return: -1, 0, +1 comparison value.
+ */
+int local_zone_cmp(const void* z1, const void* z2);
+
+/**
+ * Compare two local_data entries in rbtree. Sort canonical.
+ * @param d1: data 1
+ * @param d2: data 2
+ * @return: -1, 0, +1 comparison value.
+ */
+int local_data_cmp(const void* d1, const void* d2);
+
+/**
+ * Delete one zone
+ * @param z: to delete.
+ */
+void local_zone_delete(struct local_zone* z);
+
+/**
+ * Lookup zone that contains the given name, class.
+ * User must lock the tree or result zone.
+ * @param zones: the zones tree
+ * @param name: dname to lookup
+ * @param len: length of name.
+ * @param labs: labelcount of name.
+ * @param dclass: class to lookup.
+ * @return closest local_zone or NULL if no covering zone is found.
+ */
+struct local_zone* local_zones_lookup(struct local_zones* zones, 
+	uint8_t* name, size_t len, int labs, uint16_t dclass);
+
+/**
+ * Debug helper. Print all zones 
+ * Takes care of locking.
+ * @param zones: the zones tree
+ */
+void local_zones_print(struct local_zones* zones);
+
+/**
+ * Answer authoritatively for local zones.
+ * Takes care of locking.
+ * @param zones: the stored zones (shared, read only).
+ * @param qinfo: query info (parsed).
+ * @param edns: edns info (parsed).
+ * @param buf: buffer with query ID and flags, also for reply.
+ * @param temp: temporary storage region.
+ * @return true if answer is in buffer. false if query is not answered 
+ * by authority data. If the reply should be dropped altogether, the return 
+ * value is true, but the buffer is cleared (empty).
+ */
+int local_zones_answer(struct local_zones* zones, struct query_info* qinfo,
+	struct edns_data* edns, ldns_buffer* buf, struct regional* temp);
+
+/**
+ * Parse the string into localzone type.
+ *
+ * @param str: string to parse
+ * @param t: local zone type returned here.
+ * @return 0 on parse error.
+ */
+int local_zone_str2type(const char* str, enum localzone_type* t);
+
+/**
+ * Print localzone type to a string.  Pointer to a constant string.
+ *
+ * @param t: local zone type.
+ * @return constant string that describes type.
+ */
+const char* local_zone_type2str(enum localzone_type t);
+
+/**
+ * Find zone that with exactly given name, class.
+ * User must lock the tree or result zone.
+ * @param zones: the zones tree
+ * @param name: dname to lookup
+ * @param len: length of name.
+ * @param labs: labelcount of name.
+ * @param dclass: class to lookup.
+ * @return the exact local_zone or NULL.
+ */
+struct local_zone* local_zones_find(struct local_zones* zones, 
+	uint8_t* name, size_t len, int labs, uint16_t dclass);
+
+/**
+ * Add a new zone. Caller must hold the zones lock.
+ * Adjusts the other zones as well (parent pointers) after insertion.
+ * The zone must NOT exist (returns NULL and logs error).
+ * @param zones: the zones tree
+ * @param name: dname to add
+ * @param len: length of name.
+ * @param labs: labelcount of name.
+ * @param dclass: class to add.
+ * @param tp: type.
+ * @return local_zone or NULL on error, caller must printout memory error.
+ */
+struct local_zone* local_zones_add_zone(struct local_zones* zones, 
+	uint8_t* name, size_t len, int labs, uint16_t dclass, 
+	enum localzone_type tp);
+
+/**
+ * Delete a zone. Caller must hold the zones lock.
+ * Adjusts the other zones as well (parent pointers) after insertion.
+ * @param zones: the zones tree
+ * @param zone: the zone to delete from tree. Also deletes zone from memory.
+ */
+void local_zones_del_zone(struct local_zones* zones, struct local_zone* zone);
+
+/**
+ * Add RR data into the localzone data.
+ * Looks up the zone, if no covering zone, a transparent zone with the
+ * name of the RR is created.
+ * @param zones: the zones tree. Not locked by caller.
+ * @param rr: string with on RR.
+ * @param buf: buffer for scratch.
+ * @return false on failure.
+ */
+int local_zones_add_RR(struct local_zones* zones, const char* rr, 
+	ldns_buffer* buf);
+
+/**
+ * Remove data from domain name in the tree.
+ * All types are removed. No effect if zone or name does not exist.
+ * @param zones: zones tree.
+ * @param name: dname to remove
+ * @param len: length of name.
+ * @param labs: labelcount of name.
+ * @param dclass: class to remove.
+ */
+void local_zones_del_data(struct local_zones* zones, 
+	uint8_t* name, size_t len, int labs, uint16_t dclass);
+
+
+/** 
+ * Form wireformat from text format domain name. 
+ * @param str: the domain name in text "www.example.com"
+ * @param res: resulting wireformat is stored here with malloc.
+ * @param len: length of resulting wireformat.
+ * @param labs: number of labels in resulting wireformat.
+ * @return false on error, syntax or memory. Also logged.
+ */
+int parse_dname(const char* str, uint8_t** res, size_t* len, int* labs);
+
+#endif /* SERVICES_LOCALZONE_H */
diff --git a/3rdParty/Unbound/src/src/services/mesh.c b/3rdParty/Unbound/src/src/services/mesh.c
new file mode 100644
index 0000000..f6fd288
--- /dev/null
+++ b/3rdParty/Unbound/src/src/services/mesh.c
@@ -0,0 +1,1186 @@
+/*
+ * services/mesh.c - deal with mesh of query states and handle events for that.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to assist in dealing with a mesh of
+ * query states. This mesh is supposed to be thread-specific.
+ * It consists of query states (per qname, qtype, qclass) and connections
+ * between query states and the super and subquery states, and replies to
+ * send back to clients.
+ */
+#include "config.h"
+#include <ldns/wire2host.h>
+#include "services/mesh.h"
+#include "services/outbound_list.h"
+#include "services/cache/dns.h"
+#include "util/log.h"
+#include "util/net_help.h"
+#include "util/module.h"
+#include "util/regional.h"
+#include "util/data/msgencode.h"
+#include "util/timehist.h"
+#include "util/fptr_wlist.h"
+#include "util/alloc.h"
+#include "util/config_file.h"
+
+/** subtract timers and the values do not overflow or become negative */
+static void
+timeval_subtract(struct timeval* d, const struct timeval* end, const struct timeval* start)
+{
+#ifndef S_SPLINT_S
+	time_t end_usec = end->tv_usec;
+	d->tv_sec = end->tv_sec - start->tv_sec;
+	if(end_usec < start->tv_usec) {
+		end_usec += 1000000;
+		d->tv_sec--;
+	}
+	d->tv_usec = end_usec - start->tv_usec;
+#endif
+}
+
+/** add timers and the values do not overflow or become negative */
+static void
+timeval_add(struct timeval* d, const struct timeval* add)
+{
+#ifndef S_SPLINT_S
+	d->tv_sec += add->tv_sec;
+	d->tv_usec += add->tv_usec;
+	if(d->tv_usec > 1000000 ) {
+		d->tv_usec -= 1000000;
+		d->tv_sec++;
+	}
+#endif
+}
+
+/** divide sum of timers to get average */
+static void
+timeval_divide(struct timeval* avg, const struct timeval* sum, size_t d)
+{
+#ifndef S_SPLINT_S
+	size_t leftover;
+	if(d == 0) {
+		avg->tv_sec = 0;
+		avg->tv_usec = 0;
+		return;
+	}
+	avg->tv_sec = sum->tv_sec / d;
+	avg->tv_usec = sum->tv_usec / d;
+	/* handle fraction from seconds divide */
+	leftover = sum->tv_sec - avg->tv_sec*d;
+	avg->tv_usec += (leftover*1000000)/d;
+#endif
+}
+
+/** histogram compare of time values */
+static int
+timeval_smaller(const struct timeval* x, const struct timeval* y)
+{
+#ifndef S_SPLINT_S
+	if(x->tv_sec < y->tv_sec)
+		return 1;
+	else if(x->tv_sec == y->tv_sec) {
+		if(x->tv_usec <= y->tv_usec)
+			return 1;
+		else	return 0;
+	}
+	else	return 0;
+#endif
+}
+
+int
+mesh_state_compare(const void* ap, const void* bp)
+{
+	struct mesh_state* a = (struct mesh_state*)ap;
+	struct mesh_state* b = (struct mesh_state*)bp;
+
+	if(a->s.is_priming && !b->s.is_priming)
+		return -1;
+	if(!a->s.is_priming && b->s.is_priming)
+		return 1;
+
+	if((a->s.query_flags&BIT_RD) && !(b->s.query_flags&BIT_RD))
+		return -1;
+	if(!(a->s.query_flags&BIT_RD) && (b->s.query_flags&BIT_RD))
+		return 1;
+
+	if((a->s.query_flags&BIT_CD) && !(b->s.query_flags&BIT_CD))
+		return -1;
+	if(!(a->s.query_flags&BIT_CD) && (b->s.query_flags&BIT_CD))
+		return 1;
+
+	return query_info_compare(&a->s.qinfo, &b->s.qinfo);
+}
+
+int
+mesh_state_ref_compare(const void* ap, const void* bp)
+{
+	struct mesh_state_ref* a = (struct mesh_state_ref*)ap;
+	struct mesh_state_ref* b = (struct mesh_state_ref*)bp;
+	return mesh_state_compare(a->s, b->s);
+}
+
+struct mesh_area* 
+mesh_create(struct module_stack* stack, struct module_env* env)
+{
+	struct mesh_area* mesh = calloc(1, sizeof(struct mesh_area));
+	if(!mesh) {
+		log_err("mesh area alloc: out of memory");
+		return NULL;
+	}
+	mesh->histogram = timehist_setup();
+	mesh->qbuf_bak = ldns_buffer_new(env->cfg->msg_buffer_size);
+	if(!mesh->histogram || !mesh->qbuf_bak) {
+		free(mesh);
+		log_err("mesh area alloc: out of memory");
+		return NULL;
+	}
+	mesh->mods = *stack;
+	mesh->env = env;
+	rbtree_init(&mesh->run, &mesh_state_compare);
+	rbtree_init(&mesh->all, &mesh_state_compare);
+	mesh->num_reply_addrs = 0;
+	mesh->num_reply_states = 0;
+	mesh->num_detached_states = 0;
+	mesh->num_forever_states = 0;
+	mesh->stats_jostled = 0;
+	mesh->stats_dropped = 0;
+	mesh->max_reply_states = env->cfg->num_queries_per_thread;
+	mesh->max_forever_states = (mesh->max_reply_states+1)/2;
+#ifndef S_SPLINT_S
+	mesh->jostle_max.tv_sec = (time_t)(env->cfg->jostle_time / 1000);
+	mesh->jostle_max.tv_usec = (time_t)((env->cfg->jostle_time % 1000)
+		*1000);
+#endif
+	return mesh;
+}
+
+/** help mesh delete delete mesh states */
+static void
+mesh_delete_helper(rbnode_t* n)
+{
+	struct mesh_state* mstate = (struct mesh_state*)n->key;
+	/* perform a full delete, not only 'cleanup' routine,
+	 * because other callbacks expect a clean state in the mesh.
+	 * For 're-entrant' calls */
+	mesh_state_delete(&mstate->s);
+	/* but because these delete the items from the tree, postorder
+	 * traversal and rbtree rebalancing do not work together */
+}
+
+void 
+mesh_delete(struct mesh_area* mesh)
+{
+	if(!mesh)
+		return;
+	/* free all query states */
+	while(mesh->all.count)
+		mesh_delete_helper(mesh->all.root);
+	timehist_delete(mesh->histogram);
+	ldns_buffer_free(mesh->qbuf_bak);
+	free(mesh);
+}
+
+void
+mesh_delete_all(struct mesh_area* mesh)
+{
+	/* free all query states */
+	while(mesh->all.count)
+		mesh_delete_helper(mesh->all.root);
+	mesh->stats_dropped += mesh->num_reply_addrs;
+	/* clear mesh area references */
+	rbtree_init(&mesh->run, &mesh_state_compare);
+	rbtree_init(&mesh->all, &mesh_state_compare);
+	mesh->num_reply_addrs = 0;
+	mesh->num_reply_states = 0;
+	mesh->num_detached_states = 0;
+	mesh->num_forever_states = 0;
+	mesh->forever_first = NULL;
+	mesh->forever_last = NULL;
+	mesh->jostle_first = NULL;
+	mesh->jostle_last = NULL;
+}
+
+int mesh_make_new_space(struct mesh_area* mesh, ldns_buffer* qbuf)
+{
+	struct mesh_state* m = mesh->jostle_first;
+	/* free space is available */
+	if(mesh->num_reply_states < mesh->max_reply_states)
+		return 1;
+	/* try to kick out a jostle-list item */
+	if(m && m->reply_list && m->list_select == mesh_jostle_list) {
+		/* how old is it? */
+		struct timeval age;
+		timeval_subtract(&age, mesh->env->now_tv, 
+			&m->reply_list->start_time);
+		if(timeval_smaller(&mesh->jostle_max, &age)) {
+			/* its a goner */
+			log_nametypeclass(VERB_ALGO, "query jostled out to "
+				"make space for a new one",
+				m->s.qinfo.qname, m->s.qinfo.qtype,
+				m->s.qinfo.qclass);
+			/* backup the query */
+			if(qbuf) ldns_buffer_copy(mesh->qbuf_bak, qbuf);
+			/* notify supers */
+			if(m->super_set.count > 0) {
+				verbose(VERB_ALGO, "notify supers of failure");
+				m->s.return_msg = NULL;
+				m->s.return_rcode = LDNS_RCODE_SERVFAIL;
+				mesh_walk_supers(mesh, m);
+			}
+			mesh->stats_jostled ++;
+			mesh_state_delete(&m->s);
+			/* restore the query - note that the qinfo ptr to
+			 * the querybuffer is then correct again. */
+			if(qbuf) ldns_buffer_copy(qbuf, mesh->qbuf_bak);
+			return 1;
+		}
+	}
+	/* no space for new item */
+	return 0;
+}
+
+void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
+        uint16_t qflags, struct edns_data* edns, struct comm_reply* rep,
+        uint16_t qid)
+{
+	/* do not use CD flag from user for mesh state, we want the CD-query
+	 * to receive validation anyway, to protect out cache contents and
+	 * avoid bad-data in this cache that a downstream validator cannot
+	 * remove from this cache */
+	struct mesh_state* s = mesh_area_find(mesh, qinfo, qflags&BIT_RD, 0);
+	int was_detached = 0;
+	int was_noreply = 0;
+	int added = 0;
+	/* does this create a new reply state? */
+	if(!s || s->list_select == mesh_no_list) {
+		if(!mesh_make_new_space(mesh, rep->c->buffer)) {
+			verbose(VERB_ALGO, "Too many queries. dropping "
+				"incoming query.");
+			comm_point_drop_reply(rep);
+			mesh->stats_dropped ++;
+			return;
+		}
+		/* for this new reply state, the reply address is free,
+		 * so the limit of reply addresses does not stop reply states*/
+	} else {
+		/* protect our memory usage from storing reply addresses */
+		if(mesh->num_reply_addrs > mesh->max_reply_states*16) {
+			verbose(VERB_ALGO, "Too many requests queued. "
+				"dropping incoming query.");
+			mesh->stats_dropped++;
+			comm_point_drop_reply(rep);
+			return;
+		}
+	}
+	/* see if it already exists, if not, create one */
+	if(!s) {
+#ifdef UNBOUND_DEBUG
+		struct rbnode_t* n;
+#endif
+		s = mesh_state_create(mesh->env, qinfo, qflags&BIT_RD, 0);
+		if(!s) {
+			log_err("mesh_state_create: out of memory; SERVFAIL");
+			error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL,
+				qinfo, qid, qflags, edns);
+			comm_point_send_reply(rep);
+			return;
+		}
+#ifdef UNBOUND_DEBUG
+		n =
+#endif
+		rbtree_insert(&mesh->all, &s->node);
+		log_assert(n != NULL);
+		/* set detached (it is now) */
+		mesh->num_detached_states++;
+		added = 1;
+	}
+	if(!s->reply_list && !s->cb_list && s->super_set.count == 0)
+		was_detached = 1;
+	if(!s->reply_list && !s->cb_list)
+		was_noreply = 1;
+	/* add reply to s */
+	if(!mesh_state_add_reply(s, edns, rep, qid, qflags, qinfo->qname)) {
+			log_err("mesh_new_client: out of memory; SERVFAIL");
+			error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL,
+				qinfo, qid, qflags, edns);
+			comm_point_send_reply(rep);
+			if(added)
+				mesh_state_delete(&s->s);
+			return;
+	}
+	/* update statistics */
+	if(was_detached) {
+		log_assert(mesh->num_detached_states > 0);
+		mesh->num_detached_states--;
+	}
+	if(was_noreply) {
+		mesh->num_reply_states ++;
+	}
+	mesh->num_reply_addrs++;
+	if(s->list_select == mesh_no_list) {
+		/* move to either the forever or the jostle_list */
+		if(mesh->num_forever_states < mesh->max_forever_states) {
+			mesh->num_forever_states ++;
+			mesh_list_insert(s, &mesh->forever_first, 
+				&mesh->forever_last);
+			s->list_select = mesh_forever_list;
+		} else {
+			mesh_list_insert(s, &mesh->jostle_first, 
+				&mesh->jostle_last);
+			s->list_select = mesh_jostle_list;
+		}
+	}
+	if(added)
+		mesh_run(mesh, s, module_event_new, NULL);
+}
+
+int 
+mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
+	uint16_t qflags, struct edns_data* edns, ldns_buffer* buf, 
+	uint16_t qid, mesh_cb_func_t cb, void* cb_arg)
+{
+	struct mesh_state* s = mesh_area_find(mesh, qinfo, qflags&BIT_RD, 0);
+	int was_detached = 0;
+	int was_noreply = 0;
+	int added = 0;
+	/* there are no limits on the number of callbacks */
+
+	/* see if it already exists, if not, create one */
+	if(!s) {
+#ifdef UNBOUND_DEBUG
+		struct rbnode_t* n;
+#endif
+		s = mesh_state_create(mesh->env, qinfo, qflags&BIT_RD, 0);
+		if(!s) {
+			return 0;
+		}
+#ifdef UNBOUND_DEBUG
+		n =
+#endif
+		rbtree_insert(&mesh->all, &s->node);
+		log_assert(n != NULL);
+		/* set detached (it is now) */
+		mesh->num_detached_states++;
+		added = 1;
+	}
+	if(!s->reply_list && !s->cb_list && s->super_set.count == 0)
+		was_detached = 1;
+	if(!s->reply_list && !s->cb_list)
+		was_noreply = 1;
+	/* add reply to s */
+	if(!mesh_state_add_cb(s, edns, buf, cb, cb_arg, qid, qflags)) {
+			if(added)
+				mesh_state_delete(&s->s);
+			return 0;
+	}
+	/* update statistics */
+	if(was_detached) {
+		log_assert(mesh->num_detached_states > 0);
+		mesh->num_detached_states--;
+	}
+	if(was_noreply) {
+		mesh->num_reply_states ++;
+	}
+	mesh->num_reply_addrs++;
+	if(added)
+		mesh_run(mesh, s, module_event_new, NULL);
+	return 1;
+}
+
+void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo,
+        uint16_t qflags, uint32_t leeway)
+{
+	struct mesh_state* s = mesh_area_find(mesh, qinfo, qflags&BIT_RD, 0);
+#ifdef UNBOUND_DEBUG
+	struct rbnode_t* n;
+#endif
+	/* already exists, and for a different purpose perhaps.
+	 * if mesh_no_list, keep it that way. */
+	if(s) {
+		/* make it ignore the cache from now on */
+		if(!s->s.blacklist)
+			sock_list_insert(&s->s.blacklist, NULL, 0, s->s.region);
+		if(s->s.prefetch_leeway < leeway)
+			s->s.prefetch_leeway = leeway;
+		return;
+	}
+	if(!mesh_make_new_space(mesh, NULL)) {
+		verbose(VERB_ALGO, "Too many queries. dropped prefetch.");
+		mesh->stats_dropped ++;
+		return;
+	}
+	s = mesh_state_create(mesh->env, qinfo, qflags&BIT_RD, 0);
+	if(!s) {
+		log_err("prefetch mesh_state_create: out of memory");
+		return;
+	}
+#ifdef UNBOUND_DEBUG
+	n =
+#endif
+	rbtree_insert(&mesh->all, &s->node);
+	log_assert(n != NULL);
+	/* set detached (it is now) */
+	mesh->num_detached_states++;
+	/* make it ignore the cache */
+	sock_list_insert(&s->s.blacklist, NULL, 0, s->s.region);
+	s->s.prefetch_leeway = leeway;
+
+	if(s->list_select == mesh_no_list) {
+		/* move to either the forever or the jostle_list */
+		if(mesh->num_forever_states < mesh->max_forever_states) {
+			mesh->num_forever_states ++;
+			mesh_list_insert(s, &mesh->forever_first, 
+				&mesh->forever_last);
+			s->list_select = mesh_forever_list;
+		} else {
+			mesh_list_insert(s, &mesh->jostle_first, 
+				&mesh->jostle_last);
+			s->list_select = mesh_jostle_list;
+		}
+	}
+	mesh_run(mesh, s, module_event_new, NULL);
+}
+
+void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e,
+        struct comm_reply* reply, int what)
+{
+	enum module_ev event = module_event_reply;
+	e->qstate->reply = reply;
+	if(what != NETEVENT_NOERROR) {
+		event = module_event_noreply;
+		if(what == NETEVENT_CAPSFAIL)
+			event = module_event_capsfail;
+	}
+	mesh_run(mesh, e->qstate->mesh_info, event, e);
+}
+
+struct mesh_state* 
+mesh_state_create(struct module_env* env, struct query_info* qinfo, 
+	uint16_t qflags, int prime)
+{
+	struct regional* region = alloc_reg_obtain(env->alloc);
+	struct mesh_state* mstate;
+	int i;
+	if(!region)
+		return NULL;
+	mstate = (struct mesh_state*)regional_alloc(region, 
+		sizeof(struct mesh_state));
+	if(!mstate) {
+		alloc_reg_release(env->alloc, region);
+		return NULL;
+	}
+	memset(mstate, 0, sizeof(*mstate));
+	mstate->node = *RBTREE_NULL;
+	mstate->run_node = *RBTREE_NULL;
+	mstate->node.key = mstate;
+	mstate->run_node.key = mstate;
+	mstate->reply_list = NULL;
+	mstate->list_select = mesh_no_list;
+	mstate->replies_sent = 0;
+	rbtree_init(&mstate->super_set, &mesh_state_ref_compare);
+	rbtree_init(&mstate->sub_set, &mesh_state_ref_compare);
+	mstate->num_activated = 0;
+	/* init module qstate */
+	mstate->s.qinfo.qtype = qinfo->qtype;
+	mstate->s.qinfo.qclass = qinfo->qclass;
+	mstate->s.qinfo.qname_len = qinfo->qname_len;
+	mstate->s.qinfo.qname = regional_alloc_init(region, qinfo->qname,
+		qinfo->qname_len);
+	if(!mstate->s.qinfo.qname) {
+		alloc_reg_release(env->alloc, region);
+		return NULL;
+	}
+	/* remove all weird bits from qflags */
+	mstate->s.query_flags = (qflags & (BIT_RD|BIT_CD));
+	mstate->s.is_priming = prime;
+	mstate->s.reply = NULL;
+	mstate->s.region = region;
+	mstate->s.curmod = 0;
+	mstate->s.return_msg = 0;
+	mstate->s.return_rcode = LDNS_RCODE_NOERROR;
+	mstate->s.env = env;
+	mstate->s.mesh_info = mstate;
+	mstate->s.prefetch_leeway = 0;
+	/* init modules */
+	for(i=0; i<env->mesh->mods.num; i++) {
+		mstate->s.minfo[i] = NULL;
+		mstate->s.ext_state[i] = module_state_initial;
+	}
+	return mstate;
+}
+
+void 
+mesh_state_cleanup(struct mesh_state* mstate)
+{
+	struct mesh_area* mesh;
+	int i;
+	if(!mstate)
+		return;
+	mesh = mstate->s.env->mesh;
+	/* drop unsent replies */
+	if(!mstate->replies_sent) {
+		struct mesh_reply* rep;
+		struct mesh_cb* cb;
+		for(rep=mstate->reply_list; rep; rep=rep->next) {
+			comm_point_drop_reply(&rep->query_reply);
+			mesh->num_reply_addrs--;
+		}
+		for(cb=mstate->cb_list; cb; cb=cb->next) {
+			fptr_ok(fptr_whitelist_mesh_cb(cb->cb));
+			(*cb->cb)(cb->cb_arg, LDNS_RCODE_SERVFAIL, NULL,
+				sec_status_unchecked, NULL);
+			mesh->num_reply_addrs--;
+		}
+	}
+
+	/* de-init modules */
+	for(i=0; i<mesh->mods.num; i++) {
+		fptr_ok(fptr_whitelist_mod_clear(mesh->mods.mod[i]->clear));
+		(*mesh->mods.mod[i]->clear)(&mstate->s, i);
+		mstate->s.minfo[i] = NULL;
+		mstate->s.ext_state[i] = module_finished;
+	}
+	alloc_reg_release(mstate->s.env->alloc, mstate->s.region);
+}
+
+void 
+mesh_state_delete(struct module_qstate* qstate)
+{
+	struct mesh_area* mesh;
+	struct mesh_state_ref* super, ref;
+	struct mesh_state* mstate;
+	if(!qstate)
+		return;
+	mstate = qstate->mesh_info;
+	mesh = mstate->s.env->mesh;
+	mesh_detach_subs(&mstate->s);
+	if(mstate->list_select == mesh_forever_list) {
+		mesh->num_forever_states --;
+		mesh_list_remove(mstate, &mesh->forever_first, 
+			&mesh->forever_last);
+	} else if(mstate->list_select == mesh_jostle_list) {
+		mesh_list_remove(mstate, &mesh->jostle_first, 
+			&mesh->jostle_last);
+	}
+	if(!mstate->reply_list && !mstate->cb_list
+		&& mstate->super_set.count == 0) {
+		log_assert(mesh->num_detached_states > 0);
+		mesh->num_detached_states--;
+	}
+	if(mstate->reply_list || mstate->cb_list) {
+		log_assert(mesh->num_reply_states > 0);
+		mesh->num_reply_states--;
+	}
+	ref.node.key = &ref;
+	ref.s = mstate;
+	RBTREE_FOR(super, struct mesh_state_ref*, &mstate->super_set) {
+		(void)rbtree_delete(&super->s->sub_set, &ref);
+	}
+	(void)rbtree_delete(&mesh->run, mstate);
+	(void)rbtree_delete(&mesh->all, mstate);
+	mesh_state_cleanup(mstate);
+}
+
+/** helper recursive rbtree find routine */
+static int
+find_in_subsub(struct mesh_state* m, struct mesh_state* tofind, size_t *c)
+{
+	struct mesh_state_ref* r;
+	if((*c)++ > MESH_MAX_SUBSUB)
+		return 1;
+	RBTREE_FOR(r, struct mesh_state_ref*, &m->sub_set) {
+		if(r->s == tofind || find_in_subsub(r->s, tofind, c))
+			return 1;
+	}
+	return 0;
+}
+
+/** find cycle for already looked up mesh_state */
+static int 
+mesh_detect_cycle_found(struct module_qstate* qstate, struct mesh_state* dep_m)
+{
+	struct mesh_state* cyc_m = qstate->mesh_info;
+	size_t counter = 0;
+	if(!dep_m)
+		return 0;
+	if(dep_m == cyc_m || find_in_subsub(dep_m, cyc_m, &counter)) {
+		if(counter > MESH_MAX_SUBSUB)
+			return 2;
+		return 1;
+	}
+	return 0;
+}
+
+void mesh_detach_subs(struct module_qstate* qstate)
+{
+	struct mesh_area* mesh = qstate->env->mesh;
+	struct mesh_state_ref* ref, lookup;
+#ifdef UNBOUND_DEBUG
+	struct rbnode_t* n;
+#endif
+	lookup.node.key = &lookup;
+	lookup.s = qstate->mesh_info;
+	RBTREE_FOR(ref, struct mesh_state_ref*, &qstate->mesh_info->sub_set) {
+#ifdef UNBOUND_DEBUG
+		n =
+#endif
+		rbtree_delete(&ref->s->super_set, &lookup);
+		log_assert(n != NULL); /* must have been present */
+		if(!ref->s->reply_list && !ref->s->cb_list
+			&& ref->s->super_set.count == 0) {
+			mesh->num_detached_states++;
+			log_assert(mesh->num_detached_states + 
+				mesh->num_reply_states <= mesh->all.count);
+		}
+	}
+	rbtree_init(&qstate->mesh_info->sub_set, &mesh_state_ref_compare);
+}
+
+int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo,
+        uint16_t qflags, int prime, struct module_qstate** newq)
+{
+	/* find it, if not, create it */
+	struct mesh_area* mesh = qstate->env->mesh;
+	struct mesh_state* sub = mesh_area_find(mesh, qinfo, qflags, prime);
+	if(mesh_detect_cycle_found(qstate, sub)) {
+		verbose(VERB_ALGO, "attach failed, cycle detected");
+		return 0;
+	}
+	if(!sub) {
+#ifdef UNBOUND_DEBUG
+		struct rbnode_t* n;
+#endif
+		/* create a new one */
+		sub = mesh_state_create(qstate->env, qinfo, qflags, prime);
+		if(!sub) {
+			log_err("mesh_attach_sub: out of memory");
+			return 0;
+		}
+#ifdef UNBOUND_DEBUG
+		n =
+#endif
+		rbtree_insert(&mesh->all, &sub->node);
+		log_assert(n != NULL);
+		/* set detached (it is now) */
+		mesh->num_detached_states++;
+		/* set new query state to run */
+#ifdef UNBOUND_DEBUG
+		n =
+#endif
+		rbtree_insert(&mesh->run, &sub->run_node);
+		log_assert(n != NULL);
+		*newq = &sub->s;
+	} else
+		*newq = NULL;
+	if(!mesh_state_attachment(qstate->mesh_info, sub))
+		return 0;
+	if(!sub->reply_list && !sub->cb_list && sub->super_set.count == 1) {
+		/* it used to be detached, before this one got added */
+		log_assert(mesh->num_detached_states > 0);
+		mesh->num_detached_states--;
+	}
+	/* *newq will be run when inited after the current module stops */
+	return 1;
+}
+
+int mesh_state_attachment(struct mesh_state* super, struct mesh_state* sub)
+{
+#ifdef UNBOUND_DEBUG
+	struct rbnode_t* n;
+#endif
+	struct mesh_state_ref* subref; /* points to sub, inserted in super */
+	struct mesh_state_ref* superref; /* points to super, inserted in sub */
+	if( !(subref = regional_alloc(super->s.region,
+		sizeof(struct mesh_state_ref))) ||
+		!(superref = regional_alloc(sub->s.region,
+		sizeof(struct mesh_state_ref))) ) {
+		log_err("mesh_state_attachment: out of memory");
+		return 0;
+	}
+	superref->node.key = superref;
+	superref->s = super;
+	subref->node.key = subref;
+	subref->s = sub;
+#ifdef UNBOUND_DEBUG
+	n =
+#endif
+	rbtree_insert(&sub->super_set, &superref->node);
+	log_assert(n != NULL);
+#ifdef UNBOUND_DEBUG
+	n =
+#endif
+	rbtree_insert(&super->sub_set, &subref->node);
+	log_assert(n != NULL);
+	return 1;
+}
+
+/**
+ * callback results to mesh cb entry
+ * @param m: mesh state to send it for.
+ * @param rcode: if not 0, error code.
+ * @param rep: reply to send (or NULL if rcode is set).
+ * @param r: callback entry
+ */
+static void
+mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep,
+	struct mesh_cb* r)
+{
+	int secure;
+	char* reason = NULL;
+	/* bogus messages are not made into servfail, sec_status passed 
+	 * to the callback function */
+	if(rep && rep->security == sec_status_secure)
+		secure = 1;
+	else	secure = 0;
+	if(!rep && rcode == LDNS_RCODE_NOERROR)
+		rcode = LDNS_RCODE_SERVFAIL;
+	if(!rcode && rep->security == sec_status_bogus) {
+		if(!(reason = errinf_to_str(&m->s)))
+			rcode = LDNS_RCODE_SERVFAIL;
+	}
+	/* send the reply */
+	if(rcode) {
+		fptr_ok(fptr_whitelist_mesh_cb(r->cb));
+		(*r->cb)(r->cb_arg, rcode, r->buf, sec_status_unchecked, NULL);
+	} else {
+		size_t udp_size = r->edns.udp_size;
+		ldns_buffer_clear(r->buf);
+		r->edns.edns_version = EDNS_ADVERTISED_VERSION;
+		r->edns.udp_size = EDNS_ADVERTISED_SIZE;
+		r->edns.ext_rcode = 0;
+		r->edns.bits &= EDNS_DO;
+		if(!reply_info_answer_encode(&m->s.qinfo, rep, r->qid, 
+			r->qflags, r->buf, 0, 1, 
+			m->s.env->scratch, udp_size, &r->edns, 
+			(int)(r->edns.bits & EDNS_DO), secure)) 
+		{
+			fptr_ok(fptr_whitelist_mesh_cb(r->cb));
+			(*r->cb)(r->cb_arg, LDNS_RCODE_SERVFAIL, r->buf,
+				sec_status_unchecked, NULL);
+		} else {
+			fptr_ok(fptr_whitelist_mesh_cb(r->cb));
+			(*r->cb)(r->cb_arg, LDNS_RCODE_NOERROR, r->buf,
+				rep->security, reason);
+		}
+	}
+	free(reason);
+	m->s.env->mesh->num_reply_addrs--;
+}
+
+/**
+ * Send reply to mesh reply entry
+ * @param m: mesh state to send it for.
+ * @param rcode: if not 0, error code.
+ * @param rep: reply to send (or NULL if rcode is set).
+ * @param r: reply entry
+ * @param prev: previous reply, already has its answer encoded in buffer.
+ */
+static void
+mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
+	struct mesh_reply* r, struct mesh_reply* prev)
+{
+	struct timeval end_time;
+	struct timeval duration;
+	int secure;
+	/* examine security status */
+	if(m->s.env->need_to_validate && (!(r->qflags&BIT_CD) ||
+		m->s.env->cfg->ignore_cd) && rep && 
+		rep->security <= sec_status_bogus) {
+		rcode = LDNS_RCODE_SERVFAIL;
+		if(m->s.env->cfg->stat_extended) 
+			m->s.env->mesh->ans_bogus++;
+	}
+	if(rep && rep->security == sec_status_secure)
+		secure = 1;
+	else	secure = 0;
+	if(!rep && rcode == LDNS_RCODE_NOERROR)
+		rcode = LDNS_RCODE_SERVFAIL;
+	/* send the reply */
+	if(prev && prev->qflags == r->qflags && 
+		prev->edns.edns_present == r->edns.edns_present && 
+		prev->edns.bits == r->edns.bits && 
+		prev->edns.udp_size == r->edns.udp_size) {
+		/* if the previous reply is identical to this one, fix ID */
+		if(prev->query_reply.c->buffer != r->query_reply.c->buffer)
+			ldns_buffer_copy(r->query_reply.c->buffer, 
+				prev->query_reply.c->buffer);
+		ldns_buffer_write_at(r->query_reply.c->buffer, 0, 
+			&r->qid, sizeof(uint16_t));
+		ldns_buffer_write_at(r->query_reply.c->buffer, 12, 
+			r->qname, m->s.qinfo.qname_len);
+		comm_point_send_reply(&r->query_reply);
+	} else if(rcode) {
+		m->s.qinfo.qname = r->qname;
+		error_encode(r->query_reply.c->buffer, rcode, &m->s.qinfo,
+			r->qid, r->qflags, &r->edns);
+		comm_point_send_reply(&r->query_reply);
+	} else {
+		size_t udp_size = r->edns.udp_size;
+		r->edns.edns_version = EDNS_ADVERTISED_VERSION;
+		r->edns.udp_size = EDNS_ADVERTISED_SIZE;
+		r->edns.ext_rcode = 0;
+		r->edns.bits &= EDNS_DO;
+		m->s.qinfo.qname = r->qname;
+		if(!reply_info_answer_encode(&m->s.qinfo, rep, r->qid, 
+			r->qflags, r->query_reply.c->buffer, 0, 1, 
+			m->s.env->scratch, udp_size, &r->edns, 
+			(int)(r->edns.bits & EDNS_DO), secure)) 
+		{
+			error_encode(r->query_reply.c->buffer, 
+				LDNS_RCODE_SERVFAIL, &m->s.qinfo, r->qid, 
+				r->qflags, &r->edns);
+		}
+		comm_point_send_reply(&r->query_reply);
+	}
+	/* account */
+	m->s.env->mesh->num_reply_addrs--;
+	end_time = *m->s.env->now_tv;
+	timeval_subtract(&duration, &end_time, &r->start_time);
+	verbose(VERB_ALGO, "query took %d.%6.6d sec",
+		(int)duration.tv_sec, (int)duration.tv_usec);
+	m->s.env->mesh->replies_sent++;
+	timeval_add(&m->s.env->mesh->replies_sum_wait, &duration);
+	timehist_insert(m->s.env->mesh->histogram, &duration);
+	if(m->s.env->cfg->stat_extended) {
+		uint16_t rc = FLAGS_GET_RCODE(ldns_buffer_read_u16_at(r->
+			query_reply.c->buffer, 2));
+		if(secure) m->s.env->mesh->ans_secure++;
+		m->s.env->mesh->ans_rcode[ rc ] ++;
+		if(rc == 0 && LDNS_ANCOUNT(ldns_buffer_begin(r->
+			query_reply.c->buffer)) == 0)
+			m->s.env->mesh->ans_nodata++;
+	}
+}
+
+void mesh_query_done(struct mesh_state* mstate)
+{
+	struct mesh_reply* r;
+	struct mesh_reply* prev = NULL;
+	struct mesh_cb* c;
+	struct reply_info* rep = (mstate->s.return_msg?
+		mstate->s.return_msg->rep:NULL);
+	for(r = mstate->reply_list; r; r = r->next) {
+		mesh_send_reply(mstate, mstate->s.return_rcode, rep, r, prev);
+		prev = r;
+	}
+	mstate->replies_sent = 1;
+	for(c = mstate->cb_list; c; c = c->next) {
+		mesh_do_callback(mstate, mstate->s.return_rcode, rep, c);
+	}
+}
+
+void mesh_walk_supers(struct mesh_area* mesh, struct mesh_state* mstate)
+{
+	struct mesh_state_ref* ref;
+	RBTREE_FOR(ref, struct mesh_state_ref*, &mstate->super_set)
+	{
+		/* make super runnable */
+		(void)rbtree_insert(&mesh->run, &ref->s->run_node);
+		/* callback the function to inform super of result */
+		fptr_ok(fptr_whitelist_mod_inform_super(
+			mesh->mods.mod[ref->s->s.curmod]->inform_super));
+		(*mesh->mods.mod[ref->s->s.curmod]->inform_super)(&mstate->s, 
+			ref->s->s.curmod, &ref->s->s);
+	}
+}
+
+struct mesh_state* mesh_area_find(struct mesh_area* mesh,
+	struct query_info* qinfo, uint16_t qflags, int prime)
+{
+	struct mesh_state key;
+	struct mesh_state* result;
+
+	key.node.key = &key;
+	key.s.is_priming = prime;
+	key.s.qinfo = *qinfo;
+	key.s.query_flags = qflags;
+	
+	result = (struct mesh_state*)rbtree_search(&mesh->all, &key);
+	return result;
+}
+
+int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns,
+        ldns_buffer* buf, mesh_cb_func_t cb, void* cb_arg,
+	uint16_t qid, uint16_t qflags)
+{
+	struct mesh_cb* r = regional_alloc(s->s.region, 
+		sizeof(struct mesh_cb));
+	if(!r)
+		return 0;
+	r->buf = buf;
+	log_assert(fptr_whitelist_mesh_cb(cb)); /* early failure ifmissing*/
+	r->cb = cb;
+	r->cb_arg = cb_arg;
+	r->edns = *edns;
+	r->qid = qid;
+	r->qflags = qflags;
+	r->next = s->cb_list;
+	s->cb_list = r;
+	return 1;
+
+}
+
+int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
+        struct comm_reply* rep, uint16_t qid, uint16_t qflags, uint8_t* qname)
+{
+	struct mesh_reply* r = regional_alloc(s->s.region, 
+		sizeof(struct mesh_reply));
+	if(!r)
+		return 0;
+	r->query_reply = *rep;
+	r->edns = *edns;
+	r->qid = qid;
+	r->qflags = qflags;
+	r->start_time = *s->s.env->now_tv;
+	r->next = s->reply_list;
+	r->qname = regional_alloc_init(s->s.region, qname, 
+		s->s.qinfo.qname_len);
+	if(!r->qname)
+		return 0;
+	s->reply_list = r;
+	return 1;
+
+}
+
+/**
+ * Continue processing the mesh state at another module.
+ * Handles module to modules tranfer of control.
+ * Handles module finished.
+ * @param mesh: the mesh area.
+ * @param mstate: currently active mesh state.
+ * 	Deleted if finished, calls _done and _supers to 
+ * 	send replies to clients and inform other mesh states.
+ * 	This in turn may create additional runnable mesh states.
+ * @param s: state at which the current module exited.
+ * @param ev: the event sent to the module.
+ * 	returned is the event to send to the next module.
+ * @return true if continue processing at the new module.
+ * 	false if not continued processing is needed.
+ */
+static int
+mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate,
+	enum module_ext_state s, enum module_ev* ev)
+{
+	mstate->num_activated++;
+	if(mstate->num_activated > MESH_MAX_ACTIVATION) {
+		/* module is looping. Stop it. */
+		log_err("internal error: looping module stopped");
+		log_query_info(VERB_QUERY, "pass error for qstate",
+			&mstate->s.qinfo);
+		s = module_error;
+	}
+	if(s == module_wait_module || s == module_restart_next) {
+		/* start next module */
+		mstate->s.curmod++;
+		if(mesh->mods.num == mstate->s.curmod) {
+			log_err("Cannot pass to next module; at last module");
+			log_query_info(VERB_QUERY, "pass error for qstate",
+				&mstate->s.qinfo);
+			mstate->s.curmod--;
+			return mesh_continue(mesh, mstate, module_error, ev);
+		}
+		if(s == module_restart_next) {
+			fptr_ok(fptr_whitelist_mod_clear(
+				mesh->mods.mod[mstate->s.curmod]->clear));
+			(*mesh->mods.mod[mstate->s.curmod]->clear)
+				(&mstate->s, mstate->s.curmod);
+			mstate->s.minfo[mstate->s.curmod] = NULL;
+		}
+		*ev = module_event_pass;
+		return 1;
+	}
+	if(s == module_error && mstate->s.return_rcode == LDNS_RCODE_NOERROR) {
+		/* error is bad, handle pass back up below */
+		mstate->s.return_rcode = LDNS_RCODE_SERVFAIL;
+	}
+	if(s == module_error || s == module_finished) {
+		if(mstate->s.curmod == 0) {
+			mesh_query_done(mstate);
+			mesh_walk_supers(mesh, mstate);
+			mesh_state_delete(&mstate->s);
+			return 0;
+		}
+		/* pass along the locus of control */
+		mstate->s.curmod --;
+		*ev = module_event_moddone;
+		return 1;
+	}
+	return 0;
+}
+
+void mesh_run(struct mesh_area* mesh, struct mesh_state* mstate,
+	enum module_ev ev, struct outbound_entry* e)
+{
+	enum module_ext_state s;
+	verbose(VERB_ALGO, "mesh_run: start");
+	while(mstate) {
+		/* run the module */
+		fptr_ok(fptr_whitelist_mod_operate(
+			mesh->mods.mod[mstate->s.curmod]->operate));
+		(*mesh->mods.mod[mstate->s.curmod]->operate)
+			(&mstate->s, ev, mstate->s.curmod, e);
+
+		/* examine results */
+		mstate->s.reply = NULL;
+		regional_free_all(mstate->s.env->scratch);
+		s = mstate->s.ext_state[mstate->s.curmod];
+		verbose(VERB_ALGO, "mesh_run: %s module exit state is %s", 
+			mesh->mods.mod[mstate->s.curmod]->name, strextstate(s));
+		e = NULL;
+		if(mesh_continue(mesh, mstate, s, &ev))
+			continue;
+
+		/* run more modules */
+		ev = module_event_pass;
+		if(mesh->run.count > 0) {
+			/* pop random element off the runnable tree */
+			mstate = (struct mesh_state*)mesh->run.root->key;
+			(void)rbtree_delete(&mesh->run, mstate);
+		} else mstate = NULL;
+	}
+	if(verbosity >= VERB_ALGO) {
+		mesh_stats(mesh, "mesh_run: end");
+		mesh_log_list(mesh);
+	}
+}
+
+void 
+mesh_log_list(struct mesh_area* mesh)
+{
+	char buf[30];
+	struct mesh_state* m;
+	int num = 0;
+	RBTREE_FOR(m, struct mesh_state*, &mesh->all) {
+		snprintf(buf, sizeof(buf), "%d%s%s%s%s%s mod%d %s%s", 
+			num++, (m->s.is_priming)?"p":"",  /* prime */
+			(m->s.query_flags&BIT_RD)?"RD":"",
+			(m->s.query_flags&BIT_CD)?"CD":"",
+			(m->super_set.count==0)?"d":"", /* detached */
+			(m->sub_set.count!=0)?"c":"",  /* children */
+			m->s.curmod, (m->reply_list)?"rep":"", /*hasreply*/
+			(m->cb_list)?"cb":"" /* callbacks */
+			); 
+		log_query_info(VERB_ALGO, buf, &m->s.qinfo);
+	}
+}
+
+void 
+mesh_stats(struct mesh_area* mesh, const char* str)
+{
+	verbose(VERB_DETAIL, "%s %u recursion states (%u with reply, "
+		"%u detached), %u waiting replies, %u recursion replies "
+		"sent, %d replies dropped, %d states jostled out", 
+		str, (unsigned)mesh->all.count, 
+		(unsigned)mesh->num_reply_states,
+		(unsigned)mesh->num_detached_states,
+		(unsigned)mesh->num_reply_addrs,
+		(unsigned)mesh->replies_sent,
+		(unsigned)mesh->stats_dropped,
+		(unsigned)mesh->stats_jostled);
+	if(mesh->replies_sent > 0) {
+		struct timeval avg;
+		timeval_divide(&avg, &mesh->replies_sum_wait, 
+			mesh->replies_sent);
+		log_info("average recursion processing time "
+			"%d.%6.6d sec", (int)avg.tv_sec, (int)avg.tv_usec);
+		log_info("histogram of recursion processing times");
+		timehist_log(mesh->histogram, "recursions");
+	}
+}
+
+void 
+mesh_stats_clear(struct mesh_area* mesh)
+{
+	if(!mesh)
+		return;
+	mesh->replies_sent = 0;
+	mesh->replies_sum_wait.tv_sec = 0;
+	mesh->replies_sum_wait.tv_usec = 0;
+	mesh->stats_jostled = 0;
+	mesh->stats_dropped = 0;
+	timehist_clear(mesh->histogram);
+	mesh->ans_secure = 0;
+	mesh->ans_bogus = 0;
+	memset(&mesh->ans_rcode[0], 0, sizeof(size_t)*16);
+	mesh->ans_nodata = 0;
+}
+
+size_t 
+mesh_get_mem(struct mesh_area* mesh)
+{
+	struct mesh_state* m;
+	size_t s = sizeof(*mesh) + sizeof(struct timehist) +
+		sizeof(struct th_buck)*mesh->histogram->num +
+		sizeof(ldns_buffer) + ldns_buffer_capacity(mesh->qbuf_bak);
+	RBTREE_FOR(m, struct mesh_state*, &mesh->all) {
+		/* all, including m itself allocated in qstate region */
+		s += regional_get_mem(m->s.region);
+	}
+	return s;
+}
+
+int 
+mesh_detect_cycle(struct module_qstate* qstate, struct query_info* qinfo,
+	uint16_t flags, int prime)
+{
+	struct mesh_area* mesh = qstate->env->mesh;
+	struct mesh_state* dep_m = mesh_area_find(mesh, qinfo, flags, prime);
+	return mesh_detect_cycle_found(qstate, dep_m);
+}
+
+void mesh_list_insert(struct mesh_state* m, struct mesh_state** fp,
+        struct mesh_state** lp)
+{
+	/* insert as last element */
+	m->prev = *lp;
+	m->next = NULL;
+	if(*lp)
+		(*lp)->next = m;
+	else	*fp = m;
+	*lp = m;
+}
+
+void mesh_list_remove(struct mesh_state* m, struct mesh_state** fp,
+        struct mesh_state** lp)
+{
+	if(m->next)
+		m->next->prev = m->prev;
+	else	*lp = m->prev;
+	if(m->prev)
+		m->prev->next = m->next;
+	else	*fp = m->next;
+}
diff --git a/3rdParty/Unbound/src/src/services/mesh.h b/3rdParty/Unbound/src/src/services/mesh.h
new file mode 100644
index 0000000..5f10977
--- /dev/null
+++ b/3rdParty/Unbound/src/src/services/mesh.h
@@ -0,0 +1,571 @@
+/*
+ * services/mesh.h - deal with mesh of query states and handle events for that.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to assist in dealing with a mesh of
+ * query states. This mesh is supposed to be thread-specific.
+ * It consists of query states (per qname, qtype, qclass) and connections
+ * between query states and the super and subquery states, and replies to
+ * send back to clients.
+ */
+
+#ifndef SERVICES_MESH_H
+#define SERVICES_MESH_H
+
+#include "util/rbtree.h"
+#include "util/netevent.h"
+#include "util/data/msgparse.h"
+#include "util/module.h"
+#include "services/modstack.h"
+struct mesh_state;
+struct mesh_reply;
+struct mesh_cb;
+struct query_info;
+struct reply_info;
+struct outbound_entry;
+struct timehist;
+
+/**
+ * Maximum number of mesh state activations. Any more is likely an
+ * infinite loop in the module. It is then terminated.
+ */
+#define MESH_MAX_ACTIVATION 3000
+
+/**
+ * Max number of references-to-references-to-references.. search size.
+ * Any more is treated like 'too large', and the creation of a new
+ * dependency is failed (so that no loops can be created).
+ */
+#define MESH_MAX_SUBSUB 1024
+
+/** 
+ * Mesh of query states
+ */
+struct mesh_area {
+	/** active module stack */
+	struct module_stack mods;
+	/** environment for new states */
+	struct module_env* env;
+
+	/** set of runnable queries (mesh_state.run_node) */
+	rbtree_t run;
+	/** rbtree of all current queries (mesh_state.node)*/
+	rbtree_t all;
+
+	/** count of the total number of mesh_reply entries */
+	size_t num_reply_addrs;
+	/** count of the number of mesh_states that have mesh_replies 
+	 * Because a state can send results to multiple reply addresses,
+	 * this number must be equal or lower than num_reply_addrs. */
+	size_t num_reply_states;
+	/** number of mesh_states that have no mesh_replies, and also
+	 * an empty set of super-states, thus are 'toplevel' or detached
+	 * internal opportunistic queries */
+	size_t num_detached_states;
+	/** number of reply states in the forever list */
+	size_t num_forever_states;
+
+	/** max total number of reply states to have */
+	size_t max_reply_states;
+	/** max forever number of reply states to have */
+	size_t max_forever_states;
+
+	/** stats, cumulative number of reply states jostled out */
+	size_t stats_jostled;
+	/** stats, cumulative number of incoming client msgs dropped */
+	size_t stats_dropped;
+	/** number of replies sent */
+	size_t replies_sent;
+	/** sum of waiting times for the replies */
+	struct timeval replies_sum_wait;
+	/** histogram of time values */
+	struct timehist* histogram;
+	/** (extended stats) secure replies */
+	size_t ans_secure;
+	/** (extended stats) bogus replies */
+	size_t ans_bogus;
+	/** (extended stats) rcodes in replies */
+	size_t ans_rcode[16];
+	/** (extended stats) rcode nodata in replies */
+	size_t ans_nodata;
+
+	/** backup of query if other operations recurse and need the
+	 * network buffers */
+	ldns_buffer* qbuf_bak;
+
+	/** double linked list of the run-to-completion query states.
+	 * These are query states with a reply */
+	struct mesh_state* forever_first;
+	/** last entry in run forever list */
+	struct mesh_state* forever_last;
+
+	/** double linked list of the query states that can be jostled out
+	 * by new queries if too old.  These are query states with a reply */
+	struct mesh_state* jostle_first;
+	/** last entry in jostle list - this is the entry that is newest */
+	struct mesh_state* jostle_last;
+	/** timeout for jostling. if age is lower, it does not get jostled. */
+	struct timeval jostle_max;
+};
+
+/**
+ * A mesh query state
+ * Unique per qname, qtype, qclass (from the qstate).
+ * And RD / CD flag; in case a client turns it off.
+ * And priming queries are different from ordinary queries (because of hints).
+ *
+ * The entire structure is allocated in a region, this region is the qstate
+ * region. All parts (rbtree nodes etc) are also allocated in the region.
+ */
+struct mesh_state {
+	/** node in mesh_area all tree, key is this struct. Must be first. */
+	rbnode_t node;
+	/** node in mesh_area runnable tree, key is this struct */
+	rbnode_t run_node;
+	/** the query state. Note that the qinfo and query_flags 
+	 * may not change. */
+	struct module_qstate s;
+	/** the list of replies to clients for the results */
+	struct mesh_reply* reply_list;
+	/** the list of callbacks for the results */
+	struct mesh_cb* cb_list;
+	/** set of superstates (that want this state's result) 
+	 * contains struct mesh_state_ref* */
+	rbtree_t super_set;
+	/** set of substates (that this state needs to continue)
+	 * contains struct mesh_state_ref* */
+	rbtree_t sub_set;
+	/** number of activations for the mesh state */
+	size_t num_activated;
+
+	/** previous in linked list for reply states */
+	struct mesh_state* prev;
+	/** next in linked list for reply states */
+	struct mesh_state* next;
+	/** if this state is in the forever list, jostle list, or neither */
+	enum mesh_list_select { mesh_no_list, mesh_forever_list, 
+		mesh_jostle_list } list_select;
+
+	/** true if replies have been sent out (at end for alignment) */
+	uint8_t replies_sent;
+};
+
+/**
+ * Rbtree reference to a mesh_state.
+ * Used in super_set and sub_set. 
+ */
+struct mesh_state_ref {
+	/** node in rbtree for set, key is this structure */
+	rbnode_t node;
+	/** the mesh state */
+	struct mesh_state* s;
+};
+
+/**
+ * Reply to a client
+ */
+struct mesh_reply {
+	/** next in reply list */
+	struct mesh_reply* next;
+	/** the query reply destination, packet buffer and where to send. */
+	struct comm_reply query_reply;
+	/** edns data from query */
+	struct edns_data edns;
+	/** the time when request was entered */
+	struct timeval start_time;
+	/** id of query, in network byteorder. */
+	uint16_t qid;
+	/** flags of query, for reply flags */
+	uint16_t qflags;
+	/** qname from this query. len same as mesh qinfo. */
+	uint8_t* qname;
+};
+
+/** 
+ * Mesh result callback func.
+ * called as func(cb_arg, rcode, buffer_with_reply, security, why_bogus);
+ */
+typedef void (*mesh_cb_func_t)(void*, int, ldns_buffer*, enum sec_status, 
+	char*);
+
+/**
+ * Callback to result routine
+ */
+struct mesh_cb {
+	/** next in list */
+	struct mesh_cb* next;
+	/** edns data from query */
+	struct edns_data edns;
+	/** id of query, in network byteorder. */
+	uint16_t qid;
+	/** flags of query, for reply flags */
+	uint16_t qflags;
+	/** buffer for reply */
+	ldns_buffer* buf;
+
+	/** callback routine for results. if rcode != 0 buf has message.
+	 * called as cb(cb_arg, rcode, buf, sec_state);
+	 */
+	mesh_cb_func_t cb;
+	/** user arg for callback */
+	void* cb_arg;
+};
+
+/* ------------------- Functions for worker -------------------- */
+
+/**
+ * Allocate mesh, to empty.
+ * @param stack: module stack to activate, copied (as readonly reference).
+ * @param env: environment for new queries.
+ * @return mesh: the new mesh or NULL on error.
+ */
+struct mesh_area* mesh_create(struct module_stack* stack, 
+	struct module_env* env);
+
+/**
+ * Delete mesh, and all query states and replies in it.
+ * @param mesh: the mesh to delete.
+ */
+void mesh_delete(struct mesh_area* mesh);
+
+/**
+ * New query incoming from clients. Create new query state if needed, and
+ * add mesh_reply to it. Returns error to client on malloc failures.
+ * Will run the mesh area queries to process if a new query state is created.
+ *
+ * @param mesh: the mesh.
+ * @param qinfo: query from client.
+ * @param qflags: flags from client query.
+ * @param edns: edns data from client query.
+ * @param rep: where to reply to.
+ * @param qid: query id to reply with.
+ */
+void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
+	uint16_t qflags, struct edns_data* edns, struct comm_reply* rep, 
+	uint16_t qid);
+
+/**
+ * New query with callback. Create new query state if needed, and
+ * add mesh_cb to it. 
+ * Will run the mesh area queries to process if a new query state is created.
+ *
+ * @param mesh: the mesh.
+ * @param qinfo: query from client.
+ * @param qflags: flags from client query.
+ * @param edns: edns data from client query.
+ * @param buf: buffer for reply contents.
+ * @param qid: query id to reply with.
+ * @param cb: callback function.
+ * @param cb_arg: callback user arg.
+ * @return 0 on error.
+ */
+int mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
+	uint16_t qflags, struct edns_data* edns, ldns_buffer* buf, 
+	uint16_t qid, mesh_cb_func_t cb, void* cb_arg);
+
+/**
+ * New prefetch message. Create new query state if needed.
+ * Will run the mesh area queries to process if a new query state is created.
+ *
+ * @param mesh: the mesh.
+ * @param qinfo: query from client.
+ * @param qflags: flags from client query.
+ * @param leeway: TTL leeway what to expire earlier for this update.
+ */
+void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo,
+	uint16_t qflags, uint32_t leeway);
+
+/**
+ * Handle new event from the wire. A serviced query has returned.
+ * The query state will be made runnable, and the mesh_area will process
+ * query states until processing is complete.
+ *
+ * @param mesh: the query mesh.
+ * @param e: outbound entry, with query state to run and reply pointer.
+ * @param reply: the comm point reply info.
+ * @param what: NETEVENT_* error code (if not 0, what is wrong, TIMEOUT).
+ */
+void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e,
+	struct comm_reply* reply, int what);
+
+/* ------------------- Functions for module environment --------------- */
+
+/**
+ * Detach-subqueries.
+ * Remove all sub-query references from this query state.
+ * Keeps super-references of those sub-queries correct.
+ * Updates stat items in mesh_area structure.
+ * @param qstate: used to find mesh state.
+ */
+void mesh_detach_subs(struct module_qstate* qstate);
+
+/**
+ * Attach subquery.
+ * Creates it if it does not exist already.
+ * Keeps sub and super references correct.
+ * Performs a cycle detection - for double check - and fails if there is one.
+ * Also fails if the sub-sub-references become too large.
+ * Updates stat items in mesh_area structure.
+ * Pass if it is priming query or not.
+ * return:
+ * 	o if error (malloc) happened.
+ * 	o need to initialise the new state (module init; it is a new state).
+ * 	  so that the next run of the query with this module is successful.
+ * 	o no init needed, attachment successful.
+ *
+ * @param qstate: the state to find mesh state, and that wants to receive
+ * 	the results from the new subquery.
+ * @param qinfo: what to query for (copied).
+ * @param qflags: what flags to use (RD / CD flag or not).
+ * @param prime: if it is a (stub) priming query.
+ * @param newq: If the new subquery needs initialisation, it is returned,
+ * 	otherwise NULL is returned.
+ * @return: false on error, true if success (and init may be needed).
+ */
+int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo,
+	uint16_t qflags, int prime, struct module_qstate** newq);
+
+/**
+ * Query state is done, send messages to reply entries.
+ * Encode messages using reply entry values and the querystate (with original
+ * qinfo), using given reply_info.
+ * Pass errcode != 0 if an error reply is needed.
+ * If no reply entries, nothing is done.
+ * Must be called before a module can module_finished or return module_error.
+ * The module must handle the super query states itself as well.
+ *
+ * @param mstate: mesh state that is done. return_rcode and return_msg
+ * 	are used for replies.
+ * 	return_rcode: if not 0 (NOERROR) an error is sent back (and 
+ * 		return_msg is ignored).
+ * 	return_msg: reply to encode and send back to clients.
+ */
+void mesh_query_done(struct mesh_state* mstate);
+
+/**
+ * Call inform_super for the super query states that are interested in the 
+ * results from this query state. These can then be changed for error 
+ * or results.
+ * Called when a module is module_finished or returns module_error.
+ * The super query states become runnable with event module_event_pass,
+ * it calls the current module for the super with the inform_super event.
+ *
+ * @param mesh: mesh area to add newly runnable modules to.
+ * @param mstate: the state that has results, used to find mesh state.
+ */
+void mesh_walk_supers(struct mesh_area* mesh, struct mesh_state* mstate);
+
+/**
+ * Delete mesh state, cleanup and also rbtrees and so on.
+ * Will detach from all super/subnodes.
+ * @param qstate: to remove.
+ */
+void mesh_state_delete(struct module_qstate* qstate);
+
+/* ------------------- Functions for mesh -------------------- */
+
+/**
+ * Create and initialize a new mesh state and its query state
+ * Does not put the mesh state into rbtrees and so on.
+ * @param env: module environment to set.
+ * @param qinfo: query info that the mesh is for.
+ * @param qflags: flags for query (RD / CD flag).
+ * @param prime: if true, it is a priming query, set is_priming on mesh state.
+ * @return: new mesh state or NULL on allocation error.
+ */
+struct mesh_state* mesh_state_create(struct module_env* env, 
+	struct query_info* qinfo, uint16_t qflags, int prime);
+
+/**
+ * Cleanup a mesh state and its query state. Does not do rbtree or 
+ * reference cleanup.
+ * @param mstate: mesh state to cleanup. Its pointer may no longer be used
+ * 	afterwards. Cleanup rbtrees before calling this function.
+ */
+void mesh_state_cleanup(struct mesh_state* mstate);
+
+/**
+ * Delete all mesh states from the mesh.
+ * @param mesh: the mesh area to clear
+ */
+void mesh_delete_all(struct mesh_area* mesh);
+
+/**
+ * Find a mesh state in the mesh area. Pass relevant flags.
+ *
+ * @param mesh: the mesh area to look in.
+ * @param qinfo: what query
+ * @param qflags: if RD / CD bit is set or not.
+ * @param prime: if it is a priming query.
+ * @return: mesh state or NULL if not found.
+ */
+struct mesh_state* mesh_area_find(struct mesh_area* mesh, 
+	struct query_info* qinfo, uint16_t qflags, int prime);
+
+/**
+ * Setup attachment super/sub relation between super and sub mesh state.
+ * The relation must not be present when calling the function.
+ * Does not update stat items in mesh_area.
+ * @param super: super state.
+ * @param sub: sub state.
+ * @return: 0 on alloc error.
+ */
+int mesh_state_attachment(struct mesh_state* super, struct mesh_state* sub);
+
+/**
+ * Create new reply structure and attach it to a mesh state.
+ * Does not update stat items in mesh area.
+ * @param s: the mesh state.
+ * @param edns: edns data for reply (bufsize).
+ * @param rep: comm point reply info.
+ * @param qid: ID of reply.
+ * @param qflags: original query flags.
+ * @param qname: original query name.
+ * @return: 0 on alloc error.
+ */
+int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns, 
+	struct comm_reply* rep, uint16_t qid, uint16_t qflags, uint8_t* qname);
+
+/**
+ * Create new callback structure and attach it to a mesh state.
+ * Does not update stat items in mesh area.
+ * @param s: the mesh state.
+ * @param edns: edns data for reply (bufsize).
+ * @param buf: buffer for reply
+ * @param cb: callback to call with results.
+ * @param cb_arg: callback user arg.
+ * @param qid: ID of reply.
+ * @param qflags: original query flags.
+ * @return: 0 on alloc error.
+ */
+int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns,
+        ldns_buffer* buf, mesh_cb_func_t cb, void* cb_arg, uint16_t qid, 
+	uint16_t qflags);
+
+/**
+ * Run the mesh. Run all runnable mesh states. Which can create new
+ * runnable mesh states. Until completion. Automatically called by
+ * mesh_report_reply and mesh_new_client as needed.
+ * @param mesh: mesh area.
+ * @param mstate: first mesh state to run.
+ * @param ev: event the mstate. Others get event_pass.
+ * @param e: if a reply, its outbound entry.
+ */
+void mesh_run(struct mesh_area* mesh, struct mesh_state* mstate, 
+	enum module_ev ev, struct outbound_entry* e);
+
+/**
+ * Print some stats about the mesh to the log.
+ * @param mesh: the mesh to print it for.
+ * @param str: descriptive string to go with it.
+ */
+void mesh_stats(struct mesh_area* mesh, const char* str);
+
+/**
+ * Clear the stats that the mesh keeps (number of queries serviced)
+ * @param mesh: the mesh
+ */
+void mesh_stats_clear(struct mesh_area* mesh);
+
+/**
+ * Print all the states in the mesh to the log.
+ * @param mesh: the mesh to print all states of.
+ */
+void mesh_log_list(struct mesh_area* mesh);
+
+/**
+ * Calculate memory size in use by mesh and all queries inside it.
+ * @param mesh: the mesh to examine.
+ * @return size in bytes.
+ */
+size_t mesh_get_mem(struct mesh_area* mesh);
+
+/**
+ * Find cycle; see if the given mesh is in the targets sub, or sub-sub, ...
+ * trees.
+ * If the sub-sub structure is too large, it returns 'a cycle'=2.
+ * @param qstate: given mesh querystate.
+ * @param qinfo: query info for dependency.
+ * @param flags: query flags of dependency.
+ * @param prime: if dependency is a priming query or not.
+ * @return true if the name,type,class exists and the given qstate mesh exists
+ * 	as a dependency of that name. Thus if qstate becomes dependent on
+ * 	name,type,class then a cycle is created, this is return value 1.
+ * 	Too large to search is value 2 (also true).
+ */
+int mesh_detect_cycle(struct module_qstate* qstate, struct query_info* qinfo,
+	uint16_t flags, int prime);
+
+/** compare two mesh_states */
+int mesh_state_compare(const void* ap, const void* bp);
+
+/** compare two mesh references */
+int mesh_state_ref_compare(const void* ap, const void* bp);
+
+/**
+ * Make space for another recursion state for a reply in the mesh
+ * @param mesh: mesh area
+ * @param qbuf: query buffer to save if recursion is invoked to make space.
+ *    This buffer is necessary, because the following sequence in calls
+ *    can result in an overwrite of the incoming query:
+ *    delete_other_mesh_query - iter_clean - serviced_delete - waiting
+ *    udp query is sent - on error callback - callback sends SERVFAIL reply
+ *    over the same network channel, and shared UDP buffer is overwritten.
+ *    You can pass NULL if there is no buffer that must be backed up.
+ * @return false if no space is available.
+ */
+int mesh_make_new_space(struct mesh_area* mesh, ldns_buffer* qbuf);
+
+/**
+ * Insert mesh state into a double linked list.  Inserted at end.
+ * @param m: mesh state.
+ * @param fp: pointer to the first-elem-pointer of the list.
+ * @param lp: pointer to the last-elem-pointer of the list.
+ */
+void mesh_list_insert(struct mesh_state* m, struct mesh_state** fp,
+	struct mesh_state** lp);
+
+/**
+ * Remove mesh state from a double linked list.  Remove from any position.
+ * @param m: mesh state.
+ * @param fp: pointer to the first-elem-pointer of the list.
+ * @param lp: pointer to the last-elem-pointer of the list.
+ */
+void mesh_list_remove(struct mesh_state* m, struct mesh_state** fp,
+	struct mesh_state** lp);
+
+#endif /* SERVICES_MESH_H */
diff --git a/3rdParty/Unbound/src/src/services/modstack.c b/3rdParty/Unbound/src/src/services/modstack.c
new file mode 100644
index 0000000..7395598
--- /dev/null
+++ b/3rdParty/Unbound/src/src/services/modstack.c
@@ -0,0 +1,212 @@
+/*
+ * services/modstack.c - stack of modules
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to help maintain a stack of modules.
+ */
+#include "config.h"
+#include <ctype.h>
+#include "services/modstack.h"
+#include "util/module.h"
+#include "util/fptr_wlist.h"
+#include "iterator/iterator.h"
+#include "validator/validator.h"
+
+#ifdef WITH_PYTHONMODULE
+#include "pythonmod/pythonmod.h"
+#endif
+
+/** count number of modules (words) in the string */
+static int
+count_modules(const char* s)
+{
+        int num = 0;
+        if(!s)
+                return 0;
+        while(*s) {
+                /* skip whitespace */
+                while(*s && isspace((int)*s))
+                        s++;
+                if(*s && !isspace((int)*s)) {
+                        /* skip identifier */
+                        num++;
+                        while(*s && !isspace((int)*s))
+                                s++;
+                }
+        }
+        return num;
+}
+
+void 
+modstack_init(struct module_stack* stack)
+{
+	stack->num = 0;
+	stack->mod = NULL;
+}
+
+int 
+modstack_config(struct module_stack* stack, const char* module_conf)
+{
+        int i;
+        verbose(VERB_QUERY, "module config: \"%s\"", module_conf);
+        stack->num = count_modules(module_conf);
+        if(stack->num == 0) {
+                log_err("error: no modules specified");
+                return 0;
+        }
+        if(stack->num > MAX_MODULE) {
+                log_err("error: too many modules (%d max %d)",
+                        stack->num, MAX_MODULE);
+                return 0;
+        }
+        stack->mod = (struct module_func_block**)calloc((size_t)
+                stack->num, sizeof(struct module_func_block*));
+        if(!stack->mod) {
+                log_err("out of memory");
+                return 0;
+        }
+        for(i=0; i<stack->num; i++) {
+                stack->mod[i] = module_factory(&module_conf);
+                if(!stack->mod[i]) {
+                        log_err("Unknown value for next module: '%s'",
+                                module_conf);
+                        return 0;
+                }
+        }
+        return 1;
+}
+
+/** The list of module names */
+const char**
+module_list_avail(void)
+{
+        /* these are the modules available */
+        static const char* names[] = {
+#ifdef WITH_PYTHONMODULE
+		"python", 
+#endif
+		"validator", 
+		"iterator", 
+		NULL};
+	return names;
+}
+
+/** func block get function type */
+typedef struct module_func_block* (*fbgetfunctype)(void);
+
+/** The list of module func blocks */
+static fbgetfunctype*
+module_funcs_avail(void)
+{
+        static struct module_func_block* (*fb[])(void) = {
+#ifdef WITH_PYTHONMODULE
+		&pythonmod_get_funcblock, 
+#endif
+		&val_get_funcblock, 
+		&iter_get_funcblock, 
+		NULL};
+	return fb;
+}
+
+struct 
+module_func_block* module_factory(const char** str)
+{
+        int i = 0;
+        const char* s = *str;
+	const char** names = module_list_avail();
+	fbgetfunctype* fb = module_funcs_avail();
+        while(*s && isspace((int)*s))
+                s++;
+	while(names[i]) {
+                if(strncmp(names[i], s, strlen(names[i])) == 0) {
+                        s += strlen(names[i]);
+                        *str = s;
+                        return (*fb[i])();
+                }
+		i++;
+        }
+        return NULL;
+}
+
+int 
+modstack_setup(struct module_stack* stack, const char* module_conf,
+	struct module_env* env)
+{
+        int i;
+        if(stack->num != 0)
+                modstack_desetup(stack, env);
+        /* fixed setup of the modules */
+        if(!modstack_config(stack, module_conf)) {
+		return 0;
+        }
+        env->need_to_validate = 0; /* set by module init below */
+        for(i=0; i<stack->num; i++) {
+                verbose(VERB_OPS, "init module %d: %s",
+                        i, stack->mod[i]->name);
+                fptr_ok(fptr_whitelist_mod_init(stack->mod[i]->init));
+                if(!(*stack->mod[i]->init)(env, i)) {
+                        log_err("module init for module %s failed",
+                                stack->mod[i]->name);
+			return 0;
+                }
+        }
+	return 1;
+}
+
+void 
+modstack_desetup(struct module_stack* stack, struct module_env* env)
+{
+        int i;
+        for(i=0; i<stack->num; i++) {
+                fptr_ok(fptr_whitelist_mod_deinit(stack->mod[i]->deinit));
+                (*stack->mod[i]->deinit)(env, i);
+        }
+        stack->num = 0;
+        free(stack->mod);
+        stack->mod = NULL;
+}
+
+int 
+modstack_find(struct module_stack* stack, const char* name)
+{
+	int i;
+        for(i=0; i<stack->num; i++) {
+		if(strcmp(stack->mod[i]->name, name) == 0)
+			return i;
+	}
+	return -1;
+}
diff --git a/3rdParty/Unbound/src/src/services/modstack.h b/3rdParty/Unbound/src/src/services/modstack.h
new file mode 100644
index 0000000..869b593
--- /dev/null
+++ b/3rdParty/Unbound/src/src/services/modstack.h
@@ -0,0 +1,113 @@
+/*
+ * services/modstack.h - stack of modules
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to help maintain a stack of modules.
+ */
+
+#ifndef SERVICES_MODSTACK_H
+#define SERVICES_MODSTACK_H
+struct module_func_block;
+struct module_env;
+
+/**
+ * Stack of modules.
+ */
+struct module_stack {
+	/** the number of modules */
+	int num;
+	/** the module callbacks, array of num_modules length (ref only) */
+	struct module_func_block** mod;
+};
+
+/**
+ * Init a stack of modules
+ * @param stack: initialised as empty.
+ */
+void modstack_init(struct module_stack* stack);
+
+/**
+ * Read config file module settings and set up the modfunc block
+ * @param stack: the stack of modules (empty before call). 
+ * @param module_conf: string what modules to insert.
+ * @return false on error
+ */
+int modstack_config(struct module_stack* stack, const char* module_conf);
+
+/**
+ * Get funcblock for module name
+ * @param str: string with module name. Advanced to next value on success.
+ *	The string is assumed whitespace separated list of module names.
+ * @return funcblock or NULL on error.
+ */
+struct module_func_block* module_factory(const char** str);
+
+/**
+ * Get list of modules available.
+ * @return list of modules available. Static strings, ends with NULL.
+ */
+const char** module_list_avail(void);
+
+/**
+ * Setup modules. Assigns ids and calls module_init.
+ * @param stack: if not empty beforehand, it will be desetup()ed.
+ *	It is then modstack_configged().
+ * @param module_conf: string what modules to insert.
+ * @param env: module environment which is inited by the modules.
+ *	environment should have a superalloc, cfg,
+ *	env.need_to_validate is set by the modules.
+ * @return on false a module init failed.
+ */
+int modstack_setup(struct module_stack* stack, const char* module_conf,
+	struct module_env* env);
+
+/**
+ * Desetup the modules, deinit, delete.
+ * @param stack: made empty.
+ * @param env: module env for module deinit() calls.
+ */
+void modstack_desetup(struct module_stack* stack, struct module_env* env);
+
+/**
+ * Find index of module by name.
+ * @param stack: to look in
+ * @param name: the name to look for
+ * @return -1 on failure, otherwise index number.
+ */
+int modstack_find(struct module_stack* stack, const char* name);
+
+#endif /* SERVICES_MODSTACK_H */
diff --git a/3rdParty/Unbound/src/src/services/outbound_list.c b/3rdParty/Unbound/src/src/services/outbound_list.c
new file mode 100644
index 0000000..be49149
--- /dev/null
+++ b/3rdParty/Unbound/src/src/services/outbound_list.c
@@ -0,0 +1,89 @@
+/*
+ * services/outbound_list.c - keep list of outbound serviced queries.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to help a module keep track of the
+ * queries it has outstanding to authoritative servers.
+ */
+#include "config.h"
+#include <sys/time.h>
+#include "services/outbound_list.h"
+#include "services/outside_network.h"
+
+void 
+outbound_list_init(struct outbound_list* list)
+{
+	list->first = NULL;
+}
+
+void 
+outbound_list_clear(struct outbound_list* list)
+{
+	struct outbound_entry *p, *np;
+	p = list->first;
+	while(p) {
+		np = p->next;
+		outnet_serviced_query_stop(p->qsent, p);
+		/* in region, no free needed */
+		p = np;
+	}
+	outbound_list_init(list);
+}
+
+void 
+outbound_list_insert(struct outbound_list* list, struct outbound_entry* e)
+{
+	if(list->first)
+		list->first->prev = e;
+	e->next = list->first;
+	e->prev = NULL;
+	list->first = e;
+}
+
+void 
+outbound_list_remove(struct outbound_list* list, struct outbound_entry* e)
+{
+	if(!e)
+		return;
+	outnet_serviced_query_stop(e->qsent, e);
+	if(e->next)
+		e->next->prev = e->prev;
+	if(e->prev)
+		e->prev->next = e->next;
+	else	list->first = e->next;
+	/* in region, no free needed */
+}
diff --git a/3rdParty/Unbound/src/src/services/outbound_list.h b/3rdParty/Unbound/src/src/services/outbound_list.h
new file mode 100644
index 0000000..5631910
--- /dev/null
+++ b/3rdParty/Unbound/src/src/services/outbound_list.h
@@ -0,0 +1,105 @@
+/*
+ * services/outbound_list.h - keep list of outbound serviced queries.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to help a module keep track of the
+ * queries it has outstanding to authoritative servers.
+ */
+#ifndef SERVICES_OUTBOUND_LIST_H
+#define SERVICES_OUTBOUND_LIST_H
+struct outbound_entry;
+struct serviced_query;
+struct module_qstate;
+
+/**
+ * The outbound list. This structure is part of the module specific query
+ * state.
+ */
+struct outbound_list {
+	/** The linked list of outbound query entries. */
+	struct outbound_entry* first;
+};
+
+/**
+ * Outbound list entry. A serviced query sent by a module processing the
+ * query from the qstate. Double linked list to aid removal.
+ */
+struct outbound_entry {
+	/** next in list */
+	struct outbound_entry* next;
+	/** prev in list */
+	struct outbound_entry* prev;
+	/** The query that was sent out */
+	struct serviced_query* qsent;
+	/** the module query state that sent it */
+	struct module_qstate* qstate;
+};
+
+/**
+ * Init the user allocated outbound list structure
+ * @param list: the list structure.
+ */
+void outbound_list_init(struct outbound_list* list);
+
+/**
+ * Clear the user owner outbound list structure.
+ * Deletes serviced queries.
+ * @param list: the list structure. It is cleared, but the list struct itself
+ * 	is callers responsability to delete.
+ */
+void outbound_list_clear(struct outbound_list* list);
+
+/**
+ * Insert new entry into the list. Caller must allocate the entry with malloc.
+ * qstate and qsent are set by caller.
+ * @param list: the list to add to.
+ * @param e: entry to add, it is only half initialised at call start, fully
+ *	initialised at call end.
+ */
+void outbound_list_insert(struct outbound_list* list, 
+	struct outbound_entry* e);
+
+/**
+ * Remove an entry from the list, and deletes it. 
+ * Deletes serviced query in the entry.
+ * @param list: the list to remove from.
+ * @param e: the entry to remove.
+ */
+void outbound_list_remove(struct outbound_list* list, 
+	struct outbound_entry* e);
+
+#endif /* SERVICES_OUTBOUND_LIST_H */
diff --git a/3rdParty/Unbound/src/src/services/outside_network.c b/3rdParty/Unbound/src/src/services/outside_network.c
new file mode 100644
index 0000000..7fd408e
--- /dev/null
+++ b/3rdParty/Unbound/src/src/services/outside_network.c
@@ -0,0 +1,1990 @@
+/*
+ * services/outside_network.c - implement sending of queries and wait answer.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file has functions to send queries to authoritative servers and
+ * wait for the pending answer events.
+ */
+#include "config.h"
+#include <ctype.h>
+#ifdef HAVE_SYS_TYPES_H
+#  include <sys/types.h>
+#endif
+#include <sys/time.h>
+#include <ldns/wire2host.h>
+#include "services/outside_network.h"
+#include "services/listen_dnsport.h"
+#include "services/cache/infra.h"
+#include "util/data/msgparse.h"
+#include "util/data/msgreply.h"
+#include "util/data/msgencode.h"
+#include "util/data/dname.h"
+#include "util/netevent.h"
+#include "util/log.h"
+#include "util/net_help.h"
+#include "util/random.h"
+#include "util/fptr_wlist.h"
+#include <openssl/ssl.h>
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#include <fcntl.h>
+
+/** number of times to retry making a random ID that is unique. */
+#define MAX_ID_RETRY 1000
+/** number of times to retry finding interface, port that can be opened. */
+#define MAX_PORT_RETRY 10000
+/** number of retries on outgoing UDP queries */
+#define OUTBOUND_UDP_RETRY 1
+
+/** initiate TCP transaction for serviced query */
+static void serviced_tcp_initiate(struct outside_network* outnet, 
+	struct serviced_query* sq, ldns_buffer* buff);
+/** with a fd available, randomize and send UDP */
+static int randomize_and_send_udp(struct outside_network* outnet, 
+	struct pending* pend, ldns_buffer* packet, int timeout);
+
+int 
+pending_cmp(const void* key1, const void* key2)
+{
+	struct pending *p1 = (struct pending*)key1;
+	struct pending *p2 = (struct pending*)key2;
+	if(p1->id < p2->id)
+		return -1;
+	if(p1->id > p2->id)
+		return 1;
+	log_assert(p1->id == p2->id);
+	return sockaddr_cmp(&p1->addr, p1->addrlen, &p2->addr, p2->addrlen);
+}
+
+int 
+serviced_cmp(const void* key1, const void* key2)
+{
+	struct serviced_query* q1 = (struct serviced_query*)key1;
+	struct serviced_query* q2 = (struct serviced_query*)key2;
+	int r;
+	if(q1->qbuflen < q2->qbuflen)
+		return -1;
+	if(q1->qbuflen > q2->qbuflen)
+		return 1;
+	log_assert(q1->qbuflen == q2->qbuflen);
+	log_assert(q1->qbuflen >= 15 /* 10 header, root, type, class */);
+	/* alternate casing of qname is still the same query */
+	if((r = memcmp(q1->qbuf, q2->qbuf, 10)) != 0)
+		return r;
+	if((r = memcmp(q1->qbuf+q1->qbuflen-4, q2->qbuf+q2->qbuflen-4, 4)) != 0)
+		return r;
+	if(q1->dnssec != q2->dnssec) {
+		if(q1->dnssec < q2->dnssec)
+			return -1;
+		return 1;
+	}
+	if((r = query_dname_compare(q1->qbuf+10, q2->qbuf+10)) != 0)
+		return r;
+	return sockaddr_cmp(&q1->addr, q1->addrlen, &q2->addr, q2->addrlen);
+}
+
+/** delete waiting_tcp entry. Does not unlink from waiting list. 
+ * @param w: to delete.
+ */
+static void
+waiting_tcp_delete(struct waiting_tcp* w)
+{
+	if(!w) return;
+	if(w->timer)
+		comm_timer_delete(w->timer);
+	free(w);
+}
+
+/** 
+ * Pick random outgoing-interface of that family, and bind it.
+ * port set to 0 so OS picks a port number for us.
+ * if it is the ANY address, do not bind.
+ * @param w: tcp structure with destination address.
+ * @param s: socket fd.
+ * @return false on error, socket closed.
+ */
+static int
+pick_outgoing_tcp(struct waiting_tcp* w, int s)
+{
+	struct port_if* pi = NULL;
+	int num;
+#ifdef INET6
+	if(addr_is_ip6(&w->addr, w->addrlen))
+		num = w->outnet->num_ip6;
+	else
+#endif
+		num = w->outnet->num_ip4;
+	if(num == 0) {
+		log_err("no TCP outgoing interfaces of family");
+		log_addr(VERB_OPS, "for addr", &w->addr, w->addrlen);
+#ifndef USE_WINSOCK
+		close(s);
+#else
+		closesocket(s);
+#endif
+		return 0;
+	}
+#ifdef INET6
+	if(addr_is_ip6(&w->addr, w->addrlen))
+		pi = &w->outnet->ip6_ifs[ub_random_max(w->outnet->rnd, num)];
+	else
+#endif
+		pi = &w->outnet->ip4_ifs[ub_random_max(w->outnet->rnd, num)];
+	log_assert(pi);
+	if(addr_is_any(&pi->addr, pi->addrlen)) {
+		/* binding to the ANY interface is for listening sockets */
+		return 1;
+	}
+	/* set port to 0 */
+	if(addr_is_ip6(&pi->addr, pi->addrlen))
+		((struct sockaddr_in6*)&pi->addr)->sin6_port = 0;
+	else	((struct sockaddr_in*)&pi->addr)->sin_port = 0;
+	if(bind(s, (struct sockaddr*)&pi->addr, pi->addrlen) != 0) {
+#ifndef USE_WINSOCK
+		log_err("outgoing tcp: bind: %s", strerror(errno));
+		close(s);
+#else
+		log_err("outgoing tcp: bind: %s", 
+			wsa_strerror(WSAGetLastError()));
+		closesocket(s);
+#endif
+		return 0;
+	}
+	log_addr(VERB_ALGO, "tcp bound to src", &pi->addr, pi->addrlen);
+	return 1;
+}
+
+/** use next free buffer to service a tcp query */
+static int
+outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt, size_t pkt_len)
+{
+	struct pending_tcp* pend = w->outnet->tcp_free;
+	int s;
+	log_assert(pend);
+	log_assert(pkt);
+	log_assert(w->addrlen > 0);
+	/* open socket */
+#ifdef INET6
+	if(addr_is_ip6(&w->addr, w->addrlen))
+		s = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
+	else
+#endif
+		s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+	if(s == -1) {
+#ifndef USE_WINSOCK
+		log_err("outgoing tcp: socket: %s", strerror(errno));
+#else
+		log_err("outgoing tcp: socket: %s", 
+			wsa_strerror(WSAGetLastError()));
+#endif
+		log_addr(0, "failed address", &w->addr, w->addrlen);
+		return 0;
+	}
+	if(!pick_outgoing_tcp(w, s))
+		return 0;
+
+	fd_set_nonblock(s);
+	if(connect(s, (struct sockaddr*)&w->addr, w->addrlen) == -1) {
+#ifndef USE_WINSOCK
+#ifdef EINPROGRESS
+		if(errno != EINPROGRESS) {
+#else
+		if(1) {
+#endif
+			log_err("outgoing tcp: connect: %s", strerror(errno));
+			close(s);
+#else /* USE_WINSOCK */
+		if(WSAGetLastError() != WSAEINPROGRESS &&
+			WSAGetLastError() != WSAEWOULDBLOCK) {
+			closesocket(s);
+#endif
+			log_addr(0, "failed address", &w->addr, w->addrlen);
+			return 0;
+		}
+	}
+	if(w->outnet->sslctx && w->ssl_upstream) {
+		pend->c->ssl = outgoing_ssl_fd(w->outnet->sslctx, s);
+		if(!pend->c->ssl) {
+			pend->c->fd = s;
+			comm_point_close(pend->c);
+			return 0;
+		}
+#ifdef USE_WINSOCK
+		comm_point_tcp_win_bio_cb(pend->c, pend->c->ssl);
+#endif
+		pend->c->ssl_shake_state = comm_ssl_shake_write;
+	}
+	w->pkt = NULL;
+	w->next_waiting = (void*)pend;
+	pend->id = LDNS_ID_WIRE(pkt);
+	w->outnet->tcp_free = pend->next_free;
+	pend->next_free = NULL;
+	pend->query = w;
+	pend->c->repinfo.addrlen = w->addrlen;
+	memcpy(&pend->c->repinfo.addr, &w->addr, w->addrlen);
+	ldns_buffer_clear(pend->c->buffer);
+	ldns_buffer_write(pend->c->buffer, pkt, pkt_len);
+	ldns_buffer_flip(pend->c->buffer);
+	pend->c->tcp_is_reading = 0;
+	pend->c->tcp_byte_count = 0;
+	comm_point_start_listening(pend->c, s, -1);
+	return 1;
+}
+
+/** see if buffers can be used to service TCP queries */
+static void
+use_free_buffer(struct outside_network* outnet)
+{
+	struct waiting_tcp* w;
+	while(outnet->tcp_free && outnet->tcp_wait_first 
+		&& !outnet->want_to_quit) {
+		w = outnet->tcp_wait_first;
+		outnet->tcp_wait_first = w->next_waiting;
+		if(outnet->tcp_wait_last == w)
+			outnet->tcp_wait_last = NULL;
+		if(!outnet_tcp_take_into_use(w, w->pkt, w->pkt_len)) {
+			comm_point_callback_t* cb = w->cb;
+			void* cb_arg = w->cb_arg;
+			waiting_tcp_delete(w);
+			fptr_ok(fptr_whitelist_pending_tcp(cb));
+			(void)(*cb)(NULL, cb_arg, NETEVENT_CLOSED, NULL);
+		}
+	}
+}
+
+/** decomission a tcp buffer, closes commpoint and frees waiting_tcp entry */
+static void
+decomission_pending_tcp(struct outside_network* outnet, 
+	struct pending_tcp* pend)
+{
+	if(pend->c->ssl) {
+		SSL_shutdown(pend->c->ssl);
+		SSL_free(pend->c->ssl);
+		pend->c->ssl = NULL;
+	}
+	comm_point_close(pend->c);
+	pend->next_free = outnet->tcp_free;
+	outnet->tcp_free = pend;
+	waiting_tcp_delete(pend->query);
+	pend->query = NULL;
+	use_free_buffer(outnet);
+}
+
+int 
+outnet_tcp_cb(struct comm_point* c, void* arg, int error,
+	struct comm_reply *reply_info)
+{
+	struct pending_tcp* pend = (struct pending_tcp*)arg;
+	struct outside_network* outnet = pend->query->outnet;
+	verbose(VERB_ALGO, "outnettcp cb");
+	if(error != NETEVENT_NOERROR) {
+		verbose(VERB_QUERY, "outnettcp got tcp error %d", error);
+		/* pass error below and exit */
+	} else {
+		/* check ID */
+		if(ldns_buffer_limit(c->buffer) < sizeof(uint16_t) ||
+			LDNS_ID_WIRE(ldns_buffer_begin(c->buffer))!=pend->id) {
+			log_addr(VERB_QUERY, 
+				"outnettcp: bad ID in reply, from:",
+				&pend->query->addr, pend->query->addrlen);
+			error = NETEVENT_CLOSED;
+		}
+	}
+	fptr_ok(fptr_whitelist_pending_tcp(pend->query->cb));
+	(void)(*pend->query->cb)(c, pend->query->cb_arg, error, reply_info);
+	decomission_pending_tcp(outnet, pend);
+	return 0;
+}
+
+/** lower use count on pc, see if it can be closed */
+static void
+portcomm_loweruse(struct outside_network* outnet, struct port_comm* pc)
+{
+	struct port_if* pif;
+	pc->num_outstanding--;
+	if(pc->num_outstanding > 0) {
+		return;
+	}
+	/* close it and replace in unused list */
+	verbose(VERB_ALGO, "close of port %d", pc->number);
+	comm_point_close(pc->cp);
+	pif = pc->pif;
+	log_assert(pif->inuse > 0);
+	pif->avail_ports[pif->avail_total - pif->inuse] = pc->number;
+	pif->inuse--;
+	pif->out[pc->index] = pif->out[pif->inuse];
+	pif->out[pc->index]->index = pc->index;
+	pc->next = outnet->unused_fds;
+	outnet->unused_fds = pc;
+}
+
+/** try to send waiting UDP queries */
+static void
+outnet_send_wait_udp(struct outside_network* outnet)
+{
+	struct pending* pend;
+	/* process waiting queries */
+	while(outnet->udp_wait_first && outnet->unused_fds 
+		&& !outnet->want_to_quit) {
+		pend = outnet->udp_wait_first;
+		outnet->udp_wait_first = pend->next_waiting;
+		if(!pend->next_waiting) outnet->udp_wait_last = NULL;
+		ldns_buffer_clear(outnet->udp_buff);
+		ldns_buffer_write(outnet->udp_buff, pend->pkt, pend->pkt_len);
+		ldns_buffer_flip(outnet->udp_buff);
+		free(pend->pkt); /* freeing now makes get_mem correct */
+		pend->pkt = NULL; 
+		pend->pkt_len = 0;
+		if(!randomize_and_send_udp(outnet, pend, outnet->udp_buff, 
+			pend->timeout)) {
+			/* callback error on pending */
+			fptr_ok(fptr_whitelist_pending_udp(pend->cb));
+			(void)(*pend->cb)(outnet->unused_fds->cp, pend->cb_arg, 
+				NETEVENT_CLOSED, NULL);
+			pending_delete(outnet, pend);
+		}
+	}
+}
+
+int 
+outnet_udp_cb(struct comm_point* c, void* arg, int error,
+	struct comm_reply *reply_info)
+{
+	struct outside_network* outnet = (struct outside_network*)arg;
+	struct pending key;
+	struct pending* p;
+	verbose(VERB_ALGO, "answer cb");
+
+	if(error != NETEVENT_NOERROR) {
+		verbose(VERB_QUERY, "outnetudp got udp error %d", error);
+		return 0;
+	}
+	if(ldns_buffer_limit(c->buffer) < LDNS_HEADER_SIZE) {
+		verbose(VERB_QUERY, "outnetudp udp too short");
+		return 0;
+	}
+	log_assert(reply_info);
+
+	/* setup lookup key */
+	key.id = (unsigned)LDNS_ID_WIRE(ldns_buffer_begin(c->buffer));
+	memcpy(&key.addr, &reply_info->addr, reply_info->addrlen);
+	key.addrlen = reply_info->addrlen;
+	verbose(VERB_ALGO, "Incoming reply id = %4.4x", key.id);
+	log_addr(VERB_ALGO, "Incoming reply addr =", 
+		&reply_info->addr, reply_info->addrlen);
+
+	/* find it, see if this thing is a valid query response */
+	verbose(VERB_ALGO, "lookup size is %d entries", (int)outnet->pending->count);
+	p = (struct pending*)rbtree_search(outnet->pending, &key);
+	if(!p) {
+		verbose(VERB_QUERY, "received unwanted or unsolicited udp reply dropped.");
+		log_buf(VERB_ALGO, "dropped message", c->buffer);
+		outnet->unwanted_replies++;
+		if(outnet->unwanted_threshold && ++outnet->unwanted_total 
+			>= outnet->unwanted_threshold) {
+			log_warn("unwanted reply total reached threshold (%u)"
+				" you may be under attack."
+				" defensive action: clearing the cache",
+				(unsigned)outnet->unwanted_threshold);
+			fptr_ok(fptr_whitelist_alloc_cleanup(
+				outnet->unwanted_action));
+			(*outnet->unwanted_action)(outnet->unwanted_param);
+			outnet->unwanted_total = 0;
+		}
+		return 0;
+	}
+
+	verbose(VERB_ALGO, "received udp reply.");
+	log_buf(VERB_ALGO, "udp message", c->buffer);
+	if(p->pc->cp != c) {
+		verbose(VERB_QUERY, "received reply id,addr on wrong port. "
+			"dropped.");
+		outnet->unwanted_replies++;
+		if(outnet->unwanted_threshold && ++outnet->unwanted_total 
+			>= outnet->unwanted_threshold) {
+			log_warn("unwanted reply total reached threshold (%u)"
+				" you may be under attack."
+				" defensive action: clearing the cache",
+				(unsigned)outnet->unwanted_threshold);
+			fptr_ok(fptr_whitelist_alloc_cleanup(
+				outnet->unwanted_action));
+			(*outnet->unwanted_action)(outnet->unwanted_param);
+			outnet->unwanted_total = 0;
+		}
+		return 0;
+	}
+	comm_timer_disable(p->timer);
+	verbose(VERB_ALGO, "outnet handle udp reply");
+	/* delete from tree first in case callback creates a retry */
+	(void)rbtree_delete(outnet->pending, p->node.key);
+	fptr_ok(fptr_whitelist_pending_udp(p->cb));
+	(void)(*p->cb)(p->pc->cp, p->cb_arg, NETEVENT_NOERROR, reply_info);
+	portcomm_loweruse(outnet, p->pc);
+	pending_delete(NULL, p);
+	outnet_send_wait_udp(outnet);
+	return 0;
+}
+
+/** calculate number of ip4 and ip6 interfaces*/
+static void 
+calc_num46(char** ifs, int num_ifs, int do_ip4, int do_ip6, 
+	int* num_ip4, int* num_ip6)
+{
+	int i;
+	*num_ip4 = 0;
+	*num_ip6 = 0;
+	if(num_ifs <= 0) {
+		if(do_ip4)
+			*num_ip4 = 1;
+		if(do_ip6)
+			*num_ip6 = 1;
+		return;
+	}
+	for(i=0; i<num_ifs; i++)
+	{
+		if(str_is_ip6(ifs[i])) {
+			if(do_ip6)
+				(*num_ip6)++;
+		} else {
+			if(do_ip4)
+				(*num_ip4)++;
+		}
+	}
+
+}
+
+void 
+pending_udp_timer_cb(void *arg)
+{
+	struct pending* p = (struct pending*)arg;
+	struct outside_network* outnet = p->outnet;
+	/* it timed out */
+	verbose(VERB_ALGO, "timeout udp");
+	fptr_ok(fptr_whitelist_pending_udp(p->cb));
+	(void)(*p->cb)(p->pc->cp, p->cb_arg, NETEVENT_TIMEOUT, NULL);
+	portcomm_loweruse(outnet, p->pc);
+	pending_delete(outnet, p);
+	outnet_send_wait_udp(outnet);
+}
+
+/** create pending_tcp buffers */
+static int
+create_pending_tcp(struct outside_network* outnet, size_t bufsize)
+{
+	size_t i;
+	if(outnet->num_tcp == 0)
+		return 1; /* no tcp needed, nothing to do */
+	if(!(outnet->tcp_conns = (struct pending_tcp **)calloc(
+			outnet->num_tcp, sizeof(struct pending_tcp*))))
+		return 0;
+	for(i=0; i<outnet->num_tcp; i++) {
+		if(!(outnet->tcp_conns[i] = (struct pending_tcp*)calloc(1, 
+			sizeof(struct pending_tcp))))
+			return 0;
+		outnet->tcp_conns[i]->next_free = outnet->tcp_free;
+		outnet->tcp_free = outnet->tcp_conns[i];
+		outnet->tcp_conns[i]->c = comm_point_create_tcp_out(
+			outnet->base, bufsize, outnet_tcp_cb, 
+			outnet->tcp_conns[i]);
+		if(!outnet->tcp_conns[i]->c)
+			return 0;
+	}
+	return 1;
+}
+
+/** setup an outgoing interface, ready address */
+static int setup_if(struct port_if* pif, const char* addrstr, 
+	int* avail, int numavail, size_t numfd)
+{
+	pif->avail_total = numavail;
+	pif->avail_ports = (int*)memdup(avail, (size_t)numavail*sizeof(int));
+	if(!pif->avail_ports)
+		return 0;
+	if(!ipstrtoaddr(addrstr, UNBOUND_DNS_PORT, &pif->addr, &pif->addrlen))
+		return 0;
+	pif->maxout = (int)numfd;
+	pif->inuse = 0;
+	pif->out = (struct port_comm**)calloc(numfd, 
+		sizeof(struct port_comm*));
+	if(!pif->out)
+		return 0;
+	return 1;
+}
+
+struct outside_network* 
+outside_network_create(struct comm_base *base, size_t bufsize, 
+	size_t num_ports, char** ifs, int num_ifs, int do_ip4, 
+	int do_ip6, size_t num_tcp, struct infra_cache* infra,
+	struct ub_randstate* rnd, int use_caps_for_id, int* availports, 
+	int numavailports, size_t unwanted_threshold,
+	void (*unwanted_action)(void*), void* unwanted_param, int do_udp,
+	void* sslctx)
+{
+	struct outside_network* outnet = (struct outside_network*)
+		calloc(1, sizeof(struct outside_network));
+	size_t k;
+	if(!outnet) {
+		log_err("malloc failed");
+		return NULL;
+	}
+	comm_base_timept(base, &outnet->now_secs, &outnet->now_tv);
+	outnet->base = base;
+	outnet->num_tcp = num_tcp;
+	outnet->infra = infra;
+	outnet->rnd = rnd;
+	outnet->sslctx = sslctx;
+	outnet->svcd_overhead = 0;
+	outnet->want_to_quit = 0;
+	outnet->unwanted_threshold = unwanted_threshold;
+	outnet->unwanted_action = unwanted_action;
+	outnet->unwanted_param = unwanted_param;
+	outnet->use_caps_for_id = use_caps_for_id;
+	outnet->do_udp = do_udp;
+	if(numavailports == 0) {
+		log_err("no outgoing ports available");
+		outside_network_delete(outnet);
+		return NULL;
+	}
+#ifndef INET6
+	do_ip6 = 0;
+#endif
+	calc_num46(ifs, num_ifs, do_ip4, do_ip6, 
+		&outnet->num_ip4, &outnet->num_ip6);
+	if(outnet->num_ip4 != 0) {
+		if(!(outnet->ip4_ifs = (struct port_if*)calloc(
+			(size_t)outnet->num_ip4, sizeof(struct port_if)))) {
+			log_err("malloc failed");
+			outside_network_delete(outnet);
+			return NULL;
+		}
+	}
+	if(outnet->num_ip6 != 0) {
+		if(!(outnet->ip6_ifs = (struct port_if*)calloc(
+			(size_t)outnet->num_ip6, sizeof(struct port_if)))) {
+			log_err("malloc failed");
+			outside_network_delete(outnet);
+			return NULL;
+		}
+	}
+	if(	!(outnet->udp_buff = ldns_buffer_new(bufsize)) ||
+		!(outnet->pending = rbtree_create(pending_cmp)) ||
+		!(outnet->serviced = rbtree_create(serviced_cmp)) ||
+		!create_pending_tcp(outnet, bufsize)) {
+		log_err("malloc failed");
+		outside_network_delete(outnet);
+		return NULL;
+	}
+
+	/* allocate commpoints */
+	for(k=0; k<num_ports; k++) {
+		struct port_comm* pc;
+		pc = (struct port_comm*)calloc(1, sizeof(*pc));
+		if(!pc) {
+			log_err("malloc failed");
+			outside_network_delete(outnet);
+			return NULL;
+		}
+		pc->cp = comm_point_create_udp(outnet->base, -1, 
+			outnet->udp_buff, outnet_udp_cb, outnet);
+		if(!pc->cp) {
+			log_err("malloc failed");
+			free(pc);
+			outside_network_delete(outnet);
+			return NULL;
+		}
+		pc->next = outnet->unused_fds;
+		outnet->unused_fds = pc;
+	}
+
+	/* allocate interfaces */
+	if(num_ifs == 0) {
+		if(do_ip4 && !setup_if(&outnet->ip4_ifs[0], "0.0.0.0", 
+			availports, numavailports, num_ports)) {
+			log_err("malloc failed");
+			outside_network_delete(outnet);
+			return NULL;
+		}
+		if(do_ip6 && !setup_if(&outnet->ip6_ifs[0], "::", 
+			availports, numavailports, num_ports)) {
+			log_err("malloc failed");
+			outside_network_delete(outnet);
+			return NULL;
+		}
+	} else {
+		size_t done_4 = 0, done_6 = 0;
+		int i;
+		for(i=0; i<num_ifs; i++) {
+			if(str_is_ip6(ifs[i]) && do_ip6) {
+				if(!setup_if(&outnet->ip6_ifs[done_6], ifs[i],
+					availports, numavailports, num_ports)){
+					log_err("malloc failed");
+					outside_network_delete(outnet);
+					return NULL;
+				}
+				done_6++;
+			}
+			if(!str_is_ip6(ifs[i]) && do_ip4) {
+				if(!setup_if(&outnet->ip4_ifs[done_4], ifs[i],
+					availports, numavailports, num_ports)){
+					log_err("malloc failed");
+					outside_network_delete(outnet);
+					return NULL;
+				}
+				done_4++;
+			}
+		}
+	}
+	return outnet;
+}
+
+/** helper pending delete */
+static void
+pending_node_del(rbnode_t* node, void* arg)
+{
+	struct pending* pend = (struct pending*)node;
+	struct outside_network* outnet = (struct outside_network*)arg;
+	pending_delete(outnet, pend);
+}
+
+/** helper serviced delete */
+static void
+serviced_node_del(rbnode_t* node, void* ATTR_UNUSED(arg))
+{
+	struct serviced_query* sq = (struct serviced_query*)node;
+	struct service_callback* p = sq->cblist, *np;
+	free(sq->qbuf);
+	free(sq->zone);
+	while(p) {
+		np = p->next;
+		free(p);
+		p = np;
+	}
+	free(sq);
+}
+
+void 
+outside_network_quit_prepare(struct outside_network* outnet)
+{
+	if(!outnet)
+		return;
+	/* prevent queued items from being sent */
+	outnet->want_to_quit = 1; 
+}
+
+void 
+outside_network_delete(struct outside_network* outnet)
+{
+	if(!outnet)
+		return;
+	outnet->want_to_quit = 1;
+	/* check every element, since we can be called on malloc error */
+	if(outnet->pending) {
+		/* free pending elements, but do no unlink from tree. */
+		traverse_postorder(outnet->pending, pending_node_del, NULL);
+		free(outnet->pending);
+	}
+	if(outnet->serviced) {
+		traverse_postorder(outnet->serviced, serviced_node_del, NULL);
+		free(outnet->serviced);
+	}
+	if(outnet->udp_buff)
+		ldns_buffer_free(outnet->udp_buff);
+	if(outnet->unused_fds) {
+		struct port_comm* p = outnet->unused_fds, *np;
+		while(p) {
+			np = p->next;
+			comm_point_delete(p->cp);
+			free(p);
+			p = np;
+		}
+		outnet->unused_fds = NULL;
+	}
+	if(outnet->ip4_ifs) {
+		int i, k;
+		for(i=0; i<outnet->num_ip4; i++) {
+			for(k=0; k<outnet->ip4_ifs[i].inuse; k++) {
+				struct port_comm* pc = outnet->ip4_ifs[i].
+					out[k];
+				comm_point_delete(pc->cp);
+				free(pc);
+			}
+			free(outnet->ip4_ifs[i].avail_ports);
+			free(outnet->ip4_ifs[i].out);
+		}
+		free(outnet->ip4_ifs);
+	}
+	if(outnet->ip6_ifs) {
+		int i, k;
+		for(i=0; i<outnet->num_ip6; i++) {
+			for(k=0; k<outnet->ip6_ifs[i].inuse; k++) {
+				struct port_comm* pc = outnet->ip6_ifs[i].
+					out[k];
+				comm_point_delete(pc->cp);
+				free(pc);
+			}
+			free(outnet->ip6_ifs[i].avail_ports);
+			free(outnet->ip6_ifs[i].out);
+		}
+		free(outnet->ip6_ifs);
+	}
+	if(outnet->tcp_conns) {
+		size_t i;
+		for(i=0; i<outnet->num_tcp; i++)
+			if(outnet->tcp_conns[i]) {
+				comm_point_delete(outnet->tcp_conns[i]->c);
+				waiting_tcp_delete(outnet->tcp_conns[i]->query);
+				free(outnet->tcp_conns[i]);
+			}
+		free(outnet->tcp_conns);
+	}
+	if(outnet->tcp_wait_first) {
+		struct waiting_tcp* p = outnet->tcp_wait_first, *np;
+		while(p) {
+			np = p->next_waiting;
+			waiting_tcp_delete(p);
+			p = np;
+		}
+	}
+	if(outnet->udp_wait_first) {
+		struct pending* p = outnet->udp_wait_first, *np;
+		while(p) {
+			np = p->next_waiting;
+			pending_delete(NULL, p);
+			p = np;
+		}
+	}
+	free(outnet);
+}
+
+void 
+pending_delete(struct outside_network* outnet, struct pending* p)
+{
+	if(!p)
+		return;
+	if(outnet && outnet->udp_wait_first &&
+		(p->next_waiting || p == outnet->udp_wait_last) ) {
+		/* delete from waiting list, if it is in the waiting list */
+		struct pending* prev = NULL, *x = outnet->udp_wait_first;
+		while(x && x != p) {
+			prev = x;
+			x = x->next_waiting;
+		}
+		if(x) {
+			log_assert(x == p);
+			if(prev)
+				prev->next_waiting = p->next_waiting;
+			else	outnet->udp_wait_first = p->next_waiting;
+			if(outnet->udp_wait_last == p)
+				outnet->udp_wait_last = prev;
+		}
+	}
+	if(outnet) {
+		(void)rbtree_delete(outnet->pending, p->node.key);
+	}
+	if(p->timer)
+		comm_timer_delete(p->timer);
+	free(p->pkt);
+	free(p);
+}
+
+/**
+ * Try to open a UDP socket for outgoing communication.
+ * Sets sockets options as needed.
+ * @param addr: socket address.
+ * @param addrlen: length of address.
+ * @param port: port override for addr.
+ * @param inuse: if -1 is returned, this bool means the port was in use.
+ * @return fd or -1
+ */
+static int
+udp_sockport(struct sockaddr_storage* addr, socklen_t addrlen, int port, 
+	int* inuse)
+{
+	int fd, noproto;
+	if(addr_is_ip6(addr, addrlen)) {
+		struct sockaddr_in6* sa = (struct sockaddr_in6*)addr;
+		sa->sin6_port = (in_port_t)htons((uint16_t)port);
+		fd = create_udp_sock(AF_INET6, SOCK_DGRAM, 
+			(struct sockaddr*)addr, addrlen, 1, inuse, &noproto,
+			0, 0);
+	} else {
+		struct sockaddr_in* sa = (struct sockaddr_in*)addr;
+		sa->sin_port = (in_port_t)htons((uint16_t)port);
+		fd = create_udp_sock(AF_INET, SOCK_DGRAM, 
+			(struct sockaddr*)addr, addrlen, 1, inuse, &noproto,
+			0, 0);
+	}
+	return fd;
+}
+
+/** Select random ID */
+static int
+select_id(struct outside_network* outnet, struct pending* pend,
+	ldns_buffer* packet)
+{
+	int id_tries = 0;
+	pend->id = ((unsigned)ub_random(outnet->rnd)>>8) & 0xffff;
+	LDNS_ID_SET(ldns_buffer_begin(packet), pend->id);
+
+	/* insert in tree */
+	pend->node.key = pend;
+	while(!rbtree_insert(outnet->pending, &pend->node)) {
+		/* change ID to avoid collision */
+		pend->id = ((unsigned)ub_random(outnet->rnd)>>8) & 0xffff;
+		LDNS_ID_SET(ldns_buffer_begin(packet), pend->id);
+		id_tries++;
+		if(id_tries == MAX_ID_RETRY) {
+			pend->id=99999; /* non existant ID */
+			log_err("failed to generate unique ID, drop msg");
+			return 0;
+		}
+	}
+	verbose(VERB_ALGO, "inserted new pending reply id=%4.4x", pend->id);
+	return 1;
+}
+
+/** Select random interface and port */
+static int
+select_ifport(struct outside_network* outnet, struct pending* pend,
+	int num_if, struct port_if* ifs)
+{
+	int my_if, my_port, fd, portno, inuse, tries=0;
+	struct port_if* pif;
+	/* randomly select interface and port */
+	if(num_if == 0) {
+		verbose(VERB_QUERY, "Need to send query but have no "
+			"outgoing interfaces of that family");
+		return 0;
+	}
+	log_assert(outnet->unused_fds);
+	tries = 0;
+	while(1) {
+		my_if = ub_random_max(outnet->rnd, num_if);
+		pif = &ifs[my_if];
+		my_port = ub_random_max(outnet->rnd, pif->avail_total);
+		if(my_port < pif->inuse) {
+			/* port already open */
+			pend->pc = pif->out[my_port];
+			verbose(VERB_ALGO, "using UDP if=%d port=%d", 
+				my_if, pend->pc->number);
+			break;
+		}
+		/* try to open new port, if fails, loop to try again */
+		log_assert(pif->inuse < pif->maxout);
+		portno = pif->avail_ports[my_port - pif->inuse];
+		fd = udp_sockport(&pif->addr, pif->addrlen, portno, &inuse);
+		if(fd == -1 && !inuse) {
+			/* nonrecoverable error making socket */
+			return 0;
+		}
+		if(fd != -1) {
+			verbose(VERB_ALGO, "opened UDP if=%d port=%d", 
+				my_if, portno);
+			/* grab fd */
+			pend->pc = outnet->unused_fds;
+			outnet->unused_fds = pend->pc->next;
+
+			/* setup portcomm */
+			pend->pc->next = NULL;
+			pend->pc->number = portno;
+			pend->pc->pif = pif;
+			pend->pc->index = pif->inuse;
+			pend->pc->num_outstanding = 0;
+			comm_point_start_listening(pend->pc->cp, fd, -1);
+
+			/* grab port in interface */
+			pif->out[pif->inuse] = pend->pc;
+			pif->avail_ports[my_port - pif->inuse] =
+				pif->avail_ports[pif->avail_total-pif->inuse-1];
+			pif->inuse++;
+			break;
+		}
+		/* failed, already in use */
+		verbose(VERB_QUERY, "port %d in use, trying another", portno);
+		tries++;
+		if(tries == MAX_PORT_RETRY) {
+			log_err("failed to find an open port, drop msg");
+			return 0;
+		}
+	}
+	log_assert(pend->pc);
+	pend->pc->num_outstanding++;
+
+	return 1;
+}
+
+static int
+randomize_and_send_udp(struct outside_network* outnet, struct pending* pend,
+	ldns_buffer* packet, int timeout)
+{
+	struct timeval tv;
+
+	/* select id */
+	if(!select_id(outnet, pend, packet)) {
+		return 0;
+	}
+
+	/* select src_if, port */
+	if(addr_is_ip6(&pend->addr, pend->addrlen)) {
+		if(!select_ifport(outnet, pend, 
+			outnet->num_ip6, outnet->ip6_ifs))
+			return 0;
+	} else {
+		if(!select_ifport(outnet, pend, 
+			outnet->num_ip4, outnet->ip4_ifs))
+			return 0;
+	}
+	log_assert(pend->pc && pend->pc->cp);
+
+	/* send it over the commlink */
+	if(!comm_point_send_udp_msg(pend->pc->cp, packet, 
+		(struct sockaddr*)&pend->addr, pend->addrlen)) {
+		portcomm_loweruse(outnet, pend->pc);
+		return 0;
+	}
+
+	/* system calls to set timeout after sending UDP to make roundtrip
+	   smaller. */
+#ifndef S_SPLINT_S
+	tv.tv_sec = timeout/1000;
+	tv.tv_usec = (timeout%1000)*1000;
+#endif
+	comm_timer_set(pend->timer, &tv);
+	return 1;
+}
+
+struct pending* 
+pending_udp_query(struct outside_network* outnet, ldns_buffer* packet, 
+	struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
+	comm_point_callback_t* cb, void* cb_arg)
+{
+	struct pending* pend = (struct pending*)calloc(1, sizeof(*pend));
+	if(!pend) return NULL;
+	pend->outnet = outnet;
+	pend->addrlen = addrlen;
+	memmove(&pend->addr, addr, addrlen);
+	pend->cb = cb;
+	pend->cb_arg = cb_arg;
+	pend->node.key = pend;
+	pend->timer = comm_timer_create(outnet->base, pending_udp_timer_cb, 
+		pend);
+	if(!pend->timer) {
+		free(pend);
+		return NULL;
+	}
+
+	if(outnet->unused_fds == NULL) {
+		/* no unused fd, cannot create a new port (randomly) */
+		verbose(VERB_ALGO, "no fds available, udp query waiting");
+		pend->timeout = timeout;
+		pend->pkt_len = ldns_buffer_limit(packet);
+		pend->pkt = (uint8_t*)memdup(ldns_buffer_begin(packet),
+			pend->pkt_len);
+		if(!pend->pkt) {
+			comm_timer_delete(pend->timer);
+			free(pend);
+			return NULL;
+		}
+		/* put at end of waiting list */
+		if(outnet->udp_wait_last)
+			outnet->udp_wait_last->next_waiting = pend;
+		else 
+			outnet->udp_wait_first = pend;
+		outnet->udp_wait_last = pend;
+		return pend;
+	}
+	if(!randomize_and_send_udp(outnet, pend, packet, timeout)) {
+		pending_delete(outnet, pend);
+		return NULL;
+	}
+	return pend;
+}
+
+void
+outnet_tcptimer(void* arg)
+{
+	struct waiting_tcp* w = (struct waiting_tcp*)arg;
+	struct outside_network* outnet = w->outnet;
+	comm_point_callback_t* cb;
+	void* cb_arg;
+	if(w->pkt) {
+		/* it is on the waiting list */
+		struct waiting_tcp* p=outnet->tcp_wait_first, *prev=NULL;
+		while(p) {
+			if(p == w) {
+				if(prev) prev->next_waiting = w->next_waiting;
+				else	outnet->tcp_wait_first=w->next_waiting;
+				outnet->tcp_wait_last = prev;
+				break;
+			}
+			prev = p;
+			p=p->next_waiting;
+		}
+	} else {
+		/* it was in use */
+		struct pending_tcp* pend=(struct pending_tcp*)w->next_waiting;
+		comm_point_close(pend->c);
+		pend->query = NULL;
+		pend->next_free = outnet->tcp_free;
+		outnet->tcp_free = pend;
+	}
+	cb = w->cb;
+	cb_arg = w->cb_arg;
+	waiting_tcp_delete(w);
+	fptr_ok(fptr_whitelist_pending_tcp(cb));
+	(void)(*cb)(NULL, cb_arg, NETEVENT_TIMEOUT, NULL);
+	use_free_buffer(outnet);
+}
+
+struct waiting_tcp* 
+pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet, 
+	struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
+	comm_point_callback_t* callback, void* callback_arg, int ssl_upstream)
+{
+	struct pending_tcp* pend = outnet->tcp_free;
+	struct waiting_tcp* w;
+	struct timeval tv;
+	uint16_t id;
+	/* if no buffer is free allocate space to store query */
+	w = (struct waiting_tcp*)malloc(sizeof(struct waiting_tcp) 
+		+ (pend?0:ldns_buffer_limit(packet)));
+	if(!w) {
+		return NULL;
+	}
+	if(!(w->timer = comm_timer_create(outnet->base, outnet_tcptimer, w))) {
+		free(w);
+		return NULL;
+	}
+	w->pkt = NULL;
+	w->pkt_len = 0;
+	id = ((unsigned)ub_random(outnet->rnd)>>8) & 0xffff;
+	LDNS_ID_SET(ldns_buffer_begin(packet), id);
+	memcpy(&w->addr, addr, addrlen);
+	w->addrlen = addrlen;
+	w->outnet = outnet;
+	w->cb = callback;
+	w->cb_arg = callback_arg;
+	w->ssl_upstream = ssl_upstream;
+#ifndef S_SPLINT_S
+	tv.tv_sec = timeout;
+	tv.tv_usec = 0;
+#endif
+	comm_timer_set(w->timer, &tv);
+	if(pend) {
+		/* we have a buffer available right now */
+		if(!outnet_tcp_take_into_use(w, ldns_buffer_begin(packet),
+			ldns_buffer_limit(packet))) {
+			waiting_tcp_delete(w);
+			return NULL;
+		}
+	} else {
+		/* queue up */
+		w->pkt = (uint8_t*)w + sizeof(struct waiting_tcp);
+		w->pkt_len = ldns_buffer_limit(packet);
+		memmove(w->pkt, ldns_buffer_begin(packet), w->pkt_len);
+		w->next_waiting = NULL;
+		if(outnet->tcp_wait_last)
+			outnet->tcp_wait_last->next_waiting = w;
+		else	outnet->tcp_wait_first = w;
+		outnet->tcp_wait_last = w;
+	}
+	return w;
+}
+
+/** create query for serviced queries */
+static void
+serviced_gen_query(ldns_buffer* buff, uint8_t* qname, size_t qnamelen, 
+	uint16_t qtype, uint16_t qclass, uint16_t flags)
+{
+	ldns_buffer_clear(buff);
+	/* skip id */
+	ldns_buffer_write_u16(buff, flags);
+	ldns_buffer_write_u16(buff, 1); /* qdcount */
+	ldns_buffer_write_u16(buff, 0); /* ancount */
+	ldns_buffer_write_u16(buff, 0); /* nscount */
+	ldns_buffer_write_u16(buff, 0); /* arcount */
+	ldns_buffer_write(buff, qname, qnamelen);
+	ldns_buffer_write_u16(buff, qtype);
+	ldns_buffer_write_u16(buff, qclass);
+	ldns_buffer_flip(buff);
+}
+
+/** lookup serviced query in serviced query rbtree */
+static struct serviced_query*
+lookup_serviced(struct outside_network* outnet, ldns_buffer* buff, int dnssec,
+	struct sockaddr_storage* addr, socklen_t addrlen)
+{
+	struct serviced_query key;
+	key.node.key = &key;
+	key.qbuf = ldns_buffer_begin(buff);
+	key.qbuflen = ldns_buffer_limit(buff);
+	key.dnssec = dnssec;
+	memcpy(&key.addr, addr, addrlen);
+	key.addrlen = addrlen;
+	key.outnet = outnet;
+	return (struct serviced_query*)rbtree_search(outnet->serviced, &key);
+}
+
+/** Create new serviced entry */
+static struct serviced_query*
+serviced_create(struct outside_network* outnet, ldns_buffer* buff, int dnssec,
+	int want_dnssec, int tcp_upstream, int ssl_upstream,
+	struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
+	size_t zonelen)
+{
+	struct serviced_query* sq = (struct serviced_query*)malloc(sizeof(*sq));
+#ifdef UNBOUND_DEBUG
+	rbnode_t* ins;
+#endif
+	if(!sq) 
+		return NULL;
+	sq->node.key = sq;
+	sq->qbuf = memdup(ldns_buffer_begin(buff), ldns_buffer_limit(buff));
+	if(!sq->qbuf) {
+		free(sq);
+		return NULL;
+	}
+	sq->qbuflen = ldns_buffer_limit(buff);
+	sq->zone = memdup(zone, zonelen);
+	if(!sq->zone) {
+		free(sq->qbuf);
+		free(sq);
+		return NULL;
+	}
+	sq->zonelen = zonelen;
+	sq->dnssec = dnssec;
+	sq->want_dnssec = want_dnssec;
+	sq->tcp_upstream = tcp_upstream;
+	sq->ssl_upstream = ssl_upstream;
+	memcpy(&sq->addr, addr, addrlen);
+	sq->addrlen = addrlen;
+	sq->outnet = outnet;
+	sq->cblist = NULL;
+	sq->pending = NULL;
+	sq->status = serviced_initial;
+	sq->retry = 0;
+	sq->to_be_deleted = 0;
+#ifdef UNBOUND_DEBUG
+	ins = 
+#endif
+	rbtree_insert(outnet->serviced, &sq->node);
+	log_assert(ins != NULL); /* must not be already present */
+	return sq;
+}
+
+/** remove waiting tcp from the outnet waiting list */
+static void
+waiting_list_remove(struct outside_network* outnet, struct waiting_tcp* w)
+{
+	struct waiting_tcp* p = outnet->tcp_wait_first, *prev = NULL;
+	while(p) {
+		if(p == w) {
+			/* remove w */
+			if(prev)
+				prev->next_waiting = w->next_waiting;
+			else	outnet->tcp_wait_first = w->next_waiting;
+			if(outnet->tcp_wait_last == w)
+				outnet->tcp_wait_last = prev;
+			return;
+		}
+		prev = p;
+		p = p->next_waiting;
+	}
+}
+
+/** cleanup serviced query entry */
+static void
+serviced_delete(struct serviced_query* sq)
+{
+	if(sq->pending) {
+		/* clear up the pending query */
+		if(sq->status == serviced_query_UDP_EDNS ||
+			sq->status == serviced_query_UDP ||
+			sq->status == serviced_query_PROBE_EDNS ||
+			sq->status == serviced_query_UDP_EDNS_FRAG ||
+			sq->status == serviced_query_UDP_EDNS_fallback) {
+			struct pending* p = (struct pending*)sq->pending;
+			if(p->pc)
+				portcomm_loweruse(sq->outnet, p->pc);
+			pending_delete(sq->outnet, p);
+			/* this call can cause reentrant calls back into the
+			 * mesh */
+			outnet_send_wait_udp(sq->outnet);
+		} else {
+			struct waiting_tcp* p = (struct waiting_tcp*)
+				sq->pending;
+			if(p->pkt == NULL) {
+				decomission_pending_tcp(sq->outnet, 
+					(struct pending_tcp*)p->next_waiting);
+			} else {
+				waiting_list_remove(sq->outnet, p);
+				waiting_tcp_delete(p);
+			}
+		}
+	}
+	/* does not delete from tree, caller has to do that */
+	serviced_node_del(&sq->node, NULL);
+}
+
+/** perturb a dname capitalization randomly */
+static void
+serviced_perturb_qname(struct ub_randstate* rnd, uint8_t* qbuf, size_t len)
+{
+	uint8_t lablen;
+	uint8_t* d = qbuf + 10;
+	long int random = 0;
+	int bits = 0;
+	log_assert(len >= 10 + 5 /* offset qname, root, qtype, qclass */);
+	lablen = *d++;
+	while(lablen) {
+		while(lablen--) {
+			/* only perturb A-Z, a-z */
+			if(isalpha((int)*d)) {
+				/* get a random bit */	
+				if(bits == 0) {
+					random = ub_random(rnd);
+					bits = 30;
+				}
+				if(random & 0x1) {
+					*d = (uint8_t)toupper((int)*d);
+				} else {
+					*d = (uint8_t)tolower((int)*d);
+				}
+				random >>= 1;
+				bits--;
+			}
+			d++;
+		}
+		lablen = *d++;
+	}
+	if(verbosity >= VERB_ALGO) {
+		char buf[LDNS_MAX_DOMAINLEN+1];
+		dname_str(qbuf+10, buf);
+		verbose(VERB_ALGO, "qname perturbed to %s", buf);
+	}
+}
+
+/** put serviced query into a buffer */
+static void
+serviced_encode(struct serviced_query* sq, ldns_buffer* buff, int with_edns)
+{
+	/* if we are using 0x20 bits for ID randomness, perturb them */
+	if(sq->outnet->use_caps_for_id) {
+		serviced_perturb_qname(sq->outnet->rnd, sq->qbuf, sq->qbuflen);
+	}
+	/* generate query */
+	ldns_buffer_clear(buff);
+	ldns_buffer_write_u16(buff, 0); /* id placeholder */
+	ldns_buffer_write(buff, sq->qbuf, sq->qbuflen);
+	ldns_buffer_flip(buff);
+	if(with_edns) {
+		/* add edns section */
+		struct edns_data edns;
+		edns.edns_present = 1;
+		edns.ext_rcode = 0;
+		edns.edns_version = EDNS_ADVERTISED_VERSION;
+		if(sq->status == serviced_query_UDP_EDNS_FRAG) {
+			if(addr_is_ip6(&sq->addr, sq->addrlen)) {
+				if(EDNS_FRAG_SIZE_IP6 < EDNS_ADVERTISED_SIZE)
+					edns.udp_size = EDNS_FRAG_SIZE_IP6;
+				else	edns.udp_size = EDNS_ADVERTISED_SIZE;
+			} else {
+				if(EDNS_FRAG_SIZE_IP4 < EDNS_ADVERTISED_SIZE)
+					edns.udp_size = EDNS_FRAG_SIZE_IP4;
+				else	edns.udp_size = EDNS_ADVERTISED_SIZE;
+			}
+		} else {
+			edns.udp_size = EDNS_ADVERTISED_SIZE;
+		}
+		edns.bits = 0;
+		if(sq->dnssec & EDNS_DO)
+			edns.bits = EDNS_DO;
+		if(sq->dnssec & BIT_CD)
+			LDNS_CD_SET(ldns_buffer_begin(buff));
+		attach_edns_record(buff, &edns);
+	}
+}
+
+/**
+ * Perform serviced query UDP sending operation.
+ * Sends UDP with EDNS, unless infra host marked non EDNS.
+ * @param sq: query to send.
+ * @param buff: buffer scratch space.
+ * @return 0 on error.
+ */
+static int
+serviced_udp_send(struct serviced_query* sq, ldns_buffer* buff)
+{
+	int rtt, vs;
+	uint8_t edns_lame_known;
+	uint32_t now = *sq->outnet->now_secs;
+
+	if(!infra_host(sq->outnet->infra, &sq->addr, sq->addrlen, sq->zone,
+		sq->zonelen, now, &vs, &edns_lame_known, &rtt))
+		return 0;
+	sq->last_rtt = rtt;
+	verbose(VERB_ALGO, "EDNS lookup known=%d vs=%d", edns_lame_known, vs);
+	if(sq->status == serviced_initial) {
+		if(edns_lame_known == 0 && rtt > 5000 && rtt < 10001) {
+			/* perform EDNS lame probe - check if server is
+			 * EDNS lame (EDNS queries to it are dropped) */
+			verbose(VERB_ALGO, "serviced query: send probe to see "
+				" if use of EDNS causes timeouts");
+			/* even 700 msec may be too small */
+			rtt = 1000;
+			sq->status = serviced_query_PROBE_EDNS;
+		} else if(vs != -1) {
+			sq->status = serviced_query_UDP_EDNS;
+		} else { 	
+			sq->status = serviced_query_UDP; 
+		}
+	}
+	serviced_encode(sq, buff, (sq->status == serviced_query_UDP_EDNS) ||
+		(sq->status == serviced_query_UDP_EDNS_FRAG));
+	sq->last_sent_time = *sq->outnet->now_tv;
+	sq->edns_lame_known = (int)edns_lame_known;
+	verbose(VERB_ALGO, "serviced query UDP timeout=%d msec", rtt);
+	sq->pending = pending_udp_query(sq->outnet, buff, &sq->addr, 
+		sq->addrlen, rtt, serviced_udp_callback, sq);
+	if(!sq->pending)
+		return 0;
+	return 1;
+}
+
+/** check that perturbed qname is identical */
+static int
+serviced_check_qname(ldns_buffer* pkt, uint8_t* qbuf, size_t qbuflen)
+{
+	uint8_t* d1 = ldns_buffer_at(pkt, 12);
+	uint8_t* d2 = qbuf+10;
+	uint8_t len1, len2;
+	int count = 0;
+	log_assert(qbuflen >= 15 /* 10 header, root, type, class */);
+	len1 = *d1++;
+	len2 = *d2++;
+	if(ldns_buffer_limit(pkt) < 12+1+4) /* packet too small for qname */
+		return 0;
+	while(len1 != 0 || len2 != 0) {
+		if(LABEL_IS_PTR(len1)) {
+			d1 = ldns_buffer_at(pkt, PTR_OFFSET(len1, *d1));
+			if(d1 >= ldns_buffer_at(pkt, ldns_buffer_limit(pkt)))
+				return 0;
+			len1 = *d1++;
+			if(count++ > MAX_COMPRESS_PTRS)
+				return 0;
+			continue;
+		}
+		if(d2 > qbuf+qbuflen)
+			return 0;
+		if(len1 != len2)
+			return 0;
+		if(len1 > LDNS_MAX_LABELLEN)
+			return 0;
+		log_assert(len1 <= LDNS_MAX_LABELLEN);
+		log_assert(len2 <= LDNS_MAX_LABELLEN);
+		log_assert(len1 == len2 && len1 != 0);
+		/* compare the labels - bitwise identical */
+		if(memcmp(d1, d2, len1) != 0)
+			return 0;
+		d1 += len1;
+		d2 += len2;
+		len1 = *d1++;
+		len2 = *d2++;
+	}
+	return 1;
+}
+
+/** call the callbacks for a serviced query */
+static void
+serviced_callbacks(struct serviced_query* sq, int error, struct comm_point* c,
+	struct comm_reply* rep)
+{
+	struct service_callback* p = sq->cblist, *n;
+	int dobackup = (sq->cblist && sq->cblist->next); /* >1 cb*/
+	uint8_t *backup_p = NULL;
+	size_t backlen = 0;
+#ifdef UNBOUND_DEBUG
+	rbnode_t* rem =
+#endif
+	/* remove from tree, and schedule for deletion, so that callbacks
+	 * can safely deregister themselves and even create new serviced
+	 * queries that are identical to this one. */
+	rbtree_delete(sq->outnet->serviced, sq);
+	log_assert(rem); /* should have been present */
+	sq->to_be_deleted = 1; 
+	verbose(VERB_ALGO, "svcd callbacks start");
+	if(sq->outnet->use_caps_for_id && error == NETEVENT_NOERROR && c) {
+		/* noerror and nxdomain must have a qname in reply */
+		if(ldns_buffer_read_u16_at(c->buffer, 4) == 0 &&
+			(LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer))
+				== LDNS_RCODE_NOERROR || 
+			 LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer))
+				== LDNS_RCODE_NXDOMAIN)) {
+			verbose(VERB_DETAIL, "no qname in reply to check 0x20ID");
+			log_addr(VERB_DETAIL, "from server", 
+				&sq->addr, sq->addrlen);
+			log_buf(VERB_DETAIL, "for packet", c->buffer);
+			error = NETEVENT_CLOSED;
+			c = NULL;
+		} else if(ldns_buffer_read_u16_at(c->buffer, 4) > 0 &&
+			!serviced_check_qname(c->buffer, sq->qbuf, 
+			sq->qbuflen)) {
+			verbose(VERB_DETAIL, "wrong 0x20-ID in reply qname");
+			log_addr(VERB_DETAIL, "from server", 
+				&sq->addr, sq->addrlen);
+			log_buf(VERB_DETAIL, "for packet", c->buffer);
+			error = NETEVENT_CAPSFAIL;
+			/* and cleanup too */
+			pkt_dname_tolower(c->buffer, 
+				ldns_buffer_at(c->buffer, 12));
+		} else {
+			verbose(VERB_ALGO, "good 0x20-ID in reply qname");
+			/* cleanup caps, prettier cache contents. */
+			pkt_dname_tolower(c->buffer, 
+				ldns_buffer_at(c->buffer, 12));
+		}
+	}
+	if(dobackup && c) {
+		/* make a backup of the query, since the querystate processing
+		 * may send outgoing queries that overwrite the buffer.
+		 * use secondary buffer to store the query.
+		 * This is a data copy, but faster than packet to server */
+		backlen = ldns_buffer_limit(c->buffer);
+		backup_p = memdup(ldns_buffer_begin(c->buffer), backlen);
+		if(!backup_p) {
+			log_err("malloc failure in serviced query callbacks");
+			error = NETEVENT_CLOSED;
+			c = NULL;
+		}
+		sq->outnet->svcd_overhead = backlen;
+	}
+	while(p) {
+		n = p->next;
+		if(dobackup && c) {
+			ldns_buffer_clear(c->buffer);
+			ldns_buffer_write(c->buffer, backup_p, backlen);
+			ldns_buffer_flip(c->buffer);
+		}
+		fptr_ok(fptr_whitelist_serviced_query(p->cb));
+		(void)(*p->cb)(c, p->cb_arg, error, rep);
+		p = n;
+	}
+	if(backup_p) {
+		free(backup_p);
+		sq->outnet->svcd_overhead = 0;
+	}
+	verbose(VERB_ALGO, "svcd callbacks end");
+	log_assert(sq->cblist == NULL);
+	serviced_delete(sq);
+}
+
+int 
+serviced_tcp_callback(struct comm_point* c, void* arg, int error,
+        struct comm_reply* rep)
+{
+	struct serviced_query* sq = (struct serviced_query*)arg;
+	struct comm_reply r2;
+	sq->pending = NULL; /* removed after this callback */
+	if(error != NETEVENT_NOERROR)
+		log_addr(VERB_QUERY, "tcp error for address", 
+			&sq->addr, sq->addrlen);
+	if(error==NETEVENT_NOERROR)
+		infra_update_tcp_works(sq->outnet->infra, &sq->addr,
+			sq->addrlen, sq->zone, sq->zonelen);
+	if(error==NETEVENT_NOERROR && sq->status == serviced_query_TCP_EDNS &&
+		(LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer)) == 
+		LDNS_RCODE_FORMERR || LDNS_RCODE_WIRE(ldns_buffer_begin(
+		c->buffer)) == LDNS_RCODE_NOTIMPL) ) {
+		/* attempt to fallback to nonEDNS */
+		sq->status = serviced_query_TCP_EDNS_fallback;
+		serviced_tcp_initiate(sq->outnet, sq, c->buffer);
+		return 0;
+	} else if(error==NETEVENT_NOERROR && 
+		sq->status == serviced_query_TCP_EDNS_fallback &&
+			(LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer)) == 
+			LDNS_RCODE_NOERROR || LDNS_RCODE_WIRE(
+			ldns_buffer_begin(c->buffer)) == LDNS_RCODE_NXDOMAIN 
+			|| LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer)) 
+			== LDNS_RCODE_YXDOMAIN)) {
+		/* the fallback produced a result that looks promising, note
+		 * that this server should be approached without EDNS */
+		/* only store noEDNS in cache if domain is noDNSSEC */
+		if(!sq->want_dnssec)
+		  if(!infra_edns_update(sq->outnet->infra, &sq->addr, 
+			sq->addrlen, sq->zone, sq->zonelen, -1,
+			*sq->outnet->now_secs))
+			log_err("Out of memory caching no edns for host");
+		sq->status = serviced_query_TCP;
+	}
+	if(sq->tcp_upstream || sq->ssl_upstream) {
+	    struct timeval now = *sq->outnet->now_tv;
+	    if(now.tv_sec > sq->last_sent_time.tv_sec ||
+		(now.tv_sec == sq->last_sent_time.tv_sec &&
+		now.tv_usec > sq->last_sent_time.tv_usec)) {
+		/* convert from microseconds to milliseconds */
+		int roundtime = ((int)now.tv_sec - (int)sq->last_sent_time.tv_sec)*1000
+		  + ((int)now.tv_usec - (int)sq->last_sent_time.tv_usec)/1000;
+		verbose(VERB_ALGO, "measured TCP-time at %d msec", roundtime);
+		log_assert(roundtime >= 0);
+		/* only store if less then AUTH_TIMEOUT seconds, it could be
+		 * huge due to system-hibernated and we woke up */
+		if(roundtime < TCP_AUTH_QUERY_TIMEOUT*1000) {
+		    if(!infra_rtt_update(sq->outnet->infra, &sq->addr,
+			sq->addrlen, sq->zone, sq->zonelen, roundtime,
+			sq->last_rtt, (uint32_t)now.tv_sec))
+			log_err("out of memory noting rtt.");
+		}
+	    }
+	}
+	/* insert address into reply info */
+	if(!rep) {
+		/* create one if there isn't (on errors) */
+		rep = &r2;
+		r2.c = c;
+	}
+	memcpy(&rep->addr, &sq->addr, sq->addrlen);
+	rep->addrlen = sq->addrlen;
+	serviced_callbacks(sq, error, c, rep);
+	return 0;
+}
+
+static void
+serviced_tcp_initiate(struct outside_network* outnet, 
+	struct serviced_query* sq, ldns_buffer* buff)
+{
+	verbose(VERB_ALGO, "initiate TCP query %s", 
+		sq->status==serviced_query_TCP_EDNS?"EDNS":"");
+	serviced_encode(sq, buff, sq->status == serviced_query_TCP_EDNS);
+	sq->last_sent_time = *sq->outnet->now_tv;
+	sq->pending = pending_tcp_query(outnet, buff, &sq->addr,
+		sq->addrlen, TCP_AUTH_QUERY_TIMEOUT, serviced_tcp_callback, 
+		sq, sq->ssl_upstream);
+	if(!sq->pending) {
+		/* delete from tree so that a retry by above layer does not
+		 * clash with this entry */
+		log_err("serviced_tcp_initiate: failed to send tcp query");
+		serviced_callbacks(sq, NETEVENT_CLOSED, NULL, NULL);
+	}
+}
+
+/** Send serviced query over TCP return false on initial failure */
+static int
+serviced_tcp_send(struct serviced_query* sq, ldns_buffer* buff)
+{
+	int vs, rtt;
+	uint8_t edns_lame_known;
+	if(!infra_host(sq->outnet->infra, &sq->addr, sq->addrlen, sq->zone,
+		sq->zonelen, *sq->outnet->now_secs, &vs, &edns_lame_known,
+		&rtt))
+		return 0;
+	if(vs != -1)
+		sq->status = serviced_query_TCP_EDNS;
+	else 	sq->status = serviced_query_TCP;
+	serviced_encode(sq, buff, sq->status == serviced_query_TCP_EDNS);
+	sq->last_sent_time = *sq->outnet->now_tv;
+	sq->pending = pending_tcp_query(sq->outnet, buff, &sq->addr,
+		sq->addrlen, TCP_AUTH_QUERY_TIMEOUT, serviced_tcp_callback, 
+		sq, sq->ssl_upstream);
+	return sq->pending != NULL;
+}
+
+int 
+serviced_udp_callback(struct comm_point* c, void* arg, int error,
+        struct comm_reply* rep)
+{
+	struct serviced_query* sq = (struct serviced_query*)arg;
+	struct outside_network* outnet = sq->outnet;
+	struct timeval now = *sq->outnet->now_tv;
+	int fallback_tcp = 0;
+
+	sq->pending = NULL; /* removed after callback */
+	if(error == NETEVENT_TIMEOUT) {
+		int rto = 0;
+		if(sq->status == serviced_query_PROBE_EDNS) {
+			/* non-EDNS probe failed; we do not know its status,
+			 * keep trying with EDNS, timeout may not be caused
+			 * by EDNS. */
+			sq->status = serviced_query_UDP_EDNS;
+		}
+		if(sq->status == serviced_query_UDP_EDNS && sq->last_rtt < 5000) {
+			/* fallback to 1480/1280 */
+			sq->status = serviced_query_UDP_EDNS_FRAG;
+			log_name_addr(VERB_ALGO, "try edns1xx0", sq->qbuf+10,
+				&sq->addr, sq->addrlen);
+			if(!serviced_udp_send(sq, c->buffer)) {
+				serviced_callbacks(sq, NETEVENT_CLOSED, c, rep);
+			}
+			return 0;
+		}
+		if(sq->status == serviced_query_UDP_EDNS_FRAG) {
+			/* fragmentation size did not fix it */
+			sq->status = serviced_query_UDP_EDNS;
+		}
+		sq->retry++;
+		if(!(rto=infra_rtt_update(outnet->infra, &sq->addr, sq->addrlen,
+			sq->zone, sq->zonelen, -1, sq->last_rtt,
+			(uint32_t)now.tv_sec)))
+			log_err("out of memory in UDP exponential backoff");
+		if(sq->retry < OUTBOUND_UDP_RETRY) {
+			log_name_addr(VERB_ALGO, "retry query", sq->qbuf+10,
+				&sq->addr, sq->addrlen);
+			if(!serviced_udp_send(sq, c->buffer)) {
+				serviced_callbacks(sq, NETEVENT_CLOSED, c, rep);
+			}
+			return 0;
+		}
+		if(rto >= RTT_MAX_TIMEOUT) {
+			fallback_tcp = 1;
+			/* UDP does not work, fallback to TCP below */
+		} else {
+			serviced_callbacks(sq, NETEVENT_TIMEOUT, c, rep);
+			return 0;
+		}
+	} else if(error != NETEVENT_NOERROR) {
+		/* udp returns error (due to no ID or interface available) */
+		serviced_callbacks(sq, error, c, rep);
+		return 0;
+	}
+	if(!fallback_tcp) {
+	    if( (sq->status == serviced_query_UDP_EDNS 
+	        ||sq->status == serviced_query_UDP_EDNS_FRAG)
+		&& (LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer)) 
+			== LDNS_RCODE_FORMERR || LDNS_RCODE_WIRE(
+			ldns_buffer_begin(c->buffer)) == LDNS_RCODE_NOTIMPL)) {
+		/* try to get an answer by falling back without EDNS */
+		verbose(VERB_ALGO, "serviced query: attempt without EDNS");
+		sq->status = serviced_query_UDP_EDNS_fallback;
+		sq->retry = 0;
+		if(!serviced_udp_send(sq, c->buffer)) {
+			serviced_callbacks(sq, NETEVENT_CLOSED, c, rep);
+		}
+		return 0;
+	    } else if(sq->status == serviced_query_PROBE_EDNS) {
+		/* probe without EDNS succeeds, so we conclude that this
+		 * host likely has EDNS packets dropped */
+		log_addr(VERB_DETAIL, "timeouts, concluded that connection to "
+			"host drops EDNS packets", &sq->addr, sq->addrlen);
+		/* only store noEDNS in cache if domain is noDNSSEC */
+		if(!sq->want_dnssec)
+		  if(!infra_edns_update(outnet->infra, &sq->addr, sq->addrlen,
+			sq->zone, sq->zonelen, -1, (uint32_t)now.tv_sec)) {
+			log_err("Out of memory caching no edns for host");
+		  }
+		sq->status = serviced_query_UDP;
+	    } else if(sq->status == serviced_query_UDP_EDNS && 
+		!sq->edns_lame_known) {
+		/* now we know that edns queries received answers store that */
+		log_addr(VERB_ALGO, "serviced query: EDNS works for",
+			&sq->addr, sq->addrlen);
+		if(!infra_edns_update(outnet->infra, &sq->addr, sq->addrlen, 
+			sq->zone, sq->zonelen, 0, (uint32_t)now.tv_sec)) {
+			log_err("Out of memory caching edns works");
+		}
+		sq->edns_lame_known = 1;
+	    } else if(sq->status == serviced_query_UDP_EDNS_fallback &&
+		!sq->edns_lame_known && (LDNS_RCODE_WIRE(
+		ldns_buffer_begin(c->buffer)) == LDNS_RCODE_NOERROR || 
+		LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer)) == 
+		LDNS_RCODE_NXDOMAIN || LDNS_RCODE_WIRE(ldns_buffer_begin(
+		c->buffer)) == LDNS_RCODE_YXDOMAIN)) {
+		/* the fallback produced a result that looks promising, note
+		 * that this server should be approached without EDNS */
+		/* only store noEDNS in cache if domain is noDNSSEC */
+		if(!sq->want_dnssec) {
+		  log_addr(VERB_ALGO, "serviced query: EDNS fails for",
+			&sq->addr, sq->addrlen);
+		  if(!infra_edns_update(outnet->infra, &sq->addr, sq->addrlen,
+			sq->zone, sq->zonelen, -1, (uint32_t)now.tv_sec)) {
+			log_err("Out of memory caching no edns for host");
+		  }
+		} else {
+		  log_addr(VERB_ALGO, "serviced query: EDNS fails, but "
+		  	"not stored because need DNSSEC for", &sq->addr,
+			sq->addrlen);
+		}
+		sq->status = serviced_query_UDP;
+	    }
+	    if(now.tv_sec > sq->last_sent_time.tv_sec ||
+		(now.tv_sec == sq->last_sent_time.tv_sec &&
+		now.tv_usec > sq->last_sent_time.tv_usec)) {
+		/* convert from microseconds to milliseconds */
+		int roundtime = ((int)now.tv_sec - (int)sq->last_sent_time.tv_sec)*1000
+		  + ((int)now.tv_usec - (int)sq->last_sent_time.tv_usec)/1000;
+		verbose(VERB_ALGO, "measured roundtrip at %d msec", roundtime);
+		log_assert(roundtime >= 0);
+		/* in case the system hibernated, do not enter a huge value,
+		 * above this value gives trouble with server selection */
+		if(roundtime < 60000) {
+		    if(!infra_rtt_update(outnet->infra, &sq->addr, sq->addrlen, 
+			sq->zone, sq->zonelen, roundtime, sq->last_rtt,
+			(uint32_t)now.tv_sec))
+			log_err("out of memory noting rtt.");
+		}
+	    }
+	} /* end of if_!fallback_tcp */
+	/* perform TC flag check and TCP fallback after updating our
+	 * cache entries for EDNS status and RTT times */
+	if(LDNS_TC_WIRE(ldns_buffer_begin(c->buffer)) || fallback_tcp) {
+		/* fallback to TCP */
+		/* this discards partial UDP contents */
+		if(sq->status == serviced_query_UDP_EDNS ||
+			sq->status == serviced_query_UDP_EDNS_FRAG ||
+			sq->status == serviced_query_UDP_EDNS_fallback)
+			/* if we have unfinished EDNS_fallback, start again */
+			sq->status = serviced_query_TCP_EDNS;
+		else	sq->status = serviced_query_TCP;
+		serviced_tcp_initiate(outnet, sq, c->buffer);
+		return 0;
+	}
+	/* yay! an answer */
+	serviced_callbacks(sq, error, c, rep);
+	return 0;
+}
+
+/** find callback in list */
+static struct service_callback*
+callback_list_find(struct serviced_query* sq, void* cb_arg, 
+	int (*arg_compare)(void*,void*))
+{
+	struct service_callback* p;
+	for(p = sq->cblist; p; p = p->next) {
+		if(arg_compare(p->cb_arg, cb_arg))
+			return p;
+	}
+	return NULL;
+}
+
+struct serviced_query* 
+outnet_serviced_query(struct outside_network* outnet,
+	uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
+	uint16_t flags, int dnssec, int want_dnssec, int tcp_upstream,
+	int ssl_upstream, struct sockaddr_storage* addr, socklen_t addrlen,
+	uint8_t* zone, size_t zonelen, comm_point_callback_t* callback,
+	void* callback_arg, ldns_buffer* buff, int (*arg_compare)(void*,void*))
+{
+	struct serviced_query* sq;
+	struct service_callback* cb;
+	serviced_gen_query(buff, qname, qnamelen, qtype, qclass, flags);
+	sq = lookup_serviced(outnet, buff, dnssec, addr, addrlen);
+	if(sq) {
+		/* see if it is a duplicate notification request for cb_arg */
+		if(callback_list_find(sq, callback_arg, arg_compare)) {
+			return sq;
+		}
+	}
+	if(!(cb = (struct service_callback*)malloc(sizeof(*cb))))
+		return NULL;
+	if(!sq) {
+		/* make new serviced query entry */
+		sq = serviced_create(outnet, buff, dnssec, want_dnssec,
+			tcp_upstream, ssl_upstream, addr, addrlen, zone,
+			zonelen);
+		if(!sq) {
+			free(cb);
+			return NULL;
+		}
+		/* perform first network action */
+		if(outnet->do_udp && !(tcp_upstream || ssl_upstream)) {
+			if(!serviced_udp_send(sq, buff)) {
+				(void)rbtree_delete(outnet->serviced, sq);
+				free(sq->qbuf);
+				free(sq->zone);
+				free(sq);
+				free(cb);
+				return NULL;
+			}
+		} else {
+			if(!serviced_tcp_send(sq, buff)) {
+				(void)rbtree_delete(outnet->serviced, sq);
+				free(sq->qbuf);
+				free(sq->zone);
+				free(sq);
+				free(cb);
+				return NULL;
+			}
+		}
+	}
+	/* add callback to list of callbacks */
+	cb->cb = callback;
+	cb->cb_arg = callback_arg;
+	cb->next = sq->cblist;
+	sq->cblist = cb;
+	return sq;
+}
+
+/** remove callback from list */
+static void
+callback_list_remove(struct serviced_query* sq, void* cb_arg)
+{
+	struct service_callback** pp = &sq->cblist;
+	while(*pp) {
+		if((*pp)->cb_arg == cb_arg) {
+			struct service_callback* del = *pp;
+			*pp = del->next;
+			free(del);
+			return;
+		}
+		pp = &(*pp)->next;
+	}
+}
+
+void outnet_serviced_query_stop(struct serviced_query* sq, void* cb_arg)
+{
+	if(!sq) 
+		return;
+	callback_list_remove(sq, cb_arg);
+	/* if callbacks() routine scheduled deletion, let it do that */
+	if(!sq->cblist && !sq->to_be_deleted) {
+#ifdef UNBOUND_DEBUG
+		rbnode_t* rem =
+#endif
+		rbtree_delete(sq->outnet->serviced, sq);
+		log_assert(rem); /* should be present */
+		serviced_delete(sq); 
+	}
+}
+
+/** get memory used by waiting tcp entry (in use or not) */
+static size_t
+waiting_tcp_get_mem(struct waiting_tcp* w)
+{
+	size_t s;
+	if(!w) return 0;
+	s = sizeof(*w) + w->pkt_len;
+	if(w->timer)
+		s += comm_timer_get_mem(w->timer);
+	return s;
+}
+
+/** get memory used by port if */
+static size_t
+if_get_mem(struct port_if* pif)
+{
+	size_t s;
+	int i;
+	s = sizeof(*pif) + sizeof(int)*pif->avail_total +
+		sizeof(struct port_comm*)*pif->maxout;
+	for(i=0; i<pif->inuse; i++)
+		s += sizeof(*pif->out[i]) + 
+			comm_point_get_mem(pif->out[i]->cp);
+	return s;
+}
+
+/** get memory used by waiting udp */
+static size_t
+waiting_udp_get_mem(struct pending* w)
+{
+	size_t s;
+	s = sizeof(*w) + comm_timer_get_mem(w->timer) + w->pkt_len;
+	return s;
+}
+
+size_t outnet_get_mem(struct outside_network* outnet)
+{
+	size_t i;
+	int k;
+	struct waiting_tcp* w;
+	struct pending* u;
+	struct serviced_query* sq;
+	struct service_callback* sb;
+	struct port_comm* pc;
+	size_t s = sizeof(*outnet) + sizeof(*outnet->base) + 
+		sizeof(*outnet->udp_buff) + 
+		ldns_buffer_capacity(outnet->udp_buff);
+	/* second buffer is not ours */
+	for(pc = outnet->unused_fds; pc; pc = pc->next) {
+		s += sizeof(*pc) + comm_point_get_mem(pc->cp);
+	}
+	for(k=0; k<outnet->num_ip4; k++)
+		s += if_get_mem(&outnet->ip4_ifs[k]);
+	for(k=0; k<outnet->num_ip6; k++)
+		s += if_get_mem(&outnet->ip6_ifs[k]);
+	for(u=outnet->udp_wait_first; u; u=u->next_waiting)
+		s += waiting_udp_get_mem(u);
+	
+	s += sizeof(struct pending_tcp*)*outnet->num_tcp;
+	for(i=0; i<outnet->num_tcp; i++) {
+		s += sizeof(struct pending_tcp);
+		s += comm_point_get_mem(outnet->tcp_conns[i]->c);
+		if(outnet->tcp_conns[i]->query)
+			s += waiting_tcp_get_mem(outnet->tcp_conns[i]->query);
+	}
+	for(w=outnet->tcp_wait_first; w; w = w->next_waiting)
+		s += waiting_tcp_get_mem(w);
+	s += sizeof(*outnet->pending);
+	s += (sizeof(struct pending) + comm_timer_get_mem(NULL)) * 
+		outnet->pending->count;
+	s += sizeof(*outnet->serviced);
+	s += outnet->svcd_overhead;
+	RBTREE_FOR(sq, struct serviced_query*, outnet->serviced) {
+		s += sizeof(*sq) + sq->qbuflen;
+		for(sb = sq->cblist; sb; sb = sb->next)
+			s += sizeof(*sb);
+	}
+	return s;
+}
+
+size_t 
+serviced_get_mem(struct serviced_query* sq)
+{
+	struct service_callback* sb;
+	size_t s;
+	s = sizeof(*sq) + sq->qbuflen;
+	for(sb = sq->cblist; sb; sb = sb->next)
+		s += sizeof(*sb);
+	if(sq->status == serviced_query_UDP_EDNS ||
+		sq->status == serviced_query_UDP ||
+		sq->status == serviced_query_PROBE_EDNS ||
+		sq->status == serviced_query_UDP_EDNS_FRAG ||
+		sq->status == serviced_query_UDP_EDNS_fallback) {
+		s += sizeof(struct pending);
+		s += comm_timer_get_mem(NULL);
+	} else {
+		/* does not have size of the pkt pointer */
+		/* always has a timer except on malloc failures */
+
+		/* these sizes are part of the main outside network mem */
+		/*
+		s += sizeof(struct waiting_tcp);
+		s += comm_timer_get_mem(NULL);
+		*/
+	}
+	return s;
+}
+
diff --git a/3rdParty/Unbound/src/src/services/outside_network.h b/3rdParty/Unbound/src/src/services/outside_network.h
new file mode 100644
index 0000000..bfaab45
--- /dev/null
+++ b/3rdParty/Unbound/src/src/services/outside_network.h
@@ -0,0 +1,536 @@
+/*
+ * services/outside_network.h - listen to answers from the network
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file has functions to send queries to authoritative servers,
+ * and wait for the pending answer, with timeouts.
+ */
+
+#ifndef OUTSIDE_NETWORK_H
+#define OUTSIDE_NETWORK_H
+
+#include "util/rbtree.h"
+#include "util/netevent.h"
+struct pending;
+struct pending_timeout;
+struct ub_randstate;
+struct pending_tcp;
+struct waiting_tcp;
+struct waiting_udp;
+struct infra_cache;
+struct port_comm;
+struct port_if;
+
+/**
+ * Send queries to outside servers and wait for answers from servers.
+ * Contains answer-listen sockets.
+ */
+struct outside_network {
+	/** Base for select calls */
+	struct comm_base* base;
+	/** pointer to time in seconds */
+	uint32_t* now_secs;
+	/** pointer to time in microseconds */
+	struct timeval* now_tv;
+
+	/** buffer shared by UDP connections, since there is only one
+	    datagram at any time. */
+	ldns_buffer* udp_buff;
+	/** serviced_callbacks malloc overhead when processing multiple
+	 * identical serviced queries to the same server. */
+	size_t svcd_overhead;
+	/** use x20 bits to encode additional ID random bits */
+	int use_caps_for_id;
+	/** outside network wants to quit. Stop queued msgs from sent. */
+	int want_to_quit;
+
+	/** number of unwanted replies received (for statistics) */
+	size_t unwanted_replies;
+	/** cumulative total of unwanted replies (for defense) */
+	size_t unwanted_total;
+	/** threshold when to take defensive action. If 0 then never. */
+	size_t unwanted_threshold;
+	/** what action to take, called when defensive action is needed */
+	void (*unwanted_action)(void*);
+	/** user param for action */
+	void* unwanted_param;
+
+	/** linked list of available commpoints, unused file descriptors,
+	 * for use as outgoing UDP ports. cp.fd=-1 in them. */
+	struct port_comm* unused_fds;
+	/** if udp is done */
+	int do_udp;
+
+	/** array of outgoing IP4 interfaces */
+	struct port_if* ip4_ifs;
+	/** number of outgoing IP4 interfaces */
+	int num_ip4;
+
+	/** array of outgoing IP6 interfaces */
+	struct port_if* ip6_ifs;
+	/** number of outgoing IP6 interfaces */
+	int num_ip6;
+
+	/** pending udp queries waiting to be sent out, waiting for fd */
+	struct pending* udp_wait_first;
+	/** last pending udp query in list */
+	struct pending* udp_wait_last;
+
+	/** pending udp answers. sorted by id, addr */
+	rbtree_t* pending;
+	/** serviced queries, sorted by qbuf, addr, dnssec */
+	rbtree_t* serviced;
+	/** host cache, pointer but not owned by outnet. */
+	struct infra_cache* infra;
+	/** where to get random numbers */
+	struct ub_randstate* rnd;
+	/** ssl context to create ssl wrapped TCP with DNS connections */
+	void* sslctx;
+
+	/**
+	 * Array of tcp pending used for outgoing TCP connections.
+	 * Each can be used to establish a TCP connection with a server.
+	 * The file descriptors are -1 if they are free, and need to be 
+	 * opened for the tcp connection. Can be used for ip4 and ip6.
+	 */
+	struct pending_tcp **tcp_conns;
+	/** number of tcp communication points. */
+	size_t num_tcp;
+	/** list of tcp comm points that are free for use */
+	struct pending_tcp* tcp_free;
+	/** list of tcp queries waiting for a buffer */
+	struct waiting_tcp* tcp_wait_first;
+	/** last of waiting query list */
+	struct waiting_tcp* tcp_wait_last;
+};
+
+/**
+ * Outgoing interface. Ports available and currently used are tracked
+ * per interface
+ */
+struct port_if {
+	/** address ready to allocate new socket (except port no). */
+	struct sockaddr_storage addr;
+	/** length of addr field */
+	socklen_t addrlen;
+
+	/** the available ports array. These are unused.
+	 * Only the first total-inuse part is filled. */
+	int* avail_ports;
+	/** the total number of available ports (size of the array) */
+	int avail_total;
+
+	/** array of the commpoints currently in use. 
+	 * allocated for max number of fds, first part in use. */
+	struct port_comm** out;
+	/** max number of fds, size of out array */
+	int maxout;
+	/** number of commpoints (and thus also ports) in use */
+	int inuse;
+};
+
+/**
+ * Outgoing commpoint for UDP port.
+ */
+struct port_comm {
+	/** next in free list */
+	struct port_comm* next;
+	/** which port number (when in use) */
+	int number;
+	/** interface it is used in */
+	struct port_if* pif;
+	/** index in the out array of the interface */
+	int index;
+	/** number of outstanding queries on this port */
+	int num_outstanding;
+	/** UDP commpoint, fd=-1 if not in use */
+	struct comm_point* cp;
+};
+
+/**
+ * A query that has an answer pending for it.
+ */
+struct pending {
+	/** redblacktree entry, key is the pending struct(id, addr). */
+	rbnode_t node;
+	/** the ID for the query. int so that a value out of range can
+	 * be used to signify a pending that is for certain not present in
+	 * the rbtree. (and for which deletion is safe). */
+	unsigned int id;
+	/** remote address. */
+	struct sockaddr_storage addr;
+	/** length of addr field in use. */
+	socklen_t addrlen;
+	/** comm point it was sent on (and reply must come back on). */
+	struct port_comm* pc;
+	/** timeout event */
+	struct comm_timer* timer;
+	/** callback for the timeout, error or reply to the message */
+	comm_point_callback_t* cb;
+	/** callback user argument */
+	void* cb_arg;
+	/** the outside network it is part of */
+	struct outside_network* outnet;
+
+	/*---- filled if udp pending is waiting -----*/
+	/** next in waiting list. */
+	struct pending* next_waiting;
+	/** timeout in msec */
+	int timeout;
+	/** The query itself, the query packet to send. */
+	uint8_t* pkt;
+	/** length of query packet. */
+	size_t pkt_len;
+};
+
+/**
+ * Pending TCP query to server.
+ */
+struct pending_tcp {
+	/** next in list of free tcp comm points, or NULL. */
+	struct pending_tcp* next_free;
+	/** the ID for the query; checked in reply */
+	uint16_t id;
+	/** tcp comm point it was sent on (and reply must come back on). */
+	struct comm_point* c;
+	/** the query being serviced, NULL if the pending_tcp is unused. */
+	struct waiting_tcp* query;
+};
+
+/**
+ * Query waiting for TCP buffer.
+ */
+struct waiting_tcp {
+	/** 
+	 * next in waiting list.
+	 * if pkt==0, this points to the pending_tcp structure.
+	 */
+	struct waiting_tcp* next_waiting;
+	/** timeout event; timer keeps running whether the query is
+	 * waiting for a buffer or the tcp reply is pending */
+	struct comm_timer* timer;
+	/** the outside network it is part of */
+	struct outside_network* outnet;
+	/** remote address. */
+	struct sockaddr_storage addr;
+	/** length of addr field in use. */
+	socklen_t addrlen;
+	/** 
+	 * The query itself, the query packet to send.
+	 * allocated after the waiting_tcp structure.
+	 * set to NULL when the query is serviced and it part of pending_tcp.
+	 * if this is NULL, the next_waiting points to the pending_tcp.
+	 */
+	uint8_t* pkt;
+	/** length of query packet. */
+	size_t pkt_len;
+	/** callback for the timeout, error or reply to the message */
+	comm_point_callback_t* cb;
+	/** callback user argument */
+	void* cb_arg;
+	/** if it uses ssl upstream */
+	int ssl_upstream;
+};
+
+/**
+ * Callback to party interested in serviced query results.
+ */
+struct service_callback {
+	/** next in callback list */
+	struct service_callback* next;
+	/** callback function */
+	comm_point_callback_t* cb;
+	/** user argument for callback function */
+	void* cb_arg;
+};
+
+/** fallback size for fragmentation for EDNS in IPv4 */
+#define EDNS_FRAG_SIZE_IP4 1480
+/** fallback size for EDNS in IPv6, fits one fragment with ip6-tunnel-ids */
+#define EDNS_FRAG_SIZE_IP6 1260
+
+/**
+ * Query service record.
+ * Contains query and destination. UDP, TCP, EDNS are all tried.
+ * complete with retries and timeouts. A number of interested parties can
+ * receive a callback.
+ */
+struct serviced_query {
+	/** The rbtree node, key is this record */
+	rbnode_t node;
+	/** The query that needs to be answered. Starts with flags u16,
+	 * then qdcount, ..., including qname, qtype, qclass. Does not include
+	 * EDNS record. */
+	uint8_t* qbuf;
+	/** length of qbuf. */
+	size_t qbuflen;
+	/** If an EDNS section is included, the DO/CD bit will be turned on. */
+	int dnssec;
+	/** We want signatures, or else the answer is likely useless */
+	int want_dnssec;
+	/** tcp upstream used, use tcp, or ssl_upstream for SSL */
+	int tcp_upstream, ssl_upstream;
+	/** where to send it */
+	struct sockaddr_storage addr;
+	/** length of addr field in use. */
+	socklen_t addrlen;
+	/** zone name, uncompressed domain name in wireformat */
+	uint8_t* zone;
+	/** length of zone name */
+	size_t zonelen;
+	/** current status */
+	enum serviced_query_status {
+		/** initial status */
+		serviced_initial,
+		/** UDP with EDNS sent */
+		serviced_query_UDP_EDNS,
+		/** UDP without EDNS sent */
+		serviced_query_UDP,
+		/** TCP with EDNS sent */
+		serviced_query_TCP_EDNS,
+		/** TCP without EDNS sent */
+		serviced_query_TCP,
+		/** probe to test EDNS lameness (EDNS is dropped) */
+		serviced_query_PROBE_EDNS,
+		/** probe to test noEDNS0 (EDNS gives FORMERRorNOTIMP) */
+		serviced_query_UDP_EDNS_fallback,
+		/** probe to test TCP noEDNS0 (EDNS gives FORMERRorNOTIMP) */
+		serviced_query_TCP_EDNS_fallback,
+		/** send UDP query with EDNS1480 (or 1280) */
+		serviced_query_UDP_EDNS_FRAG
+	} 	
+		/** variable with current status */ 
+		status;
+	/** true if serviced_query is scheduled for deletion already */
+	int to_be_deleted;
+	/** number of UDP retries */
+	int retry;
+	/** time last UDP was sent */
+	struct timeval last_sent_time;
+	/** rtt of last (UDP) message */
+	int last_rtt;
+	/** do we know edns probe status already, for UDP_EDNS queries */
+	int edns_lame_known;
+	/** outside network this is part of */
+	struct outside_network* outnet;
+	/** list of interested parties that need callback on results. */
+	struct service_callback* cblist;
+	/** the UDP or TCP query that is pending, see status which */
+	void* pending;
+};
+
+/**
+ * Create outside_network structure with N udp ports.
+ * @param base: the communication base to use for event handling.
+ * @param bufsize: size for network buffers.
+ * @param num_ports: number of udp ports to open per interface.
+ * @param ifs: interface names (or NULL for default interface).
+ *    These interfaces must be able to access all authoritative servers.
+ * @param num_ifs: number of names in array ifs.
+ * @param do_ip4: service IP4.
+ * @param do_ip6: service IP6.
+ * @param num_tcp: number of outgoing tcp buffers to preallocate.
+ * @param infra: pointer to infra cached used for serviced queries.
+ * @param rnd: stored to create random numbers for serviced queries.
+ * @param use_caps_for_id: enable to use 0x20 bits to encode id randomness.
+ * @param availports: array of available ports. 
+ * @param numavailports: number of available ports in array.
+ * @param unwanted_threshold: when to take defensive action.
+ * @param unwanted_action: the action to take.
+ * @param unwanted_param: user parameter to action.
+ * @param do_udp: if udp is done.
+ * @param sslctx: context to create outgoing connections with (if enabled).
+ * @return: the new structure (with no pending answers) or NULL on error.
+ */
+struct outside_network* outside_network_create(struct comm_base* base,
+	size_t bufsize, size_t num_ports, char** ifs, int num_ifs,
+	int do_ip4, int do_ip6, size_t num_tcp, struct infra_cache* infra, 
+	struct ub_randstate* rnd, int use_caps_for_id, int* availports, 
+	int numavailports, size_t unwanted_threshold,
+	void (*unwanted_action)(void*), void* unwanted_param, int do_udp,
+	void* sslctx);
+
+/**
+ * Delete outside_network structure.
+ * @param outnet: object to delete.
+ */
+void outside_network_delete(struct outside_network* outnet);
+
+/**
+ * Prepare for quit. Sends no more queries, even if queued up.
+ * @param outnet: object to prepare for removal
+ */
+void outside_network_quit_prepare(struct outside_network* outnet);
+
+/**
+ * Send UDP query, create pending answer.
+ * Changes the ID for the query to be random and unique for that destination.
+ * @param outnet: provides the event handling
+ * @param packet: wireformat query to send to destination.
+ * @param addr: address to send to.
+ * @param addrlen: length of addr.
+ * @param timeout: in milliseconds from now.
+ * @param callback: function to call on error, timeout or reply.
+ * @param callback_arg: user argument for callback function.
+ * @return: NULL on error for malloc or socket. Else the pending query object.
+ */
+struct pending* pending_udp_query(struct outside_network* outnet, 
+	ldns_buffer* packet, struct sockaddr_storage* addr, 
+	socklen_t addrlen, int timeout, comm_point_callback_t* callback, 
+	void* callback_arg);
+
+/**
+ * Send TCP query. May wait for TCP buffer. Selects ID to be random, and 
+ * checks id.
+ * @param outnet: provides the event handling.
+ * @param packet: wireformat query to send to destination. copied from.
+ * @param addr: address to send to.
+ * @param addrlen: length of addr.
+ * @param timeout: in seconds from now.
+ *    Timer starts running now. Timer may expire if all buffers are used,
+ *    without any query been sent to the server yet.
+ * @param callback: function to call on error, timeout or reply.
+ * @param callback_arg: user argument for callback function.
+ * @param ssl_upstream: if the tcp connection must use SSL.
+ * @return: false on error for malloc or socket. Else the pending TCP object.
+ */
+struct waiting_tcp* pending_tcp_query(struct outside_network* outnet, 
+	ldns_buffer* packet, struct sockaddr_storage* addr, 
+	socklen_t addrlen, int timeout, comm_point_callback_t* callback, 
+	void* callback_arg, int ssl_upstream);
+
+/**
+ * Delete pending answer.
+ * @param outnet: outside network the pending query is part of.
+ *    Internal feature: if outnet is NULL, p is not unlinked from rbtree.
+ * @param p: deleted
+ */
+void pending_delete(struct outside_network* outnet, struct pending* p);
+
+/**
+ * Perform a serviced query to the authoritative servers.
+ * Duplicate efforts are detected, and EDNS, TCP and UDP retry is performed.
+ * @param outnet: outside network, with rbtree of serviced queries.
+ * @param qname: what qname to query.
+ * @param qnamelen: length of qname in octets including 0 root label.
+ * @param qtype: rrset type to query (host format)
+ * @param qclass: query class. (host format)
+ * @param flags: flags u16 (host format), includes opcode, CD bit.
+ * @param dnssec: if set, DO bit is set in EDNS queries.
+ *	If the value includes BIT_CD, CD bit is set when in EDNS queries.
+ *	If the value includes BIT_DO, DO bit is set when in EDNS queries.
+ * @param want_dnssec: signatures are needed, without EDNS the answer is
+ * 	likely to be useless.
+ * @param tcp_upstream: use TCP for upstream queries.
+ * @param ssl_upstream: use SSL for upstream queries.
+ * @param callback: callback function.
+ * @param callback_arg: user argument to callback function.
+ * @param addr: to which server to send the query.
+ * @param addrlen: length of addr.
+ * @param zone: name of the zone of the delegation point. wireformat dname.
+	This is the delegation point name for which the server is deemed
+	authoritative.
+ * @param zonelen: length of zone.
+ * @param buff: scratch buffer to create query contents in. Empty on exit.
+ * @param arg_compare: function to compare callback args, return true if 
+ * 	identical. It is given the callback_arg and args that are listed.
+ * @return 0 on error, or pointer to serviced query that is used to answer
+ *	this serviced query may be shared with other callbacks as well.
+ */
+struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
+	uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
+	uint16_t flags, int dnssec, int want_dnssec, int tcp_upstream,
+	int ssl_upstream, struct sockaddr_storage* addr, socklen_t addrlen,
+	uint8_t* zone, size_t zonelen, comm_point_callback_t* callback,
+	void* callback_arg, ldns_buffer* buff,
+	int (*arg_compare)(void*,void*));
+
+/**
+ * Remove service query callback.
+ * If that leads to zero callbacks, the query is completely cancelled.
+ * @param sq: serviced query to adjust.
+ * @param cb_arg: callback argument of callback that needs removal.
+ *	same as the callback_arg to outnet_serviced_query().
+ */
+void outnet_serviced_query_stop(struct serviced_query* sq, void* cb_arg);
+
+/**
+ * Get memory size in use by outside network.
+ * Counts buffers and outstanding query (serviced queries) malloced data.
+ * @param outnet: outside network structure.
+ * @return size in bytes.
+ */
+size_t outnet_get_mem(struct outside_network* outnet);
+
+/**
+ * Get memory size in use by serviced query while it is servicing callbacks.
+ * This takes into account the pre-deleted status of it; it will be deleted
+ * when the callbacks are done.
+ * @param sq: serviced query. 
+ * @return size in bytes.
+ */
+size_t serviced_get_mem(struct serviced_query* sq);
+
+/** callback for incoming udp answers from the network */
+int outnet_udp_cb(struct comm_point* c, void* arg, int error,
+	struct comm_reply *reply_info);
+
+/** callback for pending tcp connections */
+int outnet_tcp_cb(struct comm_point* c, void* arg, int error,
+	struct comm_reply *reply_info);
+
+/** callback for udp timeout */
+void pending_udp_timer_cb(void *arg);
+
+/** callback for outgoing TCP timer event */
+void outnet_tcptimer(void* arg);
+
+/** callback for serviced query UDP answers */
+int serviced_udp_callback(struct comm_point* c, void* arg, int error,
+        struct comm_reply* rep);
+
+/** TCP reply or error callback for serviced queries */
+int serviced_tcp_callback(struct comm_point* c, void* arg, int error,
+        struct comm_reply* rep);
+
+/** compare function of pending rbtree */
+int pending_cmp(const void* key1, const void* key2);
+
+/** compare function of serviced query rbtree */
+int serviced_cmp(const void* key1, const void* key2);
+
+#endif /* OUTSIDE_NETWORK_H */
diff --git a/3rdParty/Unbound/src/src/util/alloc.c b/3rdParty/Unbound/src/src/util/alloc.c
new file mode 100644
index 0000000..b5ccd96
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/alloc.c
@@ -0,0 +1,642 @@
+/*
+ * util/alloc.c - memory allocation service. 
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains memory allocation functions.
+ */
+
+#include "config.h"
+#include "util/alloc.h"
+#include "util/regional.h"
+#include "util/data/packed_rrset.h"
+#include "util/fptr_wlist.h"
+
+/** custom size of cached regional blocks */
+#define ALLOC_REG_SIZE	16384
+/** number of bits for ID part of uint64, rest for number of threads. */
+#define THRNUM_SHIFT	48	/* for 65k threads, 2^48 rrsets per thr. */
+
+/** setup new special type */
+static void
+alloc_setup_special(alloc_special_t* t)
+{
+	memset(t, 0, sizeof(*t));
+	lock_rw_init(&t->entry.lock);
+	t->entry.key = t;
+}
+
+/** prealloc some entries in the cache. To minimize contention. 
+ * Result is 1 lock per alloc_max newly created entries.
+ * @param alloc: the structure to fill up.
+ */
+static void
+prealloc(struct alloc_cache* alloc)
+{
+	alloc_special_t* p;
+	int i;
+	for(i=0; i<ALLOC_SPECIAL_MAX; i++) {
+		if(!(p = (alloc_special_t*)malloc(sizeof(alloc_special_t)))) {
+			log_err("prealloc: out of memory");
+			return;
+		}
+		alloc_setup_special(p);
+		alloc_set_special_next(p, alloc->quar);
+		alloc->quar = p;
+		alloc->num_quar++;
+	}
+}
+
+/** prealloc region blocks */
+static void
+prealloc_blocks(struct alloc_cache* alloc, size_t num)
+{
+	size_t i;
+	struct regional* r;
+	for(i=0; i<num; i++) {
+		r = regional_create_custom(ALLOC_REG_SIZE);
+		if(!r) {
+			log_err("prealloc blocks: out of memory");
+			return;
+		}
+		r->next = (char*)alloc->reg_list;
+		alloc->reg_list = r;
+		alloc->num_reg_blocks ++;
+	}
+}
+
+void 
+alloc_init(struct alloc_cache* alloc, struct alloc_cache* super,
+	int thread_num)
+{
+	memset(alloc, 0, sizeof(*alloc));
+	alloc->super = super;
+	alloc->thread_num = thread_num;
+	alloc->next_id = (uint64_t)thread_num; 	/* in steps, so that type */
+	alloc->next_id <<= THRNUM_SHIFT; 	/* of *_id is used. */
+	alloc->last_id = 1; 			/* so no 64bit constants, */
+	alloc->last_id <<= THRNUM_SHIFT; 	/* or implicit 'int' ops. */
+	alloc->last_id -= 1; 			/* for compiler portability. */
+	alloc->last_id |= alloc->next_id;
+	alloc->next_id += 1;			/* because id=0 is special. */
+	alloc->max_reg_blocks = 100;
+	alloc->num_reg_blocks = 0;
+	alloc->reg_list = NULL;
+	alloc->cleanup = NULL;
+	alloc->cleanup_arg = NULL;
+	if(alloc->super)
+		prealloc_blocks(alloc, alloc->max_reg_blocks);
+	if(!alloc->super) {
+		lock_quick_init(&alloc->lock);
+		lock_protect(&alloc->lock, alloc, sizeof(*alloc));
+	}
+}
+
+void 
+alloc_clear(struct alloc_cache* alloc)
+{
+	alloc_special_t* p, *np;
+	struct regional* r, *nr;
+	if(!alloc)
+		return;
+	if(!alloc->super) {
+		lock_quick_destroy(&alloc->lock);
+	}
+	if(alloc->super && alloc->quar) {
+		/* push entire list into super */
+		p = alloc->quar;
+		while(alloc_special_next(p)) /* find last */
+			p = alloc_special_next(p);
+		lock_quick_lock(&alloc->super->lock);
+		alloc_set_special_next(p, alloc->super->quar);
+		alloc->super->quar = alloc->quar;
+		alloc->super->num_quar += alloc->num_quar;
+		lock_quick_unlock(&alloc->super->lock);
+	} else {
+		/* free */
+		p = alloc->quar;
+		while(p) {
+			np = alloc_special_next(p);
+			/* deinit special type */
+			lock_rw_destroy(&p->entry.lock);
+			free(p);
+			p = np;
+		}
+	}
+	alloc->quar = 0;
+	alloc->num_quar = 0;
+	r = alloc->reg_list;
+	while(r) {
+		nr = (struct regional*)r->next;
+		free(r);
+		r = nr;
+	}
+	alloc->reg_list = NULL;
+	alloc->num_reg_blocks = 0;
+}
+
+uint64_t
+alloc_get_id(struct alloc_cache* alloc)
+{
+	uint64_t id = alloc->next_id++;
+	if(id == alloc->last_id) {
+		log_warn("rrset alloc: out of 64bit ids. Clearing cache.");
+		fptr_ok(fptr_whitelist_alloc_cleanup(alloc->cleanup));
+		(*alloc->cleanup)(alloc->cleanup_arg);
+
+		/* start back at first number */   	/* like in alloc_init*/
+		alloc->next_id = (uint64_t)alloc->thread_num; 	
+		alloc->next_id <<= THRNUM_SHIFT; 	/* in steps for comp. */
+		alloc->next_id += 1;			/* portability. */
+		/* and generate new and safe id */
+		id = alloc->next_id++;
+	}
+	return id;
+}
+
+alloc_special_t* 
+alloc_special_obtain(struct alloc_cache* alloc)
+{
+	alloc_special_t* p;
+	log_assert(alloc);
+	/* see if in local cache */
+	if(alloc->quar) {
+		p = alloc->quar;
+		alloc->quar = alloc_special_next(p);
+		alloc->num_quar--;
+		p->id = alloc_get_id(alloc);
+		return p;
+	}
+	/* see if in global cache */
+	if(alloc->super) {
+		/* could maybe grab alloc_max/2 entries in one go,
+		 * but really, isn't that just as fast as this code? */
+		lock_quick_lock(&alloc->super->lock);
+		if((p = alloc->super->quar)) {
+			alloc->super->quar = alloc_special_next(p);
+			alloc->super->num_quar--;
+		}
+		lock_quick_unlock(&alloc->super->lock);
+		if(p) {
+			p->id = alloc_get_id(alloc);
+			return p;
+		}
+	}
+	/* allocate new */
+	prealloc(alloc);
+	if(!(p = (alloc_special_t*)malloc(sizeof(alloc_special_t)))) {
+		log_err("alloc_special_obtain: out of memory");
+		return NULL;
+	}
+	alloc_setup_special(p);
+	p->id = alloc_get_id(alloc);
+	return p;
+}
+
+/** push mem and some more items to the super */
+static void 
+pushintosuper(struct alloc_cache* alloc, alloc_special_t* mem)
+{
+	int i;
+	alloc_special_t *p = alloc->quar;
+	log_assert(p);
+	log_assert(alloc && alloc->super && 
+		alloc->num_quar >= ALLOC_SPECIAL_MAX);
+	/* push ALLOC_SPECIAL_MAX/2 after mem */
+	alloc_set_special_next(mem, alloc->quar);
+	for(i=1; i<ALLOC_SPECIAL_MAX/2; i++) {
+		p = alloc_special_next(p);
+	}
+	alloc->quar = alloc_special_next(p);
+	alloc->num_quar -= ALLOC_SPECIAL_MAX/2;
+
+	/* dump mem+list into the super quar list */
+	lock_quick_lock(&alloc->super->lock);
+	alloc_set_special_next(p, alloc->super->quar);
+	alloc->super->quar = mem;
+	alloc->super->num_quar += ALLOC_SPECIAL_MAX/2 + 1;
+	lock_quick_unlock(&alloc->super->lock);
+	/* so 1 lock per mem+alloc/2 deletes */
+}
+
+void 
+alloc_special_release(struct alloc_cache* alloc, alloc_special_t* mem)
+{
+	log_assert(alloc);
+	if(!mem)
+		return;
+	if(!alloc->super) { 
+		lock_quick_lock(&alloc->lock); /* superalloc needs locking */
+	}
+
+	alloc_special_clean(mem);
+	if(alloc->super && alloc->num_quar >= ALLOC_SPECIAL_MAX) {
+		/* push it to the super structure */
+		pushintosuper(alloc, mem);
+		return;
+	}
+
+	alloc_set_special_next(mem, alloc->quar);
+	alloc->quar = mem;
+	alloc->num_quar++;
+	if(!alloc->super) {
+		lock_quick_unlock(&alloc->lock);
+	}
+}
+
+void 
+alloc_stats(struct alloc_cache* alloc)
+{
+	log_info("%salloc: %d in cache, %d blocks.", alloc->super?"":"sup",
+		(int)alloc->num_quar, (int)alloc->num_reg_blocks);
+}
+
+size_t alloc_get_mem(struct alloc_cache* alloc)
+{
+	alloc_special_t* p;
+	size_t s = sizeof(*alloc);
+	if(!alloc->super) { 
+		lock_quick_lock(&alloc->lock); /* superalloc needs locking */
+	}
+	s += sizeof(alloc_special_t) * alloc->num_quar;
+	for(p = alloc->quar; p; p = alloc_special_next(p)) {
+		s += lock_get_mem(&p->entry.lock);
+	}
+	s += alloc->num_reg_blocks * ALLOC_REG_SIZE;
+	if(!alloc->super) {
+		lock_quick_unlock(&alloc->lock);
+	}
+	return s;
+}
+
+struct regional* 
+alloc_reg_obtain(struct alloc_cache* alloc)
+{
+	if(alloc->num_reg_blocks > 0) {
+		struct regional* r = alloc->reg_list;
+		alloc->reg_list = (struct regional*)r->next;
+		r->next = NULL;
+		alloc->num_reg_blocks--;
+		return r;
+	}
+	return regional_create_custom(ALLOC_REG_SIZE);
+}
+
+void 
+alloc_reg_release(struct alloc_cache* alloc, struct regional* r)
+{
+	if(alloc->num_reg_blocks >= alloc->max_reg_blocks) {
+		regional_destroy(r);
+		return;
+	}
+	if(!r) return;
+	regional_free_all(r);
+	log_assert(r->next == NULL);
+	r->next = (char*)alloc->reg_list;
+	alloc->reg_list = r;
+	alloc->num_reg_blocks++;
+}
+
+void 
+alloc_set_id_cleanup(struct alloc_cache* alloc, void (*cleanup)(void*),
+        void* arg)
+{
+	alloc->cleanup = cleanup;
+	alloc->cleanup_arg = arg;
+}
+
+/** global debug value to keep track of total memory mallocs */
+size_t unbound_mem_alloc = 0;
+/** global debug value to keep track of total memory frees */
+size_t unbound_mem_freed = 0;
+#ifdef UNBOUND_ALLOC_STATS
+/** special value to know if the memory is being tracked */
+uint64_t mem_special = (uint64_t)0xfeed43327766abcdLL;
+#ifdef malloc
+#undef malloc
+#endif
+/** malloc with stats */
+void *unbound_stat_malloc(size_t size)
+{
+	void* res;
+	if(size == 0) size = 1;
+	res = malloc(size+16);
+	if(!res) return NULL;
+	unbound_mem_alloc += size;
+	log_info("stat %p=malloc(%u)", res+16, (unsigned)size);
+	memcpy(res, &size, sizeof(size));
+	memcpy(res+8, &mem_special, sizeof(mem_special));
+	return res+16;
+}
+#ifdef calloc
+#undef calloc
+#endif
+/** calloc with stats */
+void *unbound_stat_calloc(size_t nmemb, size_t size)
+{
+	size_t s = (nmemb*size==0)?(size_t)1:nmemb*size;
+	void* res = calloc(1, s+16);
+	if(!res) return NULL;
+	log_info("stat %p=calloc(%u, %u)", res+16, (unsigned)nmemb, (unsigned)size);
+	unbound_mem_alloc += s;
+	memcpy(res, &s, sizeof(s));
+	memcpy(res+8, &mem_special, sizeof(mem_special));
+	return res+16;
+}
+#ifdef free
+#undef free
+#endif
+/** free with stats */
+void unbound_stat_free(void *ptr)
+{
+	size_t s;
+	if(!ptr) return;
+	if(memcmp(ptr-8, &mem_special, sizeof(mem_special)) != 0) {
+		free(ptr);
+		return;
+	}
+	ptr-=16;
+	memcpy(&s, ptr, sizeof(s));
+	log_info("stat free(%p) size %u", ptr+16, (unsigned)s);
+	memset(ptr+8, 0, 8);
+	unbound_mem_freed += s;
+	free(ptr);
+}
+#ifdef realloc
+#undef realloc
+#endif
+/** realloc with stats */
+void *unbound_stat_realloc(void *ptr, size_t size)
+{
+	size_t cursz;
+	void* res;
+	if(!ptr) return unbound_stat_malloc(size);
+	if(memcmp(ptr-8, &mem_special, sizeof(mem_special)) != 0) {
+		return realloc(ptr, size);
+	}
+	if(size==0) {
+		unbound_stat_free(ptr);
+		return NULL;
+	}
+	ptr -= 16;
+	memcpy(&cursz, ptr, sizeof(cursz));
+	if(cursz == size) {
+		/* nothing changes */
+		return ptr;
+	}
+	res = malloc(size+16);
+	if(!res) return NULL;
+	unbound_mem_alloc += size;
+	unbound_mem_freed += cursz;
+	log_info("stat realloc(%p, %u) from %u", ptr+16, (unsigned)size, (unsigned)cursz);
+	if(cursz > size) {
+		memcpy(res+16, ptr+16, size);
+	} else if(size > cursz) {
+		memcpy(res+16, ptr+16, cursz);
+	}
+	memset(ptr+8, 0, 8);
+	free(ptr);
+	memcpy(res, &size, sizeof(size));
+	memcpy(res+8, &mem_special, sizeof(mem_special));
+	return res+16;
+}
+
+/** log to file where alloc was done */
+void *unbound_stat_malloc_log(size_t size, const char* file, int line,
+        const char* func)
+{
+	log_info("%s:%d %s malloc(%u)", file, line, func, (unsigned)size);
+	return unbound_stat_malloc(size);
+}
+
+/** log to file where alloc was done */
+void *unbound_stat_calloc_log(size_t nmemb, size_t size, const char* file,
+        int line, const char* func)
+{
+	log_info("%s:%d %s calloc(%u, %u)", file, line, func, 
+		(unsigned) nmemb, (unsigned)size);
+	return unbound_stat_calloc(nmemb, size);
+}
+
+/** log to file where free was done */
+void unbound_stat_free_log(void *ptr, const char* file, int line,
+        const char* func)
+{
+	if(ptr && memcmp(ptr-8, &mem_special, sizeof(mem_special)) == 0) {
+		size_t s;
+		memcpy(&s, ptr-16, sizeof(s));
+		log_info("%s:%d %s free(%p) size %u", 
+			file, line, func, ptr, (unsigned)s);
+	} else
+		log_info("%s:%d %s unmatched free(%p)", file, line, func, ptr);
+	unbound_stat_free(ptr);
+}
+
+/** log to file where alloc was done */
+void *unbound_stat_realloc_log(void *ptr, size_t size, const char* file,
+        int line, const char* func)
+{
+	log_info("%s:%d %s realloc(%p, %u)", file, line, func, 
+		ptr, (unsigned)size);
+	return unbound_stat_realloc(ptr, size);
+}
+
+#endif /* UNBOUND_ALLOC_STATS */
+#ifdef UNBOUND_ALLOC_LITE
+#undef malloc
+#undef calloc
+#undef free
+#undef realloc
+/** length of prefix and suffix */
+static size_t lite_pad = 16;
+/** prefix value to check */
+static char* lite_pre = "checkfront123456";
+/** suffix value to check */
+static char* lite_post= "checkafter123456";
+
+void *unbound_stat_malloc_lite(size_t size, const char* file, int line,
+        const char* func)
+{
+	/*  [prefix .. len .. actual data .. suffix] */
+	void* res = malloc(size+lite_pad*2+sizeof(size_t));
+	if(!res) return NULL;
+	memmove(res, lite_pre, lite_pad);
+	memmove(res+lite_pad, &size, sizeof(size_t));
+	memset(res+lite_pad+sizeof(size_t), 0x1a, size); /* init the memory */
+	memmove(res+lite_pad+size+sizeof(size_t), lite_post, lite_pad);
+	return res+lite_pad+sizeof(size_t);
+}
+
+void *unbound_stat_calloc_lite(size_t nmemb, size_t size, const char* file,
+        int line, const char* func)
+{
+	size_t req = nmemb * size;
+	void* res = malloc(req+lite_pad*2+sizeof(size_t));
+	if(!res) return NULL;
+	memmove(res, lite_pre, lite_pad);
+	memmove(res+lite_pad, &req, sizeof(size_t));
+	memset(res+lite_pad+sizeof(size_t), 0, req);
+	memmove(res+lite_pad+req+sizeof(size_t), lite_post, lite_pad);
+	return res+lite_pad+sizeof(size_t);
+}
+
+void unbound_stat_free_lite(void *ptr, const char* file, int line,
+        const char* func)
+{
+	void* real;
+	size_t orig = 0;
+	if(!ptr) return;
+	real = ptr-lite_pad-sizeof(size_t);
+	if(memcmp(real, lite_pre, lite_pad) != 0) {
+		log_err("free(): prefix failed %s:%d %s", file, line, func);
+		log_hex("prefix here", real, lite_pad);
+		log_hex("  should be", lite_pre, lite_pad);
+		fatal_exit("alloc assertion failed");
+	}
+	memmove(&orig, real+lite_pad, sizeof(size_t));
+	if(memcmp(real+lite_pad+orig+sizeof(size_t), lite_post, lite_pad)!=0){
+		log_err("free(): suffix failed %s:%d %s", file, line, func);
+		log_err("alloc size is %d", (int)orig);
+		log_hex("suffix here", real+lite_pad+orig+sizeof(size_t), 
+			lite_pad);
+		log_hex("  should be", lite_post, lite_pad);
+		fatal_exit("alloc assertion failed");
+	}
+	memset(real, 0xdd, orig+lite_pad*2+sizeof(size_t)); /* mark it */
+	free(real);
+}
+
+void *unbound_stat_realloc_lite(void *ptr, size_t size, const char* file,
+        int line, const char* func)
+{
+	/* always free and realloc (no growing) */
+	void* real, *newa;
+	size_t orig = 0;
+	if(!ptr) {
+		/* like malloc() */
+		return unbound_stat_malloc_lite(size, file, line, func);
+	}
+	if(!size) {
+		/* like free() */
+		unbound_stat_free_lite(ptr, file, line, func);
+		return NULL;
+	}
+	/* change allocation size and copy */
+	real = ptr-lite_pad-sizeof(size_t);
+	if(memcmp(real, lite_pre, lite_pad) != 0) {
+		log_err("realloc(): prefix failed %s:%d %s", file, line, func);
+		log_hex("prefix here", real, lite_pad);
+		log_hex("  should be", lite_pre, lite_pad);
+		fatal_exit("alloc assertion failed");
+	}
+	memmove(&orig, real+lite_pad, sizeof(size_t));
+	if(memcmp(real+lite_pad+orig+sizeof(size_t), lite_post, lite_pad)!=0){
+		log_err("realloc(): suffix failed %s:%d %s", file, line, func);
+		log_err("alloc size is %d", (int)orig);
+		log_hex("suffix here", real+lite_pad+orig+sizeof(size_t), 
+			lite_pad);
+		log_hex("  should be", lite_post, lite_pad);
+		fatal_exit("alloc assertion failed");
+	}
+	/* new alloc and copy over */
+	newa = unbound_stat_malloc_lite(size, file, line, func);
+	if(!newa)
+		return NULL;
+	if(orig < size)
+		memmove(newa, ptr, orig);
+	else	memmove(newa, ptr, size);
+	memset(real, 0xdd, orig+lite_pad*2+sizeof(size_t)); /* mark it */
+	free(real);
+	return newa;
+}
+
+char* unbound_strdup_lite(const char* s, const char* file, int line, 
+        const char* func)
+{
+	/* this routine is made to make sure strdup() uses the malloc_lite */
+	size_t l = strlen(s)+1;
+	char* n = (char*)unbound_stat_malloc_lite(l, file, line, func);
+	if(!n) return NULL;
+	memmove(n, s, l);
+	return n;
+}
+
+char* unbound_lite_wrapstr(char* s)
+{
+	char* n = unbound_strdup_lite(s, __FILE__, __LINE__, __func__);
+	free(s);
+	return n;
+}
+
+#undef ldns_pkt2wire
+ldns_status unbound_lite_pkt2wire(uint8_t **dest, const ldns_pkt *p, 
+	size_t *size)
+{
+	uint8_t* md = NULL;
+	size_t ms = 0;
+	ldns_status s = ldns_pkt2wire(&md, p, &ms);
+	if(md) {
+		*dest = unbound_stat_malloc_lite(ms, __FILE__, __LINE__, 
+			__func__);
+		*size = ms;
+		if(!*dest) { free(md); return LDNS_STATUS_MEM_ERR; }
+		memcpy(*dest, md, ms);
+		free(md);
+	} else {
+		*dest = NULL;
+		*size = 0;
+	}
+	return s;
+}
+
+#undef i2d_DSA_SIG
+int unbound_lite_i2d_DSA_SIG(DSA_SIG* dsasig, unsigned char** sig)
+{
+	unsigned char* n = NULL;
+	int r= i2d_DSA_SIG(dsasig, &n);
+	if(n) {
+		*sig = unbound_stat_malloc_lite((size_t)r, __FILE__, __LINE__, 
+			__func__);
+		if(!*sig) return -1;
+		memcpy(*sig, n, (size_t)r);
+		free(n);
+		return r;
+	}
+	*sig = NULL;
+	return r;
+}
+
+#endif /* UNBOUND_ALLOC_LITE */
diff --git a/3rdParty/Unbound/src/src/util/alloc.h b/3rdParty/Unbound/src/src/util/alloc.h
new file mode 100644
index 0000000..4ed0053
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/alloc.h
@@ -0,0 +1,214 @@
+/*
+ * util/alloc.h - memory allocation service. 
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains memory allocation functions.
+ *
+ * The reasons for this service are:
+ *	o Avoid locking costs of getting global lock to call malloc().
+ *	o The packed rrset type needs to be kept on special freelists,
+ *	  so that they are reused for other packet rrset allocations.
+ *
+ */
+
+#ifndef UTIL_ALLOC_H
+#define UTIL_ALLOC_H
+
+#include "util/locks.h"
+struct ub_packed_rrset_key;
+struct regional;
+
+/** The special type, packed rrset. Not allowed to be used for other memory */
+typedef struct ub_packed_rrset_key alloc_special_t;
+/** clean the special type. Pass pointer. */
+#define alloc_special_clean(x) (x)->id = 0;
+/** access next pointer. (in available spot). Pass pointer. */
+#define alloc_special_next(x) ((alloc_special_t*)((x)->entry.overflow_next))
+/** set next pointer. (in available spot). Pass pointers. */
+#define alloc_set_special_next(x, y) \
+	((x)->entry.overflow_next) = (struct lruhash_entry*)(y);
+
+/** how many blocks to cache locally. */
+#define ALLOC_SPECIAL_MAX 10
+
+/**
+ * Structure that provides allocation. Use one per thread.
+ * The one on top has a NULL super pointer.
+ */
+struct alloc_cache {
+	/** lock, only used for the super. */
+	lock_quick_t lock;
+	/** global allocator above this one. NULL for none (malloc/free) */
+	struct alloc_cache* super;
+	/** singly linked lists of special type. These are free for use. */
+	alloc_special_t* quar;
+	/** number of items in quarantine. */
+	size_t num_quar;
+	/** thread number for id creation */
+	int thread_num;
+	/** next id number to pass out */
+	uint64_t next_id;
+	/** last id number possible */
+	uint64_t last_id;
+	/** what function to call to cleanup when last id is reached */
+	void (*cleanup)(void*);
+	/** user arg for cleanup */
+	void* cleanup_arg;
+
+	/** how many regional blocks to keep back max */
+	size_t max_reg_blocks;
+	/** how many regional blocks are kept now */
+	size_t num_reg_blocks;
+	/** linked list of regional blocks, using regional->next */
+	struct regional* reg_list;
+};
+
+/**
+ * Init alloc (zeroes the struct).
+ * @param alloc: this parameter is allocated by the caller.
+ * @param super: super to use (init that before with super_init).
+ *    Pass this argument NULL to init the toplevel alloc structure.
+ * @param thread_num: thread number for id creation of special type.
+ */
+void alloc_init(struct alloc_cache* alloc, struct alloc_cache* super,
+	int thread_num);
+
+/**
+ * Free the alloc. Pushes all the cached items into the super structure.
+ * Or deletes them if alloc->super is NULL.
+ * Does not free the alloc struct itself (it was also allocated by caller).
+ * @param alloc: is almost zeroed on exit (except some stats).
+ */
+void alloc_clear(struct alloc_cache* alloc);
+
+/**
+ * Get a new special_t element.
+ * @param alloc: where to alloc it.
+ * @return: memory block. Will not return NULL (instead fatal_exit).
+ *    The block is zeroed.
+ */
+alloc_special_t* alloc_special_obtain(struct alloc_cache* alloc);
+
+/**
+ * Return special_t back to pool.
+ * The block is cleaned up (zeroed) which also invalidates the ID inside.
+ * @param alloc: where to alloc it.
+ * @param mem: block to free.
+ */
+void alloc_special_release(struct alloc_cache* alloc, alloc_special_t* mem);
+
+/**
+ * Set ID number of special type to a fresh new ID number.
+ * In case of ID number overflow, the rrset cache has to be cleared.
+ * @param alloc: the alloc cache
+ * @return: fresh id is returned.
+ */
+uint64_t alloc_get_id(struct alloc_cache* alloc);
+
+/**
+ * Get memory size of alloc cache, alloc structure including special types.
+ * @param alloc: on what alloc.
+ * @return size in bytes.
+ */
+size_t alloc_get_mem(struct alloc_cache* alloc);
+
+/**
+ * Print debug information (statistics).
+ * @param alloc: on what alloc.
+ */
+void alloc_stats(struct alloc_cache* alloc);
+
+/**
+ * Get a new regional for query states
+ * @param alloc: where to alloc it.
+ * @return regional for use or NULL on alloc failure.
+ */
+struct regional* alloc_reg_obtain(struct alloc_cache* alloc);
+
+/**
+ * Put regional for query states back into alloc cache.
+ * @param alloc: where to alloc it.
+ * @param r: regional to put back.
+ */
+void alloc_reg_release(struct alloc_cache* alloc, struct regional* r);
+
+/**
+ * Set cleanup on ID overflow callback function. This should remove all
+ * RRset ID references from the program. Clear the caches.
+ * @param alloc: the alloc
+ * @param cleanup: the callback function, called as cleanup(arg).
+ * @param arg: user argument to callback function.
+ */
+void alloc_set_id_cleanup(struct alloc_cache* alloc, void (*cleanup)(void*),
+	void* arg);
+
+#ifdef UNBOUND_ALLOC_LITE
+#  include <ldns/packet.h>
+#  include <openssl/ssl.h>
+#  define malloc(s) unbound_stat_malloc_lite(s, __FILE__, __LINE__, __func__)
+#  define calloc(n,s) unbound_stat_calloc_lite(n, s, __FILE__, __LINE__, __func__)
+#  define free(p) unbound_stat_free_lite(p, __FILE__, __LINE__, __func__)
+#  define realloc(p,s) unbound_stat_realloc_lite(p, s, __FILE__, __LINE__, __func__)
+void *unbound_stat_malloc_lite(size_t size, const char* file, int line,
+	const char* func);
+void *unbound_stat_calloc_lite(size_t nmemb, size_t size, const char* file,
+	int line, const char* func);
+void unbound_stat_free_lite(void *ptr, const char* file, int line,
+	const char* func);
+void *unbound_stat_realloc_lite(void *ptr, size_t size, const char* file,
+	int line, const char* func);
+#  ifdef strdup
+#    undef strdup
+#  endif
+#  define strdup(s) unbound_strdup_lite(s, __FILE__, __LINE__, __func__)
+char* unbound_strdup_lite(const char* s, const char* file, int line, 
+	const char* func);
+char* unbound_lite_wrapstr(char* s);
+#  define ldns_rr2str(rr) unbound_lite_wrapstr(ldns_rr2str(rr))
+#  define ldns_rdf2str(rdf) unbound_lite_wrapstr(ldns_rdf2str(rdf))
+#  define ldns_rr_type2str(t) unbound_lite_wrapstr(ldns_rr_type2str(t))
+#  define ldns_rr_class2str(c) unbound_lite_wrapstr(ldns_rr_class2str(c))
+#  define ldns_rr_list2str(r) unbound_lite_wrapstr(ldns_rr_list2str(r))
+#  define ldns_pkt2str(p) unbound_lite_wrapstr(ldns_pkt2str(p))
+#  define ldns_pkt_rcode2str(r) unbound_lite_wrapstr(ldns_pkt_rcode2str(r))
+#  define ldns_pkt2wire(a, r, s) unbound_lite_pkt2wire(a, r, s)
+ldns_status unbound_lite_pkt2wire(uint8_t **dest, const ldns_pkt *p, size_t *size);
+#  define i2d_DSA_SIG(d, s) unbound_lite_i2d_DSA_SIG(d, s)
+int unbound_lite_i2d_DSA_SIG(DSA_SIG* dsasig, unsigned char** sig);
+#endif /* UNBOUND_ALLOC_LITE */
+
+#endif /* UTIL_ALLOC_H */
diff --git a/3rdParty/Unbound/src/src/util/config_file.c b/3rdParty/Unbound/src/src/util/config_file.c
new file mode 100644
index 0000000..41ae87e
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/config_file.c
@@ -0,0 +1,1444 @@
+/*
+ * util/config_file.c - reads and stores the config file for unbound.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions for the config file.
+ */
+
+#include "config.h"
+#include <ctype.h>
+#include <ldns/ldns.h>
+#include "util/log.h"
+
+#include "util/configyyrename.h"
+#include "util/config_file.h"
+#include "util/configparser.h"
+#include "util/net_help.h"
+#include "util/data/msgparse.h"
+#include "util/module.h"
+#include "util/regional.h"
+#include "util/fptr_wlist.h"
+#include "util/data/dname.h"
+/** global config during parsing */
+struct config_parser_state* cfg_parser = 0;
+/** lex in file */
+extern FILE* ub_c_in;
+/** lex out file */
+extern FILE* ub_c_out;
+/** the yacc lex generated parse function */
+int ub_c_parse(void);
+/** the lexer function */
+int ub_c_lex(void);
+/** wrap function */
+int ub_c_wrap(void);
+
+/** init ports possible for use */
+static void init_outgoing_availports(int* array, int num);
+
+struct config_file* 
+config_create(void)
+{
+	struct config_file* cfg;
+	cfg = (struct config_file*)calloc(1, sizeof(struct config_file));
+	if(!cfg)
+		return NULL;
+	/* the defaults if no config is present */
+	cfg->verbosity = 1;
+	cfg->stat_interval = 0;
+	cfg->stat_cumulative = 0;
+	cfg->stat_extended = 0;
+	cfg->num_threads = 1;
+	cfg->port = UNBOUND_DNS_PORT;
+	cfg->do_ip4 = 1;
+	cfg->do_ip6 = 1;
+	cfg->do_udp = 1;
+	cfg->do_tcp = 1;
+	cfg->tcp_upstream = 0;
+	cfg->ssl_service_key = NULL;
+	cfg->ssl_service_pem = NULL;
+	cfg->ssl_port = 443;
+	cfg->ssl_upstream = 0;
+	cfg->use_syslog = 1;
+	cfg->log_time_ascii = 0;
+	cfg->log_queries = 0;
+#ifndef USE_WINSOCK
+#  ifdef USE_MINI_EVENT
+	/* select max 1024 sockets */
+	cfg->outgoing_num_ports = 960;
+	cfg->num_queries_per_thread = 512;
+#  else
+	/* libevent can use many sockets */
+	cfg->outgoing_num_ports = 4096;
+	cfg->num_queries_per_thread = 1024;
+#  endif
+	cfg->outgoing_num_tcp = 10;
+	cfg->incoming_num_tcp = 10;
+#else
+	cfg->outgoing_num_ports = 48; /* windows is limited in num fds */
+	cfg->num_queries_per_thread = 24;
+	cfg->outgoing_num_tcp = 2; /* leaves 64-52=12 for: 4if,1stop,thread4 */
+	cfg->incoming_num_tcp = 2; 
+#endif
+	cfg->edns_buffer_size = 4096; /* 4k from rfc recommendation */
+	cfg->msg_buffer_size = 65552; /* 64 k + a small margin */
+	cfg->msg_cache_size = 4 * 1024 * 1024;
+	cfg->msg_cache_slabs = 4;
+	cfg->jostle_time = 200;
+	cfg->rrset_cache_size = 4 * 1024 * 1024;
+	cfg->rrset_cache_slabs = 4;
+	cfg->host_ttl = 900;
+	cfg->bogus_ttl = 60;
+	cfg->min_ttl = 0;
+	cfg->max_ttl = 3600 * 24;
+	cfg->prefetch = 0;
+	cfg->prefetch_key = 0;
+	cfg->infra_cache_slabs = 4;
+	cfg->infra_cache_numhosts = 10000;
+	if(!(cfg->outgoing_avail_ports = (int*)calloc(65536, sizeof(int))))
+		goto error_exit;
+	init_outgoing_availports(cfg->outgoing_avail_ports, 65536);
+	if(!(cfg->username = strdup(UB_USERNAME))) goto error_exit;
+#ifdef HAVE_CHROOT
+	if(!(cfg->chrootdir = strdup(CHROOT_DIR))) goto error_exit;
+#endif
+	if(!(cfg->directory = strdup(RUN_DIR))) goto error_exit;
+	if(!(cfg->logfile = strdup(""))) goto error_exit;
+	if(!(cfg->pidfile = strdup(PIDFILE))) goto error_exit;
+	if(!(cfg->target_fetch_policy = strdup("3 2 1 0 0"))) goto error_exit;
+	cfg->donotqueryaddrs = NULL;
+	cfg->donotquery_localhost = 1;
+	cfg->root_hints = NULL;
+	cfg->do_daemonize = 1;
+	cfg->if_automatic = 0;
+	cfg->so_rcvbuf = 0;
+	cfg->so_sndbuf = 0;
+	cfg->num_ifs = 0;
+	cfg->ifs = NULL;
+	cfg->num_out_ifs = 0;
+	cfg->out_ifs = NULL;
+	cfg->stubs = NULL;
+	cfg->forwards = NULL;
+	cfg->acls = NULL;
+	cfg->harden_short_bufsize = 0;
+	cfg->harden_large_queries = 0;
+	cfg->harden_glue = 1;
+	cfg->harden_dnssec_stripped = 1;
+	cfg->harden_below_nxdomain = 0;
+	cfg->harden_referral_path = 0;
+	cfg->use_caps_bits_for_id = 0;
+	cfg->private_address = NULL;
+	cfg->private_domain = NULL;
+	cfg->unwanted_threshold = 0;
+	cfg->hide_identity = 0;
+	cfg->hide_version = 0;
+	cfg->identity = NULL;
+	cfg->version = NULL;
+	cfg->auto_trust_anchor_file_list = NULL;
+	cfg->trust_anchor_file_list = NULL;
+	cfg->trust_anchor_list = NULL;
+	cfg->trusted_keys_file_list = NULL;
+	cfg->dlv_anchor_file = NULL;
+	cfg->dlv_anchor_list = NULL;
+	cfg->domain_insecure = NULL;
+	cfg->val_date_override = 0;
+	cfg->val_sig_skew_min = 3600; /* at least daylight savings trouble */
+	cfg->val_sig_skew_max = 86400; /* at most timezone settings trouble */
+	cfg->val_clean_additional = 1;
+	cfg->val_log_level = 0;
+	cfg->val_log_squelch = 0;
+	cfg->val_permissive_mode = 0;
+	cfg->ignore_cd = 0;
+	cfg->add_holddown = 30*24*3600;
+	cfg->del_holddown = 30*24*3600;
+	cfg->keep_missing = 366*24*3600; /* one year plus a little leeway */
+	cfg->key_cache_size = 4 * 1024 * 1024;
+	cfg->key_cache_slabs = 4;
+	cfg->neg_cache_size = 1 * 1024 * 1024;
+	cfg->local_zones = NULL;
+	cfg->local_zones_nodefault = NULL;
+	cfg->local_data = NULL;
+	cfg->python_script = NULL;
+	cfg->remote_control_enable = 0;
+	cfg->control_ifs = NULL;
+	cfg->control_port = UNBOUND_CONTROL_PORT;
+	if(!(cfg->server_key_file = strdup(RUN_DIR"/unbound_server.key"))) 
+		goto error_exit;
+	if(!(cfg->server_cert_file = strdup(RUN_DIR"/unbound_server.pem"))) 
+		goto error_exit;
+	if(!(cfg->control_key_file = strdup(RUN_DIR"/unbound_control.key"))) 
+		goto error_exit;
+	if(!(cfg->control_cert_file = strdup(RUN_DIR"/unbound_control.pem"))) 
+		goto error_exit;
+
+	if(!(cfg->module_conf = strdup("validator iterator"))) goto error_exit;
+	if(!(cfg->val_nsec3_key_iterations = 
+		strdup("1024 150 2048 500 4096 2500"))) goto error_exit;
+	return cfg;
+error_exit:
+	config_delete(cfg); 
+	return NULL;
+}
+
+struct config_file* config_create_forlib(void)
+{
+	struct config_file* cfg = config_create();
+	if(!cfg) return NULL;
+	/* modifications for library use, less verbose, less memory */
+	free(cfg->chrootdir);
+	cfg->chrootdir = NULL;
+	cfg->verbosity = 0;
+	cfg->outgoing_num_ports = 16; /* in library use, this is 'reasonable'
+		and probably within the ulimit(maxfds) of the user */
+	cfg->outgoing_num_tcp = 2;
+	cfg->msg_cache_size = 1024*1024;
+	cfg->msg_cache_slabs = 1;
+	cfg->rrset_cache_size = 1024*1024;
+	cfg->rrset_cache_slabs = 1;
+	cfg->infra_cache_slabs = 1;
+	cfg->use_syslog = 0;
+	cfg->key_cache_size = 1024*1024;
+	cfg->key_cache_slabs = 1;
+	cfg->neg_cache_size = 100 * 1024;
+	cfg->donotquery_localhost = 0; /* allow, so that you can ask a
+		forward nameserver running on localhost */
+	cfg->val_log_level = 2; /* to fill why_bogus with */
+	cfg->val_log_squelch = 1;
+	return cfg;
+}
+
+/** check that the value passed is >= 0 */
+#define IS_NUMBER_OR_ZERO \
+	if(atoi(val) == 0 && strcmp(val, "0") != 0) return 0
+/** check that the value passed is > 0 */
+#define IS_NONZERO_NUMBER \
+	if(atoi(val) == 0) return 0
+/** check that the value passed is not 0 and a power of 2 */
+#define IS_POW2_NUMBER \
+	if(atoi(val) == 0 || !is_pow2((size_t)atoi(val))) return 0
+/** check that the value passed is yes or no */
+#define IS_YES_OR_NO \
+	if(strcmp(val, "yes") != 0 && strcmp(val, "no") != 0) return 0
+/** put integer_or_zero into variable */
+#define S_NUMBER_OR_ZERO(str, var) if(strcmp(opt, str) == 0) \
+	{ IS_NUMBER_OR_ZERO; cfg->var = atoi(val); }
+/** put integer_nonzero into variable */
+#define S_NUMBER_NONZERO(str, var) if(strcmp(opt, str) == 0) \
+	{ IS_NONZERO_NUMBER; cfg->var = atoi(val); }
+/** put integer_or_zero into unsigned */
+#define S_UNSIGNED_OR_ZERO(str, var) if(strcmp(opt, str) == 0) \
+	{ IS_NUMBER_OR_ZERO; cfg->var = (unsigned)atoi(val); }
+/** put integer_or_zero into size_t */
+#define S_SIZET_OR_ZERO(str, var) if(strcmp(opt, str) == 0) \
+	{ IS_NUMBER_OR_ZERO; cfg->var = (size_t)atoi(val); }
+/** put integer_nonzero into size_t */
+#define S_SIZET_NONZERO(str, var) if(strcmp(opt, str) == 0) \
+	{ IS_NONZERO_NUMBER; cfg->var = (size_t)atoi(val); }
+/** put yesno into variable */
+#define S_YNO(str, var) if(strcmp(opt, str) == 0) \
+	{ IS_YES_OR_NO; cfg->var = (strcmp(val, "yes") == 0); }
+/** put memsize into variable */
+#define S_MEMSIZE(str, var) if(strcmp(opt, str)==0) \
+	{ return cfg_parse_memsize(val, &cfg->var); }
+/** put pow2 number into variable */
+#define S_POW2(str, var) if(strcmp(opt, str)==0) \
+	{ IS_POW2_NUMBER; cfg->var = (size_t)atoi(val); }
+/** put string into variable */
+#define S_STR(str, var) if(strcmp(opt, str)==0) \
+	{ free(cfg->var); return (cfg->var = strdup(val)) != NULL; }
+/** put string into strlist */
+#define S_STRLIST(str, var) if(strcmp(opt, str)==0) \
+	{ return cfg_strlist_insert(&cfg->var, strdup(val)); }
+
+int config_set_option(struct config_file* cfg, const char* opt,
+        const char* val)
+{
+	S_NUMBER_OR_ZERO("verbosity:", verbosity)
+	else if(strcmp(opt, "statistics-interval:") == 0) {
+		if(strcmp(val, "0") == 0 || strcmp(val, "") == 0)
+			cfg->stat_interval = 0;
+		else if(atoi(val) == 0)
+			return 0;
+		else cfg->stat_interval = atoi(val);
+	} else if(strcmp(opt, "num_threads:") == 0) {
+		/* not supported, library must have 1 thread in bgworker */
+		return 0;
+	} else if(strcmp(opt, "outgoing-port-permit:") == 0) {
+		return cfg_mark_ports(val, 1, 
+			cfg->outgoing_avail_ports, 65536);
+	} else if(strcmp(opt, "outgoing-port-avoid:") == 0) {
+		return cfg_mark_ports(val, 0, 
+			cfg->outgoing_avail_ports, 65536);
+	} else if(strcmp(opt, "local-zone:") == 0) {
+		return cfg_parse_local_zone(cfg, val);
+	} else if(strcmp(opt, "val-override-date:") == 0) {
+		if(strcmp(val, "") == 0 || strcmp(val, "0") == 0) {
+			cfg->val_date_override = 0;
+		} else if(strlen(val) == 14) {
+			cfg->val_date_override = cfg_convert_timeval(val);
+			return cfg->val_date_override != 0;
+		} else {
+			if(atoi(val) == 0) return 0;
+			cfg->val_date_override = (uint32_t)atoi(val);
+		}
+	} else if(strcmp(opt, "local-data-ptr:") == 0) { 
+		char* ptr = cfg_ptr_reverse((char*)opt);
+		return cfg_strlist_insert(&cfg->local_data, ptr);
+	} else if(strcmp(opt, "logfile:") == 0) {
+		cfg->use_syslog = 0;
+		free(cfg->logfile);
+		return (cfg->logfile = strdup(val)) != NULL;
+	} 
+	else S_YNO("use-syslog:", use_syslog)
+	else S_YNO("extended-statistics:", stat_extended)
+	else S_YNO("statistics-cumulative:", stat_cumulative)
+	else S_YNO("do-ip4:", do_ip4)
+	else S_YNO("do-ip6:", do_ip6)
+	else S_YNO("do-udp:", do_udp)
+	else S_YNO("do-tcp:", do_tcp)
+	else S_YNO("tcp-upstream:", tcp_upstream)
+	else S_YNO("ssl-upstream:", ssl_upstream)
+	else S_STR("ssl-service-key:", ssl_service_key)
+	else S_STR("ssl-service-pem:", ssl_service_pem)
+	else S_NUMBER_NONZERO("ssl-port:", ssl_port)
+	else S_YNO("interface-automatic:", if_automatic)
+	else S_YNO("do-daemonize:", do_daemonize)
+	else S_NUMBER_NONZERO("port:", port)
+	else S_NUMBER_NONZERO("outgoing-range:", outgoing_num_ports)
+	else S_SIZET_OR_ZERO("outgoing-num-tcp:", outgoing_num_tcp)
+	else S_SIZET_OR_ZERO("incoming-num-tcp:", incoming_num_tcp)
+	else S_SIZET_NONZERO("edns-buffer-size:", edns_buffer_size)
+	else S_SIZET_NONZERO("msg-buffer-size:", msg_buffer_size)
+	else S_MEMSIZE("msg-cache-size:", msg_cache_size)
+	else S_POW2("msg-cache-slabs:", msg_cache_slabs)
+	else S_SIZET_NONZERO("num-queries-per-thread:",num_queries_per_thread)
+	else S_SIZET_OR_ZERO("jostle-timeout:", jostle_time)
+	else S_MEMSIZE("so-rcvbuf:", so_rcvbuf)
+	else S_MEMSIZE("so-sndbuf:", so_sndbuf)
+	else S_MEMSIZE("rrset-cache-size:", rrset_cache_size)
+	else S_POW2("rrset-cache-slabs:", rrset_cache_slabs)
+	else S_YNO("prefetch:", prefetch)
+	else S_YNO("prefetch-key:", prefetch_key)
+	else S_NUMBER_OR_ZERO("cache-max-ttl:", max_ttl)
+	else S_NUMBER_OR_ZERO("infra-host-ttl:", host_ttl)
+	else S_POW2("infra-cache-slabs:", infra_cache_slabs)
+	else S_SIZET_NONZERO("infra-cache-numhosts:", infra_cache_numhosts)
+	else S_STR("chroot:", chrootdir)
+	else S_STR("username:", username)
+	else S_STR("directory:", directory)
+	else S_STR("pidfile:", pidfile)
+	else S_YNO("hide-identity:", hide_identity)
+	else S_YNO("hide-version:", hide_version)
+	else S_STR("identity:", identity)
+	else S_STR("version:", version)
+	else S_STRLIST("root-hints:", root_hints)
+	else S_STR("target-fetch-policy:", target_fetch_policy)
+	else S_YNO("harden-glue:", harden_glue)
+	else S_YNO("harden-short-bufsize:", harden_short_bufsize)
+	else S_YNO("harden-large-queries:", harden_large_queries)
+	else S_YNO("harden-dnssec-stripped:", harden_dnssec_stripped)
+	else S_YNO("harden-below-nxdomain:", harden_below_nxdomain)
+	else S_YNO("harden-referral-path:", harden_referral_path)
+	else S_YNO("use-caps-for-id", use_caps_bits_for_id)
+	else S_SIZET_OR_ZERO("unwanted-reply-threshold:", unwanted_threshold)
+	else S_STRLIST("private-address:", private_address)
+	else S_STRLIST("private-domain:", private_domain)
+	else S_YNO("do-not-query-localhost:", donotquery_localhost)
+	else S_STRLIST("do-not-query-address:", donotqueryaddrs)
+	else S_STRLIST("auto-trust-anchor-file:", auto_trust_anchor_file_list)
+	else S_STRLIST("trust-anchor-file:", trust_anchor_file_list)
+	else S_STRLIST("trust-anchor:", trust_anchor_list)
+	else S_STRLIST("trusted-keys-file:", trusted_keys_file_list)
+	else S_STR("dlv-anchor-file:", dlv_anchor_file)
+	else S_STRLIST("dlv-anchor:", dlv_anchor_list)
+	else S_STRLIST("domain-insecure:", domain_insecure)
+	else S_NUMBER_OR_ZERO("val-bogus-ttl:", bogus_ttl)
+	else S_YNO("val-clean-additional:", val_clean_additional)
+	else S_NUMBER_OR_ZERO("val-log-level:", val_log_level)
+	else S_YNO("val-log-squelch:", val_log_squelch)
+	else S_YNO("log-queries:", log_queries)
+	else S_YNO("val-permissive-mode:", val_permissive_mode)
+	else S_YNO("ignore-cd-flag:", ignore_cd)
+	else S_STR("val-nsec3-keysize-iterations:", val_nsec3_key_iterations)
+	else S_UNSIGNED_OR_ZERO("add-holddown:", add_holddown)
+	else S_UNSIGNED_OR_ZERO("del-holddown:", del_holddown)
+	else S_UNSIGNED_OR_ZERO("keep-missing:", keep_missing)
+	else S_MEMSIZE("key-cache-size:", key_cache_size)
+	else S_POW2("key-cache-slabs:", key_cache_slabs)
+	else S_MEMSIZE("neg-cache-size:", neg_cache_size)
+	else S_STRLIST("local-data:", local_data)
+	else S_YNO("control-enable:", remote_control_enable)
+	else S_STRLIST("control-interface:", control_ifs)
+	else S_NUMBER_NONZERO("control-port:", control_port)
+	else S_STR("server-key-file:", server_key_file)
+	else S_STR("server-cert-file:", server_cert_file)
+	else S_STR("control-key-file:", control_key_file)
+	else S_STR("control-cert-file:", control_cert_file)
+	else S_STR("module-config:", module_conf)
+	else S_STR("python-script:", python_script)
+	else if (strcmp(opt, "outgoing-interface:") == 0) {
+		char* d = strdup(val);
+		char** oi = (char**)malloc((cfg->num_out_ifs+1)*sizeof(char*));
+		if(!d || !oi) { free(d); free(oi); return -1; }
+		if(cfg->out_ifs && cfg->num_out_ifs) {
+			memmove(oi, cfg->out_ifs, cfg->num_out_ifs*sizeof(char*));
+			free(cfg->out_ifs);
+		}
+		oi[cfg->num_out_ifs++] = d;
+		cfg->out_ifs = oi;
+	} else {
+		/* unknown or unsupported (from the set_option interface):
+		 * interface, outgoing-interface, access-control, 
+		 * stub-zone, name, stub-addr, stub-host, stub-prime
+		 * forward-zone, name, forward-addr, forward-host */
+		return 0;
+	}
+	return 1;
+}
+
+void config_print_func(char* line, void* arg)
+{
+	FILE* f = (FILE*)arg;
+	(void)fprintf(f, "%s\n", line);
+}
+
+/** collate func arg */
+struct config_collate_arg {
+	/** list of result items */
+	struct config_strlist_head list;
+	/** if a malloc error occurred, 0 is OK */
+	int status;
+};
+
+void config_collate_func(char* line, void* arg)
+{
+	struct config_collate_arg* m = (struct config_collate_arg*)arg;
+	if(m->status)
+		return;
+	if(!cfg_strlist_append(&m->list, strdup(line)))
+		m->status = 1;
+}
+
+int config_get_option_list(struct config_file* cfg, const char* opt,
+        struct config_strlist** list)
+{
+	struct config_collate_arg m;
+	memset(&m, 0, sizeof(m));
+	*list = NULL;
+	if(!config_get_option(cfg, opt, config_collate_func, &m))
+		return 1;
+	if(m.status) {
+		config_delstrlist(m.list.first);
+		return 2;
+	}
+	*list = m.list.first;
+	return 0;
+}
+
+int
+config_get_option_collate(struct config_file* cfg, const char* opt, char** str)
+{
+	struct config_strlist* list = NULL;
+	int r;
+	*str = NULL;
+	if((r = config_get_option_list(cfg, opt, &list)) != 0)
+		return r;
+	*str = config_collate_cat(list);
+	config_delstrlist(list);
+	if(!*str) return 2;
+	return 0;
+}
+
+char*
+config_collate_cat(struct config_strlist* list)
+{
+	size_t total = 0, left;
+	struct config_strlist* s;
+	char *r, *w;
+	if(!list) /* no elements */
+		return strdup("");
+	if(list->next == NULL) /* one element , no newline at end. */
+		return strdup(list->str);
+	/* count total length */
+	for(s=list; s; s=s->next)
+		total += strlen(s->str) + 1; /* len + newline */
+	left = total+1; /* one extra for nul at end */
+	r = malloc(left); 
+	if(!r)
+		return NULL;
+	w = r;
+	for(s=list; s; s=s->next) {
+		size_t this = strlen(s->str);
+		if(this+2 > left) { /* sanity check */
+			free(r);
+			return NULL;
+		}
+		snprintf(w, left, "%s\n", s->str);
+		w += this+1;
+		left -= this+1;
+	}
+	return r;
+}
+
+/** compare and print decimal option */
+#define O_DEC(opt, str, var) if(strcmp(opt, str)==0) \
+	{snprintf(buf, len, "%d", (int)cfg->var); \
+	func(buf, arg);}
+/** compare and print unsigned option */
+#define O_UNS(opt, str, var) if(strcmp(opt, str)==0) \
+	{snprintf(buf, len, "%u", (unsigned)cfg->var); \
+	func(buf, arg);}
+/** compare and print yesno option */
+#define O_YNO(opt, str, var) if(strcmp(opt, str)==0) \
+	{func(cfg->var?"yes":"no", arg);}
+/** compare and print string option */
+#define O_STR(opt, str, var) if(strcmp(opt, str)==0) \
+	{func(cfg->var?cfg->var:"", arg);}
+/** compare and print array option */
+#define O_IFC(opt, str, num, arr) if(strcmp(opt, str)==0) \
+	{int i; for(i=0; i<cfg->num; i++) func(cfg->arr[i], arg);}
+/** compare and print memorysize option */
+#define O_MEM(opt, str, var) if(strcmp(opt, str)==0) { \
+	if(cfg->var > 1024*1024*1024) {	\
+	  size_t f=cfg->var/(size_t)1000000, b=cfg->var%(size_t)1000000; \
+	  snprintf(buf, len, "%u%6.6u\n", (unsigned)f, (unsigned)b); \
+	} else snprintf(buf, len, "%u\n", (unsigned)cfg->var); \
+	func(buf, arg);}
+/** compare and print list option */
+#define O_LST(opt, name, lst) if(strcmp(opt, name)==0) { \
+	struct config_strlist* p = cfg->lst; \
+	for(p = cfg->lst; p; p = p->next) \
+		func(p->str, arg); \
+	}
+/** compare and print list option */
+#define O_LS2(opt, name, lst) if(strcmp(opt, name)==0) { \
+	struct config_str2list* p = cfg->lst; \
+	for(p = cfg->lst; p; p = p->next) \
+		snprintf(buf, len, "%s %s\n", p->str, p->str2); \
+		func(buf, arg); \
+	}
+
+int
+config_get_option(struct config_file* cfg, const char* opt, 
+	void (*func)(char*,void*), void* arg)
+{
+	char buf[1024];
+	size_t len = sizeof(buf);
+	fptr_ok(fptr_whitelist_print_func(func));
+	O_DEC(opt, "verbosity", verbosity)
+	else O_DEC(opt, "statistics-interval", stat_interval)
+	else O_YNO(opt, "statistics-cumulative", stat_cumulative)
+	else O_YNO(opt, "extended-statistics", stat_extended)
+	else O_YNO(opt, "use-syslog", use_syslog)
+	else O_DEC(opt, "num-threads", num_threads)
+	else O_IFC(opt, "interface", num_ifs, ifs)
+	else O_IFC(opt, "outgoing-interface", num_out_ifs, out_ifs)
+	else O_YNO(opt, "interface-automatic", if_automatic)
+	else O_DEC(opt, "port", port)
+	else O_DEC(opt, "outgoing-range", outgoing_num_ports)
+	else O_DEC(opt, "outgoing-num-tcp", outgoing_num_tcp)
+	else O_DEC(opt, "incoming-num-tcp", incoming_num_tcp)
+	else O_DEC(opt, "edns-buffer-size", edns_buffer_size)
+	else O_DEC(opt, "msg-buffer-size", msg_buffer_size)
+	else O_MEM(opt, "msg-cache-size", msg_cache_size)
+	else O_DEC(opt, "msg-cache-slabs", msg_cache_slabs)
+	else O_DEC(opt, "num-queries-per-thread", num_queries_per_thread)
+	else O_UNS(opt, "jostle-timeout", jostle_time)
+	else O_MEM(opt, "so-rcvbuf", so_rcvbuf)
+	else O_MEM(opt, "so-sndbuf", so_sndbuf)
+	else O_MEM(opt, "rrset-cache-size", rrset_cache_size)
+	else O_DEC(opt, "rrset-cache-slabs", rrset_cache_slabs)
+	else O_YNO(opt, "prefetch-key", prefetch_key)
+	else O_YNO(opt, "prefetch", prefetch)
+	else O_DEC(opt, "cache-max-ttl", max_ttl)
+	else O_DEC(opt, "infra-host-ttl", host_ttl)
+	else O_DEC(opt, "infra-cache-slabs", infra_cache_slabs)
+	else O_MEM(opt, "infra-cache-numhosts", infra_cache_numhosts)
+	else O_YNO(opt, "do-ip4", do_ip4)
+	else O_YNO(opt, "do-ip6", do_ip6)
+	else O_YNO(opt, "do-udp", do_udp)
+	else O_YNO(opt, "do-tcp", do_tcp)
+	else O_YNO(opt, "tcp-upstream", tcp_upstream)
+	else O_YNO(opt, "ssl-upstream", ssl_upstream)
+	else O_STR(opt, "ssl-service-key", ssl_service_key)
+	else O_STR(opt, "ssl-service-pem", ssl_service_pem)
+	else O_DEC(opt, "ssl-port", ssl_port)
+	else O_YNO(opt, "do-daemonize", do_daemonize)
+	else O_STR(opt, "chroot", chrootdir)
+	else O_STR(opt, "username", username)
+	else O_STR(opt, "directory", directory)
+	else O_STR(opt, "logfile", logfile)
+	else O_YNO(opt, "log-queries", log_queries)
+	else O_STR(opt, "pidfile", pidfile)
+	else O_YNO(opt, "hide-identity", hide_identity)
+	else O_YNO(opt, "hide-version", hide_version)
+	else O_STR(opt, "identity", identity)
+	else O_STR(opt, "version", version)
+	else O_STR(opt, "target-fetch-policy", target_fetch_policy)
+	else O_YNO(opt, "harden-short-bufsize", harden_short_bufsize)
+	else O_YNO(opt, "harden-large-queries", harden_large_queries)
+	else O_YNO(opt, "harden-glue", harden_glue)
+	else O_YNO(opt, "harden-dnssec-stripped", harden_dnssec_stripped)
+	else O_YNO(opt, "harden-below-nxdomain", harden_below_nxdomain)
+	else O_YNO(opt, "harden-referral-path", harden_referral_path)
+	else O_YNO(opt, "use-caps-for-id", use_caps_bits_for_id)
+	else O_DEC(opt, "unwanted-reply-threshold", unwanted_threshold)
+	else O_YNO(opt, "do-not-query-localhost", donotquery_localhost)
+	else O_STR(opt, "module-config", module_conf)
+	else O_STR(opt, "dlv-anchor-file", dlv_anchor_file)
+	else O_DEC(opt, "val-bogus-ttl", bogus_ttl)
+	else O_YNO(opt, "val-clean-additional", val_clean_additional)
+	else O_DEC(opt, "val-log-level", val_log_level)
+	else O_YNO(opt, "val-permissive-mode", val_permissive_mode)
+	else O_YNO(opt, "ignore-cd-flag", ignore_cd)
+	else O_STR(opt, "val-nsec3-keysize-iterations",val_nsec3_key_iterations)
+	else O_UNS(opt, "add-holddown", add_holddown)
+	else O_UNS(opt, "del-holddown", del_holddown)
+	else O_UNS(opt, "keep-missing", keep_missing)
+	else O_MEM(opt, "key-cache-size", key_cache_size)
+	else O_DEC(opt, "key-cache-slabs", key_cache_slabs)
+	else O_MEM(opt, "neg-cache-size", neg_cache_size)
+	else O_YNO(opt, "control-enable", remote_control_enable)
+	else O_DEC(opt, "control-port", control_port)
+	else O_STR(opt, "server-key-file", server_key_file)
+	else O_STR(opt, "server-cert-file", server_cert_file)
+	else O_STR(opt, "control-key-file", control_key_file)
+	else O_STR(opt, "control-cert-file", control_cert_file)
+	else O_LST(opt, "root-hints", root_hints)
+	else O_LS2(opt, "access-control", acls)
+	else O_LST(opt, "do-not-query-address", donotqueryaddrs)
+	else O_LST(opt, "private-address", private_address)
+	else O_LST(opt, "private-domain", private_domain)
+	else O_LST(opt, "auto-trust-anchor-file", auto_trust_anchor_file_list)
+	else O_LST(opt, "trust-anchor-file", trust_anchor_file_list)
+	else O_LST(opt, "trust-anchor", trust_anchor_list)
+	else O_LST(opt, "trusted-keys-file", trusted_keys_file_list)
+	else O_LST(opt, "dlv-anchor", dlv_anchor_list)
+	else O_LST(opt, "control-interface", control_ifs)
+	else O_LST(opt, "domain-insecure", domain_insecure)
+	else O_UNS(opt, "val-override-date", val_date_override)
+	/* not here:
+	 * outgoing-permit, outgoing-avoid - have list of ports
+	 * local-zone - zones and nodefault variables
+	 * local-data - see below
+	 * local-data-ptr - converted to local-data entries
+	 * stub-zone, name, stub-addr, stub-host, stub-prime
+	 * forward-zone, name, forward-addr, forward-host
+	 */
+	else return 0;
+	return 1;
+}
+
+/** initialize the global cfg_parser object */
+static void
+create_cfg_parser(struct config_file* cfg, char* filename, const char* chroot)
+{
+	static struct config_parser_state st;
+	cfg_parser = &st;
+	cfg_parser->filename = filename;
+	cfg_parser->line = 1;
+	cfg_parser->errors = 0;
+	cfg_parser->cfg = cfg;
+	cfg_parser->chroot = chroot;
+}
+
+int 
+config_read(struct config_file* cfg, const char* filename, const char* chroot)
+{
+	FILE *in;
+	char *fname = (char*)filename;
+	if(!fname)
+		return 1;
+	in = fopen(fname, "r");
+	if(!in) {
+		log_err("Could not open %s: %s", fname, strerror(errno));
+		return 0;
+	}
+	create_cfg_parser(cfg, fname, chroot);
+	ub_c_in = in;
+	ub_c_parse();
+	fclose(in);
+
+	if(cfg_parser->errors != 0) {
+		fprintf(stderr, "read %s failed: %d errors in configuration file\n",
+			cfg_parser->filename, cfg_parser->errors);
+		errno=EINVAL;
+		return 0;
+	}
+	return 1;
+}
+
+void
+config_delstrlist(struct config_strlist* p)
+{
+	struct config_strlist *np;
+	while(p) {
+		np = p->next;
+		free(p->str);
+		free(p);
+		p = np;
+	}
+}
+
+void
+config_deldblstrlist(struct config_str2list* p)
+{
+	struct config_str2list *np;
+	while(p) {
+		np = p->next;
+		free(p->str);
+		free(p->str2);
+		free(p);
+		p = np;
+	}
+}
+
+void
+config_delstubs(struct config_stub* p)
+{
+	struct config_stub* np;
+	while(p) {
+		np = p->next;
+		free(p->name);
+		config_delstrlist(p->hosts);
+		config_delstrlist(p->addrs);
+		free(p);
+		p = np;
+	}
+}
+
+void 
+config_delete(struct config_file* cfg)
+{
+	if(!cfg) return;
+	free(cfg->username);
+	free(cfg->chrootdir);
+	free(cfg->directory);
+	free(cfg->logfile);
+	free(cfg->pidfile);
+	free(cfg->target_fetch_policy);
+	free(cfg->ssl_service_key);
+	free(cfg->ssl_service_pem);
+	if(cfg->ifs) {
+		int i;
+		for(i=0; i<cfg->num_ifs; i++)
+			free(cfg->ifs[i]);
+		free(cfg->ifs);
+	}
+	if(cfg->out_ifs) {
+		int i;
+		for(i=0; i<cfg->num_out_ifs; i++)
+			free(cfg->out_ifs[i]);
+		free(cfg->out_ifs);
+	}
+	config_delstubs(cfg->stubs);
+	config_delstubs(cfg->forwards);
+	config_delstrlist(cfg->donotqueryaddrs);
+	config_delstrlist(cfg->root_hints);
+	free(cfg->identity);
+	free(cfg->version);
+	free(cfg->module_conf);
+	free(cfg->outgoing_avail_ports);
+	config_delstrlist(cfg->private_address);
+	config_delstrlist(cfg->private_domain);
+	config_delstrlist(cfg->auto_trust_anchor_file_list);
+	config_delstrlist(cfg->trust_anchor_file_list);
+	config_delstrlist(cfg->trusted_keys_file_list);
+	config_delstrlist(cfg->trust_anchor_list);
+	config_delstrlist(cfg->domain_insecure);
+	free(cfg->dlv_anchor_file);
+	config_delstrlist(cfg->dlv_anchor_list);
+	config_deldblstrlist(cfg->acls);
+	free(cfg->val_nsec3_key_iterations);
+	config_deldblstrlist(cfg->local_zones);
+	config_delstrlist(cfg->local_zones_nodefault);
+	config_delstrlist(cfg->local_data);
+	config_delstrlist(cfg->control_ifs);
+	free(cfg->server_key_file);
+	free(cfg->server_cert_file);
+	free(cfg->control_key_file);
+	free(cfg->control_cert_file);
+	free(cfg);
+}
+
+static void 
+init_outgoing_availports(int* a, int num)
+{
+	/* generated with make iana_update */
+	const int iana_assigned[] = {
+#include "util/iana_ports.inc"
+		-1 }; /* end marker to put behind trailing comma */
+
+	int i;
+	/* do not use <1024, that could be trouble with the system, privs */
+	for(i=1024; i<num; i++) {
+		a[i] = i;
+	}
+	/* create empty spot at 49152 to keep ephemeral ports available 
+	 * to other programs */
+	for(i=49152; i<49152+256; i++)
+		a[i] = 0;
+	/* pick out all the IANA assigned ports */
+	for(i=0; iana_assigned[i]!=-1; i++) {
+		if(iana_assigned[i] < num)
+			a[iana_assigned[i]] = 0;
+	}
+}
+
+int 
+cfg_mark_ports(const char* str, int allow, int* avail, int num)
+{
+	char* mid = strchr(str, '-');
+	if(!mid) {
+		int port = atoi(str);
+		if(port == 0 && strcmp(str, "0") != 0) {
+			log_err("cannot parse port number '%s'", str);
+			return 0;
+		}
+		if(port < num)
+			avail[port] = (allow?port:0);
+	} else {
+		int i, low, high = atoi(mid+1);
+		char buf[16];
+		if(high == 0 && strcmp(mid+1, "0") != 0) {
+			log_err("cannot parse port number '%s'", mid+1);
+			return 0;
+		}
+		if( (int)(mid-str)+1 >= (int)sizeof(buf) ) {
+			log_err("cannot parse port number '%s'", str);
+			return 0;
+		}
+		if(mid > str)
+			memcpy(buf, str, (size_t)(mid-str));
+		buf[mid-str] = 0;
+		low = atoi(buf);
+		if(low == 0 && strcmp(buf, "0") != 0) {
+			log_err("cannot parse port number '%s'", buf);
+			return 0;
+		}
+		for(i=low; i<=high; i++) {
+			if(i < num)
+				avail[i] = (allow?i:0);
+		}
+		return 1;
+	}
+	return 1;
+}
+
+int 
+cfg_scan_ports(int* avail, int num)
+{
+	int i;
+	int count = 0;
+	for(i=0; i<num; i++) {
+		if(avail[i])
+			count++;
+	}
+	return count;
+}
+
+int cfg_condense_ports(struct config_file* cfg, int** avail)
+{
+	int num = cfg_scan_ports(cfg->outgoing_avail_ports, 65536);
+	int i, at = 0;
+	*avail = NULL;
+	if(num == 0)
+		return 0;
+	*avail = (int*)malloc(sizeof(int)*num);
+	if(!*avail)
+		return 0;
+	for(i=0; i<65536; i++) {
+		if(cfg->outgoing_avail_ports[i])
+			(*avail)[at++] = cfg->outgoing_avail_ports[i];
+	}
+	log_assert(at == num);
+	return num;
+}
+
+/** print error with file and line number */
+static void ub_c_error_va_list(const char *fmt, va_list args)
+{
+	cfg_parser->errors++;
+	fprintf(stderr, "%s:%d: error: ", cfg_parser->filename,
+	cfg_parser->line);
+	vfprintf(stderr, fmt, args);
+	fprintf(stderr, "\n");
+}
+
+/** print error with file and line number */
+void ub_c_error_msg(const char* fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+	ub_c_error_va_list(fmt, args);
+	va_end(args);
+}
+
+void ub_c_error(const char *str)
+{
+	cfg_parser->errors++;
+	fprintf(stderr, "%s:%d: error: %s\n", cfg_parser->filename,
+		cfg_parser->line, str);
+}
+
+int ub_c_wrap(void)
+{
+	return 1;
+}
+
+int cfg_strlist_append(struct config_strlist_head* list, char* item)
+{
+	struct config_strlist *s;
+	if(!item || !list)
+		return 0;
+	s = (struct config_strlist*)calloc(1, sizeof(struct config_strlist));
+	if(!s)
+		return 0;
+	s->str = item;
+	s->next = NULL;
+	if(list->last)
+		list->last->next = s;
+	else
+		list->first = s;
+	list->last = s;
+	return 1;
+}
+
+int 
+cfg_strlist_insert(struct config_strlist** head, char* item)
+{
+	struct config_strlist *s;
+	if(!item || !head)
+		return 0;
+	s = (struct config_strlist*)calloc(1, sizeof(struct config_strlist));
+	if(!s)
+		return 0;
+	s->str = item;
+	s->next = *head;
+	*head = s;
+	return 1;
+}
+
+int 
+cfg_str2list_insert(struct config_str2list** head, char* item, char* i2)
+{
+	struct config_str2list *s;
+	if(!item || !i2 || !head)
+		return 0;
+	s = (struct config_str2list*)calloc(1, sizeof(struct config_str2list));
+	if(!s)
+		return 0;
+	s->str = item;
+	s->str2 = i2;
+	s->next = *head;
+	*head = s;
+	return 1;
+}
+
+uint32_t 
+cfg_convert_timeval(const char* str)
+{
+	uint32_t t;
+	struct tm tm;
+	memset(&tm, 0, sizeof(tm));
+	if(strlen(str) < 14)
+		return 0;
+	if(sscanf(str, "%4d%2d%2d%2d%2d%2d", &tm.tm_year, &tm.tm_mon, 
+		&tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6)
+		return 0;
+	tm.tm_year -= 1900;
+	tm.tm_mon--;
+	/* Check values */
+	if (tm.tm_year < 70)	return 0;
+	if (tm.tm_mon < 0 || tm.tm_mon > 11)	return 0;
+	if (tm.tm_mday < 1 || tm.tm_mday > 31) 	return 0;
+	if (tm.tm_hour < 0 || tm.tm_hour > 23)	return 0;
+	if (tm.tm_min < 0 || tm.tm_min > 59)	return 0;
+	if (tm.tm_sec < 0 || tm.tm_sec > 59)	return 0;
+	/* call ldns conversion function */
+	t = mktime_from_utc(&tm);
+	return t;
+}
+
+int 
+cfg_count_numbers(const char* s)
+{
+        /* format ::= (sp num)+ sp      */
+        /* num ::= [-](0-9)+            */
+        /* sp ::= (space|tab)*          */
+        int num = 0;
+        while(*s) {
+                while(*s && isspace((int)*s))
+                        s++;
+                if(!*s) /* end of string */
+                        break;
+                if(*s == '-')
+                        s++;
+                if(!*s) /* only - not allowed */
+                        return 0;
+                if(!isdigit((int)*s)) /* bad character */
+                        return 0;
+                while(*s && isdigit((int)*s))
+                        s++;
+                num++;
+        }
+        return num;
+}
+
+/** all digit number */
+static int isalldigit(const char* str, size_t l)
+{
+	size_t i;
+	for(i=0; i<l; i++)
+		if(!isdigit(str[i]))
+			return 0;
+	return 1;
+}
+
+int 
+cfg_parse_memsize(const char* str, size_t* res)
+{
+	size_t len = (size_t)strlen(str);
+	size_t mult = 1;
+	if(!str || len == 0) {
+		log_err("not a size: '%s'", str);
+		return 0;
+	}
+	if(isalldigit(str, len)) {
+		*res = (size_t)atol(str);
+		return 1;
+	}
+	/* check appended num */
+	while(len>0 && str[len-1]==' ')
+		len--;
+	if(len > 1 && str[len-1] == 'b') 
+		len--;
+	else if(len > 1 && str[len-1] == 'B') 
+		len--;
+	
+	if(len > 1 && tolower(str[len-1]) == 'g')
+		mult = 1024*1024*1024;
+	else if(len > 1 && tolower(str[len-1]) == 'm')
+		mult = 1024*1024;
+	else if(len > 1 && tolower(str[len-1]) == 'k')
+		mult = 1024;
+	else if(len > 0 && isdigit(str[len-1]))
+		mult = 1;
+	else {
+		log_err("unknown size specifier: '%s'", str);
+		return 0;
+	}
+	while(len>1 && str[len-2]==' ')
+		len--;
+
+	if(!isalldigit(str, len-1)) {
+		log_err("unknown size specifier: '%s'", str);
+		return 0;
+	}
+	*res = ((size_t)atol(str)) * mult;
+	return 1;
+}
+
+void 
+config_apply(struct config_file* config)
+{
+	MAX_TTL = (uint32_t)config->max_ttl;
+	MIN_TTL = (uint32_t)config->min_ttl;
+	EDNS_ADVERTISED_SIZE = (uint16_t)config->edns_buffer_size;
+	log_set_time_asc(config->log_time_ascii);
+}
+
+/** 
+ * Calculate string length of full pathname in original filesys
+ * @param fname: the path name to convert.
+ * 	Must not be null or empty.
+ * @param cfg: config struct for chroot and chdir (if set).
+ * @param use_chdir: if false, only chroot is applied.
+ * @return length of string.
+ *	remember to allocate one more for 0 at end in mallocs.
+ */
+static size_t
+strlen_after_chroot(const char* fname, struct config_file* cfg, int use_chdir)
+{
+	size_t len = 0;
+	int slashit = 0;
+	if(cfg->chrootdir && cfg->chrootdir[0] && 
+		strncmp(cfg->chrootdir, fname, strlen(cfg->chrootdir)) == 0) {
+		/* already full pathname, return it */
+		return strlen(fname);
+	}
+	/* chroot */
+	if(cfg->chrootdir && cfg->chrootdir[0]) {
+		/* start with chrootdir */
+		len += strlen(cfg->chrootdir);
+		slashit = 1;
+	}
+	/* chdir */
+#ifdef UB_ON_WINDOWS
+	if(fname[0] != 0 && fname[1] == ':') {
+		/* full path, no chdir */
+	} else
+#endif
+	if(fname[0] == '/' || !use_chdir) {
+		/* full path, no chdir */
+	} else if(cfg->directory && cfg->directory[0]) {
+		/* prepend chdir */
+		if(slashit && cfg->directory[0] != '/')
+			len++;
+		if(cfg->chrootdir && cfg->chrootdir[0] && 
+			strncmp(cfg->chrootdir, cfg->directory, 
+			strlen(cfg->chrootdir)) == 0)
+			len += strlen(cfg->directory)-strlen(cfg->chrootdir);
+		else	len += strlen(cfg->directory);
+		slashit = 1;
+	}
+	/* fname */
+	if(slashit && fname[0] != '/')
+		len++;
+	len += strlen(fname);
+	return len;
+}
+
+char*
+fname_after_chroot(const char* fname, struct config_file* cfg, int use_chdir)
+{
+	size_t len = strlen_after_chroot(fname, cfg, use_chdir);
+	int slashit = 0;
+	char* buf = (char*)malloc(len+1);
+	if(!buf)
+		return NULL;
+	buf[0] = 0;
+	/* is fname already in chroot ? */
+	if(cfg->chrootdir && cfg->chrootdir[0] && 
+		strncmp(cfg->chrootdir, fname, strlen(cfg->chrootdir)) == 0) {
+		/* already full pathname, return it */
+		strncpy(buf, fname, len);
+		buf[len] = 0;
+		return buf;
+	}
+	/* chroot */
+	if(cfg->chrootdir && cfg->chrootdir[0]) {
+		/* start with chrootdir */
+		strncpy(buf, cfg->chrootdir, len);
+		slashit = 1;
+	}
+#ifdef UB_ON_WINDOWS
+	if(fname[0] != 0 && fname[1] == ':') {
+		/* full path, no chdir */
+	} else
+#endif
+	/* chdir */
+	if(fname[0] == '/' || !use_chdir) {
+		/* full path, no chdir */
+	} else if(cfg->directory && cfg->directory[0]) {
+		/* prepend chdir */
+		if(slashit && cfg->directory[0] != '/')
+			strncat(buf, "/", len-strlen(buf));
+		/* is the directory already in the chroot? */
+		if(cfg->chrootdir && cfg->chrootdir[0] && 
+			strncmp(cfg->chrootdir, cfg->directory, 
+			strlen(cfg->chrootdir)) == 0)
+			strncat(buf, cfg->directory+strlen(cfg->chrootdir), 
+				   len-strlen(buf));
+		else strncat(buf, cfg->directory, len-strlen(buf));
+		slashit = 1;
+	}
+	/* fname */
+	if(slashit && fname[0] != '/')
+		strncat(buf, "/", len-strlen(buf));
+	strncat(buf, fname, len-strlen(buf));
+	buf[len] = 0;
+	return buf;
+}
+
+/** return next space character in string */
+static char* next_space_pos(const char* str)
+{
+	char* sp = strchr(str, ' ');
+	char* tab = strchr(str, '\t');
+	if(!tab && !sp)
+		return NULL;
+	if(!sp) return tab;
+	if(!tab) return sp;
+	return (sp<tab)?sp:tab;
+}
+
+/** return last space character in string */
+static char* last_space_pos(const char* str)
+{
+	char* sp = strrchr(str, ' ');
+	char* tab = strrchr(str, '\t');
+	if(!tab && !sp)
+		return NULL;
+	if(!sp) return tab;
+	if(!tab) return sp;
+	return (sp>tab)?sp:tab;
+}
+
+int 
+cfg_parse_local_zone(struct config_file* cfg, const char* val)
+{
+	const char *type, *name_end, *name;
+	char buf[256];
+
+	/* parse it as: [zone_name] [between stuff] [zone_type] */
+	name = val;
+	while(*name && isspace(*name))
+		name++;
+	if(!*name) {
+		log_err("syntax error: too short: %s", val);
+		return 0;
+	}
+	name_end = next_space_pos(name);
+	if(!name_end || !*name_end) {
+		log_err("syntax error: expected zone type: %s", val);
+		return 0;
+	}
+	if (name_end - name > 255) {
+		log_err("syntax error: bad zone name: %s", val);
+		return 0;
+	}
+	strncpy(buf, name, (size_t)(name_end-name));
+	buf[name_end-name] = '\0';
+
+	type = last_space_pos(name_end);
+	while(type && *type && isspace(*type))
+		type++;
+	if(!type || !*type) {
+		log_err("syntax error: expected zone type: %s", val);
+		return 0;
+	}
+
+	if(strcmp(type, "nodefault")==0) {
+		return cfg_strlist_insert(&cfg->local_zones_nodefault, 
+			strdup(name));
+	} else {
+		return cfg_str2list_insert(&cfg->local_zones, strdup(buf),
+			strdup(type));
+	}
+}
+
+char* cfg_ptr_reverse(char* str)
+{
+	char* ip, *ip_end;
+	char* name;
+	char* result;
+	char buf[1024];
+	struct sockaddr_storage addr;
+	socklen_t addrlen;
+
+	/* parse it as: [IP] [between stuff] [name] */
+	ip = str;
+	while(*ip && isspace(*ip))
+		ip++;
+	if(!*ip) {
+		log_err("syntax error: too short: %s", str);
+		return NULL;
+	}
+	ip_end = next_space_pos(ip);
+	if(!ip_end || !*ip_end) {
+		log_err("syntax error: expected name: %s", str);
+		return NULL;
+	}
+
+	name = last_space_pos(ip_end);
+	if(!name || !*name) {
+		log_err("syntax error: expected name: %s", str);
+		return NULL;
+	}
+
+	sscanf(ip, "%100s", buf);
+	buf[sizeof(buf)-1]=0;
+
+	if(!ipstrtoaddr(buf, UNBOUND_DNS_PORT, &addr, &addrlen)) {
+		log_err("syntax error: cannot parse address: %s", str);
+		return NULL;
+	}
+
+	/* reverse IPv4:
+	 * ddd.ddd.ddd.ddd.in-addr-arpa.
+	 * IPv6: (h.){32}.ip6.arpa.  */
+
+	if(addr_is_ip6(&addr, addrlen)) {
+		uint8_t ad[16];
+		const char* hex = "0123456789abcdef";
+		char *p = buf;
+		int i;
+		memmove(ad, &((struct sockaddr_in6*)&addr)->sin6_addr, 
+			sizeof(ad));
+		for(i=15; i>=0; i--) {
+			uint8_t b = ad[i];
+			*p++ = hex[ (b&0x0f) ];
+			*p++ = '.';
+			*p++ = hex[ (b&0xf0) >> 4 ];
+			*p++ = '.';
+		}
+		snprintf(buf+16*4, sizeof(buf)-16*4, "ip6.arpa. ");
+	} else {
+		uint8_t ad[4];
+		memmove(ad, &((struct sockaddr_in*)&addr)->sin_addr, 
+			sizeof(ad));
+		snprintf(buf, sizeof(buf), "%u.%u.%u.%u.in-addr.arpa. ",
+			(unsigned)ad[3], (unsigned)ad[2],
+			(unsigned)ad[1], (unsigned)ad[0]);
+	}
+
+	/* printed the reverse address, now the between goop and name on end */
+	while(*ip_end && isspace(*ip_end))
+		ip_end++;
+	if(name>ip_end) {
+		snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%.*s", 
+			(int)(name-ip_end), ip_end);
+	}
+	snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), " PTR %s", name);
+
+	result = strdup(buf);
+	if(!result) {
+		log_err("out of memory parsing %s", str);
+		return NULL;
+	}
+	return result;
+}
+
+void errinf(struct module_qstate* qstate, const char* str)
+{
+	struct config_strlist* p;
+	if(qstate->env->cfg->val_log_level < 2 || !str)
+		return;
+	p = (struct config_strlist*)regional_alloc(qstate->region, sizeof(*p));
+	if(!p) {
+		log_err("malloc failure in validator-error-info string");
+		return;
+	}
+	p->next = NULL;
+	p->str = regional_strdup(qstate->region, str);
+	if(!p->str) {
+		log_err("malloc failure in validator-error-info string");
+		return;
+	}
+	/* add at end */
+	if(qstate->errinf) {
+		struct config_strlist* q = qstate->errinf;
+		while(q->next) 
+			q = q->next;
+		q->next = p;
+	} else	qstate->errinf = p;
+}
+
+void errinf_origin(struct module_qstate* qstate, struct sock_list *origin)
+{
+	struct sock_list* p;
+	if(qstate->env->cfg->val_log_level < 2)
+		return;
+	for(p=origin; p; p=p->next) {
+		char buf[256];
+		if(p == origin)
+			snprintf(buf, sizeof(buf), "from ");
+		else	snprintf(buf, sizeof(buf), "and ");
+		if(p->len == 0)
+			snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), 
+				"cache");
+		else 
+			addr_to_str(&p->addr, p->len, buf+strlen(buf),
+				sizeof(buf)-strlen(buf));
+		errinf(qstate, buf);
+	}
+}
+
+char* errinf_to_str(struct module_qstate* qstate)
+{
+	char buf[20480];
+	char* p = buf;
+	size_t left = sizeof(buf);
+	struct config_strlist* s;
+	char dname[LDNS_MAX_DOMAINLEN+1];
+	char* t = ldns_rr_type2str(qstate->qinfo.qtype);
+	char* c = ldns_rr_class2str(qstate->qinfo.qclass);
+	if(!t || !c) {
+		free(t);
+		free(c);
+		log_err("malloc failure in errinf_to_str");
+		return NULL;
+	}
+	dname_str(qstate->qinfo.qname, dname);
+	snprintf(p, left, "validation failure <%s %s %s>:", dname, t, c);
+	free(t);
+	free(c);
+	left -= strlen(p); p += strlen(p);
+	if(!qstate->errinf)
+		snprintf(p, left, " misc failure");
+	else for(s=qstate->errinf; s; s=s->next) {
+		snprintf(p, left, " %s", s->str);
+		left -= strlen(p); p += strlen(p);
+	}
+	p = strdup(buf);
+	if(!p)
+		log_err("malloc failure in errinf_to_str");
+	return p;
+}
+
+void errinf_rrset(struct module_qstate* qstate, struct ub_packed_rrset_key *rr)
+{
+	char buf[1024];
+	char dname[LDNS_MAX_DOMAINLEN+1];
+	char *t, *c;
+	if(qstate->env->cfg->val_log_level < 2 || !rr)
+		return;
+	t = ldns_rr_type2str(ntohs(rr->rk.type));
+	c = ldns_rr_class2str(ntohs(rr->rk.rrset_class));
+	if(!t || !c) {
+		free(t);
+		free(c);
+		log_err("malloc failure in errinf_rrset");
+		return;
+	}
+	dname_str(rr->rk.dname, dname);
+	snprintf(buf, sizeof(buf), "for <%s %s %s>", dname, t, c);
+	free(t);
+	free(c);
+	errinf(qstate, buf);
+}
+
+void errinf_dname(struct module_qstate* qstate, const char* str, uint8_t* dname)
+{
+	char b[1024];
+	char buf[LDNS_MAX_DOMAINLEN+1];
+	if(qstate->env->cfg->val_log_level < 2 || !str || !dname)
+		return;
+	dname_str(dname, buf);
+	snprintf(b, sizeof(b), "%s %s", str, buf);
+	errinf(qstate, b);
+}
diff --git a/3rdParty/Unbound/src/src/util/config_file.h b/3rdParty/Unbound/src/src/util/config_file.h
new file mode 100644
index 0000000..050c3a0
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/config_file.h
@@ -0,0 +1,632 @@
+/*
+ * util/config_file.h - reads and stores the config file for unbound.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions for the config file.
+ */
+
+#ifndef UTIL_CONFIG_FILE_H
+#define UTIL_CONFIG_FILE_H
+struct config_stub;
+struct config_strlist;
+struct config_str2list;
+struct module_qstate;
+struct sock_list;
+struct ub_packed_rrset_key;
+
+/**
+ * The configuration options.
+ * Strings are malloced.
+ */
+struct config_file {
+	/** verbosity level as specified in the config file */
+	int verbosity;
+
+	/** statistics interval (in seconds) */
+	int stat_interval;
+	/** if false, statistics values are reset after printing them */
+	int stat_cumulative;
+	/** if true, the statistics are kept in greater detail */
+	int stat_extended;
+
+	/** number of threads to create */
+	int num_threads;
+
+	/** port on which queries are answered. */
+	int port;
+	/** do ip4 query support. */
+	int do_ip4;
+	/** do ip6 query support. */
+	int do_ip6;
+	/** do udp query support. */
+	int do_udp;
+	/** do tcp query support. */
+	int do_tcp;
+	/** tcp upstream queries (no UDP upstream queries) */
+	int tcp_upstream;
+
+	/** private key file for dnstcp-ssl service (enabled if not NULL) */
+	char* ssl_service_key;
+	/** public key file for dnstcp-ssl service */
+	char* ssl_service_pem;
+	/** port on which to provide ssl service */
+	int ssl_port;
+	/** if outgoing tcp connections use SSL */
+	int ssl_upstream;
+
+	/** outgoing port range number of ports (per thread) */
+	int outgoing_num_ports;
+	/** number of outgoing tcp buffers per (per thread) */
+	size_t outgoing_num_tcp;
+	/** number of incoming tcp buffers per (per thread) */
+	size_t incoming_num_tcp;
+	/** allowed udp port numbers, array with 0 if not allowed */
+	int* outgoing_avail_ports;
+
+	/** EDNS buffer size to use */
+	size_t edns_buffer_size;
+	/** number of bytes buffer size for DNS messages */
+	size_t msg_buffer_size;
+	/** size of the message cache */
+	size_t msg_cache_size;
+	/** slabs in the message cache. */
+	size_t msg_cache_slabs;
+	/** number of queries every thread can service */
+	size_t num_queries_per_thread;
+	/** number of msec to wait before items can be jostled out */
+	size_t jostle_time;
+	/** size of the rrset cache */
+	size_t rrset_cache_size;
+	/** slabs in the rrset cache */
+	size_t rrset_cache_slabs;
+	/** host cache ttl in seconds */
+	int host_ttl;
+	/** number of slabs in the infra host cache */
+	size_t infra_cache_slabs;
+	/** max number of hosts in the infra cache */
+	size_t infra_cache_numhosts;
+
+	/** the target fetch policy for the iterator */
+	char* target_fetch_policy;
+
+	/** automatic interface for incoming messages. Uses ipv6 remapping,
+	 * and recvmsg/sendmsg ancillary data to detect interfaces, boolean */
+	int if_automatic;
+	/** SO_RCVBUF size to set on port 53 UDP socket */
+	size_t so_rcvbuf;
+	/** SO_SNDBUF size to set on port 53 UDP socket */
+	size_t so_sndbuf;
+
+	/** number of interfaces to open. If 0 default all interfaces. */
+	int num_ifs;
+	/** interface description strings (IP addresses) */
+	char **ifs;
+
+	/** number of outgoing interfaces to open. 
+	 * If 0 default all interfaces. */
+	int num_out_ifs;
+	/** outgoing interface description strings (IP addresses) */
+	char **out_ifs;
+
+	/** the root hints */
+	struct config_strlist* root_hints;
+	/** the stub definitions, linked list */
+	struct config_stub* stubs;
+	/** the forward zone definitions, linked list */
+	struct config_stub* forwards;
+	/** list of donotquery addresses, linked list */
+	struct config_strlist* donotqueryaddrs;
+	/** list of access control entries, linked list */
+	struct config_str2list* acls;
+	/** use default localhost donotqueryaddr entries */
+	int donotquery_localhost;
+
+	/** harden against very small edns buffer sizes */
+	int harden_short_bufsize;
+	/** harden against very large query sizes */
+	int harden_large_queries;
+	/** harden against spoofed glue (out of zone data) */
+	int harden_glue;
+	/** harden against receiving no DNSSEC data for trust anchor */
+	int harden_dnssec_stripped;
+	/** harden against queries that fall under known nxdomain names */
+	int harden_below_nxdomain;
+	/** harden the referral path, query for NS,A,AAAA and validate */
+	int harden_referral_path;
+	/** use 0x20 bits in query as random ID bits */
+	int use_caps_bits_for_id;
+	/** strip away these private addrs from answers, no DNS Rebinding */
+	struct config_strlist* private_address;
+	/** allow domain (and subdomains) to use private address space */
+	struct config_strlist* private_domain;
+	/** what threshold for unwanted action. */
+	size_t unwanted_threshold;
+	/** the number of seconds maximal TTL used for RRsets and messages */
+	int max_ttl;
+	/** the number of seconds minimum TTL used for RRsets and messages */
+	int min_ttl;
+	/** if prefetching of messages should be performed. */
+	int prefetch;
+	/** if prefetching of DNSKEYs should be performed. */
+	int prefetch_key;
+
+	/** chrootdir, if not "" or chroot will be done */
+	char* chrootdir;
+	/** username to change to, if not "". */
+	char* username;
+	/** working directory */
+	char* directory;
+	/** filename to log to. */
+	char* logfile;
+	/** pidfile to write pid to. */
+	char* pidfile;
+
+	/** should log messages be sent to syslogd */
+	int use_syslog;
+	/** log timestamp in ascii UTC */
+	int log_time_ascii;
+	/** log queries with one line per query */
+	int log_queries;
+
+	/** do not report identity (id.server, hostname.bind) */
+	int hide_identity;
+	/** do not report version (version.server, version.bind) */
+	int hide_version;
+	/** identity, hostname is returned if "". */
+	char* identity;
+	/** version, package version returned if "". */
+	char* version;
+
+	/** the module configuration string */
+	char* module_conf;
+	
+	/** files with trusted DS and DNSKEYs in zonefile format, list */
+	struct config_strlist* trust_anchor_file_list;
+	/** list of trustanchor keys, linked list */
+	struct config_strlist* trust_anchor_list;
+	/** files with 5011 autotrust tracked keys */
+	struct config_strlist* auto_trust_anchor_file_list;
+	/** files with trusted DNSKEYs in named.conf format, list */
+	struct config_strlist* trusted_keys_file_list;
+	/** DLV anchor file */
+	char* dlv_anchor_file;
+	/** DLV anchor inline */
+	struct config_strlist* dlv_anchor_list;
+	/** insecure domain list */
+	struct config_strlist* domain_insecure;
+
+	/** if not 0, this value is the validation date for RRSIGs */
+	int32_t val_date_override;
+	/** the minimum for signature clock skew */
+	int32_t val_sig_skew_min;
+	/** the maximum for signature clock skew */
+	int32_t val_sig_skew_max;
+	/** this value sets the number of seconds before revalidating bogus */
+	int bogus_ttl; 
+	/** should validator clean additional section for secure msgs */
+	int val_clean_additional;
+	/** log bogus messages by the validator */
+	int val_log_level;
+	/** squelch val_log_level to log - this is library goes to callback */
+	int val_log_squelch;
+	/** should validator allow bogus messages to go through */
+	int val_permissive_mode;
+	/** ignore the CD flag in incoming queries and refuse them bogus data */
+	int ignore_cd;
+	/** nsec3 maximum iterations per key size, string */
+	char* val_nsec3_key_iterations;
+	/** autotrust add holddown time, in seconds */
+	unsigned int add_holddown;
+	/** autotrust del holddown time, in seconds */
+	unsigned int del_holddown;
+	/** autotrust keep_missing time, in seconds. 0 is forever. */
+	unsigned int keep_missing;
+
+	/** size of the key cache */
+	size_t key_cache_size;
+	/** slabs in the key cache. */
+	size_t key_cache_slabs;
+	/** size of the neg cache */
+	size_t neg_cache_size;
+
+	/** local zones config */
+	struct config_str2list* local_zones;
+	/** local zones nodefault list */
+	struct config_strlist* local_zones_nodefault;
+	/** local data RRs configged */
+	struct config_strlist* local_data;
+
+	/** remote control section. enable toggle. */
+	int remote_control_enable;
+	/** the interfaces the remote control should listen on */
+	struct config_strlist* control_ifs;
+	/** port number for the control port */
+	int control_port;
+	/** private key file for server */
+	char* server_key_file;
+	/** certificate file for server */
+	char* server_cert_file;
+	/** private key file for unbound-control */
+	char* control_key_file;
+	/** certificate file for unbound-control */
+	char* control_cert_file;
+
+	/** Python script file */
+	char* python_script;
+
+	/** daemonize, i.e. fork into the background. */
+	int do_daemonize;
+};
+
+/**
+ * Stub config options
+ */
+struct config_stub {
+	/** next in list */
+	struct config_stub* next;
+	/** domain name (in text) of the stub apex domain */
+	char* name;
+	/** list of stub nameserver hosts (domain name) */
+	struct config_strlist* hosts;
+	/** list of stub nameserver addresses (IP address) */
+	struct config_strlist* addrs;
+	/** if stub-prime is set */
+	int isprime;
+};
+
+/**
+ * List of strings for config options
+ */
+struct config_strlist {
+	/** next item in list */
+	struct config_strlist* next;
+	/** config option string */
+	char* str;
+};
+
+/**
+ * List of two strings for config options
+ */
+struct config_str2list {
+	/** next item in list */
+	struct config_str2list* next;
+	/** first string */
+	char* str;
+	/** second string */
+	char* str2;
+};
+
+/** List head for strlist processing, used for append operation. */
+struct config_strlist_head {
+	/** first in list of text items */
+	struct config_strlist* first;
+	/** last in list of text items */
+	struct config_strlist* last;
+};
+
+/**
+ * Create config file structure. Filled with default values.
+ * @return: the new structure or NULL on memory error.
+ */
+struct config_file* config_create(void);
+
+/**
+ * Create config file structure for library use. Filled with default values.
+ * @return: the new structure or NULL on memory error.
+ */
+struct config_file* config_create_forlib(void);
+
+/**
+ * Read the config file from the specified filename.
+ * @param config: where options are stored into, must be freshly created.
+ * @param filename: name of configfile. If NULL nothing is done.
+ * @param chroot: if not NULL, the chroot dir currently in use (for include).
+ * @return: false on error. In that case errno is set, ENOENT means 
+ * 	file not found.
+ */
+int config_read(struct config_file* config, const char* filename,
+	const char* chroot);
+
+/**
+ * Destroy the config file structure.
+ * @param config: to delete.
+ */
+void config_delete(struct config_file* config);
+
+/**
+ * Apply config to global constants; this routine is called in single thread.
+ * @param config: to apply. Side effect: global constants change.
+ */
+void config_apply(struct config_file* config);
+
+/**
+ * Set the given keyword to the given value.
+ * @param config: where to store config
+ * @param option: option name, including the ':' character.
+ * @param value: value, this string is copied if needed, or parsed.
+ * 	The caller owns the value string.
+ * @return 0 on error (malloc or syntax error).
+ */
+int config_set_option(struct config_file* config, const char* option,
+	const char* value);
+
+/** 
+ * Call print routine for the given option.
+ * @param cfg: config.
+ * @param opt: option name without trailing :. 
+ *	This is different from config_set_option.
+ * @param func: print func, called as (str, arg) for every data element.
+ * @param arg: user argument for print func.
+ * @return false if the option name is not supported (syntax error).
+ */
+int config_get_option(struct config_file* cfg, const char* opt, 
+	void (*func)(char*,void*), void* arg);
+
+/**
+ * Get an option and return strlist
+ * @param cfg: config file
+ * @param opt: option name.
+ * @param list: list is returned here. malloced, caller must free it.
+ * @return 0=OK, 1=syntax error, 2=malloc failed.
+ */
+int config_get_option_list(struct config_file* cfg, const char* opt,
+	struct config_strlist** list);
+
+/**
+ * Get an option and collate results into string
+ * @param cfg: config file
+ * @param opt: option name.
+ * @param str: string. malloced, caller must free it.
+ * @return 0=OK, 1=syntax error, 2=malloc failed.
+ */
+int config_get_option_collate(struct config_file* cfg, const char* opt, 
+	char** str);
+
+/**
+ * function to print to a file, use as func with config_get_option.
+ * @param line: text to print. \n appended.
+ * @param arg: pass a FILE*, like stdout.
+ */
+void config_print_func(char* line, void* arg);
+
+/**
+ * function to collate the text strings into a strlist_head.
+ * @param line: text to append.
+ * @param arg: pass a strlist_head structure. zeroed on start.
+ */
+void config_collate_func(char* line, void* arg);
+
+/**
+ * take a strlist_head list and return a malloc string. separated with newline.
+ * @param list: strlist first to collate. zeroes return "".
+ * @return NULL on malloc failure. Or if malloc failure happened in strlist.
+ */
+char* config_collate_cat(struct config_strlist* list);
+
+/**
+ * Append text at end of list.
+ * @param list: list head. zeroed at start.
+ * @param item: new item. malloced by caller. if NULL the insertion fails.
+ * @return true on success.
+ */
+int cfg_strlist_append(struct config_strlist_head* list, char* item);
+
+/**
+ * Insert string into strlist.
+ * @param head: pointer to strlist head variable.
+ * @param item: new item. malloced by caller. If NULL the insertion fails.
+ * @return: true on success.
+ */
+int cfg_strlist_insert(struct config_strlist** head, char* item);
+
+/**
+ * Insert string into str2list.
+ * @param head: pointer to str2list head variable.
+ * @param item: new item. malloced by caller. If NULL the insertion fails.
+ * @param i2: 2nd string, malloced by caller. If NULL the insertion fails.
+ * @return: true on success.
+ */
+int cfg_str2list_insert(struct config_str2list** head, char* item, char* i2);
+
+/**
+ * Delete items in config string list.
+ * @param list: list.
+ */
+void config_delstrlist(struct config_strlist* list);
+
+/**
+ * Delete items in config double string list.
+ * @param list: list.
+ */
+void config_deldblstrlist(struct config_str2list* list);
+
+/**
+ * Delete items in config stub list.
+ * @param list: list.
+ */
+void config_delstubs(struct config_stub* list);
+
+/**
+ * Convert 14digit to time value
+ * @param str: string of 14 digits
+ * @return time value or 0 for error.
+ */
+uint32_t cfg_convert_timeval(const char* str);
+
+/**
+ * Count number of values in the string.
+ * format ::= (sp num)+ sp
+ * num ::= [-](0-9)+
+ * sp ::= (space|tab)*
+ *
+ * @param str: string
+ * @return: 0 on parse error, or empty string, else
+ *	number of integer values in the string.
+ */
+int cfg_count_numbers(const char* str);
+
+/**
+ * Convert a 'nice' memory or file size into a bytecount
+ * From '100k' to 102400. and so on. Understands kKmMgG.
+ * k=1024, m=1024*1024, g=1024*1024*1024.
+ * @param str: string
+ * @param res: result is stored here, size in bytes.
+ * @return: true if parsed correctly, or 0 on a parse error (and an error 
+ * is logged).
+ */
+int cfg_parse_memsize(const char* str, size_t* res);
+
+/**
+ * Parse local-zone directive into two strings and register it in the config.
+ * @param cfg: to put it in.
+ * @param val: argument strings to local-zone, "example.com nodefault".
+ * @return: false on failure
+ */
+int cfg_parse_local_zone(struct config_file* cfg, const char* val);
+
+/**
+ * Mark "number" or "low-high" as available or not in ports array.
+ * @param str: string in input
+ * @param allow: give true if this range is permitted.
+ * @param avail: the array from cfg.
+ * @param num: size of the array (65536).
+ * @return: true if parsed correctly, or 0 on a parse error (and an error 
+ * is logged).
+ */
+int cfg_mark_ports(const char* str, int allow, int* avail, int num);
+
+/**
+ * Get a condensed list of ports returned. allocated.
+ * @param cfg: config file.
+ * @param avail: the available ports array is returned here.
+ * @return: number of ports in array or 0 on error.
+ */
+int cfg_condense_ports(struct config_file* cfg, int** avail);
+
+/**
+ * Scan ports available
+ * @param avail: the array from cfg.
+ * @param num: size of the array (65536).
+ * @return the number of ports available for use.
+ */
+int cfg_scan_ports(int* avail, int num);
+
+/** 
+ * Convert a filename to full pathname in original filesys
+ * @param fname: the path name to convert.
+ *      Must not be null or empty.
+ * @param cfg: config struct for chroot and chdir (if set).
+ * @param use_chdir: if false, only chroot is applied.
+ * @return pointer to malloced buffer which is: [chroot][chdir]fname
+ *      or NULL on malloc failure.
+ */
+char* fname_after_chroot(const char* fname, struct config_file* cfg, 
+	int use_chdir);
+
+/**
+ * Convert a ptr shorthand into a full reverse-notation PTR record.
+ * @param str: input string, "IP name"
+ * @return: malloced string "reversed-ip-name PTR name"
+ */
+char* cfg_ptr_reverse(char* str);
+
+/**
+ * Append text to the error info for validation.
+ * @param qstate: query state.
+ * @param str: copied into query region and appended.
+ * Failures to allocate are logged.
+ */
+void errinf(struct module_qstate* qstate, const char* str);
+
+/**
+ * Append text to error info:  from 1.2.3.4
+ * @param qstate: query state.
+ * @param origin: sock list with origin of trouble. 
+ *	Every element added.
+ *	If NULL: nothing is added.
+ *	if 0len element: 'from cache' is added.
+ */
+void errinf_origin(struct module_qstate* qstate, struct sock_list *origin);
+
+/**
+ * Append text to error info:  for RRset name type class
+ * @param qstate: query state.
+ * @param rr: rrset_key.
+ */
+void errinf_rrset(struct module_qstate* qstate, struct ub_packed_rrset_key *rr);
+
+/**
+ * Append text to error info:  str dname
+ * @param qstate: query state.
+ * @param str: explanation string
+ * @param dname: the dname.
+ */
+void errinf_dname(struct module_qstate* qstate, const char* str, 
+	uint8_t* dname);
+
+/**
+ * Create error info in string
+ * @param qstate: query state.
+ * @return string or NULL on malloc failure (already logged).
+ *    This string is malloced and has to be freed by caller.
+ */
+char* errinf_to_str(struct module_qstate* qstate);
+
+/**
+ * Used during options parsing
+ */
+struct config_parser_state {
+	/** name of file being parser */
+	char* filename;
+	/** line number in the file, starts at 1 */
+	int line;
+	/** number of errors encountered */
+	int errors;
+	/** the result of parsing is stored here. */
+	struct config_file* cfg;
+	/** the current chroot dir (or NULL if none) */
+	const char* chroot;
+};
+
+/** global config parser object used during config parsing */
+extern struct config_parser_state* cfg_parser;
+/** parsing helpers: print error with file and line numbers */
+void ub_c_error(const char* msg);
+/** parsing helpers: print error with file and line numbers */
+void ub_c_error_msg(const char* fmt, ...) ATTR_FORMAT(printf, 1, 2);
+
+#endif /* UTIL_CONFIG_FILE_H */
diff --git a/3rdParty/Unbound/src/src/util/configlexer.c b/3rdParty/Unbound/src/src/util/configlexer.c
new file mode 100644
index 0000000..132e42c
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/configlexer.c
@@ -0,0 +1,3850 @@
+#include "config.h"
+#include "util/configyyrename.h"
+
+#line 3 "<stdout>"
+
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types. 
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t; 
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else	/* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif	/* defined (__STDC__) */
+#endif	/* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index.  If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart(yyin  )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE   ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int yyleng;
+
+extern FILE *yyin, *yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+    #define YY_LESS_LINENO(n)
+    
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		*yy_cp = (yy_hold_char); \
+		YY_RESTORE_YY_MORE_OFFSET \
+		(yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+		YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+		} \
+	while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr)  )
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+	{
+	FILE *yy_input_file;
+
+	char *yy_ch_buf;		/* input buffer */
+	char *yy_buf_pos;		/* current position in input buffer */
+
+	/* Size of input buffer in bytes, not including room for EOB
+	 * characters.
+	 */
+	yy_size_t yy_buf_size;
+
+	/* Number of characters read into yy_ch_buf, not including EOB
+	 * characters.
+	 */
+	int yy_n_chars;
+
+	/* Whether we "own" the buffer - i.e., we know we created it,
+	 * and can realloc() it to grow it, and should free() it to
+	 * delete it.
+	 */
+	int yy_is_our_buffer;
+
+	/* Whether this is an "interactive" input source; if so, and
+	 * if we're using stdio for input, then we want to use getc()
+	 * instead of fread(), to make sure we stop fetching input after
+	 * each newline.
+	 */
+	int yy_is_interactive;
+
+	/* Whether we're considered to be at the beginning of a line.
+	 * If so, '^' rules will be active on the next match, otherwise
+	 * not.
+	 */
+	int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+    
+	/* Whether to try to fill the input buffer when we reach the
+	 * end of it.
+	 */
+	int yy_fill_buffer;
+
+	int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+	/* When an EOF's been seen but there's still some text to process
+	 * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+	 * shouldn't try reading from the input source any more.  We might
+	 * still have a bunch of tokens to match, though, because of
+	 * possible backing-up.
+	 *
+	 * When we actually see the EOF, we change the status to "new"
+	 * (via yyrestart()), so that the user can continue scanning by
+	 * just pointing yyin at a new input file.
+	 */
+#define YY_BUFFER_EOF_PENDING 2
+
+	};
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+                          ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+                          : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+static int yy_n_chars;		/* number of characters read into yy_ch_buf */
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 0;		/* whether we need to initialize */
+static int yy_start = 0;	/* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin.  A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void yyrestart (FILE *input_file  );
+void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer  );
+YY_BUFFER_STATE yy_create_buffer (FILE *file,int size  );
+void yy_delete_buffer (YY_BUFFER_STATE b  );
+void yy_flush_buffer (YY_BUFFER_STATE b  );
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer  );
+void yypop_buffer_state (void );
+
+static void yyensure_buffer_stack (void );
+static void yy_load_buffer_state (void );
+static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file  );
+
+#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size  );
+YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str  );
+YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len  );
+
+void *yyalloc (yy_size_t  );
+void *yyrealloc (void *,yy_size_t  );
+void yyfree (void *  );
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){ \
+        yyensure_buffer_stack (); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            yy_create_buffer(yyin,YY_BUF_SIZE ); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+	}
+
+#define yy_set_bol(at_bol) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){\
+        yyensure_buffer_stack (); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            yy_create_buffer(yyin,YY_BUF_SIZE ); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+	}
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+typedef unsigned char YY_CHAR;
+
+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int yylineno;
+
+int yylineno = 1;
+
+extern char *yytext;
+#define yytext_ptr yytext
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state  );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[]  );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+	(yytext_ptr) = yy_bp; \
+	(yytext_ptr) -= (yy_more_len); \
+	yyleng = (size_t) (yy_cp - (yytext_ptr)); \
+	(yy_hold_char) = *yy_cp; \
+	*yy_cp = '\0'; \
+	(yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 138
+#define YY_END_OF_BUFFER 139
+/* This struct is not used in this scanner,
+   but its presence is necessary. */
+struct yy_trans_info
+	{
+	flex_int32_t yy_verify;
+	flex_int32_t yy_nxt;
+	};
+static yyconst flex_int16_t yy_accept[1303] =
+    {   0,
+        1,    1,  120,  120,  124,  124,  128,  128,  132,  132,
+        1,    1,  139,  136,    1,  118,  118,  137,    2,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  137,  120,
+      121,  121,  122,  137,  124,  125,  125,  126,  137,  131,
+      128,  129,  129,  130,  137,  132,  133,  133,  134,  137,
+      135,  119,    2,  123,  135,  137,  136,    0,    1,    2,
+        2,    2,    2,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  120,    0,
+      124,    0,  131,    0,  128,  132,    0,  135,    0,    2,
+        2,  135,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  135,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  135,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,   62,  136,  136,  136,  136,  136,    6,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      135,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  135,  136,
+
+      136,  136,  136,   27,  136,  136,  136,  136,  136,   12,
+       13,  136,   15,   14,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  116,  136,
+      136,  136,  136,    3,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  135,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+
+      136,  136,  136,  136,  136,  136,  136,  127,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+       30,  136,  136,  136,  136,  136,  136,  136,   31,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,   75,  127,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,   74,  136,  136,  136,  136,  136,
+
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,   60,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,   20,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,   28,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,   29,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,   22,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,   25,   26,  136,  136,  136,   63,   64,
+      136,   61,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,    5,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,   77,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  102,  101,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,   32,  136,  136,  136,  136,
+
+      136,  136,   65,  136,  136,  136,  136,  136,  136,   98,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,   51,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  100,  136,  136,  136,
+      136,  136,  136,  136,    4,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,   95,  136,  136,  136,  136,
+
+      136,  136,  136,  110,   96,  136,   21,  136,  136,  136,
+      136,   67,   68,   66,  136,  136,  136,  136,  136,  136,
+       73,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+       97,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,   59,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,   17,  136,  136,  136,   16,
+      136,   82,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,   39,   40,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,   72,  136,  136,  136,  136,  136,  136,  136,  136,
+
+      136,  136,  136,  136,  136,   76,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  115,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,   86,  136,   90,  136,  136,  136,  136,   71,
+      136,  136,  108,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,   89,  136,  136,  136,
+      136,   41,   42,  136,   47,   91,  136,  103,   99,  136,
+       35,  136,   93,  136,  136,  136,  136,  136,    7,  136,
+       58,  107,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+
+      136,  136,  136,  136,  136,   78,  136,  136,  117,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,   92,   34,   36,  136,  136,  136,  136,  136,   57,
+      136,  136,  136,  111,   18,   19,  136,  136,  136,  136,
+      136,  136,   55,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  113,  136,  136,   33,  136,  136,  136,  136,
+      136,  136,   11,  136,  136,  136,  136,  136,  136,   10,
+      136,  136,   37,  136,  112,  136,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,   85,   84,  136,  114,  109,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+
+       43,  136,  136,  136,  136,  136,   38,  136,  136,  136,
+       79,   81,  136,  136,  136,   83,  136,  136,  136,  136,
+      136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+       23,  136,  136,  136,  136,  136,  136,  136,  136,  136,
+      136,  136,  136,  106,  136,  136,  136,  136,  136,  136,
+      136,   24,  136,    9,  136,  136,  104,   48,  136,  136,
+      136,   88,  136,   69,  136,  136,  136,   50,   54,   49,
+      136,   44,  136,    8,  136,  136,   87,  136,  136,  136,
+       53,  136,   45,  136,  105,  136,  136,   80,   70,   52,
+       46,  136,  136,  136,  136,   56,  136,  136,  136,  136,
+
+       94,    0
+    } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
+        1,    1,    4,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    2,    1,    5,    6,    1,    1,    1,    7,    1,
+        1,    1,    1,    1,    8,    1,    1,    1,    1,    1,
+        9,   10,    1,   11,    1,    1,    1,   12,    1,    1,
+        1,    1,    1,    1,   13,   14,   15,   16,   17,   18,
+       19,   20,   21,   22,   23,   24,   25,   26,   27,   28,
+       29,   30,   31,   32,   33,   34,   35,   36,   37,   38,
+        1,   39,    1,    1,    1,    1,   40,   41,   42,   43,
+
+       44,   45,   46,   47,   48,   49,   50,   51,   52,   53,
+       54,   55,   56,   57,   58,   59,   60,   61,   62,   63,
+       64,   65,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+static yyconst flex_int32_t yy_meta[66] =
+    {   0,
+        1,    2,    3,    4,    5,    1,    6,    1,    1,    1,
+        1,    7,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+static yyconst flex_int16_t yy_base[1317] =
+    {   0,
+        0,    0,   63,   66,   69,   71,   77,   83,   88,   91,
+      129,  135,  489,  442,   95, 3745, 3745, 3745,  107,  110,
+      142,  140,  108,   50,  159,  147,  121,  148,  158,  174,
+      191,  176,  190,  216,  225,  235,  214,  246,  116,  438,
+     3745, 3745, 3745,   94,  437, 3745, 3745, 3745,   96,  430,
+      435, 3745, 3745, 3745,  214,  333, 3745, 3745, 3745,  102,
+      299, 3745,  289, 3745,  184,  293,  249,  297,  111,    0,
+      301,    0,    0,  219,  223,  163,  272,  250,  284,  288,
+      285,  282,  309,  292,  287,  290,  302,  314,  232,  296,
+      336,  294,  319,  345,  328,  331,  330,  346,  337,  334,
+
+      358,  349,  369,  364,  355,  187,  371,  370,  137,  375,
+      385,  381,  376,  383,  393,  395,  397,  403,  239,  154,
+      229,  199,  186,  442,  176,  115,  277,   90,  449,  457,
+        0,  426,  413,  411,  431,  443,  439,  435,  432,  456,
+      464,  488,  462,  448,  466,  445,  472,  469,  473,  489,
+      495,  493,  500,  459,  496,  517,  520,  522,  519,  529,
+      525,  537,  545,  535,  544,  531,  547,  532,  552,  546,
+      543,  570,  562,  564,  549,  565,  566,  579,  569,  580,
+      593,  604,  602,  606,  589,  596,  597,  607,  609,  603,
+      619,  630,  624,  616,  639,  631,  633,  650,  651,  652,
+
+      623,  642,  658,  657,  643,  667,  645,  670,  668,  672,
+      674,  684,  675,  690,  686,  689,  694,  695,  702,  710,
+      712,  718,  713,  717,  724,  722,  740,  731,  723,  751,
+      732,  755,  745,  742,  765,  762,  761,  758,  753,  773,
+      750,  791,  782,  836,  780,  788,  778,  785,  787,  807,
+      820,  813,  815,  817,  826,  829,  840,  837,  841,  842,
+      860,  857,  872,  877,  862,  869,  884,  882,  885,  888,
+      886,  898,  894,  897,  901,  904,  920,  909,  917,  911,
+      929,  916,  938, 3745,  941,  919,  927,  936,  943, 3745,
+      931,  932,  946,  944,  966,  979,  959,  958,  984,  968,
+
+      976,  973,  971, 1004,  972,  975, 1017,  994,  996, 1000,
+     1008, 1011, 1016, 1018, 1010, 1024, 1035, 1039, 1015, 1043,
+     1050, 1063, 1052, 1047, 1055,  776, 1057, 1059, 1061, 1075,
+     1067, 1072, 1083, 1091, 1089, 1093, 1101, 1079, 1099, 1105,
+     1112, 1107, 1108, 1094, 1116, 1110, 1103, 1121, 1118, 1126,
+     1138, 1122, 1146, 1149, 1142, 1152, 1162, 1153, 1158, 1160,
+     1165, 1148, 1164, 1189, 1168, 1191, 1204, 1187, 1192, 1199,
+     1207, 1206, 1209, 1195, 1197, 1193, 1190, 1221, 1217, 1223,
+     1228, 1231, 1226, 1229, 1242, 1244, 1235, 1247, 1248, 1253,
+     1258, 1257, 1262, 1272, 1265, 1274, 1270, 1268, 1291, 1295,
+
+     1299, 1284, 1306, 3745, 1290, 1308, 1301, 1294, 1309, 3745,
+     3745, 1310, 3745, 3745, 1304, 1314, 1334, 1318, 1364, 1343,
+     1332, 1328, 1354, 1316, 1345, 1359, 1347, 1372, 1375, 1365,
+     1369, 1386, 1389, 1391, 1393, 1392, 1401, 1404, 1402, 1414,
+     1415, 1409, 1420, 1419, 1430, 1424, 1418, 1421, 3745, 1434,
+     1429, 1438, 1452, 3745, 1441, 1448, 1451, 1450, 1453, 1449,
+     1456, 1465, 1477, 1467, 1468, 1482, 1479, 1492, 1487, 1478,
+     1495, 1498, 1489, 1511, 1519, 1514, 1505, 1508, 1530, 1513,
+     1527, 1528, 1516, 1532, 1526, 1525, 1537, 1576, 1538, 1541,
+     1546, 1548, 1550, 1553, 1564, 1578, 1594, 1577, 1569, 1572,
+
+     1589, 1596, 1599, 1586, 1605, 1615, 1614, 3745, 1629, 1627,
+     1612, 1622, 1628, 1634, 1617, 1632, 1626, 1638, 1639, 1642,
+     3745, 1653, 1645, 1662, 1666, 1665, 1675, 1682, 3745, 1685,
+     1683, 1688, 1681, 1670, 1697, 1694, 1701, 1680, 1698, 1705,
+     1707, 1702, 1719, 1712, 1715, 1723, 1732, 1733, 1726, 1742,
+     1729, 1692, 1752, 1739, 1741, 1751, 1747, 1746,  396, 1757,
+     1767, 1763, 1762, 3745,   76, 1766, 1768, 1771, 1759, 1781,
+     1794, 1778, 1786, 1796, 1789, 1797, 1806, 1793, 1808, 1810,
+     1803, 1820, 1813, 1830, 1814, 1821, 1831, 1826, 1833, 1828,
+     1844, 1847, 1848, 1842, 3745, 1869, 1852, 1856, 1860, 1871,
+
+     1886, 1872, 1875, 1892, 1889, 1895, 1896, 1893, 1883, 1887,
+     1908, 1910, 1904, 1911, 1925, 1918, 3745, 1922, 1931, 1930,
+     1933, 1932, 1936, 1937, 1940, 1950, 1954, 3745, 1959, 1964,
+     1953, 1976, 1979, 1978, 1984, 1967, 1980, 1982, 1988, 1977,
+     1991, 1994, 3745, 1992, 2002, 2003, 2004, 2007, 2006, 2012,
+     2013, 2017, 2009, 2030, 2018, 2021, 2024, 2026, 2029, 2033,
+     2028, 2039, 3745, 2067, 2044, 2054, 2056, 2057, 2059, 2073,
+     2055, 2064, 2065, 2068, 2085, 2086, 2091, 2092, 2089, 2088,
+     2102, 2100, 2104, 2115, 2126, 2123, 3745, 2127, 2114, 2119,
+     2143, 2133, 2125, 2130, 2134, 2151, 2137, 2139, 2150, 2153,
+
+     2148, 2147, 2164, 2180, 2177, 2159, 2160, 2175, 2174, 2188,
+     2187, 2178, 2197, 3745, 3745, 2205, 2202, 2201, 3745, 3745,
+     2212, 3745, 2213, 2217, 2207, 2206, 2215, 2208, 2226, 2221,
+     2235, 2224, 2227, 2248, 2237, 2250, 3745, 2239, 2245, 2260,
+     2247, 2249, 2251, 2268, 2275, 2264, 2265, 2276, 2283, 3745,
+     2288, 2274, 2278, 2304, 2305, 2291, 2294, 2308, 2296, 2317,
+     2311, 2320, 2310, 2307, 2323, 2315, 2312, 2332, 2334, 2333,
+     2337, 2341, 2348, 2351, 2364, 2350, 3745, 3745, 2365, 2368,
+     2360, 2355, 2385, 2372, 2352, 2383, 2382, 2391, 2387, 2392,
+     2361, 2389, 2410, 2399, 2409, 3745, 2421, 2424, 2416, 2429,
+
+     2417, 2432, 3745, 2420, 2426, 2434, 2431, 2444, 2445, 3745,
+     2447, 2442, 2459, 2465, 2471, 2466, 2455, 2468, 2480, 2474,
+     2469, 2472, 2489, 2492, 2482, 2506, 2513, 2514, 2508, 2518,
+     2493, 2501, 2502, 2503, 2524, 2525, 2526, 2510, 2529, 3745,
+     2538, 2528, 2540, 2533, 2542, 2553, 2547, 2549, 2537, 2550,
+     2551, 2545, 2562, 2556, 2574, 2559, 3745, 2578, 2584, 2583,
+     2573, 2593, 2577, 2580, 3745, 2607, 2617, 2588, 2609, 2622,
+     2619, 2618, 2605, 2613, 2610, 2632, 2633, 2637, 2631, 2620,
+     2636, 2661, 2662, 2670, 2665, 2655, 2675, 2660, 2669, 2664,
+     2654, 2678, 2663, 2682, 2671, 3745, 2681, 2686, 2693, 2689,
+
+     2696, 2698, 2705, 3745, 3745, 2703, 3745, 2706, 2699, 2710,
+     2712, 3745, 3745, 3745, 2713, 2728, 2726, 2734, 2736, 2729,
+     3745, 2740, 2730, 2748, 2731, 2739, 2756, 2757, 2744, 2753,
+     3745, 2755, 2762, 2754, 2776, 2788, 2766, 2789, 2795, 2793,
+     2796, 2784, 2786, 2808, 2800, 3745, 2790, 2804, 2807, 2814,
+     2828, 2821, 2827, 2833, 2841, 3745, 2829, 2823, 2831, 3745,
+     2845, 3745, 2853, 2836, 2835, 2849, 2866, 2852, 2858, 2878,
+     2876, 2870, 2884, 2879, 3745, 3745, 2880, 2890, 2891, 2886,
+     2894, 2896, 2897, 2910, 2869, 2901, 2905, 2911, 2913, 2919,
+     2914, 3745, 2933, 2940, 2923, 2932, 2947, 2944, 2949, 2938,
+
+     2952, 2953, 2950, 2954, 2955, 3745, 2959, 2966, 2965, 2968,
+     2967, 2977, 2970, 2986, 2988, 2971, 2989, 3745, 2999, 2979,
+     3000, 2991, 3004, 2994, 2995, 3012, 3015, 3010, 3014, 3029,
+     3028, 3030, 3745, 3024, 3745, 3031, 3042, 3049, 3053, 3745,
+     3048, 3047, 3745, 3061, 3067, 3066, 3069, 3065, 3077, 3074,
+     3058, 3080, 3082, 3092, 3086, 3081, 3745, 3094, 3093, 3095,
+     3113, 3745, 3745, 3111, 3745, 3745, 3116, 3745, 3745, 3117,
+     3745, 3118, 3745, 3127, 3125, 3119, 3106, 3129, 3745, 3131,
+     3745, 3745, 3136, 3130, 3142, 3148, 3149, 3150, 3139, 3146,
+     3153, 3155, 3158, 3152, 3160, 3162, 3157, 3161, 3173, 3166,
+
+     3182, 3177, 3197, 3198, 3201, 3745, 3187, 3206, 3745, 3211,
+     3209, 3200, 3194, 3208, 3204, 3221, 3216, 3210, 3229, 3222,
+     3230, 3745, 3745, 3745, 3231, 3243, 3252, 3239, 3237, 3745,
+     3259, 3242, 3260, 3745, 3745, 3745, 3262, 3253, 3256, 3267,
+     3269, 3268, 3745, 3270, 3279, 3286, 3278, 3298, 3300, 3308,
+     3307, 3309, 3745, 3305, 3302, 3745, 3312, 3303, 3313, 3317,
+     3318, 3324, 3745, 3304, 3328, 3321, 3329, 3344, 3349, 3745,
+     3348, 3345, 3745, 3359, 3745, 3341, 3362, 3364, 3366, 3369,
+     3355, 3356, 3368, 3377, 3374, 3745, 3745, 3381, 3745, 3745,
+     3379, 3372, 3383, 3391, 3393, 3406, 3395, 3386, 3394, 3396,
+
+     3745, 3414, 3408, 3424, 3423, 3419, 3745, 3422, 3420, 3435,
+     3745, 3745, 3429, 3436, 3439, 3745, 3440, 3443, 3438, 3453,
+     3469, 3462, 3458, 3454, 3470, 3472, 3456, 3467, 3476, 3483,
+     3745, 3490, 3487, 3484, 3491, 3492, 3489, 3500, 3501, 3493,
+     3510, 3502, 3504, 3745, 3511, 3519, 3526, 3530, 3532, 3531,
+     3540, 3745, 3541, 3745, 3543, 3539, 3745, 3745, 3537, 3547,
+     3549, 3745, 3550, 3745, 3553, 3556, 3557, 3745, 3745, 3745,
+     3560, 3745, 3558, 3745, 3565, 3563, 3745, 3568, 3575, 3579,
+     3745, 3591, 3745, 3593, 3745, 3582, 3595, 3745, 3745, 3745,
+     3745, 3597, 3583, 3598, 3590, 3745, 3589, 3600, 3588, 3608,
+
+     3745, 3745, 3653, 3660, 3667, 3674, 3681,   82, 3688, 3695,
+     3702, 3709, 3716, 3723, 3730, 3737
+    } ;
+
+static yyconst flex_int16_t yy_def[1317] =
+    {   0,
+     1302,    1, 1303, 1303, 1304, 1304, 1305, 1305, 1306, 1306,
+     1307, 1307, 1302, 1308, 1302, 1302, 1302, 1302, 1309, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1310,
+     1302, 1302, 1302, 1310, 1311, 1302, 1302, 1302, 1311, 1312,
+     1302, 1302, 1302, 1302, 1312, 1313, 1302, 1302, 1302, 1313,
+     1314, 1302, 1315, 1302, 1314, 1314, 1308, 1308, 1302, 1316,
+     1309, 1316, 1309, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1310, 1310,
+     1311, 1311, 1312, 1312, 1302, 1313, 1313, 1314, 1314, 1315,
+     1315, 1314, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1314, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1314, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1302, 1308, 1308, 1308, 1308, 1308, 1302,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1314, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1314, 1308,
+
+     1308, 1308, 1308, 1302, 1308, 1308, 1308, 1308, 1308, 1302,
+     1302, 1308, 1302, 1302, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1302, 1308,
+     1308, 1308, 1308, 1302, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1314, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1302, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1302, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1302, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1302, 1314, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1302, 1308, 1308, 1308, 1308, 1308,
+
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1302, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1302, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1302, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1302, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1302, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1302, 1302, 1308, 1308, 1308, 1302, 1302,
+     1308, 1302, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1302, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1302,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1302, 1302, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1302, 1308, 1308, 1308, 1308,
+
+     1308, 1308, 1302, 1308, 1308, 1308, 1308, 1308, 1308, 1302,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1302,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1302, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1302, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1302, 1308, 1308, 1308, 1308,
+
+     1308, 1308, 1308, 1302, 1302, 1308, 1302, 1308, 1308, 1308,
+     1308, 1302, 1302, 1302, 1308, 1308, 1308, 1308, 1308, 1308,
+     1302, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1302, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1302, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1302, 1308, 1308, 1308, 1302,
+     1308, 1302, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1302, 1302, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1302, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+
+     1308, 1308, 1308, 1308, 1308, 1302, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1302, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1302, 1308, 1302, 1308, 1308, 1308, 1308, 1302,
+     1308, 1308, 1302, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1302, 1308, 1308, 1308,
+     1308, 1302, 1302, 1308, 1302, 1302, 1308, 1302, 1302, 1308,
+     1302, 1308, 1302, 1308, 1308, 1308, 1308, 1308, 1302, 1308,
+     1302, 1302, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+
+     1308, 1308, 1308, 1308, 1308, 1302, 1308, 1308, 1302, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1302, 1302, 1302, 1308, 1308, 1308, 1308, 1308, 1302,
+     1308, 1308, 1308, 1302, 1302, 1302, 1308, 1308, 1308, 1308,
+     1308, 1308, 1302, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1302, 1308, 1308, 1302, 1308, 1308, 1308, 1308,
+     1308, 1308, 1302, 1308, 1308, 1308, 1308, 1308, 1308, 1302,
+     1308, 1308, 1302, 1308, 1302, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1302, 1302, 1308, 1302, 1302,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+
+     1302, 1308, 1308, 1308, 1308, 1308, 1302, 1308, 1308, 1308,
+     1302, 1302, 1308, 1308, 1308, 1302, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1302, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1308, 1308, 1302, 1308, 1308, 1308, 1308, 1308, 1308,
+     1308, 1302, 1308, 1302, 1308, 1308, 1302, 1302, 1308, 1308,
+     1308, 1302, 1308, 1302, 1308, 1308, 1308, 1302, 1302, 1302,
+     1308, 1302, 1308, 1302, 1308, 1308, 1302, 1308, 1308, 1308,
+     1302, 1308, 1302, 1308, 1302, 1308, 1308, 1302, 1302, 1302,
+     1302, 1308, 1308, 1308, 1308, 1302, 1308, 1308, 1308, 1308,
+
+     1302,    0, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302,
+     1302, 1302, 1302, 1302, 1302, 1302
+    } ;
+
+static yyconst flex_int16_t yy_nxt[3811] =
+    {   0,
+       14,   15,   16,   17,   18,   19,   18,   14,   14,   14,
+       14,   18,   20,   14,   21,   22,   23,   24,   14,   25,
+       26,   27,   28,   29,   30,   31,   32,   33,   14,   34,
+       35,   36,   37,   38,   14,   14,   14,   14,   39,   20,
+       14,   21,   22,   23,   24,   14,   25,   26,   27,   28,
+       29,   30,   31,   32,   33,   14,   34,   35,   36,   37,
+       38,   14,   14,   14,   14,   41,   42,   43,   41,   42,
+       43,   46,   47,   46,   47,   48,   86,   48,   51,   52,
+       53,   54,   67,   18,   51,   52,   53,   54,   68,   18,
+       57,   58,   59,   57,   58,   59,   69,  119,  119,  121,
+
+       70,   44,  121,   86,   44,  126,  126,   49,   72,   49,
+       72,   72,   69,   72,  129,   55,   70,   67,   72,   67,
+       67,   55,   67,   84,   74,   75,   60,   67,  129,   60,
+       15,   16,   17,   62,   63,   64,   15,   16,   17,   62,
+       63,   64,   76,   85,  174,   73,   68,   92,   68,   65,
+       84,   74,   75,  127,   77,   65,   80,  119,  119,   68,
+       81,   78,   89,   82,   93,   90,   83,   66,   79,   76,
+       85,   87,   91,   66,   92,   68,   65,  125,   68,   88,
+       68,   77,   65,   80,   94,   68,   68,   81,   78,   89,
+       82,   93,   90,   83,  135,   79,   68,   68,   87,   91,
+
+       95,   68,  121,   97,   96,  121,   88,   98,  100,  132,
+      101,   94,   68,  171,   68,  123,  102,  123,  123,  103,
+      123,  135,  129,   99,  124,   68,  104,   95,   68,   68,
+       97,   96,  105,  133,   98,  100,  132,  101,  134,  115,
+      171,  108,  106,  102,  116,  107,  103,  112,  149,  113,
+       99,  109,   68,  104,   68,  110,  111,   68,  117,  105,
+      133,   68,  118,   68,  114,  134,  115,  122,  108,  106,
+       68,  116,  107,   68,  112,  149,  113,  120,  109,  137,
+      126,  126,  110,  111,   68,  117,  136,   68,   68,  118,
+       72,  114,   72,   72,  128,   72,  128,  128,   67,  128,
+
+       67,   67,   72,   67,   72,   72,  137,   72,   67,  138,
+       68,  139,   72,  136,  140,  141,  142,  144,  145,  146,
+       68,  150,   68,   68,  154,   68,   68,  131,   68,  148,
+       68,  147,   68,  143,   68,  155,  138,  129,  139,   73,
+       68,  140,  141,  159,  144,  145,  146,   68,  150,  160,
+      151,  154,   68,  152,  161,  156,  148,   68,  147,  157,
+      143,  163,  155,  158,  162,  164,   68,  153,   68,   68,
+      159,  127,   68,  165,   68,   68,  160,  151,  166,  170,
+      152,  161,  156,   68,   68,  167,  157,   68,  163,  168,
+      158,  162,  164,   68,  153,  169,   68,  176,  175,  173,
+
+      165,  172,   68,  179,  647,  166,  170,   68,   68,   68,
+      178,  182,  167,   68,   68,  180,  168,  177,  187,   68,
+      183,   68,  169,   68,  176,  175,  173,  181,  172,  186,
+      179,   68,  184,   68,   68,   68,  125,  178,  182,  192,
+      185,   68,  180,  123,  177,  123,  123,  183,  123,   68,
+      128,   68,  128,  128,  181,  128,  186,  188,   72,  184,
+       72,   72,  189,   72,  129,  190,  191,  185,  124,   68,
+       68,  194,  193,   68,  200,  122,  120,   68,  201,  203,
+       68,   68,  202,   68,  188,  205,   68,  204, 1302,  189,
+      212, 1302,  190,  191,   68,  131, 1302,   68,  206,  193,
+
+       68,  200,   68,  195,   68,  201,  203,   68,  196,  202,
+       68,   68,  205,  197,  204,  207,  211,  212,  208,  198,
+      199,  209,  210,  213,  214,  206,   68,   68, 1302,  216,
+      195,   68,  215,   68,   68,  196,  219, 1302,   68,  217,
+      197,  220,  207,  211,  221,  208,  198,  199,  209,  210,
+      213,  218,  222,  223, 1302,   68,  235,   68,   68,  215,
+       68,  224,  225,   68,  226,  227,  217,   68,  220,   68,
+       68,  228,  229,   68,  230,   68,  239, 1302,  218,  237,
+      223,   68,   68,   68,   68,   68,  231,   68,  224,  225,
+       68,  226,  227,  233,  234,  232,  236,  238,  228,  229,
+
+       68,  230,   68,   68,   68,  241,  237,   68,   68,  244,
+      240,  242,  247,  231,  250, 1302,  249,   68,   68,  245,
+      233,  234,  232,  236,  238,  251,  248,  129,  256,  252,
+      263,   68,  241,  243,   68,   68,  246,  240,  255,  247,
+       68,   68,   68,  249,   68,   68,  245,   68,  253,  254,
+      267,  257,  251,  248,   68,  256,  252,   68,  258,  259,
+      243,   68,   68,  246,  260,  255,  261,  264,   68,   68,
+      265,   68,  262,  266,  269,  253,  254,   68,  257, 1302,
+       68,   68,  275,   68,  272,  258,  259, 1302,   68,   68,
+       68,  260,  271,  261,  264,   68,   68,  265,  268,  262,
+
+      266,  269,  270,  273,  276,   68,   68,  274,   68,  277,
+       68,  272,   68,   68,  280,  282,  283,  278,  281,  271,
+      279,  284,   68, 1302,   68,  268,  285,   68,   68,  270,
+      273,  276,   68,   68,  274,  290,  277,  289,  291,  288,
+       68,  280,  282,  283,  278,  281,  286,  279,   68,  287,
+       68,   68,  292,  285,  294,   68,   68,  293,  295,  298,
+       68,   68,   68,  296,  289,  291,  288,  299,  304,   68,
+       68,  297, 1302,  286,  305,  308,  287, 1302,   68,  292,
+       68,  294,  303,   68,  293,  306,  298,  404,   68,   68,
+      296,   68,  300,   68,  299,  301,   68,  302,  297,   68,
+
+       68,  305,  308,   68,  307,  309,  319,  311,  320,  303,
+      321,   68,  306,  323,   68,  322,  129, 1302,   68,  300,
+       68,  310,  301,   68,  302,   68,   68,  325, 1302,   68,
+     1302,  307,  309,  319,  311,  320, 1302,  321,  324, 1302,
+      323,  327,  322,  328,  326,   68,  332,  333,  310,  312,
+      313,   68, 1302,   68,  330,   68,  331,  329,   68,  314,
+     1302,  315,  316,  317,   68,  324,  318,   68,  327,  335,
+      328,  326,  334, 1302,   68,   68,  312,  313,   68,   68,
+       68,  330,  337,  331,  329,  338,  314,  336,  315,  316,
+      317,  340,  339,  318,  341,   68,  335, 1302,   68,  334,
+
+       68,  345,  344,  346,  342,  348,  347,   68, 1302,  337,
+       68,  349,  338,  350,  336,   68,  352,  343,  340,  339,
+       68,  341,   68,   68,   68,  351,   68,  353,  345,  344,
+      346,  342,   68,  347,  356,   68,   68,  355,  349,   68,
+      350,  354,   68,  352,  343,  357,  362,   68,  358,   68,
+      359,  361,  351,  360,   68,   68,  363,   68,   68, 1302,
+      368,  356,  365,  366,  355,   68,  364,   68,  354,   68,
+       68,  367,  357,  362,   68,  358,   68,  359,  361,   68,
+      360,   68,   68,  363,   68,  369,  370,  368,  371,  365,
+      366,  372,  375,  364,  374, 1302,   68,   68,  367,  373,
+
+      376,  377,  383,  382,   68, 1302,   68, 1302,  387,   68,
+       68,   68,  369,   68,   68,  371,  378,   68,  372,  375,
+      389,  374,   68,  379,  384,  386,  373,  376,  377,  383,
+      382,  380,   68,  385,   68,  387,  388,  390,   68,  391,
+      393,  381,   68,  378,  392,  397,   68,  389,   68,   68,
+      379,  395,  386,   68,   68,   68,   68,  394,  380,  396,
+      385, 1302,   68,  388,  390,  399,  391,  393,  381,  398,
+      400,  392,  397,   68, 1302,  401,  402,   68,  395,  403,
+      405,   68,  406,  410,  394,   68,  396,  407,  129,  408,
+       68,  409,  399,   68,  411,   68,  398,   68,  412,   68,
+
+      413,   68,  401,  402,  414,   68,  403,  405,  415,  406,
+       68,  416, 1302,   68,  407,  417,  408,   68,  409,  419,
+      418,   68,  420,  423,  421,  422,  424,   68,  425,   68,
+      429,   68,   68,  430, 1302,  426,  432,   68,  416,   68,
+      427,   68,  417,   68,  428,   68,   68,  418,   68,  420,
+       68,  421,  422,  424,   68,  425,   68,  429,  431,   68,
+       68,  433,  426,  432,   68,  435,  436,  427,  437,  438,
+      439,  428,  440, 1302,  441, 1302,   68,  443, 1302, 1302,
+       68,  442,  446,  434,   68,  431,   68,   68,  433,  444,
+       68,   68,  435,  436,  450,  437,   68,  439,   68,  440,
+
+       68,  441,   68,   68,  443,  445,   68,  447,  442,  446,
+      434,  448,  451,  452,  453,  449,  444, 1302,  454,  455,
+     1302,  460,  456,  459,  457,   68,  458,   68,   68,   68,
+       68,   68,  445,   68,  447,   68,  461,   68,  465,  451,
+      452,  467,   68,  462,   68,   68,  455,   68,  460,  456,
+      459,  457,  463,  458,  464,   68,  466,  468, 1302,   68,
+      469,   68,  470,  461,   68, 1302,   68,   68,  467,   68,
+      462,  473,  472,   68,  474,  475, 1302,  471,  476,  463,
+       68,  464,   68,  466,  468,   68,   68,  469,  477,  470,
+      480,   68,  479,  481,  478,   68,   68,  488,  473,  472,
+
+       68,  474,  475,   68,  471,  476,   68,  482,   68,  483,
+       68, 1302,   68,  491,  484,  477,  485,  480,  486,  479,
+      481,  478,   68,  489,  494,  497,  487,  508,   68,  129,
+      490,  495,   68,   68,  482,  492,  483,   68,  493,   68,
+      491,  484,   68,  485,   68,  486,   68,   68,   68,  496,
+      489,  494,   68,  487,   68, 1302,   68,  490,  495,  504,
+     1302,  505,  492,  509,  506,  493,   68, 1302,  507, 1302,
+       68,  510,   68,  511, 1302, 1302,  496,  498, 1302,  499,
+     1302,   68,  500,   68,  512,   68,  504,  501,  505,  513,
+      509,  506,   68,  502,  503,  507,  514,   68,  510,  515,
+
+      511,  517,   68,   68,  498,  516,  499,   68,  520,  500,
+       68,  512,  521,   68,  501, 1302,  513,  518,  522,  523,
+      502,  503,  519,  514,   68,  531,  515,   68,  517,   68,
+       68,   68,  516,  524,  525,  520,  527,  528,  526,   68,
+       68,  529,   68,  530,  518,  522,  523,   68,  533,  519,
+      535,  532,   68,   68,  534, 1302,   68,   68,   68,   68,
+      524,  525,   68,  527,  528,  526,  536,   68,   68,  543,
+      530,  544,   68,  538,  537,  533,   68,  535,  532,   68,
+      539,  534,  540,  541,  542,  548,   68,   68,   68,   68,
+       68,   68,  547,  536,   68,  545,  543,  546,  544,  551,
+
+      538,  537,  552,   68,  550,   68,   68,  539,  553,  540,
+      541,  542,  548,  549,  555,   68,   68,   68,  554,  547,
+       68,  556,  545,  557,  546,   68,  558,   68,  559,  552,
+       68,  550,  561,   68,  560,  553,   68,  562,  564,  565,
+      549,  555,  566,   68,  563,  554,   68,  567,  556,   68,
+      557,   68,   68,  576,   68,  559,  568,   68, 1302,  561,
+      569,  560,  570,   68,   68,   68,  129, 1302,   68,  566,
+       68,  563,  578,  579,  567,   68,   68,  577,  581,   68,
+      576,  582,  580,  568,   68,  583,   68,  569,   68,  570,
+      571,   68,  572,  587,  588,  589,  573, 1302,  574,  578,
+
+      579,  590,   68,  575,  577,  581,  584,   68,  582,  580,
+       68,  593,  591,  585,   68,   68,   68,  571,  592,  572,
+      587,  588,  589,  573,   68,  574,  595,   68,  590,  596,
+      575,  586,   68,  584,   68,  594,  597,   68,  593,  591,
+      585,  598,  599,   68,  601,  592,  600,  603,  604,  608,
+       68, 1302,   68,   68,  602,   68,  596,  605,  586,  607,
+       68,  610,  594,  606,   68,   68,   68,   68,  598,  599,
+       68,  601,   68,  600,  603,  604,   68,   68,  611,  609,
+       68,  602,  612,   68,  605,  613,  607,  614,  610,  615,
+      606,   68,  616, 1302, 1302,  618,  617,  626,  619,  640,
+
+       68,  622,  620,   68,   68,  611,  609,  621,   68,  612,
+      624,  623,  613,   68,  614,  627,  628,  625,   68,   68,
+       68,   68,  618,   68,  626,  619,   68,  629,  622,  620,
+       68,  630,   68,  631,  621,   68,   68,  624,  623,   68,
+       68,  632,  627,   68,  625,   68,  633,  634,  635,  636,
+       68,  639,  643,   68,  629,  637,  638,   68,  630,  641,
+      631,   68, 1302, 1302,   68,  642,  655,   68,  632,  646,
+       68,   68,  645,  633,  634,  635,  636,   68,  639,   68,
+       68,  644,  637,  638,   68,   68,  648,  649,  656,   68,
+       68,  652,  642,  650,  653,   68,  646,   68,  651,  645,
+
+       68,   68,  654,  658,   68,   68,   68, 1302,  644,   68,
+      657,  659,  660,  648,  649,  661,   68,  663,  652,   68,
+      650,  653,  664,  662,   68,  651,  666,   68,  665,  654,
+      658,   68,   68,  667,   68,   68,  668,  657,  659,  660,
+      671,   68,  661,  669,   68,  670,   68,  672,   68,  664,
+      662,   68,   68,  666,  673,  665,  674,  676,   68,   68,
+      667,  677,  680,  668,   68,  675,   68,  671,   68,   68,
+      669,   68,  670,  678,  672,  683,  681,  682, 1302,  679,
+       68,  673,   68,  674,  676,   68,   68,  685,  677,  680,
+       68,  684,  675,  686,   68,  689,  688,  687,   68,  690,
+
+      678,  691,  683, 1302,  682,  694,  679,   68,  695,   68,
+       68,  692,  693,   68,  685,  697,  696,  698,  684, 1302,
+      699,   68,  689,  688,   68,   68,  700,   68,  691, 1302,
+       68,   68,  694,   68,   68,  695, 1302,  706,  692,  693,
+      705, 1302,   68,  696, 1302,  701,   68,  699,   68,   68,
+      702, 1302,  703,  700,  704,  711,   68,  707,  709,  708,
+       68,  714,  710,   68,  706,  715,  712,  705,   68,   68,
+       68,   68,  701,  716,   68,   68,  713,  702,   68,  703,
+      717,  704,  711,  718,  707,  709,  708,  719,   68,  710,
+      720,   68,   68,  712,  721,  722,  724,   68,  723,  730,
+
+      716,  725,   68,  713,  726,   68,  727,  717,  728,  731,
+      718,  733,  729, 1302,   68,   68,   68,   68,   68,  732,
+       68,  721,   68,  724,  737,  723,   68,  734,  725,   68,
+       68,  726,   68,  727,  736,  728,  735,  740,  744,  729,
+       68,   68,   68,  739,   68,   68,  732,   68,  738,  741,
+       68,   68,  742,  743,  734,   68,   68,  747, 1302,   68,
+      745,  736,   68,  735,   68,  744,   68,   68,   68,  746,
+      739,   68,  753,  748,  749,  738,  741,   68,  750,  742,
+      743,  751,   68,  752,  747,  757,  754,  745,  756,  758,
+      755,  759,   68,   68,   68,   68,  746,   68,  760,  753,
+
+      748,  761,   68,   68,  762,   68,   68,  763,  751,  765,
+      752,   68,  757,  754,  766,  756,  758,  755,  759,  767,
+      769,  764,  770,   68,   68,  760,   68,   68,  761,   68,
+       68,  762,  768,  771,  763,  772,  765, 1302,   68,  774,
+       68,  766,   68,  773,  778, 1302,  767,  769,  764,  775,
+      776,  781,   68,   68,  777,  779, 1302,   68,  782,  768,
+      780,   68,  772,   68,   68,   68,  774,  783,   68,  784,
+      773,   68,   68,  787,  792,   68,  775,   68,  781,  788,
+      785,   68,  779,  786,  793,   68,   68,  780,   68,   68,
+      789,   68,  790,  791,  783,  794,  784,   68,   68,  796,
+
+      787,  792,   68,  797,  799,  795,  788,  785,  802,  798,
+      786,  793,   68,   68,  801,   68,   68,  789,   68,  790,
+      791,  800,  794,  803, 1302,   68,   68,  804, 1302,  805,
+      797,  808,  795,  806,  809,   68,  798,  810, 1302,   68,
+       68,  801,  807,   68,   68,   68,   68,  812,  800,  814,
+       68,   68,  811,   68,  804,   68,  805,  813,  808,   68,
+      806,  809,   68,  815,   68,   68,  817,  816,  818,  807,
+      819,  826,  820,   68,  812,   68,  814,   68,  821,  811,
+      822,  824,  823,   68,  813,   68,   68,   68,   68,   68,
+      815,  825,  832,  817,  816,  818,  827,  819,   68,  820,
+
+      829,  828,   68,   68,  830,  821,   68,  822,  824,  823,
+      831,  833,   68,   68,   68, 1302,   68,  834,  825,  832,
+      835,   68,  840,  827,  837,  836,   68,  829,  828,   68,
+      838,  830,   68,  839,   68,  846,  841,  831,  843,  842,
+      845,  848,   68,   68,  834,   68,   68,  835,   68,   68,
+       68,  837,  836,   68,  844,   68,  847,  838,   68,  864,
+      839,   68,  846,  841,  849,  843,  842,  845,  850,  853,
+       68,   68,   68,  851,  852,   68,  857,  856, 1302,   68,
+      859,  844,  858,  847,  854,  860,   68,  855,   68,   68,
+       68,  849,  863,   68,  865,  850,  853,  870,   68,   68,
+
+      851,  852,   68,   68,  856,  861,   68,  859,  862,  858,
+       68,  854,  860,  866,  855,  867,  868,  869,  871,  863,
+       68,   68,  872,   68,  870,   68,  873,   68,  875,   68,
+       68,  876,  861,  877, 1302,  862,  878,   68,  874,  882,
+      866,  879,  867,  868,  869,  871,  880,   68,   68,  872,
+      883, 1302,  881,  873,   68,   68, 1302,  889,   68,   68,
+      877,  885,   68,  884,   68,  874,  882,   68,  879,   68,
+       68,  886,   68,  880,  887,  890, 1302,  883,  888,  881,
+       68,  891,   68,   68,  889,   68,  893,  892,  885,  894,
+      884,  896,  898,   68,  895,  899,  900,   68,  886,  897,
+
+      908,  887,  890,   68,   68,  888,   68,   68,  891,   68,
+       68,  902,   68,  893,  892,  901,  894,  915,   68,  898,
+       68,  895,  899,  903,  904,  905,  897,   68,  906,  907,
+       68,   68,  910,  909,  911,  912,  913,  914,  902,   68,
+       68,   68,  901,  916,   68,  917,   68,  919,   68, 1302,
+      903,   68,   68,  921,  923,  906,   68,  918, 1302,  910,
+      909,  911,   68,   68,   68,  922,   68,   68,  927,  920,
+      916,   68,  924,  931,  925,   68,   68,  930,   68,  926,
+       68,  928,  929,   68,  918,   68,  933,   68,   68,   68,
+      934,   68,  922,  932,   68,  927,  920,   68,  935,  924,
+
+       68,  925,  936,  937,  930,  939,  926,  941,  928,  929,
+      938,   68,   68,  933,  940,   68,   68,  934,   68,  944,
+      932,   68,   68,  942,  943,  935,   68,  945, 1302,  936,
+      937,   68,  939,  946,  941,  947,  949,  938,  948,  950,
+      951,  940,  956,   68, 1302,   68,  944,   68,   68,  952,
+      942,   68,  957,  953,  945,   68,   68,   68,   68,  954,
+       68,  958,  947,  949,  955,  948,  950,  951,  959,   68,
+       68,   68,  963,  960,   68,   68,  952,  961,  964,  957,
+      953,  962,  965,  966,  967,  970,  954,  968,  958,  972,
+      969,  955,   68,   68,  973,  974,  971,  975,   68,   68,
+
+       68,   68,   68,   68,  976,  964,  977,   68,   68,   68,
+      966,  967,  978,   68,  968,  979,   68,  969,  982,   68,
+       68,  973,  974,  971,   68,  980,  981,   68,  984,  983,
+      985,   68,  986,  977,   68,  988,   68,   68,  987,  978,
+      992,   68,  979,   68,   68,  982,  990, 1302,   68,  991,
+       68,   68,  980,  981,  989,  984,  983,  985,  993,  986,
+      995,  994,  997,  996,   68,  987,   68,   68,   68,   68,
+     1000, 1002,   68,  990,   68, 1003,  991,   68,   68,  998,
+      999,  989,   68, 1004, 1001,  993,   68,  995,  994,  997,
+      996,   68,   68,   68,   68,   68, 1005, 1000, 1002, 1006,
+
+       68, 1302, 1003, 1007,   68, 1008,  998,  999, 1009, 1010,
+     1004, 1001, 1011, 1012,   68, 1014, 1015, 1013, 1018, 1302,
+     1016, 1302,   68, 1005,   68, 1302,   68,   68,   68, 1017,
+     1007,   68, 1008,   68,   68, 1009, 1010, 1019,   68, 1011,
+     1012, 1022,   68, 1015, 1013,   68,   68, 1016, 1020, 1024,
+     1023, 1021,   68, 1026, 1027, 1032, 1017, 1025, 1028,   68,
+     1033,   68, 1029, 1035, 1019,   68,   68,   68, 1022,   68,
+     1030,   68, 1031,   68,   68, 1020, 1024, 1023, 1021,   68,
+     1026, 1027, 1032,   68, 1025, 1028, 1034,   68, 1036, 1029,
+       68,   68, 1038, 1037, 1039, 1040,   68, 1030, 1041, 1031,
+
+     1042, 1043, 1302, 1044,   68, 1302, 1050,   68,   68, 1045,
+     1046, 1047, 1302, 1034,   68, 1036,   68,   68,   68, 1038,
+     1037, 1039,   68, 1048,   68, 1041, 1049, 1042,   68,   68,
+     1044, 1051,   68, 1050,   68,   68, 1045, 1046, 1047,   68,
+     1052, 1053, 1055,   68, 1057, 1054, 1056, 1059,   68,   68,
+     1048,   68,   68, 1049, 1058, 1062, 1060,   68, 1051, 1061,
+     1063,   68, 1064, 1065, 1066, 1068, 1069, 1052, 1053, 1055,
+       68,   68, 1054, 1056, 1059, 1070,   68, 1071,   68, 1073,
+     1067, 1058,   68, 1060, 1076,   68, 1061,   68,   68, 1064,
+       68,   68,   68,   68, 1075, 1072, 1074,   68, 1077, 1079,
+
+     1081, 1080, 1070,   68,   68,   68,   68, 1067,   68,   68,
+     1082, 1076, 1084, 1078, 1085,   68, 1083,   68, 1302, 1088,
+     1086, 1075, 1072, 1074,   68, 1077,   68,   68, 1080,   68,
+     1087, 1090,   68,   68, 1092, 1094, 1091,   68,   68, 1084,
+     1078, 1085,   68, 1083, 1089, 1095, 1088, 1086,   68, 1093,
+       68, 1097,   68,   68, 1098, 1096, 1099, 1087, 1090, 1302,
+     1302, 1092,   68, 1091, 1302, 1100,   68,   68,   68,   68,
+     1103, 1089, 1095, 1101, 1102, 1104, 1093, 1106, 1097, 1108,
+       68, 1098, 1096, 1105, 1107,   68,   68,   68, 1109, 1111,
+     1110,   68, 1100, 1115, 1302, 1112,   68, 1103, 1116,   68,
+
+     1101, 1102, 1104,   68,   68,   68, 1108,   68, 1114, 1118,
+     1105, 1107,   68, 1113, 1119,   68, 1111, 1110,   68,   68,
+       68, 1117, 1112, 1121,   68, 1116, 1120, 1122, 1123, 1124,
+       68,   68,   68,   68, 1125, 1114, 1118, 1126, 1302, 1128,
+     1113, 1119, 1130, 1132,   68, 1129, 1127, 1302, 1117,   68,
+     1121,   68, 1131, 1120,   68,   68,   68,   68, 1133, 1134,
+     1135, 1136, 1137,   68, 1126,   68, 1128,   68,   68,   68,
+     1132, 1143, 1129, 1127,   68, 1138, 1139,   68, 1140, 1131,
+       68, 1141, 1144, 1142,   68, 1133,   68,   68,   68, 1137,
+       68,   68, 1146,   68, 1145,   68,   68, 1147,   68,   68,
+
+       68, 1148, 1138, 1139,   68, 1140, 1150, 1149, 1141, 1144,
+     1142,   68, 1153, 1151, 1152,   68, 1154, 1164, 1155, 1146,
+       68, 1145, 1156, 1159, 1147,   68, 1158, 1163, 1148, 1157,
+     1302, 1161,   68, 1150, 1149,   68,   68, 1160,   68,   68,
+     1151, 1152,   68, 1154,   68, 1155,   68,   68,   68,   68,
+     1159, 1162, 1166, 1158,   68, 1165, 1157, 1169, 1161,   68,
+       68, 1167, 1168, 1170, 1160, 1171, 1172,   68,   68,   68,
+     1173, 1175, 1174, 1302, 1176,   68, 1178,   68, 1162, 1166,
+       68,   68, 1165, 1179, 1169, 1180, 1177, 1181, 1167, 1168,
+       68,   68, 1171, 1172,   68, 1183, 1182,   68,   68, 1174,
+
+       68, 1176, 1184, 1178, 1185,   68,   68,   68,   68, 1186,
+     1179, 1187, 1180, 1177, 1181, 1188,   68,   68, 1189, 1302,
+     1190, 1191, 1183, 1182,   68, 1192, 1193, 1194, 1302, 1184,
+     1197, 1185, 1201, 1195, 1199, 1302,   68, 1196,   68, 1302,
+       68,   68,   68,   68, 1198,   68,   68,   68, 1191, 1202,
+       68,   68, 1192, 1193, 1194,   68,   68, 1197, 1200,   68,
+     1195, 1199,   68, 1203, 1196, 1204,   68,   68, 1205, 1206,
+     1207, 1198, 1208, 1302, 1209, 1215, 1202, 1211, 1210,   68,
+     1212, 1214,   68,   68, 1213, 1200,   68,   68, 1216, 1217,
+     1203, 1220, 1204,   68,   68, 1205, 1206,   68, 1218, 1208,
+
+       68, 1209,   68, 1222,   68, 1210,   68,   68, 1214, 1219,
+       68, 1213,   68, 1221, 1227,   68, 1217,   68, 1220,   68,
+     1223,   68, 1224, 1226,   68, 1218, 1225, 1228, 1229,   68,
+     1222,   68,   68,   68,   68, 1231, 1219, 1230, 1232, 1233,
+     1221, 1227, 1234, 1235,   68, 1237,   68, 1223, 1238, 1224,
+     1226, 1302,   68, 1225, 1228, 1229, 1240,   68,   68, 1239,
+       68,   68,   68, 1241, 1230, 1232, 1233,   68, 1242, 1234,
+     1235, 1236, 1237,   68,   68, 1238,   68,   68,   68, 1243,
+     1244,   68, 1245, 1240, 1247, 1246, 1239, 1252, 1249, 1248,
+     1241,   68,   68, 1250,   68, 1242,   68, 1251, 1236, 1253,
+
+       68, 1254, 1257, 1258, 1262,   68, 1243,   68,   68, 1245,
+       68, 1247, 1246, 1264,   68, 1249, 1248, 1256, 1255, 1259,
+     1250,   68,   68, 1260, 1251,   68, 1253,   68,   68,   68,
+       68,   68, 1261, 1263, 1265, 1267, 1266, 1268,   68,   68,
+       68, 1269,   68, 1270, 1256, 1255, 1259, 1271,   68,   68,
+     1260, 1272, 1302, 1273, 1274, 1275, 1276,   68, 1277, 1261,
+     1263, 1265, 1267, 1266,   68, 1278, 1279, 1281,   68,   68,
+       68, 1283, 1282, 1284, 1271,   68, 1285,   68,   68,   68,
+     1273,   68, 1275, 1276, 1280,   68, 1288,   68,   68, 1286,
+     1289,   68, 1278, 1279,   68,   68,   68, 1287,   68, 1282,
+
+     1284,   68, 1290,   68, 1291, 1292,   68, 1293, 1302, 1296,
+     1297, 1280, 1294,   68, 1295, 1298, 1286,   68, 1300, 1301,
+       68,   68, 1302, 1302, 1287, 1299,   68,   68,   68,   68,
+     1302,   68, 1292,   68, 1293,   68,   68, 1297,   68, 1294,
+     1302, 1295, 1298, 1302, 1302, 1300,   68, 1302, 1302, 1302,
+     1302, 1302, 1299,   40,   40,   40,   40,   40,   40,   40,
+       45,   45,   45,   45,   45,   45,   45,   50,   50,   50,
+       50,   50,   50,   50,   56,   56,   56,   56,   56,   56,
+       56,   61,   61,   61,   61,   61,   61,   61,   71,   71,
+     1302,   71,   71,   71,   71,  119,  119, 1302, 1302, 1302,
+
+      119,  119,  121,  121, 1302, 1302,  121, 1302,  121,  123,
+     1302, 1302, 1302, 1302, 1302,  123,  126,  126, 1302, 1302,
+     1302,  126,  126,  128, 1302, 1302, 1302, 1302, 1302,  128,
+      130,  130, 1302,  130,  130,  130,  130,   72,   72, 1302,
+       72,   72,   72,   72,   13, 1302, 1302, 1302, 1302, 1302,
+     1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302,
+     1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302,
+     1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302,
+     1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302,
+     1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302,
+
+     1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302
+    } ;
+
+static yyconst flex_int16_t yy_chk[3811] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    3,    3,    3,    4,    4,
+        4,    5,    5,    6,    6,    5,   24,    6,    7,    7,
+        7,    7, 1308,    7,    8,    8,    8,    8,   24,    8,
+        9,    9,    9,   10,   10,   10,   15,   44,   44,   49,
+
+       15,    3,   49,   24,    4,   60,   60,    5,   19,    6,
+       19,   19,   69,   19,  565,    7,   69,   39,   19,   39,
+       39,    8,   39,   23,   20,   20,    9,   39,  128,   10,
+       11,   11,   11,   11,   11,   11,   12,   12,   12,   12,
+       12,   12,   20,   23,  109,   19,   23,   27,   20,   11,
+       23,   20,   20,  126,   21,   12,   22,  120,  120,   27,
+       22,   21,   26,   22,   28,   26,   22,   11,   21,   20,
+       23,   25,   26,   12,   27,  109,   11,  125,   22,   25,
+       21,   21,   12,   22,   29,   26,   28,   22,   21,   26,
+       22,   28,   26,   22,   76,   21,   29,   25,   25,   26,
+
+       30,   76,  122,   31,   30,  122,   25,   31,   32,   65,
+       33,   29,   30,  106,   32,   55,   33,   55,   55,   33,
+       55,   76,   65,   31,  123,  106,   33,   30,   33,   31,
+       31,   30,   34,   74,   31,   32,   65,   33,   75,   37,
+      106,   35,   34,   33,   37,   34,   33,   36,   89,   36,
+       31,   35,   37,   33,   34,   35,   35,   74,   38,   34,
+       74,   75,   38,   35,   36,   75,   37,  121,   35,   34,
+       89,   37,   34,   36,   36,   89,   36,  119,   35,   78,
+      127,  127,   35,   35,   38,   38,   77,   67,   78,   38,
+       63,   36,   63,   63,   66,   63,   66,   66,   68,   66,
+
+       68,   68,   71,   68,   71,   71,   78,   71,   68,   79,
+       77,   80,   71,   77,   81,   82,   83,   84,   85,   86,
+       82,   90,   79,   81,   92,   85,   80,   63,   86,   88,
+       84,   87,   92,   83,   90,   93,   79,   61,   80,   71,
+       87,   81,   82,   95,   84,   85,   86,   83,   90,   96,
+       91,   92,   88,   91,   97,   93,   88,   93,   87,   94,
+       83,   99,   93,   94,   98,  100,   95,   91,   97,   96,
+       95,   56,  100,  101,   91,   99,   96,   91,  102,  105,
+       91,   97,   93,   94,   98,  103,   94,  102,   99,  103,
+       94,   98,  100,  105,   91,  104,  101,  111,  110,  108,
+
+      101,  107,  104,  113,  559,  102,  105,  103,  108,  107,
+      112,  116,  103,  110,  113,  114,  103,  111,  134,  112,
+      117,  114,  104,  111,  111,  110,  108,  115,  107,  133,
+      113,  115,  118,  116,  559,  117,   51,  112,  116,  139,
+      132,  118,  114,  124,  111,  124,  124,  117,  124,  134,
+      129,  133,  129,  129,  115,  129,  133,  135,  130,  118,
+      130,  130,  136,  130,  132,  137,  138,  132,   50,  135,
+      139,  141,  140,  138,  143,   45,   40,  137,  144,  146,
+       14,  136,  145,  146,  135,  148,  144,  147,   13,  136,
+      154,    0,  137,  138,  140,  130,    0,  154,  149,  140,
+
+      143,  143,  141,  142,  145,  144,  146,  148,  142,  145,
+      147,  149,  148,  142,  147,  150,  153,  154,  151,  142,
+      142,  151,  152,  155,  156,  149,  142,  150,    0,  158,
+      142,  152,  157,  151,  155,  142,  160,    0,  153,  158,
+      142,  161,  150,  153,  162,  151,  142,  142,  151,  152,
+      155,  159,  163,  164,    0,  156,  175,  159,  157,  157,
+      158,  165,  166,  161,  167,  168,  158,  160,  161,  166,
+      168,  169,  170,  164,  171,  162,  179,    0,  159,  177,
+      164,  171,  165,  163,  170,  167,  172,  175,  165,  166,
+      169,  167,  168,  174,  174,  173,  176,  178,  169,  170,
+
+      173,  171,  174,  176,  177,  181,  177,  179,  172,  183,
+      180,  182,  185,  172,  188,    0,  187,  178,  180,  184,
+      174,  174,  173,  176,  178,  189,  186,  185,  194,  190,
+      201,  181,  181,  182,  186,  187,  184,  180,  193,  185,
+      183,  190,  182,  187,  184,  188,  184,  189,  191,  192,
+      205,  195,  189,  186,  194,  194,  190,  191,  196,  197,
+      182,  201,  193,  184,  198,  193,  199,  202,  192,  196,
+      203,  197,  200,  204,  207,  191,  192,  195,  195,    0,
+      202,  205,  213,  207,  210,  196,  197,    0,  198,  199,
+      200,  198,  209,  199,  202,  204,  203,  203,  206,  200,
+
+      204,  207,  208,  211,  214,  206,  209,  212,  208,  215,
+      210,  210,  211,  213,  217,  219,  219,  216,  218,  209,
+      216,  220,  212,    0,  215,  206,  221,  216,  214,  208,
+      211,  214,  217,  218,  212,  225,  215,  224,  226,  223,
+      219,  217,  219,  219,  216,  218,  222,  216,  220,  222,
+      221,  223,  227,  221,  229,  224,  222,  228,  230,  233,
+      226,  229,  225,  231,  224,  226,  223,  234,  237,  228,
+      231,  232,    0,  222,  238,  241,  222,    0,  227,  227,
+      234,  229,  236,  233,  228,  239,  233,  326,  241,  230,
+      231,  239,  235,  232,  234,  235,  238,  235,  232,  237,
+
+      236,  238,  241,  235,  240,  242,  245,  243,  246,  236,
+      247,  240,  239,  249,  326,  248,  247,    0,  245,  235,
+      243,  242,  235,  248,  235,  249,  246,  251,    0,  242,
+        0,  240,  242,  245,  243,  246,    0,  247,  250,    0,
+      249,  253,  248,  254,  252,  250,  258,  258,  242,  244,
+      244,  252,    0,  253,  256,  254,  257,  255,  251,  244,
+        0,  244,  244,  244,  255,  250,  244,  256,  253,  260,
+      254,  252,  259,    0,  244,  258,  244,  244,  257,  259,
+      260,  256,  262,  257,  255,  263,  244,  261,  244,  244,
+      244,  265,  264,  244,  266,  262,  260,    0,  261,  259,
+
+      265,  269,  268,  270,  267,  272,  271,  266,    0,  262,
+      263,  273,  263,  274,  261,  264,  276,  267,  265,  264,
+      268,  266,  267,  269,  271,  275,  270,  277,  269,  268,
+      270,  267,  273,  271,  280,  274,  272,  279,  273,  275,
+      274,  278,  276,  276,  267,  281,  287,  278,  282,  280,
+      283,  286,  275,  285,  282,  279,  288,  286,  277,    0,
+      294,  280,  291,  292,  279,  287,  289,  281,  278,  291,
+      292,  293,  281,  287,  288,  282,  283,  283,  286,  285,
+      285,  289,  294,  288,  293,  295,  296,  294,  297,  291,
+      292,  298,  301,  289,  300,    0,  298,  297,  293,  299,
+
+      302,  303,  306,  305,  295,    0,  300,    0,  309,  303,
+      305,  302,  295,  306,  301,  297,  304,  296,  298,  301,
+      311,  300,  299,  304,  307,  308,  299,  302,  303,  306,
+      305,  304,  308,  307,  309,  309,  310,  312,  310,  313,
+      315,  304,  304,  304,  314,  319,  311,  311,  315,  312,
+      304,  317,  308,  319,  313,  307,  314,  316,  304,  318,
+      307,    0,  316,  310,  312,  321,  313,  315,  304,  320,
+      322,  314,  319,  317,    0,  323,  324,  318,  317,  325,
+      327,  320,  328,  332,  316,  324,  318,  329,  321,  330,
+      323,  331,  321,  325,  333,  327,  320,  328,  334,  329,
+
+      335,  322,  323,  324,  336,  331,  325,  327,  337,  328,
+      332,  338,    0,  330,  329,  339,  330,  338,  331,  341,
+      340,  333,  342,  345,  343,  344,  346,  335,  347,  334,
+      349,  336,  344,  350,    0,  348,  352,  339,  338,  337,
+      348,  347,  339,  340,  348,  342,  343,  340,  346,  342,
+      341,  343,  344,  346,  345,  347,  349,  349,  351,  348,
+      352,  353,  348,  352,  350,  354,  355,  348,  356,  357,
+      358,  348,  359,    0,  360,    0,  351,  362,    0,    0,
+      355,  361,  365,  353,  353,  351,  362,  354,  353,  363,
+      356,  358,  354,  355,  368,  356,  359,  358,  360,  359,
+
+      357,  360,  363,  361,  362,  364,  365,  366,  361,  365,
+      353,  367,  369,  370,  371,  367,  363,    0,  371,  372,
+        0,  377,  373,  376,  374,  368,  375,  364,  377,  366,
+      369,  376,  364,  374,  366,  375,  378,  370,  382,  369,
+      370,  384,  367,  379,  372,  371,  372,  373,  377,  373,
+      376,  374,  380,  375,  381,  379,  383,  385,    0,  378,
+      386,  380,  387,  378,  383,    0,  381,  384,  384,  382,
+      379,  390,  389,  387,  391,  392,    0,  388,  393,  380,
+      385,  381,  386,  383,  385,  388,  389,  386,  394,  387,
+      397,  390,  396,  398,  395,  392,  391,  405,  390,  389,
+
+      393,  391,  392,  395,  388,  393,  398,  399,  397,  400,
+      394,    0,  396,  408,  401,  394,  402,  397,  403,  396,
+      398,  395,  402,  406,  415,  418,  403,  424,  405,  399,
+      407,  416,  408,  400,  399,  409,  400,  401,  412,  407,
+      408,  401,  415,  402,  403,  403,  406,  409,  412,  417,
+      406,  415,  416,  403,  424,    0,  418,  407,  416,  420,
+        0,  421,  409,  425,  422,  412,  422,    0,  423,    0,
+      421,  426,  417,  427,    0,    0,  417,  419,    0,  419,
+        0,  420,  419,  425,  428,  427,  420,  419,  421,  429,
+      425,  422,  423,  419,  419,  423,  430,  426,  426,  431,
+
+      427,  433,  419,  430,  419,  432,  419,  431,  436,  419,
+      428,  428,  437,  429,  419,    0,  429,  434,  438,  439,
+      419,  419,  435,  430,  432,  447,  431,  433,  433,  434,
+      436,  435,  432,  440,  441,  436,  443,  444,  442,  437,
+      439,  445,  438,  446,  434,  438,  439,  442,  450,  435,
+      452,  448,  440,  441,  451,    0,  447,  444,  443,  448,
+      440,  441,  446,  443,  444,  442,  453,  451,  445,  460,
+      446,  461,  450,  455,  453,  450,  452,  452,  448,  455,
+      456,  451,  457,  458,  459,  465,  456,  460,  458,  457,
+      453,  459,  464,  453,  461,  462,  460,  463,  461,  468,
+
+      455,  453,  469,  462,  467,  464,  465,  456,  470,  457,
+      458,  459,  465,  466,  472,  463,  470,  467,  471,  464,
+      466,  473,  462,  474,  463,  469,  475,  473,  476,  469,
+      468,  467,  478,  471,  477,  470,  472,  479,  481,  482,
+      466,  472,  483,  477,  480,  471,  478,  484,  473,  474,
+      474,  480,  476,  489,  483,  476,  485,  475,    0,  478,
+      486,  477,  487,  486,  485,  481,  482,    0,  479,  483,
+      484,  480,  491,  492,  484,  487,  489,  490,  494,  490,
+      489,  495,  493,  485,  491,  496,  492,  486,  493,  487,
+      488,  494,  488,  498,  499,  500,  488,    0,  488,  491,
+
+      492,  501,  495,  488,  490,  494,  497,  499,  495,  493,
+      500,  504,  502,  497,  488,  498,  496,  488,  503,  488,
+      498,  499,  500,  488,  504,  488,  506,  501,  501,  507,
+      488,  497,  497,  497,  502,  505,  509,  503,  504,  502,
+      497,  510,  511,  505,  513,  503,  512,  515,  516,  520,
+      511,    0,  507,  506,  514,  515,  507,  517,  497,  519,
+      512,  523,  505,  518,  517,  510,  513,  509,  510,  511,
+      516,  513,  514,  512,  515,  516,  518,  519,  524,  522,
+      520,  514,  525,  523,  517,  526,  519,  527,  523,  528,
+      518,  522,  530,    0,    0,  531,  530,  538,  531,  552,
+
+      524,  534,  532,  526,  525,  524,  522,  533,  534,  525,
+      536,  535,  526,  527,  527,  539,  540,  537,  538,  533,
+      528,  531,  531,  530,  538,  531,  532,  541,  534,  532,
+      552,  542,  536,  543,  533,  535,  539,  536,  535,  537,
+      542,  544,  539,  540,  537,  541,  545,  546,  547,  548,
+      544,  551,  555,  545,  541,  549,  550,  543,  542,  553,
+      543,  546,    0,    0,  549,  554,  569,  551,  544,  558,
+      547,  548,  557,  545,  546,  547,  548,  554,  551,  555,
+      550,  556,  549,  550,  558,  557,  560,  561,  570,  556,
+      553,  566,  554,  562,  567,  560,  558,  569,  563,  557,
+
+      563,  562,  568,  572,  566,  561,  567,    0,  556,  568,
+      571,  573,  574,  560,  561,  575,  572,  577,  566,  570,
+      562,  567,  578,  576,  573,  563,  580,  575,  579,  568,
+      572,  578,  571,  581,  574,  576,  582,  571,  573,  574,
+      585,  581,  575,  583,  577,  584,  579,  586,  580,  578,
+      576,  583,  585,  580,  587,  579,  588,  590,  582,  586,
+      581,  591,  594,  582,  588,  589,  590,  585,  584,  587,
+      583,  589,  584,  592,  586,  598,  596,  597,    0,  593,
+      594,  587,  591,  588,  590,  592,  593,  600,  591,  594,
+      597,  599,  589,  601,  598,  603,  602,  601,  599,  604,
+
+      592,  605,  598,    0,  597,  608,  593,  596,  609,  600,
+      602,  606,  607,  603,  600,  611,  610,  612,  599,    0,
+      613,  609,  603,  602,  601,  610,  614,  605,  605,    0,
+      604,  608,  608,  606,  607,  609,    0,  618,  606,  607,
+      616,    0,  613,  610,    0,  615,  611,  613,  612,  614,
+      615,    0,  615,  614,  615,  623,  616,  619,  621,  620,
+      618,  626,  622,  615,  618,  627,  624,  616,  620,  619,
+      622,  621,  615,  629,  623,  624,  625,  615,  625,  615,
+      630,  615,  623,  631,  619,  621,  620,  632,  626,  622,
+      633,  631,  627,  624,  634,  635,  637,  629,  636,  644,
+
+      629,  638,  630,  625,  639,  636,  640,  630,  641,  645,
+      631,  647,  642,    0,  632,  640,  634,  633,  637,  646,
+      638,  634,  635,  637,  651,  636,  639,  648,  638,  641,
+      644,  639,  642,  640,  650,  641,  649,  654,  658,  642,
+      645,  646,  647,  653,  649,  648,  646,  653,  652,  655,
+      650,  651,  656,  657,  648,  652,  655,  661,    0,  656,
+      659,  650,  657,  649,  658,  658,  661,  659,  654,  660,
+      653,  660,  667,  662,  664,  652,  655,  662,  664,  656,
+      657,  665,  665,  666,  661,  671,  668,  659,  670,  672,
+      669,  673,  666,  671,  667,  668,  660,  669,  674,  667,
+
+      662,  675,  672,  673,  676,  664,  674,  677,  665,  679,
+      666,  670,  671,  668,  680,  670,  672,  669,  673,  681,
+      683,  678,  684,  675,  676,  674,  680,  679,  675,  677,
+      678,  676,  682,  685,  677,  686,  679,    0,  682,  689,
+      681,  680,  683,  688,  692,    0,  681,  683,  678,  690,
+      691,  695,  689,  684,  691,  693,    0,  690,  696,  682,
+      694,  686,  686,  693,  685,  688,  689,  697,  694,  698,
+      688,  692,  695,  701,  706,  697,  690,  698,  695,  702,
+      699,  691,  693,  700,  707,  702,  701,  694,  699,  696,
+      703,  700,  704,  705,  697,  708,  698,  706,  707,  710,
+
+      701,  706,  703,  711,  713,  709,  702,  699,  718,  712,
+      700,  707,  709,  708,  717,  705,  712,  703,  704,  704,
+      705,  716,  708,  721,    0,  711,  710,  723,    0,  724,
+      711,  727,  709,  725,  728,  713,  712,  729,    0,  718,
+      717,  717,  726,  716,  726,  725,  728,  731,  716,  733,
+      721,  723,  730,  727,  723,  724,  724,  732,  727,  730,
+      725,  728,  732,  734,  729,  733,  736,  735,  738,  726,
+      739,  746,  740,  731,  731,  735,  733,  738,  741,  730,
+      742,  744,  743,  739,  732,  741,  734,  742,  736,  743,
+      734,  745,  753,  736,  735,  738,  747,  739,  740,  740,
+
+      749,  748,  746,  747,  751,  741,  744,  742,  744,  743,
+      752,  754,  752,  745,  748,    0,  753,  755,  745,  753,
+      756,  749,  761,  747,  758,  757,  751,  749,  748,  756,
+      759,  751,  757,  760,  759,  767,  762,  752,  764,  763,
+      766,  769,  754,  755,  755,  764,  758,  756,  763,  761,
+      767,  758,  757,  766,  765,  760,  768,  759,  762,  785,
+      760,  765,  767,  762,  770,  764,  763,  766,  771,  774,
+      768,  770,  769,  772,  773,  771,  779,  776,    0,  772,
+      781,  765,  780,  768,  775,  782,  773,  775,  776,  774,
+      785,  770,  784,  782,  786,  771,  774,  791,  781,  791,
+
+      772,  773,  775,  779,  776,  783,  780,  781,  783,  780,
+      784,  775,  782,  787,  775,  788,  789,  790,  792,  784,
+      787,  786,  793,  783,  791,  789,  794,  792,  797,  788,
+      790,  798,  783,  799,    0,  783,  800,  794,  795,  804,
+      787,  801,  788,  789,  790,  792,  802,  795,  793,  793,
+      805,    0,  802,  794,  799,  801,    0,  812,  804,  797,
+      799,  807,  798,  806,  805,  795,  804,  800,  801,  807,
+      802,  808,  806,  802,  809,  813,    0,  805,  811,  802,
+      812,  814,  808,  809,  812,  811,  816,  815,  807,  817,
+      806,  819,  821,  817,  818,  822,  823,  813,  808,  820,
+
+      831,  809,  813,  814,  816,  811,  818,  821,  814,  815,
+      822,  825,  820,  816,  815,  824,  817,  838,  819,  821,
+      825,  818,  822,  826,  827,  828,  820,  823,  829,  830,
+      824,  831,  833,  832,  834,  835,  836,  837,  825,  832,
+      833,  834,  824,  839,  826,  841,  829,  843,  838,    0,
+      826,  827,  828,  845,  847,  829,  830,  842,    0,  833,
+      832,  834,  835,  836,  837,  846,  842,  839,  849,  844,
+      839,  844,  848,  853,  848,  849,  841,  852,  843,  848,
+      845,  850,  851,  852,  842,  847,  855,  848,  850,  851,
+      856,  846,  846,  854,  854,  849,  844,  856,  858,  848,
+
+      853,  848,  859,  860,  852,  862,  848,  864,  850,  851,
+      861,  861,  855,  855,  863,  863,  858,  856,  864,  868,
+      854,  860,  859,  866,  867,  858,  868,  869,    0,  859,
+      860,  862,  862,  870,  864,  871,  873,  861,  872,  874,
+      875,  863,  879,  873,    0,  866,  868,  869,  875,  876,
+      866,  874,  880,  877,  869,  867,  872,  871,  880,  878,
+      870,  881,  871,  873,  878,  872,  874,  875,  882,  879,
+      876,  877,  885,  883,  881,  878,  876,  884,  886,  880,
+      877,  884,  887,  888,  889,  892,  878,  890,  881,  894,
+      891,  878,  891,  886,  895,  897,  893,  898,  888,  882,
+
+      883,  893,  890,  885,  899,  886,  900,  889,  884,  895,
+      888,  889,  901,  887,  890,  902,  892,  891,  908,  897,
+      894,  895,  897,  893,  898,  903,  906,  900,  909,  908,
+      910,  899,  911,  900,  901,  916,  902,  909,  915,  901,
+      920,  906,  902,  903,  908,  908,  918,    0,  910,  919,
+      911,  915,  903,  906,  917,  909,  908,  910,  922,  911,
+      924,  923,  926,  925,  917,  915,  916,  920,  923,  925,
+      929,  932,  918,  918,  919,  933,  919,  926,  922,  927,
+      928,  917,  929,  934,  930,  922,  924,  924,  923,  926,
+      925,  930,  934,  932,  927,  928,  935,  929,  932,  936,
+
+      933,    0,  933,  937,  937,  938,  927,  928,  939,  940,
+      934,  930,  941,  942,  935,  944,  945,  943,  949,    0,
+      947,    0,  942,  935,  943,    0,  936,  938,  947,  948,
+      937,  940,  938,  939,  941,  939,  940,  950,  945,  941,
+      942,  952,  948,  945,  943,  949,  944,  947,  951,  954,
+      953,  951,  950,  957,  958,  965,  948,  955,  959,  952,
+      966,  958,  961,  968,  950,  953,  951,  957,  952,  959,
+      963,  954,  964,  965,  964,  951,  954,  953,  951,  955,
+      957,  958,  965,  961,  955,  959,  967,  966,  969,  961,
+      968,  963,  971,  970,  972,  973,  969,  963,  974,  964,
+
+      977,  978,    0,  979,  967,    0,  985,  985,  972,  980,
+      981,  982,    0,  967,  971,  969,  970,  974,  977,  971,
+      970,  972,  973,  983,  980,  974,  984,  977,  978,  979,
+      979,  986,  981,  985,  982,  983,  980,  981,  982,  986,
+      987,  988,  990,  987,  993,  989,  991,  995,  984,  988,
+      983,  989,  991,  984,  994,  998,  996,  990,  986,  997,
+      999,  995, 1000, 1001, 1002, 1004, 1005,  987,  988,  990,
+      996,  993,  989,  991,  995, 1007, 1000, 1008,  994, 1010,
+     1003,  994,  998,  996, 1013,  997,  997,  999, 1003, 1000,
+     1001, 1002, 1004, 1005, 1012, 1009, 1011, 1007, 1014, 1015,
+
+     1017, 1016, 1007, 1009, 1008, 1011, 1010, 1003, 1013, 1016,
+     1019, 1013, 1021, 1014, 1022, 1012, 1020, 1020,    0, 1025,
+     1023, 1012, 1009, 1011, 1014, 1014, 1015, 1017, 1016, 1022,
+     1024, 1027, 1024, 1025, 1029, 1031, 1028, 1019, 1021, 1021,
+     1014, 1022, 1023, 1020, 1026, 1032, 1025, 1023, 1028, 1030,
+     1026, 1036, 1029, 1027, 1037, 1034, 1038, 1024, 1027,    0,
+        0, 1029, 1034, 1028,    0, 1039, 1031, 1030, 1032, 1036,
+     1042, 1026, 1032, 1039, 1041, 1044, 1030, 1046, 1036, 1048,
+     1037, 1037, 1034, 1045, 1047, 1042, 1041, 1038, 1049, 1051,
+     1050, 1039, 1039, 1055,    0, 1052, 1051, 1042, 1056, 1044,
+
+     1039, 1041, 1044, 1048, 1046, 1045, 1048, 1047, 1054, 1059,
+     1045, 1047, 1050, 1053, 1060, 1049, 1051, 1050, 1052, 1056,
+     1053, 1058, 1052, 1064, 1055, 1056, 1061, 1067, 1070, 1072,
+     1054, 1059, 1058, 1060, 1074, 1054, 1059, 1075,    0, 1077,
+     1053, 1060, 1080, 1084, 1077, 1078, 1076,    0, 1058, 1064,
+     1064, 1061, 1083, 1061, 1067, 1070, 1072, 1076, 1085, 1086,
+     1087, 1088, 1089, 1075, 1075, 1074, 1077, 1078, 1084, 1080,
+     1084, 1095, 1078, 1076, 1083, 1090, 1091, 1089, 1092, 1083,
+     1085, 1093, 1096, 1094, 1090, 1085, 1086, 1087, 1088, 1089,
+     1094, 1091, 1098, 1092, 1097, 1097, 1093, 1099, 1095, 1098,
+
+     1096, 1100, 1090, 1091, 1100, 1092, 1102, 1101, 1093, 1096,
+     1094, 1099, 1105, 1103, 1104, 1102, 1107, 1118, 1108, 1098,
+     1101, 1097, 1110, 1113, 1099, 1107, 1112, 1117, 1100, 1111,
+        0, 1115, 1113, 1102, 1101, 1103, 1104, 1114, 1112, 1105,
+     1103, 1104, 1115, 1107, 1108, 1108, 1114, 1111, 1118, 1110,
+     1113, 1116, 1120, 1112, 1117, 1119, 1111, 1126, 1115, 1116,
+     1120, 1121, 1125, 1127, 1114, 1128, 1129, 1119, 1121, 1125,
+     1131, 1133, 1132,    0, 1137, 1129, 1139, 1128, 1116, 1120,
+     1132, 1126, 1119, 1140, 1126, 1141, 1138, 1142, 1121, 1125,
+     1127, 1138, 1128, 1129, 1139, 1145, 1144, 1131, 1133, 1132,
+
+     1137, 1137, 1146, 1139, 1147, 1140, 1142, 1141, 1144, 1148,
+     1140, 1149, 1141, 1138, 1142, 1150, 1147, 1145, 1151,    0,
+     1152, 1154, 1145, 1144, 1146, 1155, 1157, 1158,    0, 1146,
+     1161, 1147, 1166, 1159, 1164,    0, 1148, 1160, 1149,    0,
+     1155, 1158, 1164, 1154, 1162, 1151, 1150, 1152, 1154, 1167,
+     1157, 1159, 1155, 1157, 1158, 1160, 1161, 1161, 1165, 1166,
+     1159, 1164, 1162, 1168, 1160, 1169, 1165, 1167, 1171, 1172,
+     1174, 1162, 1176,    0, 1177, 1183, 1167, 1179, 1178, 1176,
+     1180, 1182, 1168, 1172, 1181, 1165, 1171, 1169, 1184, 1185,
+     1168, 1192, 1169, 1181, 1182, 1171, 1172, 1174, 1188, 1176,
+
+     1177, 1177, 1178, 1194, 1179, 1178, 1183, 1180, 1182, 1191,
+     1192, 1181, 1185, 1193, 1199, 1184, 1185, 1191, 1192, 1188,
+     1195, 1193, 1196, 1198, 1198, 1188, 1197, 1200, 1202, 1194,
+     1194, 1195, 1199, 1197, 1200, 1204, 1191, 1203, 1205, 1206,
+     1193, 1199, 1208, 1209, 1196, 1213, 1203, 1195, 1214, 1196,
+     1198,    0, 1202, 1197, 1200, 1202, 1217, 1206, 1209, 1215,
+     1208, 1205, 1204, 1218, 1203, 1205, 1206, 1213, 1219, 1208,
+     1209, 1210, 1213, 1210, 1214, 1214, 1219, 1215, 1217, 1220,
+     1221, 1218, 1222, 1217, 1224, 1223, 1215, 1229, 1226, 1225,
+     1218, 1220, 1224, 1227, 1227, 1219, 1223, 1228, 1210, 1230,
+
+     1222, 1232, 1235, 1236, 1240, 1228, 1220, 1221, 1225, 1222,
+     1226, 1224, 1223, 1242, 1229, 1226, 1225, 1234, 1233, 1237,
+     1227, 1230, 1234, 1238, 1228, 1233, 1230, 1237, 1232, 1235,
+     1236, 1240, 1239, 1241, 1243, 1246, 1245, 1247, 1238, 1239,
+     1242, 1248, 1243, 1249, 1234, 1233, 1237, 1250, 1241, 1245,
+     1238, 1251,    0, 1253, 1255, 1256, 1259, 1246, 1260, 1239,
+     1241, 1243, 1246, 1245, 1247, 1261, 1263, 1266, 1248, 1250,
+     1249, 1271, 1267, 1273, 1250, 1259, 1275, 1256, 1251, 1253,
+     1253, 1255, 1256, 1259, 1265, 1260, 1279, 1261, 1263, 1276,
+     1280, 1265, 1261, 1263, 1266, 1267, 1273, 1278, 1271, 1267,
+
+     1273, 1276, 1282, 1275, 1284, 1286, 1278, 1287,    0, 1294,
+     1295, 1265, 1292, 1279, 1293, 1297, 1276, 1280, 1299, 1300,
+     1286, 1293,    0,    0, 1278, 1298, 1299, 1297, 1295, 1282,
+        0, 1284, 1286, 1287, 1287, 1292, 1294, 1295, 1298, 1292,
+        0, 1293, 1297,    0,    0, 1299, 1300,    0,    0,    0,
+        0,    0, 1298, 1303, 1303, 1303, 1303, 1303, 1303, 1303,
+     1304, 1304, 1304, 1304, 1304, 1304, 1304, 1305, 1305, 1305,
+     1305, 1305, 1305, 1305, 1306, 1306, 1306, 1306, 1306, 1306,
+     1306, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1309, 1309,
+        0, 1309, 1309, 1309, 1309, 1310, 1310,    0,    0,    0,
+
+     1310, 1310, 1311, 1311,    0,    0, 1311,    0, 1311, 1312,
+        0,    0,    0,    0,    0, 1312, 1313, 1313,    0,    0,
+        0, 1313, 1313, 1314,    0,    0,    0,    0,    0, 1314,
+     1315, 1315,    0, 1315, 1315, 1315, 1315, 1316, 1316,    0,
+     1316, 1316, 1316, 1316, 1302, 1302, 1302, 1302, 1302, 1302,
+     1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302,
+     1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302,
+     1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302,
+     1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302,
+     1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302,
+
+     1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302
+    } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+extern int yy_flex_debug;
+int yy_flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+static int yy_more_flag = 0;
+static int yy_more_len = 0;
+#define yymore() ((yy_more_flag) = 1)
+#define YY_MORE_ADJ (yy_more_len)
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+#line 1 "util/configlexer.lex"
+#line 2 "util/configlexer.lex"
+/*
+ * configlexer.lex - lexical analyzer for unbound config file
+ *
+ * Copyright (c) 2001-2006, NLnet Labs. All rights reserved
+ *
+ * See LICENSE for the license.
+ *
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+
+#include "util/config_file.h"
+#include "util/configparser.h"
+void ub_c_error(const char *message);
+
+#if 0
+#define LEXOUT(s)  printf s /* used ONLY when debugging */
+#else
+#define LEXOUT(s)
+#endif
+
+/** avoid warning in about fwrite return value */
+#define ECHO ub_c_error_msg("syntax error at text: %s", yytext)
+
+/** A parser variable, this is a statement in the config file which is
+ * of the form variable: value1 value2 ...  nargs is the number of values. */
+#define YDVAR(nargs, var) \
+	num_args=(nargs); \
+	LEXOUT(("v(%s%d) ", yytext, num_args)); \
+	if(num_args > 0) { BEGIN(val); } \
+	return (var);
+
+struct inc_state {
+	char* filename;
+	int line;
+};
+static struct inc_state parse_stack[MAXINCLUDES];
+static YY_BUFFER_STATE include_stack[MAXINCLUDES];
+static int config_include_stack_ptr = 0;
+static int inc_prev = 0;
+static int num_args = 0;
+
+static void config_start_include(const char* filename)
+{
+	FILE *input;
+	if(strlen(filename) == 0) {
+		ub_c_error_msg("empty include file name");
+		return;
+	}
+	if(config_include_stack_ptr >= MAXINCLUDES) {
+		ub_c_error_msg("includes nested too deeply, skipped (>%d)", MAXINCLUDES);
+		return;
+	}
+	if(cfg_parser->chroot && strncmp(filename, cfg_parser->chroot,
+		strlen(cfg_parser->chroot)) == 0) {
+		filename += strlen(cfg_parser->chroot);
+	}
+	input = fopen(filename, "r");
+	if(!input) {
+		ub_c_error_msg("cannot open include file '%s': %s",
+			filename, strerror(errno));
+		return;
+	}
+	LEXOUT(("switch_to_include_file(%s) ", filename));
+	parse_stack[config_include_stack_ptr].filename = cfg_parser->filename;
+	parse_stack[config_include_stack_ptr].line = cfg_parser->line;
+	include_stack[config_include_stack_ptr] = YY_CURRENT_BUFFER;
+	cfg_parser->filename = strdup(filename);
+	cfg_parser->line = 1;
+	yy_switch_to_buffer(yy_create_buffer(input,YY_BUF_SIZE));
+	++config_include_stack_ptr;
+}
+
+static void config_end_include(void)
+{
+	--config_include_stack_ptr;
+	free(cfg_parser->filename);
+	cfg_parser->filename = parse_stack[config_include_stack_ptr].filename;
+	cfg_parser->line = parse_stack[config_include_stack_ptr].line;
+	yy_delete_buffer(YY_CURRENT_BUFFER);
+	yy_switch_to_buffer(include_stack[config_include_stack_ptr]);
+}
+
+#ifndef yy_set_bol /* compat definition, for flex 2.4.6 */
+#define yy_set_bol(at_bol) \
+        { \
+	        if ( ! yy_current_buffer ) \
+	                yy_current_buffer = yy_create_buffer(yyin,YY_BUF_SIZE ); \
+	        yy_current_buffer->yy_ch_buf[0] = ((at_bol)?'\n':' '); \
+        }
+#endif
+
+#define YY_NO_INPUT 1
+#line 100 "util/configlexer.lex"
+#ifndef YY_NO_UNPUT
+#define YY_NO_UNPUT 1
+#endif
+#ifndef YY_NO_INPUT
+#define YY_NO_INPUT 1
+#endif
+
+#line 1829 "<stdout>"
+
+#define INITIAL 0
+#define quotedstring 1
+#define singlequotedstr 2
+#define include 3
+#define include_quoted 4
+#define val 5
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+static int yy_init_globals (void );
+
+/* Accessor methods to globals.
+   These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy (void );
+
+int yyget_debug (void );
+
+void yyset_debug (int debug_flag  );
+
+YY_EXTRA_TYPE yyget_extra (void );
+
+void yyset_extra (YY_EXTRA_TYPE user_defined  );
+
+FILE *yyget_in (void );
+
+void yyset_in  (FILE * in_str  );
+
+FILE *yyget_out (void );
+
+void yyset_out  (FILE * out_str  );
+
+int yyget_leng (void );
+
+char *yyget_text (void );
+
+int yyget_lineno (void );
+
+void yyset_lineno (int line_number  );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap (void );
+#else
+extern int yywrap (void );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0)
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+		{ \
+		int c = '*'; \
+		unsigned n; \
+		for ( n = 0; n < max_size && \
+			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+			buf[n] = (char) c; \
+		if ( c == '\n' ) \
+			buf[n++] = (char) c; \
+		if ( c == EOF && ferror( yyin ) ) \
+			YY_FATAL_ERROR( "input in flex scanner failed" ); \
+		result = n; \
+		} \
+	else \
+		{ \
+		errno=0; \
+		while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+			{ \
+			if( errno != EINTR) \
+				{ \
+				YY_FATAL_ERROR( "input in flex scanner failed" ); \
+				break; \
+				} \
+			errno=0; \
+			clearerr(yyin); \
+			} \
+		}\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex (void);
+
+#define YY_DECL int yylex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+	YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+	register yy_state_type yy_current_state;
+	register char *yy_cp, *yy_bp;
+	register int yy_act;
+    
+#line 120 "util/configlexer.lex"
+
+#line 2016 "<stdout>"
+
+	if ( !(yy_init) )
+		{
+		(yy_init) = 1;
+
+#ifdef YY_USER_INIT
+		YY_USER_INIT;
+#endif
+
+		if ( ! (yy_start) )
+			(yy_start) = 1;	/* first start state */
+
+		if ( ! yyin )
+			yyin = stdin;
+
+		if ( ! yyout )
+			yyout = stdout;
+
+		if ( ! YY_CURRENT_BUFFER ) {
+			yyensure_buffer_stack ();
+			YY_CURRENT_BUFFER_LVALUE =
+				yy_create_buffer(yyin,YY_BUF_SIZE );
+		}
+
+		yy_load_buffer_state( );
+		}
+
+	while ( 1 )		/* loops until end-of-file is reached */
+		{
+		(yy_more_len) = 0;
+		if ( (yy_more_flag) )
+			{
+			(yy_more_len) = (yy_c_buf_p) - (yytext_ptr);
+			(yy_more_flag) = 0;
+			}
+		yy_cp = (yy_c_buf_p);
+
+		/* Support of yytext. */
+		*yy_cp = (yy_hold_char);
+
+		/* yy_bp points to the position in yy_ch_buf of the start of
+		 * the current run.
+		 */
+		yy_bp = yy_cp;
+
+		yy_current_state = (yy_start);
+yy_match:
+		do
+			{
+			register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+			if ( yy_accept[yy_current_state] )
+				{
+				(yy_last_accepting_state) = yy_current_state;
+				(yy_last_accepting_cpos) = yy_cp;
+				}
+			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+				{
+				yy_current_state = (int) yy_def[yy_current_state];
+				if ( yy_current_state >= 1303 )
+					yy_c = yy_meta[(unsigned int) yy_c];
+				}
+			yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+			++yy_cp;
+			}
+		while ( yy_base[yy_current_state] != 3745 );
+
+yy_find_action:
+		yy_act = yy_accept[yy_current_state];
+		if ( yy_act == 0 )
+			{ /* have to back up */
+			yy_cp = (yy_last_accepting_cpos);
+			yy_current_state = (yy_last_accepting_state);
+			yy_act = yy_accept[yy_current_state];
+			}
+
+		YY_DO_BEFORE_ACTION;
+
+do_action:	/* This label is used only to access EOF actions. */
+
+		switch ( yy_act )
+	{ /* beginning of action switch */
+			case 0: /* must back up */
+			/* undo the effects of YY_DO_BEFORE_ACTION */
+			*yy_cp = (yy_hold_char);
+			yy_cp = (yy_last_accepting_cpos);
+			yy_current_state = (yy_last_accepting_state);
+			goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 121 "util/configlexer.lex"
+{ 
+	LEXOUT(("SP ")); /* ignore */ }
+	YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 123 "util/configlexer.lex"
+{ 
+	/* note that flex makes the longest match and '.' is any but not nl */
+	LEXOUT(("comment(%s) ", yytext)); /* ignore */ }
+	YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 126 "util/configlexer.lex"
+{ YDVAR(0, VAR_SERVER) }
+	YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 127 "util/configlexer.lex"
+{ YDVAR(1, VAR_NUM_THREADS) }
+	YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 128 "util/configlexer.lex"
+{ YDVAR(1, VAR_VERBOSITY) }
+	YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 129 "util/configlexer.lex"
+{ YDVAR(1, VAR_PORT) }
+	YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 130 "util/configlexer.lex"
+{ YDVAR(1, VAR_OUTGOING_RANGE) }
+	YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 131 "util/configlexer.lex"
+{ YDVAR(1, VAR_OUTGOING_PORT_PERMIT) }
+	YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 132 "util/configlexer.lex"
+{ YDVAR(1, VAR_OUTGOING_PORT_AVOID) }
+	YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 133 "util/configlexer.lex"
+{ YDVAR(1, VAR_OUTGOING_NUM_TCP) }
+	YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 134 "util/configlexer.lex"
+{ YDVAR(1, VAR_INCOMING_NUM_TCP) }
+	YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 135 "util/configlexer.lex"
+{ YDVAR(1, VAR_DO_IP4) }
+	YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 136 "util/configlexer.lex"
+{ YDVAR(1, VAR_DO_IP6) }
+	YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 137 "util/configlexer.lex"
+{ YDVAR(1, VAR_DO_UDP) }
+	YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 138 "util/configlexer.lex"
+{ YDVAR(1, VAR_DO_TCP) }
+	YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 139 "util/configlexer.lex"
+{ YDVAR(1, VAR_TCP_UPSTREAM) }
+	YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 140 "util/configlexer.lex"
+{ YDVAR(1, VAR_SSL_UPSTREAM) }
+	YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 141 "util/configlexer.lex"
+{ YDVAR(1, VAR_SSL_SERVICE_KEY) }
+	YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 142 "util/configlexer.lex"
+{ YDVAR(1, VAR_SSL_SERVICE_PEM) }
+	YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 143 "util/configlexer.lex"
+{ YDVAR(1, VAR_SSL_PORT) }
+	YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 144 "util/configlexer.lex"
+{ YDVAR(1, VAR_DO_DAEMONIZE) }
+	YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 145 "util/configlexer.lex"
+{ YDVAR(1, VAR_INTERFACE) }
+	YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 146 "util/configlexer.lex"
+{ YDVAR(1, VAR_OUTGOING_INTERFACE) }
+	YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 147 "util/configlexer.lex"
+{ YDVAR(1, VAR_INTERFACE_AUTOMATIC) }
+	YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 148 "util/configlexer.lex"
+{ YDVAR(1, VAR_SO_RCVBUF) }
+	YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 149 "util/configlexer.lex"
+{ YDVAR(1, VAR_SO_SNDBUF) }
+	YY_BREAK
+case 27:
+YY_RULE_SETUP
+#line 150 "util/configlexer.lex"
+{ YDVAR(1, VAR_CHROOT) }
+	YY_BREAK
+case 28:
+YY_RULE_SETUP
+#line 151 "util/configlexer.lex"
+{ YDVAR(1, VAR_USERNAME) }
+	YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 152 "util/configlexer.lex"
+{ YDVAR(1, VAR_DIRECTORY) }
+	YY_BREAK
+case 30:
+YY_RULE_SETUP
+#line 153 "util/configlexer.lex"
+{ YDVAR(1, VAR_LOGFILE) }
+	YY_BREAK
+case 31:
+YY_RULE_SETUP
+#line 154 "util/configlexer.lex"
+{ YDVAR(1, VAR_PIDFILE) }
+	YY_BREAK
+case 32:
+YY_RULE_SETUP
+#line 155 "util/configlexer.lex"
+{ YDVAR(1, VAR_ROOT_HINTS) }
+	YY_BREAK
+case 33:
+YY_RULE_SETUP
+#line 156 "util/configlexer.lex"
+{ YDVAR(1, VAR_EDNS_BUFFER_SIZE) }
+	YY_BREAK
+case 34:
+YY_RULE_SETUP
+#line 157 "util/configlexer.lex"
+{ YDVAR(1, VAR_MSG_BUFFER_SIZE) }
+	YY_BREAK
+case 35:
+YY_RULE_SETUP
+#line 158 "util/configlexer.lex"
+{ YDVAR(1, VAR_MSG_CACHE_SIZE) }
+	YY_BREAK
+case 36:
+YY_RULE_SETUP
+#line 159 "util/configlexer.lex"
+{ YDVAR(1, VAR_MSG_CACHE_SLABS) }
+	YY_BREAK
+case 37:
+YY_RULE_SETUP
+#line 160 "util/configlexer.lex"
+{ YDVAR(1, VAR_RRSET_CACHE_SIZE) }
+	YY_BREAK
+case 38:
+YY_RULE_SETUP
+#line 161 "util/configlexer.lex"
+{ YDVAR(1, VAR_RRSET_CACHE_SLABS) }
+	YY_BREAK
+case 39:
+YY_RULE_SETUP
+#line 162 "util/configlexer.lex"
+{ YDVAR(1, VAR_CACHE_MAX_TTL) }
+	YY_BREAK
+case 40:
+YY_RULE_SETUP
+#line 163 "util/configlexer.lex"
+{ YDVAR(1, VAR_CACHE_MIN_TTL) }
+	YY_BREAK
+case 41:
+YY_RULE_SETUP
+#line 164 "util/configlexer.lex"
+{ YDVAR(1, VAR_INFRA_HOST_TTL) }
+	YY_BREAK
+case 42:
+YY_RULE_SETUP
+#line 165 "util/configlexer.lex"
+{ YDVAR(1, VAR_INFRA_LAME_TTL) }
+	YY_BREAK
+case 43:
+YY_RULE_SETUP
+#line 166 "util/configlexer.lex"
+{ YDVAR(1, VAR_INFRA_CACHE_SLABS) }
+	YY_BREAK
+case 44:
+YY_RULE_SETUP
+#line 167 "util/configlexer.lex"
+{ YDVAR(1, VAR_INFRA_CACHE_NUMHOSTS) }
+	YY_BREAK
+case 45:
+YY_RULE_SETUP
+#line 168 "util/configlexer.lex"
+{ YDVAR(1, VAR_INFRA_CACHE_LAME_SIZE) }
+	YY_BREAK
+case 46:
+YY_RULE_SETUP
+#line 169 "util/configlexer.lex"
+{ YDVAR(1, VAR_NUM_QUERIES_PER_THREAD) }
+	YY_BREAK
+case 47:
+YY_RULE_SETUP
+#line 170 "util/configlexer.lex"
+{ YDVAR(1, VAR_JOSTLE_TIMEOUT) }
+	YY_BREAK
+case 48:
+YY_RULE_SETUP
+#line 171 "util/configlexer.lex"
+{ YDVAR(1, VAR_TARGET_FETCH_POLICY) }
+	YY_BREAK
+case 49:
+YY_RULE_SETUP
+#line 172 "util/configlexer.lex"
+{ YDVAR(1, VAR_HARDEN_SHORT_BUFSIZE) }
+	YY_BREAK
+case 50:
+YY_RULE_SETUP
+#line 173 "util/configlexer.lex"
+{ YDVAR(1, VAR_HARDEN_LARGE_QUERIES) }
+	YY_BREAK
+case 51:
+YY_RULE_SETUP
+#line 174 "util/configlexer.lex"
+{ YDVAR(1, VAR_HARDEN_GLUE) }
+	YY_BREAK
+case 52:
+YY_RULE_SETUP
+#line 175 "util/configlexer.lex"
+{ YDVAR(1, VAR_HARDEN_DNSSEC_STRIPPED) }
+	YY_BREAK
+case 53:
+YY_RULE_SETUP
+#line 176 "util/configlexer.lex"
+{ YDVAR(1, VAR_HARDEN_BELOW_NXDOMAIN) }
+	YY_BREAK
+case 54:
+YY_RULE_SETUP
+#line 177 "util/configlexer.lex"
+{ YDVAR(1, VAR_HARDEN_REFERRAL_PATH) }
+	YY_BREAK
+case 55:
+YY_RULE_SETUP
+#line 178 "util/configlexer.lex"
+{ YDVAR(1, VAR_USE_CAPS_FOR_ID) }
+	YY_BREAK
+case 56:
+YY_RULE_SETUP
+#line 179 "util/configlexer.lex"
+{ YDVAR(1, VAR_UNWANTED_REPLY_THRESHOLD) }
+	YY_BREAK
+case 57:
+YY_RULE_SETUP
+#line 180 "util/configlexer.lex"
+{ YDVAR(1, VAR_PRIVATE_ADDRESS) }
+	YY_BREAK
+case 58:
+YY_RULE_SETUP
+#line 181 "util/configlexer.lex"
+{ YDVAR(1, VAR_PRIVATE_DOMAIN) }
+	YY_BREAK
+case 59:
+YY_RULE_SETUP
+#line 182 "util/configlexer.lex"
+{ YDVAR(1, VAR_PREFETCH_KEY) }
+	YY_BREAK
+case 60:
+YY_RULE_SETUP
+#line 183 "util/configlexer.lex"
+{ YDVAR(1, VAR_PREFETCH) }
+	YY_BREAK
+case 61:
+YY_RULE_SETUP
+#line 184 "util/configlexer.lex"
+{ YDVAR(0, VAR_STUB_ZONE) }
+	YY_BREAK
+case 62:
+YY_RULE_SETUP
+#line 185 "util/configlexer.lex"
+{ YDVAR(1, VAR_NAME) }
+	YY_BREAK
+case 63:
+YY_RULE_SETUP
+#line 186 "util/configlexer.lex"
+{ YDVAR(1, VAR_STUB_ADDR) }
+	YY_BREAK
+case 64:
+YY_RULE_SETUP
+#line 187 "util/configlexer.lex"
+{ YDVAR(1, VAR_STUB_HOST) }
+	YY_BREAK
+case 65:
+YY_RULE_SETUP
+#line 188 "util/configlexer.lex"
+{ YDVAR(1, VAR_STUB_PRIME) }
+	YY_BREAK
+case 66:
+YY_RULE_SETUP
+#line 189 "util/configlexer.lex"
+{ YDVAR(0, VAR_FORWARD_ZONE) }
+	YY_BREAK
+case 67:
+YY_RULE_SETUP
+#line 190 "util/configlexer.lex"
+{ YDVAR(1, VAR_FORWARD_ADDR) }
+	YY_BREAK
+case 68:
+YY_RULE_SETUP
+#line 191 "util/configlexer.lex"
+{ YDVAR(1, VAR_FORWARD_HOST) }
+	YY_BREAK
+case 69:
+YY_RULE_SETUP
+#line 192 "util/configlexer.lex"
+{ YDVAR(1, VAR_DO_NOT_QUERY_ADDRESS) }
+	YY_BREAK
+case 70:
+YY_RULE_SETUP
+#line 193 "util/configlexer.lex"
+{ YDVAR(1, VAR_DO_NOT_QUERY_LOCALHOST) }
+	YY_BREAK
+case 71:
+YY_RULE_SETUP
+#line 194 "util/configlexer.lex"
+{ YDVAR(2, VAR_ACCESS_CONTROL) }
+	YY_BREAK
+case 72:
+YY_RULE_SETUP
+#line 195 "util/configlexer.lex"
+{ YDVAR(1, VAR_HIDE_IDENTITY) }
+	YY_BREAK
+case 73:
+YY_RULE_SETUP
+#line 196 "util/configlexer.lex"
+{ YDVAR(1, VAR_HIDE_VERSION) }
+	YY_BREAK
+case 74:
+YY_RULE_SETUP
+#line 197 "util/configlexer.lex"
+{ YDVAR(1, VAR_IDENTITY) }
+	YY_BREAK
+case 75:
+YY_RULE_SETUP
+#line 198 "util/configlexer.lex"
+{ YDVAR(1, VAR_VERSION) }
+	YY_BREAK
+case 76:
+YY_RULE_SETUP
+#line 199 "util/configlexer.lex"
+{ YDVAR(1, VAR_MODULE_CONF) }
+	YY_BREAK
+case 77:
+YY_RULE_SETUP
+#line 200 "util/configlexer.lex"
+{ YDVAR(1, VAR_DLV_ANCHOR) }
+	YY_BREAK
+case 78:
+YY_RULE_SETUP
+#line 201 "util/configlexer.lex"
+{ YDVAR(1, VAR_DLV_ANCHOR_FILE) }
+	YY_BREAK
+case 79:
+YY_RULE_SETUP
+#line 202 "util/configlexer.lex"
+{ YDVAR(1, VAR_TRUST_ANCHOR_FILE) }
+	YY_BREAK
+case 80:
+YY_RULE_SETUP
+#line 203 "util/configlexer.lex"
+{ YDVAR(1, VAR_AUTO_TRUST_ANCHOR_FILE) }
+	YY_BREAK
+case 81:
+YY_RULE_SETUP
+#line 204 "util/configlexer.lex"
+{ YDVAR(1, VAR_TRUSTED_KEYS_FILE) }
+	YY_BREAK
+case 82:
+YY_RULE_SETUP
+#line 205 "util/configlexer.lex"
+{ YDVAR(1, VAR_TRUST_ANCHOR) }
+	YY_BREAK
+case 83:
+YY_RULE_SETUP
+#line 206 "util/configlexer.lex"
+{ YDVAR(1, VAR_VAL_OVERRIDE_DATE) }
+	YY_BREAK
+case 84:
+YY_RULE_SETUP
+#line 207 "util/configlexer.lex"
+{ YDVAR(1, VAR_VAL_SIG_SKEW_MIN) }
+	YY_BREAK
+case 85:
+YY_RULE_SETUP
+#line 208 "util/configlexer.lex"
+{ YDVAR(1, VAR_VAL_SIG_SKEW_MAX) }
+	YY_BREAK
+case 86:
+YY_RULE_SETUP
+#line 209 "util/configlexer.lex"
+{ YDVAR(1, VAR_BOGUS_TTL) }
+	YY_BREAK
+case 87:
+YY_RULE_SETUP
+#line 210 "util/configlexer.lex"
+{ YDVAR(1, VAR_VAL_CLEAN_ADDITIONAL) }
+	YY_BREAK
+case 88:
+YY_RULE_SETUP
+#line 211 "util/configlexer.lex"
+{ YDVAR(1, VAR_VAL_PERMISSIVE_MODE) }
+	YY_BREAK
+case 89:
+YY_RULE_SETUP
+#line 212 "util/configlexer.lex"
+{ YDVAR(1, VAR_IGNORE_CD_FLAG) }
+	YY_BREAK
+case 90:
+YY_RULE_SETUP
+#line 213 "util/configlexer.lex"
+{ YDVAR(1, VAR_VAL_LOG_LEVEL) }
+	YY_BREAK
+case 91:
+YY_RULE_SETUP
+#line 214 "util/configlexer.lex"
+{ YDVAR(1, VAR_KEY_CACHE_SIZE) }
+	YY_BREAK
+case 92:
+YY_RULE_SETUP
+#line 215 "util/configlexer.lex"
+{ YDVAR(1, VAR_KEY_CACHE_SLABS) }
+	YY_BREAK
+case 93:
+YY_RULE_SETUP
+#line 216 "util/configlexer.lex"
+{ YDVAR(1, VAR_NEG_CACHE_SIZE) }
+	YY_BREAK
+case 94:
+YY_RULE_SETUP
+#line 217 "util/configlexer.lex"
+{ 
+				  YDVAR(1, VAR_VAL_NSEC3_KEYSIZE_ITERATIONS) }
+	YY_BREAK
+case 95:
+YY_RULE_SETUP
+#line 219 "util/configlexer.lex"
+{ YDVAR(1, VAR_ADD_HOLDDOWN) }
+	YY_BREAK
+case 96:
+YY_RULE_SETUP
+#line 220 "util/configlexer.lex"
+{ YDVAR(1, VAR_DEL_HOLDDOWN) }
+	YY_BREAK
+case 97:
+YY_RULE_SETUP
+#line 221 "util/configlexer.lex"
+{ YDVAR(1, VAR_KEEP_MISSING) }
+	YY_BREAK
+case 98:
+YY_RULE_SETUP
+#line 222 "util/configlexer.lex"
+{ YDVAR(1, VAR_USE_SYSLOG) }
+	YY_BREAK
+case 99:
+YY_RULE_SETUP
+#line 223 "util/configlexer.lex"
+{ YDVAR(1, VAR_LOG_TIME_ASCII) }
+	YY_BREAK
+case 100:
+YY_RULE_SETUP
+#line 224 "util/configlexer.lex"
+{ YDVAR(1, VAR_LOG_QUERIES) }
+	YY_BREAK
+case 101:
+YY_RULE_SETUP
+#line 225 "util/configlexer.lex"
+{ YDVAR(2, VAR_LOCAL_ZONE) }
+	YY_BREAK
+case 102:
+YY_RULE_SETUP
+#line 226 "util/configlexer.lex"
+{ YDVAR(1, VAR_LOCAL_DATA) }
+	YY_BREAK
+case 103:
+YY_RULE_SETUP
+#line 227 "util/configlexer.lex"
+{ YDVAR(1, VAR_LOCAL_DATA_PTR) }
+	YY_BREAK
+case 104:
+YY_RULE_SETUP
+#line 228 "util/configlexer.lex"
+{ YDVAR(1, VAR_STATISTICS_INTERVAL) }
+	YY_BREAK
+case 105:
+YY_RULE_SETUP
+#line 229 "util/configlexer.lex"
+{ YDVAR(1, VAR_STATISTICS_CUMULATIVE) }
+	YY_BREAK
+case 106:
+YY_RULE_SETUP
+#line 230 "util/configlexer.lex"
+{ YDVAR(1, VAR_EXTENDED_STATISTICS) }
+	YY_BREAK
+case 107:
+YY_RULE_SETUP
+#line 231 "util/configlexer.lex"
+{ YDVAR(0, VAR_REMOTE_CONTROL) }
+	YY_BREAK
+case 108:
+YY_RULE_SETUP
+#line 232 "util/configlexer.lex"
+{ YDVAR(1, VAR_CONTROL_ENABLE) }
+	YY_BREAK
+case 109:
+YY_RULE_SETUP
+#line 233 "util/configlexer.lex"
+{ YDVAR(1, VAR_CONTROL_INTERFACE) }
+	YY_BREAK
+case 110:
+YY_RULE_SETUP
+#line 234 "util/configlexer.lex"
+{ YDVAR(1, VAR_CONTROL_PORT) }
+	YY_BREAK
+case 111:
+YY_RULE_SETUP
+#line 235 "util/configlexer.lex"
+{ YDVAR(1, VAR_SERVER_KEY_FILE) }
+	YY_BREAK
+case 112:
+YY_RULE_SETUP
+#line 236 "util/configlexer.lex"
+{ YDVAR(1, VAR_SERVER_CERT_FILE) }
+	YY_BREAK
+case 113:
+YY_RULE_SETUP
+#line 237 "util/configlexer.lex"
+{ YDVAR(1, VAR_CONTROL_KEY_FILE) }
+	YY_BREAK
+case 114:
+YY_RULE_SETUP
+#line 238 "util/configlexer.lex"
+{ YDVAR(1, VAR_CONTROL_CERT_FILE) }
+	YY_BREAK
+case 115:
+YY_RULE_SETUP
+#line 239 "util/configlexer.lex"
+{ YDVAR(1, VAR_PYTHON_SCRIPT) }
+	YY_BREAK
+case 116:
+YY_RULE_SETUP
+#line 240 "util/configlexer.lex"
+{ YDVAR(0, VAR_PYTHON) }
+	YY_BREAK
+case 117:
+YY_RULE_SETUP
+#line 241 "util/configlexer.lex"
+{ YDVAR(1, VAR_DOMAIN_INSECURE) }
+	YY_BREAK
+case 118:
+/* rule 118 can match eol */
+YY_RULE_SETUP
+#line 242 "util/configlexer.lex"
+{ LEXOUT(("NL\n")); cfg_parser->line++; }
+	YY_BREAK
+/* Quoted strings. Strip leading and ending quotes */
+case 119:
+YY_RULE_SETUP
+#line 245 "util/configlexer.lex"
+{ BEGIN(quotedstring); LEXOUT(("QS ")); }
+	YY_BREAK
+case YY_STATE_EOF(quotedstring):
+#line 246 "util/configlexer.lex"
+{
+        yyerror("EOF inside quoted string");
+	if(--num_args == 0) { BEGIN(INITIAL); }
+	else		    { BEGIN(val); }
+}
+	YY_BREAK
+case 120:
+YY_RULE_SETUP
+#line 251 "util/configlexer.lex"
+{ LEXOUT(("STR(%s) ", yytext)); yymore(); }
+	YY_BREAK
+case 121:
+/* rule 121 can match eol */
+YY_RULE_SETUP
+#line 252 "util/configlexer.lex"
+{ yyerror("newline inside quoted string, no end \""); 
+			  cfg_parser->line++; BEGIN(INITIAL); }
+	YY_BREAK
+case 122:
+YY_RULE_SETUP
+#line 254 "util/configlexer.lex"
+{
+        LEXOUT(("QE "));
+	if(--num_args == 0) { BEGIN(INITIAL); }
+	else		    { BEGIN(val); }
+        yytext[yyleng - 1] = '\0';
+	yylval.str = strdup(yytext);
+	if(!yylval.str)
+		yyerror("out of memory");
+        return STRING_ARG;
+}
+	YY_BREAK
+/* Single Quoted strings. Strip leading and ending quotes */
+case 123:
+YY_RULE_SETUP
+#line 266 "util/configlexer.lex"
+{ BEGIN(singlequotedstr); LEXOUT(("SQS ")); }
+	YY_BREAK
+case YY_STATE_EOF(singlequotedstr):
+#line 267 "util/configlexer.lex"
+{
+        yyerror("EOF inside quoted string");
+	if(--num_args == 0) { BEGIN(INITIAL); }
+	else		    { BEGIN(val); }
+}
+	YY_BREAK
+case 124:
+YY_RULE_SETUP
+#line 272 "util/configlexer.lex"
+{ LEXOUT(("STR(%s) ", yytext)); yymore(); }
+	YY_BREAK
+case 125:
+/* rule 125 can match eol */
+YY_RULE_SETUP
+#line 273 "util/configlexer.lex"
+{ yyerror("newline inside quoted string, no end '"); 
+			     cfg_parser->line++; BEGIN(INITIAL); }
+	YY_BREAK
+case 126:
+YY_RULE_SETUP
+#line 275 "util/configlexer.lex"
+{
+        LEXOUT(("SQE "));
+	if(--num_args == 0) { BEGIN(INITIAL); }
+	else		    { BEGIN(val); }
+        yytext[yyleng - 1] = '\0';
+	yylval.str = strdup(yytext);
+	if(!yylval.str)
+		yyerror("out of memory");
+        return STRING_ARG;
+}
+	YY_BREAK
+/* include: directive */
+case 127:
+YY_RULE_SETUP
+#line 287 "util/configlexer.lex"
+{ 
+	LEXOUT(("v(%s) ", yytext)); inc_prev = YYSTATE; BEGIN(include); }
+	YY_BREAK
+case YY_STATE_EOF(include):
+#line 289 "util/configlexer.lex"
+{
+        yyerror("EOF inside include directive");
+        BEGIN(inc_prev);
+}
+	YY_BREAK
+case 128:
+YY_RULE_SETUP
+#line 293 "util/configlexer.lex"
+{ LEXOUT(("ISP ")); /* ignore */ }
+	YY_BREAK
+case 129:
+/* rule 129 can match eol */
+YY_RULE_SETUP
+#line 294 "util/configlexer.lex"
+{ LEXOUT(("NL\n")); cfg_parser->line++;}
+	YY_BREAK
+case 130:
+YY_RULE_SETUP
+#line 295 "util/configlexer.lex"
+{ LEXOUT(("IQS ")); BEGIN(include_quoted); }
+	YY_BREAK
+case 131:
+YY_RULE_SETUP
+#line 296 "util/configlexer.lex"
+{
+	LEXOUT(("Iunquotedstr(%s) ", yytext));
+	config_start_include(yytext);
+	BEGIN(inc_prev);
+}
+	YY_BREAK
+case YY_STATE_EOF(include_quoted):
+#line 301 "util/configlexer.lex"
+{
+        yyerror("EOF inside quoted string");
+        BEGIN(inc_prev);
+}
+	YY_BREAK
+case 132:
+YY_RULE_SETUP
+#line 305 "util/configlexer.lex"
+{ LEXOUT(("ISTR(%s) ", yytext)); yymore(); }
+	YY_BREAK
+case 133:
+/* rule 133 can match eol */
+YY_RULE_SETUP
+#line 306 "util/configlexer.lex"
+{ yyerror("newline before \" in include name"); 
+				  cfg_parser->line++; BEGIN(inc_prev); }
+	YY_BREAK
+case 134:
+YY_RULE_SETUP
+#line 308 "util/configlexer.lex"
+{
+	LEXOUT(("IQE "));
+	yytext[yyleng - 1] = '\0';
+	config_start_include(yytext);
+	BEGIN(inc_prev);
+}
+	YY_BREAK
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(val):
+#line 314 "util/configlexer.lex"
+{
+	yy_set_bol(1); /* Set beginning of line, so "^" rules match.  */
+	if (config_include_stack_ptr == 0) {
+		yyterminate();
+	} else {
+		fclose(yyin);
+		config_end_include();
+	}
+}
+	YY_BREAK
+case 135:
+YY_RULE_SETUP
+#line 324 "util/configlexer.lex"
+{ LEXOUT(("unquotedstr(%s) ", yytext)); 
+			if(--num_args == 0) { BEGIN(INITIAL); }
+			yylval.str = strdup(yytext); return STRING_ARG; }
+	YY_BREAK
+case 136:
+YY_RULE_SETUP
+#line 328 "util/configlexer.lex"
+{
+	ub_c_error_msg("unknown keyword '%s'", yytext);
+	}
+	YY_BREAK
+case 137:
+YY_RULE_SETUP
+#line 332 "util/configlexer.lex"
+{
+	ub_c_error_msg("stray '%s'", yytext);
+	}
+	YY_BREAK
+case 138:
+YY_RULE_SETUP
+#line 336 "util/configlexer.lex"
+ECHO;
+	YY_BREAK
+#line 2887 "<stdout>"
+
+	case YY_END_OF_BUFFER:
+		{
+		/* Amount of text matched not including the EOB char. */
+		int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+		/* Undo the effects of YY_DO_BEFORE_ACTION. */
+		*yy_cp = (yy_hold_char);
+		YY_RESTORE_YY_MORE_OFFSET
+
+		if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+			{
+			/* We're scanning a new file or input source.  It's
+			 * possible that this happened because the user
+			 * just pointed yyin at a new source and called
+			 * yylex().  If so, then we have to assure
+			 * consistency between YY_CURRENT_BUFFER and our
+			 * globals.  Here is the right place to do so, because
+			 * this is the first action (other than possibly a
+			 * back-up) that will match for the new input source.
+			 */
+			(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+			YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+			}
+
+		/* Note that here we test for yy_c_buf_p "<=" to the position
+		 * of the first EOB in the buffer, since yy_c_buf_p will
+		 * already have been incremented past the NUL character
+		 * (since all states make transitions on EOB to the
+		 * end-of-buffer state).  Contrast this with the test
+		 * in input().
+		 */
+		if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+			{ /* This was really a NUL. */
+			yy_state_type yy_next_state;
+
+			(yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+			yy_current_state = yy_get_previous_state(  );
+
+			/* Okay, we're now positioned to make the NUL
+			 * transition.  We couldn't have
+			 * yy_get_previous_state() go ahead and do it
+			 * for us because it doesn't know how to deal
+			 * with the possibility of jamming (and we don't
+			 * want to build jamming into it because then it
+			 * will run more slowly).
+			 */
+
+			yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+			yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+			if ( yy_next_state )
+				{
+				/* Consume the NUL. */
+				yy_cp = ++(yy_c_buf_p);
+				yy_current_state = yy_next_state;
+				goto yy_match;
+				}
+
+			else
+				{
+				yy_cp = (yy_c_buf_p);
+				goto yy_find_action;
+				}
+			}
+
+		else switch ( yy_get_next_buffer(  ) )
+			{
+			case EOB_ACT_END_OF_FILE:
+				{
+				(yy_did_buffer_switch_on_eof) = 0;
+
+				if ( yywrap( ) )
+					{
+					/* Note: because we've taken care in
+					 * yy_get_next_buffer() to have set up
+					 * yytext, we can now set up
+					 * yy_c_buf_p so that if some total
+					 * hoser (like flex itself) wants to
+					 * call the scanner after we return the
+					 * YY_NULL, it'll still work - another
+					 * YY_NULL will get returned.
+					 */
+					(yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+					yy_act = YY_STATE_EOF(YY_START);
+					goto do_action;
+					}
+
+				else
+					{
+					if ( ! (yy_did_buffer_switch_on_eof) )
+						YY_NEW_FILE;
+					}
+				break;
+				}
+
+			case EOB_ACT_CONTINUE_SCAN:
+				(yy_c_buf_p) =
+					(yytext_ptr) + yy_amount_of_matched_text;
+
+				yy_current_state = yy_get_previous_state(  );
+
+				yy_cp = (yy_c_buf_p);
+				yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+				goto yy_match;
+
+			case EOB_ACT_LAST_MATCH:
+				(yy_c_buf_p) =
+				&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+				yy_current_state = yy_get_previous_state(  );
+
+				yy_cp = (yy_c_buf_p);
+				yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+				goto yy_find_action;
+			}
+		break;
+		}
+
+	default:
+		YY_FATAL_ERROR(
+			"fatal flex scanner internal error--no action found" );
+	} /* end of action switch */
+		} /* end of scanning one token */
+} /* end of yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *	EOB_ACT_LAST_MATCH -
+ *	EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *	EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+    	register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+	register char *source = (yytext_ptr);
+	register int number_to_move, i;
+	int ret_val;
+
+	if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+		YY_FATAL_ERROR(
+		"fatal flex scanner internal error--end of buffer missed" );
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+		{ /* Don't try to fill the buffer, so this is an EOF. */
+		if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+			{
+			/* We matched a single character, the EOB, so
+			 * treat this as a final EOF.
+			 */
+			return EOB_ACT_END_OF_FILE;
+			}
+
+		else
+			{
+			/* We matched some text prior to the EOB, first
+			 * process it.
+			 */
+			return EOB_ACT_LAST_MATCH;
+			}
+		}
+
+	/* Try to read more data. */
+
+	/* First move last chars to start of buffer. */
+	number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+	for ( i = 0; i < number_to_move; ++i )
+		*(dest++) = *(source++);
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+		/* don't do the read, it's not guaranteed to return an EOF,
+		 * just force an EOF
+		 */
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+	else
+		{
+			int num_to_read =
+			YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+		while ( num_to_read <= 0 )
+			{ /* Not enough room in the buffer - grow it. */
+
+			/* just a shorter name for the current buffer */
+			YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+			int yy_c_buf_p_offset =
+				(int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+			if ( b->yy_is_our_buffer )
+				{
+				int new_size = b->yy_buf_size * 2;
+
+				if ( new_size <= 0 )
+					b->yy_buf_size += b->yy_buf_size / 8;
+				else
+					b->yy_buf_size *= 2;
+
+				b->yy_ch_buf = (char *)
+					/* Include room in for 2 EOB chars. */
+					yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2  );
+				}
+			else
+				/* Can't grow it, we don't own it. */
+				b->yy_ch_buf = 0;
+
+			if ( ! b->yy_ch_buf )
+				YY_FATAL_ERROR(
+				"fatal error - scanner input buffer overflow" );
+
+			(yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+			num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+						number_to_move - 1;
+
+			}
+
+		if ( num_to_read > YY_READ_BUF_SIZE )
+			num_to_read = YY_READ_BUF_SIZE;
+
+		/* Read in more data. */
+		YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+			(yy_n_chars), (size_t) num_to_read );
+
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+		}
+
+	if ( (yy_n_chars) == 0 )
+		{
+		if ( number_to_move == YY_MORE_ADJ )
+			{
+			ret_val = EOB_ACT_END_OF_FILE;
+			yyrestart(yyin  );
+			}
+
+		else
+			{
+			ret_val = EOB_ACT_LAST_MATCH;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+				YY_BUFFER_EOF_PENDING;
+			}
+		}
+
+	else
+		ret_val = EOB_ACT_CONTINUE_SCAN;
+
+	if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+		/* Extend the array by 50%, plus the number we really need. */
+		yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
+		YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size  );
+		if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+			YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+	}
+
+	(yy_n_chars) += number_to_move;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+	(yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+	return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+    static yy_state_type yy_get_previous_state (void)
+{
+	register yy_state_type yy_current_state;
+	register char *yy_cp;
+    
+	yy_current_state = (yy_start);
+
+	for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+		{
+		register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+		if ( yy_accept[yy_current_state] )
+			{
+			(yy_last_accepting_state) = yy_current_state;
+			(yy_last_accepting_cpos) = yy_cp;
+			}
+		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+			{
+			yy_current_state = (int) yy_def[yy_current_state];
+			if ( yy_current_state >= 1303 )
+				yy_c = yy_meta[(unsigned int) yy_c];
+			}
+		yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+		}
+
+	return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *	next_state = yy_try_NUL_trans( current_state );
+ */
+    static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state )
+{
+	register int yy_is_jam;
+    	register char *yy_cp = (yy_c_buf_p);
+
+	register YY_CHAR yy_c = 1;
+	if ( yy_accept[yy_current_state] )
+		{
+		(yy_last_accepting_state) = yy_current_state;
+		(yy_last_accepting_cpos) = yy_cp;
+		}
+	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+		{
+		yy_current_state = (int) yy_def[yy_current_state];
+		if ( yy_current_state >= 1303 )
+			yy_c = yy_meta[(unsigned int) yy_c];
+		}
+	yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+	yy_is_jam = (yy_current_state == 1302);
+
+	return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+    static int yyinput (void)
+#else
+    static int input  (void)
+#endif
+
+{
+	int c;
+    
+	*(yy_c_buf_p) = (yy_hold_char);
+
+	if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+		{
+		/* yy_c_buf_p now points to the character we want to return.
+		 * If this occurs *before* the EOB characters, then it's a
+		 * valid NUL; if not, then we've hit the end of the buffer.
+		 */
+		if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+			/* This was really a NUL. */
+			*(yy_c_buf_p) = '\0';
+
+		else
+			{ /* need more input */
+			int offset = (yy_c_buf_p) - (yytext_ptr);
+			++(yy_c_buf_p);
+
+			switch ( yy_get_next_buffer(  ) )
+				{
+				case EOB_ACT_LAST_MATCH:
+					/* This happens because yy_g_n_b()
+					 * sees that we've accumulated a
+					 * token and flags that we need to
+					 * try matching the token before
+					 * proceeding.  But for input(),
+					 * there's no matching to consider.
+					 * So convert the EOB_ACT_LAST_MATCH
+					 * to EOB_ACT_END_OF_FILE.
+					 */
+
+					/* Reset buffer status. */
+					yyrestart(yyin );
+
+					/*FALLTHROUGH*/
+
+				case EOB_ACT_END_OF_FILE:
+					{
+					if ( yywrap( ) )
+						return EOF;
+
+					if ( ! (yy_did_buffer_switch_on_eof) )
+						YY_NEW_FILE;
+#ifdef __cplusplus
+					return yyinput();
+#else
+					return input();
+#endif
+					}
+
+				case EOB_ACT_CONTINUE_SCAN:
+					(yy_c_buf_p) = (yytext_ptr) + offset;
+					break;
+				}
+			}
+		}
+
+	c = *(unsigned char *) (yy_c_buf_p);	/* cast for 8-bit char's */
+	*(yy_c_buf_p) = '\0';	/* preserve yytext */
+	(yy_hold_char) = *++(yy_c_buf_p);
+
+	return c;
+}
+#endif	/* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * 
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+    void yyrestart  (FILE * input_file )
+{
+    
+	if ( ! YY_CURRENT_BUFFER ){
+        yyensure_buffer_stack ();
+		YY_CURRENT_BUFFER_LVALUE =
+            yy_create_buffer(yyin,YY_BUF_SIZE );
+	}
+
+	yy_init_buffer(YY_CURRENT_BUFFER,input_file );
+	yy_load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * 
+ */
+    void yy_switch_to_buffer  (YY_BUFFER_STATE  new_buffer )
+{
+    
+	/* TODO. We should be able to replace this entire function body
+	 * with
+	 *		yypop_buffer_state();
+	 *		yypush_buffer_state(new_buffer);
+     */
+	yyensure_buffer_stack ();
+	if ( YY_CURRENT_BUFFER == new_buffer )
+		return;
+
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*(yy_c_buf_p) = (yy_hold_char);
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+		}
+
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+	yy_load_buffer_state( );
+
+	/* We don't actually know whether we did this switch during
+	 * EOF (yywrap()) processing, but the only time this flag
+	 * is looked at is after yywrap() is called, so it's safe
+	 * to go ahead and always set it.
+	 */
+	(yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void yy_load_buffer_state  (void)
+{
+    	(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+	(yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+	yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+	(yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * 
+ * @return the allocated buffer state.
+ */
+    YY_BUFFER_STATE yy_create_buffer  (FILE * file, int  size )
+{
+	YY_BUFFER_STATE b;
+    
+	b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state )  );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+	b->yy_buf_size = size;
+
+	/* yy_ch_buf has to be 2 characters longer than the size given because
+	 * we need to put in 2 end-of-buffer characters.
+	 */
+	b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2  );
+	if ( ! b->yy_ch_buf )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+	b->yy_is_our_buffer = 1;
+
+	yy_init_buffer(b,file );
+
+	return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ * 
+ */
+    void yy_delete_buffer (YY_BUFFER_STATE  b )
+{
+    
+	if ( ! b )
+		return;
+
+	if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+		YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+	if ( b->yy_is_our_buffer )
+		yyfree((void *) b->yy_ch_buf  );
+
+	yyfree((void *) b  );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+    
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+    static void yy_init_buffer  (YY_BUFFER_STATE  b, FILE * file )
+
+{
+	int oerrno = errno;
+    
+	yy_flush_buffer(b );
+
+	b->yy_input_file = file;
+	b->yy_fill_buffer = 1;
+
+    /* If b is the current buffer, then yy_init_buffer was _probably_
+     * called from yyrestart() or through yy_get_next_buffer.
+     * In that case, we don't want to reset the lineno or column.
+     */
+    if (b != YY_CURRENT_BUFFER){
+        b->yy_bs_lineno = 1;
+        b->yy_bs_column = 0;
+    }
+
+        b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+    
+	errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * 
+ */
+    void yy_flush_buffer (YY_BUFFER_STATE  b )
+{
+    	if ( ! b )
+		return;
+
+	b->yy_n_chars = 0;
+
+	/* We always need two end-of-buffer characters.  The first causes
+	 * a transition to the end-of-buffer state.  The second causes
+	 * a jam in that state.
+	 */
+	b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+	b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+	b->yy_buf_pos = &b->yy_ch_buf[0];
+
+	b->yy_at_bol = 1;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	if ( b == YY_CURRENT_BUFFER )
+		yy_load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ *  the current state. This function will allocate the stack
+ *  if necessary.
+ *  @param new_buffer The new state.
+ *  
+ */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+    	if (new_buffer == NULL)
+		return;
+
+	yyensure_buffer_stack();
+
+	/* This block is copied from yy_switch_to_buffer. */
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*(yy_c_buf_p) = (yy_hold_char);
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+		}
+
+	/* Only push if top exists. Otherwise, replace top. */
+	if (YY_CURRENT_BUFFER)
+		(yy_buffer_stack_top)++;
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+	/* copied from yy_switch_to_buffer. */
+	yy_load_buffer_state( );
+	(yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ *  The next element becomes the new top.
+ *  
+ */
+void yypop_buffer_state (void)
+{
+    	if (!YY_CURRENT_BUFFER)
+		return;
+
+	yy_delete_buffer(YY_CURRENT_BUFFER );
+	YY_CURRENT_BUFFER_LVALUE = NULL;
+	if ((yy_buffer_stack_top) > 0)
+		--(yy_buffer_stack_top);
+
+	if (YY_CURRENT_BUFFER) {
+		yy_load_buffer_state( );
+		(yy_did_buffer_switch_on_eof) = 1;
+	}
+}
+
+/* Allocates the stack if it does not exist.
+ *  Guarantees space for at least one push.
+ */
+static void yyensure_buffer_stack (void)
+{
+	int num_to_alloc;
+    
+	if (!(yy_buffer_stack)) {
+
+		/* First allocation is just for 2 elements, since we don't know if this
+		 * scanner will even need a stack. We use 2 instead of 1 to avoid an
+		 * immediate realloc on the next call.
+         */
+		num_to_alloc = 1;
+		(yy_buffer_stack) = (struct yy_buffer_state**)yyalloc
+								(num_to_alloc * sizeof(struct yy_buffer_state*)
+								);
+		if ( ! (yy_buffer_stack) )
+			YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+								  
+		memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+				
+		(yy_buffer_stack_max) = num_to_alloc;
+		(yy_buffer_stack_top) = 0;
+		return;
+	}
+
+	if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+		/* Increase the buffer to prepare for a possible push. */
+		int grow_size = 8 /* arbitrary grow size */;
+
+		num_to_alloc = (yy_buffer_stack_max) + grow_size;
+		(yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc
+								((yy_buffer_stack),
+								num_to_alloc * sizeof(struct yy_buffer_state*)
+								);
+		if ( ! (yy_buffer_stack) )
+			YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+		/* zero only the new slots.*/
+		memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+		(yy_buffer_stack_max) = num_to_alloc;
+	}
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * 
+ * @return the newly allocated buffer state object. 
+ */
+YY_BUFFER_STATE yy_scan_buffer  (char * base, yy_size_t  size )
+{
+	YY_BUFFER_STATE b;
+    
+	if ( size < 2 ||
+	     base[size-2] != YY_END_OF_BUFFER_CHAR ||
+	     base[size-1] != YY_END_OF_BUFFER_CHAR )
+		/* They forgot to leave room for the EOB's. */
+		return 0;
+
+	b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state )  );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+	b->yy_buf_size = size - 2;	/* "- 2" to take care of EOB's */
+	b->yy_buf_pos = b->yy_ch_buf = base;
+	b->yy_is_our_buffer = 0;
+	b->yy_input_file = 0;
+	b->yy_n_chars = b->yy_buf_size;
+	b->yy_is_interactive = 0;
+	b->yy_at_bol = 1;
+	b->yy_fill_buffer = 0;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	yy_switch_to_buffer(b  );
+
+	return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * 
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ *       yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (yyconst char * yystr )
+{
+    
+	return yy_scan_bytes(yystr,strlen(yystr) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ * 
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes  (yyconst char * yybytes, int  _yybytes_len )
+{
+	YY_BUFFER_STATE b;
+	char *buf;
+	yy_size_t n;
+	int i;
+    
+	/* Get memory for full buffer, including space for trailing EOB's. */
+	n = _yybytes_len + 2;
+	buf = (char *) yyalloc(n  );
+	if ( ! buf )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+	for ( i = 0; i < _yybytes_len; ++i )
+		buf[i] = yybytes[i];
+
+	buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+	b = yy_scan_buffer(buf,n );
+	if ( ! b )
+		YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+	/* It's okay to grow etc. this buffer, and we should throw it
+	 * away when we're done.
+	 */
+	b->yy_is_our_buffer = 1;
+
+	return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+    	(void) fprintf( stderr, "%s\n", msg );
+	exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		yytext[yyleng] = (yy_hold_char); \
+		(yy_c_buf_p) = yytext + yyless_macro_arg; \
+		(yy_hold_char) = *(yy_c_buf_p); \
+		*(yy_c_buf_p) = '\0'; \
+		yyleng = yyless_macro_arg; \
+		} \
+	while ( 0 )
+
+/* Accessor  methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ * 
+ */
+int yyget_lineno  (void)
+{
+        
+    return yylineno;
+}
+
+/** Get the input stream.
+ * 
+ */
+FILE *yyget_in  (void)
+{
+        return yyin;
+}
+
+/** Get the output stream.
+ * 
+ */
+FILE *yyget_out  (void)
+{
+        return yyout;
+}
+
+/** Get the length of the current token.
+ * 
+ */
+int yyget_leng  (void)
+{
+        return yyleng;
+}
+
+/** Get the current token.
+ * 
+ */
+
+char *yyget_text  (void)
+{
+        return yytext;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * 
+ */
+void yyset_lineno (int  line_number )
+{
+    
+    yylineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * 
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE *  in_str )
+{
+        yyin = in_str ;
+}
+
+void yyset_out (FILE *  out_str )
+{
+        yyout = out_str ;
+}
+
+int yyget_debug  (void)
+{
+        return yy_flex_debug;
+}
+
+void yyset_debug (int  bdebug )
+{
+        yy_flex_debug = bdebug ;
+}
+
+static int yy_init_globals (void)
+{
+        /* Initialization is the same as for the non-reentrant scanner.
+     * This function is called from yylex_destroy(), so don't allocate here.
+     */
+
+    (yy_buffer_stack) = 0;
+    (yy_buffer_stack_top) = 0;
+    (yy_buffer_stack_max) = 0;
+    (yy_c_buf_p) = (char *) 0;
+    (yy_init) = 0;
+    (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+    yyin = stdin;
+    yyout = stdout;
+#else
+    yyin = (FILE *) 0;
+    yyout = (FILE *) 0;
+#endif
+
+    /* For future reference: Set errno on error, since we are called by
+     * yylex_init()
+     */
+    return 0;
+}
+
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy  (void)
+{
+    
+    /* Pop the buffer stack, destroying each element. */
+	while(YY_CURRENT_BUFFER){
+		yy_delete_buffer(YY_CURRENT_BUFFER  );
+		YY_CURRENT_BUFFER_LVALUE = NULL;
+		yypop_buffer_state();
+	}
+
+	/* Destroy the stack itself. */
+	yyfree((yy_buffer_stack) );
+	(yy_buffer_stack) = NULL;
+
+    /* Reset the globals. This is important in a non-reentrant scanner so the next time
+     * yylex() is called, initialization will occur. */
+    yy_init_globals( );
+
+    return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+	register int i;
+	for ( i = 0; i < n; ++i )
+		s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+	register int n;
+	for ( n = 0; s[n]; ++n )
+		;
+
+	return n;
+}
+#endif
+
+void *yyalloc (yy_size_t  size )
+{
+	return (void *) malloc( size );
+}
+
+void *yyrealloc  (void * ptr, yy_size_t  size )
+{
+	/* The cast to (char *) in the following accommodates both
+	 * implementations that use char* generic pointers, and those
+	 * that use void* generic pointers.  It works with the latter
+	 * because both ANSI C and C++ allow castless assignment from
+	 * any pointer type to void*, and deal with argument conversions
+	 * as though doing an assignment.
+	 */
+	return (void *) realloc( (char *) ptr, size );
+}
+
+void yyfree (void * ptr )
+{
+	free( (char *) ptr );	/* see yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 336 "util/configlexer.lex"
+
+
+
diff --git a/3rdParty/Unbound/src/src/util/configlexer.lex b/3rdParty/Unbound/src/src/util/configlexer.lex
new file mode 100644
index 0000000..2661ffd
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/configlexer.lex
@@ -0,0 +1,336 @@
+%{
+/*
+ * configlexer.lex - lexical analyzer for unbound config file
+ *
+ * Copyright (c) 2001-2006, NLnet Labs. All rights reserved
+ *
+ * See LICENSE for the license.
+ *
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+
+#include "util/config_file.h"
+#include "util/configparser.h"
+void ub_c_error(const char *message);
+
+#if 0
+#define LEXOUT(s)  printf s /* used ONLY when debugging */
+#else
+#define LEXOUT(s)
+#endif
+
+/** avoid warning in about fwrite return value */
+#define ECHO ub_c_error_msg("syntax error at text: %s", yytext)
+
+/** A parser variable, this is a statement in the config file which is
+ * of the form variable: value1 value2 ...  nargs is the number of values. */
+#define YDVAR(nargs, var) \
+	num_args=(nargs); \
+	LEXOUT(("v(%s%d) ", yytext, num_args)); \
+	if(num_args > 0) { BEGIN(val); } \
+	return (var);
+
+struct inc_state {
+	char* filename;
+	int line;
+};
+static struct inc_state parse_stack[MAXINCLUDES];
+static YY_BUFFER_STATE include_stack[MAXINCLUDES];
+static int config_include_stack_ptr = 0;
+static int inc_prev = 0;
+static int num_args = 0;
+
+static void config_start_include(const char* filename)
+{
+	FILE *input;
+	if(strlen(filename) == 0) {
+		ub_c_error_msg("empty include file name");
+		return;
+	}
+	if(config_include_stack_ptr >= MAXINCLUDES) {
+		ub_c_error_msg("includes nested too deeply, skipped (>%d)", MAXINCLUDES);
+		return;
+	}
+	if(cfg_parser->chroot && strncmp(filename, cfg_parser->chroot,
+		strlen(cfg_parser->chroot)) == 0) {
+		filename += strlen(cfg_parser->chroot);
+	}
+	input = fopen(filename, "r");
+	if(!input) {
+		ub_c_error_msg("cannot open include file '%s': %s",
+			filename, strerror(errno));
+		return;
+	}
+	LEXOUT(("switch_to_include_file(%s) ", filename));
+	parse_stack[config_include_stack_ptr].filename = cfg_parser->filename;
+	parse_stack[config_include_stack_ptr].line = cfg_parser->line;
+	include_stack[config_include_stack_ptr] = YY_CURRENT_BUFFER;
+	cfg_parser->filename = strdup(filename);
+	cfg_parser->line = 1;
+	yy_switch_to_buffer(yy_create_buffer(input, YY_BUF_SIZE));
+	++config_include_stack_ptr;
+}
+
+static void config_end_include(void)
+{
+	--config_include_stack_ptr;
+	free(cfg_parser->filename);
+	cfg_parser->filename = parse_stack[config_include_stack_ptr].filename;
+	cfg_parser->line = parse_stack[config_include_stack_ptr].line;
+	yy_delete_buffer(YY_CURRENT_BUFFER);
+	yy_switch_to_buffer(include_stack[config_include_stack_ptr]);
+}
+
+#ifndef yy_set_bol /* compat definition, for flex 2.4.6 */
+#define yy_set_bol(at_bol) \
+        { \
+	        if ( ! yy_current_buffer ) \
+	                yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+	        yy_current_buffer->yy_ch_buf[0] = ((at_bol)?'\n':' '); \
+        }
+#endif
+
+%}
+%option noinput
+%option nounput
+%{
+#ifndef YY_NO_UNPUT
+#define YY_NO_UNPUT 1
+#endif
+#ifndef YY_NO_INPUT
+#define YY_NO_INPUT 1
+#endif
+%}
+
+SPACE   [ \t]
+LETTER  [a-zA-Z]
+UNQUOTEDLETTER [^\'\"\n\r \t\\]|\\.
+UNQUOTEDLETTER_NOCOLON [^\:\'\"\n\r \t\\]|\\.
+NEWLINE [\r\n]
+COMMENT \#
+COLON 	\:
+DQANY     [^\"\n\r\\]|\\.
+SQANY     [^\'\n\r\\]|\\.
+
+%x	quotedstring singlequotedstr include include_quoted val
+
+%%
+<INITIAL,val>{SPACE}*	{ 
+	LEXOUT(("SP ")); /* ignore */ }
+<INITIAL,val>{SPACE}*{COMMENT}.*	{ 
+	/* note that flex makes the longest match and '.' is any but not nl */
+	LEXOUT(("comment(%s) ", yytext)); /* ignore */ }
+server{COLON}			{ YDVAR(0, VAR_SERVER) }
+num-threads{COLON}		{ YDVAR(1, VAR_NUM_THREADS) }
+verbosity{COLON}		{ YDVAR(1, VAR_VERBOSITY) }
+port{COLON}			{ YDVAR(1, VAR_PORT) }
+outgoing-range{COLON}		{ YDVAR(1, VAR_OUTGOING_RANGE) }
+outgoing-port-permit{COLON}	{ YDVAR(1, VAR_OUTGOING_PORT_PERMIT) }
+outgoing-port-avoid{COLON}	{ YDVAR(1, VAR_OUTGOING_PORT_AVOID) }
+outgoing-num-tcp{COLON}		{ YDVAR(1, VAR_OUTGOING_NUM_TCP) }
+incoming-num-tcp{COLON}		{ YDVAR(1, VAR_INCOMING_NUM_TCP) }
+do-ip4{COLON}			{ YDVAR(1, VAR_DO_IP4) }
+do-ip6{COLON}			{ YDVAR(1, VAR_DO_IP6) }
+do-udp{COLON}			{ YDVAR(1, VAR_DO_UDP) }
+do-tcp{COLON}			{ YDVAR(1, VAR_DO_TCP) }
+tcp-upstream{COLON}		{ YDVAR(1, VAR_TCP_UPSTREAM) }
+ssl-upstream{COLON}		{ YDVAR(1, VAR_SSL_UPSTREAM) }
+ssl-service-key{COLON}		{ YDVAR(1, VAR_SSL_SERVICE_KEY) }
+ssl-service-pem{COLON}		{ YDVAR(1, VAR_SSL_SERVICE_PEM) }
+ssl-port{COLON}			{ YDVAR(1, VAR_SSL_PORT) }
+do-daemonize{COLON}		{ YDVAR(1, VAR_DO_DAEMONIZE) }
+interface{COLON}		{ YDVAR(1, VAR_INTERFACE) }
+outgoing-interface{COLON}	{ YDVAR(1, VAR_OUTGOING_INTERFACE) }
+interface-automatic{COLON}	{ YDVAR(1, VAR_INTERFACE_AUTOMATIC) }
+so-rcvbuf{COLON}		{ YDVAR(1, VAR_SO_RCVBUF) }
+so-sndbuf{COLON}		{ YDVAR(1, VAR_SO_SNDBUF) }
+chroot{COLON}			{ YDVAR(1, VAR_CHROOT) }
+username{COLON}			{ YDVAR(1, VAR_USERNAME) }
+directory{COLON}		{ YDVAR(1, VAR_DIRECTORY) }
+logfile{COLON}			{ YDVAR(1, VAR_LOGFILE) }
+pidfile{COLON}			{ YDVAR(1, VAR_PIDFILE) }
+root-hints{COLON}		{ YDVAR(1, VAR_ROOT_HINTS) }
+edns-buffer-size{COLON}		{ YDVAR(1, VAR_EDNS_BUFFER_SIZE) }
+msg-buffer-size{COLON}		{ YDVAR(1, VAR_MSG_BUFFER_SIZE) }
+msg-cache-size{COLON}		{ YDVAR(1, VAR_MSG_CACHE_SIZE) }
+msg-cache-slabs{COLON}		{ YDVAR(1, VAR_MSG_CACHE_SLABS) }
+rrset-cache-size{COLON}		{ YDVAR(1, VAR_RRSET_CACHE_SIZE) }
+rrset-cache-slabs{COLON}	{ YDVAR(1, VAR_RRSET_CACHE_SLABS) }
+cache-max-ttl{COLON}     	{ YDVAR(1, VAR_CACHE_MAX_TTL) }
+cache-min-ttl{COLON}     	{ YDVAR(1, VAR_CACHE_MIN_TTL) }
+infra-host-ttl{COLON}		{ YDVAR(1, VAR_INFRA_HOST_TTL) }
+infra-lame-ttl{COLON}		{ YDVAR(1, VAR_INFRA_LAME_TTL) }
+infra-cache-slabs{COLON}	{ YDVAR(1, VAR_INFRA_CACHE_SLABS) }
+infra-cache-numhosts{COLON}	{ YDVAR(1, VAR_INFRA_CACHE_NUMHOSTS) }
+infra-cache-lame-size{COLON}	{ YDVAR(1, VAR_INFRA_CACHE_LAME_SIZE) }
+num-queries-per-thread{COLON}	{ YDVAR(1, VAR_NUM_QUERIES_PER_THREAD) }
+jostle-timeout{COLON}		{ YDVAR(1, VAR_JOSTLE_TIMEOUT) }
+target-fetch-policy{COLON}	{ YDVAR(1, VAR_TARGET_FETCH_POLICY) }
+harden-short-bufsize{COLON}	{ YDVAR(1, VAR_HARDEN_SHORT_BUFSIZE) }
+harden-large-queries{COLON}	{ YDVAR(1, VAR_HARDEN_LARGE_QUERIES) }
+harden-glue{COLON}		{ YDVAR(1, VAR_HARDEN_GLUE) }
+harden-dnssec-stripped{COLON}	{ YDVAR(1, VAR_HARDEN_DNSSEC_STRIPPED) }
+harden-below-nxdomain{COLON}	{ YDVAR(1, VAR_HARDEN_BELOW_NXDOMAIN) }
+harden-referral-path{COLON}	{ YDVAR(1, VAR_HARDEN_REFERRAL_PATH) }
+use-caps-for-id{COLON}		{ YDVAR(1, VAR_USE_CAPS_FOR_ID) }
+unwanted-reply-threshold{COLON}	{ YDVAR(1, VAR_UNWANTED_REPLY_THRESHOLD) }
+private-address{COLON}		{ YDVAR(1, VAR_PRIVATE_ADDRESS) }
+private-domain{COLON}		{ YDVAR(1, VAR_PRIVATE_DOMAIN) }
+prefetch-key{COLON}		{ YDVAR(1, VAR_PREFETCH_KEY) }
+prefetch{COLON}			{ YDVAR(1, VAR_PREFETCH) }
+stub-zone{COLON}		{ YDVAR(0, VAR_STUB_ZONE) }
+name{COLON}			{ YDVAR(1, VAR_NAME) }
+stub-addr{COLON}		{ YDVAR(1, VAR_STUB_ADDR) }
+stub-host{COLON}		{ YDVAR(1, VAR_STUB_HOST) }
+stub-prime{COLON}		{ YDVAR(1, VAR_STUB_PRIME) }
+forward-zone{COLON}		{ YDVAR(0, VAR_FORWARD_ZONE) }
+forward-addr{COLON}		{ YDVAR(1, VAR_FORWARD_ADDR) }
+forward-host{COLON}		{ YDVAR(1, VAR_FORWARD_HOST) }
+do-not-query-address{COLON}	{ YDVAR(1, VAR_DO_NOT_QUERY_ADDRESS) }
+do-not-query-localhost{COLON}	{ YDVAR(1, VAR_DO_NOT_QUERY_LOCALHOST) }
+access-control{COLON}		{ YDVAR(2, VAR_ACCESS_CONTROL) }
+hide-identity{COLON}		{ YDVAR(1, VAR_HIDE_IDENTITY) }
+hide-version{COLON}		{ YDVAR(1, VAR_HIDE_VERSION) }
+identity{COLON}			{ YDVAR(1, VAR_IDENTITY) }
+version{COLON}			{ YDVAR(1, VAR_VERSION) }
+module-config{COLON}     	{ YDVAR(1, VAR_MODULE_CONF) }
+dlv-anchor{COLON}		{ YDVAR(1, VAR_DLV_ANCHOR) }
+dlv-anchor-file{COLON}		{ YDVAR(1, VAR_DLV_ANCHOR_FILE) }
+trust-anchor-file{COLON}	{ YDVAR(1, VAR_TRUST_ANCHOR_FILE) }
+auto-trust-anchor-file{COLON}	{ YDVAR(1, VAR_AUTO_TRUST_ANCHOR_FILE) }
+trusted-keys-file{COLON}	{ YDVAR(1, VAR_TRUSTED_KEYS_FILE) }
+trust-anchor{COLON}		{ YDVAR(1, VAR_TRUST_ANCHOR) }
+val-override-date{COLON}	{ YDVAR(1, VAR_VAL_OVERRIDE_DATE) }
+val-sig-skew-min{COLON}		{ YDVAR(1, VAR_VAL_SIG_SKEW_MIN) }
+val-sig-skew-max{COLON}		{ YDVAR(1, VAR_VAL_SIG_SKEW_MAX) }
+val-bogus-ttl{COLON}		{ YDVAR(1, VAR_BOGUS_TTL) }
+val-clean-additional{COLON}	{ YDVAR(1, VAR_VAL_CLEAN_ADDITIONAL) }
+val-permissive-mode{COLON}	{ YDVAR(1, VAR_VAL_PERMISSIVE_MODE) }
+ignore-cd-flag{COLON}		{ YDVAR(1, VAR_IGNORE_CD_FLAG) }
+val-log-level{COLON}		{ YDVAR(1, VAR_VAL_LOG_LEVEL) }
+key-cache-size{COLON}		{ YDVAR(1, VAR_KEY_CACHE_SIZE) }
+key-cache-slabs{COLON}		{ YDVAR(1, VAR_KEY_CACHE_SLABS) }
+neg-cache-size{COLON}		{ YDVAR(1, VAR_NEG_CACHE_SIZE) }
+val-nsec3-keysize-iterations{COLON}	{ 
+				  YDVAR(1, VAR_VAL_NSEC3_KEYSIZE_ITERATIONS) }
+add-holddown{COLON}		{ YDVAR(1, VAR_ADD_HOLDDOWN) }
+del-holddown{COLON}		{ YDVAR(1, VAR_DEL_HOLDDOWN) }
+keep-missing{COLON}		{ YDVAR(1, VAR_KEEP_MISSING) }
+use-syslog{COLON}		{ YDVAR(1, VAR_USE_SYSLOG) }
+log-time-ascii{COLON}		{ YDVAR(1, VAR_LOG_TIME_ASCII) }
+log-queries{COLON}		{ YDVAR(1, VAR_LOG_QUERIES) }
+local-zone{COLON}		{ YDVAR(2, VAR_LOCAL_ZONE) }
+local-data{COLON}		{ YDVAR(1, VAR_LOCAL_DATA) }
+local-data-ptr{COLON}		{ YDVAR(1, VAR_LOCAL_DATA_PTR) }
+statistics-interval{COLON}	{ YDVAR(1, VAR_STATISTICS_INTERVAL) }
+statistics-cumulative{COLON}	{ YDVAR(1, VAR_STATISTICS_CUMULATIVE) }
+extended-statistics{COLON}	{ YDVAR(1, VAR_EXTENDED_STATISTICS) }
+remote-control{COLON}		{ YDVAR(0, VAR_REMOTE_CONTROL) }
+control-enable{COLON}		{ YDVAR(1, VAR_CONTROL_ENABLE) }
+control-interface{COLON}	{ YDVAR(1, VAR_CONTROL_INTERFACE) }
+control-port{COLON}		{ YDVAR(1, VAR_CONTROL_PORT) }
+server-key-file{COLON}		{ YDVAR(1, VAR_SERVER_KEY_FILE) }
+server-cert-file{COLON}		{ YDVAR(1, VAR_SERVER_CERT_FILE) }
+control-key-file{COLON}		{ YDVAR(1, VAR_CONTROL_KEY_FILE) }
+control-cert-file{COLON}	{ YDVAR(1, VAR_CONTROL_CERT_FILE) }
+python-script{COLON}		{ YDVAR(1, VAR_PYTHON_SCRIPT) }
+python{COLON}			{ YDVAR(0, VAR_PYTHON) }
+domain-insecure{COLON}		{ YDVAR(1, VAR_DOMAIN_INSECURE) }
+<INITIAL,val>{NEWLINE}		{ LEXOUT(("NL\n")); cfg_parser->line++; }
+
+	/* Quoted strings. Strip leading and ending quotes */
+<val>\"			{ BEGIN(quotedstring); LEXOUT(("QS ")); }
+<quotedstring><<EOF>>   {
+        yyerror("EOF inside quoted string");
+	if(--num_args == 0) { BEGIN(INITIAL); }
+	else		    { BEGIN(val); }
+}
+<quotedstring>{DQANY}*  { LEXOUT(("STR(%s) ", yytext)); yymore(); }
+<quotedstring>{NEWLINE} { yyerror("newline inside quoted string, no end \""); 
+			  cfg_parser->line++; BEGIN(INITIAL); }
+<quotedstring>\" {
+        LEXOUT(("QE "));
+	if(--num_args == 0) { BEGIN(INITIAL); }
+	else		    { BEGIN(val); }
+        yytext[yyleng - 1] = '\0';
+	yylval.str = strdup(yytext);
+	if(!yylval.str)
+		yyerror("out of memory");
+        return STRING_ARG;
+}
+
+	/* Single Quoted strings. Strip leading and ending quotes */
+<val>\'			{ BEGIN(singlequotedstr); LEXOUT(("SQS ")); }
+<singlequotedstr><<EOF>>   {
+        yyerror("EOF inside quoted string");
+	if(--num_args == 0) { BEGIN(INITIAL); }
+	else		    { BEGIN(val); }
+}
+<singlequotedstr>{SQANY}*  { LEXOUT(("STR(%s) ", yytext)); yymore(); }
+<singlequotedstr>{NEWLINE} { yyerror("newline inside quoted string, no end '"); 
+			     cfg_parser->line++; BEGIN(INITIAL); }
+<singlequotedstr>\' {
+        LEXOUT(("SQE "));
+	if(--num_args == 0) { BEGIN(INITIAL); }
+	else		    { BEGIN(val); }
+        yytext[yyleng - 1] = '\0';
+	yylval.str = strdup(yytext);
+	if(!yylval.str)
+		yyerror("out of memory");
+        return STRING_ARG;
+}
+
+	/* include: directive */
+<INITIAL,val>include{COLON}	{ 
+	LEXOUT(("v(%s) ", yytext)); inc_prev = YYSTATE; BEGIN(include); }
+<include><<EOF>>	{
+        yyerror("EOF inside include directive");
+        BEGIN(inc_prev);
+}
+<include>{SPACE}*	{ LEXOUT(("ISP ")); /* ignore */ }
+<include>{NEWLINE}	{ LEXOUT(("NL\n")); cfg_parser->line++;}
+<include>\"		{ LEXOUT(("IQS ")); BEGIN(include_quoted); }
+<include>{UNQUOTEDLETTER}*	{
+	LEXOUT(("Iunquotedstr(%s) ", yytext));
+	config_start_include(yytext);
+	BEGIN(inc_prev);
+}
+<include_quoted><<EOF>>	{
+        yyerror("EOF inside quoted string");
+        BEGIN(inc_prev);
+}
+<include_quoted>{DQANY}*	{ LEXOUT(("ISTR(%s) ", yytext)); yymore(); }
+<include_quoted>{NEWLINE}	{ yyerror("newline before \" in include name"); 
+				  cfg_parser->line++; BEGIN(inc_prev); }
+<include_quoted>\"	{
+	LEXOUT(("IQE "));
+	yytext[yyleng - 1] = '\0';
+	config_start_include(yytext);
+	BEGIN(inc_prev);
+}
+<INITIAL,val><<EOF>>	{
+	yy_set_bol(1); /* Set beginning of line, so "^" rules match.  */
+	if (config_include_stack_ptr == 0) {
+		yyterminate();
+	} else {
+		fclose(yyin);
+		config_end_include();
+	}
+}
+
+<val>{UNQUOTEDLETTER}*	{ LEXOUT(("unquotedstr(%s) ", yytext)); 
+			if(--num_args == 0) { BEGIN(INITIAL); }
+			yylval.str = strdup(yytext); return STRING_ARG; }
+
+{UNQUOTEDLETTER_NOCOLON}*	{
+	ub_c_error_msg("unknown keyword '%s'", yytext);
+	}
+
+<*>.	{
+	ub_c_error_msg("stray '%s'", yytext);
+	}
+
+%%
diff --git a/3rdParty/Unbound/src/src/util/configparser.c b/3rdParty/Unbound/src/src/util/configparser.c
new file mode 100644
index 0000000..1fe21ad
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/configparser.c
@@ -0,0 +1,3811 @@
+/* A Bison parser, made by GNU Bison 2.5.  */
+
+/* Bison implementation for Yacc-like parsers in C
+   
+      Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
+   
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+   
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+   simplifying the original so-called "semantic" parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Bison version.  */
+#define YYBISON_VERSION "2.5"
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 0
+
+/* Push parsers.  */
+#define YYPUSH 0
+
+/* Pull parsers.  */
+#define YYPULL 1
+
+/* Using locations.  */
+#define YYLSP_NEEDED 0
+
+
+
+/* Copy the first part of user declarations.  */
+
+/* Line 268 of yacc.c  */
+#line 38 "util/configparser.y"
+
+#include "config.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "util/configyyrename.h"
+#include "util/config_file.h"
+#include "util/net_help.h"
+
+int ub_c_lex(void);
+void ub_c_error(const char *message);
+
+/* these need to be global, otherwise they cannot be used inside yacc */
+extern struct config_parser_state* cfg_parser;
+
+#if 0
+#define OUTYY(s)  printf s /* used ONLY when debugging */
+#else
+#define OUTYY(s)
+#endif
+
+
+
+/* Line 268 of yacc.c  */
+#line 99 "util/configparser.c"
+
+/* Enabling traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table.  */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     SPACE = 258,
+     LETTER = 259,
+     NEWLINE = 260,
+     COMMENT = 261,
+     COLON = 262,
+     ANY = 263,
+     ZONESTR = 264,
+     STRING_ARG = 265,
+     VAR_SERVER = 266,
+     VAR_VERBOSITY = 267,
+     VAR_NUM_THREADS = 268,
+     VAR_PORT = 269,
+     VAR_OUTGOING_RANGE = 270,
+     VAR_INTERFACE = 271,
+     VAR_DO_IP4 = 272,
+     VAR_DO_IP6 = 273,
+     VAR_DO_UDP = 274,
+     VAR_DO_TCP = 275,
+     VAR_CHROOT = 276,
+     VAR_USERNAME = 277,
+     VAR_DIRECTORY = 278,
+     VAR_LOGFILE = 279,
+     VAR_PIDFILE = 280,
+     VAR_MSG_CACHE_SIZE = 281,
+     VAR_MSG_CACHE_SLABS = 282,
+     VAR_NUM_QUERIES_PER_THREAD = 283,
+     VAR_RRSET_CACHE_SIZE = 284,
+     VAR_RRSET_CACHE_SLABS = 285,
+     VAR_OUTGOING_NUM_TCP = 286,
+     VAR_INFRA_HOST_TTL = 287,
+     VAR_INFRA_LAME_TTL = 288,
+     VAR_INFRA_CACHE_SLABS = 289,
+     VAR_INFRA_CACHE_NUMHOSTS = 290,
+     VAR_INFRA_CACHE_LAME_SIZE = 291,
+     VAR_NAME = 292,
+     VAR_STUB_ZONE = 293,
+     VAR_STUB_HOST = 294,
+     VAR_STUB_ADDR = 295,
+     VAR_TARGET_FETCH_POLICY = 296,
+     VAR_HARDEN_SHORT_BUFSIZE = 297,
+     VAR_HARDEN_LARGE_QUERIES = 298,
+     VAR_FORWARD_ZONE = 299,
+     VAR_FORWARD_HOST = 300,
+     VAR_FORWARD_ADDR = 301,
+     VAR_DO_NOT_QUERY_ADDRESS = 302,
+     VAR_HIDE_IDENTITY = 303,
+     VAR_HIDE_VERSION = 304,
+     VAR_IDENTITY = 305,
+     VAR_VERSION = 306,
+     VAR_HARDEN_GLUE = 307,
+     VAR_MODULE_CONF = 308,
+     VAR_TRUST_ANCHOR_FILE = 309,
+     VAR_TRUST_ANCHOR = 310,
+     VAR_VAL_OVERRIDE_DATE = 311,
+     VAR_BOGUS_TTL = 312,
+     VAR_VAL_CLEAN_ADDITIONAL = 313,
+     VAR_VAL_PERMISSIVE_MODE = 314,
+     VAR_INCOMING_NUM_TCP = 315,
+     VAR_MSG_BUFFER_SIZE = 316,
+     VAR_KEY_CACHE_SIZE = 317,
+     VAR_KEY_CACHE_SLABS = 318,
+     VAR_TRUSTED_KEYS_FILE = 319,
+     VAR_VAL_NSEC3_KEYSIZE_ITERATIONS = 320,
+     VAR_USE_SYSLOG = 321,
+     VAR_OUTGOING_INTERFACE = 322,
+     VAR_ROOT_HINTS = 323,
+     VAR_DO_NOT_QUERY_LOCALHOST = 324,
+     VAR_CACHE_MAX_TTL = 325,
+     VAR_HARDEN_DNSSEC_STRIPPED = 326,
+     VAR_ACCESS_CONTROL = 327,
+     VAR_LOCAL_ZONE = 328,
+     VAR_LOCAL_DATA = 329,
+     VAR_INTERFACE_AUTOMATIC = 330,
+     VAR_STATISTICS_INTERVAL = 331,
+     VAR_DO_DAEMONIZE = 332,
+     VAR_USE_CAPS_FOR_ID = 333,
+     VAR_STATISTICS_CUMULATIVE = 334,
+     VAR_OUTGOING_PORT_PERMIT = 335,
+     VAR_OUTGOING_PORT_AVOID = 336,
+     VAR_DLV_ANCHOR_FILE = 337,
+     VAR_DLV_ANCHOR = 338,
+     VAR_NEG_CACHE_SIZE = 339,
+     VAR_HARDEN_REFERRAL_PATH = 340,
+     VAR_PRIVATE_ADDRESS = 341,
+     VAR_PRIVATE_DOMAIN = 342,
+     VAR_REMOTE_CONTROL = 343,
+     VAR_CONTROL_ENABLE = 344,
+     VAR_CONTROL_INTERFACE = 345,
+     VAR_CONTROL_PORT = 346,
+     VAR_SERVER_KEY_FILE = 347,
+     VAR_SERVER_CERT_FILE = 348,
+     VAR_CONTROL_KEY_FILE = 349,
+     VAR_CONTROL_CERT_FILE = 350,
+     VAR_EXTENDED_STATISTICS = 351,
+     VAR_LOCAL_DATA_PTR = 352,
+     VAR_JOSTLE_TIMEOUT = 353,
+     VAR_STUB_PRIME = 354,
+     VAR_UNWANTED_REPLY_THRESHOLD = 355,
+     VAR_LOG_TIME_ASCII = 356,
+     VAR_DOMAIN_INSECURE = 357,
+     VAR_PYTHON = 358,
+     VAR_PYTHON_SCRIPT = 359,
+     VAR_VAL_SIG_SKEW_MIN = 360,
+     VAR_VAL_SIG_SKEW_MAX = 361,
+     VAR_CACHE_MIN_TTL = 362,
+     VAR_VAL_LOG_LEVEL = 363,
+     VAR_AUTO_TRUST_ANCHOR_FILE = 364,
+     VAR_KEEP_MISSING = 365,
+     VAR_ADD_HOLDDOWN = 366,
+     VAR_DEL_HOLDDOWN = 367,
+     VAR_SO_RCVBUF = 368,
+     VAR_EDNS_BUFFER_SIZE = 369,
+     VAR_PREFETCH = 370,
+     VAR_PREFETCH_KEY = 371,
+     VAR_SO_SNDBUF = 372,
+     VAR_HARDEN_BELOW_NXDOMAIN = 373,
+     VAR_IGNORE_CD_FLAG = 374,
+     VAR_LOG_QUERIES = 375,
+     VAR_TCP_UPSTREAM = 376,
+     VAR_SSL_UPSTREAM = 377,
+     VAR_SSL_SERVICE_KEY = 378,
+     VAR_SSL_SERVICE_PEM = 379,
+     VAR_SSL_PORT = 380
+   };
+#endif
+/* Tokens.  */
+#define SPACE 258
+#define LETTER 259
+#define NEWLINE 260
+#define COMMENT 261
+#define COLON 262
+#define ANY 263
+#define ZONESTR 264
+#define STRING_ARG 265
+#define VAR_SERVER 266
+#define VAR_VERBOSITY 267
+#define VAR_NUM_THREADS 268
+#define VAR_PORT 269
+#define VAR_OUTGOING_RANGE 270
+#define VAR_INTERFACE 271
+#define VAR_DO_IP4 272
+#define VAR_DO_IP6 273
+#define VAR_DO_UDP 274
+#define VAR_DO_TCP 275
+#define VAR_CHROOT 276
+#define VAR_USERNAME 277
+#define VAR_DIRECTORY 278
+#define VAR_LOGFILE 279
+#define VAR_PIDFILE 280
+#define VAR_MSG_CACHE_SIZE 281
+#define VAR_MSG_CACHE_SLABS 282
+#define VAR_NUM_QUERIES_PER_THREAD 283
+#define VAR_RRSET_CACHE_SIZE 284
+#define VAR_RRSET_CACHE_SLABS 285
+#define VAR_OUTGOING_NUM_TCP 286
+#define VAR_INFRA_HOST_TTL 287
+#define VAR_INFRA_LAME_TTL 288
+#define VAR_INFRA_CACHE_SLABS 289
+#define VAR_INFRA_CACHE_NUMHOSTS 290
+#define VAR_INFRA_CACHE_LAME_SIZE 291
+#define VAR_NAME 292
+#define VAR_STUB_ZONE 293
+#define VAR_STUB_HOST 294
+#define VAR_STUB_ADDR 295
+#define VAR_TARGET_FETCH_POLICY 296
+#define VAR_HARDEN_SHORT_BUFSIZE 297
+#define VAR_HARDEN_LARGE_QUERIES 298
+#define VAR_FORWARD_ZONE 299
+#define VAR_FORWARD_HOST 300
+#define VAR_FORWARD_ADDR 301
+#define VAR_DO_NOT_QUERY_ADDRESS 302
+#define VAR_HIDE_IDENTITY 303
+#define VAR_HIDE_VERSION 304
+#define VAR_IDENTITY 305
+#define VAR_VERSION 306
+#define VAR_HARDEN_GLUE 307
+#define VAR_MODULE_CONF 308
+#define VAR_TRUST_ANCHOR_FILE 309
+#define VAR_TRUST_ANCHOR 310
+#define VAR_VAL_OVERRIDE_DATE 311
+#define VAR_BOGUS_TTL 312
+#define VAR_VAL_CLEAN_ADDITIONAL 313
+#define VAR_VAL_PERMISSIVE_MODE 314
+#define VAR_INCOMING_NUM_TCP 315
+#define VAR_MSG_BUFFER_SIZE 316
+#define VAR_KEY_CACHE_SIZE 317
+#define VAR_KEY_CACHE_SLABS 318
+#define VAR_TRUSTED_KEYS_FILE 319
+#define VAR_VAL_NSEC3_KEYSIZE_ITERATIONS 320
+#define VAR_USE_SYSLOG 321
+#define VAR_OUTGOING_INTERFACE 322
+#define VAR_ROOT_HINTS 323
+#define VAR_DO_NOT_QUERY_LOCALHOST 324
+#define VAR_CACHE_MAX_TTL 325
+#define VAR_HARDEN_DNSSEC_STRIPPED 326
+#define VAR_ACCESS_CONTROL 327
+#define VAR_LOCAL_ZONE 328
+#define VAR_LOCAL_DATA 329
+#define VAR_INTERFACE_AUTOMATIC 330
+#define VAR_STATISTICS_INTERVAL 331
+#define VAR_DO_DAEMONIZE 332
+#define VAR_USE_CAPS_FOR_ID 333
+#define VAR_STATISTICS_CUMULATIVE 334
+#define VAR_OUTGOING_PORT_PERMIT 335
+#define VAR_OUTGOING_PORT_AVOID 336
+#define VAR_DLV_ANCHOR_FILE 337
+#define VAR_DLV_ANCHOR 338
+#define VAR_NEG_CACHE_SIZE 339
+#define VAR_HARDEN_REFERRAL_PATH 340
+#define VAR_PRIVATE_ADDRESS 341
+#define VAR_PRIVATE_DOMAIN 342
+#define VAR_REMOTE_CONTROL 343
+#define VAR_CONTROL_ENABLE 344
+#define VAR_CONTROL_INTERFACE 345
+#define VAR_CONTROL_PORT 346
+#define VAR_SERVER_KEY_FILE 347
+#define VAR_SERVER_CERT_FILE 348
+#define VAR_CONTROL_KEY_FILE 349
+#define VAR_CONTROL_CERT_FILE 350
+#define VAR_EXTENDED_STATISTICS 351
+#define VAR_LOCAL_DATA_PTR 352
+#define VAR_JOSTLE_TIMEOUT 353
+#define VAR_STUB_PRIME 354
+#define VAR_UNWANTED_REPLY_THRESHOLD 355
+#define VAR_LOG_TIME_ASCII 356
+#define VAR_DOMAIN_INSECURE 357
+#define VAR_PYTHON 358
+#define VAR_PYTHON_SCRIPT 359
+#define VAR_VAL_SIG_SKEW_MIN 360
+#define VAR_VAL_SIG_SKEW_MAX 361
+#define VAR_CACHE_MIN_TTL 362
+#define VAR_VAL_LOG_LEVEL 363
+#define VAR_AUTO_TRUST_ANCHOR_FILE 364
+#define VAR_KEEP_MISSING 365
+#define VAR_ADD_HOLDDOWN 366
+#define VAR_DEL_HOLDDOWN 367
+#define VAR_SO_RCVBUF 368
+#define VAR_EDNS_BUFFER_SIZE 369
+#define VAR_PREFETCH 370
+#define VAR_PREFETCH_KEY 371
+#define VAR_SO_SNDBUF 372
+#define VAR_HARDEN_BELOW_NXDOMAIN 373
+#define VAR_IGNORE_CD_FLAG 374
+#define VAR_LOG_QUERIES 375
+#define VAR_TCP_UPSTREAM 376
+#define VAR_SSL_UPSTREAM 377
+#define VAR_SSL_SERVICE_KEY 378
+#define VAR_SSL_SERVICE_PEM 379
+#define VAR_SSL_PORT 380
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+
+/* Line 293 of yacc.c  */
+#line 64 "util/configparser.y"
+
+	char*	str;
+
+
+
+/* Line 293 of yacc.c  */
+#line 391 "util/configparser.c"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+/* Copy the second part of user declarations.  */
+
+
+/* Line 343 of yacc.c  */
+#line 403 "util/configparser.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+#  define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+#  define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# else
+#  define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+#  if ENABLE_NLS
+#   include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+#   define YY_(msgid) dgettext ("bison-runtime", msgid)
+#  endif
+# endif
+# ifndef YY_
+#  define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E.  */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions.  */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int yyi)
+#else
+static int
+YYID (yyi)
+    int yyi;
+#endif
+{
+  return yyi;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   elif defined __BUILTIN_VA_ARG_INCR
+#    include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+#   elif defined _AIX
+#    define YYSTACK_ALLOC __alloca
+#   elif defined _MSC_VER
+#    include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+#    define alloca _alloca
+#   else
+#    define YYSTACK_ALLOC alloca
+#    if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+#     include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#     ifndef EXIT_SUCCESS
+#      define EXIT_SUCCESS 0
+#     endif
+#    endif
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's `empty if-body' warning.  */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+    /* The OS might guarantee only one guard page at the bottom of the stack,
+       and a page size can be as small as 4096 bytes.  So we cannot safely
+       invoke alloca (N) if N exceeds 4096.  Use a slightly smaller number
+       to allow for a few compiler-allocated temporary stack slots.  */
+#   define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+#  endif
+# else
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+#   define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+#  endif
+#  if (defined __cplusplus && ! defined EXIT_SUCCESS \
+       && ! ((defined YYMALLOC || defined malloc) \
+	     && (defined YYFREE || defined free)))
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   ifndef EXIT_SUCCESS
+#    define EXIT_SUCCESS 0
+#   endif
+#  endif
+#  ifndef YYMALLOC
+#   define YYMALLOC malloc
+#   if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+#  ifndef YYFREE
+#   define YYFREE free
+#   if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+     && (! defined __cplusplus \
+	 || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  yytype_int16 yyss_alloc;
+  YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+      + YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack)				\
+    do									\
+      {									\
+	YYSIZE_T yynewbytes;						\
+	YYCOPY (&yyptr->Stack_alloc, Stack, yysize);			\
+	Stack = &yyptr->Stack_alloc;					\
+	yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+	yyptr += yynewbytes / sizeof (*yyptr);				\
+      }									\
+    while (YYID (0))
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from FROM to TO.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined __GNUC__ && 1 < __GNUC__
+#   define YYCOPY(To, From, Count) \
+      __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+#  else
+#   define YYCOPY(To, From, Count)		\
+      do					\
+	{					\
+	  YYSIZE_T yyi;				\
+	  for (yyi = 0; yyi < (Count); yyi++)	\
+	    (To)[yyi] = (From)[yyi];		\
+	}					\
+      while (YYID (0))
+#  endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state.  */
+#define YYFINAL  2
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   229
+
+/* YYNTOKENS -- Number of terminals.  */
+#define YYNTOKENS  126
+/* YYNNTS -- Number of nonterminals.  */
+#define YYNNTS  129
+/* YYNRULES -- Number of rules.  */
+#define YYNRULES  245
+/* YYNRULES -- Number of states.  */
+#define YYNSTATES  359
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
+#define YYUNDEFTOK  2
+#define YYMAXUTOK   380
+
+#define YYTRANSLATE(YYX)						\
+  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX.  */
+static const yytype_uint8 yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
+      15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
+      25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
+      35,    36,    37,    38,    39,    40,    41,    42,    43,    44,
+      45,    46,    47,    48,    49,    50,    51,    52,    53,    54,
+      55,    56,    57,    58,    59,    60,    61,    62,    63,    64,
+      65,    66,    67,    68,    69,    70,    71,    72,    73,    74,
+      75,    76,    77,    78,    79,    80,    81,    82,    83,    84,
+      85,    86,    87,    88,    89,    90,    91,    92,    93,    94,
+      95,    96,    97,    98,    99,   100,   101,   102,   103,   104,
+     105,   106,   107,   108,   109,   110,   111,   112,   113,   114,
+     115,   116,   117,   118,   119,   120,   121,   122,   123,   124,
+     125
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+   YYRHS.  */
+static const yytype_uint16 yyprhs[] =
+{
+       0,     0,     3,     4,     7,    10,    13,    16,    19,    22,
+      24,    27,    28,    30,    32,    34,    36,    38,    40,    42,
+      44,    46,    48,    50,    52,    54,    56,    58,    60,    62,
+      64,    66,    68,    70,    72,    74,    76,    78,    80,    82,
+      84,    86,    88,    90,    92,    94,    96,    98,   100,   102,
+     104,   106,   108,   110,   112,   114,   116,   118,   120,   122,
+     124,   126,   128,   130,   132,   134,   136,   138,   140,   142,
+     144,   146,   148,   150,   152,   154,   156,   158,   160,   162,
+     164,   166,   168,   170,   172,   174,   176,   178,   180,   182,
+     184,   186,   188,   190,   192,   194,   196,   198,   200,   202,
+     204,   206,   208,   210,   212,   214,   216,   218,   220,   222,
+     225,   226,   228,   230,   232,   234,   236,   239,   240,   242,
+     244,   246,   249,   252,   255,   258,   261,   264,   267,   270,
+     273,   276,   279,   282,   285,   288,   291,   294,   297,   300,
+     303,   306,   309,   312,   315,   318,   321,   324,   327,   330,
+     333,   336,   339,   342,   345,   348,   351,   354,   357,   360,
+     363,   366,   369,   372,   375,   378,   381,   384,   387,   390,
+     393,   396,   399,   402,   405,   408,   411,   414,   417,   420,
+     423,   426,   429,   432,   435,   438,   441,   444,   447,   450,
+     453,   456,   459,   462,   465,   468,   472,   475,   478,   481,
+     484,   487,   490,   493,   496,   499,   502,   505,   508,   511,
+     514,   517,   520,   523,   526,   530,   533,   536,   539,   542,
+     545,   548,   551,   554,   557,   559,   562,   563,   565,   567,
+     569,   571,   573,   575,   577,   580,   583,   586,   589,   592,
+     595,   598,   600,   603,   604,   606
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS.  */
+static const yytype_int16 yyrhs[] =
+{
+     127,     0,    -1,    -1,   127,   128,    -1,   129,   130,    -1,
+     132,   133,    -1,   135,   136,    -1,   251,   252,    -1,   241,
+     242,    -1,    11,    -1,   130,   131,    -1,    -1,   138,    -1,
+     139,    -1,   143,    -1,   146,    -1,   152,    -1,   153,    -1,
+     154,    -1,   155,    -1,   144,    -1,   165,    -1,   166,    -1,
+     167,    -1,   168,    -1,   169,    -1,   186,    -1,   187,    -1,
+     188,    -1,   190,    -1,   191,    -1,   149,    -1,   192,    -1,
+     193,    -1,   196,    -1,   194,    -1,   195,    -1,   197,    -1,
+     198,    -1,   199,    -1,   210,    -1,   178,    -1,   179,    -1,
+     180,    -1,   181,    -1,   200,    -1,   213,    -1,   174,    -1,
+     176,    -1,   214,    -1,   219,    -1,   220,    -1,   221,    -1,
+     150,    -1,   185,    -1,   228,    -1,   229,    -1,   175,    -1,
+     224,    -1,   162,    -1,   145,    -1,   170,    -1,   211,    -1,
+     217,    -1,   201,    -1,   212,    -1,   231,    -1,   232,    -1,
+     151,    -1,   140,    -1,   161,    -1,   204,    -1,   141,    -1,
+     147,    -1,   148,    -1,   171,    -1,   172,    -1,   230,    -1,
+     203,    -1,   205,    -1,   206,    -1,   142,    -1,   233,    -1,
+     189,    -1,   209,    -1,   163,    -1,   177,    -1,   215,    -1,
+     216,    -1,   218,    -1,   223,    -1,   173,    -1,   225,    -1,
+     226,    -1,   227,    -1,   182,    -1,   184,    -1,   207,    -1,
+     208,    -1,   183,    -1,   202,    -1,   222,    -1,   164,    -1,
+     156,    -1,   157,    -1,   158,    -1,   159,    -1,   160,    -1,
+      38,    -1,   133,   134,    -1,    -1,   234,    -1,   235,    -1,
+     236,    -1,   237,    -1,    44,    -1,   136,   137,    -1,    -1,
+     238,    -1,   239,    -1,   240,    -1,    13,    10,    -1,    12,
+      10,    -1,    76,    10,    -1,    79,    10,    -1,    96,    10,
+      -1,    14,    10,    -1,    16,    10,    -1,    67,    10,    -1,
+      15,    10,    -1,    80,    10,    -1,    81,    10,    -1,    31,
+      10,    -1,    60,    10,    -1,    75,    10,    -1,    17,    10,
+      -1,    18,    10,    -1,    19,    10,    -1,    20,    10,    -1,
+     121,    10,    -1,   122,    10,    -1,   123,    10,    -1,   124,
+      10,    -1,   125,    10,    -1,    77,    10,    -1,    66,    10,
+      -1,   101,    10,    -1,   120,    10,    -1,    21,    10,    -1,
+      22,    10,    -1,    23,    10,    -1,    24,    10,    -1,    25,
+      10,    -1,    68,    10,    -1,    82,    10,    -1,    83,    10,
+      -1,   109,    10,    -1,    54,    10,    -1,    64,    10,    -1,
+      55,    10,    -1,   102,    10,    -1,    48,    10,    -1,    49,
+      10,    -1,    50,    10,    -1,    51,    10,    -1,   113,    10,
+      -1,   117,    10,    -1,   114,    10,    -1,    61,    10,    -1,
+      26,    10,    -1,    27,    10,    -1,    28,    10,    -1,    98,
+      10,    -1,    29,    10,    -1,    30,    10,    -1,    32,    10,
+      -1,    33,    10,    -1,    35,    10,    -1,    36,    10,    -1,
+      34,    10,    -1,    41,    10,    -1,    42,    10,    -1,    43,
+      10,    -1,    52,    10,    -1,    71,    10,    -1,   118,    10,
+      -1,    85,    10,    -1,    78,    10,    -1,    86,    10,    -1,
+      87,    10,    -1,   115,    10,    -1,   116,    10,    -1,   100,
+      10,    -1,    47,    10,    -1,    69,    10,    -1,    72,    10,
+      10,    -1,    53,    10,    -1,    56,    10,    -1,   105,    10,
+      -1,   106,    10,    -1,    70,    10,    -1,   107,    10,    -1,
+      57,    10,    -1,    58,    10,    -1,    59,    10,    -1,   119,
+      10,    -1,   108,    10,    -1,    65,    10,    -1,   111,    10,
+      -1,   112,    10,    -1,   110,    10,    -1,    62,    10,    -1,
+      63,    10,    -1,    84,    10,    -1,    73,    10,    10,    -1,
+      74,    10,    -1,    97,    10,    -1,    37,    10,    -1,    39,
+      10,    -1,    40,    10,    -1,    99,    10,    -1,    37,    10,
+      -1,    45,    10,    -1,    46,    10,    -1,    88,    -1,   242,
+     243,    -1,    -1,   244,    -1,   246,    -1,   245,    -1,   247,
+      -1,   248,    -1,   249,    -1,   250,    -1,    89,    10,    -1,
+      91,    10,    -1,    90,    10,    -1,    92,    10,    -1,    93,
+      10,    -1,    94,    10,    -1,    95,    10,    -1,   103,    -1,
+     252,   253,    -1,    -1,   254,    -1,   104,    10,    -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
+static const yytype_uint16 yyrline[] =
+{
+       0,   109,   109,   109,   110,   110,   111,   111,   112,   116,
+     121,   122,   123,   123,   123,   124,   124,   125,   125,   125,
+     126,   126,   126,   127,   127,   127,   128,   128,   129,   129,
+     130,   130,   131,   131,   132,   132,   133,   133,   134,   134,
+     135,   135,   136,   136,   136,   137,   137,   137,   138,   138,
+     138,   139,   139,   140,   140,   141,   141,   142,   142,   143,
+     143,   143,   144,   144,   145,   145,   146,   146,   146,   147,
+     147,   148,   148,   149,   149,   150,   150,   150,   151,   151,
+     152,   152,   153,   153,   154,   154,   155,   155,   156,   156,
+     156,   157,   157,   158,   158,   158,   159,   159,   159,   160,
+     160,   160,   161,   161,   161,   162,   162,   162,   164,   176,
+     177,   178,   178,   178,   178,   180,   192,   193,   194,   194,
+     194,   196,   205,   214,   225,   234,   243,   252,   265,   280,
+     289,   298,   307,   316,   325,   334,   343,   352,   361,   370,
+     379,   388,   395,   402,   411,   420,   434,   443,   452,   459,
+     466,   473,   481,   488,   495,   502,   509,   517,   525,   533,
+     540,   547,   556,   565,   572,   579,   587,   595,   608,   619,
+     627,   640,   649,   658,   666,   679,   688,   696,   705,   713,
+     726,   733,   743,   753,   763,   773,   783,   793,   803,   810,
+     817,   826,   835,   844,   851,   861,   875,   882,   900,   913,
+     926,   935,   944,   953,   963,   973,   982,   991,   998,  1007,
+    1016,  1025,  1033,  1046,  1054,  1076,  1083,  1098,  1108,  1115,
+    1122,  1132,  1142,  1149,  1156,  1161,  1162,  1163,  1163,  1163,
+    1164,  1164,  1164,  1165,  1167,  1177,  1186,  1193,  1200,  1207,
+    1214,  1221,  1226,  1227,  1228,  1230
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
+static const char *const yytname[] =
+{
+  "$end", "error", "$undefined", "SPACE", "LETTER", "NEWLINE", "COMMENT",
+  "COLON", "ANY", "ZONESTR", "STRING_ARG", "VAR_SERVER", "VAR_VERBOSITY",
+  "VAR_NUM_THREADS", "VAR_PORT", "VAR_OUTGOING_RANGE", "VAR_INTERFACE",
+  "VAR_DO_IP4", "VAR_DO_IP6", "VAR_DO_UDP", "VAR_DO_TCP", "VAR_CHROOT",
+  "VAR_USERNAME", "VAR_DIRECTORY", "VAR_LOGFILE", "VAR_PIDFILE",
+  "VAR_MSG_CACHE_SIZE", "VAR_MSG_CACHE_SLABS",
+  "VAR_NUM_QUERIES_PER_THREAD", "VAR_RRSET_CACHE_SIZE",
+  "VAR_RRSET_CACHE_SLABS", "VAR_OUTGOING_NUM_TCP", "VAR_INFRA_HOST_TTL",
+  "VAR_INFRA_LAME_TTL", "VAR_INFRA_CACHE_SLABS",
+  "VAR_INFRA_CACHE_NUMHOSTS", "VAR_INFRA_CACHE_LAME_SIZE", "VAR_NAME",
+  "VAR_STUB_ZONE", "VAR_STUB_HOST", "VAR_STUB_ADDR",
+  "VAR_TARGET_FETCH_POLICY", "VAR_HARDEN_SHORT_BUFSIZE",
+  "VAR_HARDEN_LARGE_QUERIES", "VAR_FORWARD_ZONE", "VAR_FORWARD_HOST",
+  "VAR_FORWARD_ADDR", "VAR_DO_NOT_QUERY_ADDRESS", "VAR_HIDE_IDENTITY",
+  "VAR_HIDE_VERSION", "VAR_IDENTITY", "VAR_VERSION", "VAR_HARDEN_GLUE",
+  "VAR_MODULE_CONF", "VAR_TRUST_ANCHOR_FILE", "VAR_TRUST_ANCHOR",
+  "VAR_VAL_OVERRIDE_DATE", "VAR_BOGUS_TTL", "VAR_VAL_CLEAN_ADDITIONAL",
+  "VAR_VAL_PERMISSIVE_MODE", "VAR_INCOMING_NUM_TCP", "VAR_MSG_BUFFER_SIZE",
+  "VAR_KEY_CACHE_SIZE", "VAR_KEY_CACHE_SLABS", "VAR_TRUSTED_KEYS_FILE",
+  "VAR_VAL_NSEC3_KEYSIZE_ITERATIONS", "VAR_USE_SYSLOG",
+  "VAR_OUTGOING_INTERFACE", "VAR_ROOT_HINTS", "VAR_DO_NOT_QUERY_LOCALHOST",
+  "VAR_CACHE_MAX_TTL", "VAR_HARDEN_DNSSEC_STRIPPED", "VAR_ACCESS_CONTROL",
+  "VAR_LOCAL_ZONE", "VAR_LOCAL_DATA", "VAR_INTERFACE_AUTOMATIC",
+  "VAR_STATISTICS_INTERVAL", "VAR_DO_DAEMONIZE", "VAR_USE_CAPS_FOR_ID",
+  "VAR_STATISTICS_CUMULATIVE", "VAR_OUTGOING_PORT_PERMIT",
+  "VAR_OUTGOING_PORT_AVOID", "VAR_DLV_ANCHOR_FILE", "VAR_DLV_ANCHOR",
+  "VAR_NEG_CACHE_SIZE", "VAR_HARDEN_REFERRAL_PATH", "VAR_PRIVATE_ADDRESS",
+  "VAR_PRIVATE_DOMAIN", "VAR_REMOTE_CONTROL", "VAR_CONTROL_ENABLE",
+  "VAR_CONTROL_INTERFACE", "VAR_CONTROL_PORT", "VAR_SERVER_KEY_FILE",
+  "VAR_SERVER_CERT_FILE", "VAR_CONTROL_KEY_FILE", "VAR_CONTROL_CERT_FILE",
+  "VAR_EXTENDED_STATISTICS", "VAR_LOCAL_DATA_PTR", "VAR_JOSTLE_TIMEOUT",
+  "VAR_STUB_PRIME", "VAR_UNWANTED_REPLY_THRESHOLD", "VAR_LOG_TIME_ASCII",
+  "VAR_DOMAIN_INSECURE", "VAR_PYTHON", "VAR_PYTHON_SCRIPT",
+  "VAR_VAL_SIG_SKEW_MIN", "VAR_VAL_SIG_SKEW_MAX", "VAR_CACHE_MIN_TTL",
+  "VAR_VAL_LOG_LEVEL", "VAR_AUTO_TRUST_ANCHOR_FILE", "VAR_KEEP_MISSING",
+  "VAR_ADD_HOLDDOWN", "VAR_DEL_HOLDDOWN", "VAR_SO_RCVBUF",
+  "VAR_EDNS_BUFFER_SIZE", "VAR_PREFETCH", "VAR_PREFETCH_KEY",
+  "VAR_SO_SNDBUF", "VAR_HARDEN_BELOW_NXDOMAIN", "VAR_IGNORE_CD_FLAG",
+  "VAR_LOG_QUERIES", "VAR_TCP_UPSTREAM", "VAR_SSL_UPSTREAM",
+  "VAR_SSL_SERVICE_KEY", "VAR_SSL_SERVICE_PEM", "VAR_SSL_PORT", "$accept",
+  "toplevelvars", "toplevelvar", "serverstart", "contents_server",
+  "content_server", "stubstart", "contents_stub", "content_stub",
+  "forwardstart", "contents_forward", "content_forward",
+  "server_num_threads", "server_verbosity", "server_statistics_interval",
+  "server_statistics_cumulative", "server_extended_statistics",
+  "server_port", "server_interface", "server_outgoing_interface",
+  "server_outgoing_range", "server_outgoing_port_permit",
+  "server_outgoing_port_avoid", "server_outgoing_num_tcp",
+  "server_incoming_num_tcp", "server_interface_automatic", "server_do_ip4",
+  "server_do_ip6", "server_do_udp", "server_do_tcp", "server_tcp_upstream",
+  "server_ssl_upstream", "server_ssl_service_key",
+  "server_ssl_service_pem", "server_ssl_port", "server_do_daemonize",
+  "server_use_syslog", "server_log_time_ascii", "server_log_queries",
+  "server_chroot", "server_username", "server_directory", "server_logfile",
+  "server_pidfile", "server_root_hints", "server_dlv_anchor_file",
+  "server_dlv_anchor", "server_auto_trust_anchor_file",
+  "server_trust_anchor_file", "server_trusted_keys_file",
+  "server_trust_anchor", "server_domain_insecure", "server_hide_identity",
+  "server_hide_version", "server_identity", "server_version",
+  "server_so_rcvbuf", "server_so_sndbuf", "server_edns_buffer_size",
+  "server_msg_buffer_size", "server_msg_cache_size",
+  "server_msg_cache_slabs", "server_num_queries_per_thread",
+  "server_jostle_timeout", "server_rrset_cache_size",
+  "server_rrset_cache_slabs", "server_infra_host_ttl",
+  "server_infra_lame_ttl", "server_infra_cache_numhosts",
+  "server_infra_cache_lame_size", "server_infra_cache_slabs",
+  "server_target_fetch_policy", "server_harden_short_bufsize",
+  "server_harden_large_queries", "server_harden_glue",
+  "server_harden_dnssec_stripped", "server_harden_below_nxdomain",
+  "server_harden_referral_path", "server_use_caps_for_id",
+  "server_private_address", "server_private_domain", "server_prefetch",
+  "server_prefetch_key", "server_unwanted_reply_threshold",
+  "server_do_not_query_address", "server_do_not_query_localhost",
+  "server_access_control", "server_module_conf",
+  "server_val_override_date", "server_val_sig_skew_min",
+  "server_val_sig_skew_max", "server_cache_max_ttl",
+  "server_cache_min_ttl", "server_bogus_ttl",
+  "server_val_clean_additional", "server_val_permissive_mode",
+  "server_ignore_cd_flag", "server_val_log_level",
+  "server_val_nsec3_keysize_iterations", "server_add_holddown",
+  "server_del_holddown", "server_keep_missing", "server_key_cache_size",
+  "server_key_cache_slabs", "server_neg_cache_size", "server_local_zone",
+  "server_local_data", "server_local_data_ptr", "stub_name", "stub_host",
+  "stub_addr", "stub_prime", "forward_name", "forward_host",
+  "forward_addr", "rcstart", "contents_rc", "content_rc",
+  "rc_control_enable", "rc_control_port", "rc_control_interface",
+  "rc_server_key_file", "rc_server_cert_file", "rc_control_key_file",
+  "rc_control_cert_file", "pythonstart", "contents_py", "content_py",
+  "py_script", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+   token YYLEX-NUM.  */
+static const yytype_uint16 yytoknum[] =
+{
+       0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
+     265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
+     275,   276,   277,   278,   279,   280,   281,   282,   283,   284,
+     285,   286,   287,   288,   289,   290,   291,   292,   293,   294,
+     295,   296,   297,   298,   299,   300,   301,   302,   303,   304,
+     305,   306,   307,   308,   309,   310,   311,   312,   313,   314,
+     315,   316,   317,   318,   319,   320,   321,   322,   323,   324,
+     325,   326,   327,   328,   329,   330,   331,   332,   333,   334,
+     335,   336,   337,   338,   339,   340,   341,   342,   343,   344,
+     345,   346,   347,   348,   349,   350,   351,   352,   353,   354,
+     355,   356,   357,   358,   359,   360,   361,   362,   363,   364,
+     365,   366,   367,   368,   369,   370,   371,   372,   373,   374,
+     375,   376,   377,   378,   379,   380
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const yytype_uint8 yyr1[] =
+{
+       0,   126,   127,   127,   128,   128,   128,   128,   128,   129,
+     130,   130,   131,   131,   131,   131,   131,   131,   131,   131,
+     131,   131,   131,   131,   131,   131,   131,   131,   131,   131,
+     131,   131,   131,   131,   131,   131,   131,   131,   131,   131,
+     131,   131,   131,   131,   131,   131,   131,   131,   131,   131,
+     131,   131,   131,   131,   131,   131,   131,   131,   131,   131,
+     131,   131,   131,   131,   131,   131,   131,   131,   131,   131,
+     131,   131,   131,   131,   131,   131,   131,   131,   131,   131,
+     131,   131,   131,   131,   131,   131,   131,   131,   131,   131,
+     131,   131,   131,   131,   131,   131,   131,   131,   131,   131,
+     131,   131,   131,   131,   131,   131,   131,   131,   132,   133,
+     133,   134,   134,   134,   134,   135,   136,   136,   137,   137,
+     137,   138,   139,   140,   141,   142,   143,   144,   145,   146,
+     147,   148,   149,   150,   151,   152,   153,   154,   155,   156,
+     157,   158,   159,   160,   161,   162,   163,   164,   165,   166,
+     167,   168,   169,   170,   171,   172,   173,   174,   175,   176,
+     177,   178,   179,   180,   181,   182,   183,   184,   185,   186,
+     187,   188,   189,   190,   191,   192,   193,   194,   195,   196,
+     197,   198,   199,   200,   201,   202,   203,   204,   205,   206,
+     207,   208,   209,   210,   211,   212,   213,   214,   215,   216,
+     217,   218,   219,   220,   221,   222,   223,   224,   225,   226,
+     227,   228,   229,   230,   231,   232,   233,   234,   235,   236,
+     237,   238,   239,   240,   241,   242,   242,   243,   243,   243,
+     243,   243,   243,   243,   244,   245,   246,   247,   248,   249,
+     250,   251,   252,   252,   253,   254
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
+static const yytype_uint8 yyr2[] =
+{
+       0,     2,     0,     2,     2,     2,     2,     2,     2,     1,
+       2,     0,     1,     1,     1,     1,     1,     1,     1,     1,
+       1,     1,     1,     1,     1,     1,     1,     1,     1,     1,
+       1,     1,     1,     1,     1,     1,     1,     1,     1,     1,
+       1,     1,     1,     1,     1,     1,     1,     1,     1,     1,
+       1,     1,     1,     1,     1,     1,     1,     1,     1,     1,
+       1,     1,     1,     1,     1,     1,     1,     1,     1,     1,
+       1,     1,     1,     1,     1,     1,     1,     1,     1,     1,
+       1,     1,     1,     1,     1,     1,     1,     1,     1,     1,
+       1,     1,     1,     1,     1,     1,     1,     1,     1,     1,
+       1,     1,     1,     1,     1,     1,     1,     1,     1,     2,
+       0,     1,     1,     1,     1,     1,     2,     0,     1,     1,
+       1,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     3,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     3,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     1,     2,     0,     1,     1,     1,
+       1,     1,     1,     1,     2,     2,     2,     2,     2,     2,
+       2,     1,     2,     0,     1,     2
+};
+
+/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM.
+   Performed when YYTABLE doesn't specify something else to do.  Zero
+   means the default is an error.  */
+static const yytype_uint8 yydefact[] =
+{
+       2,     0,     1,     9,   108,   115,   224,   241,     3,    11,
+     110,   117,   226,   243,     4,     5,     6,     8,     7,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,    10,    12,    13,    69,    72,
+      81,    14,    20,    60,    15,    73,    74,    31,    53,    68,
+      16,    17,    18,    19,   103,   104,   105,   106,   107,    70,
+      59,    85,   102,    21,    22,    23,    24,    25,    61,    75,
+      76,    91,    47,    57,    48,    86,    41,    42,    43,    44,
+      95,    99,    96,    54,    26,    27,    28,    83,    29,    30,
+      32,    33,    35,    36,    34,    37,    38,    39,    45,    64,
+     100,    78,    71,    79,    80,    97,    98,    84,    40,    62,
+      65,    46,    49,    87,    88,    63,    89,    50,    51,    52,
+     101,    90,    58,    92,    93,    94,    55,    56,    77,    66,
+      67,    82,     0,     0,     0,     0,   109,   111,   112,   113,
+     114,     0,     0,     0,   116,   118,   119,   120,     0,     0,
+       0,     0,     0,     0,     0,   225,   227,   229,   228,   230,
+     231,   232,   233,     0,   242,   244,   122,   121,   126,   129,
+     127,   135,   136,   137,   138,   148,   149,   150,   151,   152,
+     169,   170,   171,   173,   174,   132,   175,   176,   179,   177,
+     178,   180,   181,   182,   193,   161,   162,   163,   164,   183,
+     196,   157,   159,   197,   202,   203,   204,   133,   168,   211,
+     212,   158,   207,   145,   128,   153,   194,   200,   184,     0,
+       0,   215,   134,   123,   144,   187,   124,   130,   131,   154,
+     155,   213,   186,   188,   189,   125,   216,   172,   192,   146,
+     160,   198,   199,   201,   206,   156,   210,   208,   209,   165,
+     167,   190,   191,   166,   185,   205,   147,   139,   140,   141,
+     142,   143,   217,   218,   219,   220,   221,   222,   223,   234,
+     236,   235,   237,   238,   239,   240,   245,   195,   214
+};
+
+/* YYDEFGOTO[NTERM-NUM].  */
+static const yytype_int16 yydefgoto[] =
+{
+      -1,     1,     8,     9,    14,   115,    10,    15,   216,    11,
+      16,   224,   116,   117,   118,   119,   120,   121,   122,   123,
+     124,   125,   126,   127,   128,   129,   130,   131,   132,   133,
+     134,   135,   136,   137,   138,   139,   140,   141,   142,   143,
+     144,   145,   146,   147,   148,   149,   150,   151,   152,   153,
+     154,   155,   156,   157,   158,   159,   160,   161,   162,   163,
+     164,   165,   166,   167,   168,   169,   170,   171,   172,   173,
+     174,   175,   176,   177,   178,   179,   180,   181,   182,   183,
+     184,   185,   186,   187,   188,   189,   190,   191,   192,   193,
+     194,   195,   196,   197,   198,   199,   200,   201,   202,   203,
+     204,   205,   206,   207,   208,   209,   210,   211,   217,   218,
+     219,   220,   225,   226,   227,    12,    17,   235,   236,   237,
+     238,   239,   240,   241,   242,    13,    18,   244,   245
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+   STATE-NUM.  */
+#define YYPACT_NINF -80
+static const yytype_int16 yypact[] =
+{
+     -80,    76,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -12,    40,    46,    32,   -79,    16,
+      17,    18,    22,    23,    24,    68,    71,    72,   105,   106,
+     107,   108,   109,   118,   119,   120,   121,   122,   123,   124,
+     125,   126,   127,   128,   130,   131,   132,   133,   134,   135,
+     136,   137,   138,   139,   140,   141,   142,   143,   144,   145,
+     146,   147,   148,   149,   150,   151,   152,   153,   155,   156,
+     157,   158,   159,   160,   161,   162,   163,   164,   165,   166,
+     167,   168,   170,   171,   172,   173,   174,   175,   176,   177,
+     178,   179,   180,   181,   182,   183,   184,   185,   186,   187,
+     188,   189,   190,   191,   192,   193,   194,   195,   196,   197,
+     198,   199,   200,   201,   202,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   203,   204,   205,   206,   -80,   -80,   -80,   -80,
+     -80,   207,   208,   209,   -80,   -80,   -80,   -80,   210,   211,
+     212,   213,   214,   215,   216,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   217,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   218,
+     219,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80
+};
+
+/* YYPGOTO[NTERM-NUM].  */
+static const yytype_int8 yypgoto[] =
+{
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]].  What to do in state STATE-NUM.  If
+   positive, shift that token.  If negative, reduce the rule which
+   number is the opposite.  If YYTABLE_NINF, syntax error.  */
+#define YYTABLE_NINF -1
+static const yytype_uint16 yytable[] =
+{
+      19,    20,    21,    22,    23,    24,    25,    26,    27,    28,
+      29,    30,    31,    32,    33,    34,    35,    36,    37,    38,
+      39,    40,    41,    42,    43,   243,   246,   247,   248,    44,
+      45,    46,   249,   250,   251,    47,    48,    49,    50,    51,
+      52,    53,    54,    55,    56,    57,    58,    59,    60,    61,
+      62,    63,    64,    65,    66,    67,    68,    69,    70,    71,
+      72,    73,    74,    75,    76,    77,    78,    79,    80,    81,
+      82,    83,    84,    85,    86,    87,     2,   212,   252,   213,
+     214,   253,   254,   221,    88,    89,    90,     3,    91,    92,
+      93,   222,   223,    94,    95,    96,    97,    98,    99,   100,
+     101,   102,   103,   104,   105,   106,   107,   108,   109,   110,
+     111,   112,   113,   114,     4,   255,   256,   257,   258,   259,
+       5,   228,   229,   230,   231,   232,   233,   234,   260,   261,
+     262,   263,   264,   265,   266,   267,   268,   269,   270,   215,
+     271,   272,   273,   274,   275,   276,   277,   278,   279,   280,
+     281,   282,   283,   284,   285,   286,   287,   288,   289,   290,
+     291,   292,   293,   294,     6,   295,   296,   297,   298,   299,
+     300,   301,   302,   303,   304,   305,   306,   307,   308,     7,
+     309,   310,   311,   312,   313,   314,   315,   316,   317,   318,
+     319,   320,   321,   322,   323,   324,   325,   326,   327,   328,
+     329,   330,   331,   332,   333,   334,   335,   336,   337,   338,
+     339,   340,   341,   342,   343,   344,   345,   346,   347,   348,
+     349,   350,   351,   352,   353,   354,   355,   356,   357,   358
+};
+
+#define yypact_value_is_default(yystate) \
+  ((yystate) == (-80))
+
+#define yytable_value_is_error(yytable_value) \
+  YYID (0)
+
+static const yytype_uint8 yycheck[] =
+{
+      12,    13,    14,    15,    16,    17,    18,    19,    20,    21,
+      22,    23,    24,    25,    26,    27,    28,    29,    30,    31,
+      32,    33,    34,    35,    36,   104,    10,    10,    10,    41,
+      42,    43,    10,    10,    10,    47,    48,    49,    50,    51,
+      52,    53,    54,    55,    56,    57,    58,    59,    60,    61,
+      62,    63,    64,    65,    66,    67,    68,    69,    70,    71,
+      72,    73,    74,    75,    76,    77,    78,    79,    80,    81,
+      82,    83,    84,    85,    86,    87,     0,    37,    10,    39,
+      40,    10,    10,    37,    96,    97,    98,    11,   100,   101,
+     102,    45,    46,   105,   106,   107,   108,   109,   110,   111,
+     112,   113,   114,   115,   116,   117,   118,   119,   120,   121,
+     122,   123,   124,   125,    38,    10,    10,    10,    10,    10,
+      44,    89,    90,    91,    92,    93,    94,    95,    10,    10,
+      10,    10,    10,    10,    10,    10,    10,    10,    10,    99,
+      10,    10,    10,    10,    10,    10,    10,    10,    10,    10,
+      10,    10,    10,    10,    10,    10,    10,    10,    10,    10,
+      10,    10,    10,    10,    88,    10,    10,    10,    10,    10,
+      10,    10,    10,    10,    10,    10,    10,    10,    10,   103,
+      10,    10,    10,    10,    10,    10,    10,    10,    10,    10,
+      10,    10,    10,    10,    10,    10,    10,    10,    10,    10,
+      10,    10,    10,    10,    10,    10,    10,    10,    10,    10,
+      10,    10,    10,    10,    10,    10,    10,    10,    10,    10,
+      10,    10,    10,    10,    10,    10,    10,    10,    10,    10
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+   symbol of state STATE-NUM.  */
+static const yytype_uint8 yystos[] =
+{
+       0,   127,     0,    11,    38,    44,    88,   103,   128,   129,
+     132,   135,   241,   251,   130,   133,   136,   242,   252,    12,
+      13,    14,    15,    16,    17,    18,    19,    20,    21,    22,
+      23,    24,    25,    26,    27,    28,    29,    30,    31,    32,
+      33,    34,    35,    36,    41,    42,    43,    47,    48,    49,
+      50,    51,    52,    53,    54,    55,    56,    57,    58,    59,
+      60,    61,    62,    63,    64,    65,    66,    67,    68,    69,
+      70,    71,    72,    73,    74,    75,    76,    77,    78,    79,
+      80,    81,    82,    83,    84,    85,    86,    87,    96,    97,
+      98,   100,   101,   102,   105,   106,   107,   108,   109,   110,
+     111,   112,   113,   114,   115,   116,   117,   118,   119,   120,
+     121,   122,   123,   124,   125,   131,   138,   139,   140,   141,
+     142,   143,   144,   145,   146,   147,   148,   149,   150,   151,
+     152,   153,   154,   155,   156,   157,   158,   159,   160,   161,
+     162,   163,   164,   165,   166,   167,   168,   169,   170,   171,
+     172,   173,   174,   175,   176,   177,   178,   179,   180,   181,
+     182,   183,   184,   185,   186,   187,   188,   189,   190,   191,
+     192,   193,   194,   195,   196,   197,   198,   199,   200,   201,
+     202,   203,   204,   205,   206,   207,   208,   209,   210,   211,
+     212,   213,   214,   215,   216,   217,   218,   219,   220,   221,
+     222,   223,   224,   225,   226,   227,   228,   229,   230,   231,
+     232,   233,    37,    39,    40,    99,   134,   234,   235,   236,
+     237,    37,    45,    46,   137,   238,   239,   240,    89,    90,
+      91,    92,    93,    94,    95,   243,   244,   245,   246,   247,
+     248,   249,   250,   104,   253,   254,    10,    10,    10,    10,
+      10,    10,    10,    10,    10,    10,    10,    10,    10,    10,
+      10,    10,    10,    10,    10,    10,    10,    10,    10,    10,
+      10,    10,    10,    10,    10,    10,    10,    10,    10,    10,
+      10,    10,    10,    10,    10,    10,    10,    10,    10,    10,
+      10,    10,    10,    10,    10,    10,    10,    10,    10,    10,
+      10,    10,    10,    10,    10,    10,    10,    10,    10,    10,
+      10,    10,    10,    10,    10,    10,    10,    10,    10,    10,
+      10,    10,    10,    10,    10,    10,    10,    10,    10,    10,
+      10,    10,    10,    10,    10,    10,    10,    10,    10,    10,
+      10,    10,    10,    10,    10,    10,    10,    10,    10,    10,
+      10,    10,    10,    10,    10,    10,    10,    10,    10
+};
+
+#define yyerrok		(yyerrstatus = 0)
+#define yyclearin	(yychar = YYEMPTY)
+#define YYEMPTY		(-2)
+#define YYEOF		0
+
+#define YYACCEPT	goto yyacceptlab
+#define YYABORT		goto yyabortlab
+#define YYERROR		goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror.  This remains here temporarily
+   to ease the transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  However,
+   YYFAIL appears to be in use.  Nevertheless, it is formally deprecated
+   in Bison 2.4.2's NEWS entry, where a plan to phase it out is
+   discussed.  */
+
+#define YYFAIL		goto yyerrlab
+#if defined YYFAIL
+  /* This is here to suppress warnings from the GCC cpp's
+     -Wunused-macros.  Normally we don't worry about that warning, but
+     some users do, and we want to make it easy for users to remove
+     YYFAIL uses, which will produce warnings from Bison 2.5.  */
+#endif
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)					\
+do								\
+  if (yychar == YYEMPTY && yylen == 1)				\
+    {								\
+      yychar = (Token);						\
+      yylval = (Value);						\
+      YYPOPSTACK (1);						\
+      goto yybackup;						\
+    }								\
+  else								\
+    {								\
+      yyerror (YY_("syntax error: cannot back up")); \
+      YYERROR;							\
+    }								\
+while (YYID (0))
+
+
+#define YYTERROR	1
+#define YYERRCODE	256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+   If N is 0, then set CURRENT to the empty location which ends
+   the previous symbol: RHS[0] (always defined).  */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N)				\
+    do									\
+      if (YYID (N))                                                    \
+	{								\
+	  (Current).first_line   = YYRHSLOC (Rhs, 1).first_line;	\
+	  (Current).first_column = YYRHSLOC (Rhs, 1).first_column;	\
+	  (Current).last_line    = YYRHSLOC (Rhs, N).last_line;		\
+	  (Current).last_column  = YYRHSLOC (Rhs, N).last_column;	\
+	}								\
+      else								\
+	{								\
+	  (Current).first_line   = (Current).last_line   =		\
+	    YYRHSLOC (Rhs, 0).last_line;				\
+	  (Current).first_column = (Current).last_column =		\
+	    YYRHSLOC (Rhs, 0).last_column;				\
+	}								\
+    while (YYID (0))
+#endif
+
+
+/* This macro is provided for backward compatibility. */
+
+#ifndef YY_LOCATION_PRINT
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments.  */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)			\
+do {						\
+  if (yydebug)					\
+    YYFPRINTF Args;				\
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)			  \
+do {									  \
+  if (yydebug)								  \
+    {									  \
+      YYFPRINTF (stderr, "%s ", Title);					  \
+      yy_symbol_print (stderr,						  \
+		  Type, Value); \
+      YYFPRINTF (stderr, "\n");						  \
+    }									  \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+#endif
+{
+  if (!yyvaluep)
+    return;
+# ifdef YYPRINT
+  if (yytype < YYNTOKENS)
+    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+  YYUSE (yyoutput);
+# endif
+  switch (yytype)
+    {
+      default:
+	break;
+    }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+#endif
+{
+  if (yytype < YYNTOKENS)
+    YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+  else
+    YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+  yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+  YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+#else
+static void
+yy_stack_print (yybottom, yytop)
+    yytype_int16 *yybottom;
+    yytype_int16 *yytop;
+#endif
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (; yybottom <= yytop; yybottom++)
+    {
+      int yybot = *yybottom;
+      YYFPRINTF (stderr, " %d", yybot);
+    }
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)				\
+do {								\
+  if (yydebug)							\
+    yy_stack_print ((Bottom), (Top));				\
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+    YYSTYPE *yyvsp;
+    int yyrule;
+#endif
+{
+  int yynrhs = yyr2[yyrule];
+  int yyi;
+  unsigned long int yylno = yyrline[yyrule];
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+	     yyrule - 1, yylno);
+  /* The symbols being reduced.  */
+  for (yyi = 0; yyi < yynrhs; yyi++)
+    {
+      YYFPRINTF (stderr, "   $%d = ", yyi + 1);
+      yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+		       &(yyvsp[(yyi + 1) - (yynrhs)])
+		       		       );
+      YYFPRINTF (stderr, "\n");
+    }
+}
+
+# define YY_REDUCE_PRINT(Rule)		\
+do {					\
+  if (yydebug)				\
+    yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef	YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined __GLIBC__ && defined _STRING_H
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+    const char *yystr;
+#endif
+{
+  YYSIZE_T yylen;
+  for (yylen = 0; yystr[yylen]; yylen++)
+    continue;
+  return yylen;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+    char *yydest;
+    const char *yysrc;
+#endif
+{
+  char *yyd = yydest;
+  const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+   quotes and backslashes, so that it's suitable for yyerror.  The
+   heuristic is that double-quoting is unnecessary unless the string
+   contains an apostrophe, a comma, or backslash (other than
+   backslash-backslash).  YYSTR is taken from yytname.  If YYRES is
+   null, do not copy; instead, return the length of what the result
+   would have been.  */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+  if (*yystr == '"')
+    {
+      YYSIZE_T yyn = 0;
+      char const *yyp = yystr;
+
+      for (;;)
+	switch (*++yyp)
+	  {
+	  case '\'':
+	  case ',':
+	    goto do_not_strip_quotes;
+
+	  case '\\':
+	    if (*++yyp != '\\')
+	      goto do_not_strip_quotes;
+	    /* Fall through.  */
+	  default:
+	    if (yyres)
+	      yyres[yyn] = *yyp;
+	    yyn++;
+	    break;
+
+	  case '"':
+	    if (yyres)
+	      yyres[yyn] = '\0';
+	    return yyn;
+	  }
+    do_not_strip_quotes: ;
+    }
+
+  if (! yyres)
+    return yystrlen (yystr);
+
+  return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+   about the unexpected token YYTOKEN for the state stack whose top is
+   YYSSP.
+
+   Return 0 if *YYMSG was successfully written.  Return 1 if *YYMSG is
+   not large enough to hold the message.  In that case, also set
+   *YYMSG_ALLOC to the required number of bytes.  Return 2 if the
+   required number of bytes is too large to store.  */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+                yytype_int16 *yyssp, int yytoken)
+{
+  YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]);
+  YYSIZE_T yysize = yysize0;
+  YYSIZE_T yysize1;
+  enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+  /* Internationalized format string. */
+  const char *yyformat = 0;
+  /* Arguments of yyformat. */
+  char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+  /* Number of reported tokens (one for the "unexpected", one per
+     "expected"). */
+  int yycount = 0;
+
+  /* There are many possibilities here to consider:
+     - Assume YYFAIL is not used.  It's too flawed to consider.  See
+       <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html>
+       for details.  YYERROR is fine as it does not invoke this
+       function.
+     - If this state is a consistent state with a default action, then
+       the only way this function was invoked is if the default action
+       is an error action.  In that case, don't check for expected
+       tokens because there are none.
+     - The only way there can be no lookahead present (in yychar) is if
+       this state is a consistent state with a default action.  Thus,
+       detecting the absence of a lookahead is sufficient to determine
+       that there is no unexpected or expected token to report.  In that
+       case, just report a simple "syntax error".
+     - Don't assume there isn't a lookahead just because this state is a
+       consistent state with a default action.  There might have been a
+       previous inconsistent state, consistent state with a non-default
+       action, or user semantic action that manipulated yychar.
+     - Of course, the expected token list depends on states to have
+       correct lookahead information, and it depends on the parser not
+       to perform extra reductions after fetching a lookahead from the
+       scanner and before detecting a syntax error.  Thus, state merging
+       (from LALR or IELR) and default reductions corrupt the expected
+       token list.  However, the list is correct for canonical LR with
+       one exception: it will still contain any token that will not be
+       accepted due to an error action in a later state.
+  */
+  if (yytoken != YYEMPTY)
+    {
+      int yyn = yypact[*yyssp];
+      yyarg[yycount++] = yytname[yytoken];
+      if (!yypact_value_is_default (yyn))
+        {
+          /* Start YYX at -YYN if negative to avoid negative indexes in
+             YYCHECK.  In other words, skip the first -YYN actions for
+             this state because they are default actions.  */
+          int yyxbegin = yyn < 0 ? -yyn : 0;
+          /* Stay within bounds of both yycheck and yytname.  */
+          int yychecklim = YYLAST - yyn + 1;
+          int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+          int yyx;
+
+          for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+            if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+                && !yytable_value_is_error (yytable[yyx + yyn]))
+              {
+                if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+                  {
+                    yycount = 1;
+                    yysize = yysize0;
+                    break;
+                  }
+                yyarg[yycount++] = yytname[yyx];
+                yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+                if (! (yysize <= yysize1
+                       && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+                  return 2;
+                yysize = yysize1;
+              }
+        }
+    }
+
+  switch (yycount)
+    {
+# define YYCASE_(N, S)                      \
+      case N:                               \
+        yyformat = S;                       \
+      break
+      YYCASE_(0, YY_("syntax error"));
+      YYCASE_(1, YY_("syntax error, unexpected %s"));
+      YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+      YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+      YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+      YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+    }
+
+  yysize1 = yysize + yystrlen (yyformat);
+  if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+    return 2;
+  yysize = yysize1;
+
+  if (*yymsg_alloc < yysize)
+    {
+      *yymsg_alloc = 2 * yysize;
+      if (! (yysize <= *yymsg_alloc
+             && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+        *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+      return 1;
+    }
+
+  /* Avoid sprintf, as that infringes on the user's name space.
+     Don't have undefined behavior even if the translation
+     produced a string with the wrong number of "%s"s.  */
+  {
+    char *yyp = *yymsg;
+    int yyi = 0;
+    while ((*yyp = *yyformat) != '\0')
+      if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+        {
+          yyp += yytnamerr (yyp, yyarg[yyi++]);
+          yyformat += 2;
+        }
+      else
+        {
+          yyp++;
+          yyformat++;
+        }
+  }
+  return 0;
+}
+#endif /* YYERROR_VERBOSE */
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+    const char *yymsg;
+    int yytype;
+    YYSTYPE *yyvaluep;
+#endif
+{
+  YYUSE (yyvaluep);
+
+  if (!yymsg)
+    yymsg = "Deleting";
+  YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+  switch (yytype)
+    {
+
+      default:
+	break;
+    }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes.  */
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+/* The lookahead symbol.  */
+int yychar;
+
+/* The semantic value of the lookahead symbol.  */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far.  */
+int yynerrs;
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+    void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+    int yystate;
+    /* Number of tokens to shift before error messages enabled.  */
+    int yyerrstatus;
+
+    /* The stacks and their tools:
+       `yyss': related to states.
+       `yyvs': related to semantic values.
+
+       Refer to the stacks thru separate pointers, to allow yyoverflow
+       to reallocate them elsewhere.  */
+
+    /* The state stack.  */
+    yytype_int16 yyssa[YYINITDEPTH];
+    yytype_int16 *yyss;
+    yytype_int16 *yyssp;
+
+    /* The semantic value stack.  */
+    YYSTYPE yyvsa[YYINITDEPTH];
+    YYSTYPE *yyvs;
+    YYSTYPE *yyvsp;
+
+    YYSIZE_T yystacksize;
+
+  int yyn;
+  int yyresult;
+  /* Lookahead token as an internal (translated) token number.  */
+  int yytoken;
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+
+#if YYERROR_VERBOSE
+  /* Buffer for error messages, and its allocated size.  */
+  char yymsgbuf[128];
+  char *yymsg = yymsgbuf;
+  YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N))
+
+  /* The number of symbols on the RHS of the reduced rule.
+     Keep to zero when no symbol should be popped.  */
+  int yylen = 0;
+
+  yytoken = 0;
+  yyss = yyssa;
+  yyvs = yyvsa;
+  yystacksize = YYINITDEPTH;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY; /* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+  yyssp = yyss;
+  yyvsp = yyvs;
+
+  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed.  So pushing a state here evens the stacks.  */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyss + yystacksize - 1 <= yyssp)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+	/* Give user a chance to reallocate the stack.  Use copies of
+	   these so that the &'s don't force the real ones into
+	   memory.  */
+	YYSTYPE *yyvs1 = yyvs;
+	yytype_int16 *yyss1 = yyss;
+
+	/* Each stack pointer address is followed by the size of the
+	   data in use in that stack, in bytes.  This used to be a
+	   conditional around just the two extra args, but that might
+	   be undefined if yyoverflow is a macro.  */
+	yyoverflow (YY_("memory exhausted"),
+		    &yyss1, yysize * sizeof (*yyssp),
+		    &yyvs1, yysize * sizeof (*yyvsp),
+		    &yystacksize);
+
+	yyss = yyss1;
+	yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyexhaustedlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+	goto yyexhaustedlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+	yystacksize = YYMAXDEPTH;
+
+      {
+	yytype_int16 *yyss1 = yyss;
+	union yyalloc *yyptr =
+	  (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+	if (! yyptr)
+	  goto yyexhaustedlab;
+	YYSTACK_RELOCATE (yyss_alloc, yyss);
+	YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+#  undef YYSTACK_RELOCATE
+	if (yyss1 != yyssa)
+	  YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+		  (unsigned long int) yystacksize));
+
+      if (yyss + yystacksize - 1 <= yyssp)
+	YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  if (yystate == YYFINAL)
+    YYACCEPT;
+
+  goto yybackup;
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+  /* Do appropriate processing given the current state.  Read a
+     lookahead token if we need one and don't already have one.  */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+  yyn = yypact[yystate];
+  if (yypact_value_is_default (yyn))
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = YYLEX;
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = yytoken = YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yytable_value_is_error (yyn))
+        goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  /* Shift the lookahead token.  */
+  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+  /* Discard the shifted token.  */
+  yychar = YYEMPTY;
+
+  yystate = yyn;
+  *++yyvsp = yylval;
+
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     `$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+        case 9:
+
+/* Line 1806 of yacc.c  */
+#line 117 "util/configparser.y"
+    { 
+		OUTYY(("\nP(server:)\n")); 
+	}
+    break;
+
+  case 108:
+
+/* Line 1806 of yacc.c  */
+#line 165 "util/configparser.y"
+    {
+		struct config_stub* s;
+		OUTYY(("\nP(stub_zone:)\n")); 
+		s = (struct config_stub*)calloc(1, sizeof(struct config_stub));
+		if(s) {
+			s->next = cfg_parser->cfg->stubs;
+			cfg_parser->cfg->stubs = s;
+		} else 
+			yyerror("out of memory");
+	}
+    break;
+
+  case 115:
+
+/* Line 1806 of yacc.c  */
+#line 181 "util/configparser.y"
+    {
+		struct config_stub* s;
+		OUTYY(("\nP(forward_zone:)\n")); 
+		s = (struct config_stub*)calloc(1, sizeof(struct config_stub));
+		if(s) {
+			s->next = cfg_parser->cfg->forwards;
+			cfg_parser->cfg->forwards = s;
+		} else 
+			yyerror("out of memory");
+	}
+    break;
+
+  case 121:
+
+/* Line 1806 of yacc.c  */
+#line 197 "util/configparser.y"
+    { 
+		OUTYY(("P(server_num_threads:%s)\n", (yyvsp[(2) - (2)].str))); 
+		if(atoi((yyvsp[(2) - (2)].str)) == 0 && strcmp((yyvsp[(2) - (2)].str), "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->num_threads = atoi((yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 122:
+
+/* Line 1806 of yacc.c  */
+#line 206 "util/configparser.y"
+    { 
+		OUTYY(("P(server_verbosity:%s)\n", (yyvsp[(2) - (2)].str))); 
+		if(atoi((yyvsp[(2) - (2)].str)) == 0 && strcmp((yyvsp[(2) - (2)].str), "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->verbosity = atoi((yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 123:
+
+/* Line 1806 of yacc.c  */
+#line 215 "util/configparser.y"
+    { 
+		OUTYY(("P(server_statistics_interval:%s)\n", (yyvsp[(2) - (2)].str))); 
+		if(strcmp((yyvsp[(2) - (2)].str), "") == 0 || strcmp((yyvsp[(2) - (2)].str), "0") == 0)
+			cfg_parser->cfg->stat_interval = 0;
+		else if(atoi((yyvsp[(2) - (2)].str)) == 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->stat_interval = atoi((yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 124:
+
+/* Line 1806 of yacc.c  */
+#line 226 "util/configparser.y"
+    {
+		OUTYY(("P(server_statistics_cumulative:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->stat_cumulative = (strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 125:
+
+/* Line 1806 of yacc.c  */
+#line 235 "util/configparser.y"
+    {
+		OUTYY(("P(server_extended_statistics:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->stat_extended = (strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 126:
+
+/* Line 1806 of yacc.c  */
+#line 244 "util/configparser.y"
+    {
+		OUTYY(("P(server_port:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(atoi((yyvsp[(2) - (2)].str)) == 0)
+			yyerror("port number expected");
+		else cfg_parser->cfg->port = atoi((yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 127:
+
+/* Line 1806 of yacc.c  */
+#line 253 "util/configparser.y"
+    {
+		OUTYY(("P(server_interface:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(cfg_parser->cfg->num_ifs == 0)
+			cfg_parser->cfg->ifs = calloc(1, sizeof(char*));
+		else 	cfg_parser->cfg->ifs = realloc(cfg_parser->cfg->ifs,
+				(cfg_parser->cfg->num_ifs+1)*sizeof(char*));
+		if(!cfg_parser->cfg->ifs)
+			yyerror("out of memory");
+		else
+			cfg_parser->cfg->ifs[cfg_parser->cfg->num_ifs++] = (yyvsp[(2) - (2)].str);
+	}
+    break;
+
+  case 128:
+
+/* Line 1806 of yacc.c  */
+#line 266 "util/configparser.y"
+    {
+		OUTYY(("P(server_outgoing_interface:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(cfg_parser->cfg->num_out_ifs == 0)
+			cfg_parser->cfg->out_ifs = calloc(1, sizeof(char*));
+		else 	cfg_parser->cfg->out_ifs = realloc(
+			cfg_parser->cfg->out_ifs, 
+			(cfg_parser->cfg->num_out_ifs+1)*sizeof(char*));
+		if(!cfg_parser->cfg->out_ifs)
+			yyerror("out of memory");
+		else
+			cfg_parser->cfg->out_ifs[
+				cfg_parser->cfg->num_out_ifs++] = (yyvsp[(2) - (2)].str);
+	}
+    break;
+
+  case 129:
+
+/* Line 1806 of yacc.c  */
+#line 281 "util/configparser.y"
+    {
+		OUTYY(("P(server_outgoing_range:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(atoi((yyvsp[(2) - (2)].str)) == 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->outgoing_num_ports = atoi((yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 130:
+
+/* Line 1806 of yacc.c  */
+#line 290 "util/configparser.y"
+    {
+		OUTYY(("P(server_outgoing_port_permit:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(!cfg_mark_ports((yyvsp[(2) - (2)].str), 1, 
+			cfg_parser->cfg->outgoing_avail_ports, 65536))
+			yyerror("port number or range (\"low-high\") expected");
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 131:
+
+/* Line 1806 of yacc.c  */
+#line 299 "util/configparser.y"
+    {
+		OUTYY(("P(server_outgoing_port_avoid:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(!cfg_mark_ports((yyvsp[(2) - (2)].str), 0, 
+			cfg_parser->cfg->outgoing_avail_ports, 65536))
+			yyerror("port number or range (\"low-high\") expected");
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 132:
+
+/* Line 1806 of yacc.c  */
+#line 308 "util/configparser.y"
+    {
+		OUTYY(("P(server_outgoing_num_tcp:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(atoi((yyvsp[(2) - (2)].str)) == 0 && strcmp((yyvsp[(2) - (2)].str), "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->outgoing_num_tcp = atoi((yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 133:
+
+/* Line 1806 of yacc.c  */
+#line 317 "util/configparser.y"
+    {
+		OUTYY(("P(server_incoming_num_tcp:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(atoi((yyvsp[(2) - (2)].str)) == 0 && strcmp((yyvsp[(2) - (2)].str), "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->incoming_num_tcp = atoi((yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 134:
+
+/* Line 1806 of yacc.c  */
+#line 326 "util/configparser.y"
+    {
+		OUTYY(("P(server_interface_automatic:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->if_automatic = (strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 135:
+
+/* Line 1806 of yacc.c  */
+#line 335 "util/configparser.y"
+    {
+		OUTYY(("P(server_do_ip4:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->do_ip4 = (strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 136:
+
+/* Line 1806 of yacc.c  */
+#line 344 "util/configparser.y"
+    {
+		OUTYY(("P(server_do_ip6:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->do_ip6 = (strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 137:
+
+/* Line 1806 of yacc.c  */
+#line 353 "util/configparser.y"
+    {
+		OUTYY(("P(server_do_udp:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->do_udp = (strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 138:
+
+/* Line 1806 of yacc.c  */
+#line 362 "util/configparser.y"
+    {
+		OUTYY(("P(server_do_tcp:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->do_tcp = (strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 139:
+
+/* Line 1806 of yacc.c  */
+#line 371 "util/configparser.y"
+    {
+		OUTYY(("P(server_tcp_upstream:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->tcp_upstream = (strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 140:
+
+/* Line 1806 of yacc.c  */
+#line 380 "util/configparser.y"
+    {
+		OUTYY(("P(server_ssl_upstream:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->ssl_upstream = (strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 141:
+
+/* Line 1806 of yacc.c  */
+#line 389 "util/configparser.y"
+    {
+		OUTYY(("P(server_ssl_service_key:%s)\n", (yyvsp[(2) - (2)].str)));
+		free(cfg_parser->cfg->ssl_service_key);
+		cfg_parser->cfg->ssl_service_key = (yyvsp[(2) - (2)].str);
+	}
+    break;
+
+  case 142:
+
+/* Line 1806 of yacc.c  */
+#line 396 "util/configparser.y"
+    {
+		OUTYY(("P(server_ssl_service_pem:%s)\n", (yyvsp[(2) - (2)].str)));
+		free(cfg_parser->cfg->ssl_service_pem);
+		cfg_parser->cfg->ssl_service_pem = (yyvsp[(2) - (2)].str);
+	}
+    break;
+
+  case 143:
+
+/* Line 1806 of yacc.c  */
+#line 403 "util/configparser.y"
+    {
+		OUTYY(("P(server_ssl_port:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(atoi((yyvsp[(2) - (2)].str)) == 0)
+			yyerror("port number expected");
+		else cfg_parser->cfg->ssl_port = atoi((yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 144:
+
+/* Line 1806 of yacc.c  */
+#line 412 "util/configparser.y"
+    {
+		OUTYY(("P(server_do_daemonize:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->do_daemonize = (strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 145:
+
+/* Line 1806 of yacc.c  */
+#line 421 "util/configparser.y"
+    {
+		OUTYY(("P(server_use_syslog:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->use_syslog = (strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+#if !defined(HAVE_SYSLOG_H) && !defined(UB_ON_WINDOWS)
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") == 0)
+			yyerror("no syslog services are available. "
+				"(reconfigure and compile to add)");
+#endif
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 146:
+
+/* Line 1806 of yacc.c  */
+#line 435 "util/configparser.y"
+    {
+		OUTYY(("P(server_log_time_ascii:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->log_time_ascii = (strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 147:
+
+/* Line 1806 of yacc.c  */
+#line 444 "util/configparser.y"
+    {
+		OUTYY(("P(server_log_queries:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->log_queries = (strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 148:
+
+/* Line 1806 of yacc.c  */
+#line 453 "util/configparser.y"
+    {
+		OUTYY(("P(server_chroot:%s)\n", (yyvsp[(2) - (2)].str)));
+		free(cfg_parser->cfg->chrootdir);
+		cfg_parser->cfg->chrootdir = (yyvsp[(2) - (2)].str);
+	}
+    break;
+
+  case 149:
+
+/* Line 1806 of yacc.c  */
+#line 460 "util/configparser.y"
+    {
+		OUTYY(("P(server_username:%s)\n", (yyvsp[(2) - (2)].str)));
+		free(cfg_parser->cfg->username);
+		cfg_parser->cfg->username = (yyvsp[(2) - (2)].str);
+	}
+    break;
+
+  case 150:
+
+/* Line 1806 of yacc.c  */
+#line 467 "util/configparser.y"
+    {
+		OUTYY(("P(server_directory:%s)\n", (yyvsp[(2) - (2)].str)));
+		free(cfg_parser->cfg->directory);
+		cfg_parser->cfg->directory = (yyvsp[(2) - (2)].str);
+	}
+    break;
+
+  case 151:
+
+/* Line 1806 of yacc.c  */
+#line 474 "util/configparser.y"
+    {
+		OUTYY(("P(server_logfile:%s)\n", (yyvsp[(2) - (2)].str)));
+		free(cfg_parser->cfg->logfile);
+		cfg_parser->cfg->logfile = (yyvsp[(2) - (2)].str);
+		cfg_parser->cfg->use_syslog = 0;
+	}
+    break;
+
+  case 152:
+
+/* Line 1806 of yacc.c  */
+#line 482 "util/configparser.y"
+    {
+		OUTYY(("P(server_pidfile:%s)\n", (yyvsp[(2) - (2)].str)));
+		free(cfg_parser->cfg->pidfile);
+		cfg_parser->cfg->pidfile = (yyvsp[(2) - (2)].str);
+	}
+    break;
+
+  case 153:
+
+/* Line 1806 of yacc.c  */
+#line 489 "util/configparser.y"
+    {
+		OUTYY(("P(server_root_hints:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->root_hints, (yyvsp[(2) - (2)].str)))
+			yyerror("out of memory");
+	}
+    break;
+
+  case 154:
+
+/* Line 1806 of yacc.c  */
+#line 496 "util/configparser.y"
+    {
+		OUTYY(("P(server_dlv_anchor_file:%s)\n", (yyvsp[(2) - (2)].str)));
+		free(cfg_parser->cfg->dlv_anchor_file);
+		cfg_parser->cfg->dlv_anchor_file = (yyvsp[(2) - (2)].str);
+	}
+    break;
+
+  case 155:
+
+/* Line 1806 of yacc.c  */
+#line 503 "util/configparser.y"
+    {
+		OUTYY(("P(server_dlv_anchor:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->dlv_anchor_list, (yyvsp[(2) - (2)].str)))
+			yyerror("out of memory");
+	}
+    break;
+
+  case 156:
+
+/* Line 1806 of yacc.c  */
+#line 510 "util/configparser.y"
+    {
+		OUTYY(("P(server_auto_trust_anchor_file:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->
+			auto_trust_anchor_file_list, (yyvsp[(2) - (2)].str)))
+			yyerror("out of memory");
+	}
+    break;
+
+  case 157:
+
+/* Line 1806 of yacc.c  */
+#line 518 "util/configparser.y"
+    {
+		OUTYY(("P(server_trust_anchor_file:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->
+			trust_anchor_file_list, (yyvsp[(2) - (2)].str)))
+			yyerror("out of memory");
+	}
+    break;
+
+  case 158:
+
+/* Line 1806 of yacc.c  */
+#line 526 "util/configparser.y"
+    {
+		OUTYY(("P(server_trusted_keys_file:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->
+			trusted_keys_file_list, (yyvsp[(2) - (2)].str)))
+			yyerror("out of memory");
+	}
+    break;
+
+  case 159:
+
+/* Line 1806 of yacc.c  */
+#line 534 "util/configparser.y"
+    {
+		OUTYY(("P(server_trust_anchor:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->trust_anchor_list, (yyvsp[(2) - (2)].str)))
+			yyerror("out of memory");
+	}
+    break;
+
+  case 160:
+
+/* Line 1806 of yacc.c  */
+#line 541 "util/configparser.y"
+    {
+		OUTYY(("P(server_domain_insecure:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->domain_insecure, (yyvsp[(2) - (2)].str)))
+			yyerror("out of memory");
+	}
+    break;
+
+  case 161:
+
+/* Line 1806 of yacc.c  */
+#line 548 "util/configparser.y"
+    {
+		OUTYY(("P(server_hide_identity:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->hide_identity = (strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 162:
+
+/* Line 1806 of yacc.c  */
+#line 557 "util/configparser.y"
+    {
+		OUTYY(("P(server_hide_version:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->hide_version = (strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 163:
+
+/* Line 1806 of yacc.c  */
+#line 566 "util/configparser.y"
+    {
+		OUTYY(("P(server_identity:%s)\n", (yyvsp[(2) - (2)].str)));
+		free(cfg_parser->cfg->identity);
+		cfg_parser->cfg->identity = (yyvsp[(2) - (2)].str);
+	}
+    break;
+
+  case 164:
+
+/* Line 1806 of yacc.c  */
+#line 573 "util/configparser.y"
+    {
+		OUTYY(("P(server_version:%s)\n", (yyvsp[(2) - (2)].str)));
+		free(cfg_parser->cfg->version);
+		cfg_parser->cfg->version = (yyvsp[(2) - (2)].str);
+	}
+    break;
+
+  case 165:
+
+/* Line 1806 of yacc.c  */
+#line 580 "util/configparser.y"
+    {
+		OUTYY(("P(server_so_rcvbuf:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(!cfg_parse_memsize((yyvsp[(2) - (2)].str), &cfg_parser->cfg->so_rcvbuf))
+			yyerror("buffer size expected");
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 166:
+
+/* Line 1806 of yacc.c  */
+#line 588 "util/configparser.y"
+    {
+		OUTYY(("P(server_so_sndbuf:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(!cfg_parse_memsize((yyvsp[(2) - (2)].str), &cfg_parser->cfg->so_sndbuf))
+			yyerror("buffer size expected");
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 167:
+
+/* Line 1806 of yacc.c  */
+#line 596 "util/configparser.y"
+    {
+		OUTYY(("P(server_edns_buffer_size:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(atoi((yyvsp[(2) - (2)].str)) == 0)
+			yyerror("number expected");
+		else if (atoi((yyvsp[(2) - (2)].str)) < 12)
+			yyerror("edns buffer size too small");
+		else if (atoi((yyvsp[(2) - (2)].str)) > 65535)
+			cfg_parser->cfg->edns_buffer_size = 65535;
+		else cfg_parser->cfg->edns_buffer_size = atoi((yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 168:
+
+/* Line 1806 of yacc.c  */
+#line 609 "util/configparser.y"
+    {
+		OUTYY(("P(server_msg_buffer_size:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(atoi((yyvsp[(2) - (2)].str)) == 0)
+			yyerror("number expected");
+		else if (atoi((yyvsp[(2) - (2)].str)) < 4096)
+			yyerror("message buffer size too small (use 4096)");
+		else cfg_parser->cfg->msg_buffer_size = atoi((yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 169:
+
+/* Line 1806 of yacc.c  */
+#line 620 "util/configparser.y"
+    {
+		OUTYY(("P(server_msg_cache_size:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(!cfg_parse_memsize((yyvsp[(2) - (2)].str), &cfg_parser->cfg->msg_cache_size))
+			yyerror("memory size expected");
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 170:
+
+/* Line 1806 of yacc.c  */
+#line 628 "util/configparser.y"
+    {
+		OUTYY(("P(server_msg_cache_slabs:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(atoi((yyvsp[(2) - (2)].str)) == 0)
+			yyerror("number expected");
+		else {
+			cfg_parser->cfg->msg_cache_slabs = atoi((yyvsp[(2) - (2)].str));
+			if(!is_pow2(cfg_parser->cfg->msg_cache_slabs))
+				yyerror("must be a power of 2");
+		}
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 171:
+
+/* Line 1806 of yacc.c  */
+#line 641 "util/configparser.y"
+    {
+		OUTYY(("P(server_num_queries_per_thread:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(atoi((yyvsp[(2) - (2)].str)) == 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->num_queries_per_thread = atoi((yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 172:
+
+/* Line 1806 of yacc.c  */
+#line 650 "util/configparser.y"
+    {
+		OUTYY(("P(server_jostle_timeout:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(atoi((yyvsp[(2) - (2)].str)) == 0 && strcmp((yyvsp[(2) - (2)].str), "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->jostle_time = atoi((yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 173:
+
+/* Line 1806 of yacc.c  */
+#line 659 "util/configparser.y"
+    {
+		OUTYY(("P(server_rrset_cache_size:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(!cfg_parse_memsize((yyvsp[(2) - (2)].str), &cfg_parser->cfg->rrset_cache_size))
+			yyerror("memory size expected");
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 174:
+
+/* Line 1806 of yacc.c  */
+#line 667 "util/configparser.y"
+    {
+		OUTYY(("P(server_rrset_cache_slabs:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(atoi((yyvsp[(2) - (2)].str)) == 0)
+			yyerror("number expected");
+		else {
+			cfg_parser->cfg->rrset_cache_slabs = atoi((yyvsp[(2) - (2)].str));
+			if(!is_pow2(cfg_parser->cfg->rrset_cache_slabs))
+				yyerror("must be a power of 2");
+		}
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 175:
+
+/* Line 1806 of yacc.c  */
+#line 680 "util/configparser.y"
+    {
+		OUTYY(("P(server_infra_host_ttl:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(atoi((yyvsp[(2) - (2)].str)) == 0 && strcmp((yyvsp[(2) - (2)].str), "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->host_ttl = atoi((yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 176:
+
+/* Line 1806 of yacc.c  */
+#line 689 "util/configparser.y"
+    {
+		OUTYY(("P(server_infra_lame_ttl:%s)\n", (yyvsp[(2) - (2)].str)));
+		verbose(VERB_DETAIL, "ignored infra-lame-ttl: %s (option "
+			"removed, use infra-host-ttl)", (yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 177:
+
+/* Line 1806 of yacc.c  */
+#line 697 "util/configparser.y"
+    {
+		OUTYY(("P(server_infra_cache_numhosts:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(atoi((yyvsp[(2) - (2)].str)) == 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->infra_cache_numhosts = atoi((yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 178:
+
+/* Line 1806 of yacc.c  */
+#line 706 "util/configparser.y"
+    {
+		OUTYY(("P(server_infra_cache_lame_size:%s)\n", (yyvsp[(2) - (2)].str)));
+		verbose(VERB_DETAIL, "ignored infra-cache-lame-size: %s "
+			"(option removed, use infra-cache-numhosts)", (yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 179:
+
+/* Line 1806 of yacc.c  */
+#line 714 "util/configparser.y"
+    {
+		OUTYY(("P(server_infra_cache_slabs:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(atoi((yyvsp[(2) - (2)].str)) == 0)
+			yyerror("number expected");
+		else {
+			cfg_parser->cfg->infra_cache_slabs = atoi((yyvsp[(2) - (2)].str));
+			if(!is_pow2(cfg_parser->cfg->infra_cache_slabs))
+				yyerror("must be a power of 2");
+		}
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 180:
+
+/* Line 1806 of yacc.c  */
+#line 727 "util/configparser.y"
+    {
+		OUTYY(("P(server_target_fetch_policy:%s)\n", (yyvsp[(2) - (2)].str)));
+		free(cfg_parser->cfg->target_fetch_policy);
+		cfg_parser->cfg->target_fetch_policy = (yyvsp[(2) - (2)].str);
+	}
+    break;
+
+  case 181:
+
+/* Line 1806 of yacc.c  */
+#line 734 "util/configparser.y"
+    {
+		OUTYY(("P(server_harden_short_bufsize:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->harden_short_bufsize = 
+			(strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 182:
+
+/* Line 1806 of yacc.c  */
+#line 744 "util/configparser.y"
+    {
+		OUTYY(("P(server_harden_large_queries:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->harden_large_queries = 
+			(strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 183:
+
+/* Line 1806 of yacc.c  */
+#line 754 "util/configparser.y"
+    {
+		OUTYY(("P(server_harden_glue:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->harden_glue = 
+			(strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 184:
+
+/* Line 1806 of yacc.c  */
+#line 764 "util/configparser.y"
+    {
+		OUTYY(("P(server_harden_dnssec_stripped:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->harden_dnssec_stripped = 
+			(strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 185:
+
+/* Line 1806 of yacc.c  */
+#line 774 "util/configparser.y"
+    {
+		OUTYY(("P(server_harden_below_nxdomain:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->harden_below_nxdomain = 
+			(strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 186:
+
+/* Line 1806 of yacc.c  */
+#line 784 "util/configparser.y"
+    {
+		OUTYY(("P(server_harden_referral_path:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->harden_referral_path = 
+			(strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 187:
+
+/* Line 1806 of yacc.c  */
+#line 794 "util/configparser.y"
+    {
+		OUTYY(("P(server_use_caps_for_id:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->use_caps_bits_for_id = 
+			(strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 188:
+
+/* Line 1806 of yacc.c  */
+#line 804 "util/configparser.y"
+    {
+		OUTYY(("P(server_private_address:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->private_address, (yyvsp[(2) - (2)].str)))
+			yyerror("out of memory");
+	}
+    break;
+
+  case 189:
+
+/* Line 1806 of yacc.c  */
+#line 811 "util/configparser.y"
+    {
+		OUTYY(("P(server_private_domain:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->private_domain, (yyvsp[(2) - (2)].str)))
+			yyerror("out of memory");
+	}
+    break;
+
+  case 190:
+
+/* Line 1806 of yacc.c  */
+#line 818 "util/configparser.y"
+    {
+		OUTYY(("P(server_prefetch:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->prefetch = (strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 191:
+
+/* Line 1806 of yacc.c  */
+#line 827 "util/configparser.y"
+    {
+		OUTYY(("P(server_prefetch_key:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->prefetch_key = (strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 192:
+
+/* Line 1806 of yacc.c  */
+#line 836 "util/configparser.y"
+    {
+		OUTYY(("P(server_unwanted_reply_threshold:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(atoi((yyvsp[(2) - (2)].str)) == 0 && strcmp((yyvsp[(2) - (2)].str), "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->unwanted_threshold = atoi((yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 193:
+
+/* Line 1806 of yacc.c  */
+#line 845 "util/configparser.y"
+    {
+		OUTYY(("P(server_do_not_query_address:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->donotqueryaddrs, (yyvsp[(2) - (2)].str)))
+			yyerror("out of memory");
+	}
+    break;
+
+  case 194:
+
+/* Line 1806 of yacc.c  */
+#line 852 "util/configparser.y"
+    {
+		OUTYY(("P(server_do_not_query_localhost:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->donotquery_localhost = 
+			(strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 195:
+
+/* Line 1806 of yacc.c  */
+#line 862 "util/configparser.y"
+    {
+		OUTYY(("P(server_access_control:%s %s)\n", (yyvsp[(2) - (3)].str), (yyvsp[(3) - (3)].str)));
+		if(strcmp((yyvsp[(3) - (3)].str), "deny")!=0 && strcmp((yyvsp[(3) - (3)].str), "refuse")!=0 &&
+			strcmp((yyvsp[(3) - (3)].str), "allow")!=0 && 
+			strcmp((yyvsp[(3) - (3)].str), "allow_snoop")!=0) {
+			yyerror("expected deny, refuse, allow or allow_snoop "
+				"in access control action");
+		} else {
+			if(!cfg_str2list_insert(&cfg_parser->cfg->acls, (yyvsp[(2) - (3)].str), (yyvsp[(3) - (3)].str)))
+				fatal_exit("out of memory adding acl");
+		}
+	}
+    break;
+
+  case 196:
+
+/* Line 1806 of yacc.c  */
+#line 876 "util/configparser.y"
+    {
+		OUTYY(("P(server_module_conf:%s)\n", (yyvsp[(2) - (2)].str)));
+		free(cfg_parser->cfg->module_conf);
+		cfg_parser->cfg->module_conf = (yyvsp[(2) - (2)].str);
+	}
+    break;
+
+  case 197:
+
+/* Line 1806 of yacc.c  */
+#line 883 "util/configparser.y"
+    {
+		OUTYY(("P(server_val_override_date:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strlen((yyvsp[(2) - (2)].str)) == 0 || strcmp((yyvsp[(2) - (2)].str), "0") == 0) {
+			cfg_parser->cfg->val_date_override = 0;
+		} else if(strlen((yyvsp[(2) - (2)].str)) == 14) {
+			cfg_parser->cfg->val_date_override = 
+				cfg_convert_timeval((yyvsp[(2) - (2)].str));
+			if(!cfg_parser->cfg->val_date_override)
+				yyerror("bad date/time specification");
+		} else {
+			if(atoi((yyvsp[(2) - (2)].str)) == 0)
+				yyerror("number expected");
+			cfg_parser->cfg->val_date_override = atoi((yyvsp[(2) - (2)].str));
+		}
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 198:
+
+/* Line 1806 of yacc.c  */
+#line 901 "util/configparser.y"
+    {
+		OUTYY(("P(server_val_sig_skew_min:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strlen((yyvsp[(2) - (2)].str)) == 0 || strcmp((yyvsp[(2) - (2)].str), "0") == 0) {
+			cfg_parser->cfg->val_sig_skew_min = 0;
+		} else {
+			cfg_parser->cfg->val_sig_skew_min = atoi((yyvsp[(2) - (2)].str));
+			if(!cfg_parser->cfg->val_sig_skew_min)
+				yyerror("number expected");
+		}
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 199:
+
+/* Line 1806 of yacc.c  */
+#line 914 "util/configparser.y"
+    {
+		OUTYY(("P(server_val_sig_skew_max:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strlen((yyvsp[(2) - (2)].str)) == 0 || strcmp((yyvsp[(2) - (2)].str), "0") == 0) {
+			cfg_parser->cfg->val_sig_skew_max = 0;
+		} else {
+			cfg_parser->cfg->val_sig_skew_max = atoi((yyvsp[(2) - (2)].str));
+			if(!cfg_parser->cfg->val_sig_skew_max)
+				yyerror("number expected");
+		}
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 200:
+
+/* Line 1806 of yacc.c  */
+#line 927 "util/configparser.y"
+    {
+		OUTYY(("P(server_cache_max_ttl:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(atoi((yyvsp[(2) - (2)].str)) == 0 && strcmp((yyvsp[(2) - (2)].str), "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->max_ttl = atoi((yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 201:
+
+/* Line 1806 of yacc.c  */
+#line 936 "util/configparser.y"
+    {
+		OUTYY(("P(server_cache_min_ttl:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(atoi((yyvsp[(2) - (2)].str)) == 0 && strcmp((yyvsp[(2) - (2)].str), "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->min_ttl = atoi((yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 202:
+
+/* Line 1806 of yacc.c  */
+#line 945 "util/configparser.y"
+    {
+		OUTYY(("P(server_bogus_ttl:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(atoi((yyvsp[(2) - (2)].str)) == 0 && strcmp((yyvsp[(2) - (2)].str), "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->bogus_ttl = atoi((yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 203:
+
+/* Line 1806 of yacc.c  */
+#line 954 "util/configparser.y"
+    {
+		OUTYY(("P(server_val_clean_additional:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->val_clean_additional = 
+			(strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 204:
+
+/* Line 1806 of yacc.c  */
+#line 964 "util/configparser.y"
+    {
+		OUTYY(("P(server_val_permissive_mode:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->val_permissive_mode = 
+			(strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 205:
+
+/* Line 1806 of yacc.c  */
+#line 974 "util/configparser.y"
+    {
+		OUTYY(("P(server_ignore_cd_flag:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->ignore_cd = (strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 206:
+
+/* Line 1806 of yacc.c  */
+#line 983 "util/configparser.y"
+    {
+		OUTYY(("P(server_val_log_level:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(atoi((yyvsp[(2) - (2)].str)) == 0 && strcmp((yyvsp[(2) - (2)].str), "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->val_log_level = atoi((yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 207:
+
+/* Line 1806 of yacc.c  */
+#line 992 "util/configparser.y"
+    {
+		OUTYY(("P(server_val_nsec3_keysize_iterations:%s)\n", (yyvsp[(2) - (2)].str)));
+		free(cfg_parser->cfg->val_nsec3_key_iterations);
+		cfg_parser->cfg->val_nsec3_key_iterations = (yyvsp[(2) - (2)].str);
+	}
+    break;
+
+  case 208:
+
+/* Line 1806 of yacc.c  */
+#line 999 "util/configparser.y"
+    {
+		OUTYY(("P(server_add_holddown:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(atoi((yyvsp[(2) - (2)].str)) == 0 && strcmp((yyvsp[(2) - (2)].str), "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->add_holddown = atoi((yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 209:
+
+/* Line 1806 of yacc.c  */
+#line 1008 "util/configparser.y"
+    {
+		OUTYY(("P(server_del_holddown:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(atoi((yyvsp[(2) - (2)].str)) == 0 && strcmp((yyvsp[(2) - (2)].str), "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->del_holddown = atoi((yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 210:
+
+/* Line 1806 of yacc.c  */
+#line 1017 "util/configparser.y"
+    {
+		OUTYY(("P(server_keep_missing:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(atoi((yyvsp[(2) - (2)].str)) == 0 && strcmp((yyvsp[(2) - (2)].str), "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->keep_missing = atoi((yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 211:
+
+/* Line 1806 of yacc.c  */
+#line 1026 "util/configparser.y"
+    {
+		OUTYY(("P(server_key_cache_size:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(!cfg_parse_memsize((yyvsp[(2) - (2)].str), &cfg_parser->cfg->key_cache_size))
+			yyerror("memory size expected");
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 212:
+
+/* Line 1806 of yacc.c  */
+#line 1034 "util/configparser.y"
+    {
+		OUTYY(("P(server_key_cache_slabs:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(atoi((yyvsp[(2) - (2)].str)) == 0)
+			yyerror("number expected");
+		else {
+			cfg_parser->cfg->key_cache_slabs = atoi((yyvsp[(2) - (2)].str));
+			if(!is_pow2(cfg_parser->cfg->key_cache_slabs))
+				yyerror("must be a power of 2");
+		}
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 213:
+
+/* Line 1806 of yacc.c  */
+#line 1047 "util/configparser.y"
+    {
+		OUTYY(("P(server_neg_cache_size:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(!cfg_parse_memsize((yyvsp[(2) - (2)].str), &cfg_parser->cfg->neg_cache_size))
+			yyerror("memory size expected");
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 214:
+
+/* Line 1806 of yacc.c  */
+#line 1055 "util/configparser.y"
+    {
+		OUTYY(("P(server_local_zone:%s %s)\n", (yyvsp[(2) - (3)].str), (yyvsp[(3) - (3)].str)));
+		if(strcmp((yyvsp[(3) - (3)].str), "static")!=0 && strcmp((yyvsp[(3) - (3)].str), "deny")!=0 &&
+		   strcmp((yyvsp[(3) - (3)].str), "refuse")!=0 && strcmp((yyvsp[(3) - (3)].str), "redirect")!=0 &&
+		   strcmp((yyvsp[(3) - (3)].str), "transparent")!=0 && strcmp((yyvsp[(3) - (3)].str), "nodefault")!=0
+		   && strcmp((yyvsp[(3) - (3)].str), "typetransparent")!=0)
+			yyerror("local-zone type: expected static, deny, "
+				"refuse, redirect, transparent, "
+				"typetransparent or nodefault");
+		else if(strcmp((yyvsp[(3) - (3)].str), "nodefault")==0) {
+			if(!cfg_strlist_insert(&cfg_parser->cfg->
+				local_zones_nodefault, (yyvsp[(2) - (3)].str)))
+				fatal_exit("out of memory adding local-zone");
+			free((yyvsp[(3) - (3)].str));
+		} else {
+			if(!cfg_str2list_insert(&cfg_parser->cfg->local_zones, 
+				(yyvsp[(2) - (3)].str), (yyvsp[(3) - (3)].str)))
+				fatal_exit("out of memory adding local-zone");
+		}
+	}
+    break;
+
+  case 215:
+
+/* Line 1806 of yacc.c  */
+#line 1077 "util/configparser.y"
+    {
+		OUTYY(("P(server_local_data:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->local_data, (yyvsp[(2) - (2)].str)))
+			fatal_exit("out of memory adding local-data");
+	}
+    break;
+
+  case 216:
+
+/* Line 1806 of yacc.c  */
+#line 1084 "util/configparser.y"
+    {
+		char* ptr;
+		OUTYY(("P(server_local_data_ptr:%s)\n", (yyvsp[(2) - (2)].str)));
+		ptr = cfg_ptr_reverse((yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+		if(ptr) {
+			if(!cfg_strlist_insert(&cfg_parser->cfg->
+				local_data, ptr))
+				fatal_exit("out of memory adding local-data");
+		} else {
+			yyerror("local-data-ptr could not be reversed");
+		}
+	}
+    break;
+
+  case 217:
+
+/* Line 1806 of yacc.c  */
+#line 1099 "util/configparser.y"
+    {
+		OUTYY(("P(name:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(cfg_parser->cfg->stubs->name)
+			yyerror("stub name override, there must be one name "
+				"for one stub-zone");
+		free(cfg_parser->cfg->stubs->name);
+		cfg_parser->cfg->stubs->name = (yyvsp[(2) - (2)].str);
+	}
+    break;
+
+  case 218:
+
+/* Line 1806 of yacc.c  */
+#line 1109 "util/configparser.y"
+    {
+		OUTYY(("P(stub-host:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->stubs->hosts, (yyvsp[(2) - (2)].str)))
+			yyerror("out of memory");
+	}
+    break;
+
+  case 219:
+
+/* Line 1806 of yacc.c  */
+#line 1116 "util/configparser.y"
+    {
+		OUTYY(("P(stub-addr:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->stubs->addrs, (yyvsp[(2) - (2)].str)))
+			yyerror("out of memory");
+	}
+    break;
+
+  case 220:
+
+/* Line 1806 of yacc.c  */
+#line 1123 "util/configparser.y"
+    {
+		OUTYY(("P(stub-prime:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->stubs->isprime = 
+			(strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 221:
+
+/* Line 1806 of yacc.c  */
+#line 1133 "util/configparser.y"
+    {
+		OUTYY(("P(name:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(cfg_parser->cfg->forwards->name)
+			yyerror("forward name override, there must be one "
+				"name for one forward-zone");
+		free(cfg_parser->cfg->forwards->name);
+		cfg_parser->cfg->forwards->name = (yyvsp[(2) - (2)].str);
+	}
+    break;
+
+  case 222:
+
+/* Line 1806 of yacc.c  */
+#line 1143 "util/configparser.y"
+    {
+		OUTYY(("P(forward-host:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->forwards->hosts, (yyvsp[(2) - (2)].str)))
+			yyerror("out of memory");
+	}
+    break;
+
+  case 223:
+
+/* Line 1806 of yacc.c  */
+#line 1150 "util/configparser.y"
+    {
+		OUTYY(("P(forward-addr:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->forwards->addrs, (yyvsp[(2) - (2)].str)))
+			yyerror("out of memory");
+	}
+    break;
+
+  case 224:
+
+/* Line 1806 of yacc.c  */
+#line 1157 "util/configparser.y"
+    { 
+		OUTYY(("\nP(remote-control:)\n")); 
+	}
+    break;
+
+  case 234:
+
+/* Line 1806 of yacc.c  */
+#line 1168 "util/configparser.y"
+    {
+		OUTYY(("P(control_enable:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(strcmp((yyvsp[(2) - (2)].str), "yes") != 0 && strcmp((yyvsp[(2) - (2)].str), "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->remote_control_enable = 
+			(strcmp((yyvsp[(2) - (2)].str), "yes")==0);
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 235:
+
+/* Line 1806 of yacc.c  */
+#line 1178 "util/configparser.y"
+    {
+		OUTYY(("P(control_port:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(atoi((yyvsp[(2) - (2)].str)) == 0)
+			yyerror("control port number expected");
+		else cfg_parser->cfg->control_port = atoi((yyvsp[(2) - (2)].str));
+		free((yyvsp[(2) - (2)].str));
+	}
+    break;
+
+  case 236:
+
+/* Line 1806 of yacc.c  */
+#line 1187 "util/configparser.y"
+    {
+		OUTYY(("P(control_interface:%s)\n", (yyvsp[(2) - (2)].str)));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->control_ifs, (yyvsp[(2) - (2)].str)))
+			yyerror("out of memory");
+	}
+    break;
+
+  case 237:
+
+/* Line 1806 of yacc.c  */
+#line 1194 "util/configparser.y"
+    {
+		OUTYY(("P(rc_server_key_file:%s)\n", (yyvsp[(2) - (2)].str)));
+		free(cfg_parser->cfg->server_key_file);
+		cfg_parser->cfg->server_key_file = (yyvsp[(2) - (2)].str);
+	}
+    break;
+
+  case 238:
+
+/* Line 1806 of yacc.c  */
+#line 1201 "util/configparser.y"
+    {
+		OUTYY(("P(rc_server_cert_file:%s)\n", (yyvsp[(2) - (2)].str)));
+		free(cfg_parser->cfg->server_cert_file);
+		cfg_parser->cfg->server_cert_file = (yyvsp[(2) - (2)].str);
+	}
+    break;
+
+  case 239:
+
+/* Line 1806 of yacc.c  */
+#line 1208 "util/configparser.y"
+    {
+		OUTYY(("P(rc_control_key_file:%s)\n", (yyvsp[(2) - (2)].str)));
+		free(cfg_parser->cfg->control_key_file);
+		cfg_parser->cfg->control_key_file = (yyvsp[(2) - (2)].str);
+	}
+    break;
+
+  case 240:
+
+/* Line 1806 of yacc.c  */
+#line 1215 "util/configparser.y"
+    {
+		OUTYY(("P(rc_control_cert_file:%s)\n", (yyvsp[(2) - (2)].str)));
+		free(cfg_parser->cfg->control_cert_file);
+		cfg_parser->cfg->control_cert_file = (yyvsp[(2) - (2)].str);
+	}
+    break;
+
+  case 241:
+
+/* Line 1806 of yacc.c  */
+#line 1222 "util/configparser.y"
+    { 
+		OUTYY(("\nP(python:)\n")); 
+	}
+    break;
+
+  case 245:
+
+/* Line 1806 of yacc.c  */
+#line 1231 "util/configparser.y"
+    {
+		OUTYY(("P(python-script:%s)\n", (yyvsp[(2) - (2)].str)));
+		free(cfg_parser->cfg->python_script);
+		cfg_parser->cfg->python_script = (yyvsp[(2) - (2)].str);
+	}
+    break;
+
+
+
+/* Line 1806 of yacc.c  */
+#line 3577 "util/configparser.c"
+      default: break;
+    }
+  /* User semantic actions sometimes alter yychar, and that requires
+     that yytoken be updated with the new translation.  We take the
+     approach of translating immediately before every use of yytoken.
+     One alternative is translating here after every semantic action,
+     but that translation would be missed if the semantic action invokes
+     YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+     if it invokes YYBACKUP.  In the case of YYABORT or YYACCEPT, an
+     incorrect destructor might then be invoked immediately.  In the
+     case of YYERROR or YYBACKUP, subsequent parser actions might lead
+     to an incorrect destructor call or verbose syntax error message
+     before the lookahead is translated.  */
+  YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+
+  *++yyvsp = yyval;
+
+  /* Now `shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTOKENS];
+
+  goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+  /* Make sure we have latest lookahead translation.  See comments at
+     user semantic actions for why this is necessary.  */
+  yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+#if ! YYERROR_VERBOSE
+      yyerror (YY_("syntax error"));
+#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+                                        yyssp, yytoken)
+      {
+        char const *yymsgp = YY_("syntax error");
+        int yysyntax_error_status;
+        yysyntax_error_status = YYSYNTAX_ERROR;
+        if (yysyntax_error_status == 0)
+          yymsgp = yymsg;
+        else if (yysyntax_error_status == 1)
+          {
+            if (yymsg != yymsgbuf)
+              YYSTACK_FREE (yymsg);
+            yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+            if (!yymsg)
+              {
+                yymsg = yymsgbuf;
+                yymsg_alloc = sizeof yymsgbuf;
+                yysyntax_error_status = 2;
+              }
+            else
+              {
+                yysyntax_error_status = YYSYNTAX_ERROR;
+                yymsgp = yymsg;
+              }
+          }
+        yyerror (yymsgp);
+        if (yysyntax_error_status == 2)
+          goto yyexhaustedlab;
+      }
+# undef YYSYNTAX_ERROR
+#endif
+    }
+
+
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse lookahead token after an
+	 error, discard it.  */
+
+      if (yychar <= YYEOF)
+	{
+	  /* Return failure if at end of input.  */
+	  if (yychar == YYEOF)
+	    YYABORT;
+	}
+      else
+	{
+	  yydestruct ("Error: discarding",
+		      yytoken, &yylval);
+	  yychar = YYEMPTY;
+	}
+    }
+
+  /* Else will try to reuse lookahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+
+  /* Pacify compilers like GCC when the user code never invokes
+     YYERROR and the label yyerrorlab therefore never appears in user
+     code.  */
+  if (/*CONSTCOND*/ 0)
+     goto yyerrorlab;
+
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYERROR.  */
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;	/* Each real token shifted decrements this.  */
+
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (!yypact_value_is_default (yyn))
+	{
+	  yyn += YYTERROR;
+	  if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+	    {
+	      yyn = yytable[yyn];
+	      if (0 < yyn)
+		break;
+	    }
+	}
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+	YYABORT;
+
+
+      yydestruct ("Error: popping",
+		  yystos[yystate], yyvsp);
+      YYPOPSTACK (1);
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  *++yyvsp = yylval;
+
+
+  /* Shift the error token.  */
+  YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+#if !defined(yyoverflow) || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here.  |
+`-------------------------------------------------*/
+yyexhaustedlab:
+  yyerror (YY_("memory exhausted"));
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+  if (yychar != YYEMPTY)
+    {
+      /* Make sure we have latest lookahead translation.  See comments at
+         user semantic actions for why this is necessary.  */
+      yytoken = YYTRANSLATE (yychar);
+      yydestruct ("Cleanup: discarding lookahead",
+                  yytoken, &yylval);
+    }
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYABORT or YYACCEPT.  */
+  YYPOPSTACK (yylen);
+  YY_STACK_PRINT (yyss, yyssp);
+  while (yyssp != yyss)
+    {
+      yydestruct ("Cleanup: popping",
+		  yystos[*yyssp], yyvsp);
+      YYPOPSTACK (1);
+    }
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+  if (yymsg != yymsgbuf)
+    YYSTACK_FREE (yymsg);
+#endif
+  /* Make sure YYID is used.  */
+  return YYID (yyresult);
+}
+
+
+
+/* Line 2067 of yacc.c  */
+#line 1236 "util/configparser.y"
+
+
+/* parse helper routines could be here */
+
diff --git a/3rdParty/Unbound/src/src/util/configparser.h b/3rdParty/Unbound/src/src/util/configparser.h
new file mode 100644
index 0000000..fc3bffa
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/configparser.h
@@ -0,0 +1,314 @@
+/* A Bison parser, made by GNU Bison 2.5.  */
+
+/* Bison interface for Yacc-like parsers in C
+   
+      Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
+   
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+   
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     SPACE = 258,
+     LETTER = 259,
+     NEWLINE = 260,
+     COMMENT = 261,
+     COLON = 262,
+     ANY = 263,
+     ZONESTR = 264,
+     STRING_ARG = 265,
+     VAR_SERVER = 266,
+     VAR_VERBOSITY = 267,
+     VAR_NUM_THREADS = 268,
+     VAR_PORT = 269,
+     VAR_OUTGOING_RANGE = 270,
+     VAR_INTERFACE = 271,
+     VAR_DO_IP4 = 272,
+     VAR_DO_IP6 = 273,
+     VAR_DO_UDP = 274,
+     VAR_DO_TCP = 275,
+     VAR_CHROOT = 276,
+     VAR_USERNAME = 277,
+     VAR_DIRECTORY = 278,
+     VAR_LOGFILE = 279,
+     VAR_PIDFILE = 280,
+     VAR_MSG_CACHE_SIZE = 281,
+     VAR_MSG_CACHE_SLABS = 282,
+     VAR_NUM_QUERIES_PER_THREAD = 283,
+     VAR_RRSET_CACHE_SIZE = 284,
+     VAR_RRSET_CACHE_SLABS = 285,
+     VAR_OUTGOING_NUM_TCP = 286,
+     VAR_INFRA_HOST_TTL = 287,
+     VAR_INFRA_LAME_TTL = 288,
+     VAR_INFRA_CACHE_SLABS = 289,
+     VAR_INFRA_CACHE_NUMHOSTS = 290,
+     VAR_INFRA_CACHE_LAME_SIZE = 291,
+     VAR_NAME = 292,
+     VAR_STUB_ZONE = 293,
+     VAR_STUB_HOST = 294,
+     VAR_STUB_ADDR = 295,
+     VAR_TARGET_FETCH_POLICY = 296,
+     VAR_HARDEN_SHORT_BUFSIZE = 297,
+     VAR_HARDEN_LARGE_QUERIES = 298,
+     VAR_FORWARD_ZONE = 299,
+     VAR_FORWARD_HOST = 300,
+     VAR_FORWARD_ADDR = 301,
+     VAR_DO_NOT_QUERY_ADDRESS = 302,
+     VAR_HIDE_IDENTITY = 303,
+     VAR_HIDE_VERSION = 304,
+     VAR_IDENTITY = 305,
+     VAR_VERSION = 306,
+     VAR_HARDEN_GLUE = 307,
+     VAR_MODULE_CONF = 308,
+     VAR_TRUST_ANCHOR_FILE = 309,
+     VAR_TRUST_ANCHOR = 310,
+     VAR_VAL_OVERRIDE_DATE = 311,
+     VAR_BOGUS_TTL = 312,
+     VAR_VAL_CLEAN_ADDITIONAL = 313,
+     VAR_VAL_PERMISSIVE_MODE = 314,
+     VAR_INCOMING_NUM_TCP = 315,
+     VAR_MSG_BUFFER_SIZE = 316,
+     VAR_KEY_CACHE_SIZE = 317,
+     VAR_KEY_CACHE_SLABS = 318,
+     VAR_TRUSTED_KEYS_FILE = 319,
+     VAR_VAL_NSEC3_KEYSIZE_ITERATIONS = 320,
+     VAR_USE_SYSLOG = 321,
+     VAR_OUTGOING_INTERFACE = 322,
+     VAR_ROOT_HINTS = 323,
+     VAR_DO_NOT_QUERY_LOCALHOST = 324,
+     VAR_CACHE_MAX_TTL = 325,
+     VAR_HARDEN_DNSSEC_STRIPPED = 326,
+     VAR_ACCESS_CONTROL = 327,
+     VAR_LOCAL_ZONE = 328,
+     VAR_LOCAL_DATA = 329,
+     VAR_INTERFACE_AUTOMATIC = 330,
+     VAR_STATISTICS_INTERVAL = 331,
+     VAR_DO_DAEMONIZE = 332,
+     VAR_USE_CAPS_FOR_ID = 333,
+     VAR_STATISTICS_CUMULATIVE = 334,
+     VAR_OUTGOING_PORT_PERMIT = 335,
+     VAR_OUTGOING_PORT_AVOID = 336,
+     VAR_DLV_ANCHOR_FILE = 337,
+     VAR_DLV_ANCHOR = 338,
+     VAR_NEG_CACHE_SIZE = 339,
+     VAR_HARDEN_REFERRAL_PATH = 340,
+     VAR_PRIVATE_ADDRESS = 341,
+     VAR_PRIVATE_DOMAIN = 342,
+     VAR_REMOTE_CONTROL = 343,
+     VAR_CONTROL_ENABLE = 344,
+     VAR_CONTROL_INTERFACE = 345,
+     VAR_CONTROL_PORT = 346,
+     VAR_SERVER_KEY_FILE = 347,
+     VAR_SERVER_CERT_FILE = 348,
+     VAR_CONTROL_KEY_FILE = 349,
+     VAR_CONTROL_CERT_FILE = 350,
+     VAR_EXTENDED_STATISTICS = 351,
+     VAR_LOCAL_DATA_PTR = 352,
+     VAR_JOSTLE_TIMEOUT = 353,
+     VAR_STUB_PRIME = 354,
+     VAR_UNWANTED_REPLY_THRESHOLD = 355,
+     VAR_LOG_TIME_ASCII = 356,
+     VAR_DOMAIN_INSECURE = 357,
+     VAR_PYTHON = 358,
+     VAR_PYTHON_SCRIPT = 359,
+     VAR_VAL_SIG_SKEW_MIN = 360,
+     VAR_VAL_SIG_SKEW_MAX = 361,
+     VAR_CACHE_MIN_TTL = 362,
+     VAR_VAL_LOG_LEVEL = 363,
+     VAR_AUTO_TRUST_ANCHOR_FILE = 364,
+     VAR_KEEP_MISSING = 365,
+     VAR_ADD_HOLDDOWN = 366,
+     VAR_DEL_HOLDDOWN = 367,
+     VAR_SO_RCVBUF = 368,
+     VAR_EDNS_BUFFER_SIZE = 369,
+     VAR_PREFETCH = 370,
+     VAR_PREFETCH_KEY = 371,
+     VAR_SO_SNDBUF = 372,
+     VAR_HARDEN_BELOW_NXDOMAIN = 373,
+     VAR_IGNORE_CD_FLAG = 374,
+     VAR_LOG_QUERIES = 375,
+     VAR_TCP_UPSTREAM = 376,
+     VAR_SSL_UPSTREAM = 377,
+     VAR_SSL_SERVICE_KEY = 378,
+     VAR_SSL_SERVICE_PEM = 379,
+     VAR_SSL_PORT = 380
+   };
+#endif
+/* Tokens.  */
+#define SPACE 258
+#define LETTER 259
+#define NEWLINE 260
+#define COMMENT 261
+#define COLON 262
+#define ANY 263
+#define ZONESTR 264
+#define STRING_ARG 265
+#define VAR_SERVER 266
+#define VAR_VERBOSITY 267
+#define VAR_NUM_THREADS 268
+#define VAR_PORT 269
+#define VAR_OUTGOING_RANGE 270
+#define VAR_INTERFACE 271
+#define VAR_DO_IP4 272
+#define VAR_DO_IP6 273
+#define VAR_DO_UDP 274
+#define VAR_DO_TCP 275
+#define VAR_CHROOT 276
+#define VAR_USERNAME 277
+#define VAR_DIRECTORY 278
+#define VAR_LOGFILE 279
+#define VAR_PIDFILE 280
+#define VAR_MSG_CACHE_SIZE 281
+#define VAR_MSG_CACHE_SLABS 282
+#define VAR_NUM_QUERIES_PER_THREAD 283
+#define VAR_RRSET_CACHE_SIZE 284
+#define VAR_RRSET_CACHE_SLABS 285
+#define VAR_OUTGOING_NUM_TCP 286
+#define VAR_INFRA_HOST_TTL 287
+#define VAR_INFRA_LAME_TTL 288
+#define VAR_INFRA_CACHE_SLABS 289
+#define VAR_INFRA_CACHE_NUMHOSTS 290
+#define VAR_INFRA_CACHE_LAME_SIZE 291
+#define VAR_NAME 292
+#define VAR_STUB_ZONE 293
+#define VAR_STUB_HOST 294
+#define VAR_STUB_ADDR 295
+#define VAR_TARGET_FETCH_POLICY 296
+#define VAR_HARDEN_SHORT_BUFSIZE 297
+#define VAR_HARDEN_LARGE_QUERIES 298
+#define VAR_FORWARD_ZONE 299
+#define VAR_FORWARD_HOST 300
+#define VAR_FORWARD_ADDR 301
+#define VAR_DO_NOT_QUERY_ADDRESS 302
+#define VAR_HIDE_IDENTITY 303
+#define VAR_HIDE_VERSION 304
+#define VAR_IDENTITY 305
+#define VAR_VERSION 306
+#define VAR_HARDEN_GLUE 307
+#define VAR_MODULE_CONF 308
+#define VAR_TRUST_ANCHOR_FILE 309
+#define VAR_TRUST_ANCHOR 310
+#define VAR_VAL_OVERRIDE_DATE 311
+#define VAR_BOGUS_TTL 312
+#define VAR_VAL_CLEAN_ADDITIONAL 313
+#define VAR_VAL_PERMISSIVE_MODE 314
+#define VAR_INCOMING_NUM_TCP 315
+#define VAR_MSG_BUFFER_SIZE 316
+#define VAR_KEY_CACHE_SIZE 317
+#define VAR_KEY_CACHE_SLABS 318
+#define VAR_TRUSTED_KEYS_FILE 319
+#define VAR_VAL_NSEC3_KEYSIZE_ITERATIONS 320
+#define VAR_USE_SYSLOG 321
+#define VAR_OUTGOING_INTERFACE 322
+#define VAR_ROOT_HINTS 323
+#define VAR_DO_NOT_QUERY_LOCALHOST 324
+#define VAR_CACHE_MAX_TTL 325
+#define VAR_HARDEN_DNSSEC_STRIPPED 326
+#define VAR_ACCESS_CONTROL 327
+#define VAR_LOCAL_ZONE 328
+#define VAR_LOCAL_DATA 329
+#define VAR_INTERFACE_AUTOMATIC 330
+#define VAR_STATISTICS_INTERVAL 331
+#define VAR_DO_DAEMONIZE 332
+#define VAR_USE_CAPS_FOR_ID 333
+#define VAR_STATISTICS_CUMULATIVE 334
+#define VAR_OUTGOING_PORT_PERMIT 335
+#define VAR_OUTGOING_PORT_AVOID 336
+#define VAR_DLV_ANCHOR_FILE 337
+#define VAR_DLV_ANCHOR 338
+#define VAR_NEG_CACHE_SIZE 339
+#define VAR_HARDEN_REFERRAL_PATH 340
+#define VAR_PRIVATE_ADDRESS 341
+#define VAR_PRIVATE_DOMAIN 342
+#define VAR_REMOTE_CONTROL 343
+#define VAR_CONTROL_ENABLE 344
+#define VAR_CONTROL_INTERFACE 345
+#define VAR_CONTROL_PORT 346
+#define VAR_SERVER_KEY_FILE 347
+#define VAR_SERVER_CERT_FILE 348
+#define VAR_CONTROL_KEY_FILE 349
+#define VAR_CONTROL_CERT_FILE 350
+#define VAR_EXTENDED_STATISTICS 351
+#define VAR_LOCAL_DATA_PTR 352
+#define VAR_JOSTLE_TIMEOUT 353
+#define VAR_STUB_PRIME 354
+#define VAR_UNWANTED_REPLY_THRESHOLD 355
+#define VAR_LOG_TIME_ASCII 356
+#define VAR_DOMAIN_INSECURE 357
+#define VAR_PYTHON 358
+#define VAR_PYTHON_SCRIPT 359
+#define VAR_VAL_SIG_SKEW_MIN 360
+#define VAR_VAL_SIG_SKEW_MAX 361
+#define VAR_CACHE_MIN_TTL 362
+#define VAR_VAL_LOG_LEVEL 363
+#define VAR_AUTO_TRUST_ANCHOR_FILE 364
+#define VAR_KEEP_MISSING 365
+#define VAR_ADD_HOLDDOWN 366
+#define VAR_DEL_HOLDDOWN 367
+#define VAR_SO_RCVBUF 368
+#define VAR_EDNS_BUFFER_SIZE 369
+#define VAR_PREFETCH 370
+#define VAR_PREFETCH_KEY 371
+#define VAR_SO_SNDBUF 372
+#define VAR_HARDEN_BELOW_NXDOMAIN 373
+#define VAR_IGNORE_CD_FLAG 374
+#define VAR_LOG_QUERIES 375
+#define VAR_TCP_UPSTREAM 376
+#define VAR_SSL_UPSTREAM 377
+#define VAR_SSL_SERVICE_KEY 378
+#define VAR_SSL_SERVICE_PEM 379
+#define VAR_SSL_PORT 380
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+
+/* Line 2068 of yacc.c  */
+#line 64 "util/configparser.y"
+
+	char*	str;
+
+
+
+/* Line 2068 of yacc.c  */
+#line 306 "util/configparser.h"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+extern YYSTYPE yylval;
+
+
diff --git a/3rdParty/Unbound/src/src/util/configparser.y b/3rdParty/Unbound/src/src/util/configparser.y
new file mode 100644
index 0000000..09f77c5
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/configparser.y
@@ -0,0 +1,1238 @@
+/*
+ * configparser.y -- yacc grammar for unbound configuration files
+ *
+ * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ * 
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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 "config.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "util/configyyrename.h"
+#include "util/config_file.h"
+#include "util/net_help.h"
+
+int ub_c_lex(void);
+void ub_c_error(const char *message);
+
+/* these need to be global, otherwise they cannot be used inside yacc */
+extern struct config_parser_state* cfg_parser;
+
+#if 0
+#define OUTYY(s)  printf s /* used ONLY when debugging */
+#else
+#define OUTYY(s)
+#endif
+
+%}
+%union {
+	char*	str;
+};
+
+%token SPACE LETTER NEWLINE COMMENT COLON ANY ZONESTR
+%token <str> STRING_ARG
+%token VAR_SERVER VAR_VERBOSITY VAR_NUM_THREADS VAR_PORT
+%token VAR_OUTGOING_RANGE VAR_INTERFACE
+%token VAR_DO_IP4 VAR_DO_IP6 VAR_DO_UDP VAR_DO_TCP 
+%token VAR_CHROOT VAR_USERNAME VAR_DIRECTORY VAR_LOGFILE VAR_PIDFILE
+%token VAR_MSG_CACHE_SIZE VAR_MSG_CACHE_SLABS VAR_NUM_QUERIES_PER_THREAD
+%token VAR_RRSET_CACHE_SIZE VAR_RRSET_CACHE_SLABS VAR_OUTGOING_NUM_TCP
+%token VAR_INFRA_HOST_TTL VAR_INFRA_LAME_TTL VAR_INFRA_CACHE_SLABS
+%token VAR_INFRA_CACHE_NUMHOSTS VAR_INFRA_CACHE_LAME_SIZE VAR_NAME
+%token VAR_STUB_ZONE VAR_STUB_HOST VAR_STUB_ADDR VAR_TARGET_FETCH_POLICY
+%token VAR_HARDEN_SHORT_BUFSIZE VAR_HARDEN_LARGE_QUERIES
+%token VAR_FORWARD_ZONE VAR_FORWARD_HOST VAR_FORWARD_ADDR
+%token VAR_DO_NOT_QUERY_ADDRESS VAR_HIDE_IDENTITY VAR_HIDE_VERSION
+%token VAR_IDENTITY VAR_VERSION VAR_HARDEN_GLUE VAR_MODULE_CONF
+%token VAR_TRUST_ANCHOR_FILE VAR_TRUST_ANCHOR VAR_VAL_OVERRIDE_DATE
+%token VAR_BOGUS_TTL VAR_VAL_CLEAN_ADDITIONAL VAR_VAL_PERMISSIVE_MODE
+%token VAR_INCOMING_NUM_TCP VAR_MSG_BUFFER_SIZE VAR_KEY_CACHE_SIZE
+%token VAR_KEY_CACHE_SLABS VAR_TRUSTED_KEYS_FILE 
+%token VAR_VAL_NSEC3_KEYSIZE_ITERATIONS VAR_USE_SYSLOG 
+%token VAR_OUTGOING_INTERFACE VAR_ROOT_HINTS VAR_DO_NOT_QUERY_LOCALHOST
+%token VAR_CACHE_MAX_TTL VAR_HARDEN_DNSSEC_STRIPPED VAR_ACCESS_CONTROL
+%token VAR_LOCAL_ZONE VAR_LOCAL_DATA VAR_INTERFACE_AUTOMATIC
+%token VAR_STATISTICS_INTERVAL VAR_DO_DAEMONIZE VAR_USE_CAPS_FOR_ID
+%token VAR_STATISTICS_CUMULATIVE VAR_OUTGOING_PORT_PERMIT 
+%token VAR_OUTGOING_PORT_AVOID VAR_DLV_ANCHOR_FILE VAR_DLV_ANCHOR
+%token VAR_NEG_CACHE_SIZE VAR_HARDEN_REFERRAL_PATH VAR_PRIVATE_ADDRESS
+%token VAR_PRIVATE_DOMAIN VAR_REMOTE_CONTROL VAR_CONTROL_ENABLE
+%token VAR_CONTROL_INTERFACE VAR_CONTROL_PORT VAR_SERVER_KEY_FILE
+%token VAR_SERVER_CERT_FILE VAR_CONTROL_KEY_FILE VAR_CONTROL_CERT_FILE
+%token VAR_EXTENDED_STATISTICS VAR_LOCAL_DATA_PTR VAR_JOSTLE_TIMEOUT
+%token VAR_STUB_PRIME VAR_UNWANTED_REPLY_THRESHOLD VAR_LOG_TIME_ASCII
+%token VAR_DOMAIN_INSECURE VAR_PYTHON VAR_PYTHON_SCRIPT VAR_VAL_SIG_SKEW_MIN
+%token VAR_VAL_SIG_SKEW_MAX VAR_CACHE_MIN_TTL VAR_VAL_LOG_LEVEL
+%token VAR_AUTO_TRUST_ANCHOR_FILE VAR_KEEP_MISSING VAR_ADD_HOLDDOWN 
+%token VAR_DEL_HOLDDOWN VAR_SO_RCVBUF VAR_EDNS_BUFFER_SIZE VAR_PREFETCH
+%token VAR_PREFETCH_KEY VAR_SO_SNDBUF VAR_HARDEN_BELOW_NXDOMAIN
+%token VAR_IGNORE_CD_FLAG VAR_LOG_QUERIES VAR_TCP_UPSTREAM VAR_SSL_UPSTREAM
+%token VAR_SSL_SERVICE_KEY VAR_SSL_SERVICE_PEM VAR_SSL_PORT
+
+%%
+toplevelvars: /* empty */ | toplevelvars toplevelvar ;
+toplevelvar: serverstart contents_server | stubstart contents_stub |
+	forwardstart contents_forward | pythonstart contents_py | 
+	rcstart contents_rc
+	;
+
+/* server: declaration */
+serverstart: VAR_SERVER
+	{ 
+		OUTYY(("\nP(server:)\n")); 
+	}
+	;
+contents_server: contents_server content_server 
+	| ;
+content_server: server_num_threads | server_verbosity | server_port |
+	server_outgoing_range | server_do_ip4 |
+	server_do_ip6 | server_do_udp | server_do_tcp | 
+	server_interface | server_chroot | server_username | 
+	server_directory | server_logfile | server_pidfile |
+	server_msg_cache_size | server_msg_cache_slabs |
+	server_num_queries_per_thread | server_rrset_cache_size | 
+	server_rrset_cache_slabs | server_outgoing_num_tcp | 
+	server_infra_host_ttl | server_infra_lame_ttl | 
+	server_infra_cache_slabs | server_infra_cache_numhosts |
+	server_infra_cache_lame_size | server_target_fetch_policy | 
+	server_harden_short_bufsize | server_harden_large_queries |
+	server_do_not_query_address | server_hide_identity |
+	server_hide_version | server_identity | server_version |
+	server_harden_glue | server_module_conf | server_trust_anchor_file |
+	server_trust_anchor | server_val_override_date | server_bogus_ttl |
+	server_val_clean_additional | server_val_permissive_mode |
+	server_incoming_num_tcp | server_msg_buffer_size | 
+	server_key_cache_size | server_key_cache_slabs | 
+	server_trusted_keys_file | server_val_nsec3_keysize_iterations |
+	server_use_syslog | server_outgoing_interface | server_root_hints |
+	server_do_not_query_localhost | server_cache_max_ttl |
+	server_harden_dnssec_stripped | server_access_control |
+	server_local_zone | server_local_data | server_interface_automatic |
+	server_statistics_interval | server_do_daemonize | 
+	server_use_caps_for_id | server_statistics_cumulative |
+	server_outgoing_port_permit | server_outgoing_port_avoid |
+	server_dlv_anchor_file | server_dlv_anchor | server_neg_cache_size |
+	server_harden_referral_path | server_private_address |
+	server_private_domain | server_extended_statistics | 
+	server_local_data_ptr | server_jostle_timeout | 
+	server_unwanted_reply_threshold | server_log_time_ascii | 
+	server_domain_insecure | server_val_sig_skew_min | 
+	server_val_sig_skew_max | server_cache_min_ttl | server_val_log_level |
+	server_auto_trust_anchor_file | server_add_holddown | 
+	server_del_holddown | server_keep_missing | server_so_rcvbuf |
+	server_edns_buffer_size | server_prefetch | server_prefetch_key |
+	server_so_sndbuf | server_harden_below_nxdomain | server_ignore_cd_flag |
+	server_log_queries | server_tcp_upstream | server_ssl_upstream |
+	server_ssl_service_key | server_ssl_service_pem | server_ssl_port
+	;
+stubstart: VAR_STUB_ZONE
+	{
+		struct config_stub* s;
+		OUTYY(("\nP(stub_zone:)\n")); 
+		s = (struct config_stub*)calloc(1, sizeof(struct config_stub));
+		if(s) {
+			s->next = cfg_parser->cfg->stubs;
+			cfg_parser->cfg->stubs = s;
+		} else 
+			yyerror("out of memory");
+	}
+	;
+contents_stub: contents_stub content_stub 
+	| ;
+content_stub: stub_name | stub_host | stub_addr | stub_prime
+	;
+forwardstart: VAR_FORWARD_ZONE
+	{
+		struct config_stub* s;
+		OUTYY(("\nP(forward_zone:)\n")); 
+		s = (struct config_stub*)calloc(1, sizeof(struct config_stub));
+		if(s) {
+			s->next = cfg_parser->cfg->forwards;
+			cfg_parser->cfg->forwards = s;
+		} else 
+			yyerror("out of memory");
+	}
+	;
+contents_forward: contents_forward content_forward 
+	| ;
+content_forward: forward_name | forward_host | forward_addr 
+	;
+server_num_threads: VAR_NUM_THREADS STRING_ARG 
+	{ 
+		OUTYY(("P(server_num_threads:%s)\n", $2)); 
+		if(atoi($2) == 0 && strcmp($2, "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->num_threads = atoi($2);
+		free($2);
+	}
+	;
+server_verbosity: VAR_VERBOSITY STRING_ARG 
+	{ 
+		OUTYY(("P(server_verbosity:%s)\n", $2)); 
+		if(atoi($2) == 0 && strcmp($2, "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->verbosity = atoi($2);
+		free($2);
+	}
+	;
+server_statistics_interval: VAR_STATISTICS_INTERVAL STRING_ARG 
+	{ 
+		OUTYY(("P(server_statistics_interval:%s)\n", $2)); 
+		if(strcmp($2, "") == 0 || strcmp($2, "0") == 0)
+			cfg_parser->cfg->stat_interval = 0;
+		else if(atoi($2) == 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->stat_interval = atoi($2);
+		free($2);
+	}
+	;
+server_statistics_cumulative: VAR_STATISTICS_CUMULATIVE STRING_ARG
+	{
+		OUTYY(("P(server_statistics_cumulative:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->stat_cumulative = (strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_extended_statistics: VAR_EXTENDED_STATISTICS STRING_ARG
+	{
+		OUTYY(("P(server_extended_statistics:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->stat_extended = (strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_port: VAR_PORT STRING_ARG
+	{
+		OUTYY(("P(server_port:%s)\n", $2));
+		if(atoi($2) == 0)
+			yyerror("port number expected");
+		else cfg_parser->cfg->port = atoi($2);
+		free($2);
+	}
+	;
+server_interface: VAR_INTERFACE STRING_ARG
+	{
+		OUTYY(("P(server_interface:%s)\n", $2));
+		if(cfg_parser->cfg->num_ifs == 0)
+			cfg_parser->cfg->ifs = calloc(1, sizeof(char*));
+		else 	cfg_parser->cfg->ifs = realloc(cfg_parser->cfg->ifs,
+				(cfg_parser->cfg->num_ifs+1)*sizeof(char*));
+		if(!cfg_parser->cfg->ifs)
+			yyerror("out of memory");
+		else
+			cfg_parser->cfg->ifs[cfg_parser->cfg->num_ifs++] = $2;
+	}
+	;
+server_outgoing_interface: VAR_OUTGOING_INTERFACE STRING_ARG
+	{
+		OUTYY(("P(server_outgoing_interface:%s)\n", $2));
+		if(cfg_parser->cfg->num_out_ifs == 0)
+			cfg_parser->cfg->out_ifs = calloc(1, sizeof(char*));
+		else 	cfg_parser->cfg->out_ifs = realloc(
+			cfg_parser->cfg->out_ifs, 
+			(cfg_parser->cfg->num_out_ifs+1)*sizeof(char*));
+		if(!cfg_parser->cfg->out_ifs)
+			yyerror("out of memory");
+		else
+			cfg_parser->cfg->out_ifs[
+				cfg_parser->cfg->num_out_ifs++] = $2;
+	}
+	;
+server_outgoing_range: VAR_OUTGOING_RANGE STRING_ARG
+	{
+		OUTYY(("P(server_outgoing_range:%s)\n", $2));
+		if(atoi($2) == 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->outgoing_num_ports = atoi($2);
+		free($2);
+	}
+	;
+server_outgoing_port_permit: VAR_OUTGOING_PORT_PERMIT STRING_ARG
+	{
+		OUTYY(("P(server_outgoing_port_permit:%s)\n", $2));
+		if(!cfg_mark_ports($2, 1, 
+			cfg_parser->cfg->outgoing_avail_ports, 65536))
+			yyerror("port number or range (\"low-high\") expected");
+		free($2);
+	}
+	;
+server_outgoing_port_avoid: VAR_OUTGOING_PORT_AVOID STRING_ARG
+	{
+		OUTYY(("P(server_outgoing_port_avoid:%s)\n", $2));
+		if(!cfg_mark_ports($2, 0, 
+			cfg_parser->cfg->outgoing_avail_ports, 65536))
+			yyerror("port number or range (\"low-high\") expected");
+		free($2);
+	}
+	;
+server_outgoing_num_tcp: VAR_OUTGOING_NUM_TCP STRING_ARG
+	{
+		OUTYY(("P(server_outgoing_num_tcp:%s)\n", $2));
+		if(atoi($2) == 0 && strcmp($2, "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->outgoing_num_tcp = atoi($2);
+		free($2);
+	}
+	;
+server_incoming_num_tcp: VAR_INCOMING_NUM_TCP STRING_ARG
+	{
+		OUTYY(("P(server_incoming_num_tcp:%s)\n", $2));
+		if(atoi($2) == 0 && strcmp($2, "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->incoming_num_tcp = atoi($2);
+		free($2);
+	}
+	;
+server_interface_automatic: VAR_INTERFACE_AUTOMATIC STRING_ARG
+	{
+		OUTYY(("P(server_interface_automatic:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->if_automatic = (strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_do_ip4: VAR_DO_IP4 STRING_ARG
+	{
+		OUTYY(("P(server_do_ip4:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->do_ip4 = (strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_do_ip6: VAR_DO_IP6 STRING_ARG
+	{
+		OUTYY(("P(server_do_ip6:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->do_ip6 = (strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_do_udp: VAR_DO_UDP STRING_ARG
+	{
+		OUTYY(("P(server_do_udp:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->do_udp = (strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_do_tcp: VAR_DO_TCP STRING_ARG
+	{
+		OUTYY(("P(server_do_tcp:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->do_tcp = (strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_tcp_upstream: VAR_TCP_UPSTREAM STRING_ARG
+	{
+		OUTYY(("P(server_tcp_upstream:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->tcp_upstream = (strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_ssl_upstream: VAR_SSL_UPSTREAM STRING_ARG
+	{
+		OUTYY(("P(server_ssl_upstream:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->ssl_upstream = (strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_ssl_service_key: VAR_SSL_SERVICE_KEY STRING_ARG
+	{
+		OUTYY(("P(server_ssl_service_key:%s)\n", $2));
+		free(cfg_parser->cfg->ssl_service_key);
+		cfg_parser->cfg->ssl_service_key = $2;
+	}
+	;
+server_ssl_service_pem: VAR_SSL_SERVICE_PEM STRING_ARG
+	{
+		OUTYY(("P(server_ssl_service_pem:%s)\n", $2));
+		free(cfg_parser->cfg->ssl_service_pem);
+		cfg_parser->cfg->ssl_service_pem = $2;
+	}
+	;
+server_ssl_port: VAR_SSL_PORT STRING_ARG
+	{
+		OUTYY(("P(server_ssl_port:%s)\n", $2));
+		if(atoi($2) == 0)
+			yyerror("port number expected");
+		else cfg_parser->cfg->ssl_port = atoi($2);
+		free($2);
+	}
+	;
+server_do_daemonize: VAR_DO_DAEMONIZE STRING_ARG
+	{
+		OUTYY(("P(server_do_daemonize:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->do_daemonize = (strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_use_syslog: VAR_USE_SYSLOG STRING_ARG
+	{
+		OUTYY(("P(server_use_syslog:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->use_syslog = (strcmp($2, "yes")==0);
+#if !defined(HAVE_SYSLOG_H) && !defined(UB_ON_WINDOWS)
+		if(strcmp($2, "yes") == 0)
+			yyerror("no syslog services are available. "
+				"(reconfigure and compile to add)");
+#endif
+		free($2);
+	}
+	;
+server_log_time_ascii: VAR_LOG_TIME_ASCII STRING_ARG
+	{
+		OUTYY(("P(server_log_time_ascii:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->log_time_ascii = (strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_log_queries: VAR_LOG_QUERIES STRING_ARG
+	{
+		OUTYY(("P(server_log_queries:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->log_queries = (strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_chroot: VAR_CHROOT STRING_ARG
+	{
+		OUTYY(("P(server_chroot:%s)\n", $2));
+		free(cfg_parser->cfg->chrootdir);
+		cfg_parser->cfg->chrootdir = $2;
+	}
+	;
+server_username: VAR_USERNAME STRING_ARG
+	{
+		OUTYY(("P(server_username:%s)\n", $2));
+		free(cfg_parser->cfg->username);
+		cfg_parser->cfg->username = $2;
+	}
+	;
+server_directory: VAR_DIRECTORY STRING_ARG
+	{
+		OUTYY(("P(server_directory:%s)\n", $2));
+		free(cfg_parser->cfg->directory);
+		cfg_parser->cfg->directory = $2;
+	}
+	;
+server_logfile: VAR_LOGFILE STRING_ARG
+	{
+		OUTYY(("P(server_logfile:%s)\n", $2));
+		free(cfg_parser->cfg->logfile);
+		cfg_parser->cfg->logfile = $2;
+		cfg_parser->cfg->use_syslog = 0;
+	}
+	;
+server_pidfile: VAR_PIDFILE STRING_ARG
+	{
+		OUTYY(("P(server_pidfile:%s)\n", $2));
+		free(cfg_parser->cfg->pidfile);
+		cfg_parser->cfg->pidfile = $2;
+	}
+	;
+server_root_hints: VAR_ROOT_HINTS STRING_ARG
+	{
+		OUTYY(("P(server_root_hints:%s)\n", $2));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->root_hints, $2))
+			yyerror("out of memory");
+	}
+	;
+server_dlv_anchor_file: VAR_DLV_ANCHOR_FILE STRING_ARG
+	{
+		OUTYY(("P(server_dlv_anchor_file:%s)\n", $2));
+		free(cfg_parser->cfg->dlv_anchor_file);
+		cfg_parser->cfg->dlv_anchor_file = $2;
+	}
+	;
+server_dlv_anchor: VAR_DLV_ANCHOR STRING_ARG
+	{
+		OUTYY(("P(server_dlv_anchor:%s)\n", $2));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->dlv_anchor_list, $2))
+			yyerror("out of memory");
+	}
+	;
+server_auto_trust_anchor_file: VAR_AUTO_TRUST_ANCHOR_FILE STRING_ARG
+	{
+		OUTYY(("P(server_auto_trust_anchor_file:%s)\n", $2));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->
+			auto_trust_anchor_file_list, $2))
+			yyerror("out of memory");
+	}
+	;
+server_trust_anchor_file: VAR_TRUST_ANCHOR_FILE STRING_ARG
+	{
+		OUTYY(("P(server_trust_anchor_file:%s)\n", $2));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->
+			trust_anchor_file_list, $2))
+			yyerror("out of memory");
+	}
+	;
+server_trusted_keys_file: VAR_TRUSTED_KEYS_FILE STRING_ARG
+	{
+		OUTYY(("P(server_trusted_keys_file:%s)\n", $2));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->
+			trusted_keys_file_list, $2))
+			yyerror("out of memory");
+	}
+	;
+server_trust_anchor: VAR_TRUST_ANCHOR STRING_ARG
+	{
+		OUTYY(("P(server_trust_anchor:%s)\n", $2));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->trust_anchor_list, $2))
+			yyerror("out of memory");
+	}
+	;
+server_domain_insecure: VAR_DOMAIN_INSECURE STRING_ARG
+	{
+		OUTYY(("P(server_domain_insecure:%s)\n", $2));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->domain_insecure, $2))
+			yyerror("out of memory");
+	}
+	;
+server_hide_identity: VAR_HIDE_IDENTITY STRING_ARG
+	{
+		OUTYY(("P(server_hide_identity:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->hide_identity = (strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_hide_version: VAR_HIDE_VERSION STRING_ARG
+	{
+		OUTYY(("P(server_hide_version:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->hide_version = (strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_identity: VAR_IDENTITY STRING_ARG
+	{
+		OUTYY(("P(server_identity:%s)\n", $2));
+		free(cfg_parser->cfg->identity);
+		cfg_parser->cfg->identity = $2;
+	}
+	;
+server_version: VAR_VERSION STRING_ARG
+	{
+		OUTYY(("P(server_version:%s)\n", $2));
+		free(cfg_parser->cfg->version);
+		cfg_parser->cfg->version = $2;
+	}
+	;
+server_so_rcvbuf: VAR_SO_RCVBUF STRING_ARG
+	{
+		OUTYY(("P(server_so_rcvbuf:%s)\n", $2));
+		if(!cfg_parse_memsize($2, &cfg_parser->cfg->so_rcvbuf))
+			yyerror("buffer size expected");
+		free($2);
+	}
+	;
+server_so_sndbuf: VAR_SO_SNDBUF STRING_ARG
+	{
+		OUTYY(("P(server_so_sndbuf:%s)\n", $2));
+		if(!cfg_parse_memsize($2, &cfg_parser->cfg->so_sndbuf))
+			yyerror("buffer size expected");
+		free($2);
+	}
+	;
+server_edns_buffer_size: VAR_EDNS_BUFFER_SIZE STRING_ARG
+	{
+		OUTYY(("P(server_edns_buffer_size:%s)\n", $2));
+		if(atoi($2) == 0)
+			yyerror("number expected");
+		else if (atoi($2) < 12)
+			yyerror("edns buffer size too small");
+		else if (atoi($2) > 65535)
+			cfg_parser->cfg->edns_buffer_size = 65535;
+		else cfg_parser->cfg->edns_buffer_size = atoi($2);
+		free($2);
+	}
+	;
+server_msg_buffer_size: VAR_MSG_BUFFER_SIZE STRING_ARG
+	{
+		OUTYY(("P(server_msg_buffer_size:%s)\n", $2));
+		if(atoi($2) == 0)
+			yyerror("number expected");
+		else if (atoi($2) < 4096)
+			yyerror("message buffer size too small (use 4096)");
+		else cfg_parser->cfg->msg_buffer_size = atoi($2);
+		free($2);
+	}
+	;
+server_msg_cache_size: VAR_MSG_CACHE_SIZE STRING_ARG
+	{
+		OUTYY(("P(server_msg_cache_size:%s)\n", $2));
+		if(!cfg_parse_memsize($2, &cfg_parser->cfg->msg_cache_size))
+			yyerror("memory size expected");
+		free($2);
+	}
+	;
+server_msg_cache_slabs: VAR_MSG_CACHE_SLABS STRING_ARG
+	{
+		OUTYY(("P(server_msg_cache_slabs:%s)\n", $2));
+		if(atoi($2) == 0)
+			yyerror("number expected");
+		else {
+			cfg_parser->cfg->msg_cache_slabs = atoi($2);
+			if(!is_pow2(cfg_parser->cfg->msg_cache_slabs))
+				yyerror("must be a power of 2");
+		}
+		free($2);
+	}
+	;
+server_num_queries_per_thread: VAR_NUM_QUERIES_PER_THREAD STRING_ARG
+	{
+		OUTYY(("P(server_num_queries_per_thread:%s)\n", $2));
+		if(atoi($2) == 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->num_queries_per_thread = atoi($2);
+		free($2);
+	}
+	;
+server_jostle_timeout: VAR_JOSTLE_TIMEOUT STRING_ARG
+	{
+		OUTYY(("P(server_jostle_timeout:%s)\n", $2));
+		if(atoi($2) == 0 && strcmp($2, "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->jostle_time = atoi($2);
+		free($2);
+	}
+	;
+server_rrset_cache_size: VAR_RRSET_CACHE_SIZE STRING_ARG
+	{
+		OUTYY(("P(server_rrset_cache_size:%s)\n", $2));
+		if(!cfg_parse_memsize($2, &cfg_parser->cfg->rrset_cache_size))
+			yyerror("memory size expected");
+		free($2);
+	}
+	;
+server_rrset_cache_slabs: VAR_RRSET_CACHE_SLABS STRING_ARG
+	{
+		OUTYY(("P(server_rrset_cache_slabs:%s)\n", $2));
+		if(atoi($2) == 0)
+			yyerror("number expected");
+		else {
+			cfg_parser->cfg->rrset_cache_slabs = atoi($2);
+			if(!is_pow2(cfg_parser->cfg->rrset_cache_slabs))
+				yyerror("must be a power of 2");
+		}
+		free($2);
+	}
+	;
+server_infra_host_ttl: VAR_INFRA_HOST_TTL STRING_ARG
+	{
+		OUTYY(("P(server_infra_host_ttl:%s)\n", $2));
+		if(atoi($2) == 0 && strcmp($2, "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->host_ttl = atoi($2);
+		free($2);
+	}
+	;
+server_infra_lame_ttl: VAR_INFRA_LAME_TTL STRING_ARG
+	{
+		OUTYY(("P(server_infra_lame_ttl:%s)\n", $2));
+		verbose(VERB_DETAIL, "ignored infra-lame-ttl: %s (option "
+			"removed, use infra-host-ttl)", $2);
+		free($2);
+	}
+	;
+server_infra_cache_numhosts: VAR_INFRA_CACHE_NUMHOSTS STRING_ARG
+	{
+		OUTYY(("P(server_infra_cache_numhosts:%s)\n", $2));
+		if(atoi($2) == 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->infra_cache_numhosts = atoi($2);
+		free($2);
+	}
+	;
+server_infra_cache_lame_size: VAR_INFRA_CACHE_LAME_SIZE STRING_ARG
+	{
+		OUTYY(("P(server_infra_cache_lame_size:%s)\n", $2));
+		verbose(VERB_DETAIL, "ignored infra-cache-lame-size: %s "
+			"(option removed, use infra-cache-numhosts)", $2);
+		free($2);
+	}
+	;
+server_infra_cache_slabs: VAR_INFRA_CACHE_SLABS STRING_ARG
+	{
+		OUTYY(("P(server_infra_cache_slabs:%s)\n", $2));
+		if(atoi($2) == 0)
+			yyerror("number expected");
+		else {
+			cfg_parser->cfg->infra_cache_slabs = atoi($2);
+			if(!is_pow2(cfg_parser->cfg->infra_cache_slabs))
+				yyerror("must be a power of 2");
+		}
+		free($2);
+	}
+	;
+server_target_fetch_policy: VAR_TARGET_FETCH_POLICY STRING_ARG
+	{
+		OUTYY(("P(server_target_fetch_policy:%s)\n", $2));
+		free(cfg_parser->cfg->target_fetch_policy);
+		cfg_parser->cfg->target_fetch_policy = $2;
+	}
+	;
+server_harden_short_bufsize: VAR_HARDEN_SHORT_BUFSIZE STRING_ARG
+	{
+		OUTYY(("P(server_harden_short_bufsize:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->harden_short_bufsize = 
+			(strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_harden_large_queries: VAR_HARDEN_LARGE_QUERIES STRING_ARG
+	{
+		OUTYY(("P(server_harden_large_queries:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->harden_large_queries = 
+			(strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_harden_glue: VAR_HARDEN_GLUE STRING_ARG
+	{
+		OUTYY(("P(server_harden_glue:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->harden_glue = 
+			(strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_harden_dnssec_stripped: VAR_HARDEN_DNSSEC_STRIPPED STRING_ARG
+	{
+		OUTYY(("P(server_harden_dnssec_stripped:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->harden_dnssec_stripped = 
+			(strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_harden_below_nxdomain: VAR_HARDEN_BELOW_NXDOMAIN STRING_ARG
+	{
+		OUTYY(("P(server_harden_below_nxdomain:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->harden_below_nxdomain = 
+			(strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_harden_referral_path: VAR_HARDEN_REFERRAL_PATH STRING_ARG
+	{
+		OUTYY(("P(server_harden_referral_path:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->harden_referral_path = 
+			(strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_use_caps_for_id: VAR_USE_CAPS_FOR_ID STRING_ARG
+	{
+		OUTYY(("P(server_use_caps_for_id:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->use_caps_bits_for_id = 
+			(strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_private_address: VAR_PRIVATE_ADDRESS STRING_ARG
+	{
+		OUTYY(("P(server_private_address:%s)\n", $2));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->private_address, $2))
+			yyerror("out of memory");
+	}
+	;
+server_private_domain: VAR_PRIVATE_DOMAIN STRING_ARG
+	{
+		OUTYY(("P(server_private_domain:%s)\n", $2));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->private_domain, $2))
+			yyerror("out of memory");
+	}
+	;
+server_prefetch: VAR_PREFETCH STRING_ARG
+	{
+		OUTYY(("P(server_prefetch:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->prefetch = (strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_prefetch_key: VAR_PREFETCH_KEY STRING_ARG
+	{
+		OUTYY(("P(server_prefetch_key:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->prefetch_key = (strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_unwanted_reply_threshold: VAR_UNWANTED_REPLY_THRESHOLD STRING_ARG
+	{
+		OUTYY(("P(server_unwanted_reply_threshold:%s)\n", $2));
+		if(atoi($2) == 0 && strcmp($2, "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->unwanted_threshold = atoi($2);
+		free($2);
+	}
+	;
+server_do_not_query_address: VAR_DO_NOT_QUERY_ADDRESS STRING_ARG
+	{
+		OUTYY(("P(server_do_not_query_address:%s)\n", $2));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->donotqueryaddrs, $2))
+			yyerror("out of memory");
+	}
+	;
+server_do_not_query_localhost: VAR_DO_NOT_QUERY_LOCALHOST STRING_ARG
+	{
+		OUTYY(("P(server_do_not_query_localhost:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->donotquery_localhost = 
+			(strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_access_control: VAR_ACCESS_CONTROL STRING_ARG STRING_ARG
+	{
+		OUTYY(("P(server_access_control:%s %s)\n", $2, $3));
+		if(strcmp($3, "deny")!=0 && strcmp($3, "refuse")!=0 &&
+			strcmp($3, "allow")!=0 && 
+			strcmp($3, "allow_snoop")!=0) {
+			yyerror("expected deny, refuse, allow or allow_snoop "
+				"in access control action");
+		} else {
+			if(!cfg_str2list_insert(&cfg_parser->cfg->acls, $2, $3))
+				fatal_exit("out of memory adding acl");
+		}
+	}
+	;
+server_module_conf: VAR_MODULE_CONF STRING_ARG
+	{
+		OUTYY(("P(server_module_conf:%s)\n", $2));
+		free(cfg_parser->cfg->module_conf);
+		cfg_parser->cfg->module_conf = $2;
+	}
+	;
+server_val_override_date: VAR_VAL_OVERRIDE_DATE STRING_ARG
+	{
+		OUTYY(("P(server_val_override_date:%s)\n", $2));
+		if(strlen($2) == 0 || strcmp($2, "0") == 0) {
+			cfg_parser->cfg->val_date_override = 0;
+		} else if(strlen($2) == 14) {
+			cfg_parser->cfg->val_date_override = 
+				cfg_convert_timeval($2);
+			if(!cfg_parser->cfg->val_date_override)
+				yyerror("bad date/time specification");
+		} else {
+			if(atoi($2) == 0)
+				yyerror("number expected");
+			cfg_parser->cfg->val_date_override = atoi($2);
+		}
+		free($2);
+	}
+	;
+server_val_sig_skew_min: VAR_VAL_SIG_SKEW_MIN STRING_ARG
+	{
+		OUTYY(("P(server_val_sig_skew_min:%s)\n", $2));
+		if(strlen($2) == 0 || strcmp($2, "0") == 0) {
+			cfg_parser->cfg->val_sig_skew_min = 0;
+		} else {
+			cfg_parser->cfg->val_sig_skew_min = atoi($2);
+			if(!cfg_parser->cfg->val_sig_skew_min)
+				yyerror("number expected");
+		}
+		free($2);
+	}
+	;
+server_val_sig_skew_max: VAR_VAL_SIG_SKEW_MAX STRING_ARG
+	{
+		OUTYY(("P(server_val_sig_skew_max:%s)\n", $2));
+		if(strlen($2) == 0 || strcmp($2, "0") == 0) {
+			cfg_parser->cfg->val_sig_skew_max = 0;
+		} else {
+			cfg_parser->cfg->val_sig_skew_max = atoi($2);
+			if(!cfg_parser->cfg->val_sig_skew_max)
+				yyerror("number expected");
+		}
+		free($2);
+	}
+	;
+server_cache_max_ttl: VAR_CACHE_MAX_TTL STRING_ARG
+	{
+		OUTYY(("P(server_cache_max_ttl:%s)\n", $2));
+		if(atoi($2) == 0 && strcmp($2, "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->max_ttl = atoi($2);
+		free($2);
+	}
+	;
+server_cache_min_ttl: VAR_CACHE_MIN_TTL STRING_ARG
+	{
+		OUTYY(("P(server_cache_min_ttl:%s)\n", $2));
+		if(atoi($2) == 0 && strcmp($2, "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->min_ttl = atoi($2);
+		free($2);
+	}
+	;
+server_bogus_ttl: VAR_BOGUS_TTL STRING_ARG
+	{
+		OUTYY(("P(server_bogus_ttl:%s)\n", $2));
+		if(atoi($2) == 0 && strcmp($2, "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->bogus_ttl = atoi($2);
+		free($2);
+	}
+	;
+server_val_clean_additional: VAR_VAL_CLEAN_ADDITIONAL STRING_ARG
+	{
+		OUTYY(("P(server_val_clean_additional:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->val_clean_additional = 
+			(strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_val_permissive_mode: VAR_VAL_PERMISSIVE_MODE STRING_ARG
+	{
+		OUTYY(("P(server_val_permissive_mode:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->val_permissive_mode = 
+			(strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_ignore_cd_flag: VAR_IGNORE_CD_FLAG STRING_ARG
+	{
+		OUTYY(("P(server_ignore_cd_flag:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->ignore_cd = (strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+server_val_log_level: VAR_VAL_LOG_LEVEL STRING_ARG
+	{
+		OUTYY(("P(server_val_log_level:%s)\n", $2));
+		if(atoi($2) == 0 && strcmp($2, "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->val_log_level = atoi($2);
+		free($2);
+	}
+	;
+server_val_nsec3_keysize_iterations: VAR_VAL_NSEC3_KEYSIZE_ITERATIONS STRING_ARG
+	{
+		OUTYY(("P(server_val_nsec3_keysize_iterations:%s)\n", $2));
+		free(cfg_parser->cfg->val_nsec3_key_iterations);
+		cfg_parser->cfg->val_nsec3_key_iterations = $2;
+	}
+	;
+server_add_holddown: VAR_ADD_HOLDDOWN STRING_ARG
+	{
+		OUTYY(("P(server_add_holddown:%s)\n", $2));
+		if(atoi($2) == 0 && strcmp($2, "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->add_holddown = atoi($2);
+		free($2);
+	}
+	;
+server_del_holddown: VAR_DEL_HOLDDOWN STRING_ARG
+	{
+		OUTYY(("P(server_del_holddown:%s)\n", $2));
+		if(atoi($2) == 0 && strcmp($2, "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->del_holddown = atoi($2);
+		free($2);
+	}
+	;
+server_keep_missing: VAR_KEEP_MISSING STRING_ARG
+	{
+		OUTYY(("P(server_keep_missing:%s)\n", $2));
+		if(atoi($2) == 0 && strcmp($2, "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->keep_missing = atoi($2);
+		free($2);
+	}
+	;
+server_key_cache_size: VAR_KEY_CACHE_SIZE STRING_ARG
+	{
+		OUTYY(("P(server_key_cache_size:%s)\n", $2));
+		if(!cfg_parse_memsize($2, &cfg_parser->cfg->key_cache_size))
+			yyerror("memory size expected");
+		free($2);
+	}
+	;
+server_key_cache_slabs: VAR_KEY_CACHE_SLABS STRING_ARG
+	{
+		OUTYY(("P(server_key_cache_slabs:%s)\n", $2));
+		if(atoi($2) == 0)
+			yyerror("number expected");
+		else {
+			cfg_parser->cfg->key_cache_slabs = atoi($2);
+			if(!is_pow2(cfg_parser->cfg->key_cache_slabs))
+				yyerror("must be a power of 2");
+		}
+		free($2);
+	}
+	;
+server_neg_cache_size: VAR_NEG_CACHE_SIZE STRING_ARG
+	{
+		OUTYY(("P(server_neg_cache_size:%s)\n", $2));
+		if(!cfg_parse_memsize($2, &cfg_parser->cfg->neg_cache_size))
+			yyerror("memory size expected");
+		free($2);
+	}
+	;
+server_local_zone: VAR_LOCAL_ZONE STRING_ARG STRING_ARG
+	{
+		OUTYY(("P(server_local_zone:%s %s)\n", $2, $3));
+		if(strcmp($3, "static")!=0 && strcmp($3, "deny")!=0 &&
+		   strcmp($3, "refuse")!=0 && strcmp($3, "redirect")!=0 &&
+		   strcmp($3, "transparent")!=0 && strcmp($3, "nodefault")!=0
+		   && strcmp($3, "typetransparent")!=0)
+			yyerror("local-zone type: expected static, deny, "
+				"refuse, redirect, transparent, "
+				"typetransparent or nodefault");
+		else if(strcmp($3, "nodefault")==0) {
+			if(!cfg_strlist_insert(&cfg_parser->cfg->
+				local_zones_nodefault, $2))
+				fatal_exit("out of memory adding local-zone");
+			free($3);
+		} else {
+			if(!cfg_str2list_insert(&cfg_parser->cfg->local_zones, 
+				$2, $3))
+				fatal_exit("out of memory adding local-zone");
+		}
+	}
+	;
+server_local_data: VAR_LOCAL_DATA STRING_ARG
+	{
+		OUTYY(("P(server_local_data:%s)\n", $2));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->local_data, $2))
+			fatal_exit("out of memory adding local-data");
+	}
+	;
+server_local_data_ptr: VAR_LOCAL_DATA_PTR STRING_ARG
+	{
+		char* ptr;
+		OUTYY(("P(server_local_data_ptr:%s)\n", $2));
+		ptr = cfg_ptr_reverse($2);
+		free($2);
+		if(ptr) {
+			if(!cfg_strlist_insert(&cfg_parser->cfg->
+				local_data, ptr))
+				fatal_exit("out of memory adding local-data");
+		} else {
+			yyerror("local-data-ptr could not be reversed");
+		}
+	}
+	;
+stub_name: VAR_NAME STRING_ARG
+	{
+		OUTYY(("P(name:%s)\n", $2));
+		if(cfg_parser->cfg->stubs->name)
+			yyerror("stub name override, there must be one name "
+				"for one stub-zone");
+		free(cfg_parser->cfg->stubs->name);
+		cfg_parser->cfg->stubs->name = $2;
+	}
+	;
+stub_host: VAR_STUB_HOST STRING_ARG
+	{
+		OUTYY(("P(stub-host:%s)\n", $2));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->stubs->hosts, $2))
+			yyerror("out of memory");
+	}
+	;
+stub_addr: VAR_STUB_ADDR STRING_ARG
+	{
+		OUTYY(("P(stub-addr:%s)\n", $2));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->stubs->addrs, $2))
+			yyerror("out of memory");
+	}
+	;
+stub_prime: VAR_STUB_PRIME STRING_ARG
+	{
+		OUTYY(("P(stub-prime:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->stubs->isprime = 
+			(strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+forward_name: VAR_NAME STRING_ARG
+	{
+		OUTYY(("P(name:%s)\n", $2));
+		if(cfg_parser->cfg->forwards->name)
+			yyerror("forward name override, there must be one "
+				"name for one forward-zone");
+		free(cfg_parser->cfg->forwards->name);
+		cfg_parser->cfg->forwards->name = $2;
+	}
+	;
+forward_host: VAR_FORWARD_HOST STRING_ARG
+	{
+		OUTYY(("P(forward-host:%s)\n", $2));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->forwards->hosts, $2))
+			yyerror("out of memory");
+	}
+	;
+forward_addr: VAR_FORWARD_ADDR STRING_ARG
+	{
+		OUTYY(("P(forward-addr:%s)\n", $2));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->forwards->addrs, $2))
+			yyerror("out of memory");
+	}
+	;
+rcstart: VAR_REMOTE_CONTROL
+	{ 
+		OUTYY(("\nP(remote-control:)\n")); 
+	}
+	;
+contents_rc: contents_rc content_rc 
+	| ;
+content_rc: rc_control_enable | rc_control_interface | rc_control_port |
+	rc_server_key_file | rc_server_cert_file | rc_control_key_file |
+	rc_control_cert_file
+	;
+rc_control_enable: VAR_CONTROL_ENABLE STRING_ARG
+	{
+		OUTYY(("P(control_enable:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->remote_control_enable = 
+			(strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
+rc_control_port: VAR_CONTROL_PORT STRING_ARG
+	{
+		OUTYY(("P(control_port:%s)\n", $2));
+		if(atoi($2) == 0)
+			yyerror("control port number expected");
+		else cfg_parser->cfg->control_port = atoi($2);
+		free($2);
+	}
+	;
+rc_control_interface: VAR_CONTROL_INTERFACE STRING_ARG
+	{
+		OUTYY(("P(control_interface:%s)\n", $2));
+		if(!cfg_strlist_insert(&cfg_parser->cfg->control_ifs, $2))
+			yyerror("out of memory");
+	}
+	;
+rc_server_key_file: VAR_SERVER_KEY_FILE STRING_ARG
+	{
+		OUTYY(("P(rc_server_key_file:%s)\n", $2));
+		free(cfg_parser->cfg->server_key_file);
+		cfg_parser->cfg->server_key_file = $2;
+	}
+	;
+rc_server_cert_file: VAR_SERVER_CERT_FILE STRING_ARG
+	{
+		OUTYY(("P(rc_server_cert_file:%s)\n", $2));
+		free(cfg_parser->cfg->server_cert_file);
+		cfg_parser->cfg->server_cert_file = $2;
+	}
+	;
+rc_control_key_file: VAR_CONTROL_KEY_FILE STRING_ARG
+	{
+		OUTYY(("P(rc_control_key_file:%s)\n", $2));
+		free(cfg_parser->cfg->control_key_file);
+		cfg_parser->cfg->control_key_file = $2;
+	}
+	;
+rc_control_cert_file: VAR_CONTROL_CERT_FILE STRING_ARG
+	{
+		OUTYY(("P(rc_control_cert_file:%s)\n", $2));
+		free(cfg_parser->cfg->control_cert_file);
+		cfg_parser->cfg->control_cert_file = $2;
+	}
+	;
+pythonstart: VAR_PYTHON
+	{ 
+		OUTYY(("\nP(python:)\n")); 
+	}
+	;
+contents_py: contents_py content_py
+	| ;
+content_py: py_script
+	;
+py_script: VAR_PYTHON_SCRIPT STRING_ARG
+	{
+		OUTYY(("P(python-script:%s)\n", $2));
+		free(cfg_parser->cfg->python_script);
+		cfg_parser->cfg->python_script = $2;
+	}
+%%
+
+/* parse helper routines could be here */
diff --git a/3rdParty/Unbound/src/src/util/configyyrename.h b/3rdParty/Unbound/src/src/util/configyyrename.h
new file mode 100644
index 0000000..f529be5
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/configyyrename.h
@@ -0,0 +1,88 @@
+/*
+ * configyyrename.h -- renames for config file yy values to avoid conflicts.
+ *
+ * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
+ *
+ * See LICENSE for the license.
+ *
+ */
+
+#ifndef UTIL_CONFIGYYRENAME_H
+#define UTIL_CONFIGYYRENAME_H
+
+/* defines to change symbols so that no yacc/lex symbols clash */
+#define yymaxdepth ub_c_maxdepth
+#define yyparse ub_c_parse
+#define yylex   ub_c_lex
+#define yyerror ub_c_error
+#define yylval  ub_c_lval
+#define yychar  ub_c_char
+#define yydebug ub_c_debug
+#define yypact  ub_c_pact
+#define yyr1    ub_c_r1
+#define yyr2    ub_c_r2
+#define yydef   ub_c_def
+#define yychk   ub_c_chk
+#define yypgo   ub_c_pgo
+#define yyact   ub_c_act
+#define yyexca  ub_c_exca
+#define yyerrflag ub_c_errflag
+#define yynerrs ub_c_nerrs
+#define yyps    ub_c_ps
+#define yypv    ub_c_pv
+#define yys     ub_c_s
+#define yy_yys  ub_c_yys
+#define yystate ub_c_state
+#define yytmp   ub_c_tmp
+#define yyv     ub_c_v
+#define yy_yyv  ub_c_yyv
+#define yyval   ub_c_val
+#define yylloc  ub_c_lloc
+#define yyreds  ub_c_reds
+#define yytoks  ub_c_toks
+#define yylhs   ub_c_yylhs
+#define yylen   ub_c_yylen
+#define yydefred ub_c_yydefred
+#define yydgoto ub_c_yydgoto
+#define yysindex ub_c_yysindex
+#define yyrindex ub_c_yyrindex
+#define yygindex ub_c_yygindex
+#define yytable  ub_c_yytable
+#define yycheck  ub_c_yycheck
+#define yyname   ub_c_yyname
+#define yyrule   ub_c_yyrule
+#define yyin    ub_c_in
+#define yyout   ub_c_out
+#define yywrap  ub_c_wrap
+#define yy_load_buffer_state ub_c_load_buffer_state
+#define yy_switch_to_buffer ub_c_switch_to_buffer
+#define yy_flush_buffer ub_c_flush_buffer
+#define yy_init_buffer ub_c_init_buffer
+#define yy_scan_buffer ub_c_scan_buffer
+#define yy_scan_bytes ub_c_scan_bytes
+#define yy_scan_string ub_c_scan_string
+#define yy_create_buffer ub_c_create_buffer
+#define yyrestart ub_c_restart
+#define yy_delete_buffer ub_c_delete_buffer
+#define yypop_buffer_state ub_c_pop_buffer_state
+#define yypush_buffer_state ub_c_push_buffer_state
+#define yyunput ub_c_unput
+#define yyset_in ub_c_set_in
+#define yyget_in ub_c_get_in
+#define yyset_out ub_c_set_out
+#define yyget_out ub_c_get_out
+#define yyget_lineno ub_c_get_lineno
+#define yyset_lineno ub_c_set_lineno
+#define yyset_debug ub_c_set_debug
+#define yyget_debug ub_c_get_debug
+#define yy_flex_debug ub_c_flex_debug
+#define yylex_destroy ub_c_lex_destroy
+#define yyfree ub_c_free
+#define yyrealloc ub_c_realloc
+#define yyalloc ub_c_alloc
+#define yymalloc ub_c_malloc
+#define yyget_leng ub_c_get_leng
+#define yylineno ub_c_lineno
+#define yyget_text ub_c_get_text
+
+#endif /* UTIL_CONFIGYYRENAME_H */
diff --git a/3rdParty/Unbound/src/src/util/data/dname.c b/3rdParty/Unbound/src/src/util/data/dname.c
new file mode 100644
index 0000000..d2b2997
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/data/dname.c
@@ -0,0 +1,781 @@
+/*
+ * util/data/dname.h - domain name handling
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains domain name handling functions.
+ */
+
+#include "config.h"
+#include <ctype.h>
+#include "util/data/dname.h"
+#include "util/data/msgparse.h"
+#include "util/log.h"
+#include "util/storage/lookup3.h"
+
+/* determine length of a dname in buffer, no compression pointers allowed */
+size_t
+query_dname_len(ldns_buffer* query)
+{
+	size_t len = 0;
+	size_t labellen;
+	while(1) {
+		if(ldns_buffer_remaining(query) < 1)
+			return 0; /* parse error, need label len */
+		labellen = ldns_buffer_read_u8(query);
+		if(labellen&0xc0)
+			return 0; /* no compression allowed in queries */
+		len += labellen + 1;
+		if(len > LDNS_MAX_DOMAINLEN)
+			return 0; /* too long */
+		if(labellen == 0)
+			return len;
+		if(ldns_buffer_remaining(query) < labellen)
+			return 0; /* parse error, need content */
+		ldns_buffer_skip(query, (ssize_t)labellen);
+	}
+}
+
+size_t 
+dname_valid(uint8_t* dname, size_t maxlen)
+{
+	size_t len = 0;
+	size_t labellen;
+	labellen = *dname++;
+	while(labellen) {
+		if(labellen&0xc0)
+			return 0; /* no compression ptrs allowed */
+		len += labellen + 1;
+		if(len >= LDNS_MAX_DOMAINLEN)
+			return 0; /* too long */
+		if(len > maxlen)
+			return 0; /* does not fit in memory allocation */
+		dname += labellen;
+		labellen = *dname++;
+	}
+	len += 1;
+	if(len > maxlen)
+		return 0; /* does not fit in memory allocation */
+	return len;
+}
+
+/** compare uncompressed, noncanonical, registers are hints for speed */
+int 
+query_dname_compare(register uint8_t* d1, register uint8_t* d2)
+{
+	register uint8_t lab1, lab2;
+	log_assert(d1 && d2);
+	lab1 = *d1++;
+	lab2 = *d2++;
+	while( lab1 != 0 || lab2 != 0 ) {
+		/* compare label length */
+		/* if one dname ends, it has labellength 0 */
+		if(lab1 != lab2) {
+			if(lab1 < lab2)
+				return -1;
+			return 1;
+		}
+		log_assert(lab1 == lab2 && lab1 != 0);
+		/* compare lowercased labels. */
+		while(lab1--) {
+			/* compare bytes first for speed */
+			if(*d1 != *d2 && 
+				tolower((int)*d1) != tolower((int)*d2)) {
+				if(tolower((int)*d1) < tolower((int)*d2))
+					return -1;
+				return 1;
+			}
+			d1++;
+			d2++;
+		}
+		/* next pair of labels. */
+		lab1 = *d1++;
+		lab2 = *d2++;
+	}
+	return 0;
+}
+
+void 
+query_dname_tolower(uint8_t* dname)
+{
+	/* the dname is stored uncompressed */
+	uint8_t labellen;
+	labellen = *dname;
+	while(labellen) {
+		dname++;
+		while(labellen--) {
+			*dname = (uint8_t)tolower((int)*dname);
+			dname++;
+		}
+		labellen = *dname;
+	}
+}
+
+void 
+pkt_dname_tolower(ldns_buffer* pkt, uint8_t* dname)
+{
+	uint8_t lablen;
+	int count = 0;
+	if(dname >= ldns_buffer_end(pkt))
+		return;
+	lablen = *dname++;
+	while(lablen) {
+		if(LABEL_IS_PTR(lablen)) {
+			if((size_t)PTR_OFFSET(lablen, *dname) 
+				>= ldns_buffer_limit(pkt))
+				return;
+			dname = ldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
+			lablen = *dname++;
+			if(count++ > MAX_COMPRESS_PTRS)
+				return;
+			continue;
+		}
+		if(dname+lablen >= ldns_buffer_end(pkt))
+			return;
+		while(lablen--) {
+			*dname = (uint8_t)tolower((int)*dname);
+			dname++;
+		}
+		if(dname >= ldns_buffer_end(pkt))
+			return;
+		lablen = *dname++;
+	}
+}
+
+
+size_t
+pkt_dname_len(ldns_buffer* pkt)
+{
+	size_t len = 0;
+	int ptrcount = 0;
+	uint8_t labellen;
+	size_t endpos = 0;
+
+	/* read dname and determine length */
+	/* check compression pointers, loops, out of bounds */
+	while(1) {
+		/* read next label */
+		if(ldns_buffer_remaining(pkt) < 1)
+			return 0;
+		labellen = ldns_buffer_read_u8(pkt);
+		if(LABEL_IS_PTR(labellen)) {
+			/* compression ptr */
+			uint16_t ptr;
+			if(ldns_buffer_remaining(pkt) < 1)
+				return 0;
+			ptr = PTR_OFFSET(labellen, ldns_buffer_read_u8(pkt));
+			if(ptrcount++ > MAX_COMPRESS_PTRS)
+				return 0; /* loop! */
+			if(ldns_buffer_limit(pkt) <= ptr)
+				return 0; /* out of bounds! */
+			if(!endpos)
+				endpos = ldns_buffer_position(pkt);
+			ldns_buffer_set_position(pkt, ptr);
+		} else {
+			/* label contents */
+			if(labellen > 0x3f)
+				return 0; /* label too long */
+			len += 1 + labellen;
+			if(len > LDNS_MAX_DOMAINLEN)
+				return 0;
+			if(labellen == 0) {
+				/* end of dname */
+				break;
+			}
+			if(ldns_buffer_remaining(pkt) < labellen)
+				return 0;
+			ldns_buffer_skip(pkt, (ssize_t)labellen);
+		}
+	}
+	if(endpos)
+		ldns_buffer_set_position(pkt, endpos);
+
+	return len;
+}
+
+int 
+dname_pkt_compare(ldns_buffer* pkt, uint8_t* d1, uint8_t* d2)
+{
+	uint8_t len1, len2;
+	log_assert(pkt && d1 && d2);
+	len1 = *d1++;
+	len2 = *d2++;
+	while( len1 != 0 || len2 != 0 ) {
+		/* resolve ptrs */
+		if(LABEL_IS_PTR(len1)) {
+			d1 = ldns_buffer_at(pkt, PTR_OFFSET(len1, *d1));
+			len1 = *d1++;
+			continue;
+		}
+		if(LABEL_IS_PTR(len2)) {
+			d2 = ldns_buffer_at(pkt, PTR_OFFSET(len2, *d2));
+			len2 = *d2++;
+			continue;
+		}
+		/* check label length */
+		log_assert(len1 <= LDNS_MAX_LABELLEN);
+		log_assert(len2 <= LDNS_MAX_LABELLEN);
+		if(len1 != len2) {
+			if(len1 < len2) return -1;
+			return 1;
+		}
+		log_assert(len1 == len2 && len1 != 0);
+		/* compare labels */
+		while(len1--) {
+			if(tolower((int)*d1++) != tolower((int)*d2++)) {
+				if(tolower((int)d1[-1]) < tolower((int)d2[-1]))
+					return -1;
+				return 1;
+			}
+		}
+		len1 = *d1++;
+		len2 = *d2++;
+	}
+	return 0;
+}
+
+hashvalue_t 
+dname_query_hash(uint8_t* dname, hashvalue_t h)
+{
+	uint8_t labuf[LDNS_MAX_LABELLEN+1];
+	uint8_t lablen;
+	int i;
+
+	/* preserve case of query, make hash label by label */
+	lablen = *dname++;
+	while(lablen) {
+		log_assert(lablen <= LDNS_MAX_LABELLEN);
+		labuf[0] = lablen;
+		i=0;
+		while(lablen--)
+			labuf[++i] = (uint8_t)tolower((int)*dname++);
+		h = hashlittle(labuf, labuf[0] + 1, h);
+		lablen = *dname++;
+	}
+
+	return h;
+}
+
+hashvalue_t 
+dname_pkt_hash(ldns_buffer* pkt, uint8_t* dname, hashvalue_t h)
+{
+	uint8_t labuf[LDNS_MAX_LABELLEN+1];
+	uint8_t lablen;
+	int i;
+
+	/* preserve case of query, make hash label by label */
+	lablen = *dname++;
+	while(lablen) {
+		if(LABEL_IS_PTR(lablen)) {
+			/* follow pointer */
+			dname = ldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
+			lablen = *dname++;
+			continue;
+		}
+		log_assert(lablen <= LDNS_MAX_LABELLEN);
+		labuf[0] = lablen;
+		i=0;
+		while(lablen--)
+			labuf[++i] = (uint8_t)tolower((int)*dname++);
+		h = hashlittle(labuf, labuf[0] + 1, h);
+		lablen = *dname++;
+	}
+
+	return h;
+}
+
+void dname_pkt_copy(ldns_buffer* pkt, uint8_t* to, uint8_t* dname)
+{
+	/* copy over the dname and decompress it at the same time */
+	size_t len = 0;
+	uint8_t lablen;
+	lablen = *dname++;
+	while(lablen) {
+		if(LABEL_IS_PTR(lablen)) {
+			/* follow pointer */
+			dname = ldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
+			lablen = *dname++;
+			continue;
+		}
+		log_assert(lablen <= LDNS_MAX_LABELLEN);
+		len += (size_t)lablen+1;
+		if(len >= LDNS_MAX_DOMAINLEN) {
+			*to = 0; /* end the result prematurely */
+			log_err("bad dname in dname_pkt_copy");
+			return;
+		}
+		*to++ = lablen;
+		memmove(to, dname, lablen);
+		dname += lablen;
+		to += lablen;
+		lablen = *dname++;
+	}
+	/* copy last \0 */
+	*to = 0;
+}
+
+void dname_print(FILE* out, ldns_buffer* pkt, uint8_t* dname)
+{
+	uint8_t lablen;
+	if(!out) out = stdout;
+	if(!dname) return;
+
+	lablen = *dname++;
+	if(!lablen) 
+		fputc('.', out);
+	while(lablen) {
+		if(LABEL_IS_PTR(lablen)) {
+			/* follow pointer */
+			if(!pkt) {
+				fputs("??compressionptr??", out);
+				return;
+			}
+			dname = ldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
+			lablen = *dname++;
+			continue;
+		}
+		if(lablen > LDNS_MAX_LABELLEN) {
+			fputs("??extendedlabel??", out);
+			return;
+		}
+		while(lablen--)
+			fputc((int)*dname++, out);
+		fputc('.', out);
+		lablen = *dname++;
+	}
+}
+
+int 
+dname_count_labels(uint8_t* dname)
+{
+	uint8_t lablen;
+	int labs = 1;
+
+	lablen = *dname++;
+	while(lablen) {
+		labs++;
+		dname += lablen;
+		lablen = *dname++;
+	}
+	return labs;
+}
+
+int 
+dname_count_size_labels(uint8_t* dname, size_t* size)
+{	
+	uint8_t lablen;
+	int labs = 1;
+	size_t sz = 1;
+
+	lablen = *dname++;
+	while(lablen) {
+		labs++;
+		sz += lablen+1;
+		dname += lablen;
+		lablen = *dname++;
+	}
+	*size = sz;
+	return labs;
+}
+
+/**
+ * Compare labels in memory, lowercase while comparing.
+ * @param p1: label 1
+ * @param p2: label 2
+ * @param len: number of bytes to compare.
+ * @return: 0, -1, +1 comparison result.
+ */
+static int
+memlowercmp(uint8_t* p1, uint8_t* p2, uint8_t len)
+{
+	while(len--) {
+		if(*p1 != *p2 && tolower((int)*p1) != tolower((int)*p2)) {
+			if(tolower((int)*p1) < tolower((int)*p2))
+				return -1;
+			return 1;
+		}
+		p1++;
+		p2++;
+	}
+	return 0;
+}
+
+int 
+dname_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, int* mlabs)
+{
+	uint8_t len1, len2;
+	int atlabel = labs1;
+	int lastmlabs;
+	int lastdiff = 0;
+	/* first skip so that we compare same label. */
+	if(labs1 > labs2) {
+		while(atlabel > labs2) {
+			len1 = *d1++;
+			d1 += len1;
+			atlabel--;
+		}
+		log_assert(atlabel == labs2);
+	} else if(labs1 < labs2) {
+		atlabel = labs2;
+		while(atlabel > labs1) {
+			len2 = *d2++;
+			d2 += len2;
+			atlabel--;
+		}
+		log_assert(atlabel == labs1);
+	}
+	lastmlabs = atlabel+1;
+	/* now at same label in d1 and d2, atlabel */
+	/* www.example.com.                  */
+	/* 4   3       2  1   atlabel number */
+	/* repeat until at root label (which is always the same) */
+	while(atlabel > 1) {
+		len1 = *d1++;
+		len2 = *d2++;
+		if(len1 != len2) {
+			log_assert(len1 != 0 && len2 != 0);
+			if(len1<len2)
+				lastdiff = -1;
+			else	lastdiff = 1;
+			lastmlabs = atlabel;
+			d1 += len1;
+			d2 += len2;
+		} else {
+			/* memlowercmp is inlined here; or just like
+			 * if((c=memlowercmp(d1, d2, len1)) != 0) { 
+			 *	lastdiff = c;
+			 *	lastmlabs = atlabel; } apart from d1++,d2++ */
+			while(len1) {
+				if(*d1 != *d2 && tolower((int)*d1) 
+					!= tolower((int)*d2)) {
+					if(tolower((int)*d1) < 
+						tolower((int)*d2)) {
+						lastdiff = -1;
+						lastmlabs = atlabel;
+						d1 += len1;
+						d2 += len1;
+						break;
+					}
+					lastdiff = 1;
+					lastmlabs = atlabel;
+					d1 += len1;
+					d2 += len1;
+					break; /* out of memlowercmp */
+				}
+				d1++;
+				d2++;
+				len1--;
+			}
+		}
+		atlabel--;
+	}
+	/* last difference atlabel number, so number of labels matching,
+	 * at the right side, is one less. */
+	*mlabs = lastmlabs-1;
+	if(lastdiff == 0) {
+		/* all labels compared were equal, check if one has more
+		 * labels, so that example.com. > com. */
+		if(labs1 > labs2)
+			return 1;
+		else if(labs1 < labs2)
+			return -1;
+	}
+	return lastdiff;
+}
+
+int 
+dname_buffer_write(ldns_buffer* pkt, uint8_t* dname)
+{
+	uint8_t lablen;
+
+	if(ldns_buffer_remaining(pkt) < 1)
+		return 0;
+	lablen = *dname++;
+	ldns_buffer_write_u8(pkt, lablen);
+	while(lablen) {
+		if(ldns_buffer_remaining(pkt) < (size_t)lablen+1)
+			return 0;
+		ldns_buffer_write(pkt, dname, lablen);
+		dname += lablen;
+		lablen = *dname++;
+		ldns_buffer_write_u8(pkt, lablen);
+	}
+	return 1;
+}
+
+void dname_str(uint8_t* dname, char* str)
+{
+	size_t len = 0;
+	uint8_t lablen = 0;
+	char* s = str;
+	if(!dname || !*dname) {
+		*s++ = '.';
+		*s = 0;
+		return;
+	}
+	lablen = *dname++;
+	while(lablen) {
+		if(lablen > LDNS_MAX_LABELLEN) {
+			*s++ = '#';
+			*s = 0;
+			return;
+		}
+		len += lablen+1;
+		if(len >= LDNS_MAX_DOMAINLEN-1) {
+			*s++ = '&';
+			*s = 0;
+			return;
+		}
+		while(lablen--) {
+			if(isalnum((int)*dname) 
+				|| *dname == '-' || *dname == '_' 
+				|| *dname == '*')
+				*s++ = *(char*)dname++;
+			else	{
+				*s++ = '?';
+				dname++;
+			}
+		}
+		*s++ = '.';
+		lablen = *dname++;
+	}
+	*s = 0;
+}
+
+int 
+dname_strict_subdomain(uint8_t* d1, int labs1, uint8_t* d2, int labs2)
+{
+	int m;
+	/* check subdomain: d1: www.example.com. and d2: example.com. */
+	if(labs2 >= labs1) 
+		return 0;
+	if(dname_lab_cmp(d1, labs1, d2, labs2, &m) > 0) {
+		/* subdomain if all labels match */
+		return (m == labs2);
+	}
+	return 0;
+}
+
+int 
+dname_strict_subdomain_c(uint8_t* d1, uint8_t* d2)
+{
+	return dname_strict_subdomain(d1, dname_count_labels(d1), d2,
+		dname_count_labels(d2));
+}
+
+int 
+dname_subdomain_c(uint8_t* d1, uint8_t* d2)
+{
+	int m;
+	/* check subdomain: d1: www.example.com. and d2: example.com. */
+	/*  	or 	    d1: example.com. and d2: example.com. */
+	int labs1 = dname_count_labels(d1);
+	int labs2 = dname_count_labels(d2);
+	if(labs2 > labs1) 
+		return 0;
+	if(dname_lab_cmp(d1, labs1, d2, labs2, &m) < 0) {
+		/* must have been example.com , www.example.com - wrong */
+		/* or otherwise different dnames */
+		return 0;
+	}
+	return (m == labs2);
+}
+
+int 
+dname_is_root(uint8_t* dname)
+{
+	uint8_t len;
+	log_assert(dname);
+	len = dname[0];
+	log_assert(!LABEL_IS_PTR(len));
+	return (len == 0);
+}
+
+void 
+dname_remove_label(uint8_t** dname, size_t* len)
+{
+	size_t lablen;
+	log_assert(dname && *dname && len);
+	lablen = (*dname)[0];
+	log_assert(!LABEL_IS_PTR(lablen));
+	log_assert(*len > lablen);
+	if(lablen == 0)
+		return; /* do not modify root label */
+	*len -= lablen+1;
+	*dname += lablen+1;
+}
+
+void 
+dname_remove_labels(uint8_t** dname, size_t* len, int n)
+{
+	int i;
+	for(i=0; i<n; i++)
+		dname_remove_label(dname, len);
+}
+
+int 
+dname_signame_label_count(uint8_t* dname)
+{
+	uint8_t lablen;
+	int count = 0;
+	if(!*dname)
+		return 0;
+	if(dname[0] == 1 && dname[1] == '*')
+		dname += 2;
+	lablen = dname[0];
+	while(lablen) {
+		count++;
+		dname += lablen;
+		dname += 1;
+		lablen = dname[0];
+	}
+	return count;
+}
+
+int 
+dname_is_wild(uint8_t* dname)
+{
+	return (dname[0] == 1 && dname[1] == '*');
+}
+
+/**
+ * Compare labels in memory, lowercase while comparing.
+ * Returns canonical order for labels. If all is equal, the
+ * shortest is first.
+ *
+ * @param p1: label 1
+ * @param len1: length of label 1.
+ * @param p2: label 2
+ * @param len2: length of label 2.
+ * @return: 0, -1, +1 comparison result.
+ */
+static int
+memcanoncmp(uint8_t* p1, uint8_t len1, uint8_t* p2, uint8_t len2)
+{
+	uint8_t min = (len1<len2)?len1:len2;
+	int c = memlowercmp(p1, p2, min);
+	if(c != 0)
+		return c;
+	/* equal, see who is shortest */
+	if(len1 < len2)
+		return -1;
+	if(len1 > len2)
+		return 1;
+	return 0;
+}
+
+
+int 
+dname_canon_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, int* mlabs)
+{
+	/* like dname_lab_cmp, but with different label comparison,
+	 * empty character sorts before \000.
+	 * So   ylyly is before z. */
+	uint8_t len1, len2;
+	int atlabel = labs1;
+	int lastmlabs;
+	int lastdiff = 0;
+	int c;
+	/* first skip so that we compare same label. */
+	if(labs1 > labs2) {
+		while(atlabel > labs2) {
+			len1 = *d1++;
+			d1 += len1;
+			atlabel--;
+		}
+		log_assert(atlabel == labs2);
+	} else if(labs1 < labs2) {
+		atlabel = labs2;
+		while(atlabel > labs1) {
+			len2 = *d2++;
+			d2 += len2;
+			atlabel--;
+		}
+		log_assert(atlabel == labs1);
+	}
+	lastmlabs = atlabel+1;
+	/* now at same label in d1 and d2, atlabel */
+	/* www.example.com.                  */
+	/* 4   3       2  1   atlabel number */
+	/* repeat until at root label (which is always the same) */
+	while(atlabel > 1) {
+		len1 = *d1++;
+		len2 = *d2++;
+
+		if((c=memcanoncmp(d1, len1, d2, len2)) != 0) {
+			if(c<0)
+				lastdiff = -1;
+			else	lastdiff = 1;
+			lastmlabs = atlabel;
+		}
+
+		d1 += len1;
+		d2 += len2;
+		atlabel--;
+	}
+	/* last difference atlabel number, so number of labels matching,
+	 * at the right side, is one less. */
+	*mlabs = lastmlabs-1;
+	if(lastdiff == 0) {
+		/* all labels compared were equal, check if one has more
+		 * labels, so that example.com. > com. */
+		if(labs1 > labs2)
+			return 1;
+		else if(labs1 < labs2)
+			return -1;
+	}
+	return lastdiff;
+}
+
+int
+dname_canonical_compare(uint8_t* d1, uint8_t* d2)
+{
+	int labs1, labs2, m;
+	labs1 = dname_count_labels(d1);
+	labs2 = dname_count_labels(d2);
+	return dname_canon_lab_cmp(d1, labs1, d2, labs2, &m);
+}
+
+uint8_t* dname_get_shared_topdomain(uint8_t* d1, uint8_t* d2)
+{
+	int labs1, labs2, m;
+	size_t len = LDNS_MAX_DOMAINLEN;
+	labs1 = dname_count_labels(d1);
+	labs2 = dname_count_labels(d2);
+	(void)dname_lab_cmp(d1, labs1, d2, labs2, &m);
+	dname_remove_labels(&d1, &len, labs1-m);
+	return d1;
+}
diff --git a/3rdParty/Unbound/src/src/util/data/dname.h b/3rdParty/Unbound/src/src/util/data/dname.h
new file mode 100644
index 0000000..b942848
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/data/dname.h
@@ -0,0 +1,303 @@
+/*
+ * util/data/dname.h - domain name routines
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to deal with domain names (dnames).
+ *
+ * Some of the functions deal with domain names as a wireformat buffer,
+ * with a length.
+ */
+
+#ifndef UTIL_DATA_DNAME_H
+#define UTIL_DATA_DNAME_H
+#include "util/storage/lruhash.h"
+
+/** max number of compression ptrs to follow */
+#define MAX_COMPRESS_PTRS 256
+
+/** 
+ * Determine length of dname in buffer, no compression ptrs allowed, 
+ * @param query: the ldns buffer, current position at start of dname.
+ *	at end, position is at end of the dname.
+ * @return: 0 on parse failure, or length including ending 0 of dname. 
+ */
+size_t query_dname_len(ldns_buffer* query);
+
+/**
+ * Determine if dname in memory is correct. no compression ptrs allowed.
+ * @param dname: where dname starts in memory.
+ * @param len: dname is not allowed to exceed this length (i.e. of allocation).
+ * @return length of dname if dname is ok, 0 on a parse error.
+ */
+size_t dname_valid(uint8_t* dname, size_t len);
+
+/** lowercase query dname */
+void query_dname_tolower(uint8_t* dname);
+
+/** 
+ * lowercase pkt dname (follows compression pointers)
+ * @param pkt: the packet, used to follow compression pointers. Position 
+ *	is unchanged.
+ * @param dname: start of dname in packet.
+ */
+void pkt_dname_tolower(ldns_buffer* pkt, uint8_t* dname);
+
+/**
+ * Compare query dnames (uncompressed storage). The Dnames passed do not
+ * have to be lowercased, comparison routine does this.
+ *
+ * This routine is special, in that the comparison that it does corresponds
+ * with the canonical comparison needed when comparing dnames inside rdata
+ * for RR types that need canonicalization. That means that the first byte
+ * that is smaller (possibly after lowercasing) makes an RR smaller, or the
+ * shortest name makes an RR smaller.
+ *
+ * This routine does not compute the canonical order needed for NSEC 
+ * processing.
+ *
+ * Dnames have to be valid format.
+ * @param d1: dname to compare
+ * @param d2: dname to compare
+ * @return: -1, 0, or +1 depending on comparison results.
+ * 	Sort order is first difference found. not the canonical ordering.
+ */
+int query_dname_compare(uint8_t* d1, uint8_t* d2);
+
+/**
+ * Determine correct, compressed, dname present in packet.
+ * Checks for parse errors.
+ * @param pkt: packet to read from (from current start position).
+ * @return: 0 on parse error.
+ *	At exit the position is right after the (compressed) dname.
+ *	Compression pointers are followed and checked for loops.
+ *	The uncompressed wireformat length is returned.
+ */
+size_t pkt_dname_len(ldns_buffer* pkt);
+
+/**
+ * Compare dnames in packet (compressed). Dnames must be valid.
+ * routine performs lowercasing, so the packet casing is preserved.
+ * @param pkt: packet, used to resolve compression pointers.
+ * @param d1: dname to compare
+ * @param d2: dname to compare
+ * @return: -1, 0, or +1 depending on comparison results.
+ * 	Sort order is first difference found. not the canonical ordering.
+ */
+int dname_pkt_compare(ldns_buffer* pkt, uint8_t* d1, uint8_t* d2);
+
+/**
+ * Hash dname, label by label, lowercasing, into hashvalue.
+ * Dname in query format (not compressed).
+ * @param dname: dname to hash.
+ * @param h: initial hash value.
+ * @return: result hash value.
+ */
+hashvalue_t dname_query_hash(uint8_t* dname, hashvalue_t h);
+
+/**
+ * Hash dname, label by label, lowercasing, into hashvalue.
+ * Dname in pkt format (compressed).
+ * @param pkt: packet, for resolving compression pointers.
+ * @param dname: dname to hash, pointer to the pkt buffer.
+ * 	Must be valid format. No loops, etc.
+ * @param h: initial hash value.
+ * @return: result hash value.
+ * 	Result is the same as dname_query_hash, even if compression is used.
+ */
+hashvalue_t dname_pkt_hash(ldns_buffer* pkt, uint8_t* dname, hashvalue_t h);
+
+/**
+ * Copy over a valid dname and decompress it.
+ * @param pkt: packet to resolve compression pointers.
+ * @param to: buffer of size from pkt_len function to hold result.
+ * @param dname: pointer into packet where dname starts.
+ */
+void dname_pkt_copy(ldns_buffer* pkt, uint8_t* to, uint8_t* dname);
+
+/**
+ * Copy over a valid dname to a packet.
+ * @param pkt: packet to copy to.
+ * @param dname: dname to copy.
+ * @return: 0 if not enough space in buffer.
+ */
+int dname_buffer_write(ldns_buffer* pkt, uint8_t* dname);
+
+/**
+ * Count the number of labels in an uncompressed dname in memory.
+ * @param dname: pointer to uncompressed dname.
+ * @return: count of labels, including root label, "com." has 2 labels.
+ */
+int dname_count_labels(uint8_t* dname);
+
+/**
+ * Count labels and dname length both, for uncompressed dname in memory.
+ * @param dname: pointer to uncompressed dname.
+ * @param size: length of dname, including root label.
+ * @return: count of labels, including root label, "com." has 2 labels.
+ */
+int dname_count_size_labels(uint8_t* dname, size_t* size);
+
+/**
+ * Compare dnames, sorted not canonical, but by label.
+ * Such that zone contents follows zone apex.
+ * @param d1: first dname. pointer to uncompressed wireformat.
+ * @param labs1: number of labels in first dname.
+ * @param d2: second dname. pointer to uncompressed wireformat.
+ * @param labs2: number of labels in second dname.
+ * @param mlabs: number of labels that matched exactly (the shared topdomain).
+ * @return: 0 for equal, -1 smaller, or +1 d1 larger than d2.
+ */
+int dname_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, int* mlabs);
+
+/**
+ * See if domain name d1 is a strict subdomain of d2.
+ * That is a subdomain, but not equal. 
+ * @param d1: domain name, uncompressed wireformat
+ * @param labs1: number of labels in d1, including root label.
+ * @param d2: domain name, uncompressed wireformat
+ * @param labs2: number of labels in d2, including root label.
+ * @return true if d1 is a subdomain of d2, but not equal to d2.
+ */
+int dname_strict_subdomain(uint8_t* d1, int labs1, uint8_t* d2, int labs2);
+
+/**
+ * Like dname_strict_subdomain but counts labels 
+ * @param d1: domain name, uncompressed wireformat
+ * @param d2: domain name, uncompressed wireformat
+ * @return true if d1 is a subdomain of d2, but not equal to d2.
+ */
+int dname_strict_subdomain_c(uint8_t* d1, uint8_t* d2);
+
+/**
+ * Counts labels. Tests is d1 is a subdomain of d2.
+ * @param d1: domain name, uncompressed wireformat
+ * @param d2: domain name, uncompressed wireformat
+ * @return true if d1 is a subdomain of d2.
+ */
+int dname_subdomain_c(uint8_t* d1, uint8_t* d2);
+
+/** 
+ * Debug helper. Print wireformat dname to output. 
+ * @param out: like stdout or a file.
+ * @param pkt: if not NULL, the packet for resolving compression ptrs.
+ * @param dname: pointer to (start of) dname.
+ */
+void dname_print(FILE* out, ldns_buffer* pkt, uint8_t* dname);
+
+/** 
+ * Debug helper. Print dname to given string buffer (string buffer must
+ * be at least 255 chars + 1 for the 0, in printable form.
+ * This may lose information (? for nonprintable characters, or & if
+ * the name is too long, # for a bad label length).
+ * @param dname: uncompressed wireformat.
+ * @param str: buffer of 255+1 length.
+ */
+void dname_str(uint8_t* dname, char* str);
+
+/**
+ * Returns true if the uncompressed wireformat dname is the root "."
+ * @param dname: the dname to check
+ * @return true if ".", false if not.
+ */
+int dname_is_root(uint8_t* dname);
+
+/**
+ * Snip off first label from a dname, returning the parent zone.
+ * @param dname: from what to strip off. uncompressed wireformat.
+ * @param len: length, adjusted to become less.
+ * @return stripped off, or "." if input was ".".
+ */
+void dname_remove_label(uint8_t** dname, size_t* len);
+
+/**
+ * Snip off first N labels from a dname, returning the parent zone.
+ * @param dname: from what to strip off. uncompressed wireformat.
+ * @param len: length, adjusted to become less.
+ * @param n: number of labels to strip off (from the left).
+ * 	if 0, nothing happens.
+ * @return stripped off, or "." if input was ".".
+ */
+void dname_remove_labels(uint8_t** dname, size_t* len, int n);
+
+/**
+ * Count labels for the RRSIG signature label field.
+ * Like a normal labelcount, but "*" wildcard and "." root are not counted.
+ * @param dname: valid uncompressed wireformat.
+ * @return number of labels like in RRSIG; '*' and '.' are not counted.
+ */
+int dname_signame_label_count(uint8_t* dname);
+
+/**
+ * Return true if the label is a wildcard, *.example.com.
+ * @param dname: valid uncompressed wireformat.
+ * @return true if wildcard, or false.
+ */
+int dname_is_wild(uint8_t* dname);
+
+/**
+ * Compare dnames, Canonical in rfc4034 sense, but by label.
+ * Such that zone contents follows zone apex.
+ *
+ * @param d1: first dname. pointer to uncompressed wireformat.
+ * @param labs1: number of labels in first dname.
+ * @param d2: second dname. pointer to uncompressed wireformat.
+ * @param labs2: number of labels in second dname.
+ * @param mlabs: number of labels that matched exactly (the shared topdomain).
+ * @return: 0 for equal, -1 smaller, or +1 d1 larger than d2.
+ */
+int dname_canon_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, 
+	int* mlabs);
+
+/**
+ * Canonical dname compare. Takes care of counting labels.
+ * Per rfc 4034 canonical order.
+ *
+ * @param d1: first dname. pointer to uncompressed wireformat.
+ * @param d2: second dname. pointer to uncompressed wireformat.
+ * @return: 0 for equal, -1 smaller, or +1 d1 larger than d2.
+ */
+int dname_canonical_compare(uint8_t* d1, uint8_t* d2);
+
+/**
+ * Get the shared topdomain between two names. Root "." or longer.
+ * @param d1: first dname. pointer to uncompressed wireformat.
+ * @param d2: second dname. pointer to uncompressed wireformat.
+ * @return pointer to shared topdomain. Ptr to a part of d1.
+ */
+uint8_t* dname_get_shared_topdomain(uint8_t* d1, uint8_t* d2);
+
+#endif /* UTIL_DATA_DNAME_H */
diff --git a/3rdParty/Unbound/src/src/util/data/msgencode.c b/3rdParty/Unbound/src/src/util/data/msgencode.c
new file mode 100644
index 0000000..a48a0a9
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/data/msgencode.c
@@ -0,0 +1,806 @@
+/*
+ * util/data/msgencode.c - Encode DNS messages, queries and replies.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains a routines to encode DNS messages.
+ */
+
+#include "config.h"
+#include <ldns/wire2host.h>
+#include "util/data/msgencode.h"
+#include "util/data/msgreply.h"
+#include "util/data/msgparse.h"
+#include "util/data/dname.h"
+#include "util/log.h"
+#include "util/regional.h"
+#include "util/net_help.h"
+
+/** return code that means the function ran out of memory. negative so it does
+ * not conflict with DNS rcodes. */
+#define RETVAL_OUTMEM	-2
+/** return code that means the data did not fit (completely) in the packet */
+#define RETVAL_TRUNC	-4
+/** return code that means all is peachy keen. Equal to DNS rcode NOERROR */
+#define RETVAL_OK	0
+
+/**
+ * Data structure to help domain name compression in outgoing messages.
+ * A tree of dnames and their offsets in the packet is kept.
+ * It is kept sorted, not canonical, but by label at least, so that after
+ * a lookup of a name you know its closest match, and the parent from that
+ * closest match. These are possible compression targets.
+ *
+ * It is a binary tree, not a rbtree or balanced tree, as the effort
+ * of keeping it balanced probably outweighs usefulness (given typical
+ * DNS packet size).
+ */
+struct compress_tree_node {
+	/** left node in tree, all smaller to this */
+	struct compress_tree_node* left;
+	/** right node in tree, all larger than this */
+	struct compress_tree_node* right;
+
+	/** the parent node - not for tree, but zone parent. One less label */
+	struct compress_tree_node* parent;
+	/** the domain name for this node. Pointer to uncompressed memory. */
+	uint8_t* dname;
+	/** number of labels in domain name, kept to help compare func. */
+	int labs;
+	/** offset in packet that points to this dname */
+	size_t offset;
+};
+
+/**
+ * Find domain name in tree, returns exact and closest match.
+ * @param tree: root of tree.
+ * @param dname: pointer to uncompressed dname.
+ * @param labs: number of labels in domain name.
+ * @param match: closest or exact match.
+ *	guaranteed to be smaller or equal to the sought dname.
+ *	can be null if the tree is empty.
+ * @param matchlabels: number of labels that match with closest match.
+ *	can be zero is there is no match.
+ * @param insertpt: insert location for dname, if not found.
+ * @return: 0 if no exact match.
+ */
+static int
+compress_tree_search(struct compress_tree_node** tree, uint8_t* dname,
+	int labs, struct compress_tree_node** match, int* matchlabels,
+	struct compress_tree_node*** insertpt)
+{
+	int c, n, closen=0;
+	struct compress_tree_node* p = *tree;
+	struct compress_tree_node* close = 0;
+	struct compress_tree_node** prev = tree;
+	while(p) {
+		if((c = dname_lab_cmp(dname, labs, p->dname, p->labs, &n)) 
+			== 0) {
+			*matchlabels = n;
+			*match = p;
+			return 1;
+		}
+		if(c<0) {
+			prev = &p->left;
+			p = p->left;
+		} else	{
+			closen = n;
+			close = p; /* p->dname is smaller than dname */
+			prev = &p->right;
+			p = p->right;
+		}
+	}
+	*insertpt = prev;
+	*matchlabels = closen;
+	*match = close;
+	return 0;
+}
+
+/**
+ * Lookup a domain name in compression tree.
+ * @param tree: root of tree (not the node with '.').
+ * @param dname: pointer to uncompressed dname.
+ * @param labs: number of labels in domain name.
+ * @param insertpt: insert location for dname, if not found.
+ * @return: 0 if not found or compress treenode with best compression.
+ */
+static struct compress_tree_node*
+compress_tree_lookup(struct compress_tree_node** tree, uint8_t* dname,
+	int labs, struct compress_tree_node*** insertpt)
+{
+	struct compress_tree_node* p;
+	int m;
+	if(labs <= 1)
+		return 0; /* do not compress root node */
+	if(compress_tree_search(tree, dname, labs, &p, &m, insertpt)) {
+		/* exact match */
+		return p;
+	}
+	/* return some ancestor of p that compresses well. */
+	if(m>1) {
+		/* www.example.com. (labs=4) matched foo.example.com.(labs=4)
+		 * then matchcount = 3. need to go up. */
+		while(p && p->labs > m)
+			p = p->parent;
+		return p;
+	}
+	return 0;
+}
+
+/**
+ * Create node for domain name compression tree.
+ * @param dname: pointer to uncompressed dname (stored in tree).
+ * @param labs: number of labels in dname.
+ * @param offset: offset into packet for dname.
+ * @param region: how to allocate memory for new node.
+ * @return new node or 0 on malloc failure.
+ */
+static struct compress_tree_node*
+compress_tree_newnode(uint8_t* dname, int labs, size_t offset, 
+	struct regional* region)
+{
+	struct compress_tree_node* n = (struct compress_tree_node*)
+		regional_alloc(region, sizeof(struct compress_tree_node));
+	if(!n) return 0;
+	n->left = 0;
+	n->right = 0;
+	n->parent = 0;
+	n->dname = dname;
+	n->labs = labs;
+	n->offset = offset;
+	return n;
+}
+
+/**
+ * Store domain name and ancestors into compression tree.
+ * @param dname: pointer to uncompressed dname (stored in tree).
+ * @param labs: number of labels in dname.
+ * @param offset: offset into packet for dname.
+ * @param region: how to allocate memory for new node.
+ * @param closest: match from previous lookup, used to compress dname.
+ *	may be NULL if no previous match.
+ *	if the tree has an ancestor of dname already, this must be it.
+ * @param insertpt: where to insert the dname in tree. 
+ * @return: 0 on memory error.
+ */
+static int
+compress_tree_store(uint8_t* dname, int labs, size_t offset, 
+	struct regional* region, struct compress_tree_node* closest, 
+	struct compress_tree_node** insertpt)
+{
+	uint8_t lablen;
+	struct compress_tree_node* newnode;
+	struct compress_tree_node* prevnode = NULL;
+	int uplabs = labs-1; /* does not store root in tree */
+	if(closest) uplabs = labs - closest->labs;
+	log_assert(uplabs >= 0);
+	/* algorithms builds up a vine of dname-labels to hang into tree */
+	while(uplabs--) {
+		if(offset > PTR_MAX_OFFSET) {
+			/* insertion failed, drop vine */
+			return 1; /* compression pointer no longer useful */
+		}
+		if(!(newnode = compress_tree_newnode(dname, labs, offset, 
+			region))) {
+			/* insertion failed, drop vine */
+			return 0;
+		}
+
+		if(prevnode) {
+			/* chain nodes together, last one has one label more,
+			 * so is larger than newnode, thus goes right. */
+			newnode->right = prevnode;
+			prevnode->parent = newnode;
+		}
+
+		/* next label */
+		lablen = *dname++;
+		dname += lablen;
+		offset += lablen+1;
+		prevnode = newnode;
+		labs--;
+	}
+	/* if we have a vine, hang the vine into the tree */
+	if(prevnode) {
+		*insertpt = prevnode;
+		prevnode->parent = closest;
+	}
+	return 1;
+}
+
+/** compress a domain name */
+static int
+write_compressed_dname(ldns_buffer* pkt, uint8_t* dname, int labs,
+	struct compress_tree_node* p)
+{
+	/* compress it */
+	int labcopy = labs - p->labs;
+	uint8_t lablen;
+	uint16_t ptr;
+
+	if(labs == 1) {
+		/* write root label */
+		if(ldns_buffer_remaining(pkt) < 1)
+			return 0;
+		ldns_buffer_write_u8(pkt, 0);
+		return 1;
+	}
+
+	/* copy the first couple of labels */
+	while(labcopy--) {
+		lablen = *dname++;
+		if(ldns_buffer_remaining(pkt) < (size_t)lablen+1)
+			return 0;
+		ldns_buffer_write_u8(pkt, lablen);
+		ldns_buffer_write(pkt, dname, lablen);
+		dname += lablen;
+	}
+	/* insert compression ptr */
+	if(ldns_buffer_remaining(pkt) < 2)
+		return 0;
+	ptr = PTR_CREATE(p->offset);
+	ldns_buffer_write_u16(pkt, ptr);
+	return 1;
+}
+
+/** compress owner name of RR, return RETVAL_OUTMEM RETVAL_TRUNC */
+static int
+compress_owner(struct ub_packed_rrset_key* key, ldns_buffer* pkt, 
+	struct regional* region, struct compress_tree_node** tree, 
+	size_t owner_pos, uint16_t* owner_ptr, int owner_labs)
+{
+	struct compress_tree_node* p;
+	struct compress_tree_node** insertpt;
+	if(!*owner_ptr) {
+		/* compress first time dname */
+		if((p = compress_tree_lookup(tree, key->rk.dname, 
+			owner_labs, &insertpt))) {
+			if(p->labs == owner_labs) 
+				/* avoid ptr chains, since some software is
+				 * not capable of decoding ptr after a ptr. */
+				*owner_ptr = htons(PTR_CREATE(p->offset));
+			if(!write_compressed_dname(pkt, key->rk.dname, 
+				owner_labs, p))
+				return RETVAL_TRUNC;
+			/* check if typeclass+4 ttl + rdatalen is available */
+			if(ldns_buffer_remaining(pkt) < 4+4+2)
+				return RETVAL_TRUNC;
+		} else {
+			/* no compress */
+			if(ldns_buffer_remaining(pkt) < key->rk.dname_len+4+4+2)
+				return RETVAL_TRUNC;
+			ldns_buffer_write(pkt, key->rk.dname, 
+				key->rk.dname_len);
+			if(owner_pos <= PTR_MAX_OFFSET)
+				*owner_ptr = htons(PTR_CREATE(owner_pos));
+		}
+		if(!compress_tree_store(key->rk.dname, owner_labs, 
+			owner_pos, region, p, insertpt))
+			return RETVAL_OUTMEM;
+	} else {
+		/* always compress 2nd-further RRs in RRset */
+		if(owner_labs == 1) {
+			if(ldns_buffer_remaining(pkt) < 1+4+4+2) 
+				return RETVAL_TRUNC;
+			ldns_buffer_write_u8(pkt, 0);
+		} else {
+			if(ldns_buffer_remaining(pkt) < 2+4+4+2) 
+				return RETVAL_TRUNC;
+			ldns_buffer_write(pkt, owner_ptr, 2);
+		}
+	}
+	return RETVAL_OK;
+}
+
+/** compress any domain name to the packet, return RETVAL_* */
+static int
+compress_any_dname(uint8_t* dname, ldns_buffer* pkt, int labs, 
+	struct regional* region, struct compress_tree_node** tree)
+{
+	struct compress_tree_node* p;
+	struct compress_tree_node** insertpt = NULL;
+	size_t pos = ldns_buffer_position(pkt);
+	if((p = compress_tree_lookup(tree, dname, labs, &insertpt))) {
+		if(!write_compressed_dname(pkt, dname, labs, p))
+			return RETVAL_TRUNC;
+	} else {
+		if(!dname_buffer_write(pkt, dname))
+			return RETVAL_TRUNC;
+	}
+	if(!compress_tree_store(dname, labs, pos, region, p, insertpt))
+		return RETVAL_OUTMEM;
+	return RETVAL_OK;
+}
+
+/** return true if type needs domain name compression in rdata */
+static const ldns_rr_descriptor*
+type_rdata_compressable(struct ub_packed_rrset_key* key)
+{
+	uint16_t t = ntohs(key->rk.type);
+	if(ldns_rr_descript(t) && 
+		ldns_rr_descript(t)->_compress == LDNS_RR_COMPRESS)
+		return ldns_rr_descript(t);
+	return 0;
+}
+
+/** compress domain names in rdata, return RETVAL_* */
+static int
+compress_rdata(ldns_buffer* pkt, uint8_t* rdata, size_t todolen, 
+	struct regional* region, struct compress_tree_node** tree, 
+	const ldns_rr_descriptor* desc)
+{
+	int labs, r, rdf = 0;
+	size_t dname_len, len, pos = ldns_buffer_position(pkt);
+	uint8_t count = desc->_dname_count;
+
+	ldns_buffer_skip(pkt, 2); /* rdata len fill in later */
+	/* space for rdatalen checked for already */
+	rdata += 2;
+	todolen -= 2;
+	while(todolen > 0 && count) {
+		switch(desc->_wireformat[rdf]) {
+		case LDNS_RDF_TYPE_DNAME:
+			labs = dname_count_size_labels(rdata, &dname_len);
+			if((r=compress_any_dname(rdata, pkt, labs, region, 
+				tree)) != RETVAL_OK)
+				return r;
+			rdata += dname_len;
+			todolen -= dname_len;
+			count--;
+			len = 0;
+			break;
+		case LDNS_RDF_TYPE_STR:
+			len = *rdata + 1;
+			break;
+		default:
+			len = get_rdf_size(desc->_wireformat[rdf]);
+		}
+		if(len) {
+			/* copy over */
+			if(ldns_buffer_remaining(pkt) < len)
+				return RETVAL_TRUNC;
+			ldns_buffer_write(pkt, rdata, len);
+			todolen -= len;
+			rdata += len;
+		}
+		rdf++;
+	}
+	/* copy remainder */
+	if(todolen > 0) {
+		if(ldns_buffer_remaining(pkt) < todolen)
+			return RETVAL_TRUNC;
+		ldns_buffer_write(pkt, rdata, todolen);
+	}
+
+	/* set rdata len */
+	ldns_buffer_write_u16_at(pkt, pos, ldns_buffer_position(pkt)-pos-2);
+	return RETVAL_OK;
+}
+
+/** Returns true if RR type should be included */
+static int
+rrset_belongs_in_reply(ldns_pkt_section s, uint16_t rrtype, uint16_t qtype, 
+	int dnssec)
+{
+	if(dnssec)
+		return 1;
+	/* skip non DNSSEC types, except if directly queried for */
+	if(s == LDNS_SECTION_ANSWER) {
+		if(qtype == LDNS_RR_TYPE_ANY || qtype == rrtype)
+			return 1;
+	}
+	/* check DNSSEC-ness */
+	switch(rrtype) {
+		case LDNS_RR_TYPE_SIG:
+		case LDNS_RR_TYPE_KEY:
+		case LDNS_RR_TYPE_NXT:
+		case LDNS_RR_TYPE_DS:
+		case LDNS_RR_TYPE_RRSIG:
+		case LDNS_RR_TYPE_NSEC:
+		case LDNS_RR_TYPE_DNSKEY:
+		case LDNS_RR_TYPE_NSEC3:
+		case LDNS_RR_TYPE_NSEC3PARAMS:
+			return 0;
+	}
+	return 1;
+}
+
+/** store rrset in buffer in wireformat, return RETVAL_* */
+static int
+packed_rrset_encode(struct ub_packed_rrset_key* key, ldns_buffer* pkt, 
+	uint16_t* num_rrs, uint32_t timenow, struct regional* region,
+	int do_data, int do_sig, struct compress_tree_node** tree,
+	ldns_pkt_section s, uint16_t qtype, int dnssec)
+{
+	size_t i, owner_pos;
+	int r, owner_labs;
+	uint16_t owner_ptr = 0;
+	struct packed_rrset_data* data = (struct packed_rrset_data*)
+		key->entry.data;
+	
+	/* does this RR type belong in the answer? */
+	if(!rrset_belongs_in_reply(s, ntohs(key->rk.type), qtype, dnssec))
+		return RETVAL_OK;
+
+	owner_labs = dname_count_labels(key->rk.dname);
+	owner_pos = ldns_buffer_position(pkt);
+
+	if(do_data) {
+		const ldns_rr_descriptor* c = type_rdata_compressable(key);
+		for(i=0; i<data->count; i++) {
+			if((r=compress_owner(key, pkt, region, tree, 
+				owner_pos, &owner_ptr, owner_labs))
+				!= RETVAL_OK)
+				return r;
+			ldns_buffer_write(pkt, &key->rk.type, 2);
+			ldns_buffer_write(pkt, &key->rk.rrset_class, 2);
+			if(data->rr_ttl[i] < timenow)
+				ldns_buffer_write_u32(pkt, 0);
+			else 	ldns_buffer_write_u32(pkt, 
+					data->rr_ttl[i]-timenow);
+			if(c) {
+				if((r=compress_rdata(pkt, data->rr_data[i],
+					data->rr_len[i], region, tree, c))
+					!= RETVAL_OK)
+					return r;
+			} else {
+				if(ldns_buffer_remaining(pkt) < data->rr_len[i])
+					return RETVAL_TRUNC;
+				ldns_buffer_write(pkt, data->rr_data[i], 
+					data->rr_len[i]);
+			}
+		}
+	}
+	/* insert rrsigs */
+	if(do_sig && dnssec) {
+		size_t total = data->count+data->rrsig_count;
+		for(i=data->count; i<total; i++) {
+			if(owner_ptr && owner_labs != 1) {
+				if(ldns_buffer_remaining(pkt) <
+					2+4+4+data->rr_len[i]) 
+					return RETVAL_TRUNC;
+				ldns_buffer_write(pkt, &owner_ptr, 2);
+			} else {
+				if((r=compress_any_dname(key->rk.dname, 
+					pkt, owner_labs, region, tree))
+					!= RETVAL_OK)
+					return r;
+				if(ldns_buffer_remaining(pkt) < 
+					4+4+data->rr_len[i])
+					return RETVAL_TRUNC;
+			}
+			ldns_buffer_write_u16(pkt, LDNS_RR_TYPE_RRSIG);
+			ldns_buffer_write(pkt, &key->rk.rrset_class, 2);
+			if(data->rr_ttl[i] < timenow)
+				ldns_buffer_write_u32(pkt, 0);
+			else 	ldns_buffer_write_u32(pkt, 
+					data->rr_ttl[i]-timenow);
+			/* rrsig rdata cannot be compressed, perform 100+ byte
+			 * memcopy. */
+			ldns_buffer_write(pkt, data->rr_data[i],
+				data->rr_len[i]);
+		}
+	}
+	/* change rrnum only after we are sure it fits */
+	if(do_data)
+		*num_rrs += data->count;
+	if(do_sig && dnssec)
+		*num_rrs += data->rrsig_count;
+
+	return RETVAL_OK;
+}
+
+/** store msg section in wireformat buffer, return RETVAL_* */
+static int
+insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs,
+	ldns_buffer* pkt, size_t rrsets_before, uint32_t timenow, 
+	struct regional* region, struct compress_tree_node** tree,
+	ldns_pkt_section s, uint16_t qtype, int dnssec)
+{
+	int r;
+	size_t i, setstart;
+	*num_rrs = 0;
+	if(s != LDNS_SECTION_ADDITIONAL) {
+		if(s == LDNS_SECTION_ANSWER && qtype == LDNS_RR_TYPE_ANY)
+			dnssec = 1; /* include all types in ANY answer */
+	  	for(i=0; i<num_rrsets; i++) {
+			setstart = ldns_buffer_position(pkt);
+			if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i], 
+				pkt, num_rrs, timenow, region, 1, 1, tree,
+				s, qtype, dnssec))
+				!= RETVAL_OK) {
+				/* Bad, but if due to size must set TC bit */
+				/* trim off the rrset neatly. */
+				ldns_buffer_set_position(pkt, setstart);
+				return r;
+			}
+		}
+	} else {
+	  	for(i=0; i<num_rrsets; i++) {
+			setstart = ldns_buffer_position(pkt);
+			if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i], 
+				pkt, num_rrs, timenow, region, 1, 0, tree,
+				s, qtype, dnssec))
+				!= RETVAL_OK) {
+				ldns_buffer_set_position(pkt, setstart);
+				return r;
+			}
+		}
+		if(dnssec)
+	  	  for(i=0; i<num_rrsets; i++) {
+			setstart = ldns_buffer_position(pkt);
+			if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i], 
+				pkt, num_rrs, timenow, region, 0, 1, tree,
+				s, qtype, dnssec))
+				!= RETVAL_OK) {
+				ldns_buffer_set_position(pkt, setstart);
+				return r;
+			}
+		  }
+	}
+	return RETVAL_OK;
+}
+
+/** store query section in wireformat buffer, return RETVAL */
+static int
+insert_query(struct query_info* qinfo, struct compress_tree_node** tree, 
+	ldns_buffer* buffer, struct regional* region)
+{
+	if(ldns_buffer_remaining(buffer) < 
+		qinfo->qname_len+sizeof(uint16_t)*2)
+		return RETVAL_TRUNC; /* buffer too small */
+	/* the query is the first name inserted into the tree */
+	if(!compress_tree_store(qinfo->qname, 
+		dname_count_labels(qinfo->qname), 
+		ldns_buffer_position(buffer), region, NULL, tree))
+		return RETVAL_OUTMEM;
+	if(ldns_buffer_current(buffer) == qinfo->qname)
+		ldns_buffer_skip(buffer, (ssize_t)qinfo->qname_len);
+	else	ldns_buffer_write(buffer, qinfo->qname, qinfo->qname_len);
+	ldns_buffer_write_u16(buffer, qinfo->qtype);
+	ldns_buffer_write_u16(buffer, qinfo->qclass);
+	return RETVAL_OK;
+}
+
+int 
+reply_info_encode(struct query_info* qinfo, struct reply_info* rep, 
+	uint16_t id, uint16_t flags, ldns_buffer* buffer, uint32_t timenow, 
+	struct regional* region, uint16_t udpsize, int dnssec)
+{
+	uint16_t ancount=0, nscount=0, arcount=0;
+	struct compress_tree_node* tree = 0;
+	int r;
+
+	ldns_buffer_clear(buffer);
+	if(udpsize < ldns_buffer_limit(buffer))
+		ldns_buffer_set_limit(buffer, udpsize);
+	if(ldns_buffer_remaining(buffer) < LDNS_HEADER_SIZE)
+		return 0;
+
+	ldns_buffer_write(buffer, &id, sizeof(uint16_t));
+	ldns_buffer_write_u16(buffer, flags);
+	ldns_buffer_write_u16(buffer, rep->qdcount);
+	/* set an, ns, ar counts to zero in case of small packets */
+	ldns_buffer_write(buffer, "\000\000\000\000\000\000", 6);
+
+	/* insert query section */
+	if(rep->qdcount) {
+		if((r=insert_query(qinfo, &tree, buffer, region)) != 
+			RETVAL_OK) {
+			if(r == RETVAL_TRUNC) {
+				/* create truncated message */
+				ldns_buffer_write_u16_at(buffer, 4, 0);
+				LDNS_TC_SET(ldns_buffer_begin(buffer));
+				ldns_buffer_flip(buffer);
+				return 1;
+			}
+			return 0;
+		}
+	}
+
+	/* insert answer section */
+	if((r=insert_section(rep, rep->an_numrrsets, &ancount, buffer, 
+		0, timenow, region, &tree, LDNS_SECTION_ANSWER, qinfo->qtype, 
+		dnssec)) != RETVAL_OK) {
+		if(r == RETVAL_TRUNC) {
+			/* create truncated message */
+			ldns_buffer_write_u16_at(buffer, 6, ancount);
+			LDNS_TC_SET(ldns_buffer_begin(buffer));
+			ldns_buffer_flip(buffer);
+			return 1;
+		}
+		return 0;
+	}
+	ldns_buffer_write_u16_at(buffer, 6, ancount);
+
+	/* insert auth section */
+	if((r=insert_section(rep, rep->ns_numrrsets, &nscount, buffer, 
+		rep->an_numrrsets, timenow, region, &tree,
+		LDNS_SECTION_AUTHORITY, qinfo->qtype, dnssec)) != RETVAL_OK) {
+		if(r == RETVAL_TRUNC) {
+			/* create truncated message */
+			ldns_buffer_write_u16_at(buffer, 8, nscount);
+			LDNS_TC_SET(ldns_buffer_begin(buffer));
+			ldns_buffer_flip(buffer);
+			return 1;
+		}
+		return 0;
+	}
+	ldns_buffer_write_u16_at(buffer, 8, nscount);
+
+	/* insert add section */
+	if((r=insert_section(rep, rep->ar_numrrsets, &arcount, buffer, 
+		rep->an_numrrsets + rep->ns_numrrsets, timenow, region, 
+		&tree, LDNS_SECTION_ADDITIONAL, qinfo->qtype, 
+		dnssec)) != RETVAL_OK) {
+		if(r == RETVAL_TRUNC) {
+			/* no need to set TC bit, this is the additional */
+			ldns_buffer_write_u16_at(buffer, 10, arcount);
+			ldns_buffer_flip(buffer);
+			return 1;
+		}
+		return 0;
+	}
+	ldns_buffer_write_u16_at(buffer, 10, arcount);
+	ldns_buffer_flip(buffer);
+	return 1;
+}
+
+uint16_t
+calc_edns_field_size(struct edns_data* edns)
+{
+	if(!edns || !edns->edns_present) 
+		return 0;
+	/* domain root '.' + type + class + ttl + rdatalen(=0) */
+	return 1 + 2 + 2 + 4 + 2;
+}
+
+void
+attach_edns_record(ldns_buffer* pkt, struct edns_data* edns)
+{
+	size_t len;
+	if(!edns || !edns->edns_present)
+		return;
+	/* inc additional count */
+	ldns_buffer_write_u16_at(pkt, 10,
+		ldns_buffer_read_u16_at(pkt, 10) + 1);
+	len = ldns_buffer_limit(pkt);
+	ldns_buffer_clear(pkt);
+	ldns_buffer_set_position(pkt, len);
+	/* write EDNS record */
+	ldns_buffer_write_u8(pkt, 0); /* '.' label */
+	ldns_buffer_write_u16(pkt, LDNS_RR_TYPE_OPT); /* type */
+	ldns_buffer_write_u16(pkt, edns->udp_size); /* class */
+	ldns_buffer_write_u8(pkt, edns->ext_rcode); /* ttl */
+	ldns_buffer_write_u8(pkt, edns->edns_version);
+	ldns_buffer_write_u16(pkt, edns->bits);
+	ldns_buffer_write_u16(pkt, 0); /* rdatalen */
+	ldns_buffer_flip(pkt);
+}
+
+int 
+reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep, 
+	uint16_t id, uint16_t qflags, ldns_buffer* pkt, uint32_t timenow,
+	int cached, struct regional* region, uint16_t udpsize, 
+	struct edns_data* edns, int dnssec, int secure)
+{
+	uint16_t flags;
+	int attach_edns = 1;
+
+	if(!cached || rep->authoritative) {
+		/* original flags, copy RD and CD bits from query. */
+		flags = rep->flags | (qflags & (BIT_RD|BIT_CD)); 
+	} else {
+		/* remove AA bit, copy RD and CD bits from query. */
+		flags = (rep->flags & ~BIT_AA) | (qflags & (BIT_RD|BIT_CD)); 
+	}
+	if(secure && (dnssec || (qflags&BIT_AD)))
+		flags |= BIT_AD;
+	log_assert(flags & BIT_QR); /* QR bit must be on in our replies */
+	if(udpsize < LDNS_HEADER_SIZE)
+		return 0;
+	if(udpsize < LDNS_HEADER_SIZE + calc_edns_field_size(edns)) {
+		/* packet too small to contain edns, omit it. */
+		attach_edns = 0;
+	} else {
+		/* reserve space for edns record */
+		udpsize -= calc_edns_field_size(edns);
+	}
+
+	if(!reply_info_encode(qinf, rep, id, flags, pkt, timenow, region,
+		udpsize, dnssec)) {
+		log_err("reply encode: out of memory");
+		return 0;
+	}
+	if(attach_edns)
+		attach_edns_record(pkt, edns);
+	return 1;
+}
+
+void 
+qinfo_query_encode(ldns_buffer* pkt, struct query_info* qinfo)
+{
+	uint16_t flags = 0; /* QUERY, NOERROR */
+	ldns_buffer_clear(pkt);
+	log_assert(ldns_buffer_remaining(pkt) >= 12+255+4/*max query*/);
+	ldns_buffer_skip(pkt, 2); /* id done later */
+	ldns_buffer_write_u16(pkt, flags);
+	ldns_buffer_write_u16(pkt, 1); /* query count */
+	ldns_buffer_write(pkt, "\000\000\000\000\000\000", 6); /* counts */
+	ldns_buffer_write(pkt, qinfo->qname, qinfo->qname_len);
+	ldns_buffer_write_u16(pkt, qinfo->qtype);
+	ldns_buffer_write_u16(pkt, qinfo->qclass);
+	ldns_buffer_flip(pkt);
+}
+
+void 
+error_encode(ldns_buffer* buf, int r, struct query_info* qinfo,
+	uint16_t qid, uint16_t qflags, struct edns_data* edns)
+{
+	uint16_t flags;
+
+	ldns_buffer_clear(buf);
+	ldns_buffer_write(buf, &qid, sizeof(uint16_t));
+	flags = (uint16_t)(BIT_QR | BIT_RA | r); /* QR and retcode*/
+	flags |= (qflags & (BIT_RD|BIT_CD)); /* copy RD and CD bit */
+	ldns_buffer_write_u16(buf, flags);
+	if(qinfo) flags = 1;
+	else	flags = 0;
+	ldns_buffer_write_u16(buf, flags);
+	flags = 0;
+	ldns_buffer_write(buf, &flags, sizeof(uint16_t));
+	ldns_buffer_write(buf, &flags, sizeof(uint16_t));
+	ldns_buffer_write(buf, &flags, sizeof(uint16_t));
+	if(qinfo) {
+		if(ldns_buffer_current(buf) == qinfo->qname)
+			ldns_buffer_skip(buf, (ssize_t)qinfo->qname_len);
+		else	ldns_buffer_write(buf, qinfo->qname, qinfo->qname_len);
+		ldns_buffer_write_u16(buf, qinfo->qtype);
+		ldns_buffer_write_u16(buf, qinfo->qclass);
+	}
+	ldns_buffer_flip(buf);
+	if(edns) {
+		struct edns_data es = *edns;
+		es.edns_version = EDNS_ADVERTISED_VERSION;
+		es.udp_size = EDNS_ADVERTISED_SIZE;
+		es.ext_rcode = 0;
+		es.bits &= EDNS_DO;
+		if(ldns_buffer_limit(buf) + calc_edns_field_size(&es) >
+			edns->udp_size)
+			return;
+		attach_edns_record(buf, &es);
+	}
+}
diff --git a/3rdParty/Unbound/src/src/util/data/msgencode.h b/3rdParty/Unbound/src/src/util/data/msgencode.h
new file mode 100644
index 0000000..74d6c1f
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/data/msgencode.h
@@ -0,0 +1,130 @@
+/*
+ * util/data/msgencode.h - encode compressed DNS messages.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains temporary data structures and routines to create
+ * compressed DNS messages.
+ */
+
+#ifndef UTIL_DATA_MSGENCODE_H
+#define UTIL_DATA_MSGENCODE_H
+struct query_info;
+struct reply_info;
+struct regional;
+struct edns_data;
+
+/** 
+ * Generate answer from reply_info.
+ * @param qinf: query information that provides query section in packet.
+ * @param rep: reply to fill in.
+ * @param id: id word from the query.
+ * @param qflags: flags word from the query.
+ * @param dest: buffer to put message into; will truncate if it does not fit.
+ * @param timenow: time to subtract.
+ * @param cached: set true if a cached reply (so no AA bit).
+ *	set false for the first reply.
+ * @param region: where to allocate temp variables (for compression).
+ * @param udpsize: size of the answer, 512, from EDNS, or 64k for TCP.
+ * @param edns: EDNS data included in the answer, NULL for none.
+ *	or if edns_present = 0, it is not included.
+ * @param dnssec: if 0 DNSSEC records are omitted from the answer.
+ * @param secure: if 1, the AD bit is set in the reply.
+ * @return: 0 on error (server failure).
+ */
+int reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep, 
+	uint16_t id, uint16_t qflags, ldns_buffer* dest, uint32_t timenow,
+	int cached, struct regional* region, uint16_t udpsize, 
+	struct edns_data* edns, int dnssec, int secure);
+
+/**
+ * Regenerate the wireformat from the stored msg reply.
+ * If the buffer is too small then the message is truncated at a whole
+ * rrset and the TC bit set, or whole rrsets are left out of the additional
+ * and the TC bit is not set.
+ * @param qinfo: query info to store.
+ * @param rep: reply to store.
+ * @param id: id value to store, network order.
+ * @param flags: flags value to store, host order.
+ * @param buffer: buffer to store the packet into.
+ * @param timenow: time now, to adjust ttl values.
+ * @param region: to store temporary data in.
+ * @param udpsize: size of the answer, 512, from EDNS, or 64k for TCP.
+ * @param dnssec: if 0 DNSSEC records are omitted from the answer.
+ * @return: nonzero is success, or 
+ *	0 on error: malloc failure (no log_err has been done).
+ */
+int reply_info_encode(struct query_info* qinfo, struct reply_info* rep, 
+	uint16_t id, uint16_t flags, ldns_buffer* buffer, uint32_t timenow, 
+	struct regional* region, uint16_t udpsize, int dnssec);
+
+/**
+ * Encode query packet. Assumes the buffer is large enough.
+ * @param pkt: where to store the packet.
+ * @param qinfo: query info.
+ */
+void qinfo_query_encode(ldns_buffer* pkt, struct query_info* qinfo);
+
+/**
+ * Estimate size of EDNS record in packet. EDNS record will be no larger.
+ * @param edns: edns data or NULL.
+ * @return octets to reserve for EDNS.
+ */
+uint16_t calc_edns_field_size(struct edns_data* edns);
+
+/**
+ * Attach EDNS record to buffer. Buffer has complete packet. There must
+ * be enough room left for the EDNS record.
+ * @param pkt: packet added to.
+ * @param edns: if NULL or present=0, nothing is added to the packet.
+ */
+void attach_edns_record(ldns_buffer* pkt, struct edns_data* edns);
+
+/** 
+ * Encode an error. With QR and RA set.
+ *
+ * @param pkt: where to store the packet.
+ * @param r: RCODE value to encode.
+ * @param qinfo: if not NULL, the query is included.
+ * @param qid: query ID to set in packet. network order.
+ * @param qflags: original query flags (to copy RD and CD bits). host order.
+ * @param edns: if not NULL, this is the query edns info,
+ * 	and an edns reply is attached. Only attached if EDNS record fits reply.
+ */
+void error_encode(ldns_buffer* pkt, int r, struct query_info* qinfo,
+	uint16_t qid, uint16_t qflags, struct edns_data* edns);
+
+#endif /* UTIL_DATA_MSGENCODE_H */
diff --git a/3rdParty/Unbound/src/src/util/data/msgparse.c b/3rdParty/Unbound/src/src/util/data/msgparse.c
new file mode 100644
index 0000000..a03f543
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/data/msgparse.c
@@ -0,0 +1,1018 @@
+/* 
+ * util/data/msgparse.c - parse wireformat DNS messages.
+ * 
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ * 
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+/**
+ * \file
+ * Routines for message parsing a packet buffer to a descriptive structure.
+ */
+#include "config.h"
+#include <ldns/ldns.h>
+#include "util/data/msgparse.h"
+#include "util/net_help.h"
+#include "util/data/dname.h"
+#include "util/data/packed_rrset.h"
+#include "util/storage/lookup3.h"
+#include "util/regional.h"
+
+/** smart comparison of (compressed, valid) dnames from packet */
+static int
+smart_compare(ldns_buffer* pkt, uint8_t* dnow, 
+	uint8_t* dprfirst, uint8_t* dprlast)
+{
+	if(LABEL_IS_PTR(*dnow)) {
+		/* ptr points to a previous dname */
+		uint8_t* p = ldns_buffer_at(pkt, PTR_OFFSET(dnow[0], dnow[1]));
+		if( p == dprfirst || p == dprlast )
+			return 0;
+		/* prev dname is also a ptr, both ptrs are the same. */
+		if(LABEL_IS_PTR(*dprlast) &&
+			dprlast[0] == dnow[0] && dprlast[1] == dnow[1])
+			return 0;
+	}
+	return dname_pkt_compare(pkt, dnow, dprlast);
+}
+
+/**
+ * Allocate new rrset in region, fill with data.
+ */
+static struct rrset_parse* 
+new_rrset(struct msg_parse* msg, uint8_t* dname, size_t dnamelen, 
+	uint16_t type, uint16_t dclass, hashvalue_t hash, 
+	uint32_t rrset_flags, ldns_pkt_section section, 
+	struct regional* region)
+{
+	struct rrset_parse* p = regional_alloc(region, sizeof(*p));
+	if(!p) return NULL;
+	p->rrset_bucket_next = msg->hashtable[hash & (PARSE_TABLE_SIZE-1)];
+	msg->hashtable[hash & (PARSE_TABLE_SIZE-1)] = p;
+	p->rrset_all_next = 0;
+	if(msg->rrset_last)
+		msg->rrset_last->rrset_all_next = p;
+	else 	msg->rrset_first = p;
+	msg->rrset_last = p;
+	p->hash = hash;
+	p->section = section;
+	p->dname = dname;
+	p->dname_len = dnamelen;
+	p->type = type;
+	p->rrset_class = dclass;
+	p->flags = rrset_flags;
+	p->rr_count = 0;
+	p->size = 0;
+	p->rr_first = 0;
+	p->rr_last = 0;
+	p->rrsig_count = 0;
+	p->rrsig_first = 0;
+	p->rrsig_last = 0;
+	return p;
+}
+
+/** See if next rrset is nsec at zone apex */
+static int
+nsec_at_apex(ldns_buffer* pkt)
+{
+	/* we are at ttl position in packet. */
+	size_t pos = ldns_buffer_position(pkt);
+	uint16_t rdatalen;
+	if(ldns_buffer_remaining(pkt) < 7) /* ttl+len+root */
+		return 0; /* eek! */
+	ldns_buffer_skip(pkt, 4); /* ttl */;
+	rdatalen = ldns_buffer_read_u16(pkt);
+	if(ldns_buffer_remaining(pkt) < rdatalen) {
+		ldns_buffer_set_position(pkt, pos);
+		return 0; /* parse error happens later */
+	}
+	/* must validate the nsec next domain name format */
+	if(pkt_dname_len(pkt) == 0) {
+		ldns_buffer_set_position(pkt, pos);
+		return 0; /* parse error */
+	}
+
+	/* see if SOA bit is set. */
+	if(ldns_buffer_position(pkt) < pos+4+rdatalen) {
+		/* nsec type bitmap contains items */
+		uint8_t win, blen, bits;
+		/* need: windownum, bitmap len, firstbyte */
+		if(ldns_buffer_position(pkt)+3 > pos+4+rdatalen) {
+			ldns_buffer_set_position(pkt, pos);
+			return 0; /* malformed nsec */
+		}
+		win = ldns_buffer_read_u8(pkt);
+		blen = ldns_buffer_read_u8(pkt);
+		bits = ldns_buffer_read_u8(pkt);
+		/* 0window always first window. bitlen >=1 or parse
+		   error really. bit 0x2 is SOA. */
+		if(win == 0 && blen >= 1 && (bits & 0x02)) {
+			ldns_buffer_set_position(pkt, pos);
+			return 1;
+		}
+	}
+
+	ldns_buffer_set_position(pkt, pos);
+	return 0;
+}
+
+/** Calculate rrset flags */
+static uint32_t
+pkt_rrset_flags(ldns_buffer* pkt, uint16_t type, ldns_pkt_section sec)
+{
+	uint32_t f = 0;
+	if(type == LDNS_RR_TYPE_NSEC && nsec_at_apex(pkt)) {
+		f |= PACKED_RRSET_NSEC_AT_APEX;
+	} else if(type == LDNS_RR_TYPE_SOA && sec == LDNS_SECTION_AUTHORITY) {
+		f |= PACKED_RRSET_SOA_NEG;
+	}
+	return f;
+}
+
+hashvalue_t
+pkt_hash_rrset(ldns_buffer* pkt, uint8_t* dname, uint16_t type, 
+	uint16_t dclass, uint32_t rrset_flags)
+{
+	/* note this MUST be identical to rrset_key_hash in packed_rrset.c */
+	/* this routine handles compressed names */
+	hashvalue_t h = 0xab;
+	h = dname_pkt_hash(pkt, dname, h);
+	h = hashlittle(&type, sizeof(type), h);		/* host order */
+	h = hashlittle(&dclass, sizeof(dclass), h);	/* netw order */
+	h = hashlittle(&rrset_flags, sizeof(uint32_t), h);
+	return h;
+}
+
+/** create partial dname hash for rrset hash */
+static hashvalue_t
+pkt_hash_rrset_first(ldns_buffer* pkt, uint8_t* dname)
+{
+	/* works together with pkt_hash_rrset_rest */
+	/* note this MUST be identical to rrset_key_hash in packed_rrset.c */
+	/* this routine handles compressed names */
+	hashvalue_t h = 0xab;
+	h = dname_pkt_hash(pkt, dname, h);
+	return h;
+}
+
+/** create a rrset hash from a partial dname hash */
+static hashvalue_t
+pkt_hash_rrset_rest(hashvalue_t dname_h, uint16_t type, uint16_t dclass, 
+	uint32_t rrset_flags)
+{
+	/* works together with pkt_hash_rrset_first */
+	/* note this MUST be identical to rrset_key_hash in packed_rrset.c */
+	hashvalue_t h;
+	h = hashlittle(&type, sizeof(type), dname_h);	/* host order */
+	h = hashlittle(&dclass, sizeof(dclass), h);	/* netw order */
+	h = hashlittle(&rrset_flags, sizeof(uint32_t), h);
+	return h;
+}
+
+/** compare rrset_parse with data */
+static int
+rrset_parse_equals(struct rrset_parse* p, ldns_buffer* pkt, hashvalue_t h, 
+	uint32_t rrset_flags, uint8_t* dname, size_t dnamelen, 
+	uint16_t type, uint16_t dclass)
+{
+	if(p->hash == h && p->dname_len == dnamelen && p->type == type &&
+		p->rrset_class == dclass && p->flags == rrset_flags &&
+		dname_pkt_compare(pkt, dname, p->dname) == 0)
+		return 1;
+	return 0;
+}
+
+
+struct rrset_parse*
+msgparse_hashtable_lookup(struct msg_parse* msg, ldns_buffer* pkt, 
+	hashvalue_t h, uint32_t rrset_flags, uint8_t* dname, size_t dnamelen, 
+	uint16_t type, uint16_t dclass)
+{
+	struct rrset_parse* p = msg->hashtable[h & (PARSE_TABLE_SIZE-1)];
+	while(p) {
+		if(rrset_parse_equals(p, pkt, h, rrset_flags, dname, dnamelen,
+			type, dclass))
+			return p;
+		p = p->rrset_bucket_next;
+	}
+	return NULL;
+}
+
+/** return type networkformat that rrsig in packet covers */
+static int
+pkt_rrsig_covered(ldns_buffer* pkt, uint8_t* here, uint16_t* type)
+{
+	size_t pos = ldns_buffer_position(pkt);
+	ldns_buffer_set_position(pkt, (size_t)(here-ldns_buffer_begin(pkt)));
+	/* ttl + len + size of small rrsig(rootlabel, no signature) */
+	if(ldns_buffer_remaining(pkt) < 4+2+19)
+		return 0;
+	ldns_buffer_skip(pkt, 4); /* ttl */
+	if(ldns_buffer_read_u16(pkt) < 19) /* too short */ {
+		ldns_buffer_set_position(pkt, pos);
+		return 0;
+	}
+	*type = ldns_buffer_read_u16(pkt);
+	ldns_buffer_set_position(pkt, pos);
+	return 1;
+}
+
+/** true if covered type equals prevtype */
+static int
+pkt_rrsig_covered_equals(ldns_buffer* pkt, uint8_t* here, uint16_t type)
+{
+	uint16_t t;
+	if(pkt_rrsig_covered(pkt, here, &t) && t == type)
+		return 1;
+	return 0;
+}
+
+void
+msgparse_bucket_remove(struct msg_parse* msg, struct rrset_parse* rrset)
+{
+	struct rrset_parse** p;
+	p = &msg->hashtable[ rrset->hash & (PARSE_TABLE_SIZE-1) ];
+	while(*p) {
+		if(*p == rrset) {
+			*p = rrset->rrset_bucket_next;
+			return;
+		}
+		p = &( (*p)->rrset_bucket_next );
+	}
+}
+
+/** change section of rrset from previous to current section */
+static void
+change_section(struct msg_parse* msg, struct rrset_parse* rrset,
+	ldns_pkt_section section)
+{
+	struct rrset_parse *p, *prev;
+	/* remove from list */
+	if(section == rrset->section)
+		return;
+	p = msg->rrset_first;
+	prev = 0;
+	while(p) {
+		if(p == rrset) {
+			if(prev) prev->rrset_all_next = p->rrset_all_next;
+			else	msg->rrset_first = p->rrset_all_next;
+			if(msg->rrset_last == rrset)
+				msg->rrset_last = prev;
+			break;
+		}
+		prev = p;
+		p = p->rrset_all_next;
+	}
+	/* remove from count */
+	switch(rrset->section) {
+		case LDNS_SECTION_ANSWER: msg->an_rrsets--; break;
+		case LDNS_SECTION_AUTHORITY: msg->ns_rrsets--; break;
+		case LDNS_SECTION_ADDITIONAL: msg->ar_rrsets--; break;
+		default: log_assert(0);
+	}
+	/* insert at end of list */
+	rrset->rrset_all_next = 0;
+	if(msg->rrset_last)
+		msg->rrset_last->rrset_all_next = rrset;
+	else	msg->rrset_first = rrset;
+	msg->rrset_last = rrset;
+	/* up count of new section */
+	switch(section) {
+		case LDNS_SECTION_AUTHORITY: msg->ns_rrsets++; break;
+		case LDNS_SECTION_ADDITIONAL: msg->ar_rrsets++; break;
+		default: log_assert(0);
+	}
+	rrset->section = section;
+}
+
+/** see if rrset of type RRSIG contains sig over given type */
+static int
+rrset_has_sigover(ldns_buffer* pkt, struct rrset_parse* rrset, uint16_t type,
+	int* hasother)
+{
+	int res = 0;
+	struct rr_parse* rr = rrset->rr_first;
+	log_assert( rrset->type == LDNS_RR_TYPE_RRSIG );
+	while(rr) {
+		if(pkt_rrsig_covered_equals(pkt, rr->ttl_data, type))
+			res = 1;
+		else	*hasother = 1;
+		rr = rr->next;
+	}
+	return res;
+}
+
+/** move rrsigs from sigset to dataset */
+static int
+moveover_rrsigs(ldns_buffer* pkt, struct regional* region, 
+	struct rrset_parse* sigset, struct rrset_parse* dataset, int duplicate)
+{
+	struct rr_parse* sig = sigset->rr_first;
+	struct rr_parse* prev = NULL;
+	struct rr_parse* insert;
+	struct rr_parse* nextsig;
+	while(sig) {
+		nextsig = sig->next;
+		if(pkt_rrsig_covered_equals(pkt, sig->ttl_data, 
+			dataset->type)) {
+			if(duplicate) {
+				/* new */
+				insert = (struct rr_parse*)regional_alloc(
+					region, sizeof(struct rr_parse));
+				if(!insert) return 0;
+				insert->outside_packet = 0;
+				insert->ttl_data = sig->ttl_data;
+				insert->size = sig->size;
+				/* prev not used */
+			} else {
+				/* remove from sigset */
+				if(prev) prev->next = sig->next;
+				else	sigset->rr_first = sig->next;
+				if(sigset->rr_last == sig)
+					sigset->rr_last = prev;
+				sigset->rr_count--;
+				sigset->size -= sig->size;
+				insert = sig;
+				/* prev not changed */
+			}
+			/* add to dataset */
+			dataset->rrsig_count++;
+			insert->next = 0;
+			if(dataset->rrsig_last) 
+				dataset->rrsig_last->next = insert;
+			else	dataset->rrsig_first = insert;
+			dataset->rrsig_last = insert;
+			dataset->size += insert->size;
+		} else  {
+			prev = sig;
+		}
+		sig = nextsig;
+	}
+	return 1;
+}
+
+/** change an rrsig rrset for use as data rrset */
+static struct rrset_parse*
+change_rrsig_rrset(struct rrset_parse* sigset, struct msg_parse* msg, 
+	ldns_buffer* pkt, uint16_t datatype, uint32_t rrset_flags,
+	int hasother, ldns_pkt_section section, struct regional* region)
+{
+	struct rrset_parse* dataset = sigset;
+	hashvalue_t hash = pkt_hash_rrset(pkt, sigset->dname, datatype, 
+		sigset->rrset_class, rrset_flags);
+	log_assert( sigset->type == LDNS_RR_TYPE_RRSIG );
+	log_assert( datatype != LDNS_RR_TYPE_RRSIG );
+	if(hasother) {
+		/* need to make new rrset to hold data type */
+		dataset = new_rrset(msg, sigset->dname, sigset->dname_len, 
+			datatype, sigset->rrset_class, hash, rrset_flags, 
+			section, region);
+		if(!dataset) 
+			return NULL;
+		switch(section) {
+			case LDNS_SECTION_ANSWER: msg->an_rrsets++; break;
+			case LDNS_SECTION_AUTHORITY: msg->ns_rrsets++; break;
+			case LDNS_SECTION_ADDITIONAL: msg->ar_rrsets++; break;
+			default: log_assert(0);
+		}
+		if(!moveover_rrsigs(pkt, region, sigset, dataset, 
+			msg->qtype == LDNS_RR_TYPE_RRSIG ||
+			(msg->qtype == LDNS_RR_TYPE_ANY &&
+			section != LDNS_SECTION_ANSWER) ))
+			return NULL;
+		return dataset;
+	}
+	/* changeover the type of the rrset to data set */
+	msgparse_bucket_remove(msg, dataset);
+	/* insert into new hash bucket */
+	dataset->rrset_bucket_next = msg->hashtable[hash&(PARSE_TABLE_SIZE-1)];
+	msg->hashtable[hash&(PARSE_TABLE_SIZE-1)] = dataset;
+	dataset->hash = hash;
+	/* use section of data item for result */
+	change_section(msg, dataset, section);
+	dataset->type = datatype;
+	dataset->flags = rrset_flags;
+	dataset->rrsig_count += dataset->rr_count;
+	dataset->rr_count = 0;
+	/* move sigs to end of siglist */
+	if(dataset->rrsig_last)
+		dataset->rrsig_last->next = dataset->rr_first;
+	else	dataset->rrsig_first = dataset->rr_first;
+	dataset->rrsig_last = dataset->rr_last;
+	dataset->rr_first = 0;
+	dataset->rr_last = 0;
+	return dataset;
+}
+
+/** Find rrset. If equal to previous it is fast. hash if not so.
+ * @param msg: the message with hash table.
+ * @param pkt: the packet in wireformat (needed for compression ptrs).
+ * @param dname: pointer to start of dname (compressed) in packet.
+ * @param dnamelen: uncompressed wirefmt length of dname.
+ * @param type: type of current rr.
+ * @param dclass: class of current rr.
+ * @param hash: hash value is returned if the rrset could not be found.
+ * @param rrset_flags: is returned if the rrset could not be found.
+ * @param prev_dname_first: dname of last seen RR. First seen dname.
+ * @param prev_dname_last: dname of last seen RR. Last seen dname.
+ * @param prev_dnamelen: dname len of last seen RR.
+ * @param prev_type: type of last seen RR.
+ * @param prev_dclass: class of last seen RR.
+ * @param rrset_prev: last seen RRset.
+ * @param section: the current section in the packet.
+ * @param region: used to allocate temporary parsing data.
+ * @return 0 on out of memory.
+ */
+static int
+find_rrset(struct msg_parse* msg, ldns_buffer* pkt, uint8_t* dname, 
+	size_t dnamelen, uint16_t type, uint16_t dclass, hashvalue_t* hash, 
+	uint32_t* rrset_flags,
+	uint8_t** prev_dname_first, uint8_t** prev_dname_last,
+	size_t* prev_dnamelen, uint16_t* prev_type,
+	uint16_t* prev_dclass, struct rrset_parse** rrset_prev,
+	ldns_pkt_section section, struct regional* region)
+{
+	hashvalue_t dname_h = pkt_hash_rrset_first(pkt, dname);
+	uint16_t covtype;
+	if(*rrset_prev) {
+		/* check if equal to previous item */
+		if(type == *prev_type && dclass == *prev_dclass &&
+			dnamelen == *prev_dnamelen &&
+			smart_compare(pkt, dname, *prev_dname_first, 
+				*prev_dname_last) == 0 &&
+			type != LDNS_RR_TYPE_RRSIG) {
+			/* same as previous */
+			*prev_dname_last = dname;
+			return 1;
+		}
+		/* check if rrsig over previous item */
+		if(type == LDNS_RR_TYPE_RRSIG && dclass == *prev_dclass &&
+			pkt_rrsig_covered_equals(pkt, ldns_buffer_current(pkt),
+				*prev_type) &&
+			smart_compare(pkt, dname, *prev_dname_first,
+				*prev_dname_last) == 0) {
+			/* covers previous */
+			*prev_dname_last = dname;
+			return 1;
+		}
+	}
+	/* find by hashing and lookup in hashtable */
+	*rrset_flags = pkt_rrset_flags(pkt, type, section);
+	
+	/* if rrsig - try to lookup matching data set first */
+	if(type == LDNS_RR_TYPE_RRSIG && pkt_rrsig_covered(pkt, 
+		ldns_buffer_current(pkt), &covtype)) {
+		*hash = pkt_hash_rrset_rest(dname_h, covtype, dclass, 
+			*rrset_flags);
+		*rrset_prev = msgparse_hashtable_lookup(msg, pkt, *hash, 
+			*rrset_flags, dname, dnamelen, covtype, dclass);
+		if(!*rrset_prev && covtype == LDNS_RR_TYPE_NSEC) {
+			/* if NSEC try with NSEC apex bit twiddled */
+			*rrset_flags ^= PACKED_RRSET_NSEC_AT_APEX;
+			*hash = pkt_hash_rrset_rest(dname_h, covtype, dclass, 
+				*rrset_flags);
+			*rrset_prev = msgparse_hashtable_lookup(msg, pkt, 
+				*hash, *rrset_flags, dname, dnamelen, covtype, 
+				dclass);
+			if(!*rrset_prev) /* untwiddle if not found */
+				*rrset_flags ^= PACKED_RRSET_NSEC_AT_APEX;
+		}
+		if(!*rrset_prev && covtype == LDNS_RR_TYPE_SOA) {
+			/* if SOA try with SOA neg flag twiddled */
+			*rrset_flags ^= PACKED_RRSET_SOA_NEG;
+			*hash = pkt_hash_rrset_rest(dname_h, covtype, dclass, 
+				*rrset_flags);
+			*rrset_prev = msgparse_hashtable_lookup(msg, pkt, 
+				*hash, *rrset_flags, dname, dnamelen, covtype, 
+				dclass);
+			if(!*rrset_prev) /* untwiddle if not found */
+				*rrset_flags ^= PACKED_RRSET_SOA_NEG;
+		}
+		if(*rrset_prev) {
+			*prev_dname_first = (*rrset_prev)->dname;
+			*prev_dname_last = dname;
+			*prev_dnamelen = dnamelen;
+			*prev_type = covtype;
+			*prev_dclass = dclass;
+			return 1;
+		}
+	}
+	if(type != LDNS_RR_TYPE_RRSIG) {
+		int hasother = 0;
+		/* find matching rrsig */
+		*hash = pkt_hash_rrset_rest(dname_h, LDNS_RR_TYPE_RRSIG, 
+			dclass, 0);
+		*rrset_prev = msgparse_hashtable_lookup(msg, pkt, *hash, 
+			0, dname, dnamelen, LDNS_RR_TYPE_RRSIG, 
+			dclass);
+		if(*rrset_prev && rrset_has_sigover(pkt, *rrset_prev, type,
+			&hasother)) {
+			/* yes! */
+			*prev_dname_first = (*rrset_prev)->dname;
+			*prev_dname_last = dname;
+			*prev_dnamelen = dnamelen;
+			*prev_type = type;
+			*prev_dclass = dclass;
+			*rrset_prev = change_rrsig_rrset(*rrset_prev, msg, 
+				pkt, type, *rrset_flags, hasother, section, 
+				region);
+			if(!*rrset_prev) return 0;
+			return 1;
+		}
+	}
+
+	*hash = pkt_hash_rrset_rest(dname_h, type, dclass, *rrset_flags);
+	*rrset_prev = msgparse_hashtable_lookup(msg, pkt, *hash, *rrset_flags, 
+		dname, dnamelen, type, dclass);
+	if(*rrset_prev)
+		*prev_dname_first = (*rrset_prev)->dname;
+	else 	*prev_dname_first = dname;
+	*prev_dname_last = dname;
+	*prev_dnamelen = dnamelen;
+	*prev_type = type;
+	*prev_dclass = dclass;
+	return 1;
+}
+
+/**
+ * Parse query section. 
+ * @param pkt: packet, position at call must be at start of query section.
+ *	at end position is after query section.
+ * @param msg: store results here.
+ * @return: 0 if OK, or rcode on error.
+ */
+static int
+parse_query_section(ldns_buffer* pkt, struct msg_parse* msg)
+{
+	if(msg->qdcount == 0)
+		return 0;
+	if(msg->qdcount > 1)
+		return LDNS_RCODE_FORMERR;
+	log_assert(msg->qdcount == 1);
+	if(ldns_buffer_remaining(pkt) <= 0)
+		return LDNS_RCODE_FORMERR;
+	msg->qname = ldns_buffer_current(pkt);
+	if((msg->qname_len = pkt_dname_len(pkt)) == 0)
+		return LDNS_RCODE_FORMERR;
+	if(ldns_buffer_remaining(pkt) < sizeof(uint16_t)*2)
+		return LDNS_RCODE_FORMERR;
+	msg->qtype = ldns_buffer_read_u16(pkt);
+	msg->qclass = ldns_buffer_read_u16(pkt);
+	return 0;
+}
+
+size_t
+get_rdf_size(ldns_rdf_type rdf)
+{
+	switch(rdf) {
+		case LDNS_RDF_TYPE_CLASS:
+		case LDNS_RDF_TYPE_ALG:
+		case LDNS_RDF_TYPE_INT8:
+			return 1;
+			break;
+		case LDNS_RDF_TYPE_INT16:
+		case LDNS_RDF_TYPE_TYPE:
+		case LDNS_RDF_TYPE_CERT_ALG:
+			return 2;
+			break;
+		case LDNS_RDF_TYPE_INT32:
+		case LDNS_RDF_TYPE_TIME:
+		case LDNS_RDF_TYPE_A:
+		case LDNS_RDF_TYPE_PERIOD:
+			return 4;
+			break;
+		case LDNS_RDF_TYPE_TSIGTIME:
+			return 6;
+			break;
+		case LDNS_RDF_TYPE_AAAA:
+			return 16;
+			break;
+		default:
+			log_assert(false); /* add type above */
+			/* only types that appear before a domain  *
+			 * name are needed. rest is simply copied. */
+	}
+	return 0;
+}
+
+/** calculate the size of one rr */
+static int
+calc_size(ldns_buffer* pkt, uint16_t type, struct rr_parse* rr)
+{
+	const ldns_rr_descriptor* desc;
+	uint16_t pkt_len; /* length of rr inside the packet */
+	rr->size = sizeof(uint16_t); /* the rdatalen */
+	ldns_buffer_skip(pkt, 4); /* skip ttl */
+	pkt_len = ldns_buffer_read_u16(pkt);
+	if(ldns_buffer_remaining(pkt) < pkt_len)
+		return 0;
+	desc = ldns_rr_descript(type);
+	if(pkt_len > 0 && desc && desc->_dname_count > 0) {
+		int count = (int)desc->_dname_count;
+		int rdf = 0;
+		size_t len;
+		size_t oldpos;
+		/* skip first part. */
+		while(pkt_len > 0 && count) {
+			switch(desc->_wireformat[rdf]) {
+			case LDNS_RDF_TYPE_DNAME:
+				/* decompress every domain name */
+				oldpos = ldns_buffer_position(pkt);
+				if((len = pkt_dname_len(pkt)) == 0)
+					return 0; /* malformed dname */
+				if(ldns_buffer_position(pkt)-oldpos > pkt_len)
+					return 0; /* dname exceeds rdata */
+				pkt_len -= ldns_buffer_position(pkt)-oldpos;
+				rr->size += len;
+				count--;
+				len = 0;
+				break;
+			case LDNS_RDF_TYPE_STR:
+				if(pkt_len < 1)
+					return 0; /* len byte exceeds rdata */
+				len = ldns_buffer_current(pkt)[0] + 1;
+				break;
+			default:
+				len = get_rdf_size(desc->_wireformat[rdf]);
+			}
+			if(len) {
+				if(pkt_len < len)
+					return 0; /* exceeds rdata */
+				pkt_len -= len;
+				ldns_buffer_skip(pkt, (ssize_t)len);
+				rr->size += len;
+			}
+			rdf++;
+		}
+	}
+	/* remaining rdata */
+	rr->size += pkt_len;
+	ldns_buffer_skip(pkt, (ssize_t)pkt_len);
+	return 1;
+}
+
+/** skip rr ttl and rdata */
+static int
+skip_ttl_rdata(ldns_buffer* pkt) 
+{
+	uint16_t rdatalen;
+	if(ldns_buffer_remaining(pkt) < 6) /* ttl + rdatalen */
+		return 0;
+	ldns_buffer_skip(pkt, 4); /* ttl */
+	rdatalen = ldns_buffer_read_u16(pkt);
+	if(ldns_buffer_remaining(pkt) < rdatalen)
+		return 0;
+	ldns_buffer_skip(pkt, (ssize_t)rdatalen);
+	return 1;
+}
+
+/** see if RRSIG is a duplicate of another */
+static int
+sig_is_double(ldns_buffer* pkt, struct rrset_parse* rrset, uint8_t* ttldata)
+{
+	uint16_t rlen, siglen;
+	size_t pos = ldns_buffer_position(pkt);
+	struct rr_parse* sig;
+	if(ldns_buffer_remaining(pkt) < 6) 
+		return 0;
+	ldns_buffer_skip(pkt, 4); /* ttl */
+	rlen = ldns_buffer_read_u16(pkt);
+	if(ldns_buffer_remaining(pkt) < rlen) {
+		ldns_buffer_set_position(pkt, pos);
+		return 0;
+	}
+	ldns_buffer_set_position(pkt, pos);
+
+	sig = rrset->rrsig_first;
+	while(sig) {
+		/* check if rdatalen is same */
+		memmove(&siglen, sig->ttl_data+4, sizeof(siglen));
+		siglen = ntohs(siglen);
+		/* checks if data in packet is exactly the same, this means
+		 * also dname in rdata is the same, but rrsig is not allowed
+		 * to have compressed dnames anyway. If it is compressed anyway
+		 * it will lead to duplicate rrs for qtype=RRSIG. (or ANY).
+		 *
+		 * Cannot use sig->size because size of the other one is not 
+		 * calculated yet.
+		 */
+		if(siglen == rlen) {
+			if(siglen>0 && memcmp(sig->ttl_data+6, ttldata+6, 
+				siglen) == 0) {
+				/* same! */
+				return 1;
+			}
+		}
+		sig = sig->next;
+	}
+	return 0;
+}
+
+/** Add rr (from packet here) to rrset, skips rr */
+static int
+add_rr_to_rrset(struct rrset_parse* rrset, ldns_buffer* pkt, 
+	struct msg_parse* msg, struct regional* region, 
+	ldns_pkt_section section, uint16_t type)
+{
+	struct rr_parse* rr;
+	/* check section of rrset. */
+	if(rrset->section != section && type != LDNS_RR_TYPE_RRSIG &&
+		rrset->type != LDNS_RR_TYPE_RRSIG) {
+		/* silently drop it - we drop the last part, since
+		 * trust in rr data depends on the section it is in. 
+		 * the less trustworthy part is discarded. 
+		 * also the last part is more likely to be incomplete.
+		 * RFC 2181: must put RRset only once in response. */
+		/*
+		verbose(VERB_QUERY, "Packet contains rrset data in "
+			"multiple sections, dropped last part.");
+		log_buf(VERB_QUERY, "packet was", pkt);
+		*/
+		/* forwards */
+		if(!skip_ttl_rdata(pkt))
+			return LDNS_RCODE_FORMERR;
+		return 0;
+	} 
+
+	if( (msg->qtype == LDNS_RR_TYPE_RRSIG ||
+	     msg->qtype == LDNS_RR_TYPE_ANY) 
+	    && sig_is_double(pkt, rrset, ldns_buffer_current(pkt))) {
+		if(!skip_ttl_rdata(pkt))
+			return LDNS_RCODE_FORMERR;
+		return 0;
+	}
+	
+	/* create rr */
+	if(!(rr = (struct rr_parse*)regional_alloc(region, sizeof(*rr))))
+		return LDNS_RCODE_SERVFAIL;
+	rr->outside_packet = 0;
+	rr->ttl_data = ldns_buffer_current(pkt);
+	rr->next = 0;
+	if(type == LDNS_RR_TYPE_RRSIG && rrset->type != LDNS_RR_TYPE_RRSIG) {
+		if(rrset->rrsig_last) 
+			rrset->rrsig_last->next = rr;
+		else	rrset->rrsig_first = rr;
+		rrset->rrsig_last = rr;
+		rrset->rrsig_count++;
+	} else {
+		if(rrset->rr_last)
+			rrset->rr_last->next = rr;
+		else	rrset->rr_first = rr;
+		rrset->rr_last = rr;
+		rrset->rr_count++;
+	}
+
+	/* calc decompressed size */
+	if(!calc_size(pkt, type, rr))
+		return LDNS_RCODE_FORMERR;
+	rrset->size += rr->size;
+
+	return 0;
+}
+
+/**
+ * Parse packet RR section, for answer, authority and additional sections. 
+ * @param pkt: packet, position at call must be at start of section.
+ *	at end position is after section.
+ * @param msg: store results here.
+ * @param region: how to alloc results.
+ * @param section: section enum.
+ * @param num_rrs: how many rrs are in the section.
+ * @param num_rrsets: returns number of rrsets in the section.
+ * @return: 0 if OK, or rcode on error.
+ */
+static int
+parse_section(ldns_buffer* pkt, struct msg_parse* msg, 
+	struct regional* region, ldns_pkt_section section, 
+	uint16_t num_rrs, size_t* num_rrsets)
+{
+	uint16_t i;
+	uint8_t* dname, *prev_dname_f = NULL, *prev_dname_l = NULL;
+	size_t dnamelen, prev_dnamelen = 0;
+	uint16_t type, prev_type = 0;
+	uint16_t dclass, prev_dclass = 0;
+	uint32_t rrset_flags = 0;
+	hashvalue_t hash = 0;
+	struct rrset_parse* rrset = NULL;
+	int r;
+
+	if(num_rrs == 0)
+		return 0;
+	if(ldns_buffer_remaining(pkt) <= 0)
+		return LDNS_RCODE_FORMERR;
+	for(i=0; i<num_rrs; i++) {
+		/* parse this RR. */
+		dname = ldns_buffer_current(pkt);
+		if((dnamelen = pkt_dname_len(pkt)) == 0)
+			return LDNS_RCODE_FORMERR;
+		if(ldns_buffer_remaining(pkt) < 10) /* type, class, ttl, len */
+			return LDNS_RCODE_FORMERR;
+		type = ldns_buffer_read_u16(pkt);
+		ldns_buffer_read(pkt, &dclass, sizeof(dclass));
+
+		if(0) { /* debug show what is being parsed. */
+			if(type == LDNS_RR_TYPE_RRSIG) {
+				uint16_t t;
+				if(pkt_rrsig_covered(pkt, 
+					ldns_buffer_current(pkt), &t))
+					fprintf(stderr, "parse of %s(%d) [%s(%d)]",
+					ldns_rr_descript(type)?
+					ldns_rr_descript(type)->_name: "??",
+					(int)type,
+					ldns_rr_descript(t)?
+					ldns_rr_descript(t)->_name: "??",
+					(int)t);
+			} else
+			  fprintf(stderr, "parse of %s(%d)",
+				ldns_rr_descript(type)?
+				ldns_rr_descript(type)->_name: "??",
+				(int)type);
+			fprintf(stderr, " %s(%d) ",
+				ldns_lookup_by_id(ldns_rr_classes, 
+				(int)ntohs(dclass))?ldns_lookup_by_id(
+				ldns_rr_classes, (int)ntohs(dclass))->name: 
+				"??", (int)ntohs(dclass));
+			dname_print(stderr, pkt, dname);
+			fprintf(stderr, "\n");
+		}
+
+		/* see if it is part of an existing RR set */
+		if(!find_rrset(msg, pkt, dname, dnamelen, type, dclass, &hash, 
+			&rrset_flags, &prev_dname_f, &prev_dname_l, 
+			&prev_dnamelen, &prev_type, &prev_dclass, &rrset, 
+			section, region))
+			return LDNS_RCODE_SERVFAIL;
+		if(!rrset) {
+			/* it is a new RR set. hash&flags already calculated.*/
+			(*num_rrsets)++;
+			rrset = new_rrset(msg, dname, dnamelen, type, dclass,
+				hash, rrset_flags, section, region);
+			if(!rrset) 
+				return LDNS_RCODE_SERVFAIL;
+		}
+		else if(0)	{ 
+			fprintf(stderr, "is part of existing: ");
+			dname_print(stderr, pkt, rrset->dname);
+			fprintf(stderr, " type %s(%d)\n",
+				ldns_rr_descript(rrset->type)?
+				ldns_rr_descript(rrset->type)->_name: "??",
+				(int)rrset->type);
+		}
+		/* add to rrset. */
+		if((r=add_rr_to_rrset(rrset, pkt, msg, region, section, 
+			type)) != 0)
+			return r;
+	}
+	return 0;
+}
+
+int
+parse_packet(ldns_buffer* pkt, struct msg_parse* msg, struct regional* region)
+{
+	int ret;
+	if(ldns_buffer_remaining(pkt) < LDNS_HEADER_SIZE)
+		return LDNS_RCODE_FORMERR;
+	/* read the header */
+	ldns_buffer_read(pkt, &msg->id, sizeof(uint16_t));
+	msg->flags = ldns_buffer_read_u16(pkt);
+	msg->qdcount = ldns_buffer_read_u16(pkt);
+	msg->ancount = ldns_buffer_read_u16(pkt);
+	msg->nscount = ldns_buffer_read_u16(pkt);
+	msg->arcount = ldns_buffer_read_u16(pkt);
+	if(msg->qdcount > 1)
+		return LDNS_RCODE_FORMERR;
+	if((ret = parse_query_section(pkt, msg)) != 0)
+		return ret;
+	if((ret = parse_section(pkt, msg, region, LDNS_SECTION_ANSWER,
+		msg->ancount, &msg->an_rrsets)) != 0)
+		return ret;
+	if((ret = parse_section(pkt, msg, region, LDNS_SECTION_AUTHORITY,
+		msg->nscount, &msg->ns_rrsets)) != 0)
+		return ret;
+	if(ldns_buffer_remaining(pkt) == 0 && msg->arcount == 1) {
+		/* BIND accepts leniently that an EDNS record is missing.
+		 * so, we do too. */
+	} else if((ret = parse_section(pkt, msg, region,
+		LDNS_SECTION_ADDITIONAL, msg->arcount, &msg->ar_rrsets)) != 0)
+		return ret;
+	/* if(ldns_buffer_remaining(pkt) > 0) { */
+		/* there is spurious data at end of packet. ignore */
+	/* } */
+	msg->rrset_count = msg->an_rrsets + msg->ns_rrsets + msg->ar_rrsets;
+	return 0;
+}
+
+int 
+parse_extract_edns(struct msg_parse* msg, struct edns_data* edns)
+{
+	struct rrset_parse* rrset = msg->rrset_first;
+	struct rrset_parse* prev = 0;
+	struct rrset_parse* found = 0;
+	struct rrset_parse* found_prev = 0;
+	/* since the class encodes the UDP size, we cannot use hash table to
+	 * find the EDNS OPT record. Scan the packet. */
+	while(rrset) {
+		if(rrset->type == LDNS_RR_TYPE_OPT) {
+			/* only one OPT RR allowed. */
+			if(found) return LDNS_RCODE_FORMERR;
+			/* found it! */
+			found_prev = prev;
+			found = rrset;
+		}
+		prev = rrset;
+		rrset = rrset->rrset_all_next;
+	}
+	if(!found) {
+		memset(edns, 0, sizeof(*edns));
+		edns->udp_size = 512;
+		return 0;
+	}
+	/* check the found RRset */
+	/* most lenient check possible. ignore dname, use last opt */
+	if(found->section != LDNS_SECTION_ADDITIONAL)
+		return LDNS_RCODE_FORMERR; 
+	if(found->rr_count == 0)
+		return LDNS_RCODE_FORMERR;
+	if(0) { /* strict checking of dname and RRcount */
+		if(found->dname_len != 1 || !found->dname 
+			|| found->dname[0] != 0) return LDNS_RCODE_FORMERR; 
+		if(found->rr_count != 1) return LDNS_RCODE_FORMERR; 
+	}
+	log_assert(found->rr_first && found->rr_last);
+
+	/* remove from packet */
+	if(found_prev)	found_prev->rrset_all_next = found->rrset_all_next;
+	else	msg->rrset_first = found->rrset_all_next;
+	if(found == msg->rrset_last)
+		msg->rrset_last = found_prev;
+	msg->arcount --;
+	msg->ar_rrsets --;
+	msg->rrset_count --;
+	
+	/* take the data ! */
+	edns->edns_present = 1;
+	edns->ext_rcode = found->rr_last->ttl_data[0];
+	edns->edns_version = found->rr_last->ttl_data[1];
+	edns->bits = ldns_read_uint16(&found->rr_last->ttl_data[2]);
+	edns->udp_size = ntohs(found->rrset_class);
+	/* ignore rdata and rrsigs */
+	return 0;
+}
+
+int 
+parse_edns_from_pkt(ldns_buffer* pkt, struct edns_data* edns)
+{
+	log_assert(LDNS_QDCOUNT(ldns_buffer_begin(pkt)) == 1);
+	log_assert(LDNS_ANCOUNT(ldns_buffer_begin(pkt)) == 0);
+	log_assert(LDNS_NSCOUNT(ldns_buffer_begin(pkt)) == 0);
+	/* check edns section is present */
+	if(LDNS_ARCOUNT(ldns_buffer_begin(pkt)) > 1) {
+		return LDNS_RCODE_FORMERR;
+	}
+	if(LDNS_ARCOUNT(ldns_buffer_begin(pkt)) == 0) {
+		memset(edns, 0, sizeof(*edns));
+		edns->udp_size = 512;
+		return 0;
+	}
+	/* domain name must be the root of length 1. */
+	if(pkt_dname_len(pkt) != 1)
+		return LDNS_RCODE_FORMERR;
+	if(ldns_buffer_remaining(pkt) < 10) /* type, class, ttl, rdatalen */
+		return LDNS_RCODE_FORMERR;
+	if(ldns_buffer_read_u16(pkt) != LDNS_RR_TYPE_OPT)
+		return LDNS_RCODE_FORMERR;
+	edns->edns_present = 1;
+	edns->udp_size = ldns_buffer_read_u16(pkt); /* class is udp size */
+	edns->ext_rcode = ldns_buffer_read_u8(pkt); /* ttl used for bits */
+	edns->edns_version = ldns_buffer_read_u8(pkt);
+	edns->bits = ldns_buffer_read_u16(pkt);
+	/* ignore rdata and rrsigs */
+	return 0;
+}
diff --git a/3rdParty/Unbound/src/src/util/data/msgparse.h b/3rdParty/Unbound/src/src/util/data/msgparse.h
new file mode 100644
index 0000000..830d68e
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/data/msgparse.h
@@ -0,0 +1,299 @@
+/*
+ * util/data/msgparse.h - parse wireformat DNS messages.
+ * 
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ * 
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+/**
+ * \file
+ * Contains message parsing data structures.
+ * These point back into the packet buffer.
+ *
+ * During parsing RRSIGS are put together with the rrsets they (claim to) sign.
+ * This process works as follows:
+ *	o if RRSIG follows the data rrset, it is added to the rrset rrsig list.
+ *	o if no matching data rrset is found, the RRSIG becomes a new rrset.
+ *	o If the data rrset later follows the RRSIG
+ *		o See if the RRSIG rrset contains multiple types, and needs to
+ *		  have the rrsig(s) for that data type split off.
+ *		o Put the data rr as data type in the rrset and rrsig in list.
+ *	o RRSIGs are allowed to move to a different section. The section of
+ *	  the data item is used for the final rrset.
+ *	o multiple signatures over an RRset are possible.
+ *
+ * For queries of qtype=RRSIG, some special handling is needed, to avoid
+ * splitting the RRSIG in the answer section.
+ *	o duplicate, not split, RRSIGs from the answer section, if qtype=RRSIG.
+ *	o check for doubles in the rrsig list when adding an RRSIG to data,
+ *	  so that a data rrset is signed by RRSIGs with different rdata.
+ *	  when qtype=RRSIG.
+ * This will move the RRSIG from the answer section to sign the data further
+ * in the packet (if possible). If then after that, more RRSIGs are found
+ * that sign the data as well, doubles are removed.
+ */
+
+#ifndef UTIL_DATA_MSGPARSE_H
+#define UTIL_DATA_MSGPARSE_H
+#include "util/storage/lruhash.h"
+#include <ldns/packet.h>
+struct rrset_parse;
+struct rr_parse;
+struct regional;
+
+/** number of buckets in parse rrset hash table. Must be power of 2. */
+#define PARSE_TABLE_SIZE 32
+/** Maximum TTL that is allowed. */
+extern uint32_t MAX_TTL;
+/** Minimum TTL that is allowed. */
+extern uint32_t MIN_TTL;
+/** Negative cache time (for entries without any RRs.) */
+#define NORR_TTL 5 /* seconds */
+
+/**
+ * Data stored in scratch pad memory during parsing.
+ * Stores the data that will enter into the msgreply and packet result.
+ */
+struct msg_parse {
+	/** id from message, network format. */
+	uint16_t id;
+	/** flags from message, host format. */
+	uint16_t flags;
+	/** count of RRs, host format */
+	uint16_t qdcount;
+	/** count of RRs, host format */
+	uint16_t ancount;
+	/** count of RRs, host format */
+	uint16_t nscount;
+	/** count of RRs, host format */
+	uint16_t arcount;
+	/** count of RRsets per section. */
+	size_t an_rrsets;
+	/** count of RRsets per section. */
+	size_t ns_rrsets; 
+	/** count of RRsets per section. */
+	size_t ar_rrsets;
+	/** total number of rrsets found. */
+	size_t rrset_count;
+
+	/** query dname (pointer to start location in packet, NULL if none */
+	uint8_t* qname;
+	/** length of query dname in octets, 0 if none */
+	size_t qname_len;
+	/** query type, host order. 0 if qdcount=0 */
+	uint16_t qtype;
+	/** query class, host order. 0 if qdcount=0 */
+	uint16_t qclass;
+
+	/**
+	 * Hash table array used during parsing to lookup rrset types.
+	 * Based on name, type, class.  Same hash value as in rrset cache.
+	 */
+	struct rrset_parse* hashtable[PARSE_TABLE_SIZE];
+	
+	/** linked list of rrsets that have been found (in order). */
+	struct rrset_parse* rrset_first;
+	/** last element of rrset list. */
+	struct rrset_parse* rrset_last;
+};
+
+/**
+ * Data stored for an rrset during parsing.
+ */
+struct rrset_parse {
+	/** next in hash bucket */
+	struct rrset_parse* rrset_bucket_next;
+	/** next in list of all rrsets */
+	struct rrset_parse* rrset_all_next;
+	/** hash value of rrset */
+	hashvalue_t hash;
+	/** which section was it found in: one of
+	 * LDNS_SECTION_ANSWER, LDNS_SECTION_AUTHORITY, LDNS_SECTION_ADDITIONAL
+	 */
+	ldns_pkt_section section;
+	/** start of (possibly compressed) dname in packet */
+	uint8_t* dname;
+	/** length of the dname uncompressed wireformat */
+	size_t dname_len;
+	/** type, host order. */
+	uint16_t type;
+	/** class, network order. var name so that it is not a c++ keyword. */
+	uint16_t rrset_class;
+	/** the flags for the rrset, like for packedrrset */
+	uint32_t flags;
+	/** number of RRs in the rr list */
+	size_t rr_count;
+	/** sum of RR rdata sizes */
+	size_t size;
+	/** linked list of RRs in this rrset. */
+	struct rr_parse* rr_first;
+	/** last in list of RRs in this rrset. */
+	struct rr_parse* rr_last;
+	/** number of RRSIGs over this rrset. */
+	size_t rrsig_count;
+	/** linked list of RRsig RRs over this rrset. */
+	struct rr_parse* rrsig_first;
+	/** last in list of RRSIG RRs over this rrset. */
+	struct rr_parse* rrsig_last;
+};
+
+/**
+ * Data stored for an RR during parsing.
+ */
+struct rr_parse {
+	/** 
+	 * Pointer to the RR. Points to start of TTL value in the packet.
+	 * Rdata length and rdata follow it.
+	 * its dname, type and class are the same and stored for the rrset.
+	 */
+	uint8_t* ttl_data;
+	/** true if ttl_data is not part of the packet, but elsewhere in mem.
+	 * Set for generated CNAMEs for DNAMEs. */
+	int outside_packet;
+	/** the length of the rdata if allocated (with no dname compression)*/
+	size_t size;
+	/** next in list of RRs. */
+	struct rr_parse* next;
+};
+
+/** Check if label length is first octet of a compression pointer, pass u8. */
+#define LABEL_IS_PTR(x) ( ((x)&0xc0) == 0xc0 )
+/** Calculate destination offset of a compression pointer. pass first and
+ * second octets of the compression pointer. */
+#define PTR_OFFSET(x, y) ( ((x)&0x3f)<<8 | (y) )
+/** create a compression pointer to the given offset. */
+#define PTR_CREATE(offset) ((uint16_t)(0xc000 | (offset)))
+
+/** error codes, extended with EDNS, so > 15. */
+#define EDNS_RCODE_BADVERS	16	/** bad EDNS version */
+/** largest valid compression offset */
+#define PTR_MAX_OFFSET 	0x3fff
+
+/**
+ * EDNS data storage
+ * EDNS rdata is ignored.
+ */
+struct edns_data {
+	/** if EDNS OPT record was present */
+	int edns_present;
+	/** Extended RCODE */
+	uint8_t ext_rcode;
+	/** The EDNS version number */
+	uint8_t edns_version;
+	/** the EDNS bits field from ttl (host order): Z */
+	uint16_t bits;
+	/** UDP reassembly size. */
+	uint16_t udp_size;
+};
+
+/**
+ * Obtain size in the packet of an rr type, that is before dname type.
+ * Do TYPE_DNAME, and type STR, yourself. Gives size for most regular types.
+ * @param rdf: the rdf type from the descriptor.
+ * @return: size in octets. 0 on failure.
+ */
+size_t get_rdf_size(ldns_rdf_type rdf);
+
+/**
+ * Parse the packet.
+ * @param pkt: packet, position at call must be at start of packet.
+ *	at end position is after packet.
+ * @param msg: where to store results.
+ * @param region: how to alloc results.
+ * @return: 0 if OK, or rcode on error.
+ */
+int parse_packet(ldns_buffer* pkt, struct msg_parse* msg, 
+	struct regional* region);
+
+/**
+ * After parsing the packet, extract EDNS data from packet.
+ * If not present this is noted in the data structure.
+ * If a parse error happens, an error code is returned.
+ *
+ * Quirks:
+ *	o ignores OPT rdata.
+ *	o ignores OPT owner name.
+ *	o ignores extra OPT records, except the last one in the packet.
+ *
+ * @param msg: parsed message structure. Modified on exit, if EDNS was present
+ * 	it is removed from the additional section.
+ * @param edns: the edns data is stored here. Does not have to be initialised.
+ * @return: 0 on success. or an RCODE on an error.
+ *	RCODE formerr if OPT in wrong section, and so on.
+ */
+int parse_extract_edns(struct msg_parse* msg, struct edns_data* edns);
+
+/**
+ * If EDNS data follows a query section, extract it and initialize edns struct.
+ * @param pkt: the packet. position at start must be right after the query
+ *	section. At end, right after EDNS data or no movement if failed.
+ * @param edns: the edns data allocated by the caller. Does not have to be
+ *	initialised.
+ * @return: 0 on success, or an RCODE on error.
+ *	RCODE formerr if OPT is badly formatted and so on.
+ */
+int parse_edns_from_pkt(ldns_buffer* pkt, struct edns_data* edns);
+
+/**
+ * Calculate hash value for rrset in packet.
+ * @param pkt: the packet.
+ * @param dname: pointer to uncompressed dname, or compressed dname in packet.
+ * @param type: rrset type in host order.
+ * @param dclass: rrset class in network order.
+ * @param rrset_flags: rrset flags (same as packed_rrset flags).
+ * @return hash value
+ */
+hashvalue_t pkt_hash_rrset(ldns_buffer* pkt, uint8_t* dname, uint16_t type,
+        uint16_t dclass, uint32_t rrset_flags);
+
+/**
+ * Lookup in msg hashtable to find a rrset.
+ * @param msg: with the hashtable.
+ * @param pkt: packet for compressed names.
+ * @param h: hash value
+ * @param rrset_flags: flags of rrset sought for.
+ * @param dname: name of rrset sought for.
+ * @param dnamelen: len of dname.
+ * @param type: rrset type, host order.
+ * @param dclass: rrset class, network order.
+ * @return NULL or the rrset_parse if found.
+ */
+struct rrset_parse* msgparse_hashtable_lookup(struct msg_parse* msg, 
+	ldns_buffer* pkt, hashvalue_t h, uint32_t rrset_flags, 
+	uint8_t* dname, size_t dnamelen, uint16_t type, uint16_t dclass);
+
+/**
+ * Remove rrset from hash table.
+ * @param msg: with hashtable.
+ * @param rrset: with hash value and id info.
+ */
+void msgparse_bucket_remove(struct msg_parse* msg, struct rrset_parse* rrset);
+
+#endif /* UTIL_DATA_MSGPARSE_H */
diff --git a/3rdParty/Unbound/src/src/util/data/msgreply.c b/3rdParty/Unbound/src/src/util/data/msgreply.c
new file mode 100644
index 0000000..6d711ff
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/data/msgreply.c
@@ -0,0 +1,838 @@
+/*
+ * util/data/msgreply.c - store message and reply data. 
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains a data structure to store a message and its reply.
+ */
+
+#include "config.h"
+#include <ldns/ldns.h>
+#include "util/data/msgreply.h"
+#include "util/storage/lookup3.h"
+#include "util/log.h"
+#include "util/alloc.h"
+#include "util/netevent.h"
+#include "util/net_help.h"
+#include "util/data/dname.h"
+#include "util/regional.h"
+#include "util/data/msgparse.h"
+#include "util/data/msgencode.h"
+
+/** MAX TTL default for messages and rrsets */
+uint32_t MAX_TTL = 3600 * 24 * 10; /* ten days */
+/** MIN TTL default for messages and rrsets */
+uint32_t MIN_TTL = 0;
+
+/** allocate qinfo, return 0 on error */
+static int
+parse_create_qinfo(ldns_buffer* pkt, struct msg_parse* msg, 
+	struct query_info* qinf, struct regional* region)
+{
+	if(msg->qname) {
+		if(region)
+			qinf->qname = (uint8_t*)regional_alloc(region, 
+				msg->qname_len);
+		else	qinf->qname = (uint8_t*)malloc(msg->qname_len);
+		if(!qinf->qname) return 0;
+		dname_pkt_copy(pkt, qinf->qname, msg->qname);
+	} else	qinf->qname = 0;
+	qinf->qname_len = msg->qname_len;
+	qinf->qtype = msg->qtype;
+	qinf->qclass = msg->qclass;
+	return 1;
+}
+
+/** constructor for replyinfo */
+static struct reply_info*
+construct_reply_info_base(struct regional* region, uint16_t flags, size_t qd,
+	uint32_t ttl, uint32_t prettl, size_t an, size_t ns, size_t ar, 
+	size_t total, enum sec_status sec)
+{
+	struct reply_info* rep;
+	/* rrset_count-1 because the first ref is part of the struct. */
+	size_t s = sizeof(struct reply_info) - sizeof(struct rrset_ref) +
+		sizeof(struct ub_packed_rrset_key*) * total;
+	if(region)
+		rep = (struct reply_info*)regional_alloc(region, s);
+	else	rep = (struct reply_info*)malloc(s + 
+			sizeof(struct rrset_ref) * (total));
+	if(!rep) 
+		return NULL;
+	rep->flags = flags;
+	rep->qdcount = qd;
+	rep->ttl = ttl;
+	rep->prefetch_ttl = prettl;
+	rep->an_numrrsets = an;
+	rep->ns_numrrsets = ns;
+	rep->ar_numrrsets = ar;
+	rep->rrset_count = total;
+	rep->security = sec;
+	rep->authoritative = 0;
+	/* array starts after the refs */
+	if(region)
+		rep->rrsets = (struct ub_packed_rrset_key**)&(rep->ref[0]);
+	else	rep->rrsets = (struct ub_packed_rrset_key**)&(rep->ref[total]);
+	/* zero the arrays to assist cleanup in case of malloc failure */
+	memset( rep->rrsets, 0, sizeof(struct ub_packed_rrset_key*) * total);
+	if(!region)
+		memset( &rep->ref[0], 0, sizeof(struct rrset_ref) * total);
+	return rep;
+}
+
+/** allocate replyinfo, return 0 on error */
+static int
+parse_create_repinfo(struct msg_parse* msg, struct reply_info** rep,
+	struct regional* region)
+{
+	*rep = construct_reply_info_base(region, msg->flags, msg->qdcount, 0, 
+		0, msg->an_rrsets, msg->ns_rrsets, msg->ar_rrsets, 
+		msg->rrset_count, sec_status_unchecked);
+	if(!*rep)
+		return 0;
+	return 1;
+}
+
+/** allocate (special) rrset keys, return 0 on error */
+static int
+repinfo_alloc_rrset_keys(struct reply_info* rep, struct alloc_cache* alloc, 
+	struct regional* region)
+{
+	size_t i;
+	for(i=0; i<rep->rrset_count; i++) {
+		if(region) {
+			rep->rrsets[i] = (struct ub_packed_rrset_key*)
+				regional_alloc(region, 
+				sizeof(struct ub_packed_rrset_key));
+			if(rep->rrsets[i]) {
+				memset(rep->rrsets[i], 0, 
+					sizeof(struct ub_packed_rrset_key));
+				rep->rrsets[i]->entry.key = rep->rrsets[i];
+			}
+		}
+		else	rep->rrsets[i] = alloc_special_obtain(alloc);
+		if(!rep->rrsets[i])
+			return 0;
+		rep->rrsets[i]->entry.data = NULL;
+	}
+	return 1;
+}
+
+/** do the rdata copy */
+static int
+rdata_copy(ldns_buffer* pkt, struct packed_rrset_data* data, uint8_t* to, 
+	struct rr_parse* rr, uint32_t* rr_ttl, uint16_t type)
+{
+	uint16_t pkt_len;
+	const ldns_rr_descriptor* desc;
+
+	*rr_ttl = ldns_read_uint32(rr->ttl_data);
+	/* RFC 2181 Section 8. if msb of ttl is set treat as if zero. */
+	if(*rr_ttl & 0x80000000U)
+		*rr_ttl = 0;
+	if(*rr_ttl < MIN_TTL)
+		*rr_ttl = MIN_TTL;
+	if(*rr_ttl < data->ttl)
+		data->ttl = *rr_ttl;
+
+	if(rr->outside_packet) {
+		/* uncompressed already, only needs copy */
+		memmove(to, rr->ttl_data+sizeof(uint32_t), rr->size);
+		return 1;
+	}
+
+	ldns_buffer_set_position(pkt, (size_t)
+		(rr->ttl_data - ldns_buffer_begin(pkt) + sizeof(uint32_t)));
+	/* insert decompressed size into rdata len stored in memory */
+	/* -2 because rdatalen bytes are not included. */
+	pkt_len = htons(rr->size - 2);
+	memmove(to, &pkt_len, sizeof(uint16_t));
+	to += 2;
+	/* read packet rdata len */
+	pkt_len = ldns_buffer_read_u16(pkt);
+	if(ldns_buffer_remaining(pkt) < pkt_len)
+		return 0;
+	desc = ldns_rr_descript(type);
+	if(pkt_len > 0 && desc && desc->_dname_count > 0) {
+		int count = (int)desc->_dname_count;
+		int rdf = 0;
+		size_t len;
+		size_t oldpos;
+		/* decompress dnames. */
+		while(pkt_len > 0 && count) {
+			switch(desc->_wireformat[rdf]) {
+			case LDNS_RDF_TYPE_DNAME:
+				oldpos = ldns_buffer_position(pkt);
+				dname_pkt_copy(pkt, to, 
+					ldns_buffer_current(pkt));
+				to += pkt_dname_len(pkt);
+				pkt_len -= ldns_buffer_position(pkt)-oldpos;
+				count--;
+				len = 0;
+				break;
+			case LDNS_RDF_TYPE_STR:
+				len = ldns_buffer_current(pkt)[0] + 1;
+				break;
+			default:
+				len = get_rdf_size(desc->_wireformat[rdf]);
+				break;
+			}
+			if(len) {
+				memmove(to, ldns_buffer_current(pkt), len);
+				to += len;
+				ldns_buffer_skip(pkt, (ssize_t)len);
+				log_assert(len <= pkt_len);
+				pkt_len -= len;
+			}
+			rdf++;
+		}
+	}
+	/* copy remaining rdata */
+	if(pkt_len >  0)
+		memmove(to, ldns_buffer_current(pkt), pkt_len);
+	
+	return 1;
+}
+
+/** copy over the data into packed rrset */
+static int
+parse_rr_copy(ldns_buffer* pkt, struct rrset_parse* pset, 
+	struct packed_rrset_data* data)
+{
+	size_t i;
+	struct rr_parse* rr = pset->rr_first;
+	uint8_t* nextrdata;
+	size_t total = pset->rr_count + pset->rrsig_count;
+	data->ttl = MAX_TTL;
+	data->count = pset->rr_count;
+	data->rrsig_count = pset->rrsig_count;
+	data->trust = rrset_trust_none;
+	data->security = sec_status_unchecked;
+	/* layout: struct - rr_len - rr_data - rr_ttl - rdata - rrsig */
+	data->rr_len = (size_t*)((uint8_t*)data + 
+		sizeof(struct packed_rrset_data));
+	data->rr_data = (uint8_t**)&(data->rr_len[total]);
+	data->rr_ttl = (uint32_t*)&(data->rr_data[total]);
+	nextrdata = (uint8_t*)&(data->rr_ttl[total]);
+	for(i=0; i<data->count; i++) {
+		data->rr_len[i] = rr->size;
+		data->rr_data[i] = nextrdata;
+		nextrdata += rr->size;
+		if(!rdata_copy(pkt, data, data->rr_data[i], rr, 
+			&data->rr_ttl[i], pset->type))
+			return 0;
+		rr = rr->next;
+	}
+	/* if rrsig, its rdata is at nextrdata */
+	rr = pset->rrsig_first;
+	for(i=data->count; i<total; i++) {
+		data->rr_len[i] = rr->size;
+		data->rr_data[i] = nextrdata;
+		nextrdata += rr->size;
+		if(!rdata_copy(pkt, data, data->rr_data[i], rr, 
+			&data->rr_ttl[i], LDNS_RR_TYPE_RRSIG))
+			return 0;
+		rr = rr->next;
+	}
+	return 1;
+}
+
+/** create rrset return 0 on failure */
+static int
+parse_create_rrset(ldns_buffer* pkt, struct rrset_parse* pset,
+	struct packed_rrset_data** data, struct regional* region)
+{
+	/* allocate */
+	size_t s = sizeof(struct packed_rrset_data) + 
+		(pset->rr_count + pset->rrsig_count) * 
+		(sizeof(size_t)+sizeof(uint8_t*)+sizeof(uint32_t)) + 
+		pset->size;
+	if(region)
+		*data = regional_alloc(region, s);
+	else	*data = malloc(s);
+	if(!*data)
+		return 0;
+	/* copy & decompress */
+	if(!parse_rr_copy(pkt, pset, *data)) {
+		if(!region) free(*data);
+		return 0;
+	}
+	return 1;
+}
+
+/** get trust value for rrset */
+static enum rrset_trust
+get_rrset_trust(struct msg_parse* msg, struct rrset_parse* rrset)
+{
+	uint16_t AA = msg->flags & BIT_AA;
+	if(rrset->section == LDNS_SECTION_ANSWER) {
+		if(AA) {
+			/* RFC2181 says remainder of CNAME chain is nonauth*/
+			if(msg->rrset_first && 
+				msg->rrset_first->section==LDNS_SECTION_ANSWER
+				&& msg->rrset_first->type==LDNS_RR_TYPE_CNAME){
+				if(rrset == msg->rrset_first)
+					return rrset_trust_ans_AA;
+				else 	return rrset_trust_ans_noAA;
+			}
+			if(msg->rrset_first && 
+				msg->rrset_first->section==LDNS_SECTION_ANSWER
+				&& msg->rrset_first->type==LDNS_RR_TYPE_DNAME){
+				if(rrset == msg->rrset_first ||
+				   rrset == msg->rrset_first->rrset_all_next)
+					return rrset_trust_ans_AA;
+				else 	return rrset_trust_ans_noAA;
+			}
+			return rrset_trust_ans_AA;
+		}
+		else	return rrset_trust_ans_noAA;
+	} else if(rrset->section == LDNS_SECTION_AUTHORITY) {
+		if(AA)	return rrset_trust_auth_AA;
+		else	return rrset_trust_auth_noAA;
+	} else {
+		/* addit section */
+		if(AA)	return rrset_trust_add_AA;
+		else	return rrset_trust_add_noAA;
+	}
+	/* NOTREACHED */
+	return rrset_trust_none;
+}
+
+int
+parse_copy_decompress_rrset(ldns_buffer* pkt, struct msg_parse* msg,
+	struct rrset_parse *pset, struct regional* region, 
+	struct ub_packed_rrset_key* pk)
+{
+	struct packed_rrset_data* data;
+	pk->rk.flags = pset->flags;
+	pk->rk.dname_len = pset->dname_len;
+	if(region)
+		pk->rk.dname = (uint8_t*)regional_alloc(
+			region, pset->dname_len);
+	else	pk->rk.dname = 
+			(uint8_t*)malloc(pset->dname_len);
+	if(!pk->rk.dname)
+		return 0;
+	/** copy & decompress dname */
+	dname_pkt_copy(pkt, pk->rk.dname, pset->dname);
+	/** copy over type and class */
+	pk->rk.type = htons(pset->type);
+	pk->rk.rrset_class = pset->rrset_class;
+	/** read data part. */
+	if(!parse_create_rrset(pkt, pset, &data, region))
+		return 0;
+	pk->entry.data = (void*)data;
+	pk->entry.key = (void*)pk;
+	pk->entry.hash = pset->hash;
+	data->trust = get_rrset_trust(msg, pset);
+	return 1;
+}
+
+/** 
+ * Copy and decompress rrs
+ * @param pkt: the packet for compression pointer resolution.
+ * @param msg: the parsed message
+ * @param rep: reply info to put rrs into.
+ * @param region: if not NULL, used for allocation.
+ * @return 0 on failure.
+ */
+static int
+parse_copy_decompress(ldns_buffer* pkt, struct msg_parse* msg,
+	struct reply_info* rep, struct regional* region)
+{
+	size_t i;
+	struct rrset_parse *pset = msg->rrset_first;
+	struct packed_rrset_data* data;
+	log_assert(rep);
+	rep->ttl = MAX_TTL;
+	rep->security = sec_status_unchecked;
+	if(rep->rrset_count == 0)
+		rep->ttl = NORR_TTL;
+
+	for(i=0; i<rep->rrset_count; i++) {
+		if(!parse_copy_decompress_rrset(pkt, msg, pset, region,
+			rep->rrsets[i]))
+			return 0;
+		data = (struct packed_rrset_data*)rep->rrsets[i]->entry.data;
+		if(data->ttl < rep->ttl)
+			rep->ttl = data->ttl;
+
+		pset = pset->rrset_all_next;
+	}
+	rep->prefetch_ttl = PREFETCH_TTL_CALC(rep->ttl);
+	return 1;
+}
+
+int 
+parse_create_msg(ldns_buffer* pkt, struct msg_parse* msg,
+	struct alloc_cache* alloc, struct query_info* qinf, 
+	struct reply_info** rep, struct regional* region)
+{
+	log_assert(pkt && msg);
+	if(!parse_create_qinfo(pkt, msg, qinf, region))
+		return 0;
+	if(!parse_create_repinfo(msg, rep, region))
+		return 0;
+	if(!repinfo_alloc_rrset_keys(*rep, alloc, region))
+		return 0;
+	if(!parse_copy_decompress(pkt, msg, *rep, region))
+		return 0;
+	return 1;
+}
+
+int reply_info_parse(ldns_buffer* pkt, struct alloc_cache* alloc,
+        struct query_info* qinf, struct reply_info** rep, 
+	struct regional* region, struct edns_data* edns)
+{
+	/* use scratch pad region-allocator during parsing. */
+	struct msg_parse* msg;
+	int ret;
+	
+	qinf->qname = NULL;
+	*rep = NULL;
+	if(!(msg = regional_alloc(region, sizeof(*msg)))) {
+		return LDNS_RCODE_SERVFAIL;
+	}
+	memset(msg, 0, sizeof(*msg));
+	
+	ldns_buffer_set_position(pkt, 0);
+	if((ret = parse_packet(pkt, msg, region)) != 0) {
+		return ret;
+	}
+	if((ret = parse_extract_edns(msg, edns)) != 0)
+		return ret;
+
+	/* parse OK, allocate return structures */
+	/* this also performs dname decompression */
+	if(!parse_create_msg(pkt, msg, alloc, qinf, rep, NULL)) {
+		query_info_clear(qinf);
+		reply_info_parsedelete(*rep, alloc);
+		*rep = NULL;
+		return LDNS_RCODE_SERVFAIL;
+	}
+	return 0;
+}
+
+/** helper compare function to sort in lock order */
+static int
+reply_info_sortref_cmp(const void* a, const void* b)
+{
+	struct rrset_ref* x = (struct rrset_ref*)a;
+	struct rrset_ref* y = (struct rrset_ref*)b;
+	if(x->key < y->key) return -1;
+	if(x->key > y->key) return 1;
+	return 0;
+}
+
+void 
+reply_info_sortref(struct reply_info* rep)
+{
+	qsort(&rep->ref[0], rep->rrset_count, sizeof(struct rrset_ref),
+		reply_info_sortref_cmp);
+}
+
+void 
+reply_info_set_ttls(struct reply_info* rep, uint32_t timenow)
+{
+	size_t i, j;
+	rep->ttl += timenow;
+	rep->prefetch_ttl += timenow;
+	for(i=0; i<rep->rrset_count; i++) {
+		struct packed_rrset_data* data = (struct packed_rrset_data*)
+			rep->ref[i].key->entry.data;
+		if(i>0 && rep->ref[i].key == rep->ref[i-1].key)
+			continue;
+		data->ttl += timenow;
+		for(j=0; j<data->count + data->rrsig_count; j++) {
+			data->rr_ttl[j] += timenow;
+		}
+	}
+}
+
+void 
+reply_info_parsedelete(struct reply_info* rep, struct alloc_cache* alloc)
+{
+	size_t i;
+	if(!rep) 
+		return;
+	/* no need to lock, since not shared in hashtables. */
+	for(i=0; i<rep->rrset_count; i++) {
+		ub_packed_rrset_parsedelete(rep->rrsets[i], alloc);
+	}
+	free(rep);
+}
+
+int 
+query_info_parse(struct query_info* m, ldns_buffer* query)
+{
+	uint8_t* q = ldns_buffer_begin(query);
+	/* minimum size: header + \0 + qtype + qclass */
+	if(ldns_buffer_limit(query) < LDNS_HEADER_SIZE + 5)
+		return 0;
+	if(LDNS_OPCODE_WIRE(q) != LDNS_PACKET_QUERY || 
+		LDNS_QDCOUNT(q) != 1 || ldns_buffer_position(query) != 0)
+		return 0;
+	ldns_buffer_skip(query, LDNS_HEADER_SIZE);
+	m->qname = ldns_buffer_current(query);
+	if((m->qname_len = query_dname_len(query)) == 0)
+		return 0; /* parse error */
+	if(ldns_buffer_remaining(query) < 4)
+		return 0; /* need qtype, qclass */
+	m->qtype = ldns_buffer_read_u16(query);
+	m->qclass = ldns_buffer_read_u16(query);
+	return 1;
+}
+
+/** tiny subroutine for msgreply_compare */
+#define COMPARE_IT(x, y) \
+	if( (x) < (y) ) return -1; \
+	else if( (x) > (y) ) return +1; \
+	log_assert( (x) == (y) );
+
+int 
+query_info_compare(void* m1, void* m2)
+{
+	struct query_info* msg1 = (struct query_info*)m1;
+	struct query_info* msg2 = (struct query_info*)m2;
+	int mc;
+	/* from most different to least different for speed */
+	COMPARE_IT(msg1->qtype, msg2->qtype);
+	if((mc = query_dname_compare(msg1->qname, msg2->qname)) != 0)
+		return mc;
+	log_assert(msg1->qname_len == msg2->qname_len);
+	COMPARE_IT(msg1->qclass, msg2->qclass);
+	return 0;
+#undef COMPARE_IT
+}
+
+void 
+query_info_clear(struct query_info* m)
+{
+	free(m->qname);
+	m->qname = NULL;
+}
+
+size_t 
+msgreply_sizefunc(void* k, void* d)
+{
+	struct msgreply_entry* q = (struct msgreply_entry*)k;
+	struct reply_info* r = (struct reply_info*)d;
+	size_t s = sizeof(struct msgreply_entry) + sizeof(struct reply_info)
+		+ q->key.qname_len + lock_get_mem(&q->entry.lock)
+		- sizeof(struct rrset_ref);
+	s += r->rrset_count * sizeof(struct rrset_ref);
+	s += r->rrset_count * sizeof(struct ub_packed_rrset_key*);
+	return s;
+}
+
+void 
+query_entry_delete(void *k, void* ATTR_UNUSED(arg))
+{
+	struct msgreply_entry* q = (struct msgreply_entry*)k;
+	lock_rw_destroy(&q->entry.lock);
+	query_info_clear(&q->key);
+	free(q);
+}
+
+void 
+reply_info_delete(void* d, void* ATTR_UNUSED(arg))
+{
+	struct reply_info* r = (struct reply_info*)d;
+	free(r);
+}
+
+hashvalue_t 
+query_info_hash(struct query_info *q)
+{
+	hashvalue_t h = 0xab;
+	h = hashlittle(&q->qtype, sizeof(q->qtype), h);
+	h = hashlittle(&q->qclass, sizeof(q->qclass), h);
+	h = dname_query_hash(q->qname, h);
+	return h;
+}
+
+struct msgreply_entry* 
+query_info_entrysetup(struct query_info* q, struct reply_info* r, 
+	hashvalue_t h)
+{
+	struct msgreply_entry* e = (struct msgreply_entry*)malloc( 
+		sizeof(struct msgreply_entry));
+	if(!e) return NULL;
+	memcpy(&e->key, q, sizeof(*q));
+	e->entry.hash = h;
+	e->entry.key = e;
+	e->entry.data = r;
+	lock_rw_init(&e->entry.lock);
+	lock_protect(&e->entry.lock, &e->key, sizeof(e->key));
+	lock_protect(&e->entry.lock, &e->entry.hash, sizeof(e->entry.hash) +
+		sizeof(e->entry.key) + sizeof(e->entry.data));
+	lock_protect(&e->entry.lock, e->key.qname, e->key.qname_len);
+	q->qname = NULL;
+	return e;
+}
+
+/** copy rrsets from replyinfo to dest replyinfo */
+static int
+repinfo_copy_rrsets(struct reply_info* dest, struct reply_info* from, 
+	struct regional* region)
+{
+	size_t i, s;
+	struct packed_rrset_data* fd, *dd;
+	struct ub_packed_rrset_key* fk, *dk;
+	for(i=0; i<dest->rrset_count; i++) {
+		fk = from->rrsets[i];
+		dk = dest->rrsets[i];
+		fd = (struct packed_rrset_data*)fk->entry.data;
+		dk->entry.hash = fk->entry.hash;
+		dk->rk = fk->rk;
+		if(region) {
+			dk->id = fk->id;
+			dk->rk.dname = (uint8_t*)regional_alloc_init(region,
+				fk->rk.dname, fk->rk.dname_len);
+		} else	
+			dk->rk.dname = (uint8_t*)memdup(fk->rk.dname, 
+				fk->rk.dname_len);
+		if(!dk->rk.dname)
+			return 0;
+		s = packed_rrset_sizeof(fd);
+		if(region)
+			dd = (struct packed_rrset_data*)regional_alloc_init(
+				region, fd, s);
+		else	dd = (struct packed_rrset_data*)memdup(fd, s);
+		if(!dd) 
+			return 0;
+		packed_rrset_ptr_fixup(dd);
+		dk->entry.data = (void*)dd;
+	}
+	return 1;
+}
+
+struct reply_info* 
+reply_info_copy(struct reply_info* rep, struct alloc_cache* alloc, 
+	struct regional* region)
+{
+	struct reply_info* cp;
+	cp = construct_reply_info_base(region, rep->flags, rep->qdcount, 
+		rep->ttl, rep->prefetch_ttl, rep->an_numrrsets, 
+		rep->ns_numrrsets, rep->ar_numrrsets, rep->rrset_count, 
+		rep->security);
+	if(!cp)
+		return NULL;
+	/* allocate ub_key structures special or not */
+	if(!repinfo_alloc_rrset_keys(cp, alloc, region)) {
+		if(!region)
+			reply_info_parsedelete(cp, alloc);
+		return NULL;
+	}
+	if(!repinfo_copy_rrsets(cp, rep, region)) {
+		if(!region)
+			reply_info_parsedelete(cp, alloc);
+		return NULL;
+	}
+	return cp;
+}
+
+uint8_t* 
+reply_find_final_cname_target(struct query_info* qinfo, struct reply_info* rep)
+{
+	uint8_t* sname = qinfo->qname;
+	size_t snamelen = qinfo->qname_len;
+	size_t i;
+	for(i=0; i<rep->an_numrrsets; i++) {
+		struct ub_packed_rrset_key* s = rep->rrsets[i];
+		/* follow CNAME chain (if any) */
+		if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME && 
+			ntohs(s->rk.rrset_class) == qinfo->qclass && 
+			snamelen == s->rk.dname_len &&
+			query_dname_compare(sname, s->rk.dname) == 0) {
+			get_cname_target(s, &sname, &snamelen);
+		}
+	}
+	if(sname != qinfo->qname)
+		return sname;
+	return NULL;
+}
+
+struct ub_packed_rrset_key* 
+reply_find_answer_rrset(struct query_info* qinfo, struct reply_info* rep)
+{
+	uint8_t* sname = qinfo->qname;
+	size_t snamelen = qinfo->qname_len;
+	size_t i;
+	for(i=0; i<rep->an_numrrsets; i++) {
+		struct ub_packed_rrset_key* s = rep->rrsets[i];
+		/* first match type, for query of qtype cname */
+		if(ntohs(s->rk.type) == qinfo->qtype && 
+			ntohs(s->rk.rrset_class) == qinfo->qclass && 
+			snamelen == s->rk.dname_len &&
+			query_dname_compare(sname, s->rk.dname) == 0) {
+			return s;
+		}
+		/* follow CNAME chain (if any) */
+		if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME && 
+			ntohs(s->rk.rrset_class) == qinfo->qclass && 
+			snamelen == s->rk.dname_len &&
+			query_dname_compare(sname, s->rk.dname) == 0) {
+			get_cname_target(s, &sname, &snamelen);
+		}
+	}
+	return NULL;
+}
+
+struct ub_packed_rrset_key* reply_find_rrset_section_an(struct reply_info* rep,
+	uint8_t* name, size_t namelen, uint16_t type, uint16_t dclass)
+{
+	size_t i;
+	for(i=0; i<rep->an_numrrsets; i++) {
+		struct ub_packed_rrset_key* s = rep->rrsets[i];
+		if(ntohs(s->rk.type) == type && 
+			ntohs(s->rk.rrset_class) == dclass && 
+			namelen == s->rk.dname_len &&
+			query_dname_compare(name, s->rk.dname) == 0) {
+			return s;
+		}
+	}
+	return NULL;
+}
+
+struct ub_packed_rrset_key* reply_find_rrset_section_ns(struct reply_info* rep,
+	uint8_t* name, size_t namelen, uint16_t type, uint16_t dclass)
+{
+	size_t i;
+	for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep->ns_numrrsets; i++) {
+		struct ub_packed_rrset_key* s = rep->rrsets[i];
+		if(ntohs(s->rk.type) == type && 
+			ntohs(s->rk.rrset_class) == dclass && 
+			namelen == s->rk.dname_len &&
+			query_dname_compare(name, s->rk.dname) == 0) {
+			return s;
+		}
+	}
+	return NULL;
+}
+
+struct ub_packed_rrset_key* reply_find_rrset(struct reply_info* rep,
+	uint8_t* name, size_t namelen, uint16_t type, uint16_t dclass)
+{
+	size_t i;
+	for(i=0; i<rep->rrset_count; i++) {
+		struct ub_packed_rrset_key* s = rep->rrsets[i];
+		if(ntohs(s->rk.type) == type && 
+			ntohs(s->rk.rrset_class) == dclass && 
+			namelen == s->rk.dname_len &&
+			query_dname_compare(name, s->rk.dname) == 0) {
+			return s;
+		}
+	}
+	return NULL;
+}
+
+void 
+log_dns_msg(const char* str, struct query_info* qinfo, struct reply_info* rep)
+{
+	/* not particularly fast but flexible, make wireformat and print */
+	ldns_buffer* buf = ldns_buffer_new(65535);
+	struct regional* region = regional_create();
+	if(!reply_info_encode(qinfo, rep, 0, rep->flags, buf, 0, 
+		region, 65535, 1)) {
+		log_info("%s: log_dns_msg: out of memory", str);
+	} else {
+		ldns_status s;
+		ldns_pkt* pkt = NULL;
+		s = ldns_buffer2pkt_wire(&pkt, buf);
+		if(s != LDNS_STATUS_OK) {
+			log_info("%s: log_dns_msg: ldns parse gave: %s",
+				str, ldns_get_errorstr_by_id(s));
+		} else {
+			ldns_buffer_clear(buf);
+			s = ldns_pkt2buffer_str(buf, pkt);
+			if(s != LDNS_STATUS_OK) {
+				log_info("%s: log_dns_msg: ldns tostr gave: %s",
+					str, ldns_get_errorstr_by_id(s));
+			} else {
+				log_info("%s %s", 
+					str, (char*)ldns_buffer_begin(buf));
+			}
+		}
+		ldns_pkt_free(pkt);
+	}
+	ldns_buffer_free(buf);
+	regional_destroy(region);
+}
+
+void 
+log_query_info(enum verbosity_value v, const char* str, 
+	struct query_info* qinf)
+{
+	log_nametypeclass(v, str, qinf->qname, qinf->qtype, qinf->qclass);
+}
+
+int
+reply_check_cname_chain(struct reply_info* rep) 
+{
+	/* check only answer section rrs for matching cname chain.
+	 * the cache may return changed rdata, but owner names are untouched.*/
+	size_t i;
+	uint8_t* sname = rep->rrsets[0]->rk.dname;
+	size_t snamelen = rep->rrsets[0]->rk.dname_len;
+	for(i=0; i<rep->an_numrrsets; i++) {
+		uint16_t t = ntohs(rep->rrsets[i]->rk.type);
+		if(t == LDNS_RR_TYPE_DNAME)
+			continue; /* skip dnames; note TTL 0 not cached */
+		/* verify that owner matches current sname */
+		if(query_dname_compare(sname, rep->rrsets[i]->rk.dname) != 0){
+			/* cname chain broken */
+			return 0;
+		}
+		/* if this is a cname; move on */
+		if(t == LDNS_RR_TYPE_CNAME) {
+			get_cname_target(rep->rrsets[i], &sname, &snamelen);
+		}
+	}
+	return 1;
+}
+
+int
+reply_all_rrsets_secure(struct reply_info* rep) 
+{
+	size_t i;
+	for(i=0; i<rep->rrset_count; i++) {
+		if( ((struct packed_rrset_data*)rep->rrsets[i]->entry.data)
+			->security != sec_status_secure )
+		return 0;
+	}
+	return 1;
+}
diff --git a/3rdParty/Unbound/src/src/util/data/msgreply.h b/3rdParty/Unbound/src/src/util/data/msgreply.h
new file mode 100644
index 0000000..a32f2b1
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/data/msgreply.h
@@ -0,0 +1,417 @@
+/*
+ * util/data/msgreply.h - store message and reply data. 
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains a data structure to store a message and its reply.
+ */
+
+#ifndef UTIL_DATA_MSGREPLY_H
+#define UTIL_DATA_MSGREPLY_H
+#include "util/storage/lruhash.h"
+#include "util/data/packed_rrset.h"
+struct comm_reply;
+struct alloc_cache;
+struct iovec;
+struct regional;
+struct edns_data;
+struct msg_parse;
+struct rrset_parse;
+
+/** calculate the prefetch TTL as 90% of original. Calculation
+ * without numerical overflow (uin32_t) */
+#define PREFETCH_TTL_CALC(ttl) ((ttl) - (ttl)/10)
+
+/**
+ * Structure to store query information that makes answers to queries
+ * different.
+ */
+struct query_info {
+	/** 
+	 * Salient data on the query: qname, in wireformat. 
+	 * can be allocated or a pointer to outside buffer.
+	 * User has to keep track on the status of this.
+	 */
+	uint8_t* qname;
+	/** length of qname (including last 0 octet) */
+	size_t qname_len;
+	/** qtype, host byte order */
+	uint16_t qtype;
+	/** qclass, host byte order */
+	uint16_t qclass;
+};
+
+/**
+ * Information to reference an rrset
+ */
+struct rrset_ref {
+	/** the key with lock, and ptr to packed data. */
+	struct ub_packed_rrset_key* key;
+	/** id needed */
+	rrset_id_t id;
+};
+
+/**
+ * Structure to store DNS query and the reply packet.
+ * To use it, copy over the flags from reply and modify using flags from
+ * the query (RD,CD if not AA). prepend ID. 
+ *
+ * Memory layout is:
+ *	o struct
+ *	o rrset_ref array
+ *	o packed_rrset_key* array.
+ *
+ * Memory layout is sometimes not packed, when the message is synthesized,
+ * for easy of the generation. It is allocated packed when it is copied
+ * from the region allocation to the malloc allocation.
+ */
+struct reply_info {
+	/** the flags for the answer, host byte order. */
+	uint16_t flags;
+
+	/**
+	 * This flag informs unbound the answer is authoritative and 
+	 * the AA flag should be preserved. 
+	 */
+	uint8_t authoritative;
+
+	/**
+	 * Number of RRs in the query section.
+	 * If qdcount is not 0, then it is 1, and the data that appears
+	 * in the reply is the same as the query_info.
+	 * Host byte order.
+	 */
+	uint8_t qdcount;
+
+	/** 
+	 * TTL of the entire reply (for negative caching).
+	 * only for use when there are 0 RRsets in this message.
+	 * if there are RRsets, check those instead.
+	 */
+	uint32_t ttl;
+
+	/**
+	 * TTL for prefetch. After it has expired, a prefetch is suitable.
+	 * Smaller than the TTL, otherwise the prefetch would not happen.
+	 */
+	uint32_t prefetch_ttl;
+
+	/** 32 bit padding to pad struct member alignment to 64 bits. */
+	uint32_t padding;
+
+	/**
+	 * The security status from DNSSEC validation of this message.
+	 */
+	enum sec_status security;
+
+	/**
+	 * Number of RRsets in each section.
+	 * The answer section. Add up the RRs in every RRset to calculate
+	 * the number of RRs, and the count for the dns packet. 
+	 * The number of RRs in RRsets can change due to RRset updates.
+	 */
+	size_t an_numrrsets;
+
+	/** Count of authority section RRsets */
+	size_t ns_numrrsets; 
+	/** Count of additional section RRsets */
+	size_t ar_numrrsets;
+
+	/** number of RRsets: an_numrrsets + ns_numrrsets + ar_numrrsets */
+	size_t rrset_count;
+
+	/** 
+	 * List of pointers (only) to the rrsets in the order in which 
+	 * they appear in the reply message.  
+	 * Number of elements is ancount+nscount+arcount RRsets.
+	 * This is a pointer to that array. 
+	 * Use the accessor function for access.
+	 */
+	struct ub_packed_rrset_key** rrsets;
+
+	/** 
+	 * Packed array of ids (see counts) and pointers to packed_rrset_key.
+	 * The number equals ancount+nscount+arcount RRsets. 
+	 * These are sorted in ascending pointer, the locking order. So
+	 * this list can be locked (and id, ttl checked), to see if 
+	 * all the data is available and recent enough.
+	 *
+	 * This is defined as an array of size 1, so that the compiler 
+	 * associates the identifier with this position in the structure.
+	 * Array bound overflow on this array then gives access to the further
+	 * elements of the array, which are allocated after the main structure.
+	 *
+	 * It could be more pure to define as array of size 0, ref[0].
+	 * But ref[1] may be less confusing for compilers.
+	 * Use the accessor function for access.
+	 */
+	struct rrset_ref ref[1];
+};
+
+/**
+ * Structure to keep hash table entry for message replies.
+ */
+struct msgreply_entry {
+	/** the hash table key */
+	struct query_info key;
+	/** the hash table entry, data is struct reply_info* */
+	struct lruhash_entry entry;
+};
+
+/** 
+ * Parse wire query into a queryinfo structure, return 0 on parse error. 
+ * initialises the (prealloced) queryinfo structure as well.
+ * This query structure contains a pointer back info the buffer!
+ * This pointer avoids memory allocation. allocqname does memory allocation.
+ * @param m: the prealloced queryinfo structure to put query into.
+ *    must be unused, or _clear()ed.
+ * @param query: the wireformat packet query. starts with ID.
+ * @return: 0 on format error.
+ */
+int query_info_parse(struct query_info* m, ldns_buffer* query);
+
+/**
+ * Parse query reply.
+ * Fills in preallocated query_info structure (with ptr into buffer).
+ * Allocates reply_info and packed_rrsets. These are not yet added to any
+ * caches or anything, this is only parsing. Returns formerror on qdcount > 1.
+ * @param pkt: the packet buffer. Must be positioned after the query section.
+ * @param alloc: creates packed rrset key structures.
+ * @param rep: allocated reply_info is returned (only on no error).
+ * @param qinf: query_info is returned (only on no error).
+ * @param region: where to store temporary data (for parsing).
+ * @param edns: where to store edns information, does not need to be inited.
+ * @return: zero is OK, or DNS error code in case of error
+ *	o FORMERR for parse errors.
+ *	o SERVFAIL for memory allocation errors.
+ */
+int reply_info_parse(ldns_buffer* pkt, struct alloc_cache* alloc,
+	struct query_info* qinf, struct reply_info** rep, 
+	struct regional* region, struct edns_data* edns);
+
+/**
+ * Allocate and decompress parsed message and rrsets.
+ * @param pkt: for name decompression.
+ * @param msg: parsed message in scratch region.
+ * @param alloc: alloc cache for special rrset key structures.
+ *	Not used if region!=NULL, it can be NULL in that case.
+ * @param qinf: where to store query info.
+ *	qinf itself is allocated by the caller.
+ * @param rep: reply info is allocated and returned.
+ * @param region: if this parameter is NULL then malloc and the alloc is used.
+ *	otherwise, everything is allocated in this region.
+ *	In a region, no special rrset key structures are needed (not shared),
+ *	and no rrset_ref array in the reply is built up.
+ * @return 0 if allocation failed.
+ */
+int parse_create_msg(ldns_buffer* pkt, struct msg_parse* msg,
+        struct alloc_cache* alloc, struct query_info* qinf,
+	struct reply_info** rep, struct regional* region);
+
+/**
+ * Sorts the ref array.
+ * @param rep: reply info. rrsets must be filled in.
+ */
+void reply_info_sortref(struct reply_info* rep);
+
+/**
+ * Set TTLs inside the replyinfo to absolute values.
+ * @param rep: reply info. rrsets must be filled in. 
+ *	Also refs must be filled in.
+ * @param timenow: the current time.
+ */
+void reply_info_set_ttls(struct reply_info* rep, uint32_t timenow);
+
+/** 
+ * Delete reply_info and packed_rrsets (while they are not yet added to the
+ * hashtables.). Returns rrsets to the alloc cache.
+ * @param rep: reply_info to delete.
+ * @param alloc: where to return rrset structures to.
+ */
+void reply_info_parsedelete(struct reply_info* rep, struct alloc_cache* alloc);
+
+/**
+ * Compare two queryinfo structures, on query and type, class. 
+ * It is _not_ sorted in canonical ordering.
+ * @param m1: struct query_info* , void* here to ease use as function pointer.
+ * @param m2: struct query_info* , void* here to ease use as function pointer.
+ * @return: 0 = same, -1 m1 is smaller, +1 m1 is larger.
+ */
+int query_info_compare(void* m1, void* m2);
+
+/** clear out query info structure */
+void query_info_clear(struct query_info* m);
+
+/** calculate size of struct query_info + reply_info */
+size_t msgreply_sizefunc(void* k, void* d);
+
+/** delete msgreply_entry key structure */
+void query_entry_delete(void *q, void* arg);
+
+/** delete reply_info data structure */
+void reply_info_delete(void* d, void* arg);
+
+/** calculate hash value of query_info, lowercases the qname */
+hashvalue_t query_info_hash(struct query_info *q);
+
+/**
+ * Setup query info entry
+ * @param q: query info to copy. Emptied as if clear is called.
+ * @param r: reply to init data.
+ * @param h: hash value.
+ * @return: newly allocated message reply cache item.
+ */
+struct msgreply_entry* query_info_entrysetup(struct query_info* q,
+	struct reply_info* r, hashvalue_t h);
+
+/**
+ * Copy reply_info and all rrsets in it and allocate.
+ * @param rep: what to copy, probably inside region, no ref[] array in it.
+ * @param alloc: how to allocate rrset keys.
+ *	Not used if region!=NULL, it can be NULL in that case.
+ * @param region: if this parameter is NULL then malloc and the alloc is used.
+ *	otherwise, everything is allocated in this region.
+ *	In a region, no special rrset key structures are needed (not shared),
+ *	and no rrset_ref array in the reply is built up.
+ * @return new reply info or NULL on memory error.
+ */
+struct reply_info* reply_info_copy(struct reply_info* rep, 
+	struct alloc_cache* alloc, struct regional* region);
+
+/**
+ * Copy a parsed rrset into given key, decompressing and allocating rdata.
+ * @param pkt: packet for decompression
+ * @param msg: the parser message (for flags for trust).
+ * @param pset: the parsed rrset to copy.
+ * @param region: if NULL - malloc, else data is allocated in this region.
+ * @param pk: a freshly obtained rrsetkey structure. No dname is set yet,
+ *	will be set on return.
+ *	Note that TTL will still be relative on return.
+ * @return false on alloc failure.
+ */
+int parse_copy_decompress_rrset(ldns_buffer* pkt, struct msg_parse* msg,
+	struct rrset_parse *pset, struct regional* region, 
+	struct ub_packed_rrset_key* pk);
+
+/**
+ * Find final cname target in reply, the one matching qinfo. Follows CNAMEs.
+ * @param qinfo: what to start with.
+ * @param rep: looks in answer section of this message.
+ * @return: pointer dname, or NULL if not found.
+ */
+uint8_t* reply_find_final_cname_target(struct query_info* qinfo,
+	struct reply_info* rep);
+
+/**
+ * Check if cname chain in cached reply is still valid.
+ * @param rep: reply to check.
+ * @return: true if valid, false if invalid.
+ */
+int reply_check_cname_chain(struct reply_info* rep);
+
+/**
+ * Check security status of all RRs in the message.
+ * @param rep: reply to check
+ * @return: true if all RRs are secure. False if not.
+ *    True if there are zero RRs.
+ */
+int reply_all_rrsets_secure(struct reply_info* rep);
+
+/**
+ * Find answer rrset in reply, the one matching qinfo. Follows CNAMEs, so the
+ * result may have a different owner name.
+ * @param qinfo: what to look for.
+ * @param rep: looks in answer section of this message.
+ * @return: pointer to rrset, or NULL if not found.
+ */
+struct ub_packed_rrset_key* reply_find_answer_rrset(struct query_info* qinfo,
+	struct reply_info* rep);
+
+/**
+ * Find rrset in reply, inside the answer section. Does not follow CNAMEs.
+ * @param rep: looks in answer section of this message.
+ * @param name: what to look for.
+ * @param namelen: length of name.
+ * @param type: looks for (host order).
+ * @param dclass: looks for (host order).
+ * @return: pointer to rrset, or NULL if not found.
+ */
+struct ub_packed_rrset_key* reply_find_rrset_section_an(struct reply_info* rep,
+	uint8_t* name, size_t namelen, uint16_t type, uint16_t dclass);
+
+/**
+ * Find rrset in reply, inside the authority section. Does not follow CNAMEs.
+ * @param rep: looks in authority section of this message.
+ * @param name: what to look for.
+ * @param namelen: length of name.
+ * @param type: looks for (host order).
+ * @param dclass: looks for (host order).
+ * @return: pointer to rrset, or NULL if not found.
+ */
+struct ub_packed_rrset_key* reply_find_rrset_section_ns(struct reply_info* rep,
+	uint8_t* name, size_t namelen, uint16_t type, uint16_t dclass);
+
+/**
+ * Find rrset in reply, inside any section. Does not follow CNAMEs.
+ * @param rep: looks in answer,authority and additional section of this message.
+ * @param name: what to look for.
+ * @param namelen: length of name.
+ * @param type: looks for (host order).
+ * @param dclass: looks for (host order).
+ * @return: pointer to rrset, or NULL if not found.
+ */
+struct ub_packed_rrset_key* reply_find_rrset(struct reply_info* rep,
+	uint8_t* name, size_t namelen, uint16_t type, uint16_t dclass);
+
+/**
+ * Debug send the query info and reply info to the log in readable form.
+ * @param str: descriptive string printed with packet content.
+ * @param qinfo: query section.
+ * @param rep: rest of message.
+ */
+void log_dns_msg(const char* str, struct query_info* qinfo, 
+	struct reply_info* rep);
+
+/**
+ * Print string with neat domain name, type, class from query info.
+ * @param v: at what verbosity level to print this.
+ * @param str: string of message.
+ * @param qinf: query info structure with name, type and class.
+ */
+void log_query_info(enum verbosity_value v, const char* str, 
+	struct query_info* qinf);
+
+#endif /* UTIL_DATA_MSGREPLY_H */
diff --git a/3rdParty/Unbound/src/src/util/data/packed_rrset.c b/3rdParty/Unbound/src/src/util/data/packed_rrset.c
new file mode 100644
index 0000000..e1fc2e5
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/data/packed_rrset.c
@@ -0,0 +1,488 @@
+/*
+ * util/data/packed_rrset.c - data storage for a set of resource records.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains the data storage for RRsets.
+ */
+
+#include "config.h"
+#include <ldns/wire2host.h>
+#include "util/data/packed_rrset.h"
+#include "util/data/dname.h"
+#include "util/storage/lookup3.h"
+#include "util/log.h"
+#include "util/alloc.h"
+#include "util/regional.h"
+#include "util/net_help.h"
+
+void
+ub_packed_rrset_parsedelete(struct ub_packed_rrset_key* pkey,
+        struct alloc_cache* alloc)
+{
+	if(!pkey)
+		return;
+	if(pkey->entry.data)
+		free(pkey->entry.data);
+	pkey->entry.data = NULL;
+	if(pkey->rk.dname)
+		free(pkey->rk.dname);
+	pkey->rk.dname = NULL;
+	pkey->id = 0;
+	alloc_special_release(alloc, pkey);
+}
+
+size_t 
+ub_rrset_sizefunc(void* key, void* data)
+{
+	struct ub_packed_rrset_key* k = (struct ub_packed_rrset_key*)key;
+	struct packed_rrset_data* d = (struct packed_rrset_data*)data;
+	size_t s = sizeof(struct ub_packed_rrset_key) + k->rk.dname_len;
+	s += packed_rrset_sizeof(d) + lock_get_mem(&k->entry.lock);
+	return s;
+}
+
+size_t 
+packed_rrset_sizeof(struct packed_rrset_data* d)
+{
+	size_t s;
+	if(d->rrsig_count > 0) {
+		s = ((uint8_t*)d->rr_data[d->count+d->rrsig_count-1] - 
+			(uint8_t*)d) + d->rr_len[d->count+d->rrsig_count-1];
+	} else {
+		log_assert(d->count > 0);
+		s = ((uint8_t*)d->rr_data[d->count-1] - (uint8_t*)d) + 
+			d->rr_len[d->count-1];
+	}
+	return s;
+}
+
+int 
+ub_rrset_compare(void* k1, void* k2)
+{
+	struct ub_packed_rrset_key* key1 = (struct ub_packed_rrset_key*)k1;
+	struct ub_packed_rrset_key* key2 = (struct ub_packed_rrset_key*)k2;
+	int c;
+	if(key1 == key2)
+		return 0;
+	if(key1->rk.type != key2->rk.type) {
+		if(key1->rk.type < key2->rk.type)
+			return -1;
+		return 1;
+	}
+	if(key1->rk.dname_len != key2->rk.dname_len) {
+		if(key1->rk.dname_len < key2->rk.dname_len)
+			return -1;
+		return 1;
+	}
+	if((c=query_dname_compare(key1->rk.dname, key2->rk.dname)) != 0)
+		return c;
+	if(key1->rk.rrset_class != key2->rk.rrset_class) {
+		if(key1->rk.rrset_class < key2->rk.rrset_class)
+			return -1;
+		return 1;
+	}
+	if(key1->rk.flags != key2->rk.flags) {
+		if(key1->rk.flags < key2->rk.flags)
+			return -1;
+		return 1;
+	}
+	return 0;
+}
+
+void 
+ub_rrset_key_delete(void* key, void* userdata)
+{
+	struct ub_packed_rrset_key* k = (struct ub_packed_rrset_key*)key;
+	struct alloc_cache* a = (struct alloc_cache*)userdata;
+	k->id = 0;
+	free(k->rk.dname);
+	k->rk.dname = NULL;
+	alloc_special_release(a, k);
+}
+
+void 
+rrset_data_delete(void* data, void* ATTR_UNUSED(userdata))
+{
+	struct packed_rrset_data* d = (struct packed_rrset_data*)data;
+	free(d);
+}
+
+int 
+rrsetdata_equal(struct packed_rrset_data* d1, struct packed_rrset_data* d2)
+{
+	size_t i;
+	size_t total;
+	if(d1->count != d2->count || d1->rrsig_count != d2->rrsig_count) 
+		return 0;
+	total = d1->count + d1->rrsig_count;
+	for(i=0; i<total; i++) {
+		if(d1->rr_len[i] != d2->rr_len[i])
+			return 0;
+		if(memcmp(d1->rr_data[i], d2->rr_data[i], d1->rr_len[i]) != 0)
+			return 0;
+	}
+	return 1;
+}
+
+hashvalue_t 
+rrset_key_hash(struct packed_rrset_key* key)
+{
+	/* type is hashed in host order */
+	uint16_t t = ntohs(key->type);
+	/* Note this MUST be identical to pkt_hash_rrset in msgparse.c */
+	/* this routine does not have a compressed name */
+	hashvalue_t h = 0xab;
+	h = dname_query_hash(key->dname, h);
+	h = hashlittle(&t, sizeof(t), h);
+	h = hashlittle(&key->rrset_class, sizeof(uint16_t), h);
+	h = hashlittle(&key->flags, sizeof(uint32_t), h);
+	return h;
+}
+
+void 
+packed_rrset_ptr_fixup(struct packed_rrset_data* data)
+{
+	size_t i;
+	size_t total = data->count + data->rrsig_count;
+	uint8_t* nextrdata;
+	/* fixup pointers in packed rrset data */
+	data->rr_len = (size_t*)((uint8_t*)data +
+		sizeof(struct packed_rrset_data));
+	data->rr_data = (uint8_t**)&(data->rr_len[total]);
+	data->rr_ttl = (uint32_t*)&(data->rr_data[total]);
+	nextrdata = (uint8_t*)&(data->rr_ttl[total]);
+	for(i=0; i<total; i++) {
+		data->rr_data[i] = nextrdata;
+		nextrdata += data->rr_len[i];
+	}
+}
+
+void 
+get_cname_target(struct ub_packed_rrset_key* rrset, uint8_t** dname, 
+	size_t* dname_len)
+{
+	struct packed_rrset_data* d;
+	size_t len;
+	if(ntohs(rrset->rk.type) != LDNS_RR_TYPE_CNAME && 
+		ntohs(rrset->rk.type) != LDNS_RR_TYPE_DNAME)
+		return;
+	d = (struct packed_rrset_data*)rrset->entry.data;
+	if(d->count < 1)
+		return;
+	if(d->rr_len[0] < 3) /* at least rdatalen + 0byte root label */
+		return;
+	len = ldns_read_uint16(d->rr_data[0]);
+	if(len != d->rr_len[0] - sizeof(uint16_t))
+		return;
+	if(dname_valid(d->rr_data[0]+sizeof(uint16_t), len) != len)
+		return;
+	*dname = d->rr_data[0]+sizeof(uint16_t);
+	*dname_len = len;
+}
+
+void 
+packed_rrset_ttl_add(struct packed_rrset_data* data, uint32_t add)
+{
+	size_t i;
+	size_t total = data->count + data->rrsig_count;
+	data->ttl += add;
+	for(i=0; i<total; i++)
+		data->rr_ttl[i] += add;
+}
+
+const char* 
+rrset_trust_to_string(enum rrset_trust s)
+{
+	switch(s) {
+	case rrset_trust_none: 		return "rrset_trust_none";
+	case rrset_trust_add_noAA: 	return "rrset_trust_add_noAA";
+	case rrset_trust_auth_noAA: 	return "rrset_trust_auth_noAA";
+	case rrset_trust_add_AA: 	return "rrset_trust_add_AA";
+	case rrset_trust_nonauth_ans_AA:return "rrset_trust_nonauth_ans_AA";
+	case rrset_trust_ans_noAA: 	return "rrset_trust_ans_noAA";
+	case rrset_trust_glue: 		return "rrset_trust_glue";
+	case rrset_trust_auth_AA: 	return "rrset_trust_auth_AA";
+	case rrset_trust_ans_AA: 	return "rrset_trust_ans_AA";
+	case rrset_trust_sec_noglue: 	return "rrset_trust_sec_noglue";
+	case rrset_trust_prim_noglue: 	return "rrset_trust_prim_noglue";
+	case rrset_trust_validated: 	return "rrset_trust_validated";
+	case rrset_trust_ultimate: 	return "rrset_trust_ultimate";
+	}
+	return "unknown_rrset_trust_value";
+}
+
+const char* 
+sec_status_to_string(enum sec_status s)
+{
+	switch(s) {
+	case sec_status_unchecked: 	return "sec_status_unchecked";
+	case sec_status_bogus: 		return "sec_status_bogus";
+	case sec_status_indeterminate: 	return "sec_status_indeterminate";
+	case sec_status_insecure: 	return "sec_status_insecure";
+	case sec_status_secure: 	return "sec_status_secure";
+	}
+	return "unknown_sec_status_value";
+}
+
+void log_rrset_key(enum verbosity_value v, const char* str, 
+	struct ub_packed_rrset_key* rrset)
+{
+	if(verbosity >= v)
+		log_nametypeclass(v, str, rrset->rk.dname,
+			ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class));
+}
+
+uint32_t 
+ub_packed_rrset_ttl(struct ub_packed_rrset_key* key)
+{
+	struct packed_rrset_data* d = (struct packed_rrset_data*)key->
+		entry.data;
+	return d->ttl;
+}
+
+struct ub_packed_rrset_key*
+packed_rrset_copy_region(struct ub_packed_rrset_key* key, 
+	struct regional* region, uint32_t now)
+{
+	struct ub_packed_rrset_key* ck = regional_alloc(region, 
+		sizeof(struct ub_packed_rrset_key));
+	struct packed_rrset_data* d;
+	struct packed_rrset_data* data = (struct packed_rrset_data*)
+		key->entry.data;
+	size_t dsize, i;
+	if(!ck)
+		return NULL;
+	ck->id = key->id;
+	memset(&ck->entry, 0, sizeof(ck->entry));
+	ck->entry.hash = key->entry.hash;
+	ck->entry.key = ck;
+	ck->rk = key->rk;
+	ck->rk.dname = regional_alloc_init(region, key->rk.dname, 
+		key->rk.dname_len);
+	if(!ck->rk.dname)
+		return NULL;
+	dsize = packed_rrset_sizeof(data);
+	d = (struct packed_rrset_data*)regional_alloc_init(region, data, dsize);
+	if(!d)
+		return NULL;
+	ck->entry.data = d;
+	packed_rrset_ptr_fixup(d);
+	/* make TTLs relative - once per rrset */
+	for(i=0; i<d->count + d->rrsig_count; i++) {
+		if(d->rr_ttl[i] < now)
+			d->rr_ttl[i] = 0;
+		else	d->rr_ttl[i] -= now;
+	}
+	if(d->ttl < now)
+		d->ttl = 0;
+	else	d->ttl -= now;
+	return ck;
+}
+
+struct ub_packed_rrset_key* 
+packed_rrset_copy_alloc(struct ub_packed_rrset_key* key, 
+	struct alloc_cache* alloc, uint32_t now)
+{
+	struct packed_rrset_data* fd, *dd;
+	struct ub_packed_rrset_key* dk = alloc_special_obtain(alloc);
+	if(!dk) return NULL;
+	fd = (struct packed_rrset_data*)key->entry.data;
+	dk->entry.hash = key->entry.hash;
+	dk->rk = key->rk;
+	dk->rk.dname = (uint8_t*)memdup(key->rk.dname, key->rk.dname_len);
+	if(!dk->rk.dname) {
+		alloc_special_release(alloc, dk);
+		return NULL;
+	}
+	dd = (struct packed_rrset_data*)memdup(fd, packed_rrset_sizeof(fd));
+	if(!dd) {
+		free(dk->rk.dname);
+		alloc_special_release(alloc, dk);
+		return NULL;
+	}
+	packed_rrset_ptr_fixup(dd);
+	dk->entry.data = (void*)dd;
+	packed_rrset_ttl_add(dd, now);
+	return dk;
+}
+
+struct ub_packed_rrset_key* 
+ub_packed_rrset_heap_key(ldns_rr_list* rrset)
+{
+	struct ub_packed_rrset_key* k;
+	ldns_rr* rr;
+	if(!rrset)
+		return NULL;
+	rr = ldns_rr_list_rr(rrset, 0);
+	if(!rr)
+		return NULL;
+	k = (struct ub_packed_rrset_key*)calloc(1, sizeof(*k));
+	if(!k)
+		return NULL;
+	k->rk.type = htons(ldns_rr_get_type(rr));
+	k->rk.rrset_class = htons(ldns_rr_get_class(rr));
+	k->rk.dname_len = ldns_rdf_size(ldns_rr_owner(rr));
+	k->rk.dname = memdup(ldns_rdf_data(ldns_rr_owner(rr)),
+		ldns_rdf_size(ldns_rr_owner(rr)));
+	if(!k->rk.dname) {
+		free(k);
+		return NULL;
+	}
+	return k;
+}
+
+struct packed_rrset_data* 
+packed_rrset_heap_data(ldns_rr_list* rrset)
+{
+	struct packed_rrset_data* data;
+	size_t count=0, rrsig_count=0, len=0, i, j, total;
+	uint8_t* nextrdata;
+	if(!rrset || ldns_rr_list_rr_count(rrset)==0)
+		return NULL;
+	/* count sizes */
+	for(i=0; i<ldns_rr_list_rr_count(rrset); i++) {
+		ldns_rr* rr = ldns_rr_list_rr(rrset, i);
+		if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_RRSIG)
+			rrsig_count++;
+		else 	count++;
+		for(j=0; j<ldns_rr_rd_count(rr); j++)
+			len += ldns_rdf_size(ldns_rr_rdf(rr, j));
+		len += 2; /* sizeof the rdlength */
+	}
+
+	/* allocate */
+	total = count + rrsig_count;
+	len += sizeof(*data) + total*(sizeof(size_t) + sizeof(uint32_t) + 
+		sizeof(uint8_t*));
+	data = (struct packed_rrset_data*)calloc(1, len);
+	if(!data)
+		return NULL;
+	
+	/* fill it */
+	data->ttl = ldns_rr_ttl(ldns_rr_list_rr(rrset, 0));
+	data->count = count;
+	data->rrsig_count = rrsig_count;
+	data->rr_len = (size_t*)((uint8_t*)data +
+		sizeof(struct packed_rrset_data));
+	data->rr_data = (uint8_t**)&(data->rr_len[total]);
+	data->rr_ttl = (uint32_t*)&(data->rr_data[total]);
+	nextrdata = (uint8_t*)&(data->rr_ttl[total]);
+
+	/* fill out len, ttl, fields */
+	for(i=0; i<total; i++) {
+		ldns_rr* rr = ldns_rr_list_rr(rrset, i);
+		data->rr_ttl[i] = ldns_rr_ttl(rr);
+		if(data->rr_ttl[i] < data->ttl)
+			data->ttl = data->rr_ttl[i];
+		data->rr_len[i] = 2; /* the rdlength */
+		for(j=0; j<ldns_rr_rd_count(rr); j++)
+			data->rr_len[i] += ldns_rdf_size(ldns_rr_rdf(rr, j));
+	}
+
+	/* fixup rest of ptrs */
+	for(i=0; i<total; i++) {
+		data->rr_data[i] = nextrdata;
+		nextrdata += data->rr_len[i];
+	}
+
+	/* copy data in there */
+	for(i=0; i<total; i++) {
+		ldns_rr* rr = ldns_rr_list_rr(rrset, i);
+		uint16_t rdlen = htons(data->rr_len[i]-2);
+		size_t p = sizeof(rdlen);
+		memmove(data->rr_data[i], &rdlen, p);
+		for(j=0; j<ldns_rr_rd_count(rr); j++) {
+			ldns_rdf* rd = ldns_rr_rdf(rr, j);
+			memmove(data->rr_data[i]+p, ldns_rdf_data(rd),
+				ldns_rdf_size(rd));
+			p += ldns_rdf_size(rd);
+		}
+	}
+
+	if(data->rrsig_count && data->count == 0) {
+		data->count = data->rrsig_count; /* rrset type is RRSIG */
+		data->rrsig_count = 0;
+	}
+	return data;
+}
+
+/** convert i'th rr to ldns_rr */
+static ldns_rr*
+torr(struct ub_packed_rrset_key* k, ldns_buffer* buf, size_t i)
+{
+	struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data;
+	ldns_rr* rr = NULL;
+	size_t pos = 0;
+	ldns_status s;
+	ldns_buffer_clear(buf);
+	ldns_buffer_write(buf, k->rk.dname, k->rk.dname_len);
+	if(i < d->count)
+		ldns_buffer_write(buf, &k->rk.type, sizeof(uint16_t));
+	else 	ldns_buffer_write_u16(buf, LDNS_RR_TYPE_RRSIG);
+	ldns_buffer_write(buf, &k->rk.rrset_class, sizeof(uint16_t));
+	ldns_buffer_write_u32(buf, d->rr_ttl[i]);
+	ldns_buffer_write(buf, d->rr_data[i], d->rr_len[i]);
+	ldns_buffer_flip(buf);
+	s = ldns_wire2rr(&rr, ldns_buffer_begin(buf), ldns_buffer_limit(buf),
+		&pos, LDNS_SECTION_ANSWER);
+	if(s == LDNS_STATUS_OK)
+		return rr;
+	return NULL;
+}
+
+ldns_rr_list* 
+packed_rrset_to_rr_list(struct ub_packed_rrset_key* k, ldns_buffer* buf)
+{
+	struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data;
+	ldns_rr_list* r = ldns_rr_list_new();
+	size_t i;
+	if(!r)
+		return NULL;
+	for(i=0; i<d->count+d->rrsig_count; i++) {
+		ldns_rr* rr = torr(k, buf, i);
+		if(!rr) {
+			ldns_rr_list_deep_free(r);
+			return NULL;
+		}
+		if(!ldns_rr_list_push_rr(r, rr)) {
+			ldns_rr_free(rr);
+			ldns_rr_list_deep_free(r);
+			return NULL;
+		}
+	}
+	return r;
+}
diff --git a/3rdParty/Unbound/src/src/util/data/packed_rrset.h b/3rdParty/Unbound/src/src/util/data/packed_rrset.h
new file mode 100644
index 0000000..ad11a80
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/data/packed_rrset.h
@@ -0,0 +1,434 @@
+/*
+ * util/data/packed_rrset.h - data storage for a set of resource records.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains the data storage for RRsets.
+ */
+
+#ifndef UTIL_DATA_PACKED_RRSET_H
+#define UTIL_DATA_PACKED_RRSET_H
+#include "util/storage/lruhash.h"
+#include <ldns/rr.h>
+struct alloc_cache;
+struct regional;
+
+/** type used to uniquely identify rrsets. Cannot be reused without
+ * clearing the cache. */
+typedef uint64_t rrset_id_t;
+
+/** this rrset is NSEC and is at zone apex (at child side of zonecut) */
+#define PACKED_RRSET_NSEC_AT_APEX 0x1
+/** this rrset is A/AAAA and is in-zone-glue (from parent side of zonecut) */
+#define PACKED_RRSET_PARENT_SIDE 0x2
+/** this rrset is SOA and has the negative ttl (from nxdomain or nodata),
+ * this is set on SOA rrsets in the authority section, to keep its TTL separate
+ * from the SOA in the answer section from a direct SOA query or ANY query. */
+#define PACKED_RRSET_SOA_NEG 0x4
+
+/**
+ * The identifying information for an RRset.
+ */
+struct packed_rrset_key {
+	/**
+	 * The domain name. If not null (for id=0) it is allocated, and
+	 * contains the wireformat domain name.
+	 * This dname is not canonicalized.
+	 */
+	uint8_t* dname;
+	/** 
+	 * Length of the domain name, including last 0 root octet. 
+	 */
+	size_t dname_len;
+	/**
+	 * Flags. 32bit to be easy for hashing:
+	 * 	o PACKED_RRSET_NSEC_AT_APEX
+	 * 	o PACKED_RRSET_PARENT_SIDE
+	 * 	o PACKED_RRSET_SOA_NEG
+	 */
+	uint32_t flags;
+	/** the rrset type in network format */
+	uint16_t type;
+	/** the rrset class in network format */
+	uint16_t rrset_class;
+};
+
+/**
+ * This structure contains an RRset. A set of resource records that
+ * share the same domain name, type and class.
+ *
+ * Due to memory management and threading, the key structure cannot be
+ * deleted, although the data can be. The id can be set to 0 to store and the
+ * structure can be recycled with a new id.
+ */
+struct ub_packed_rrset_key {
+	/** 
+	 * entry into hashtable. Note the lock is never destroyed,
+	 *  even when this key is retired to the cache. 
+	 * the data pointer (if not null) points to a struct packed_rrset.
+	 */
+	struct lruhash_entry entry;
+	/** 
+	 * the ID of this rrset. unique, based on threadid + sequenceno. 
+	 * ids are not reused, except after flushing the cache.
+	 * zero is an unused entry, and never a valid id.
+	 * Check this value after getting entry.lock.
+	 * The other values in this struct may only be altered after changing
+	 * the id (which needs a writelock on entry.lock).
+	 */
+	rrset_id_t id;
+	/** key data: dname, type and class */
+	struct packed_rrset_key rk;
+};
+
+/**
+ * RRset trustworthiness. Bigger value is more trust. RFC 2181.
+ * The rrset_trust_add_noAA, rrset_trust_auth_noAA, rrset_trust_add_AA,
+ * are mentioned as the same trustworthiness in 2181, but split up here
+ * for ease of processing.
+ *
+ * rrset_trust_nonauth_ans_AA, rrset_trust_ans_noAA
+ * are also mentioned as the same trustworthiness in 2181, but split up here
+ * for ease of processing.
+ *
+ * Added trust_none for a sane initial value, smaller than anything else.
+ * Added validated and ultimate trust for keys and rrsig validated content.
+ */
+enum rrset_trust {
+	/** initial value for trust */
+	rrset_trust_none = 0,
+	/** Additional information from non-authoritative answers */
+	rrset_trust_add_noAA,
+	/** Data from the authority section of a non-authoritative answer */
+	rrset_trust_auth_noAA,
+	/** Additional information from an authoritative answer */
+	rrset_trust_add_AA,
+	/** non-authoritative data from the answer section of authoritative
+	 * answers */
+	rrset_trust_nonauth_ans_AA,
+	/** Data from the answer section of a non-authoritative answer */
+	rrset_trust_ans_noAA,
+	/** Glue from a primary zone, or glue from a zone transfer */
+	rrset_trust_glue,
+	/** Data from the authority section of an authoritative answer */
+	rrset_trust_auth_AA,
+	/** The authoritative data included in the answer section of an
+	 *  authoritative reply */
+	rrset_trust_ans_AA,
+	/** Data from a zone transfer, other than glue */
+	rrset_trust_sec_noglue,
+	/** Data from a primary zone file, other than glue data */
+	rrset_trust_prim_noglue,
+	/** DNSSEC(rfc4034) validated with trusted keys */
+	rrset_trust_validated,
+	/** ultimately trusted, no more trust is possible; 
+	 * trusted keys from the unbound configuration setup. */
+	rrset_trust_ultimate
+};
+
+/**
+ * Security status from validation for data.
+ * The order is significant; more secure, more proven later.
+ */
+enum sec_status {
+	/** UNCHECKED means that object has yet to be validated. */
+	sec_status_unchecked = 0,
+	/** BOGUS means that the object (RRset or message) failed to validate
+	 *  (according to local policy), but should have validated. */
+	sec_status_bogus,
+	/** INDETERMINATE means that the object is insecure, but not 
+	 * authoritatively so. Generally this means that the RRset is not 
+	 * below a configured trust anchor. */
+	sec_status_indeterminate,
+	/** INSECURE means that the object is authoritatively known to be 
+	 * insecure. Generally this means that this RRset is below a trust 
+	 * anchor, but also below a verified, insecure delegation. */
+	sec_status_insecure,
+	/** SECURE means that the object (RRset or message) validated 
+	 * according to local policy. */
+	sec_status_secure
+};
+
+/**
+ * RRset data.
+ *
+ * The data is packed, stored contiguously in memory.
+ * memory layout:
+ *	o base struct
+ *	o rr_len size_t array
+ *	o rr_data uint8_t* array
+ *	o rr_ttl uint32_t array (after size_t and ptrs because those may be
+ *		64bit and this array before those would make them unaligned).
+ *		Since the stuff before is 32/64bit, rr_ttl is 32 bit aligned.
+ *	o rr_data rdata wireformats
+ *	o rrsig_data rdata wireformat(s)
+ *
+ * Rdata is stored in wireformat. The dname is stored in wireformat.
+ * TTLs are stored as absolute values (and could be expired).
+ *
+ * RRSIGs are stored in the arrays after the regular rrs.
+ *
+ * You need the packed_rrset_key to know dname, type, class of the
+ * resource records in this RRset. (if signed the rrsig gives the type too).
+ *
+ * On the wire an RR is:
+ *	name, type, class, ttl, rdlength, rdata.
+ * So we need to send the following per RR:
+ *	key.dname, ttl, rr_data[i].
+ *	since key.dname ends with type and class.
+ *	and rr_data starts with the rdlength.
+ *	the ttl value to send changes due to time.
+ */
+struct packed_rrset_data {
+	/** TTL (in seconds like time()) of the rrset.
+	 * Same for all RRs see rfc2181(5.2).  */
+	uint32_t ttl;
+	/** number of rrs. */
+	size_t count;
+	/** number of rrsigs, if 0 no rrsigs */
+	size_t rrsig_count;
+	/** the trustworthiness of the rrset data */
+	enum rrset_trust trust; 
+	/** security status of the rrset data */
+	enum sec_status security;
+	/** length of every rr's rdata, rr_len[i] is size of rr_data[i]. */
+	size_t* rr_len;
+	/** ttl of every rr. rr_ttl[i] ttl of rr i. */
+	uint32_t *rr_ttl;
+	/** 
+	 * Array of pointers to every rr's rdata. 
+	 * The rr_data[i] rdata is stored in uncompressed wireformat. 
+	 * The first uint16_t of rr_data[i] is network format rdlength.
+	 *
+	 * rr_data[count] to rr_data[count+rrsig_count] contain the rrsig data.
+	 */
+	uint8_t** rr_data;
+};
+
+/**
+ * An RRset can be represented using both key and data together.
+ * Split into key and data structures to simplify implementation of
+ * caching schemes.
+ */
+struct packed_rrset {
+	/** domain name, type and class */
+	struct packed_rrset_key* k;
+	/** ttl, count and rdatas (and rrsig) */
+	struct packed_rrset_data* d;
+};
+
+/**
+ * list of packed rrsets
+ */
+struct packed_rrset_list {
+	/** next in list */
+	struct packed_rrset_list* next;
+	/** rrset key and data */
+	struct packed_rrset rrset;
+};
+
+/**
+ * Delete packed rrset key and data, not entered in hashtables yet.
+ * Used during parsing.
+ * @param pkey: rrset key structure with locks, key and data pointers.
+ * @param alloc: where to return the unfree-able key structure.
+ */
+void ub_packed_rrset_parsedelete(struct ub_packed_rrset_key* pkey,
+	struct alloc_cache* alloc);
+
+/**
+ * Memory size of rrset data. RRset data must be filled in correctly.
+ * @param data: data to examine.
+ * @return size in bytes.
+ */
+size_t packed_rrset_sizeof(struct packed_rrset_data* data);
+
+/**
+ * Get TTL of rrset. RRset data must be filled in correctly.
+ * @param key: rrset key, with data to examine.
+ * @return ttl value.
+ */
+uint32_t ub_packed_rrset_ttl(struct ub_packed_rrset_key* key);
+
+/**
+ * Calculate memory size of rrset entry. For hash table usage.
+ * @param key: struct ub_packed_rrset_key*.
+ * @param data: struct packed_rrset_data*.
+ * @return size in bytes.
+ */
+size_t ub_rrset_sizefunc(void* key, void* data);
+
+/**
+ * compares two rrset keys.
+ * @param k1: struct ub_packed_rrset_key*.
+ * @param k2: struct ub_packed_rrset_key*.
+ * @return 0 if equal.
+ */
+int ub_rrset_compare(void* k1, void* k2);
+
+/**
+ * compare two rrset data structures.
+ * Compared rdata and rrsigdata, not the trust or ttl value.
+ * @param d1: data to compare.
+ * @param d2: data to compare.
+ * @return 1 if equal.
+ */
+int rrsetdata_equal(struct packed_rrset_data* d1, struct packed_rrset_data* d2);
+
+/**
+ * Old key to be deleted. RRset keys are recycled via alloc.
+ * The id is set to 0. So that other threads, after acquiring a lock always
+ * get the correct value, in this case the 0 deleted-special value.
+ * @param key: struct ub_packed_rrset_key*.
+ * @param userdata: alloc structure to use for recycling.
+ */
+void ub_rrset_key_delete(void* key, void* userdata);
+
+/**
+ * Old data to be deleted.
+ * @param data: what to delete.
+ * @param userdata: user data ptr.
+ */
+void rrset_data_delete(void* data, void* userdata);
+
+/**
+ * Calculate hash value for a packed rrset key.
+ * @param key: the rrset key with name, type, class, flags.
+ * @return hash value.
+ */
+hashvalue_t rrset_key_hash(struct packed_rrset_key* key);
+
+/**
+ * Fixup pointers in fixed data packed_rrset_data blob.
+ * After a memcpy of the data for example. Will set internal pointers right.
+ * @param data: rrset data structure. Otherwise correctly filled in.
+ */
+void packed_rrset_ptr_fixup(struct packed_rrset_data* data);
+
+/**
+ * Fixup TTLs in fixed data packed_rrset_data blob.
+ * @param data: rrset data structure. Otherwise correctly filled in.
+ * @param add: how many seconds to add, pass time(0) for example.
+ */
+void packed_rrset_ttl_add(struct packed_rrset_data* data, uint32_t add);
+
+/**
+ * Utility procedure to extract CNAME target name from its rdata.
+ * Failsafes; it will change passed dname to a valid dname or do nothing.
+ * @param rrset: the rrset structure. Must be a CNAME. 
+ *	Only first RR is used (multiple RRs are technically illegal anyway).
+ * 	Also works on type DNAME. Returns target name.
+ * @param dname: this pointer is updated to point into the cname rdata.
+ *	If a failsafe fails, nothing happens to the pointer (such as the
+ *	rdata was not a valid dname, not a CNAME, ...).
+ * @param dname_len: length of dname is returned.
+ */
+void get_cname_target(struct ub_packed_rrset_key* rrset, uint8_t** dname, 
+	size_t* dname_len);
+
+/**
+ * Get a printable string for a rrset trust value 
+ * @param s: rrset trust value
+ * @return printable string.
+ */
+const char* rrset_trust_to_string(enum rrset_trust s);
+
+/**
+ * Get a printable string for a security status value 
+ * @param s: security status
+ * @return printable string.
+ */
+const char* sec_status_to_string(enum sec_status s);
+
+/**
+ * Print string with neat domain name, type, class from rrset.
+ * @param v: at what verbosity level to print this.
+ * @param str: string of message.
+ * @param rrset: structure with name, type and class.
+ */
+void log_rrset_key(enum verbosity_value v, const char* str, 
+	struct ub_packed_rrset_key* rrset);
+
+/** 
+ * Allocate rrset in region - no more locks needed 
+ * @param key: a (just from rrset cache looked up) rrset key + valid,
+ * 	packed data record.
+ * @param region: where to alloc the copy
+ * @param now: adjust the TTLs to be relative (subtract from all TTLs).
+ * @return new region-alloced rrset key or NULL on alloc failure.
+ */
+struct ub_packed_rrset_key* packed_rrset_copy_region(
+	struct ub_packed_rrset_key* key, struct regional* region, 
+	uint32_t now);
+
+/** 
+ * Allocate rrset with malloc (from region or you are holding the lock).
+ * @param key: key with data entry.
+ * @param alloc: alloc_cache to create rrset_keys
+ * @param now: adjust the TTLs to be absolute (add to all TTLs).
+ * @return new region-alloced rrset key or NULL on alloc failure.
+ */
+struct ub_packed_rrset_key* packed_rrset_copy_alloc(
+	struct ub_packed_rrset_key* key, struct alloc_cache* alloc, 
+	uint32_t now);
+
+/**
+ * Create a ub_packed_rrset_key allocated on the heap.
+ * It therefore does not have the correct ID value, and cannot be used
+ * inside the cache.  It can be used in storage outside of the cache.
+ * Keys for the cache have to be obtained from alloc.h .
+ * @param rrset: the ldns rr set.
+ * @return key allocated or NULL on failure.
+ */
+struct ub_packed_rrset_key* ub_packed_rrset_heap_key(ldns_rr_list* rrset);
+
+/**
+ * Create packed_rrset data on the heap.
+ * @param rrset: the ldns rr set with the data to copy.
+ * @return data allocated or NULL on failure.
+ */
+struct packed_rrset_data* packed_rrset_heap_data(ldns_rr_list* rrset);
+
+/**
+ * Convert packed rrset to ldns rr list.
+ * @param rrset: packed rrset.
+ * @param buf: scratch buffer.
+ * @return rr list or NULL on failure.
+ */
+ldns_rr_list* packed_rrset_to_rr_list(struct ub_packed_rrset_key* rrset,
+	ldns_buffer* buf);
+
+#endif /* UTIL_DATA_PACKED_RRSET_H */
diff --git a/3rdParty/Unbound/src/src/util/fptr_wlist.c b/3rdParty/Unbound/src/src/util/fptr_wlist.c
new file mode 100644
index 0000000..cf3ebf6
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/fptr_wlist.c
@@ -0,0 +1,388 @@
+/*
+ * util/fptr_wlist.c - function pointer whitelists.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions that check function pointers.
+ * The functions contain a whitelist of known good callback values.
+ * Any other values lead to an error. 
+ *
+ * Due to the listing nature, this file violates all the modularization
+ * boundaries in the program.
+ */
+#include "config.h"
+#include "util/fptr_wlist.h"
+#include "util/mini_event.h"
+#include "daemon/worker.h"
+#include "daemon/remote.h"
+#include "services/outside_network.h"
+#include "services/mesh.h"
+#include "services/localzone.h"
+#include "services/cache/infra.h"
+#include "services/cache/rrset.h"
+#include "iterator/iterator.h"
+#include "iterator/iter_fwd.h"
+#include "validator/validator.h"
+#include "validator/val_anchor.h"
+#include "validator/val_nsec3.h"
+#include "validator/val_sigcrypt.h"
+#include "validator/val_kentry.h"
+#include "validator/val_neg.h"
+#include "validator/autotrust.h"
+#include "util/data/msgreply.h"
+#include "util/data/packed_rrset.h"
+#include "util/storage/slabhash.h"
+#include "util/storage/dnstree.h"
+#include "util/locks.h"
+#include "libunbound/libworker.h"
+#include "libunbound/context.h"
+#include "util/tube.h"
+#include "util/config_file.h"
+#ifdef UB_ON_WINDOWS
+#include "winrc/win_svc.h"
+#endif
+
+#ifdef WITH_PYTHONMODULE
+#include "pythonmod/pythonmod.h"
+#endif
+
+int 
+fptr_whitelist_comm_point(comm_point_callback_t *fptr)
+{
+	if(fptr == &worker_handle_request) return 1;
+	else if(fptr == &outnet_udp_cb) return 1;
+	else if(fptr == &outnet_tcp_cb) return 1;
+	else if(fptr == &tube_handle_listen) return 1;
+	return 0;
+}
+
+int 
+fptr_whitelist_comm_point_raw(comm_point_callback_t *fptr)
+{
+	if(fptr == &tube_handle_listen) return 1;
+	else if(fptr == &tube_handle_write) return 1;
+	else if(fptr == &remote_accept_callback) return 1;
+	else if(fptr == &remote_control_callback) return 1;
+	return 0;
+}
+
+int 
+fptr_whitelist_comm_timer(void (*fptr)(void*))
+{
+	if(fptr == &pending_udp_timer_cb) return 1;
+	else if(fptr == &outnet_tcptimer) return 1;
+	else if(fptr == &worker_stat_timer_cb) return 1;
+	else if(fptr == &worker_probe_timer_cb) return 1;
+#ifdef UB_ON_WINDOWS
+	else if(fptr == &wsvc_cron_cb) return 1;
+#endif
+	return 0;
+}
+
+int 
+fptr_whitelist_comm_signal(void (*fptr)(int, void*))
+{
+	if(fptr == &worker_sighandler) return 1;
+	return 0;
+}
+
+int 
+fptr_whitelist_event(void (*fptr)(int, short, void *))
+{
+	if(fptr == &comm_point_udp_callback) return 1;
+	else if(fptr == &comm_point_udp_ancil_callback) return 1;
+	else if(fptr == &comm_point_tcp_accept_callback) return 1;
+	else if(fptr == &comm_point_tcp_handle_callback) return 1;
+	else if(fptr == &comm_timer_callback) return 1;
+	else if(fptr == &comm_signal_callback) return 1;
+	else if(fptr == &comm_point_local_handle_callback) return 1;
+	else if(fptr == &comm_point_raw_handle_callback) return 1;
+	else if(fptr == &tube_handle_signal) return 1;
+#ifdef UB_ON_WINDOWS
+	else if(fptr == &worker_win_stop_cb) return 1;
+#endif
+	return 0;
+}
+
+int 
+fptr_whitelist_pending_udp(comm_point_callback_t *fptr)
+{
+	if(fptr == &serviced_udp_callback) return 1;
+	else if(fptr == &worker_handle_reply) return 1;
+	else if(fptr == &libworker_handle_reply) return 1;
+	return 0;
+}
+
+int 
+fptr_whitelist_pending_tcp(comm_point_callback_t *fptr)
+{
+	if(fptr == &serviced_tcp_callback) return 1;
+	else if(fptr == &worker_handle_reply) return 1;
+	else if(fptr == &libworker_handle_reply) return 1;
+	return 0;
+}
+
+int 
+fptr_whitelist_serviced_query(comm_point_callback_t *fptr)
+{
+	if(fptr == &worker_handle_service_reply) return 1;
+	else if(fptr == &libworker_handle_service_reply) return 1;
+	return 0;
+}
+
+int 
+fptr_whitelist_rbtree_cmp(int (*fptr) (const void *, const void *))
+{
+	if(fptr == &mesh_state_compare) return 1;
+	else if(fptr == &mesh_state_ref_compare) return 1;
+	else if(fptr == &addr_tree_compare) return 1;
+	else if(fptr == &local_zone_cmp) return 1;
+	else if(fptr == &local_data_cmp) return 1;
+	else if(fptr == &fwd_cmp) return 1;
+	else if(fptr == &pending_cmp) return 1;
+	else if(fptr == &serviced_cmp) return 1;
+	else if(fptr == &name_tree_compare) return 1;
+	else if(fptr == &order_lock_cmp) return 1;
+	else if(fptr == &codeline_cmp) return 1;
+	else if(fptr == &nsec3_hash_cmp) return 1;
+	else if(fptr == &mini_ev_cmp) return 1;
+	else if(fptr == &anchor_cmp) return 1;
+	else if(fptr == &canonical_tree_compare) return 1;
+	else if(fptr == &context_query_cmp) return 1;
+	else if(fptr == &val_neg_data_compare) return 1;
+	else if(fptr == &val_neg_zone_compare) return 1;
+	else if(fptr == &probetree_cmp) return 1;
+	else if(fptr == &replay_var_compare) return 1;
+	return 0;
+}
+
+int 
+fptr_whitelist_hash_sizefunc(lruhash_sizefunc_t fptr)
+{
+	if(fptr == &msgreply_sizefunc) return 1;
+	else if(fptr == &ub_rrset_sizefunc) return 1;
+	else if(fptr == &infra_sizefunc) return 1;
+	else if(fptr == &key_entry_sizefunc) return 1;
+	else if(fptr == &test_slabhash_sizefunc) return 1;
+	return 0;
+}
+
+int 
+fptr_whitelist_hash_compfunc(lruhash_compfunc_t fptr)
+{
+	if(fptr == &query_info_compare) return 1;
+	else if(fptr == &ub_rrset_compare) return 1;
+	else if(fptr == &infra_compfunc) return 1;
+	else if(fptr == &key_entry_compfunc) return 1;
+	else if(fptr == &test_slabhash_compfunc) return 1;
+	return 0;
+}
+
+int 
+fptr_whitelist_hash_delkeyfunc(lruhash_delkeyfunc_t fptr)
+{
+	if(fptr == &query_entry_delete) return 1;
+	else if(fptr == &ub_rrset_key_delete) return 1;
+	else if(fptr == &infra_delkeyfunc) return 1;
+	else if(fptr == &key_entry_delkeyfunc) return 1;
+	else if(fptr == &test_slabhash_delkey) return 1;
+	return 0;
+}
+
+int 
+fptr_whitelist_hash_deldatafunc(lruhash_deldatafunc_t fptr)
+{
+	if(fptr == &reply_info_delete) return 1;
+	else if(fptr == &rrset_data_delete) return 1;
+	else if(fptr == &infra_deldatafunc) return 1;
+	else if(fptr == &key_entry_deldatafunc) return 1;
+	else if(fptr == &test_slabhash_deldata) return 1;
+	return 0;
+}
+
+int 
+fptr_whitelist_hash_markdelfunc(lruhash_markdelfunc_t fptr)
+{
+	if(fptr == NULL) return 1;
+	else if(fptr == &rrset_markdel) return 1;
+	return 0;
+}
+
+/** whitelist env->send_query callbacks */
+int 
+fptr_whitelist_modenv_send_query(struct outbound_entry* (*fptr)(
+        uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
+        uint16_t flags, int dnssec, int want_dnssec, 
+	struct sockaddr_storage* addr, socklen_t addrlen, 
+	uint8_t* zone, size_t zonelen,
+	struct module_qstate* q))
+{
+	if(fptr == &worker_send_query) return 1;
+	else if(fptr == &libworker_send_query) return 1;
+	return 0;
+}
+
+int 
+fptr_whitelist_modenv_detach_subs(void (*fptr)(
+        struct module_qstate* qstate))
+{
+	if(fptr == &mesh_detach_subs) return 1;
+	return 0;
+}
+
+int 
+fptr_whitelist_modenv_attach_sub(int (*fptr)(
+        struct module_qstate* qstate, struct query_info* qinfo,
+        uint16_t qflags, int prime, struct module_qstate** newq))
+{
+	if(fptr == &mesh_attach_sub) return 1;
+	return 0;
+}
+
+int 
+fptr_whitelist_modenv_kill_sub(void (*fptr)(struct module_qstate* newq))
+{
+	if(fptr == &mesh_state_delete) return 1;
+	return 0;
+}
+
+int 
+fptr_whitelist_modenv_detect_cycle(int (*fptr)(        
+	struct module_qstate* qstate, struct query_info* qinfo,         
+	uint16_t flags, int prime))
+{
+	if(fptr == &mesh_detect_cycle) return 1;
+	return 0;
+}
+
+int 
+fptr_whitelist_mod_init(int (*fptr)(struct module_env* env, int id))
+{
+	if(fptr == &iter_init) return 1;
+	else if(fptr == &val_init) return 1;
+#ifdef WITH_PYTHONMODULE
+	else if(fptr == &pythonmod_init) return 1;
+#endif
+	return 0;
+}
+
+int 
+fptr_whitelist_mod_deinit(void (*fptr)(struct module_env* env, int id))
+{
+	if(fptr == &iter_deinit) return 1;
+	else if(fptr == &val_deinit) return 1;
+#ifdef WITH_PYTHONMODULE
+	else if(fptr == &pythonmod_deinit) return 1;
+#endif
+	return 0;
+}
+
+int 
+fptr_whitelist_mod_operate(void (*fptr)(struct module_qstate* qstate,
+        enum module_ev event, int id, struct outbound_entry* outbound))
+{
+	if(fptr == &iter_operate) return 1;
+	else if(fptr == &val_operate) return 1;
+#ifdef WITH_PYTHONMODULE
+	else if(fptr == &pythonmod_operate) return 1;
+#endif
+	return 0;
+}
+
+int 
+fptr_whitelist_mod_inform_super(void (*fptr)(
+        struct module_qstate* qstate, int id, struct module_qstate* super))
+{
+	if(fptr == &iter_inform_super) return 1;
+	else if(fptr == &val_inform_super) return 1;
+#ifdef WITH_PYTHONMODULE
+	else if(fptr == &pythonmod_inform_super) return 1;
+#endif
+	return 0;
+}
+
+int 
+fptr_whitelist_mod_clear(void (*fptr)(struct module_qstate* qstate,
+        int id))
+{
+	if(fptr == &iter_clear) return 1;
+	else if(fptr == &val_clear) return 1;
+#ifdef WITH_PYTHONMODULE
+	else if(fptr == &pythonmod_clear) return 1;
+#endif
+	return 0;
+}
+
+int 
+fptr_whitelist_mod_get_mem(size_t (*fptr)(struct module_env* env, int id))
+{
+	if(fptr == &iter_get_mem) return 1;
+	else if(fptr == &val_get_mem) return 1;
+#ifdef WITH_PYTHONMODULE
+	else if(fptr == &pythonmod_get_mem) return 1;
+#endif
+	return 0;
+}
+
+int 
+fptr_whitelist_alloc_cleanup(void (*fptr)(void*))
+{
+	if(fptr == &worker_alloc_cleanup) return 1;
+	return 0;
+}
+
+int fptr_whitelist_tube_listen(tube_callback_t* fptr)
+{
+	if(fptr == &worker_handle_control_cmd) return 1;
+	else if(fptr == &libworker_handle_control_cmd) return 1;
+	return 0;
+}
+
+int fptr_whitelist_mesh_cb(mesh_cb_func_t fptr)
+{
+	if(fptr == &libworker_fg_done_cb) return 1;
+	else if(fptr == &libworker_bg_done_cb) return 1;
+	else if(fptr == &probe_answer_cb) return 1;
+	return 0;
+}
+
+int fptr_whitelist_print_func(void (*fptr)(char*,void*))
+{
+	if(fptr == &config_print_func) return 1;
+	else if(fptr == &config_collate_func) return 1;
+	else if(fptr == &remote_get_opt_ssl) return 1;
+	return 0;
+}
diff --git a/3rdParty/Unbound/src/src/util/fptr_wlist.h b/3rdParty/Unbound/src/src/util/fptr_wlist.h
new file mode 100644
index 0000000..8ec823d
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/fptr_wlist.h
@@ -0,0 +1,343 @@
+/*
+ * util/fptr_wlist.h - function pointer whitelists.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions that check function pointers.
+ * The functions contain a whitelist of known good callback values.
+ * Any other values lead to an error. 
+ * 
+ * This prevent heap overflow based exploits, where the callback pointer
+ * is overwritten by a buffer overflow (apart from this defense, buffer 
+ * overflows should be fixed of course).
+ *
+ * Function pointers are used in
+ * 	o network code callbacks.
+ * 	o rbtree, lruhash, region data manipulation
+ *		in lruhash, the assertions are before the critical regions.
+ *		in other places, assertions are before the callback.
+ * 	o module operations.
+ */
+
+#ifndef UTIL_FPTR_WLIST_H
+#define UTIL_FPTR_WLIST_H
+#include "util/netevent.h"
+#include "util/storage/lruhash.h"
+#include "util/module.h"
+#include "util/tube.h"
+#include "services/mesh.h"
+
+/**
+ * Macro to perform an assertion check for fptr wlist checks.
+ * Does not get disabled in optimize mode. Check adds security by layers.
+ */
+#if defined(EXPORT_ALL_SYMBOLS)
+#define fptr_ok(x) /* nothing, dll-exe memory layout on win disables it */
+#else
+#define fptr_ok(x) \
+	do { if(!(x)) \
+		fatal_exit("%s:%d: %s: pointer whitelist %s failed", \
+		__FILE__, __LINE__, __func__, #x); \
+	} while(0);
+#endif
+
+/**
+ * Check function pointer whitelist for comm_point callback values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_comm_point(comm_point_callback_t *fptr);
+
+/**
+ * Check function pointer whitelist for raw comm_point callback values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_comm_point_raw(comm_point_callback_t *fptr);
+
+/**
+ * Check function pointer whitelist for comm_timer callback values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_comm_timer(void (*fptr)(void*));
+
+/**
+ * Check function pointer whitelist for comm_signal callback values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_comm_signal(void (*fptr)(int, void*));
+
+/**
+ * Check function pointer whitelist for event structure callback values.
+ * This is not called by libevent itself, but checked by netevent.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_event(void (*fptr)(int, short, void *));
+
+/**
+ * Check function pointer whitelist for pending udp callback values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_pending_udp(comm_point_callback_t *fptr);
+
+/**
+ * Check function pointer whitelist for pending tcp callback values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_pending_tcp(comm_point_callback_t *fptr);
+
+/**
+ * Check function pointer whitelist for serviced query callback values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_serviced_query(comm_point_callback_t *fptr);
+
+/**
+ * Check function pointer whitelist for rbtree cmp callback values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_rbtree_cmp(int (*fptr) (const void *, const void *));
+
+/**
+ * Check function pointer whitelist for lruhash sizefunc callback values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_hash_sizefunc(lruhash_sizefunc_t fptr);
+
+/**
+ * Check function pointer whitelist for lruhash compfunc callback values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_hash_compfunc(lruhash_compfunc_t fptr);
+
+/**
+ * Check function pointer whitelist for lruhash delkeyfunc callback values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_hash_delkeyfunc(lruhash_delkeyfunc_t fptr);
+
+/**
+ * Check function pointer whitelist for lruhash deldata callback values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_hash_deldatafunc(lruhash_deldatafunc_t fptr);
+
+/**
+ * Check function pointer whitelist for lruhash markdel callback values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_hash_markdelfunc(lruhash_markdelfunc_t fptr);
+
+/**
+ * Check function pointer whitelist for module_env send_query callback values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_modenv_send_query(struct outbound_entry* (*fptr)(
+	uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, 
+	uint16_t flags, int dnssec, int want_dnssec,
+	struct sockaddr_storage* addr, socklen_t addrlen, 
+	uint8_t* zone, size_t zonelen,
+	struct module_qstate* q));
+
+/**
+ * Check function pointer whitelist for module_env detach_subs callback values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_modenv_detach_subs(void (*fptr)(
+	struct module_qstate* qstate));
+
+/**
+ * Check function pointer whitelist for module_env attach_sub callback values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_modenv_attach_sub(int (*fptr)(
+	struct module_qstate* qstate, struct query_info* qinfo, 
+	uint16_t qflags, int prime, struct module_qstate** newq));
+
+/**
+ * Check function pointer whitelist for module_env kill_sub callback values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_modenv_kill_sub(void (*fptr)(struct module_qstate* newq));
+
+/**
+ * Check function pointer whitelist for module_env detect_cycle callback values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_modenv_detect_cycle(int (*fptr)(
+	struct module_qstate* qstate, struct query_info* qinfo, 
+	uint16_t flags, int prime));
+
+/**
+ * Check function pointer whitelist for module init call values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_mod_init(int (*fptr)(struct module_env* env, int id));
+
+/**
+ * Check function pointer whitelist for module deinit call values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_mod_deinit(void (*fptr)(struct module_env* env, int id));
+
+/**
+ * Check function pointer whitelist for module operate call values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_mod_operate(void (*fptr)(struct module_qstate* qstate, 
+	enum module_ev event, int id, struct outbound_entry* outbound));
+
+/**
+ * Check function pointer whitelist for module inform_super call values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_mod_inform_super(void (*fptr)(
+	struct module_qstate* qstate, int id, struct module_qstate* super));
+
+/**
+ * Check function pointer whitelist for module clear call values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_mod_clear(void (*fptr)(struct module_qstate* qstate, 
+	int id));
+
+/**
+ * Check function pointer whitelist for module get_mem call values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_mod_get_mem(size_t (*fptr)(struct module_env* env, int id));
+
+/**
+ * Check function pointer whitelist for alloc clear on id overflow call values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_alloc_cleanup(void (*fptr)(void*));
+
+/**
+ * Check function pointer whitelist for tube listen handler values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_tube_listen(tube_callback_t* fptr);
+
+/**
+ * Check function pointer whitelist for mesh state callback values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_mesh_cb(mesh_cb_func_t fptr);
+
+/**
+ * Check function pointer whitelist for config_get_option func values.
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_print_func(void (*fptr)(char*,void*));
+
+/** Due to module breakage by fptr wlist, these test app declarations
+ * are presented here */
+/** 
+ * compare two order_ids from lock-verify test app 
+ * @param e1: first order_id
+ * @param e2: second order_id
+ * @return compare code -1, 0, +1 (like memcmp).
+ */
+int order_lock_cmp(const void* e1, const void* e2);
+
+/** 
+ * compare two codeline structs for rbtree from memstats test app 
+ * @param a: codeline
+ * @param b: codeline
+ * @return compare code -1, 0, +1 (like memcmp).
+ */
+int codeline_cmp(const void* a, const void* b);
+
+/** compare two replay_vars */
+int replay_var_compare(const void* a, const void* b);
+
+#endif /* UTIL_FPTR_WLIST_H */
diff --git a/3rdParty/Unbound/src/src/util/iana_ports.inc b/3rdParty/Unbound/src/src/util/iana_ports.inc
new file mode 100644
index 0000000..86264e1
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/iana_ports.inc
@@ -0,0 +1,5334 @@
+1,
+2,
+3,
+5,
+7,
+9,
+11,
+13,
+17,
+18,
+19,
+20,
+21,
+22,
+23,
+24,
+25,
+27,
+29,
+31,
+33,
+35,
+37,
+38,
+39,
+41,
+42,
+43,
+44,
+45,
+46,
+47,
+48,
+49,
+50,
+51,
+52,
+53,
+54,
+55,
+56,
+57,
+58,
+59,
+61,
+62,
+63,
+64,
+65,
+66,
+67,
+68,
+69,
+70,
+71,
+72,
+73,
+74,
+75,
+76,
+77,
+78,
+79,
+80,
+82,
+83,
+84,
+85,
+86,
+87,
+88,
+89,
+90,
+91,
+92,
+93,
+94,
+95,
+96,
+97,
+98,
+99,
+101,
+102,
+103,
+104,
+105,
+106,
+107,
+108,
+109,
+110,
+111,
+112,
+113,
+115,
+116,
+117,
+118,
+119,
+120,
+121,
+122,
+123,
+124,
+125,
+126,
+127,
+128,
+129,
+130,
+131,
+132,
+133,
+134,
+135,
+136,
+137,
+138,
+139,
+140,
+141,
+142,
+143,
+144,
+145,
+146,
+147,
+148,
+149,
+150,
+151,
+152,
+153,
+154,
+155,
+156,
+157,
+158,
+159,
+160,
+161,
+162,
+163,
+164,
+165,
+166,
+167,
+168,
+169,
+170,
+171,
+172,
+173,
+174,
+175,
+176,
+177,
+178,
+179,
+180,
+181,
+182,
+183,
+184,
+185,
+186,
+187,
+188,
+189,
+190,
+191,
+192,
+193,
+194,
+195,
+196,
+197,
+198,
+199,
+200,
+201,
+202,
+203,
+204,
+205,
+206,
+207,
+208,
+209,
+210,
+211,
+212,
+213,
+214,
+215,
+216,
+217,
+218,
+219,
+220,
+221,
+222,
+223,
+224,
+242,
+243,
+244,
+245,
+246,
+247,
+248,
+256,
+257,
+259,
+260,
+261,
+262,
+263,
+264,
+265,
+266,
+267,
+268,
+269,
+270,
+280,
+281,
+282,
+283,
+284,
+286,
+287,
+308,
+309,
+310,
+311,
+312,
+313,
+314,
+315,
+316,
+317,
+318,
+319,
+320,
+321,
+322,
+333,
+344,
+345,
+346,
+347,
+348,
+349,
+350,
+351,
+352,
+353,
+354,
+355,
+356,
+357,
+358,
+359,
+360,
+361,
+362,
+363,
+364,
+365,
+366,
+367,
+368,
+369,
+370,
+371,
+372,
+373,
+374,
+375,
+376,
+377,
+378,
+379,
+380,
+381,
+382,
+383,
+384,
+385,
+386,
+387,
+388,
+389,
+390,
+391,
+392,
+393,
+394,
+395,
+396,
+397,
+398,
+399,
+400,
+401,
+402,
+403,
+404,
+405,
+406,
+407,
+408,
+409,
+410,
+411,
+412,
+413,
+414,
+415,
+416,
+417,
+418,
+419,
+420,
+421,
+422,
+423,
+424,
+425,
+426,
+427,
+428,
+429,
+430,
+431,
+432,
+433,
+434,
+435,
+436,
+437,
+438,
+439,
+440,
+441,
+442,
+443,
+444,
+445,
+446,
+447,
+448,
+449,
+450,
+451,
+452,
+453,
+454,
+455,
+456,
+457,
+458,
+459,
+460,
+461,
+462,
+463,
+464,
+465,
+466,
+467,
+468,
+469,
+470,
+471,
+472,
+473,
+474,
+475,
+476,
+477,
+478,
+479,
+480,
+481,
+482,
+483,
+484,
+485,
+486,
+487,
+488,
+489,
+490,
+491,
+492,
+493,
+494,
+495,
+496,
+497,
+498,
+499,
+500,
+501,
+502,
+503,
+504,
+505,
+506,
+507,
+508,
+509,
+510,
+511,
+512,
+513,
+514,
+515,
+516,
+517,
+518,
+519,
+520,
+521,
+522,
+523,
+524,
+525,
+526,
+527,
+528,
+529,
+530,
+531,
+532,
+533,
+534,
+535,
+536,
+537,
+538,
+539,
+540,
+541,
+542,
+543,
+544,
+545,
+546,
+547,
+548,
+549,
+550,
+551,
+552,
+553,
+554,
+555,
+556,
+557,
+558,
+559,
+560,
+561,
+562,
+563,
+564,
+565,
+566,
+567,
+568,
+569,
+570,
+571,
+572,
+573,
+574,
+575,
+576,
+577,
+578,
+579,
+580,
+581,
+582,
+583,
+584,
+586,
+587,
+588,
+589,
+590,
+591,
+592,
+593,
+594,
+595,
+596,
+597,
+598,
+599,
+600,
+601,
+602,
+603,
+604,
+605,
+606,
+607,
+608,
+609,
+610,
+611,
+612,
+613,
+614,
+615,
+616,
+617,
+618,
+619,
+620,
+621,
+622,
+623,
+624,
+625,
+626,
+627,
+628,
+629,
+630,
+631,
+632,
+633,
+634,
+635,
+636,
+637,
+638,
+639,
+640,
+641,
+642,
+643,
+644,
+645,
+646,
+647,
+648,
+649,
+650,
+651,
+652,
+653,
+654,
+655,
+656,
+657,
+658,
+660,
+661,
+662,
+663,
+664,
+665,
+666,
+667,
+668,
+669,
+670,
+671,
+672,
+673,
+674,
+675,
+676,
+677,
+678,
+679,
+680,
+681,
+682,
+683,
+684,
+685,
+686,
+687,
+688,
+689,
+690,
+691,
+692,
+693,
+694,
+695,
+696,
+697,
+698,
+699,
+700,
+701,
+702,
+704,
+705,
+706,
+707,
+709,
+710,
+711,
+712,
+713,
+714,
+715,
+716,
+729,
+730,
+731,
+741,
+742,
+744,
+747,
+748,
+749,
+750,
+751,
+752,
+753,
+754,
+758,
+759,
+760,
+761,
+762,
+763,
+764,
+765,
+767,
+769,
+770,
+771,
+772,
+773,
+774,
+775,
+776,
+777,
+780,
+800,
+801,
+810,
+828,
+829,
+830,
+831,
+832,
+833,
+847,
+848,
+860,
+861,
+862,
+873,
+886,
+887,
+888,
+900,
+901,
+902,
+903,
+910,
+911,
+912,
+913,
+989,
+990,
+991,
+992,
+993,
+995,
+996,
+997,
+998,
+999,
+1000,
+1008,
+1010,
+1021,
+1022,
+1025,
+1026,
+1029,
+1030,
+1031,
+1032,
+1033,
+1034,
+1035,
+1036,
+1037,
+1038,
+1039,
+1040,
+1041,
+1042,
+1043,
+1044,
+1045,
+1046,
+1047,
+1048,
+1049,
+1050,
+1051,
+1052,
+1053,
+1054,
+1055,
+1056,
+1057,
+1058,
+1059,
+1060,
+1061,
+1062,
+1063,
+1064,
+1065,
+1066,
+1067,
+1068,
+1069,
+1070,
+1071,
+1072,
+1073,
+1074,
+1075,
+1076,
+1077,
+1078,
+1079,
+1080,
+1081,
+1082,
+1083,
+1084,
+1085,
+1086,
+1087,
+1088,
+1089,
+1090,
+1091,
+1092,
+1093,
+1094,
+1095,
+1096,
+1097,
+1098,
+1099,
+1100,
+1101,
+1102,
+1103,
+1104,
+1105,
+1106,
+1107,
+1108,
+1110,
+1111,
+1112,
+1113,
+1114,
+1115,
+1116,
+1117,
+1118,
+1119,
+1120,
+1121,
+1122,
+1123,
+1124,
+1125,
+1126,
+1127,
+1128,
+1129,
+1130,
+1131,
+1132,
+1133,
+1134,
+1135,
+1136,
+1137,
+1138,
+1139,
+1140,
+1141,
+1142,
+1143,
+1144,
+1145,
+1146,
+1147,
+1148,
+1149,
+1150,
+1151,
+1152,
+1153,
+1154,
+1155,
+1156,
+1157,
+1158,
+1159,
+1160,
+1161,
+1162,
+1163,
+1164,
+1165,
+1166,
+1167,
+1168,
+1169,
+1170,
+1171,
+1172,
+1173,
+1174,
+1175,
+1176,
+1177,
+1178,
+1179,
+1180,
+1181,
+1182,
+1183,
+1184,
+1185,
+1186,
+1187,
+1188,
+1189,
+1190,
+1191,
+1192,
+1193,
+1194,
+1195,
+1196,
+1197,
+1198,
+1199,
+1200,
+1201,
+1202,
+1203,
+1204,
+1205,
+1206,
+1207,
+1208,
+1209,
+1210,
+1211,
+1212,
+1213,
+1214,
+1215,
+1216,
+1217,
+1218,
+1219,
+1220,
+1221,
+1222,
+1223,
+1224,
+1225,
+1226,
+1227,
+1228,
+1229,
+1230,
+1231,
+1233,
+1234,
+1235,
+1236,
+1237,
+1238,
+1239,
+1240,
+1241,
+1242,
+1243,
+1244,
+1245,
+1246,
+1247,
+1248,
+1249,
+1250,
+1251,
+1252,
+1253,
+1254,
+1255,
+1256,
+1257,
+1258,
+1259,
+1260,
+1261,
+1262,
+1263,
+1264,
+1265,
+1266,
+1267,
+1268,
+1269,
+1270,
+1271,
+1272,
+1273,
+1274,
+1275,
+1276,
+1277,
+1278,
+1279,
+1280,
+1281,
+1282,
+1283,
+1284,
+1285,
+1286,
+1287,
+1288,
+1289,
+1290,
+1291,
+1292,
+1293,
+1294,
+1295,
+1296,
+1297,
+1298,
+1299,
+1300,
+1301,
+1302,
+1303,
+1304,
+1305,
+1306,
+1307,
+1308,
+1309,
+1310,
+1311,
+1312,
+1313,
+1314,
+1315,
+1316,
+1317,
+1318,
+1319,
+1320,
+1321,
+1322,
+1323,
+1324,
+1325,
+1326,
+1327,
+1328,
+1329,
+1330,
+1331,
+1332,
+1333,
+1334,
+1335,
+1336,
+1337,
+1338,
+1339,
+1340,
+1341,
+1342,
+1343,
+1344,
+1345,
+1346,
+1347,
+1348,
+1349,
+1350,
+1351,
+1352,
+1353,
+1354,
+1355,
+1356,
+1357,
+1358,
+1359,
+1360,
+1361,
+1362,
+1363,
+1364,
+1365,
+1366,
+1367,
+1368,
+1369,
+1370,
+1371,
+1372,
+1373,
+1374,
+1375,
+1376,
+1377,
+1378,
+1379,
+1380,
+1381,
+1382,
+1383,
+1384,
+1385,
+1386,
+1387,
+1388,
+1389,
+1390,
+1391,
+1392,
+1393,
+1394,
+1395,
+1396,
+1397,
+1398,
+1399,
+1400,
+1401,
+1402,
+1403,
+1404,
+1405,
+1406,
+1407,
+1408,
+1409,
+1410,
+1411,
+1412,
+1413,
+1414,
+1415,
+1416,
+1417,
+1418,
+1419,
+1420,
+1421,
+1422,
+1423,
+1424,
+1425,
+1426,
+1427,
+1428,
+1429,
+1430,
+1431,
+1432,
+1433,
+1434,
+1435,
+1436,
+1437,
+1438,
+1439,
+1440,
+1441,
+1442,
+1443,
+1444,
+1445,
+1446,
+1447,
+1448,
+1449,
+1450,
+1451,
+1452,
+1453,
+1454,
+1455,
+1456,
+1457,
+1458,
+1459,
+1460,
+1461,
+1462,
+1463,
+1464,
+1465,
+1466,
+1467,
+1468,
+1469,
+1470,
+1471,
+1472,
+1473,
+1474,
+1475,
+1476,
+1477,
+1478,
+1479,
+1480,
+1481,
+1482,
+1483,
+1484,
+1485,
+1486,
+1487,
+1488,
+1489,
+1490,
+1492,
+1493,
+1494,
+1495,
+1496,
+1497,
+1498,
+1499,
+1500,
+1501,
+1502,
+1503,
+1504,
+1505,
+1506,
+1507,
+1508,
+1509,
+1510,
+1511,
+1512,
+1513,
+1514,
+1515,
+1516,
+1517,
+1518,
+1519,
+1520,
+1521,
+1522,
+1523,
+1524,
+1525,
+1526,
+1527,
+1529,
+1530,
+1531,
+1532,
+1533,
+1534,
+1535,
+1536,
+1537,
+1538,
+1539,
+1540,
+1541,
+1542,
+1543,
+1544,
+1545,
+1546,
+1547,
+1548,
+1549,
+1550,
+1551,
+1552,
+1553,
+1554,
+1555,
+1556,
+1557,
+1558,
+1559,
+1560,
+1561,
+1562,
+1563,
+1564,
+1565,
+1566,
+1567,
+1568,
+1569,
+1570,
+1571,
+1572,
+1573,
+1574,
+1575,
+1576,
+1577,
+1578,
+1579,
+1580,
+1581,
+1582,
+1583,
+1584,
+1585,
+1586,
+1587,
+1588,
+1589,
+1590,
+1591,
+1592,
+1593,
+1594,
+1595,
+1596,
+1597,
+1598,
+1599,
+1600,
+1601,
+1602,
+1603,
+1604,
+1605,
+1606,
+1607,
+1608,
+1609,
+1610,
+1611,
+1612,
+1613,
+1614,
+1615,
+1616,
+1617,
+1618,
+1619,
+1620,
+1621,
+1622,
+1623,
+1624,
+1625,
+1626,
+1627,
+1628,
+1629,
+1630,
+1631,
+1632,
+1633,
+1634,
+1635,
+1636,
+1637,
+1638,
+1639,
+1640,
+1641,
+1642,
+1643,
+1645,
+1646,
+1647,
+1648,
+1649,
+1650,
+1651,
+1652,
+1653,
+1654,
+1655,
+1656,
+1657,
+1658,
+1659,
+1660,
+1661,
+1662,
+1663,
+1664,
+1665,
+1666,
+1667,
+1668,
+1669,
+1670,
+1671,
+1672,
+1673,
+1674,
+1675,
+1676,
+1677,
+1678,
+1679,
+1680,
+1681,
+1682,
+1683,
+1684,
+1685,
+1686,
+1687,
+1688,
+1689,
+1690,
+1691,
+1692,
+1693,
+1694,
+1695,
+1696,
+1697,
+1698,
+1699,
+1700,
+1701,
+1702,
+1703,
+1704,
+1705,
+1706,
+1707,
+1708,
+1709,
+1710,
+1711,
+1712,
+1713,
+1714,
+1715,
+1716,
+1717,
+1718,
+1719,
+1720,
+1721,
+1722,
+1723,
+1724,
+1725,
+1726,
+1727,
+1728,
+1729,
+1730,
+1731,
+1732,
+1733,
+1734,
+1735,
+1736,
+1737,
+1738,
+1739,
+1740,
+1741,
+1742,
+1743,
+1744,
+1745,
+1746,
+1747,
+1748,
+1749,
+1750,
+1751,
+1752,
+1754,
+1755,
+1756,
+1757,
+1758,
+1759,
+1760,
+1761,
+1762,
+1763,
+1764,
+1765,
+1766,
+1767,
+1768,
+1769,
+1770,
+1771,
+1772,
+1773,
+1774,
+1776,
+1777,
+1778,
+1779,
+1780,
+1781,
+1782,
+1784,
+1785,
+1786,
+1787,
+1788,
+1789,
+1790,
+1791,
+1792,
+1793,
+1794,
+1795,
+1796,
+1797,
+1798,
+1799,
+1800,
+1801,
+1802,
+1803,
+1804,
+1805,
+1806,
+1807,
+1808,
+1809,
+1810,
+1811,
+1812,
+1813,
+1814,
+1815,
+1816,
+1817,
+1818,
+1819,
+1820,
+1821,
+1822,
+1823,
+1824,
+1825,
+1826,
+1827,
+1828,
+1829,
+1830,
+1831,
+1832,
+1833,
+1834,
+1835,
+1836,
+1837,
+1838,
+1839,
+1840,
+1841,
+1842,
+1843,
+1844,
+1845,
+1846,
+1847,
+1848,
+1849,
+1850,
+1851,
+1852,
+1853,
+1854,
+1855,
+1856,
+1857,
+1858,
+1859,
+1860,
+1861,
+1862,
+1863,
+1864,
+1865,
+1866,
+1867,
+1868,
+1869,
+1870,
+1871,
+1872,
+1873,
+1874,
+1875,
+1876,
+1877,
+1878,
+1879,
+1880,
+1881,
+1882,
+1883,
+1884,
+1885,
+1886,
+1887,
+1888,
+1889,
+1890,
+1891,
+1892,
+1893,
+1894,
+1896,
+1897,
+1898,
+1899,
+1900,
+1901,
+1902,
+1903,
+1904,
+1905,
+1906,
+1907,
+1908,
+1909,
+1910,
+1911,
+1912,
+1913,
+1914,
+1915,
+1916,
+1917,
+1918,
+1919,
+1920,
+1921,
+1922,
+1923,
+1924,
+1925,
+1926,
+1927,
+1928,
+1929,
+1930,
+1931,
+1932,
+1933,
+1934,
+1935,
+1936,
+1937,
+1938,
+1939,
+1940,
+1941,
+1942,
+1943,
+1944,
+1945,
+1946,
+1947,
+1948,
+1949,
+1950,
+1951,
+1952,
+1953,
+1954,
+1955,
+1956,
+1957,
+1958,
+1959,
+1960,
+1961,
+1962,
+1963,
+1964,
+1965,
+1966,
+1967,
+1968,
+1969,
+1970,
+1971,
+1972,
+1973,
+1974,
+1975,
+1976,
+1977,
+1978,
+1979,
+1980,
+1981,
+1982,
+1983,
+1984,
+1985,
+1986,
+1987,
+1988,
+1989,
+1990,
+1991,
+1992,
+1993,
+1994,
+1995,
+1996,
+1997,
+1998,
+1999,
+2000,
+2001,
+2002,
+2003,
+2004,
+2005,
+2006,
+2007,
+2008,
+2009,
+2010,
+2011,
+2012,
+2013,
+2014,
+2015,
+2016,
+2017,
+2018,
+2019,
+2020,
+2021,
+2022,
+2023,
+2024,
+2025,
+2026,
+2027,
+2028,
+2029,
+2030,
+2031,
+2032,
+2033,
+2034,
+2035,
+2036,
+2037,
+2038,
+2039,
+2040,
+2041,
+2042,
+2043,
+2044,
+2045,
+2046,
+2047,
+2048,
+2049,
+2050,
+2051,
+2052,
+2053,
+2054,
+2055,
+2056,
+2057,
+2058,
+2059,
+2060,
+2061,
+2062,
+2063,
+2064,
+2065,
+2066,
+2067,
+2068,
+2069,
+2070,
+2071,
+2072,
+2073,
+2074,
+2075,
+2076,
+2077,
+2078,
+2079,
+2080,
+2081,
+2082,
+2083,
+2084,
+2085,
+2086,
+2087,
+2088,
+2089,
+2090,
+2091,
+2092,
+2093,
+2094,
+2095,
+2096,
+2097,
+2098,
+2099,
+2100,
+2101,
+2102,
+2103,
+2104,
+2105,
+2106,
+2107,
+2108,
+2109,
+2110,
+2111,
+2112,
+2113,
+2114,
+2115,
+2116,
+2117,
+2118,
+2119,
+2120,
+2121,
+2122,
+2123,
+2124,
+2125,
+2126,
+2127,
+2128,
+2129,
+2130,
+2131,
+2132,
+2133,
+2134,
+2135,
+2136,
+2137,
+2138,
+2139,
+2140,
+2141,
+2142,
+2143,
+2144,
+2145,
+2146,
+2147,
+2148,
+2149,
+2150,
+2151,
+2152,
+2153,
+2154,
+2155,
+2156,
+2157,
+2158,
+2159,
+2160,
+2161,
+2162,
+2163,
+2164,
+2165,
+2166,
+2167,
+2168,
+2169,
+2170,
+2171,
+2172,
+2173,
+2174,
+2175,
+2176,
+2177,
+2178,
+2179,
+2180,
+2181,
+2182,
+2183,
+2184,
+2185,
+2186,
+2187,
+2190,
+2191,
+2192,
+2193,
+2197,
+2198,
+2199,
+2200,
+2201,
+2202,
+2203,
+2204,
+2205,
+2206,
+2207,
+2208,
+2209,
+2210,
+2211,
+2212,
+2213,
+2214,
+2215,
+2216,
+2217,
+2218,
+2219,
+2220,
+2221,
+2222,
+2223,
+2224,
+2226,
+2227,
+2228,
+2229,
+2230,
+2231,
+2232,
+2233,
+2234,
+2235,
+2236,
+2237,
+2238,
+2239,
+2240,
+2241,
+2242,
+2243,
+2244,
+2245,
+2246,
+2247,
+2248,
+2249,
+2250,
+2251,
+2252,
+2253,
+2254,
+2255,
+2256,
+2257,
+2258,
+2260,
+2261,
+2262,
+2263,
+2264,
+2265,
+2266,
+2267,
+2268,
+2269,
+2270,
+2271,
+2272,
+2273,
+2274,
+2275,
+2276,
+2277,
+2278,
+2279,
+2280,
+2281,
+2282,
+2283,
+2284,
+2285,
+2286,
+2287,
+2288,
+2289,
+2290,
+2291,
+2292,
+2293,
+2294,
+2295,
+2296,
+2297,
+2298,
+2299,
+2300,
+2301,
+2302,
+2303,
+2304,
+2305,
+2306,
+2307,
+2308,
+2309,
+2310,
+2311,
+2312,
+2313,
+2314,
+2315,
+2316,
+2317,
+2318,
+2319,
+2320,
+2321,
+2322,
+2323,
+2324,
+2325,
+2326,
+2327,
+2328,
+2329,
+2330,
+2331,
+2332,
+2333,
+2334,
+2335,
+2336,
+2337,
+2338,
+2339,
+2340,
+2341,
+2342,
+2343,
+2344,
+2345,
+2346,
+2347,
+2348,
+2349,
+2350,
+2351,
+2352,
+2353,
+2354,
+2355,
+2356,
+2357,
+2358,
+2359,
+2360,
+2361,
+2362,
+2363,
+2364,
+2365,
+2366,
+2367,
+2368,
+2370,
+2371,
+2372,
+2381,
+2382,
+2383,
+2384,
+2385,
+2386,
+2387,
+2388,
+2389,
+2390,
+2391,
+2392,
+2393,
+2394,
+2395,
+2396,
+2397,
+2398,
+2399,
+2400,
+2401,
+2402,
+2403,
+2404,
+2405,
+2406,
+2407,
+2408,
+2409,
+2410,
+2411,
+2412,
+2413,
+2414,
+2415,
+2416,
+2417,
+2418,
+2419,
+2420,
+2421,
+2422,
+2423,
+2424,
+2425,
+2427,
+2428,
+2429,
+2430,
+2431,
+2432,
+2433,
+2434,
+2435,
+2436,
+2437,
+2438,
+2439,
+2440,
+2441,
+2442,
+2443,
+2444,
+2445,
+2446,
+2447,
+2448,
+2449,
+2450,
+2451,
+2452,
+2453,
+2454,
+2455,
+2456,
+2457,
+2458,
+2459,
+2460,
+2461,
+2462,
+2463,
+2464,
+2465,
+2466,
+2467,
+2468,
+2469,
+2470,
+2471,
+2472,
+2473,
+2474,
+2475,
+2476,
+2477,
+2478,
+2479,
+2480,
+2481,
+2482,
+2483,
+2484,
+2485,
+2486,
+2487,
+2488,
+2489,
+2490,
+2491,
+2492,
+2493,
+2494,
+2495,
+2496,
+2497,
+2498,
+2499,
+2500,
+2501,
+2502,
+2503,
+2504,
+2505,
+2506,
+2507,
+2508,
+2509,
+2510,
+2511,
+2512,
+2513,
+2514,
+2515,
+2516,
+2517,
+2518,
+2519,
+2520,
+2521,
+2522,
+2523,
+2524,
+2525,
+2526,
+2527,
+2528,
+2529,
+2530,
+2531,
+2532,
+2533,
+2534,
+2535,
+2536,
+2537,
+2538,
+2539,
+2540,
+2541,
+2542,
+2543,
+2544,
+2545,
+2546,
+2547,
+2548,
+2549,
+2550,
+2551,
+2552,
+2553,
+2554,
+2555,
+2556,
+2557,
+2558,
+2559,
+2560,
+2561,
+2562,
+2563,
+2565,
+2566,
+2567,
+2568,
+2569,
+2570,
+2571,
+2572,
+2573,
+2574,
+2575,
+2576,
+2577,
+2578,
+2579,
+2580,
+2581,
+2582,
+2583,
+2584,
+2585,
+2586,
+2587,
+2588,
+2589,
+2590,
+2591,
+2592,
+2593,
+2594,
+2595,
+2596,
+2597,
+2598,
+2599,
+2600,
+2601,
+2602,
+2603,
+2604,
+2605,
+2606,
+2607,
+2608,
+2609,
+2610,
+2611,
+2612,
+2613,
+2614,
+2615,
+2616,
+2617,
+2618,
+2619,
+2620,
+2621,
+2622,
+2623,
+2624,
+2625,
+2626,
+2627,
+2628,
+2629,
+2630,
+2631,
+2632,
+2633,
+2634,
+2635,
+2636,
+2637,
+2638,
+2639,
+2640,
+2641,
+2642,
+2643,
+2644,
+2645,
+2646,
+2647,
+2648,
+2649,
+2650,
+2651,
+2652,
+2653,
+2654,
+2655,
+2656,
+2657,
+2658,
+2659,
+2660,
+2661,
+2662,
+2663,
+2664,
+2665,
+2666,
+2667,
+2668,
+2669,
+2670,
+2671,
+2672,
+2673,
+2674,
+2675,
+2676,
+2677,
+2678,
+2679,
+2680,
+2681,
+2683,
+2684,
+2685,
+2686,
+2687,
+2688,
+2689,
+2690,
+2691,
+2692,
+2694,
+2695,
+2696,
+2697,
+2698,
+2699,
+2700,
+2701,
+2702,
+2703,
+2704,
+2705,
+2706,
+2707,
+2708,
+2709,
+2710,
+2711,
+2712,
+2713,
+2714,
+2715,
+2716,
+2717,
+2718,
+2719,
+2720,
+2721,
+2722,
+2723,
+2724,
+2725,
+2726,
+2727,
+2728,
+2729,
+2730,
+2731,
+2732,
+2733,
+2734,
+2735,
+2736,
+2737,
+2738,
+2739,
+2740,
+2741,
+2742,
+2743,
+2744,
+2745,
+2746,
+2747,
+2748,
+2749,
+2750,
+2751,
+2752,
+2753,
+2754,
+2755,
+2756,
+2757,
+2758,
+2759,
+2760,
+2761,
+2762,
+2763,
+2764,
+2765,
+2766,
+2767,
+2768,
+2769,
+2770,
+2771,
+2772,
+2773,
+2774,
+2775,
+2776,
+2777,
+2778,
+2779,
+2780,
+2781,
+2782,
+2783,
+2784,
+2785,
+2786,
+2787,
+2788,
+2789,
+2790,
+2791,
+2792,
+2793,
+2795,
+2796,
+2797,
+2798,
+2799,
+2800,
+2801,
+2802,
+2803,
+2804,
+2805,
+2806,
+2807,
+2808,
+2809,
+2810,
+2811,
+2812,
+2813,
+2814,
+2815,
+2816,
+2817,
+2818,
+2819,
+2820,
+2821,
+2822,
+2823,
+2824,
+2826,
+2827,
+2828,
+2829,
+2830,
+2831,
+2832,
+2833,
+2834,
+2835,
+2836,
+2837,
+2838,
+2839,
+2840,
+2841,
+2842,
+2843,
+2844,
+2845,
+2846,
+2847,
+2848,
+2849,
+2850,
+2851,
+2852,
+2853,
+2854,
+2855,
+2856,
+2857,
+2858,
+2859,
+2860,
+2861,
+2862,
+2863,
+2864,
+2865,
+2866,
+2867,
+2868,
+2869,
+2870,
+2871,
+2872,
+2874,
+2875,
+2876,
+2877,
+2878,
+2879,
+2880,
+2881,
+2882,
+2883,
+2884,
+2885,
+2886,
+2887,
+2888,
+2889,
+2890,
+2891,
+2892,
+2893,
+2894,
+2895,
+2896,
+2897,
+2898,
+2899,
+2900,
+2901,
+2902,
+2903,
+2904,
+2906,
+2907,
+2908,
+2909,
+2910,
+2911,
+2912,
+2913,
+2914,
+2915,
+2916,
+2917,
+2918,
+2919,
+2920,
+2921,
+2922,
+2923,
+2924,
+2926,
+2927,
+2928,
+2929,
+2930,
+2931,
+2932,
+2933,
+2934,
+2935,
+2936,
+2937,
+2938,
+2939,
+2940,
+2941,
+2942,
+2943,
+2944,
+2945,
+2946,
+2947,
+2948,
+2949,
+2950,
+2951,
+2952,
+2953,
+2954,
+2955,
+2956,
+2957,
+2958,
+2959,
+2960,
+2961,
+2962,
+2963,
+2964,
+2965,
+2966,
+2967,
+2968,
+2969,
+2970,
+2971,
+2972,
+2973,
+2974,
+2975,
+2976,
+2977,
+2978,
+2979,
+2980,
+2981,
+2982,
+2983,
+2984,
+2985,
+2986,
+2987,
+2988,
+2989,
+2990,
+2991,
+2992,
+2993,
+2994,
+2995,
+2996,
+2997,
+2998,
+3000,
+3002,
+3003,
+3004,
+3005,
+3006,
+3007,
+3008,
+3009,
+3010,
+3011,
+3012,
+3013,
+3014,
+3015,
+3016,
+3017,
+3018,
+3019,
+3020,
+3021,
+3022,
+3023,
+3024,
+3025,
+3026,
+3027,
+3028,
+3029,
+3030,
+3031,
+3032,
+3033,
+3034,
+3035,
+3036,
+3037,
+3038,
+3039,
+3040,
+3041,
+3042,
+3043,
+3044,
+3045,
+3046,
+3047,
+3048,
+3049,
+3050,
+3051,
+3052,
+3053,
+3054,
+3055,
+3056,
+3057,
+3058,
+3059,
+3060,
+3061,
+3062,
+3063,
+3064,
+3065,
+3066,
+3067,
+3068,
+3069,
+3070,
+3071,
+3072,
+3073,
+3074,
+3075,
+3076,
+3077,
+3078,
+3079,
+3080,
+3081,
+3082,
+3083,
+3084,
+3085,
+3086,
+3087,
+3088,
+3089,
+3090,
+3091,
+3093,
+3094,
+3095,
+3096,
+3098,
+3099,
+3100,
+3101,
+3102,
+3103,
+3104,
+3105,
+3106,
+3107,
+3108,
+3109,
+3110,
+3111,
+3112,
+3113,
+3114,
+3115,
+3116,
+3117,
+3118,
+3119,
+3120,
+3122,
+3123,
+3124,
+3125,
+3127,
+3128,
+3129,
+3130,
+3131,
+3132,
+3133,
+3134,
+3135,
+3136,
+3137,
+3138,
+3139,
+3140,
+3141,
+3142,
+3143,
+3144,
+3145,
+3146,
+3147,
+3148,
+3149,
+3150,
+3151,
+3152,
+3153,
+3154,
+3155,
+3156,
+3157,
+3158,
+3159,
+3160,
+3161,
+3162,
+3163,
+3164,
+3165,
+3166,
+3167,
+3168,
+3169,
+3170,
+3171,
+3172,
+3173,
+3174,
+3175,
+3176,
+3177,
+3178,
+3179,
+3180,
+3181,
+3182,
+3183,
+3184,
+3185,
+3186,
+3187,
+3188,
+3189,
+3190,
+3191,
+3192,
+3193,
+3194,
+3195,
+3196,
+3197,
+3198,
+3199,
+3200,
+3201,
+3202,
+3203,
+3204,
+3205,
+3206,
+3207,
+3208,
+3209,
+3210,
+3211,
+3212,
+3213,
+3214,
+3215,
+3216,
+3217,
+3218,
+3219,
+3220,
+3221,
+3222,
+3223,
+3224,
+3225,
+3226,
+3227,
+3228,
+3229,
+3230,
+3231,
+3232,
+3233,
+3234,
+3235,
+3236,
+3237,
+3238,
+3239,
+3240,
+3241,
+3242,
+3243,
+3244,
+3245,
+3246,
+3247,
+3248,
+3249,
+3250,
+3251,
+3252,
+3253,
+3254,
+3255,
+3256,
+3257,
+3258,
+3259,
+3260,
+3261,
+3262,
+3263,
+3264,
+3265,
+3266,
+3267,
+3268,
+3269,
+3270,
+3271,
+3272,
+3273,
+3274,
+3275,
+3276,
+3277,
+3278,
+3279,
+3280,
+3281,
+3282,
+3283,
+3284,
+3285,
+3286,
+3287,
+3288,
+3289,
+3290,
+3291,
+3292,
+3293,
+3294,
+3295,
+3296,
+3297,
+3298,
+3299,
+3302,
+3303,
+3304,
+3305,
+3306,
+3307,
+3308,
+3309,
+3310,
+3311,
+3312,
+3313,
+3314,
+3315,
+3316,
+3317,
+3318,
+3319,
+3320,
+3321,
+3326,
+3327,
+3328,
+3329,
+3330,
+3331,
+3332,
+3333,
+3334,
+3335,
+3336,
+3337,
+3338,
+3339,
+3340,
+3341,
+3342,
+3343,
+3344,
+3345,
+3346,
+3347,
+3348,
+3349,
+3350,
+3351,
+3352,
+3353,
+3354,
+3355,
+3356,
+3357,
+3358,
+3359,
+3360,
+3361,
+3362,
+3363,
+3364,
+3365,
+3366,
+3372,
+3373,
+3374,
+3375,
+3376,
+3377,
+3378,
+3379,
+3380,
+3381,
+3382,
+3383,
+3384,
+3385,
+3386,
+3387,
+3388,
+3389,
+3390,
+3391,
+3392,
+3393,
+3394,
+3395,
+3396,
+3397,
+3398,
+3399,
+3400,
+3401,
+3402,
+3405,
+3406,
+3407,
+3408,
+3409,
+3410,
+3411,
+3412,
+3413,
+3414,
+3415,
+3416,
+3417,
+3418,
+3419,
+3420,
+3421,
+3422,
+3423,
+3424,
+3425,
+3426,
+3427,
+3428,
+3429,
+3430,
+3431,
+3432,
+3433,
+3434,
+3435,
+3436,
+3437,
+3438,
+3439,
+3440,
+3441,
+3442,
+3443,
+3444,
+3445,
+3446,
+3447,
+3448,
+3449,
+3450,
+3451,
+3452,
+3453,
+3455,
+3456,
+3457,
+3458,
+3459,
+3460,
+3461,
+3462,
+3463,
+3464,
+3465,
+3466,
+3467,
+3468,
+3469,
+3470,
+3471,
+3472,
+3473,
+3474,
+3475,
+3476,
+3477,
+3478,
+3479,
+3480,
+3481,
+3482,
+3483,
+3484,
+3485,
+3486,
+3487,
+3488,
+3489,
+3490,
+3491,
+3492,
+3493,
+3494,
+3495,
+3496,
+3497,
+3498,
+3499,
+3500,
+3501,
+3502,
+3503,
+3504,
+3505,
+3506,
+3507,
+3508,
+3509,
+3510,
+3511,
+3512,
+3513,
+3514,
+3515,
+3516,
+3517,
+3518,
+3519,
+3520,
+3521,
+3522,
+3523,
+3524,
+3525,
+3526,
+3527,
+3528,
+3529,
+3530,
+3531,
+3532,
+3533,
+3534,
+3535,
+3536,
+3537,
+3538,
+3539,
+3540,
+3541,
+3542,
+3543,
+3544,
+3545,
+3547,
+3548,
+3549,
+3550,
+3551,
+3552,
+3553,
+3554,
+3555,
+3556,
+3557,
+3558,
+3559,
+3560,
+3561,
+3562,
+3563,
+3564,
+3567,
+3568,
+3569,
+3570,
+3571,
+3572,
+3573,
+3574,
+3575,
+3576,
+3577,
+3578,
+3579,
+3580,
+3581,
+3582,
+3583,
+3584,
+3585,
+3586,
+3587,
+3588,
+3589,
+3590,
+3591,
+3592,
+3593,
+3594,
+3595,
+3596,
+3597,
+3598,
+3599,
+3600,
+3601,
+3602,
+3603,
+3604,
+3605,
+3606,
+3607,
+3608,
+3609,
+3610,
+3611,
+3612,
+3613,
+3614,
+3615,
+3616,
+3617,
+3618,
+3619,
+3620,
+3621,
+3622,
+3623,
+3624,
+3625,
+3626,
+3627,
+3628,
+3629,
+3630,
+3631,
+3632,
+3633,
+3634,
+3635,
+3636,
+3637,
+3638,
+3639,
+3640,
+3641,
+3642,
+3643,
+3644,
+3645,
+3646,
+3647,
+3648,
+3649,
+3650,
+3651,
+3652,
+3653,
+3654,
+3655,
+3656,
+3657,
+3658,
+3659,
+3660,
+3661,
+3662,
+3663,
+3664,
+3665,
+3666,
+3667,
+3668,
+3669,
+3670,
+3671,
+3672,
+3673,
+3674,
+3675,
+3676,
+3677,
+3678,
+3679,
+3680,
+3681,
+3682,
+3683,
+3684,
+3685,
+3686,
+3687,
+3688,
+3689,
+3690,
+3691,
+3692,
+3695,
+3696,
+3697,
+3698,
+3699,
+3700,
+3701,
+3702,
+3703,
+3704,
+3705,
+3706,
+3707,
+3708,
+3709,
+3710,
+3711,
+3712,
+3713,
+3714,
+3715,
+3716,
+3717,
+3718,
+3719,
+3720,
+3721,
+3722,
+3723,
+3724,
+3725,
+3726,
+3727,
+3728,
+3729,
+3730,
+3731,
+3732,
+3733,
+3734,
+3735,
+3736,
+3738,
+3739,
+3740,
+3741,
+3742,
+3743,
+3744,
+3745,
+3746,
+3747,
+3748,
+3749,
+3750,
+3751,
+3752,
+3753,
+3754,
+3755,
+3756,
+3757,
+3758,
+3759,
+3760,
+3761,
+3762,
+3763,
+3764,
+3765,
+3767,
+3768,
+3769,
+3770,
+3771,
+3772,
+3773,
+3774,
+3775,
+3776,
+3777,
+3778,
+3779,
+3780,
+3781,
+3782,
+3783,
+3784,
+3785,
+3786,
+3787,
+3788,
+3789,
+3790,
+3791,
+3792,
+3793,
+3794,
+3795,
+3796,
+3797,
+3798,
+3799,
+3800,
+3801,
+3802,
+3803,
+3804,
+3805,
+3806,
+3807,
+3808,
+3809,
+3810,
+3811,
+3812,
+3813,
+3814,
+3815,
+3816,
+3817,
+3818,
+3819,
+3820,
+3821,
+3822,
+3823,
+3824,
+3825,
+3826,
+3827,
+3828,
+3829,
+3830,
+3831,
+3832,
+3833,
+3834,
+3835,
+3836,
+3837,
+3838,
+3839,
+3840,
+3841,
+3842,
+3843,
+3844,
+3845,
+3846,
+3847,
+3848,
+3849,
+3850,
+3851,
+3852,
+3853,
+3854,
+3855,
+3856,
+3857,
+3858,
+3859,
+3860,
+3861,
+3862,
+3863,
+3865,
+3866,
+3867,
+3869,
+3870,
+3871,
+3872,
+3873,
+3874,
+3875,
+3876,
+3877,
+3878,
+3879,
+3880,
+3881,
+3882,
+3883,
+3884,
+3885,
+3886,
+3887,
+3888,
+3889,
+3890,
+3891,
+3892,
+3893,
+3894,
+3895,
+3896,
+3897,
+3898,
+3899,
+3900,
+3901,
+3902,
+3903,
+3904,
+3905,
+3906,
+3907,
+3908,
+3909,
+3910,
+3911,
+3912,
+3913,
+3914,
+3915,
+3916,
+3917,
+3918,
+3919,
+3920,
+3921,
+3922,
+3923,
+3924,
+3925,
+3926,
+3927,
+3928,
+3929,
+3930,
+3931,
+3932,
+3933,
+3934,
+3935,
+3936,
+3937,
+3938,
+3939,
+3940,
+3941,
+3942,
+3943,
+3944,
+3945,
+3946,
+3947,
+3948,
+3949,
+3950,
+3951,
+3952,
+3953,
+3954,
+3955,
+3956,
+3957,
+3958,
+3959,
+3960,
+3961,
+3962,
+3963,
+3964,
+3965,
+3966,
+3967,
+3968,
+3969,
+3970,
+3971,
+3972,
+3973,
+3974,
+3975,
+3976,
+3977,
+3978,
+3979,
+3980,
+3981,
+3982,
+3983,
+3984,
+3985,
+3986,
+3987,
+3988,
+3989,
+3990,
+3991,
+3992,
+3993,
+3995,
+3996,
+3997,
+3998,
+3999,
+4000,
+4001,
+4002,
+4003,
+4004,
+4005,
+4006,
+4007,
+4008,
+4009,
+4010,
+4011,
+4012,
+4013,
+4014,
+4015,
+4016,
+4017,
+4018,
+4019,
+4020,
+4021,
+4022,
+4023,
+4024,
+4025,
+4026,
+4027,
+4028,
+4029,
+4030,
+4031,
+4032,
+4033,
+4034,
+4035,
+4036,
+4037,
+4038,
+4039,
+4040,
+4041,
+4042,
+4043,
+4044,
+4045,
+4046,
+4047,
+4049,
+4050,
+4051,
+4052,
+4053,
+4054,
+4055,
+4056,
+4057,
+4058,
+4059,
+4060,
+4061,
+4062,
+4063,
+4064,
+4065,
+4066,
+4067,
+4068,
+4069,
+4070,
+4071,
+4072,
+4073,
+4074,
+4075,
+4076,
+4077,
+4079,
+4080,
+4081,
+4082,
+4083,
+4084,
+4086,
+4089,
+4090,
+4091,
+4092,
+4093,
+4094,
+4095,
+4096,
+4097,
+4098,
+4099,
+4100,
+4101,
+4102,
+4103,
+4104,
+4105,
+4106,
+4107,
+4108,
+4109,
+4110,
+4111,
+4112,
+4113,
+4114,
+4115,
+4116,
+4117,
+4118,
+4119,
+4121,
+4122,
+4123,
+4124,
+4125,
+4126,
+4127,
+4128,
+4129,
+4130,
+4131,
+4132,
+4133,
+4134,
+4135,
+4136,
+4137,
+4138,
+4139,
+4140,
+4141,
+4142,
+4143,
+4145,
+4146,
+4147,
+4148,
+4149,
+4150,
+4151,
+4152,
+4153,
+4154,
+4155,
+4156,
+4157,
+4158,
+4159,
+4160,
+4161,
+4162,
+4163,
+4164,
+4165,
+4166,
+4167,
+4168,
+4169,
+4172,
+4173,
+4174,
+4177,
+4178,
+4179,
+4180,
+4181,
+4182,
+4183,
+4184,
+4185,
+4188,
+4191,
+4192,
+4199,
+4300,
+4301,
+4302,
+4303,
+4304,
+4305,
+4306,
+4307,
+4308,
+4309,
+4310,
+4320,
+4321,
+4322,
+4323,
+4324,
+4325,
+4326,
+4327,
+4328,
+4340,
+4341,
+4342,
+4343,
+4344,
+4345,
+4346,
+4347,
+4348,
+4349,
+4350,
+4351,
+4352,
+4353,
+4354,
+4355,
+4356,
+4357,
+4358,
+4359,
+4361,
+4362,
+4368,
+4369,
+4370,
+4371,
+4372,
+4373,
+4375,
+4376,
+4377,
+4378,
+4379,
+4389,
+4390,
+4394,
+4395,
+4400,
+4401,
+4402,
+4403,
+4404,
+4405,
+4406,
+4425,
+4426,
+4430,
+4441,
+4442,
+4443,
+4444,
+4445,
+4446,
+4447,
+4448,
+4449,
+4450,
+4451,
+4452,
+4453,
+4454,
+4455,
+4456,
+4457,
+4458,
+4484,
+4486,
+4488,
+4500,
+4535,
+4536,
+4537,
+4538,
+4545,
+4546,
+4547,
+4548,
+4549,
+4550,
+4551,
+4552,
+4554,
+4555,
+4556,
+4557,
+4558,
+4559,
+4566,
+4567,
+4568,
+4569,
+4591,
+4592,
+4593,
+4594,
+4595,
+4596,
+4597,
+4598,
+4599,
+4600,
+4601,
+4658,
+4659,
+4660,
+4661,
+4662,
+4663,
+4664,
+4665,
+4666,
+4667,
+4668,
+4669,
+4670,
+4671,
+4672,
+4673,
+4674,
+4675,
+4676,
+4677,
+4678,
+4679,
+4680,
+4681,
+4682,
+4683,
+4684,
+4685,
+4686,
+4687,
+4688,
+4689,
+4690,
+4691,
+4692,
+4700,
+4701,
+4702,
+4725,
+4726,
+4727,
+4728,
+4729,
+4730,
+4732,
+4737,
+4738,
+4739,
+4740,
+4741,
+4742,
+4743,
+4744,
+4745,
+4749,
+4750,
+4751,
+4752,
+4784,
+4785,
+4800,
+4801,
+4802,
+4803,
+4804,
+4827,
+4837,
+4838,
+4839,
+4840,
+4841,
+4842,
+4843,
+4844,
+4845,
+4846,
+4847,
+4848,
+4849,
+4850,
+4851,
+4867,
+4868,
+4869,
+4870,
+4871,
+4876,
+4877,
+4878,
+4881,
+4882,
+4884,
+4885,
+4894,
+4899,
+4900,
+4914,
+4937,
+4940,
+4941,
+4942,
+4949,
+4950,
+4951,
+4952,
+4969,
+4970,
+4986,
+4987,
+4988,
+4989,
+4990,
+4991,
+4999,
+5000,
+5001,
+5002,
+5003,
+5004,
+5005,
+5006,
+5007,
+5008,
+5009,
+5010,
+5011,
+5012,
+5013,
+5014,
+5020,
+5021,
+5022,
+5023,
+5024,
+5025,
+5026,
+5027,
+5029,
+5030,
+5031,
+5042,
+5043,
+5044,
+5046,
+5047,
+5049,
+5050,
+5051,
+5052,
+5055,
+5056,
+5057,
+5058,
+5059,
+5060,
+5061,
+5062,
+5064,
+5065,
+5066,
+5067,
+5069,
+5070,
+5071,
+5072,
+5073,
+5074,
+5079,
+5080,
+5081,
+5082,
+5083,
+5084,
+5085,
+5092,
+5093,
+5094,
+5099,
+5100,
+5101,
+5102,
+5104,
+5105,
+5111,
+5112,
+5116,
+5133,
+5136,
+5137,
+5145,
+5150,
+5151,
+5152,
+5154,
+5155,
+5164,
+5165,
+5166,
+5167,
+5168,
+5190,
+5191,
+5192,
+5193,
+5200,
+5201,
+5202,
+5203,
+5223,
+5224,
+5225,
+5226,
+5227,
+5234,
+5235,
+5236,
+5237,
+5245,
+5246,
+5247,
+5248,
+5249,
+5250,
+5251,
+5252,
+5264,
+5265,
+5270,
+5271,
+5272,
+5282,
+5298,
+5299,
+5300,
+5301,
+5302,
+5303,
+5304,
+5305,
+5306,
+5307,
+5308,
+5309,
+5310,
+5312,
+5313,
+5314,
+5315,
+5343,
+5344,
+5350,
+5351,
+5352,
+5353,
+5354,
+5355,
+5356,
+5357,
+5358,
+5359,
+5360,
+5361,
+5362,
+5363,
+5397,
+5398,
+5399,
+5400,
+5401,
+5402,
+5403,
+5404,
+5405,
+5406,
+5407,
+5408,
+5409,
+5410,
+5411,
+5412,
+5413,
+5414,
+5415,
+5416,
+5417,
+5418,
+5419,
+5420,
+5421,
+5422,
+5423,
+5424,
+5425,
+5426,
+5427,
+5428,
+5429,
+5430,
+5431,
+5432,
+5433,
+5434,
+5435,
+5436,
+5437,
+5443,
+5453,
+5454,
+5455,
+5456,
+5461,
+5462,
+5463,
+5464,
+5465,
+5500,
+5501,
+5502,
+5503,
+5504,
+5505,
+5506,
+5553,
+5554,
+5555,
+5556,
+5567,
+5568,
+5573,
+5580,
+5581,
+5582,
+5583,
+5584,
+5585,
+5597,
+5598,
+5599,
+5600,
+5601,
+5602,
+5603,
+5604,
+5605,
+5627,
+5628,
+5629,
+5630,
+5631,
+5632,
+5633,
+5634,
+5671,
+5672,
+5673,
+5674,
+5675,
+5676,
+5677,
+5678,
+5679,
+5680,
+5681,
+5682,
+5683,
+5688,
+5689,
+5713,
+5714,
+5715,
+5716,
+5717,
+5718,
+5719,
+5720,
+5721,
+5722,
+5723,
+5724,
+5728,
+5729,
+5730,
+5741,
+5742,
+5743,
+5744,
+5745,
+5746,
+5747,
+5748,
+5750,
+5755,
+5757,
+5766,
+5767,
+5768,
+5769,
+5770,
+5771,
+5777,
+5781,
+5782,
+5783,
+5784,
+5785,
+5786,
+5787,
+5793,
+5794,
+5813,
+5814,
+5859,
+5863,
+5900,
+5910,
+5911,
+5912,
+5913,
+5963,
+5968,
+5969,
+5984,
+5985,
+5986,
+5987,
+5988,
+5989,
+5990,
+5991,
+5992,
+5999,
+6000,
+6064,
+6065,
+6066,
+6069,
+6070,
+6071,
+6072,
+6073,
+6074,
+6082,
+6083,
+6085,
+6086,
+6087,
+6100,
+6101,
+6102,
+6103,
+6104,
+6105,
+6106,
+6107,
+6108,
+6109,
+6110,
+6111,
+6112,
+6122,
+6123,
+6124,
+6133,
+6140,
+6141,
+6142,
+6143,
+6144,
+6145,
+6146,
+6147,
+6148,
+6149,
+6160,
+6161,
+6162,
+6163,
+6200,
+6222,
+6241,
+6242,
+6243,
+6244,
+6251,
+6252,
+6253,
+6268,
+6269,
+6300,
+6301,
+6306,
+6315,
+6316,
+6320,
+6321,
+6322,
+6324,
+6343,
+6346,
+6347,
+6350,
+6355,
+6360,
+6370,
+6382,
+6389,
+6390,
+6417,
+6420,
+6421,
+6443,
+6444,
+6445,
+6446,
+6456,
+6471,
+6480,
+6481,
+6482,
+6483,
+6484,
+6485,
+6486,
+6487,
+6488,
+6489,
+6500,
+6501,
+6502,
+6503,
+6505,
+6506,
+6507,
+6508,
+6509,
+6510,
+6514,
+6515,
+6543,
+6544,
+6547,
+6548,
+6549,
+6550,
+6551,
+6558,
+6566,
+6568,
+6579,
+6580,
+6581,
+6582,
+6583,
+6619,
+6620,
+6621,
+6622,
+6623,
+6626,
+6627,
+6628,
+6657,
+6670,
+6671,
+6672,
+6673,
+6678,
+6679,
+6689,
+6696,
+6701,
+6702,
+6703,
+6714,
+6715,
+6767,
+6768,
+6769,
+6770,
+6771,
+6785,
+6786,
+6787,
+6788,
+6789,
+6790,
+6791,
+6801,
+6831,
+6841,
+6842,
+6850,
+6868,
+6888,
+6935,
+6936,
+6946,
+6951,
+6961,
+6962,
+6963,
+6964,
+6965,
+6966,
+6969,
+6997,
+6998,
+6999,
+7000,
+7001,
+7002,
+7003,
+7004,
+7005,
+7006,
+7007,
+7008,
+7009,
+7010,
+7011,
+7012,
+7013,
+7014,
+7015,
+7019,
+7020,
+7021,
+7022,
+7023,
+7024,
+7025,
+7030,
+7070,
+7071,
+7080,
+7099,
+7100,
+7101,
+7107,
+7121,
+7128,
+7129,
+7161,
+7162,
+7163,
+7164,
+7165,
+7166,
+7169,
+7170,
+7171,
+7174,
+7200,
+7201,
+7227,
+7262,
+7272,
+7273,
+7274,
+7275,
+7276,
+7277,
+7278,
+7279,
+7280,
+7281,
+7282,
+7365,
+7391,
+7392,
+7393,
+7394,
+7395,
+7397,
+7400,
+7401,
+7402,
+7410,
+7421,
+7426,
+7427,
+7428,
+7429,
+7430,
+7431,
+7437,
+7443,
+7473,
+7491,
+7500,
+7501,
+7510,
+7511,
+7542,
+7543,
+7544,
+7545,
+7546,
+7547,
+7548,
+7549,
+7550,
+7560,
+7566,
+7570,
+7588,
+7624,
+7627,
+7628,
+7629,
+7633,
+7648,
+7674,
+7675,
+7676,
+7677,
+7680,
+7689,
+7697,
+7707,
+7708,
+7720,
+7724,
+7725,
+7726,
+7727,
+7734,
+7738,
+7741,
+7743,
+7744,
+7747,
+7777,
+7778,
+7779,
+7781,
+7786,
+7787,
+7789,
+7794,
+7797,
+7798,
+7799,
+7800,
+7801,
+7810,
+7845,
+7846,
+7880,
+7887,
+7900,
+7901,
+7902,
+7903,
+7913,
+7932,
+7933,
+7967,
+7979,
+7980,
+7982,
+7998,
+7999,
+8000,
+8001,
+8002,
+8003,
+8005,
+8008,
+8019,
+8020,
+8021,
+8022,
+8025,
+8026,
+8032,
+8033,
+8034,
+8040,
+8052,
+8053,
+8054,
+8055,
+8056,
+8057,
+8058,
+8059,
+8074,
+8080,
+8081,
+8082,
+8083,
+8086,
+8087,
+8088,
+8097,
+8100,
+8115,
+8116,
+8118,
+8121,
+8122,
+8128,
+8129,
+8130,
+8131,
+8132,
+8148,
+8149,
+8160,
+8161,
+8182,
+8184,
+8192,
+8194,
+8195,
+8199,
+8200,
+8201,
+8202,
+8204,
+8205,
+8206,
+8207,
+8208,
+8230,
+8243,
+8276,
+8280,
+8292,
+8294,
+8300,
+8301,
+8320,
+8321,
+8351,
+8376,
+8377,
+8378,
+8379,
+8380,
+8383,
+8400,
+8401,
+8402,
+8403,
+8416,
+8417,
+8442,
+8443,
+8444,
+8450,
+8472,
+8473,
+8474,
+8500,
+8501,
+8554,
+8555,
+8567,
+8600,
+8610,
+8611,
+8612,
+8613,
+8614,
+8686,
+8699,
+8732,
+8733,
+8763,
+8764,
+8765,
+8770,
+8786,
+8787,
+8793,
+8800,
+8804,
+8873,
+8880,
+8883,
+8888,
+8889,
+8890,
+8891,
+8892,
+8893,
+8894,
+8899,
+8900,
+8901,
+8910,
+8911,
+8912,
+8913,
+8954,
+8989,
+8990,
+8991,
+8999,
+9000,
+9001,
+9002,
+9007,
+9009,
+9020,
+9021,
+9022,
+9023,
+9024,
+9025,
+9026,
+9080,
+9084,
+9085,
+9086,
+9087,
+9088,
+9089,
+9090,
+9091,
+9092,
+9100,
+9101,
+9102,
+9103,
+9104,
+9105,
+9106,
+9119,
+9131,
+9160,
+9161,
+9162,
+9163,
+9164,
+9191,
+9200,
+9201,
+9202,
+9203,
+9204,
+9205,
+9206,
+9207,
+9208,
+9209,
+9210,
+9211,
+9212,
+9213,
+9214,
+9215,
+9216,
+9217,
+9222,
+9255,
+9278,
+9279,
+9280,
+9281,
+9282,
+9283,
+9284,
+9285,
+9286,
+9287,
+9292,
+9293,
+9294,
+9295,
+9300,
+9318,
+9321,
+9343,
+9344,
+9346,
+9374,
+9380,
+9396,
+9397,
+9400,
+9401,
+9402,
+9418,
+9443,
+9444,
+9450,
+9500,
+9522,
+9535,
+9536,
+9555,
+9592,
+9593,
+9594,
+9595,
+9596,
+9597,
+9598,
+9599,
+9600,
+9612,
+9618,
+9628,
+9629,
+9632,
+9667,
+9668,
+9694,
+9695,
+9700,
+9747,
+9750,
+9753,
+9762,
+9800,
+9801,
+9802,
+9875,
+9876,
+9888,
+9889,
+9898,
+9899,
+9900,
+9901,
+9903,
+9909,
+9911,
+9950,
+9951,
+9952,
+9953,
+9956,
+9966,
+9987,
+9990,
+9991,
+9992,
+9993,
+9994,
+9995,
+9996,
+9997,
+9998,
+9999,
+10000,
+10001,
+10002,
+10003,
+10007,
+10008,
+10009,
+10050,
+10051,
+10080,
+10081,
+10100,
+10101,
+10102,
+10103,
+10104,
+10107,
+10110,
+10111,
+10113,
+10114,
+10115,
+10116,
+10117,
+10128,
+10160,
+10161,
+10162,
+10200,
+10201,
+10252,
+10260,
+10288,
+10500,
+10540,
+10541,
+10542,
+10543,
+10544,
+10800,
+10805,
+10810,
+10860,
+10990,
+11000,
+11001,
+11106,
+11111,
+11112,
+11161,
+11162,
+11163,
+11164,
+11165,
+11171,
+11201,
+11208,
+11211,
+11319,
+11320,
+11321,
+11367,
+11371,
+11600,
+11720,
+11751,
+11876,
+11877,
+11967,
+12000,
+12001,
+12002,
+12003,
+12004,
+12005,
+12006,
+12007,
+12008,
+12012,
+12013,
+12109,
+12121,
+12168,
+12172,
+12300,
+12321,
+12322,
+12345,
+12753,
+13160,
+13216,
+13217,
+13218,
+13223,
+13224,
+13400,
+13720,
+13721,
+13722,
+13724,
+13782,
+13783,
+13785,
+13786,
+13818,
+13819,
+13820,
+13821,
+13822,
+13929,
+14000,
+14001,
+14033,
+14034,
+14141,
+14142,
+14145,
+14149,
+14154,
+14250,
+14414,
+14936,
+14937,
+15000,
+15118,
+15345,
+15363,
+15555,
+15660,
+15740,
+15998,
+16003,
+16161,
+16309,
+16310,
+16311,
+16360,
+16361,
+16367,
+16368,
+16384,
+16666,
+16900,
+16950,
+16991,
+16992,
+16993,
+16994,
+16995,
+17007,
+17185,
+17219,
+17221,
+17222,
+17234,
+17235,
+17500,
+17729,
+17754,
+17755,
+17756,
+18000,
+18181,
+18182,
+18183,
+18184,
+18185,
+18186,
+18187,
+18241,
+18262,
+18463,
+18634,
+18635,
+18769,
+18881,
+18888,
+19000,
+19191,
+19194,
+19283,
+19315,
+19398,
+19410,
+19411,
+19412,
+19539,
+19540,
+19541,
+19999,
+20000,
+20001,
+20002,
+20003,
+20005,
+20012,
+20014,
+20034,
+20046,
+20048,
+20049,
+20167,
+20202,
+20222,
+20480,
+20670,
+20999,
+21000,
+21554,
+21590,
+21800,
+21845,
+21846,
+21847,
+21848,
+21849,
+22000,
+22001,
+22002,
+22003,
+22004,
+22005,
+22273,
+22305,
+22343,
+22347,
+22350,
+22555,
+22763,
+22800,
+22951,
+23000,
+23001,
+23002,
+23003,
+23004,
+23005,
+23272,
+23333,
+23400,
+23401,
+23402,
+24000,
+24001,
+24002,
+24003,
+24004,
+24005,
+24006,
+24242,
+24249,
+24321,
+24386,
+24465,
+24554,
+24676,
+24677,
+24678,
+24680,
+24922,
+25000,
+25001,
+25002,
+25003,
+25004,
+25005,
+25006,
+25007,
+25008,
+25009,
+25793,
+25900,
+25901,
+25902,
+25903,
+26000,
+26133,
+26208,
+26260,
+26261,
+26262,
+26263,
+26486,
+26487,
+26489,
+27345,
+27442,
+27504,
+27782,
+27999,
+28000,
+28240,
+29167,
+30001,
+30002,
+30260,
+30999,
+31029,
+31416,
+31457,
+31620,
+31765,
+31948,
+31949,
+32034,
+32249,
+32483,
+32635,
+32636,
+32767,
+32768,
+32769,
+32770,
+32771,
+32772,
+32773,
+32774,
+32775,
+32776,
+32777,
+32801,
+32896,
+33123,
+33331,
+33434,
+33656,
+34249,
+34378,
+34379,
+34962,
+34963,
+34964,
+34980,
+35355,
+36001,
+36865,
+37475,
+37654,
+38201,
+38202,
+38203,
+39681,
+40000,
+40841,
+40842,
+40843,
+40853,
+41111,
+41794,
+41795,
+42508,
+42509,
+42510,
+43188,
+43189,
+43190,
+43440,
+43441,
+44321,
+44322,
+44323,
+44544,
+44553,
+44600,
+44818,
+45000,
+45054,
+45678,
+45825,
+45966,
+46999,
+47000,
+47557,
+47624,
+47806,
+47808,
+48000,
+48001,
+48002,
+48003,
+48128,
+48129,
+48556,
+48619,
diff --git a/3rdParty/Unbound/src/src/util/locks.c b/3rdParty/Unbound/src/src/util/locks.c
new file mode 100644
index 0000000..425f9b9
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/locks.c
@@ -0,0 +1,264 @@
+/**
+ * util/locks.c - unbound locking primitives
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ * 
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ * Implementation of locking and threading support.
+ * A place for locking debug code since most locking functions are macros.
+ */
+
+#include "config.h"
+#include "util/locks.h"
+#include <signal.h>
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+/** block all signals, masks them away. */
+void 
+ub_thread_blocksigs(void)
+{
+#if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS) || defined(HAVE_SIGPROCMASK)
+#  if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS)
+	int err;
+#  endif
+	sigset_t sigset;
+	sigfillset(&sigset);
+#ifdef HAVE_PTHREAD
+	if((err=pthread_sigmask(SIG_SETMASK, &sigset, NULL)))
+		fatal_exit("pthread_sigmask: %s", strerror(err));
+#else
+#  ifdef HAVE_SOLARIS_THREADS
+	if((err=thr_sigsetmask(SIG_SETMASK, &sigset, NULL)))
+		fatal_exit("thr_sigsetmask: %s", strerror(err));
+#  else 
+	/* have nothing, do single process signal mask */
+	if(sigprocmask(SIG_SETMASK, &sigset, NULL))
+		fatal_exit("sigprocmask: %s", strerror(errno));
+#  endif /* HAVE_SOLARIS_THREADS */
+#endif /* HAVE_PTHREAD */
+#endif /* have signal stuff */
+}
+
+/** unblock one signal, so we can catch it */
+void ub_thread_sig_unblock(int sig)
+{
+#if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS) || defined(HAVE_SIGPROCMASK)
+#  if defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS)
+	int err;
+#  endif
+	sigset_t sigset;
+	sigemptyset(&sigset);
+	sigaddset(&sigset, sig);
+#ifdef HAVE_PTHREAD
+	if((err=pthread_sigmask(SIG_UNBLOCK, &sigset, NULL)))
+		fatal_exit("pthread_sigmask: %s", strerror(err));
+#else
+#  ifdef HAVE_SOLARIS_THREADS
+	if((err=thr_sigsetmask(SIG_UNBLOCK, &sigset, NULL)))
+		fatal_exit("thr_sigsetmask: %s", strerror(err));
+#  else 
+	/* have nothing, do single thread case */
+	if(sigprocmask(SIG_UNBLOCK, &sigset, NULL))
+		fatal_exit("sigprocmask: %s", strerror(errno));
+#  endif /* HAVE_SOLARIS_THREADS */
+#endif /* HAVE_PTHREAD */
+#else
+	(void)sig;
+#endif /* have signal stuff */
+}
+
+#if !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS) && !defined(HAVE_WINDOWS_THREADS)
+/**
+ * No threading available: fork a new process.
+ * This means no shared data structure, and no locking.
+ * Only the main thread ever returns. Exits on errors.
+ * @param thr: the location where to store the thread-id.
+ * @param func: function body of the thread. Return value of func is lost.
+ * @param arg: user argument to func.
+ */
+void 
+ub_thr_fork_create(ub_thread_t* thr, void* (*func)(void*), void* arg)
+{
+	pid_t pid = fork();
+	switch(pid) {
+	default:	/* main */
+			*thr = (ub_thread_t)pid;
+			return;
+	case 0: 	/* child */
+			*thr = (ub_thread_t)getpid();
+			(void)(*func)(arg);
+			exit(0);
+	case -1:	/* error */
+			fatal_exit("could not fork: %s", strerror(errno));
+	}
+}
+
+/**
+ * There is no threading. Wait for a process to terminate.
+ * Note that ub_thread_t is defined as pid_t.
+ * @param thread: the process id to wait for.
+ */
+void ub_thr_fork_wait(ub_thread_t thread)
+{
+	int status = 0;
+	if(waitpid((pid_t)thread, &status, 0) == -1)
+		log_err("waitpid(%d): %s", (int)thread, strerror(errno));
+	if(status != 0)
+		log_warn("process %d abnormal exit with status %d",
+			(int)thread, status);
+}
+#endif /* !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS) && !defined(HAVE_WINDOWS_THREADS) */
+
+#ifdef HAVE_SOLARIS_THREADS
+void* ub_thread_key_get(ub_thread_key_t key)
+{
+	void* ret=NULL;
+	LOCKRET(thr_getspecific(key, &ret));
+	return ret;
+}
+#endif
+
+#ifdef HAVE_WINDOWS_THREADS
+/** log a windows GetLastError message */
+static void log_win_err(const char* str, DWORD err)
+{
+	LPTSTR buf;
+	if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | 
+		FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER, 
+		NULL, err, 0, (LPTSTR)&buf, 0, NULL) == 0) {
+		/* could not format error message */
+		log_err("%s, GetLastError=%d", str, (int)err);
+		return;
+	}
+	log_err("%s, (err=%d): %s", str, (int)err, buf);
+	LocalFree(buf);
+}
+
+void lock_basic_init(lock_basic_t* lock)
+{
+	/* implement own lock, because windows HANDLE as Mutex usage
+	 * uses too many handles and would bog down the whole system. */
+	(void)InterlockedExchange(lock, 0);
+}
+
+void lock_basic_destroy(lock_basic_t* lock)
+{
+	(void)InterlockedExchange(lock, 0);
+}
+
+void lock_basic_lock(lock_basic_t* lock)
+{
+	LONG wait = 1; /* wait 1 msec at first */
+
+	while(InterlockedExchange(lock, 1)) {
+		/* if the old value was 1 then if was already locked */
+		Sleep(wait); /* wait with sleep */
+		wait *= 2;   /* exponential backoff for waiting */
+	}
+	/* the old value was 0, but we inserted 1, we locked it! */
+}
+
+void lock_basic_unlock(lock_basic_t* lock)
+{
+	/* unlock it by inserting the value of 0. xchg for cache coherency. */
+	(void)InterlockedExchange(lock, 0);
+}
+
+void ub_thread_key_create(ub_thread_key_t* key, void* f)
+{
+	*key = TlsAlloc();
+	if(*key == TLS_OUT_OF_INDEXES) {
+		*key = 0;
+		log_win_err("TlsAlloc Failed(OUT_OF_INDEXES)", GetLastError());
+	}
+	else ub_thread_key_set(*key, f);
+}
+
+void ub_thread_key_set(ub_thread_key_t key, void* v)
+{
+	if(!TlsSetValue(key, v)) {
+		log_win_err("TlsSetValue failed", GetLastError());
+	}
+}
+
+void* ub_thread_key_get(ub_thread_key_t key)
+{
+	void* ret = (void*)TlsGetValue(key);
+	if(ret == NULL && GetLastError() != ERROR_SUCCESS) {
+		log_win_err("TlsGetValue failed", GetLastError());
+	}
+	return ret;
+}
+
+void ub_thread_create(ub_thread_t* thr, void* (*func)(void*), void* arg)
+{
+#ifndef HAVE__BEGINTHREADEX
+	*thr = CreateThread(NULL, /* default security (no inherit handle) */
+		0, /* default stack size */
+		(LPTHREAD_START_ROUTINE)func, arg,
+		0, /* default flags, run immediately */
+		NULL); /* do not store thread identifier anywhere */
+#else
+	/* the begintheadex routine setups for the C lib; aligns stack */
+	*thr=(ub_thread_t)_beginthreadex(NULL, 0, (void*)func, arg, 0, NULL);
+#endif
+	if(*thr == NULL) {
+		log_win_err("CreateThread failed", GetLastError());
+		fatal_exit("thread create failed");
+	}
+}
+
+ub_thread_t ub_thread_self(void)
+{
+	return GetCurrentThread();
+}
+
+void ub_thread_join(ub_thread_t thr)
+{
+	DWORD ret = WaitForSingleObject(thr, INFINITE);
+	if(ret == WAIT_FAILED) {
+		log_win_err("WaitForSingleObject(Thread):WAIT_FAILED", 
+			GetLastError());
+	} else if(ret == WAIT_TIMEOUT) {
+		log_win_err("WaitForSingleObject(Thread):WAIT_TIMEOUT", 
+			GetLastError());
+	}
+	/* and close the handle to the thread */
+	if(!CloseHandle(thr)) {
+		log_win_err("CloseHandle(Thread) failed", GetLastError());
+	}
+}
+#endif /* HAVE_WINDOWS_THREADS */
diff --git a/3rdParty/Unbound/src/src/util/locks.h b/3rdParty/Unbound/src/src/util/locks.h
new file mode 100644
index 0000000..91be5c3
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/locks.h
@@ -0,0 +1,293 @@
+/**
+ * util/locks.h - unbound locking primitives
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ * 
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+#ifndef UTIL_LOCKS_H
+#define UTIL_LOCKS_H
+
+/**
+ * \file
+ * Locking primitives.
+ * If pthreads is available, these are used.
+ * If no locking exists, they do nothing.
+ *
+ * The idea is to have different sorts of locks for different tasks.
+ * This allows the locking code to be ported more easily.
+ *
+ * Types of locks that are supported.
+ *   o lock_rw: lock that has many readers and one writer (to a data entry).
+ *   o lock_basic: simple mutex. Blocking, one person has access only.
+ *     This lock is meant for non performance sensitive uses.
+ *   o lock_quick: speed lock. For performance sensitive locking of critical
+ *     sections. Could be implemented by a mutex or a spinlock.
+ * 
+ * Also thread creation and deletion functions are defined here.
+ */
+
+#include "util/log.h"
+
+/**
+ * The following macro is used to check the return value of the
+ * pthread calls. They return 0 on success and an errno on error.
+ * The errno is logged to the logfile with a descriptive comment.
+ */
+#define LOCKRET(func) do {\
+	int lockret_err;		\
+	if( (lockret_err=(func)) != 0)		\
+		log_err("%s at %d could not " #func ": %s", \
+		__FILE__, __LINE__, strerror(lockret_err));	\
+ 	} while(0)
+
+/** DEBUG: use thread debug whenever possible */
+#if defined(HAVE_PTHREAD) && defined(HAVE_PTHREAD_SPINLOCK_T) && defined(ENABLE_LOCK_CHECKS)
+#  define USE_THREAD_DEBUG
+#endif
+
+#ifdef USE_THREAD_DEBUG
+/******************* THREAD DEBUG ************************/
+/* (some) checking; to detect races and deadlocks. */
+#include "testcode/checklocks.h"
+
+#else /* USE_THREAD_DEBUG */
+#define lock_protect(lock, area, size) /* nop */
+#define lock_unprotect(lock, area) /* nop */
+#define lock_get_mem(lock) (0) /* nothing */
+#define checklock_start() /* nop */
+#define checklock_stop() /* nop */
+
+#ifdef HAVE_PTHREAD
+#include <pthread.h>
+
+/******************* PTHREAD ************************/
+
+/** use pthread mutex for basic lock */
+typedef pthread_mutex_t lock_basic_t;
+/** small front for pthread init func, NULL is default attrs. */
+#define lock_basic_init(lock) LOCKRET(pthread_mutex_init(lock, NULL))
+#define lock_basic_destroy(lock) LOCKRET(pthread_mutex_destroy(lock))
+#define lock_basic_lock(lock) LOCKRET(pthread_mutex_lock(lock))
+#define lock_basic_unlock(lock) LOCKRET(pthread_mutex_unlock(lock))
+
+#ifndef HAVE_PTHREAD_RWLOCK_T
+/** in case rwlocks are not supported, use a mutex. */
+typedef pthread_mutex_t lock_rw_t;
+#define lock_rw_init(lock) LOCKRET(pthread_mutex_init(lock, NULL))
+#define lock_rw_destroy(lock) LOCKRET(pthread_mutex_destroy(lock))
+#define lock_rw_rdlock(lock) LOCKRET(pthread_mutex_lock(lock))
+#define lock_rw_wrlock(lock) LOCKRET(pthread_mutex_lock(lock))
+#define lock_rw_unlock(lock) LOCKRET(pthread_mutex_unlock(lock))
+#else /* HAVE_PTHREAD_RWLOCK_T */
+/** we use the pthread rwlock */
+typedef pthread_rwlock_t lock_rw_t;
+/** small front for pthread init func, NULL is default attrs. */
+#define lock_rw_init(lock) LOCKRET(pthread_rwlock_init(lock, NULL))
+#define lock_rw_destroy(lock) LOCKRET(pthread_rwlock_destroy(lock))
+#define lock_rw_rdlock(lock) LOCKRET(pthread_rwlock_rdlock(lock))
+#define lock_rw_wrlock(lock) LOCKRET(pthread_rwlock_wrlock(lock))
+#define lock_rw_unlock(lock) LOCKRET(pthread_rwlock_unlock(lock))
+#endif /* HAVE_PTHREAD_RWLOCK_T */
+
+#ifndef HAVE_PTHREAD_SPINLOCK_T
+/** in case spinlocks are not supported, use a mutex. */
+typedef pthread_mutex_t lock_quick_t;
+/** small front for pthread init func, NULL is default attrs. */
+#define lock_quick_init(lock) LOCKRET(pthread_mutex_init(lock, NULL))
+#define lock_quick_destroy(lock) LOCKRET(pthread_mutex_destroy(lock))
+#define lock_quick_lock(lock) LOCKRET(pthread_mutex_lock(lock))
+#define lock_quick_unlock(lock) LOCKRET(pthread_mutex_unlock(lock))
+
+#else /* HAVE_PTHREAD_SPINLOCK_T */
+/** use pthread spinlock for the quick lock */
+typedef pthread_spinlock_t lock_quick_t;
+/** 
+ * allocate process private since this is available whether
+ * Thread Process-Shared Synchronization is supported or not.
+ * This means only threads inside this process may access the lock.
+ * (not threads from another process that shares memory).
+ * spinlocks are not supported on all pthread platforms. 
+ */
+#define lock_quick_init(lock) LOCKRET(pthread_spin_init(lock, PTHREAD_PROCESS_PRIVATE))
+#define lock_quick_destroy(lock) LOCKRET(pthread_spin_destroy(lock))
+#define lock_quick_lock(lock) LOCKRET(pthread_spin_lock(lock))
+#define lock_quick_unlock(lock) LOCKRET(pthread_spin_unlock(lock))
+
+#endif /* HAVE SPINLOCK */
+
+/** Thread creation */
+typedef pthread_t ub_thread_t;
+/** Pass where to store tread_t in thr. Use default NULL attributes. */
+#define ub_thread_create(thr, func, arg) LOCKRET(pthread_create(thr, NULL, func, arg))
+/** get self id. */
+#define ub_thread_self() pthread_self()
+/** wait for another thread to terminate */
+#define ub_thread_join(thread) LOCKRET(pthread_join(thread, NULL))
+typedef pthread_key_t ub_thread_key_t;
+#define ub_thread_key_create(key, f) LOCKRET(pthread_key_create(key, f))
+#define ub_thread_key_set(key, v) LOCKRET(pthread_setspecific(key, v))
+#define ub_thread_key_get(key) pthread_getspecific(key)
+
+#else /* we do not HAVE_PTHREAD */
+#ifdef HAVE_SOLARIS_THREADS
+
+/******************* SOLARIS THREADS ************************/
+#include <synch.h>
+#include <thread.h>
+
+typedef rwlock_t lock_rw_t;
+#define lock_rw_init(lock) LOCKRET(rwlock_init(lock, USYNC_THREAD, NULL))
+#define lock_rw_destroy(lock) LOCKRET(rwlock_destroy(lock))
+#define lock_rw_rdlock(lock) LOCKRET(rw_rdlock(lock))
+#define lock_rw_wrlock(lock) LOCKRET(rw_wrlock(lock))
+#define lock_rw_unlock(lock) LOCKRET(rw_unlock(lock))
+
+/** use basic mutex */
+typedef mutex_t lock_basic_t;
+#define lock_basic_init(lock) LOCKRET(mutex_init(lock, USYNC_THREAD, NULL))
+#define lock_basic_destroy(lock) LOCKRET(mutex_destroy(lock))
+#define lock_basic_lock(lock) LOCKRET(mutex_lock(lock))
+#define lock_basic_unlock(lock) LOCKRET(mutex_unlock(lock))
+
+/** No spinlocks in solaris threads API. Use a mutex. */
+typedef mutex_t lock_quick_t;
+#define lock_quick_init(lock) LOCKRET(mutex_init(lock, USYNC_THREAD, NULL))
+#define lock_quick_destroy(lock) LOCKRET(mutex_destroy(lock))
+#define lock_quick_lock(lock) LOCKRET(mutex_lock(lock))
+#define lock_quick_unlock(lock) LOCKRET(mutex_unlock(lock))
+
+/** Thread creation, create a default thread. */
+typedef thread_t ub_thread_t;
+#define ub_thread_create(thr, func, arg) LOCKRET(thr_create(NULL, NULL, func, arg, NULL, thr))
+#define ub_thread_self() thr_self()
+#define ub_thread_join(thread) LOCKRET(thr_join(thread, NULL, NULL))
+typedef thread_key_t ub_thread_key_t;
+#define ub_thread_key_create(key, f) LOCKRET(thr_keycreate(key, f))
+#define ub_thread_key_set(key, v) LOCKRET(thr_setspecific(key, v))
+void* ub_thread_key_get(ub_thread_key_t key);
+
+
+#else /* we do not HAVE_SOLARIS_THREADS and no PTHREADS */
+/******************* WINDOWS THREADS ************************/
+#ifdef HAVE_WINDOWS_THREADS
+#include <windows.h>
+
+/* Use a mutex */
+typedef LONG lock_rw_t;
+#define lock_rw_init(lock) lock_basic_init(lock)
+#define lock_rw_destroy(lock) lock_basic_destroy(lock)
+#define lock_rw_rdlock(lock) lock_basic_lock(lock)
+#define lock_rw_wrlock(lock) lock_basic_lock(lock)
+#define lock_rw_unlock(lock) lock_basic_unlock(lock)
+
+/** the basic lock is a mutex, implemented opaquely, for error handling. */
+typedef LONG lock_basic_t;
+void lock_basic_init(lock_basic_t* lock);
+void lock_basic_destroy(lock_basic_t* lock);
+void lock_basic_lock(lock_basic_t* lock);
+void lock_basic_unlock(lock_basic_t* lock);
+
+/** on windows no spinlock, use mutex too. */
+typedef LONG lock_quick_t;
+#define lock_quick_init(lock) lock_basic_init(lock)
+#define lock_quick_destroy(lock) lock_basic_destroy(lock)
+#define lock_quick_lock(lock) lock_basic_lock(lock)
+#define lock_quick_unlock(lock) lock_basic_unlock(lock)
+
+/** Thread creation, create a default thread. */
+typedef HANDLE ub_thread_t;
+void ub_thread_create(ub_thread_t* thr, void* (*func)(void*), void* arg);
+ub_thread_t ub_thread_self(void);
+void ub_thread_join(ub_thread_t thr);
+typedef DWORD ub_thread_key_t;
+void ub_thread_key_create(ub_thread_key_t* key, void* f);
+void ub_thread_key_set(ub_thread_key_t key, void* v);
+void* ub_thread_key_get(ub_thread_key_t key);
+
+#else /* we do not HAVE_SOLARIS_THREADS, PTHREADS or WINDOWS_THREADS */
+
+/******************* NO THREADS ************************/
+#define THREADS_DISABLED 1
+/** In case there is no thread support, define locks to do nothing */
+typedef int lock_rw_t;
+#define lock_rw_init(lock) /* nop */
+#define lock_rw_destroy(lock) /* nop */
+#define lock_rw_rdlock(lock) /* nop */
+#define lock_rw_wrlock(lock) /* nop */
+#define lock_rw_unlock(lock) /* nop */
+
+/** define locks to do nothing */
+typedef int lock_basic_t;
+#define lock_basic_init(lock) /* nop */
+#define lock_basic_destroy(lock) /* nop */
+#define lock_basic_lock(lock) /* nop */
+#define lock_basic_unlock(lock) /* nop */
+
+/** define locks to do nothing */
+typedef int lock_quick_t;
+#define lock_quick_init(lock) /* nop */
+#define lock_quick_destroy(lock) /* nop */
+#define lock_quick_lock(lock) /* nop */
+#define lock_quick_unlock(lock) /* nop */
+
+/** Thread creation, threads do not exist */
+typedef pid_t ub_thread_t;
+/** ub_thread_create is simulated with fork (extremely heavy threads,
+  * with no shared memory). */
+#define ub_thread_create(thr, func, arg) \
+	ub_thr_fork_create(thr, func, arg)
+#define ub_thread_self() getpid()
+#define ub_thread_join(thread) ub_thr_fork_wait(thread)
+void ub_thr_fork_wait(ub_thread_t thread);
+void ub_thr_fork_create(ub_thread_t* thr, void* (*func)(void*), void* arg);
+typedef void* ub_thread_key_t;
+#define ub_thread_key_create(key, f) (*(key)) = NULL
+#define ub_thread_key_set(key, v) (key) = (v)
+#define ub_thread_key_get(key) (key)
+
+#endif /* HAVE_WINDOWS_THREADS */
+#endif /* HAVE_SOLARIS_THREADS */
+#endif /* HAVE_PTHREAD */
+#endif /* USE_THREAD_DEBUG */
+
+/**
+ * Block all signals for this thread.
+ * fatal exit on error.
+ */
+void ub_thread_blocksigs(void);
+
+/**
+ * unblock one signal for this thread.
+ */
+void ub_thread_sig_unblock(int sig);
+
+#endif /* UTIL_LOCKS_H */
diff --git a/3rdParty/Unbound/src/src/util/log.c b/3rdParty/Unbound/src/src/util/log.c
new file mode 100644
index 0000000..fc07dc6
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/log.c
@@ -0,0 +1,453 @@
+/*
+ * util/log.c - implementation of the log code
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+/**
+ * \file
+ * Implementation of log.h.
+ */
+
+#include "config.h"
+#include "util/log.h"
+#include "util/locks.h"
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+#ifdef HAVE_SYSLOG_H
+#  include <syslog.h>
+#else
+/**define LOG_ constants */
+#  define LOG_CRIT 2
+#  define LOG_ERR 3
+#  define LOG_WARNING 4
+#  define LOG_NOTICE 5
+#  define LOG_INFO 6
+#  define LOG_DEBUG 7
+#endif
+#ifdef UB_ON_WINDOWS
+#  include "winrc/win_svc.h"
+#endif
+
+/* default verbosity */
+enum verbosity_value verbosity = 0;
+/** the file logged to. */
+static FILE* logfile = 0;
+/** if key has been created */
+static int key_created = 0;
+/** pthread key for thread ids in logfile */
+static ub_thread_key_t logkey;
+/** the identity of this executable/process */
+static const char* ident="unbound";
+#if defined(HAVE_SYSLOG_H) || defined(UB_ON_WINDOWS)
+/** are we using syslog(3) to log to */
+static int logging_to_syslog = 0;
+#endif /* HAVE_SYSLOG_H */
+/** time to print in log, if NULL, use time(2) */
+static uint32_t* log_now = NULL;
+/** print time in UTC or in secondsfrom1970 */
+static int log_time_asc = 0;
+
+void
+log_init(const char* filename, int use_syslog, const char* chrootdir)
+{
+	FILE *f;
+	if(!key_created) {
+		key_created = 1;
+		ub_thread_key_create(&logkey, NULL);
+	}
+	if(logfile 
+#if defined(HAVE_SYSLOG_H) || defined(UB_ON_WINDOWS)
+	|| logging_to_syslog
+#endif
+	)
+	verbose(VERB_QUERY, "switching log to %s", 
+		use_syslog?"syslog":(filename&&filename[0]?filename:"stderr"));
+	if(logfile && logfile != stderr)
+		fclose(logfile);
+#ifdef HAVE_SYSLOG_H
+	if(logging_to_syslog) {
+		closelog();
+		logging_to_syslog = 0;
+	}
+	if(use_syslog) {
+		/* do not delay opening until first write, because we may
+		 * chroot and no longer be able to access dev/log and so on */
+		openlog(ident, LOG_NDELAY, LOG_DAEMON);
+		logging_to_syslog = 1;
+		return;
+	}
+#elif defined(UB_ON_WINDOWS)
+	if(logging_to_syslog) {
+		logging_to_syslog = 0;
+	}
+	if(use_syslog) {
+		logging_to_syslog = 1;
+		return;
+	}
+#endif /* HAVE_SYSLOG_H */
+	if(!filename || !filename[0]) {
+		logfile = stderr;
+		return;
+	}
+	/* open the file for logging */
+	if(chrootdir && chrootdir[0] && strncmp(filename, chrootdir,
+		strlen(chrootdir)) == 0) 
+		filename += strlen(chrootdir);
+	f = fopen(filename, "a");
+	if(!f) {
+		log_err("Could not open logfile %s: %s", filename, 
+			strerror(errno));
+		return;
+	}
+#ifndef UB_ON_WINDOWS
+	/* line buffering does not work on windows */
+	setvbuf(f, NULL, (int)_IOLBF, 0);
+#endif
+	logfile = f;
+}
+
+void log_file(FILE *f)
+{
+	logfile = f;
+}
+
+void log_thread_set(int* num)
+{
+	ub_thread_key_set(logkey, num);
+}
+
+void log_ident_set(const char* id)
+{
+	ident = id;
+}
+
+void log_set_time(uint32_t* t)
+{
+	log_now = t;
+}
+
+void log_set_time_asc(int use_asc)
+{
+	log_time_asc = use_asc;
+}
+
+void
+log_vmsg(int pri, const char* type,
+	const char *format, va_list args)
+{
+	char message[MAXSYSLOGMSGLEN];
+	unsigned int* tid = (unsigned int*)ub_thread_key_get(logkey);
+	time_t now;
+#if defined(HAVE_STRFTIME) && defined(HAVE_LOCALTIME_R) 
+	char tmbuf[32];
+	struct tm tm;
+#endif
+	(void)pri;
+	vsnprintf(message, sizeof(message), format, args);
+#ifdef HAVE_SYSLOG_H
+	if(logging_to_syslog) {
+		syslog(pri, "[%d:%x] %s: %s", 
+			(int)getpid(), tid?*tid:0, type, message);
+		return;
+	}
+#elif defined(UB_ON_WINDOWS)
+	if(logging_to_syslog) {
+		char m[32768];
+		HANDLE* s;
+		LPCTSTR str = m;
+		DWORD tp = MSG_GENERIC_ERR;
+		WORD wt = EVENTLOG_ERROR_TYPE;
+		if(strcmp(type, "info") == 0) {
+			tp=MSG_GENERIC_INFO;
+			wt=EVENTLOG_INFORMATION_TYPE;
+		} else if(strcmp(type, "warning") == 0) {
+			tp=MSG_GENERIC_WARN;
+			wt=EVENTLOG_WARNING_TYPE;
+		} else if(strcmp(type, "notice") == 0 
+			|| strcmp(type, "debug") == 0) {
+			tp=MSG_GENERIC_SUCCESS;
+			wt=EVENTLOG_SUCCESS;
+		}
+		snprintf(m, sizeof(m), "[%s:%x] %s: %s", 
+			ident, tid?*tid:0, type, message);
+		s = RegisterEventSource(NULL, SERVICE_NAME);
+		if(!s) return;
+		ReportEvent(s, wt, 0, tp, NULL, 1, 0, &str, NULL);
+		DeregisterEventSource(s);
+		return;
+	}
+#endif /* HAVE_SYSLOG_H */
+	if(!logfile) return;
+	if(log_now)
+		now = (time_t)*log_now;
+	else	now = (time_t)time(NULL);
+#if defined(HAVE_STRFTIME) && defined(HAVE_LOCALTIME_R) 
+	if(log_time_asc && strftime(tmbuf, sizeof(tmbuf), "%b %d %H:%M:%S",
+		localtime_r(&now, &tm))%(sizeof(tmbuf)) != 0) {
+		/* %sizeof buf!=0 because old strftime returned max on error */
+		fprintf(logfile, "%s %s[%d:%x] %s: %s\n", tmbuf, 
+			ident, (int)getpid(), tid?*tid:0, type, message);
+	} else
+#endif
+	fprintf(logfile, "[%u] %s[%d:%x] %s: %s\n", (unsigned)now, 
+		ident, (int)getpid(), tid?*tid:0, type, message);
+#ifdef UB_ON_WINDOWS
+	/* line buffering does not work on windows */
+	fflush(logfile);
+#endif
+}
+
+/**
+ * implementation of log_info
+ * @param format: format string printf-style.
+ */
+void
+log_info(const char *format, ...)
+{
+        va_list args;
+	va_start(args, format);
+	log_vmsg(LOG_INFO, "info", format, args);
+	va_end(args);
+}
+
+/**
+ * implementation of log_err
+ * @param format: format string printf-style.
+ */
+void
+log_err(const char *format, ...)
+{
+        va_list args;
+	va_start(args, format);
+	log_vmsg(LOG_ERR, "error", format, args);
+	va_end(args);
+}
+
+/**
+ * implementation of log_warn
+ * @param format: format string printf-style.
+ */
+void
+log_warn(const char *format, ...)
+{
+        va_list args;
+	va_start(args, format);
+	log_vmsg(LOG_WARNING, "warning", format, args);
+	va_end(args);
+}
+
+/**
+ * implementation of fatal_exit
+ * @param format: format string printf-style.
+ */
+void
+fatal_exit(const char *format, ...)
+{
+        va_list args;
+	va_start(args, format);
+	log_vmsg(LOG_CRIT, "fatal error", format, args);
+	va_end(args);
+	exit(1);
+}
+
+/**
+ * implementation of verbose
+ * @param level: verbose level for the message.
+ * @param format: format string printf-style.
+ */
+void
+verbose(enum verbosity_value level, const char* format, ...)
+{
+        va_list args;
+	va_start(args, format);
+	if(verbosity >= level) {
+		if(level == VERB_OPS)
+			log_vmsg(LOG_NOTICE, "notice", format, args);
+		else if(level == VERB_DETAIL)
+			log_vmsg(LOG_INFO, "info", format, args);
+		else	log_vmsg(LOG_DEBUG, "debug", format, args);
+	}
+	va_end(args);
+}
+
+/** log hex data */
+static void 
+log_hex_f(enum verbosity_value v, const char* msg, void* data, size_t length)
+{
+	size_t i, j;
+	uint8_t* data8 = (uint8_t*)data;
+	const char* hexchar = "0123456789ABCDEF";
+	char buf[1024+1]; /* alloc blocksize hex chars + \0 */
+	const size_t blocksize = 512;
+	size_t len;
+
+	if(length == 0) {
+		verbose(v, "%s[%u]", msg, (unsigned)length);
+		return;
+	}
+
+	for(i=0; i<length; i+=blocksize/2) {
+		len = blocksize/2;
+		if(length - i < blocksize/2)
+			len = length - i;
+		for(j=0; j<len; j++) {
+			buf[j*2] = hexchar[ data8[i+j] >> 4 ];
+			buf[j*2 + 1] = hexchar[ data8[i+j] & 0xF ];
+		}
+		buf[len*2] = 0;
+		verbose(v, "%s[%u:%u] %.*s", msg, (unsigned)length, 
+			(unsigned)i, (int)len*2, buf);
+	}
+}
+
+void 
+log_hex(const char* msg, void* data, size_t length)
+{
+	log_hex_f(verbosity, msg, data, length);
+}
+
+void log_buf(enum verbosity_value level, const char* msg, ldns_buffer* buf)
+{
+	if(verbosity < level)
+		return;
+	log_hex_f(level, msg, ldns_buffer_begin(buf), ldns_buffer_limit(buf));
+}
+
+#ifdef USE_WINSOCK
+char* wsa_strerror(DWORD err)
+{
+	static char unknown[32];
+
+	switch(err) {
+	case WSA_INVALID_HANDLE: return "Specified event object handle is invalid.";
+	case WSA_NOT_ENOUGH_MEMORY: return "Insufficient memory available.";
+	case WSA_INVALID_PARAMETER: return "One or more parameters are invalid.";
+	case WSA_OPERATION_ABORTED: return "Overlapped operation aborted.";
+	case WSA_IO_INCOMPLETE: return "Overlapped I/O event object not in signaled state.";
+	case WSA_IO_PENDING: return "Overlapped operations will complete later.";
+	case WSAEINTR: return "Interrupted function call.";
+	case WSAEBADF: return "File handle is not valid.";
+ 	case WSAEACCES: return "Permission denied.";
+	case WSAEFAULT: return "Bad address.";
+	case WSAEINVAL: return "Invalid argument.";
+	case WSAEMFILE: return "Too many open files.";
+	case WSAEWOULDBLOCK: return "Resource temporarily unavailable.";
+	case WSAEINPROGRESS: return "Operation now in progress.";
+	case WSAEALREADY: return "Operation already in progress.";
+	case WSAENOTSOCK: return "Socket operation on nonsocket.";
+	case WSAEDESTADDRREQ: return "Destination address required.";
+	case WSAEMSGSIZE: return "Message too long.";
+	case WSAEPROTOTYPE: return "Protocol wrong type for socket.";
+	case WSAENOPROTOOPT: return "Bad protocol option.";
+	case WSAEPROTONOSUPPORT: return "Protocol not supported.";
+	case WSAESOCKTNOSUPPORT: return "Socket type not supported.";
+	case WSAEOPNOTSUPP: return "Operation not supported.";
+	case WSAEPFNOSUPPORT: return "Protocol family not supported.";
+	case WSAEAFNOSUPPORT: return "Address family not supported by protocol family.";
+	case WSAEADDRINUSE: return "Address already in use.";
+	case WSAEADDRNOTAVAIL: return "Cannot assign requested address.";
+	case WSAENETDOWN: return "Network is down.";
+	case WSAENETUNREACH: return "Network is unreachable.";
+	case WSAENETRESET: return "Network dropped connection on reset.";
+	case WSAECONNABORTED: return "Software caused connection abort.";
+	case WSAECONNRESET: return "Connection reset by peer.";
+	case WSAENOBUFS: return "No buffer space available.";
+	case WSAEISCONN: return "Socket is already connected.";
+	case WSAENOTCONN: return "Socket is not connected.";
+	case WSAESHUTDOWN: return "Cannot send after socket shutdown.";
+	case WSAETOOMANYREFS: return "Too many references.";
+	case WSAETIMEDOUT: return "Connection timed out.";
+	case WSAECONNREFUSED: return "Connection refused.";
+	case WSAELOOP: return "Cannot translate name.";
+	case WSAENAMETOOLONG: return "Name too long.";
+	case WSAEHOSTDOWN: return "Host is down.";
+	case WSAEHOSTUNREACH: return "No route to host.";
+	case WSAENOTEMPTY: return "Directory not empty.";
+	case WSAEPROCLIM: return "Too many processes.";
+	case WSAEUSERS: return "User quota exceeded.";
+	case WSAEDQUOT: return "Disk quota exceeded.";
+	case WSAESTALE: return "Stale file handle reference.";
+	case WSAEREMOTE: return "Item is remote.";
+	case WSASYSNOTREADY: return "Network subsystem is unavailable.";
+	case WSAVERNOTSUPPORTED: return "Winsock.dll version out of range.";
+	case WSANOTINITIALISED: return "Successful WSAStartup not yet performed.";
+	case WSAEDISCON: return "Graceful shutdown in progress.";
+	case WSAENOMORE: return "No more results.";
+	case WSAECANCELLED: return "Call has been canceled.";
+	case WSAEINVALIDPROCTABLE: return "Procedure call table is invalid.";
+	case WSAEINVALIDPROVIDER: return "Service provider is invalid.";
+	case WSAEPROVIDERFAILEDINIT: return "Service provider failed to initialize.";
+	case WSASYSCALLFAILURE: return "System call failure.";
+	case WSASERVICE_NOT_FOUND: return "Service not found.";
+	case WSATYPE_NOT_FOUND: return "Class type not found.";
+	case WSA_E_NO_MORE: return "No more results.";
+	case WSA_E_CANCELLED: return "Call was canceled.";
+	case WSAEREFUSED: return "Database query was refused.";
+	case WSAHOST_NOT_FOUND: return "Host not found.";
+	case WSATRY_AGAIN: return "Nonauthoritative host not found.";
+	case WSANO_RECOVERY: return "This is a nonrecoverable error.";
+	case WSANO_DATA: return "Valid name, no data record of requested type.";
+	case WSA_QOS_RECEIVERS: return "QOS receivers.";
+	case WSA_QOS_SENDERS: return "QOS senders.";
+	case WSA_QOS_NO_SENDERS: return "No QOS senders.";
+	case WSA_QOS_NO_RECEIVERS: return "QOS no receivers.";
+	case WSA_QOS_REQUEST_CONFIRMED: return "QOS request confirmed.";
+	case WSA_QOS_ADMISSION_FAILURE: return "QOS admission error.";
+	case WSA_QOS_POLICY_FAILURE: return "QOS policy failure.";
+	case WSA_QOS_BAD_STYLE: return "QOS bad style.";
+	case WSA_QOS_BAD_OBJECT: return "QOS bad object.";
+	case WSA_QOS_TRAFFIC_CTRL_ERROR: return "QOS traffic control error.";
+	case WSA_QOS_GENERIC_ERROR: return "QOS generic error.";
+	case WSA_QOS_ESERVICETYPE: return "QOS service type error.";
+	case WSA_QOS_EFLOWSPEC: return "QOS flowspec error.";
+	case WSA_QOS_EPROVSPECBUF: return "Invalid QOS provider buffer.";
+	case WSA_QOS_EFILTERSTYLE: return "Invalid QOS filter style.";
+	case WSA_QOS_EFILTERTYPE: return "Invalid QOS filter type.";
+	case WSA_QOS_EFILTERCOUNT: return "Incorrect QOS filter count.";
+	case WSA_QOS_EOBJLENGTH: return "Invalid QOS object length.";
+	case WSA_QOS_EFLOWCOUNT: return "Incorrect QOS flow count.";
+	/*case WSA_QOS_EUNKOWNPSOBJ: return "Unrecognized QOS object.";*/
+	case WSA_QOS_EPOLICYOBJ: return "Invalid QOS policy object.";
+	case WSA_QOS_EFLOWDESC: return "Invalid QOS flow descriptor.";
+	case WSA_QOS_EPSFLOWSPEC: return "Invalid QOS provider-specific flowspec.";
+	case WSA_QOS_EPSFILTERSPEC: return "Invalid QOS provider-specific filterspec.";
+	case WSA_QOS_ESDMODEOBJ: return "Invalid QOS shape discard mode object.";
+	case WSA_QOS_ESHAPERATEOBJ: return "Invalid QOS shaping rate object.";
+	case WSA_QOS_RESERVED_PETYPE: return "Reserved policy QOS element type.";
+	default:
+		snprintf(unknown, sizeof(unknown),
+			"unknown WSA error code %d", (int)err);
+		return unknown;
+	}
+}
+#endif /* USE_WINSOCK */
diff --git a/3rdParty/Unbound/src/src/util/log.h b/3rdParty/Unbound/src/src/util/log.h
new file mode 100644
index 0000000..270ffc5
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/log.h
@@ -0,0 +1,198 @@
+/*
+ * util/log.h - logging service
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains logging functions.
+ */
+
+#ifndef UTIL_LOG_H
+#define UTIL_LOG_H
+#include <ldns/buffer.h>
+
+/**
+ * verbosity value:
+ */
+enum verbosity_value {
+ /** 0 - no verbose messages */
+	NO_VERBOSE = 0,
+ /** 1 - operational information */
+ 	VERB_OPS,
+ /** 2 - detailed information */
+ 	VERB_DETAIL,
+ /** 3 - query level information */
+ 	VERB_QUERY,
+ /** 4 - algorithm level information */
+ 	VERB_ALGO,
+ /** 5 - querier client information */
+	VERB_CLIENT
+};
+
+/** The global verbosity setting */
+extern enum verbosity_value verbosity;
+
+/**
+ * log a verbose message, pass the level for this message.
+ * It has printf formatted arguments. No trailing newline is needed.
+ * @param level: verbosity level for this message, compared to global 
+ *	verbosity setting.
+ * @param format: printf-style format string. Arguments follow.
+ */
+void verbose(enum verbosity_value level, 
+	const char* format, ...) ATTR_FORMAT(printf, 2, 3);
+
+/**
+ * call this to initialize logging services.
+ * @param filename: if NULL stderr is used.
+ * @param use_syslog: set to true to ignore filename and use syslog(3).
+ * @param chrootdir: to which directory we have been chrooted, if any.
+ */
+void log_init(const char* filename, int use_syslog, const char* chrootdir);
+
+/**
+ * Set logging to go to the specified file *.
+ * This setting does not affect the use_syslog setting.
+ * @param f: to that file, or pass NULL to disable logging.
+ */
+void log_file(FILE *f);
+
+/**
+ * Init a thread (will print this number for the thread log entries).
+ * Must be called from the thread itself. If not called 0 is printed.
+ * @param num: number to print for this thread. Owned by caller, must
+ *	continue to exist.
+ */
+void log_thread_set(int* num);
+
+/**
+ * Set identity to print, default is 'unbound'. 
+ * @param id: string to print. Name of executable.
+ */
+void log_ident_set(const char* id);
+
+/**
+ * Set the time value to print in log entries.
+ * @param t: the point is copied and used to find the time.
+ * 	if NULL, time(2) is used.
+ */
+void log_set_time(uint32_t* t);
+
+/**
+ * Set if the time value is printed ascii or decimal in log entries.
+ * @param use_asc: if true, ascii is printed, otherwise decimal.
+ *	If the conversion fails or you have no time functions, 
+ *	decimal is printed.
+ */
+void log_set_time_asc(int use_asc);
+
+/**
+ * Log informational message.
+ * Pass printf formatted arguments. No trailing newline is needed.
+ * @param format: printf-style format string. Arguments follow.
+ */
+void log_info(const char* format, ...) ATTR_FORMAT(printf, 1, 2);
+
+/**
+ * Log error message.
+ * Pass printf formatted arguments. No trailing newline is needed.
+ * @param format: printf-style format string. Arguments follow.
+ */
+void log_err(const char* format, ...) ATTR_FORMAT(printf, 1, 2);
+
+/**
+ * Log warning message.
+ * Pass printf formatted arguments. No trailing newline is needed.
+ * @param format: printf-style format string. Arguments follow.
+ */
+void log_warn(const char* format, ...) ATTR_FORMAT(printf, 1, 2);
+
+/**
+ * Log a hex-string to the log. Can be any length.
+ * performs mallocs to do so, slow. But debug useful.
+ * @param msg: string desc to accompany the hexdump.
+ * @param data: data to dump in hex format.
+ * @param length: length of data.
+ */
+void log_hex(const char* msg, void* data, size_t length);
+
+/**
+ * Easy alternative for log_hex, takes a ldns_buffer.
+ * @param level: verbosity level for this message, compared to global 
+ *	verbosity setting.
+ * @param msg: string desc to print
+ * @param buf: the buffer.
+ */
+void log_buf(enum verbosity_value level, const char* msg, ldns_buffer* buf);
+
+/**
+ * Log fatal error message, and exit the current process.
+ * Pass printf formatted arguments. No trailing newline is needed.
+ * @param format: printf-style format string. Arguments follow.
+ */
+void fatal_exit(const char* format, ...) ATTR_FORMAT(printf, 1, 2);
+
+/**
+ * va_list argument version of log_info.
+ * @param pri: priority type, for example 5 (INFO).
+ * @param type: string to designate type of message (info, error).
+ * @param format: the printf style format to print. no newline.
+ * @param args: arguments for format string.
+ */
+void log_vmsg(int pri, const char* type, const char* format, va_list args);
+
+/**
+ * an assertion that is thrown to the logfile.
+ */
+#ifdef UNBOUND_DEBUG
+#  define log_assert(x) \
+	do { if(!(x)) \
+		fatal_exit("%s:%d: %s: assertion %s failed", \
+			__FILE__, __LINE__, __func__, #x); \
+	} while(0);
+#else
+#  define log_assert(x) /*nothing*/
+#endif
+
+#ifdef USE_WINSOCK
+/**
+ * Convert WSA error into string.
+ * @param err: from WSAGetLastError()
+ * @return: string.
+ */
+char* wsa_strerror(DWORD err);
+#endif /* USE_WINSOCK */
+
+#endif /* UTIL_LOG_H */
diff --git a/3rdParty/Unbound/src/src/util/mini_event.c b/3rdParty/Unbound/src/src/util/mini_event.c
new file mode 100644
index 0000000..f66214d
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/mini_event.c
@@ -0,0 +1,394 @@
+/*
+ * mini_event.c - implementation of part of libevent api, portably.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ * 
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ * 
+ */
+
+/**
+ * \file
+ * fake libevent implementation. Less broad in functionality, and only
+ * supports select(2).
+ */
+
+#include "config.h"
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+#include <sys/time.h>
+
+#if defined(USE_MINI_EVENT) && !defined(USE_WINSOCK)
+#include <signal.h>
+#include "util/mini_event.h"
+#include "util/fptr_wlist.h"
+
+/** compare events in tree, based on timevalue, ptr for uniqueness */
+int mini_ev_cmp(const void* a, const void* b)
+{
+	const struct event *e = (const struct event*)a;
+	const struct event *f = (const struct event*)b;
+	if(e->ev_timeout.tv_sec < f->ev_timeout.tv_sec)
+		return -1;
+	if(e->ev_timeout.tv_sec > f->ev_timeout.tv_sec)
+		return 1;
+	if(e->ev_timeout.tv_usec < f->ev_timeout.tv_usec)
+		return -1;
+	if(e->ev_timeout.tv_usec > f->ev_timeout.tv_usec)
+		return 1;
+	if(e < f)
+		return -1;
+	if(e > f)
+		return 1;
+	return 0;
+}
+
+/** set time */
+static int
+settime(struct event_base* base)
+{
+	if(gettimeofday(base->time_tv, NULL) < 0) {
+		return -1;
+	}
+#ifndef S_SPLINT_S
+	*base->time_secs = (uint32_t)base->time_tv->tv_sec;
+#endif
+	return 0;
+}
+
+/** create event base */
+void *event_init(uint32_t* time_secs, struct timeval* time_tv)
+{
+	struct event_base* base = (struct event_base*)malloc(
+		sizeof(struct event_base));
+	if(!base)
+		return NULL;
+	memset(base, 0, sizeof(*base));
+	base->time_secs = time_secs;
+	base->time_tv = time_tv;
+	if(settime(base) < 0) {
+		event_base_free(base);
+		return NULL;
+	}
+	base->times = rbtree_create(mini_ev_cmp);
+	if(!base->times) {
+		event_base_free(base);
+		return NULL;
+	}
+	base->capfd = MAX_FDS;
+#ifdef FD_SETSIZE
+	if((int)FD_SETSIZE < base->capfd)
+		base->capfd = (int)FD_SETSIZE;
+#endif
+	base->fds = (struct event**)calloc((size_t)base->capfd, 
+		sizeof(struct event*));
+	if(!base->fds) {
+		event_base_free(base);
+		return NULL;
+	}
+	base->signals = (struct event**)calloc(MAX_SIG, sizeof(struct event*));
+	if(!base->signals) {
+		event_base_free(base);
+		return NULL;
+	}
+#ifndef S_SPLINT_S
+	FD_ZERO(&base->reads);
+	FD_ZERO(&base->writes);
+#endif
+	return base;
+}
+
+/** get version */
+const char *event_get_version(void)
+{
+	return "mini-event-"PACKAGE_VERSION;
+}
+
+/** get polling method, select */
+const char *event_get_method(void)
+{
+	return "select";
+}
+
+/** call timeouts handlers, and return how long to wait for next one or -1 */
+static void handle_timeouts(struct event_base* base, struct timeval* now, 
+	struct timeval* wait)
+{
+	struct event* p;
+#ifndef S_SPLINT_S
+	wait->tv_sec = (time_t)-1;
+#endif
+
+	while((rbnode_t*)(p = (struct event*)rbtree_first(base->times))
+		!=RBTREE_NULL) {
+#ifndef S_SPLINT_S
+		if(p->ev_timeout.tv_sec > now->tv_sec ||
+			(p->ev_timeout.tv_sec==now->tv_sec && 
+		 	p->ev_timeout.tv_usec > now->tv_usec)) {
+			/* there is a next larger timeout. wait for it */
+			wait->tv_sec = p->ev_timeout.tv_sec - now->tv_sec;
+			if(now->tv_usec > p->ev_timeout.tv_usec) {
+				wait->tv_sec--;
+				wait->tv_usec = 1000000 - (now->tv_usec -
+					p->ev_timeout.tv_usec);
+			} else {
+				wait->tv_usec = p->ev_timeout.tv_usec 
+					- now->tv_usec;
+			}
+			return;
+		}
+#endif
+		/* event times out, remove it */
+		(void)rbtree_delete(base->times, p);
+		p->ev_events &= ~EV_TIMEOUT;
+		fptr_ok(fptr_whitelist_event(p->ev_callback));
+		(*p->ev_callback)(p->ev_fd, EV_TIMEOUT, p->ev_arg);
+	}
+}
+
+/** call select and callbacks for that */
+static int handle_select(struct event_base* base, struct timeval* wait)
+{
+	fd_set r, w;
+	int ret, i;
+
+#ifndef S_SPLINT_S
+	if(wait->tv_sec==(time_t)-1)
+		wait = NULL;
+#endif
+	memmove(&r, &base->reads, sizeof(fd_set));
+	memmove(&w, &base->writes, sizeof(fd_set));
+	memmove(&base->ready, &base->content, sizeof(fd_set));
+
+	if((ret = select(base->maxfd+1, &r, &w, NULL, wait)) == -1) {
+		ret = errno;
+		if(settime(base) < 0)
+			return -1;
+		errno = ret;
+		if(ret == EAGAIN || ret == EINTR)
+			return 0;
+		return -1;
+	}
+	if(settime(base) < 0)
+		return -1;
+	
+	for(i=0; i<base->maxfd+1; i++) {
+		short bits = 0;
+		if(!base->fds[i] || !(FD_ISSET(i, &base->ready))) {
+			continue;
+		}
+		if(FD_ISSET(i, &r)) {
+			bits |= EV_READ;
+			ret--;
+		}
+		if(FD_ISSET(i, &w)) {
+			bits |= EV_WRITE;
+			ret--;
+		}
+		bits &= base->fds[i]->ev_events;
+		if(bits) {
+			fptr_ok(fptr_whitelist_event(
+				base->fds[i]->ev_callback));
+			(*base->fds[i]->ev_callback)(base->fds[i]->ev_fd, 
+				bits, base->fds[i]->ev_arg);
+			if(ret==0)
+				break;
+		}
+	}
+	return 0;
+}
+
+/** run select in a loop */
+int event_base_dispatch(struct event_base* base)
+{
+	struct timeval wait;
+	if(settime(base) < 0)
+		return -1;
+	while(!base->need_to_exit)
+	{
+		/* see if timeouts need handling */
+		handle_timeouts(base, base->time_tv, &wait);
+		if(base->need_to_exit)
+			return 0;
+		/* do select */
+		if(handle_select(base, &wait) < 0) {
+			if(base->need_to_exit)
+				return 0;
+			return -1;
+		}
+	}
+	return 0;
+}
+
+/** exit that loop */
+int event_base_loopexit(struct event_base* base, 
+	struct timeval* ATTR_UNUSED(tv))
+{
+	base->need_to_exit = 1;
+	return 0;
+}
+
+/* free event base, free events yourself */
+void event_base_free(struct event_base* base)
+{
+	if(!base)
+		return;
+	if(base->times)
+		free(base->times);
+	if(base->fds)
+		free(base->fds);
+	if(base->signals)
+		free(base->signals);
+	free(base);
+}
+
+/** set content of event */
+void event_set(struct event* ev, int fd, short bits, 
+	void (*cb)(int, short, void *), void* arg)
+{
+	ev->node.key = ev;
+	ev->ev_fd = fd;
+	ev->ev_events = bits;
+	ev->ev_callback = cb;
+	fptr_ok(fptr_whitelist_event(ev->ev_callback));
+	ev->ev_arg = arg;
+	ev->added = 0;
+}
+
+/* add event to a base */
+int event_base_set(struct event_base* base, struct event* ev)
+{
+	ev->ev_base = base;
+	ev->added = 0;
+	return 0;
+}
+
+/* add event to make it active, you may not change it with event_set anymore */
+int event_add(struct event* ev, struct timeval* tv)
+{
+	if(ev->added)
+		event_del(ev);
+	if(ev->ev_fd != -1 && ev->ev_fd >= ev->ev_base->capfd)
+		return -1;
+	if( (ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
+		ev->ev_base->fds[ev->ev_fd] = ev;
+		if(ev->ev_events&EV_READ) {
+			FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->reads);
+		}
+		if(ev->ev_events&EV_WRITE) {
+			FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->writes);
+		}
+		FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->content);
+		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->ready);
+		if(ev->ev_fd > ev->ev_base->maxfd)
+			ev->ev_base->maxfd = ev->ev_fd;
+	}
+	if(tv && (ev->ev_events&EV_TIMEOUT)) {
+#ifndef S_SPLINT_S
+		struct timeval *now = ev->ev_base->time_tv;
+		ev->ev_timeout.tv_sec = tv->tv_sec + now->tv_sec;
+		ev->ev_timeout.tv_usec = tv->tv_usec + now->tv_usec;
+		while(ev->ev_timeout.tv_usec > 1000000) {
+			ev->ev_timeout.tv_usec -= 1000000;
+			ev->ev_timeout.tv_sec++;
+		}
+#endif
+		(void)rbtree_insert(ev->ev_base->times, &ev->node);
+	}
+	ev->added = 1;
+	return 0;
+}
+
+/* remove event, you may change it again */
+int event_del(struct event* ev)
+{
+	if(ev->ev_fd != -1 && ev->ev_fd >= ev->ev_base->capfd)
+		return -1;
+	if((ev->ev_events&EV_TIMEOUT))
+		(void)rbtree_delete(ev->ev_base->times, &ev->node);
+	if((ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
+		ev->ev_base->fds[ev->ev_fd] = NULL;
+		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->reads);
+		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->writes);
+		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->ready);
+		FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->content);
+	}
+	ev->added = 0;
+	return 0;
+}
+
+/** which base gets to handle signals */
+static struct event_base* signal_base = NULL;
+/** signal handler */
+static RETSIGTYPE sigh(int sig)
+{
+	struct event* ev;
+	if(!signal_base || sig < 0 || sig >= MAX_SIG)
+		return;
+	ev = signal_base->signals[sig];
+	if(!ev)
+		return;
+	fptr_ok(fptr_whitelist_event(ev->ev_callback));
+	(*ev->ev_callback)(sig, EV_SIGNAL, ev->ev_arg);
+}
+
+/** install signal handler */
+int signal_add(struct event* ev, struct timeval* ATTR_UNUSED(tv))
+{
+	if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
+		return -1;
+	signal_base = ev->ev_base;
+	ev->ev_base->signals[ev->ev_fd] = ev;
+	ev->added = 1;
+	if(signal(ev->ev_fd, sigh) == SIG_ERR) {
+		return -1;
+	}
+	return 0;
+}
+
+/** remove signal handler */
+int signal_del(struct event* ev)
+{
+	if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
+		return -1;
+	ev->ev_base->signals[ev->ev_fd] = NULL;
+	ev->added = 0;
+	return 0;
+}
+
+#else /* USE_MINI_EVENT */
+#ifndef USE_WINSOCK
+int mini_ev_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
+{
+	return 0;
+}
+#endif /* not USE_WINSOCK */
+#endif /* USE_MINI_EVENT */
diff --git a/3rdParty/Unbound/src/src/util/mini_event.h b/3rdParty/Unbound/src/src/util/mini_event.h
new file mode 100644
index 0000000..248468a
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/mini_event.h
@@ -0,0 +1,177 @@
+/*
+ * mini-event.h - micro implementation of libevent api, using select() only.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ * 
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ * This file implements part of the event(3) libevent api.
+ * The back end is only select. Max number of fds is limited.
+ * Max number of signals is limited, one handler per signal only.
+ * And one handler per fd.
+ *
+ * Although limited to select() and a max (1024) open fds, it
+ * is efficient:
+ * o dispatch call caches fd_sets to use. 
+ * o handler calling takes time ~ to the number of fds.
+ * o timeouts are stored in a redblack tree, sorted, so take log(n).
+ * Timeouts are only accurate to the second (no subsecond accuracy).
+ * To avoid cpu hogging, fractional timeouts are rounded up to a whole second.
+ */
+
+#ifndef MINI_EVENT_H
+#define MINI_EVENT_H
+
+#if defined(USE_MINI_EVENT) && !defined(USE_WINSOCK)
+
+#ifndef HAVE_EVENT_BASE_FREE
+#define HAVE_EVENT_BASE_FREE
+#endif 
+
+/** event timeout */
+#define EV_TIMEOUT	0x01
+/** event fd readable */
+#define EV_READ		0x02
+/** event fd writable */
+#define EV_WRITE	0x04
+/** event signal */
+#define EV_SIGNAL	0x08
+/** event must persist */
+#define EV_PERSIST	0x10
+
+/* needs our redblack tree */
+#include "rbtree.h"
+
+/** max number of file descriptors to support */
+#define MAX_FDS 1024
+/** max number of signals to support */
+#define MAX_SIG 32
+
+/** event base */
+struct event_base
+{
+	/** sorted by timeout (absolute), ptr */
+	rbtree_t* times;
+	/** array of 0 - maxfd of ptr to event for it */
+	struct event** fds;
+	/** max fd in use */
+	int maxfd;
+	/** capacity - size of the fds array */
+	int capfd;
+	/* fdset for read write, for fds ready, and added */
+	fd_set 
+		/** fds for reading */
+		reads, 
+		/** fds for writing */
+		writes, 
+		/** fds determined ready for use */
+		ready, 
+		/** ready plus newly added events. */
+		content;
+	/** array of 0 - maxsig of ptr to event for it */
+	struct event** signals;
+	/** if we need to exit */
+	int need_to_exit;
+	/** where to store time in seconds */
+	uint32_t* time_secs;
+	/** where to store time in microseconds */
+	struct timeval* time_tv;
+};
+
+/**
+ * Event structure. Has some of the event elements.
+ */
+struct event {
+	/** node in timeout rbtree */
+	rbnode_t node;
+	/** is event already added */
+	int added;
+
+	/** event base it belongs to */
+	struct event_base *ev_base;
+	/** fd to poll or -1 for timeouts. signal number for sigs. */
+	int ev_fd;
+	/** what events this event is interested in, see EV_.. above. */
+	short ev_events;
+	/** timeout value */
+	struct timeval ev_timeout;
+
+	/** callback to call: fd, eventbits, userarg */
+	void (*ev_callback)(int, short, void *arg);
+	/** callback user arg */
+	void *ev_arg;
+};
+
+/* function prototypes (some are as they appear in event.h) */
+/** create event base */
+void *event_init(uint32_t* time_secs, struct timeval* time_tv);
+/** get version */
+const char *event_get_version(void);
+/** get polling method, select */
+const char *event_get_method(void);
+/** run select in a loop */
+int event_base_dispatch(struct event_base *);
+/** exit that loop */
+int event_base_loopexit(struct event_base *, struct timeval *);
+/** free event base. Free events yourself */
+void event_base_free(struct event_base *);
+/** set content of event */
+void event_set(struct event *, int, short, void (*)(int, short, void *), void *);
+/** add event to a base. You *must* call this for every event. */
+int event_base_set(struct event_base *, struct event *);
+/** add event to make it active. You may not change it with event_set anymore */
+int event_add(struct event *, struct timeval *);
+/** remove event. You may change it again */
+int event_del(struct event *);
+
+/** add a timer */
+#define evtimer_add(ev, tv)             event_add(ev, tv)
+/** remove a timer */
+#define evtimer_del(ev)                 event_del(ev)
+
+/* uses different implementation. Cannot mix fd/timeouts and signals inside
+ * the same struct event. create several event structs for that.  */
+/** install signal handler */
+int signal_add(struct event *, struct timeval *);
+/** set signal event contents */
+#define signal_set(ev, x, cb, arg)      \
+        event_set(ev, x, EV_SIGNAL|EV_PERSIST, cb, arg)
+/** remove signal handler */
+int signal_del(struct event *);
+
+#endif /* USE_MINI_EVENT and not USE_WINSOCK */
+
+/** compare events in tree, based on timevalue, ptr for uniqueness */
+int mini_ev_cmp(const void* a, const void* b);
+
+#endif /* MINI_EVENT_H */
diff --git a/3rdParty/Unbound/src/src/util/module.c b/3rdParty/Unbound/src/src/util/module.c
new file mode 100644
index 0000000..b45ec6f
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/module.c
@@ -0,0 +1,71 @@
+/*
+ * util/module.c - module interface
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+/**
+ * \file
+ * Implementation of module.h.
+ */
+
+#include "config.h"
+#include "util/module.h"
+
+const char* 
+strextstate(enum module_ext_state s)
+{
+	switch(s) {
+	case module_state_initial: return "module_state_initial";
+	case module_wait_reply: return "module_wait_reply";
+	case module_wait_module: return "module_wait_module";
+	case module_restart_next: return "module_restart_next";
+	case module_wait_subquery: return "module_wait_subquery";
+	case module_error: return "module_error";
+	case module_finished: return "module_finished";
+	}
+	return "bad_extstate_value";
+}
+
+const char* 
+strmodulevent(enum module_ev e)
+{
+	switch(e) {
+	case module_event_new: return "module_event_new";
+	case module_event_pass: return "module_event_pass";
+	case module_event_reply: return "module_event_reply";
+	case module_event_noreply: return "module_event_noreply";
+	case module_event_capsfail: return "module_event_capsfail";
+	case module_event_moddone: return "module_event_moddone";
+	case module_event_error: return "module_event_error";
+	}
+	return "bad_event_value";
+}
diff --git a/3rdParty/Unbound/src/src/util/module.h b/3rdParty/Unbound/src/src/util/module.h
new file mode 100644
index 0000000..41caab4
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/module.h
@@ -0,0 +1,393 @@
+/*
+ * util/module.h - DNS handling module interface
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains the interface for DNS handling modules.
+ */
+
+#ifndef UTIL_MODULE_H
+#define UTIL_MODULE_H
+#include "util/storage/lruhash.h"
+#include "util/data/msgreply.h"
+#include "util/data/msgparse.h"
+struct alloc_cache;
+struct rrset_cache;
+struct key_cache;
+struct config_file;
+struct slabhash;
+struct query_info;
+struct edns_data;
+struct regional;
+struct worker;
+struct module_qstate;
+struct ub_randstate;
+struct mesh_area;
+struct mesh_state;
+struct val_anchors;
+struct val_neg_cache;
+struct iter_forwards;
+
+/** Maximum number of modules in operation */
+#define MAX_MODULE 5
+
+/**
+ * Module environment.
+ * Services and data provided to the module.
+ */
+struct module_env {
+	/* --- data --- */
+	/** config file with config options */
+	struct config_file* cfg;
+	/** shared message cache */
+	struct slabhash* msg_cache;
+	/** shared rrset cache */
+	struct rrset_cache* rrset_cache;
+	/** shared infrastructure cache (edns, lameness) */
+	struct infra_cache* infra_cache;
+	/** shared key cache */
+	struct key_cache* key_cache;
+
+	/* --- services --- */
+	/** 
+	 * Send serviced DNS query to server. UDP/TCP and EDNS is handled.
+	 * operate() should return with wait_reply. Later on a callback 
+	 * will cause operate() to be called with event timeout or reply.
+	 * The time until a timeout is calculated from roundtrip timing,
+	 * several UDP retries are attempted.
+	 * @param qname: query name. (host order)
+	 * @param qnamelen: length in bytes of qname, including trailing 0.
+	 * @param qtype: query type. (host order)
+	 * @param qclass: query class. (host order)
+	 * @param flags: host order flags word, with opcode and CD bit.
+	 * @param dnssec: if set, EDNS record will have bits set.
+	 *	If EDNS_DO bit is set, DO bit is set in EDNS records.
+	 *	If BIT_CD is set, CD bit is set in queries with EDNS records.
+	 * @param want_dnssec: if set, the validator wants DNSSEC.  Without
+	 * 	EDNS, the answer is likely to be useless for this domain.
+	 * @param addr: where to.
+	 * @param addrlen: length of addr.
+	 * @param zone: delegation point name.
+	 * @param zonelen: length of zone name.
+	 * @param q: wich query state to reactivate upon return.
+	 * @return: false on failure (memory or socket related). no query was
+	 *	sent. Or returns an outbound entry with qsent and qstate set.
+	 *	This outbound_entry will be used on later module invocations
+	 *	that involve this query (timeout, error or reply).
+	 */
+	struct outbound_entry* (*send_query)(uint8_t* qname, size_t qnamelen, 
+		uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec, 
+		int want_dnssec, struct sockaddr_storage* addr, 
+		socklen_t addrlen, uint8_t* zone, size_t zonelen,
+		struct module_qstate* q);
+
+	/**
+	 * Detach-subqueries.
+	 * Remove all sub-query references from this query state.
+	 * Keeps super-references of those sub-queries correct.
+	 * Updates stat items in mesh_area structure.
+	 * @param qstate: used to find mesh state.
+	 */
+	void (*detach_subs)(struct module_qstate* qstate);
+
+	/**
+	 * Attach subquery.
+	 * Creates it if it does not exist already.
+	 * Keeps sub and super references correct.
+	 * Updates stat items in mesh_area structure.
+	 * Pass if it is priming query or not.
+	 * return:
+	 * o if error (malloc) happened.
+	 * o need to initialise the new state (module init; it is a new state).
+	 *   so that the next run of the query with this module is successful.
+	 * o no init needed, attachment successful.
+	 * 
+	 * @param qstate: the state to find mesh state, and that wants to 
+	 * 	receive the results from the new subquery.
+	 * @param qinfo: what to query for (copied).
+	 * @param qflags: what flags to use (RD, CD flag or not).
+	 * @param prime: if it is a (stub) priming query.
+	 * @param newq: If the new subquery needs initialisation, it is 
+	 * 	returned, otherwise NULL is returned.
+	 * @return: false on error, true if success (and init may be needed).
+	 */ 
+	int (*attach_sub)(struct module_qstate* qstate, 
+		struct query_info* qinfo, uint16_t qflags, int prime, 
+		struct module_qstate** newq);
+
+	/**
+	 * Kill newly attached sub. If attach_sub returns newq for 
+	 * initialisation, but that fails, then this routine will cleanup and
+	 * delete the fresly created sub.
+	 * @param newq: the new subquery that is no longer needed.
+	 * 	It is removed.
+	 */
+	void (*kill_sub)(struct module_qstate* newq);
+
+	/**
+	 * Detect if adding a dependency for qstate on name,type,class will
+	 * create a dependency cycle.
+	 * @param qstate: given mesh querystate.
+	 * @param qinfo: query info for dependency. 
+	 * @param flags: query flags of dependency, RD/CD flags.
+	 * @param prime: if dependency is a priming query or not.
+	 * @return true if the name,type,class exists and the given 
+	 * 	qstate mesh exists as a dependency of that name. Thus 
+	 * 	if qstate becomes dependent on name,type,class then a 
+	 * 	cycle is created.
+	 */
+	int (*detect_cycle)(struct module_qstate* qstate, 
+		struct query_info* qinfo, uint16_t flags, int prime);
+
+	/** region for temporary usage. May be cleared after operate() call. */
+	struct regional* scratch;
+	/** buffer for temporary usage. May be cleared after operate() call. */
+	ldns_buffer* scratch_buffer;
+	/** internal data for daemon - worker thread. */
+	struct worker* worker;
+	/** mesh area with query state dependencies */
+	struct mesh_area* mesh;
+	/** allocation service */
+	struct alloc_cache* alloc;
+	/** random table to generate random numbers */
+	struct ub_randstate* rnd;
+	/** time in seconds, converted to integer */
+	uint32_t* now;
+	/** time in microseconds. Relatively recent. */
+	struct timeval* now_tv;
+	/** is validation required for messages, controls client-facing
+	 * validation status (AD bits) and servfails */
+	int need_to_validate;
+	/** trusted key storage; these are the configured keys, if not NULL,
+	 * otherwise configured by validator. These are the trust anchors,
+	 * and are not primed and ready for validation, but on the bright
+	 * side, they are read only memory, thus no locks and fast. */
+	struct val_anchors* anchors;
+	/** negative cache, configured by the validator. if not NULL,
+	 * contains NSEC record lookup trees. */
+	struct val_neg_cache* neg_cache;
+	/** the 5011-probe timer (if any) */
+	struct comm_timer* probe_timer;
+	/** Mapping of forwarding zones to targets.
+	 * iterator forwarder information. per-thread, created by worker */
+	struct iter_forwards* fwds;
+	/** module specific data. indexed by module id. */
+	void* modinfo[MAX_MODULE];
+};
+
+/**
+ * External visible states of the module state machine 
+ * Modules may also have an internal state.
+ * Modules are supposed to run to completion or until blocked.
+ */
+enum module_ext_state {
+	/** initial state - new query */
+	module_state_initial = 0,
+	/** waiting for reply to outgoing network query */
+	module_wait_reply,
+	/** module is waiting for another module */
+	module_wait_module,
+	/** module is waiting for another module; that other is restarted */
+	module_restart_next,
+	/** module is waiting for sub-query */
+	module_wait_subquery,
+	/** module could not finish the query */
+	module_error,
+	/** module is finished with query */
+	module_finished
+};
+
+/**
+ * Events that happen to modules, that start or wakeup modules.
+ */
+enum module_ev {
+	/** new query */
+	module_event_new = 0,
+	/** query passed by other module */
+	module_event_pass,
+	/** reply inbound from server */
+	module_event_reply,
+	/** no reply, timeout or other error */
+	module_event_noreply,
+	/** reply is there, but capitalisation check failed */
+	module_event_capsfail,
+	/** next module is done, and its reply is awaiting you */
+	module_event_moddone,
+	/** error */
+	module_event_error
+};
+
+/** 
+ * Linked list of sockaddrs 
+ * May be allocated such that only 'len' bytes of addr exist for the structure.
+ */
+struct sock_list {
+	/** next in list */
+	struct sock_list* next;
+	/** length of addr */
+	socklen_t len;
+	/** sockaddr */
+	struct sockaddr_storage addr;
+};
+
+/**
+ * Module state, per query.
+ */
+struct module_qstate {
+	/** which query is being answered: name, type, class */
+	struct query_info qinfo;
+	/** flags uint16 from query */
+	uint16_t query_flags;
+	/** if this is a (stub or root) priming query (with hints) */
+	int is_priming;
+
+	/** comm_reply contains server replies */
+	struct comm_reply* reply;
+	/** the reply message, with message for client and calling module */
+	struct dns_msg* return_msg;
+	/** the rcode, in case of error, instead of a reply message */
+	int return_rcode;
+	/** origin of the reply (can be NULL from cache, list for cnames) */
+	struct sock_list* reply_origin;
+	/** IP blacklist for queries */
+	struct sock_list* blacklist;
+	/** region for this query. Cleared when query process finishes. */
+	struct regional* region;
+	/** failure reason information if val-log-level is high */
+	struct config_strlist* errinf;
+
+	/** which module is executing */
+	int curmod;
+	/** module states */
+	enum module_ext_state ext_state[MAX_MODULE];
+	/** module specific data for query. indexed by module id. */
+	void* minfo[MAX_MODULE];
+	/** environment for this query */
+	struct module_env* env;
+	/** mesh related information for this query */
+	struct mesh_state* mesh_info;
+	/** how many seconds before expiry is this prefetched (0 if not) */
+	uint32_t prefetch_leeway;
+};
+
+/** 
+ * Module functionality block
+ */
+struct module_func_block {
+	/** text string name of module */
+	const char* name;
+
+	/** 
+	 * init the module. Called once for the global state.
+	 * This is the place to apply settings from the config file.
+	 * @param env: module environment.
+	 * @param id: module id number.
+	 * return: 0 on error
+	 */
+	int (*init)(struct module_env* env, int id);
+
+	/**
+	 * de-init, delete, the module. Called once for the global state.
+	 * @param env: module environment.
+	 * @param id: module id number.
+	 */
+	void (*deinit)(struct module_env* env, int id);
+
+	/**
+	 * accept a new query, or work further on existing query.
+	 * Changes the qstate->ext_state to be correct on exit.
+	 * @param ev: event that causes the module state machine to 
+	 *	(re-)activate.
+	 * @param qstate: the query state. 
+	 *	Note that this method is not allowed to change the
+	 *	query state 'identity', that is query info, qflags,
+	 *	and priming status.
+	 *	Attach a subquery to get results to a different query.
+	 * @param id: module id number that operate() is called on. 
+	 * @param outbound: if not NULL this event is due to the reply/timeout
+	 *	or error on this outbound query.
+	 * @return: if at exit the ext_state is:
+	 *	o wait_module: next module is started. (with pass event).
+	 *	o error or finished: previous module is resumed.
+	 *	o otherwise it waits until that event happens (assumes
+	 *	  the service routine to make subrequest or send message
+	 *	  have been called.
+	 */
+	void (*operate)(struct module_qstate* qstate, enum module_ev event, 
+		int id, struct outbound_entry* outbound);
+
+	/**
+	 * inform super querystate about the results from this subquerystate.
+	 * Is called when the querystate is finished.  The method invoked is
+	 * the one from the current module active in the super querystate.
+	 * @param qstate: the query state that is finished.
+	 *	Examine return_rcode and return_reply in the qstate.
+	 * @param id: module id for this module.
+	 *	This coincides with the current module for the super qstate.
+	 * @param super: the super querystate that needs to be informed.
+	 */
+	void (*inform_super)(struct module_qstate* qstate, int id,
+		struct module_qstate* super);
+
+	/**
+	 * clear module specific data
+	 */
+	void (*clear)(struct module_qstate* qstate, int id);
+
+	/**
+	 * How much memory is the module specific data using. 
+	 * @param env: module environment.
+	 * @param id: the module id.
+	 * @return the number of bytes that are alloced.
+	 */
+	size_t (*get_mem)(struct module_env* env, int id);
+};
+
+/** 
+ * Debug utility: module external qstate to string 
+ * @param s: the state value.
+ * @return descriptive string.
+ */
+const char* strextstate(enum module_ext_state s);
+
+/** 
+ * Debug utility: module event to string 
+ * @param e: the module event value.
+ * @return descriptive string.
+ */
+const char* strmodulevent(enum module_ev e);
+
+#endif /* UTIL_MODULE_H */
diff --git a/3rdParty/Unbound/src/src/util/net_help.c b/3rdParty/Unbound/src/src/util/net_help.c
new file mode 100644
index 0000000..b3136a3
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/net_help.c
@@ -0,0 +1,693 @@
+/*
+ * util/net_help.c - implementation of the network helper code
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+/**
+ * \file
+ * Implementation of net_help.h.
+ */
+
+#include "config.h"
+#include <ldns/ldns.h>
+#include "util/net_help.h"
+#include "util/log.h"
+#include "util/data/dname.h"
+#include "util/module.h"
+#include "util/regional.h"
+#include <fcntl.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+/** max length of an IP address (the address portion) that we allow */
+#define MAX_ADDR_STRLEN 128 /* characters */
+/** default value for EDNS ADVERTISED size */
+uint16_t EDNS_ADVERTISED_SIZE = 4096;
+
+/* returns true is string addr is an ip6 specced address */
+int
+str_is_ip6(const char* str)
+{
+	if(strchr(str, ':'))
+		return 1;
+	else    return 0;
+}
+
+int 
+fd_set_nonblock(int s) 
+{
+#ifdef HAVE_FCNTL
+	int flag;
+	if((flag = fcntl(s, F_GETFL)) == -1) {
+		log_err("can't fcntl F_GETFL: %s", strerror(errno));
+		flag = 0;
+	}
+	flag |= O_NONBLOCK;
+	if(fcntl(s, F_SETFL, flag) == -1) {
+		log_err("can't fcntl F_SETFL: %s", strerror(errno));
+		return 0;
+	}
+#elif defined(HAVE_IOCTLSOCKET)
+	unsigned long on = 1;
+	if(ioctlsocket(s, FIONBIO, &on) != 0) {
+		log_err("can't ioctlsocket FIONBIO on: %s", 
+			wsa_strerror(WSAGetLastError()));
+	}
+#endif
+	return 1;
+}
+
+int 
+fd_set_block(int s) 
+{
+#ifdef HAVE_FCNTL
+	int flag;
+	if((flag = fcntl(s, F_GETFL)) == -1) {
+		log_err("cannot fcntl F_GETFL: %s", strerror(errno));
+		flag = 0;
+	}
+	flag &= ~O_NONBLOCK;
+	if(fcntl(s, F_SETFL, flag) == -1) {
+		log_err("cannot fcntl F_SETFL: %s", strerror(errno));
+		return 0;
+	}
+#elif defined(HAVE_IOCTLSOCKET)
+	unsigned long off = 0;
+	if(ioctlsocket(s, FIONBIO, &off) != 0) {
+		log_err("can't ioctlsocket FIONBIO off: %s", 
+			wsa_strerror(WSAGetLastError()));
+	}
+#endif	
+	return 1;
+}
+
+int 
+is_pow2(size_t num)
+{
+	if(num == 0) return 1;
+	return (num & (num-1)) == 0;
+}
+
+void* 
+memdup(void* data, size_t len)
+{
+	void* d;
+	if(!data) return NULL;
+	if(len == 0) return NULL;
+	d = malloc(len);
+	if(!d) return NULL;
+	memcpy(d, data, len);
+	return d;
+}
+
+void
+log_addr(enum verbosity_value v, const char* str, 
+	struct sockaddr_storage* addr, socklen_t addrlen)
+{
+	uint16_t port;
+	const char* family = "unknown";
+	char dest[100];
+	int af = (int)((struct sockaddr_in*)addr)->sin_family;
+	void* sinaddr = &((struct sockaddr_in*)addr)->sin_addr;
+	if(verbosity < v)
+		return;
+	switch(af) {
+		case AF_INET: family="ip4"; break;
+		case AF_INET6: family="ip6";
+			sinaddr = &((struct sockaddr_in6*)addr)->sin6_addr;
+			break;
+		case AF_UNIX: family="unix"; break;
+		default: break;
+	}
+	if(inet_ntop(af, sinaddr, dest, (socklen_t)sizeof(dest)) == 0) {
+		strncpy(dest, "(inet_ntop error)", sizeof(dest));
+	}
+	dest[sizeof(dest)-1] = 0;
+	port = ntohs(((struct sockaddr_in*)addr)->sin_port);
+	if(verbosity >= 4)
+		verbose(v, "%s %s %s port %d (len %d)", str, family, dest, 
+			(int)port, (int)addrlen);
+	else	verbose(v, "%s %s port %d", str, dest, (int)port);
+}
+
+int 
+extstrtoaddr(const char* str, struct sockaddr_storage* addr,
+	socklen_t* addrlen)
+{
+	char* s;
+	int port = UNBOUND_DNS_PORT;
+	if((s=strchr(str, '@'))) {
+		char buf[MAX_ADDR_STRLEN];
+		if(s-str >= MAX_ADDR_STRLEN) {
+			return 0;
+		}
+		strncpy(buf, str, MAX_ADDR_STRLEN);
+		buf[s-str] = 0;
+		port = atoi(s+1);
+		if(port == 0 && strcmp(s+1,"0")!=0) {
+			return 0;
+		}
+		return ipstrtoaddr(buf, port, addr, addrlen);
+	}
+	return ipstrtoaddr(str, port, addr, addrlen);
+}
+
+
+int 
+ipstrtoaddr(const char* ip, int port, struct sockaddr_storage* addr,
+	socklen_t* addrlen)
+{
+	uint16_t p;
+	if(!ip) return 0;
+	p = (uint16_t) port;
+	if(str_is_ip6(ip)) {
+		char buf[MAX_ADDR_STRLEN];
+		char* s;
+		struct sockaddr_in6* sa = (struct sockaddr_in6*)addr;
+		*addrlen = (socklen_t)sizeof(struct sockaddr_in6);
+		memset(sa, 0, *addrlen);
+		sa->sin6_family = AF_INET6;
+		sa->sin6_port = (in_port_t)htons(p);
+		if((s=strchr(ip, '%'))) { /* ip6%interface, rfc 4007 */
+			if(s-ip >= MAX_ADDR_STRLEN)
+				return 0;
+			strncpy(buf, ip, MAX_ADDR_STRLEN);
+			buf[s-ip]=0;
+			sa->sin6_scope_id = (uint32_t)atoi(s+1);
+			ip = buf;
+		}
+		if(inet_pton((int)sa->sin6_family, ip, &sa->sin6_addr) <= 0) {
+			return 0;
+		}
+	} else { /* ip4 */
+		struct sockaddr_in* sa = (struct sockaddr_in*)addr;
+		*addrlen = (socklen_t)sizeof(struct sockaddr_in);
+		memset(sa, 0, *addrlen);
+		sa->sin_family = AF_INET;
+		sa->sin_port = (in_port_t)htons(p);
+		if(inet_pton((int)sa->sin_family, ip, &sa->sin_addr) <= 0) {
+			return 0;
+		}
+	}
+	return 1;
+}
+
+int netblockstrtoaddr(const char* str, int port, struct sockaddr_storage* addr,
+        socklen_t* addrlen, int* net)
+{
+	char* s = NULL;
+	*net = (str_is_ip6(str)?128:32);
+	if((s=strchr(str, '/'))) {
+		if(atoi(s+1) > *net) {
+			log_err("netblock too large: %s", str);
+			return 0;
+		}
+		*net = atoi(s+1);
+		if(*net == 0 && strcmp(s+1, "0") != 0) {
+			log_err("cannot parse netblock: '%s'", str);
+			return 0;
+		}
+		if(!(s = strdup(str))) {
+			log_err("out of memory");
+			return 0;
+		}
+		*strchr(s, '/') = '\0';
+	}
+	if(!ipstrtoaddr(s?s:str, port, addr, addrlen)) {
+		free(s);
+		log_err("cannot parse ip address: '%s'", str);
+		return 0;
+	}
+	if(s) {
+		free(s);
+		addr_mask(addr, *addrlen, *net);
+	}
+	return 1;
+}
+
+void
+log_nametypeclass(enum verbosity_value v, const char* str, uint8_t* name, 
+	uint16_t type, uint16_t dclass)
+{
+	char buf[LDNS_MAX_DOMAINLEN+1];
+	char t[12], c[12];
+	const char *ts, *cs; 
+	if(verbosity < v)
+		return;
+	dname_str(name, buf);
+	if(type == LDNS_RR_TYPE_TSIG) ts = "TSIG";
+	else if(type == LDNS_RR_TYPE_IXFR) ts = "IXFR";
+	else if(type == LDNS_RR_TYPE_AXFR) ts = "AXFR";
+	else if(type == LDNS_RR_TYPE_MAILB) ts = "MAILB";
+	else if(type == LDNS_RR_TYPE_MAILA) ts = "MAILA";
+	else if(type == LDNS_RR_TYPE_ANY) ts = "ANY";
+	else if(ldns_rr_descript(type) && ldns_rr_descript(type)->_name)
+		ts = ldns_rr_descript(type)->_name;
+	else {
+		snprintf(t, sizeof(t), "TYPE%d", (int)type);
+		ts = t;
+	}
+	if(ldns_lookup_by_id(ldns_rr_classes, (int)dclass) &&
+		ldns_lookup_by_id(ldns_rr_classes, (int)dclass)->name)
+		cs = ldns_lookup_by_id(ldns_rr_classes, (int)dclass)->name;
+	else {
+		snprintf(c, sizeof(c), "CLASS%d", (int)dclass);
+		cs = c;
+	}
+	log_info("%s %s %s %s", str, buf, ts, cs);
+}
+
+void log_name_addr(enum verbosity_value v, const char* str, uint8_t* zone, 
+	struct sockaddr_storage* addr, socklen_t addrlen)
+{
+	uint16_t port;
+	const char* family = "unknown_family ";
+	char namebuf[LDNS_MAX_DOMAINLEN+1];
+	char dest[100];
+	int af = (int)((struct sockaddr_in*)addr)->sin_family;
+	void* sinaddr = &((struct sockaddr_in*)addr)->sin_addr;
+	if(verbosity < v)
+		return;
+	switch(af) {
+		case AF_INET: family=""; break;
+		case AF_INET6: family="";
+			sinaddr = &((struct sockaddr_in6*)addr)->sin6_addr;
+			break;
+		case AF_UNIX: family="unix_family "; break;
+		default: break;
+	}
+	if(inet_ntop(af, sinaddr, dest, (socklen_t)sizeof(dest)) == 0) {
+		strncpy(dest, "(inet_ntop error)", sizeof(dest));
+	}
+	dest[sizeof(dest)-1] = 0;
+	port = ntohs(((struct sockaddr_in*)addr)->sin_port);
+	dname_str(zone, namebuf);
+	if(af != AF_INET && af != AF_INET6)
+		verbose(v, "%s <%s> %s%s#%d (addrlen %d)",
+			str, namebuf, family, dest, (int)port, (int)addrlen);
+	else	verbose(v, "%s <%s> %s%s#%d",
+			str, namebuf, family, dest, (int)port);
+}
+
+int
+sockaddr_cmp(struct sockaddr_storage* addr1, socklen_t len1, 
+	struct sockaddr_storage* addr2, socklen_t len2)
+{
+	struct sockaddr_in* p1_in = (struct sockaddr_in*)addr1;
+	struct sockaddr_in* p2_in = (struct sockaddr_in*)addr2;
+	struct sockaddr_in6* p1_in6 = (struct sockaddr_in6*)addr1;
+	struct sockaddr_in6* p2_in6 = (struct sockaddr_in6*)addr2;
+	if(len1 < len2)
+		return -1;
+	if(len1 > len2)
+		return 1;
+	log_assert(len1 == len2);
+	if( p1_in->sin_family < p2_in->sin_family)
+		return -1;
+	if( p1_in->sin_family > p2_in->sin_family)
+		return 1;
+	log_assert( p1_in->sin_family == p2_in->sin_family );
+	/* compare ip4 */
+	if( p1_in->sin_family == AF_INET ) {
+		/* just order it, ntohs not required */
+		if(p1_in->sin_port < p2_in->sin_port)
+			return -1;
+		if(p1_in->sin_port > p2_in->sin_port)
+			return 1;
+		log_assert(p1_in->sin_port == p2_in->sin_port);
+		return memcmp(&p1_in->sin_addr, &p2_in->sin_addr, INET_SIZE);
+	} else if (p1_in6->sin6_family == AF_INET6) {
+		/* just order it, ntohs not required */
+		if(p1_in6->sin6_port < p2_in6->sin6_port)
+			return -1;
+		if(p1_in6->sin6_port > p2_in6->sin6_port)
+			return 1;
+		log_assert(p1_in6->sin6_port == p2_in6->sin6_port);
+		return memcmp(&p1_in6->sin6_addr, &p2_in6->sin6_addr, 
+			INET6_SIZE);
+	} else {
+		/* eek unknown type, perform this comparison for sanity. */
+		return memcmp(addr1, addr2, len1);
+	}
+}
+
+int
+sockaddr_cmp_addr(struct sockaddr_storage* addr1, socklen_t len1, 
+	struct sockaddr_storage* addr2, socklen_t len2)
+{
+	struct sockaddr_in* p1_in = (struct sockaddr_in*)addr1;
+	struct sockaddr_in* p2_in = (struct sockaddr_in*)addr2;
+	struct sockaddr_in6* p1_in6 = (struct sockaddr_in6*)addr1;
+	struct sockaddr_in6* p2_in6 = (struct sockaddr_in6*)addr2;
+	if(len1 < len2)
+		return -1;
+	if(len1 > len2)
+		return 1;
+	log_assert(len1 == len2);
+	if( p1_in->sin_family < p2_in->sin_family)
+		return -1;
+	if( p1_in->sin_family > p2_in->sin_family)
+		return 1;
+	log_assert( p1_in->sin_family == p2_in->sin_family );
+	/* compare ip4 */
+	if( p1_in->sin_family == AF_INET ) {
+		return memcmp(&p1_in->sin_addr, &p2_in->sin_addr, INET_SIZE);
+	} else if (p1_in6->sin6_family == AF_INET6) {
+		return memcmp(&p1_in6->sin6_addr, &p2_in6->sin6_addr, 
+			INET6_SIZE);
+	} else {
+		/* eek unknown type, perform this comparison for sanity. */
+		return memcmp(addr1, addr2, len1);
+	}
+}
+
+int
+addr_is_ip6(struct sockaddr_storage* addr, socklen_t len)
+{
+	if(len == (socklen_t)sizeof(struct sockaddr_in6) &&
+		((struct sockaddr_in6*)addr)->sin6_family == AF_INET6)
+		return 1;
+	else    return 0;
+}
+
+void
+addr_mask(struct sockaddr_storage* addr, socklen_t len, int net)
+{
+	uint8_t mask[8] = {0x0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe};
+	int i, max;
+	uint8_t* s;
+	if(addr_is_ip6(addr, len)) {
+		s = (uint8_t*)&((struct sockaddr_in6*)addr)->sin6_addr;
+		max = 128;
+	} else {
+		s = (uint8_t*)&((struct sockaddr_in*)addr)->sin_addr;
+		max = 32;
+	}
+	if(net >= max)
+		return;
+	for(i=net/8+1; i<max/8; i++) {
+		s[i] = 0;
+	}
+	s[net/8] &= mask[net&0x7];
+}
+
+int
+addr_in_common(struct sockaddr_storage* addr1, int net1,
+	struct sockaddr_storage* addr2, int net2, socklen_t addrlen)
+{
+	int min = (net1<net2)?net1:net2;
+	int i, to;
+	int match = 0;
+	uint8_t* s1, *s2;
+	if(addr_is_ip6(addr1, addrlen)) {
+		s1 = (uint8_t*)&((struct sockaddr_in6*)addr1)->sin6_addr;
+		s2 = (uint8_t*)&((struct sockaddr_in6*)addr2)->sin6_addr;
+		to = 16;
+	} else {
+		s1 = (uint8_t*)&((struct sockaddr_in*)addr1)->sin_addr;
+		s2 = (uint8_t*)&((struct sockaddr_in*)addr2)->sin_addr;
+		to = 4;
+	}
+	/* match = bits_in_common(s1, s2, to); */
+	for(i=0; i<to; i++) {
+		if(s1[i] == s2[i]) {
+			match += 8;
+		} else {
+			uint8_t z = s1[i]^s2[i];
+			log_assert(z);
+			while(!(z&0x80)) {
+				match++;
+				z<<=1;
+			}
+			break;
+		}
+	}
+	if(match > min) match = min;
+	return match;
+}
+
+void 
+addr_to_str(struct sockaddr_storage* addr, socklen_t addrlen, 
+	char* buf, size_t len)
+{
+	int af = (int)((struct sockaddr_in*)addr)->sin_family;
+	void* sinaddr = &((struct sockaddr_in*)addr)->sin_addr;
+	if(addr_is_ip6(addr, addrlen))
+		sinaddr = &((struct sockaddr_in6*)addr)->sin6_addr;
+	if(inet_ntop(af, sinaddr, buf, (socklen_t)len) == 0) {
+		snprintf(buf, len, "(inet_ntop_error)");
+	}
+}
+
+int 
+addr_is_ip4mapped(struct sockaddr_storage* addr, socklen_t addrlen)
+{
+	/* prefix for ipv4 into ipv6 mapping is ::ffff:x.x.x.x */
+	const uint8_t map_prefix[16] = 
+		{0,0,0,0,  0,0,0,0, 0,0,0xff,0xff, 0,0,0,0};
+	uint8_t* s;
+	if(!addr_is_ip6(addr, addrlen))
+		return 0;
+	/* s is 16 octet ipv6 address string */
+	s = (uint8_t*)&((struct sockaddr_in6*)addr)->sin6_addr;
+	return (memcmp(s, map_prefix, 12) == 0);
+}
+
+int addr_is_broadcast(struct sockaddr_storage* addr, socklen_t addrlen)
+{
+	int af = (int)((struct sockaddr_in*)addr)->sin_family;
+	void* sinaddr = &((struct sockaddr_in*)addr)->sin_addr;
+	return af == AF_INET && addrlen>=(socklen_t)sizeof(struct sockaddr_in)
+		&& memcmp(sinaddr, "\377\377\377\377", 4) == 0;
+}
+
+int addr_is_any(struct sockaddr_storage* addr, socklen_t addrlen)
+{
+	int af = (int)((struct sockaddr_in*)addr)->sin_family;
+	void* sinaddr = &((struct sockaddr_in*)addr)->sin_addr;
+	void* sin6addr = &((struct sockaddr_in6*)addr)->sin6_addr;
+	if(af == AF_INET && addrlen>=(socklen_t)sizeof(struct sockaddr_in)
+		&& memcmp(sinaddr, "\000\000\000\000", 4) == 0)
+		return 1;
+	else if(af==AF_INET6 && addrlen>=(socklen_t)sizeof(struct sockaddr_in6)
+		&& memcmp(sin6addr, "\000\000\000\000\000\000\000\000"
+		"\000\000\000\000\000\000\000\000", 16) == 0)
+		return 1;
+	return 0;
+}
+
+void sock_list_insert(struct sock_list** list, struct sockaddr_storage* addr,
+	socklen_t len, struct regional* region)
+{
+	struct sock_list* add = (struct sock_list*)regional_alloc(region,
+		sizeof(*add) - sizeof(add->addr) + (size_t)len);
+	if(!add) {
+		log_err("out of memory in socketlist insert");
+		return;
+	}
+	log_assert(list);
+	add->next = *list;
+	add->len = len;
+	*list = add;
+	if(len) memmove(&add->addr, addr, len);
+}
+
+void sock_list_prepend(struct sock_list** list, struct sock_list* add)
+{
+	struct sock_list* last = add;
+	if(!last) 
+		return;
+	while(last->next)
+		last = last->next;
+	last->next = *list;
+	*list = add;
+}
+
+int sock_list_find(struct sock_list* list, struct sockaddr_storage* addr,
+        socklen_t len)
+{
+	while(list) {
+		if(len == list->len) {
+			if(len == 0 || sockaddr_cmp_addr(addr, len, 
+				&list->addr, list->len) == 0)
+				return 1;
+		}
+		list = list->next;
+	}
+	return 0;
+}
+
+void sock_list_merge(struct sock_list** list, struct regional* region,
+	struct sock_list* add)
+{
+	struct sock_list* p;
+	for(p=add; p; p=p->next) {
+		if(!sock_list_find(*list, &p->addr, p->len))
+			sock_list_insert(list, &p->addr, p->len, region);
+	}
+}
+
+void
+log_crypto_err(const char* str)
+{
+	/* error:[error code]:[library name]:[function name]:[reason string] */
+	char buf[128];
+	unsigned long e;
+	ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
+	log_err("%s crypto %s", str, buf);
+	while( (e=ERR_get_error()) ) {
+		ERR_error_string_n(e, buf, sizeof(buf));
+		log_err("and additionally crypto %s", buf);
+	}
+}
+
+void* listen_sslctx_create(char* key, char* pem, char* verifypem)
+{
+	SSL_CTX* ctx = SSL_CTX_new(SSLv23_server_method());
+	if(!ctx) {
+		log_crypto_err("could not SSL_CTX_new");
+		return NULL;
+	}
+	/* no SSLv2 because has defects */
+	if(!(SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2) & SSL_OP_NO_SSLv2)){
+		log_crypto_err("could not set SSL_OP_NO_SSLv2");
+		SSL_CTX_free(ctx);
+		return NULL;
+	}
+	if(!SSL_CTX_use_certificate_file(ctx, pem, SSL_FILETYPE_PEM)) {
+		log_err("error for cert file: %s", pem);
+		log_crypto_err("error in SSL_CTX use_certificate_file");
+		SSL_CTX_free(ctx);
+		return NULL;
+	}
+	if(!SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM)) {
+		log_err("error for private key file: %s", key);
+		log_crypto_err("Error in SSL_CTX use_PrivateKey_file");
+		SSL_CTX_free(ctx);
+		return NULL;
+	}
+	if(!SSL_CTX_check_private_key(ctx)) {
+		log_err("error for key file: %s", key);
+		log_crypto_err("Error in SSL_CTX check_private_key");
+		SSL_CTX_free(ctx);
+		return NULL;
+	}
+
+	if(verifypem && verifypem[0]) {
+		if(!SSL_CTX_load_verify_locations(ctx, verifypem, NULL)) {
+			log_crypto_err("Error in SSL_CTX verify locations");
+			SSL_CTX_free(ctx);
+			return NULL;
+		}
+		SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(
+			verifypem));
+		SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
+	}
+	return ctx;
+}
+
+void* connect_sslctx_create(char* key, char* pem, char* verifypem)
+{
+	SSL_CTX* ctx = SSL_CTX_new(SSLv23_client_method());
+	if(!ctx) {
+		log_crypto_err("could not allocate SSL_CTX pointer");
+		return NULL;
+	}
+	if(!(SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2) & SSL_OP_NO_SSLv2)) {
+		log_crypto_err("could not set SSL_OP_NO_SSLv2");
+		SSL_CTX_free(ctx);
+		return NULL;
+	}
+	if(key && key[0]) {
+		if(!SSL_CTX_use_certificate_file(ctx, pem, SSL_FILETYPE_PEM)) {
+			log_err("error in client certificate %s", pem);
+			log_crypto_err("error in certificate file");
+			SSL_CTX_free(ctx);
+			return NULL;
+		}
+		if(!SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM)) {
+			log_err("error in client private key %s", key);
+			log_crypto_err("error in key file");
+			SSL_CTX_free(ctx);
+			return NULL;
+		}
+		if(!SSL_CTX_check_private_key(ctx)) {
+			log_err("error in client key %s", key);
+			log_crypto_err("error in SSL_CTX_check_private_key");
+			SSL_CTX_free(ctx);
+			return NULL;
+		}
+	}
+	if(verifypem && verifypem[0]) {
+		if(!SSL_CTX_load_verify_locations(ctx, verifypem, NULL) != 1) {
+			log_crypto_err("error in SSL_CTX verify");
+			SSL_CTX_free(ctx);
+			return NULL;
+		}
+		SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
+	}
+	return ctx;
+}
+
+void* incoming_ssl_fd(void* sslctx, int fd)
+{
+	SSL* ssl = SSL_new((SSL_CTX*)sslctx);
+	if(!ssl) {
+		log_crypto_err("could not SSL_new");
+		return NULL;
+	}
+	SSL_set_accept_state(ssl);
+	(void)SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
+	if(!SSL_set_fd(ssl, fd)) {
+		log_crypto_err("could not SSL_set_fd");
+		SSL_free(ssl);
+		return NULL;
+	}
+	return ssl;
+}
+
+void* outgoing_ssl_fd(void* sslctx, int fd)
+{
+	SSL* ssl = SSL_new((SSL_CTX*)sslctx);
+	if(!ssl) {
+		log_crypto_err("could not SSL_new");
+		return NULL;
+	}
+	SSL_set_connect_state(ssl);
+	(void)SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
+	if(!SSL_set_fd(ssl, fd)) {
+		log_crypto_err("could not SSL_set_fd");
+		SSL_free(ssl);
+		return NULL;
+	}
+	return ssl;
+}
diff --git a/3rdParty/Unbound/src/src/util/net_help.h b/3rdParty/Unbound/src/src/util/net_help.h
new file mode 100644
index 0000000..a45270c
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/net_help.h
@@ -0,0 +1,366 @@
+/*
+ * util/net_help.h - network help functions 
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to perform network related tasks.
+ */
+
+#ifndef NET_HELP_H
+#define NET_HELP_H
+#include "util/log.h"
+struct sock_list;
+struct regional;
+
+/** DNS constants for uint16_t style flag manipulation. host byteorder. 
+ *                                1  1  1  1  1  1
+ *  0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * |QR|   Opcode  |AA|TC|RD|RA| Z|AD|CD|   RCODE   |
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ */ 
+/** CD flag */
+#define BIT_CD 0x0010
+/** AD flag */
+#define BIT_AD 0x0020
+/** Z flag */
+#define BIT_Z  0x0040
+/** RA flag */
+#define BIT_RA 0x0080
+/** RD flag */
+#define BIT_RD 0x0100
+/** TC flag */
+#define BIT_TC 0x0200
+/** AA flag */
+#define BIT_AA 0x0400
+/** QR flag */
+#define BIT_QR 0x8000
+/** get RCODE bits from uint16 flags */
+#define FLAGS_GET_RCODE(f) ((f) & 0xf)
+/** set RCODE bits in uint16 flags */
+#define FLAGS_SET_RCODE(f, r) (f = (((f) & 0xfff0) | (r)))
+
+/** timeout in seconds for UDP queries to auth servers. */
+#define UDP_AUTH_QUERY_TIMEOUT 4
+/** timeout in seconds for TCP queries to auth servers. */
+#define TCP_AUTH_QUERY_TIMEOUT 30
+/** Advertised version of EDNS capabilities */
+#define EDNS_ADVERTISED_VERSION         0
+/** Advertised size of EDNS capabilities */
+extern uint16_t EDNS_ADVERTISED_SIZE;
+/** bits for EDNS bitfield */
+#define EDNS_DO 0x8000 /* Dnssec Ok */
+/** byte size of ip4 address */
+#define INET_SIZE 4
+/** byte size of ip6 address */
+#define INET6_SIZE 16
+
+/** DNSKEY zone sign key flag */
+#define DNSKEY_BIT_ZSK 0x0100
+/** DNSKEY secure entry point, KSK flag */
+#define DNSKEY_BIT_SEP 0x0001
+
+/**
+ * See if string is ip4 or ip6.
+ * @param str: IP specification.
+ * @return: true if string addr is an ip6 specced address.
+ */
+int str_is_ip6(const char* str);
+
+/**
+ * Set fd nonblocking.
+ * @param s: file descriptor.
+ * @return: 0 on error (error is printed to log).
+ */
+int fd_set_nonblock(int s); 
+
+/**
+ * Set fd (back to) blocking.
+ * @param s: file descriptor.
+ * @return: 0 on error (error is printed to log).
+ */
+int fd_set_block(int s); 
+
+/**
+ * See if number is a power of 2.
+ * @param num: the value.
+ * @return: true if the number is a power of 2.
+ */
+int is_pow2(size_t num);
+
+/**
+ * Allocate memory and copy over contents.
+ * @param data: what to copy over.
+ * @param len: length of data.
+ * @return: NULL on malloc failure, or newly malloced data.
+ */
+void* memdup(void* data, size_t len);
+
+/**
+ * Prints the sockaddr in readable format with log_info. Debug helper.
+ * @param v: at what verbosity level to print this.
+ * @param str: descriptive string printed with it.
+ * @param addr: the sockaddr to print. Can be ip4 or ip6.
+ * @param addrlen: length of addr.
+ */
+void log_addr(enum verbosity_value v, const char* str, 
+	struct sockaddr_storage* addr, socklen_t addrlen);
+
+/**
+ * Prints zone name and sockaddr in readable format with log_info. Debug.
+ * @param v: at what verbosity level to print this.
+ * @param str: descriptive string printed with it.
+ * @param zone: DNS domain name, uncompressed wireformat.
+ * @param addr: the sockaddr to print. Can be ip4 or ip6.
+ * @param addrlen: length of addr.
+ */
+void log_name_addr(enum verbosity_value v, const char* str, uint8_t* zone, 
+	struct sockaddr_storage* addr, socklen_t addrlen);
+
+/**
+ * Convert address string, with "@port" appendix, to sockaddr.
+ * Uses DNS port by default.
+ * @param str: the string
+ * @param addr: where to store sockaddr.
+ * @param addrlen: length of stored sockaddr is returned.
+ * @return 0 on error.
+ */
+int extstrtoaddr(const char* str, struct sockaddr_storage* addr, 
+	socklen_t* addrlen);
+
+/**
+ * Convert ip address string and port to sockaddr.
+ * @param ip: ip4 or ip6 address string.
+ * @param port: port number, host format.
+ * @param addr: where to store sockaddr.
+ * @param addrlen: length of stored sockaddr is returned.
+ * @return 0 on error.
+ */
+int ipstrtoaddr(const char* ip, int port, struct sockaddr_storage* addr,
+	socklen_t* addrlen);
+
+/**
+ * Convert ip netblock (ip/netsize) string and port to sockaddr.
+ * *SLOW*, does a malloc internally to avoid writing over 'ip' string.
+ * @param ip: ip4 or ip6 address string.
+ * @param port: port number, host format.
+ * @param addr: where to store sockaddr.
+ * @param addrlen: length of stored sockaddr is returned.
+ * @param net: netblock size is returned.
+ * @return 0 on error.
+ */
+int netblockstrtoaddr(const char* ip, int port, struct sockaddr_storage* addr,
+	socklen_t* addrlen, int* net);
+
+/**
+ * Print string with neat domain name, type and class.
+ * @param v: at what verbosity level to print this.
+ * @param str: string of message.
+ * @param name: domain name uncompressed wireformat.
+ * @param type: host format RR type.
+ * @param dclass: host format RR class.
+ */
+void log_nametypeclass(enum verbosity_value v, const char* str, 
+	uint8_t* name, uint16_t type, uint16_t dclass);
+
+/**
+ * Compare two sockaddrs. Imposes an ordering on the addresses.
+ * Compares address and port.
+ * @param addr1: address 1.
+ * @param len1: lengths of addr1.
+ * @param addr2: address 2.
+ * @param len2: lengths of addr2.
+ * @return: 0 if addr1 == addr2. -1 if addr1 is smaller, +1 if larger.
+ */
+int sockaddr_cmp(struct sockaddr_storage* addr1, socklen_t len1, 
+	struct sockaddr_storage* addr2, socklen_t len2);
+
+/**
+ * Compare two sockaddrs. Compares address, not the port.
+ * @param addr1: address 1.
+ * @param len1: lengths of addr1.
+ * @param addr2: address 2.
+ * @param len2: lengths of addr2.
+ * @return: 0 if addr1 == addr2. -1 if addr1 is smaller, +1 if larger.
+ */
+int sockaddr_cmp_addr(struct sockaddr_storage* addr1, socklen_t len1, 
+	struct sockaddr_storage* addr2, socklen_t len2);
+
+/**
+ * Checkout address family.
+ * @param addr: the sockaddr to examine.
+ * @param len: the length of addr.
+ * @return: true if sockaddr is ip6.
+ */
+int addr_is_ip6(struct sockaddr_storage* addr, socklen_t len);
+
+/**
+ * Make sure the sockaddr ends in zeroes. For tree insertion and subsequent
+ * comparison.
+ * @param addr: the ip4 or ip6 addr.
+ * @param len: length of addr.
+ * @param net: number of bits to leave untouched, the rest of the netblock
+ * 	address is zeroed.
+ */
+void addr_mask(struct sockaddr_storage* addr, socklen_t len, int net);
+
+/**
+ * See how many bits are shared, equal, between two addrs.
+ * @param addr1: first addr.
+ * @param net1: netblock size of first addr.
+ * @param addr2: second addr.
+ * @param net2: netblock size of second addr.
+ * @param addrlen: length of first addr and of second addr.
+ * 	They must be of the same length (i.e. same type IP4, IP6).
+ * @return: number of bits the same.
+ */
+int addr_in_common(struct sockaddr_storage* addr1, int net1,
+	struct sockaddr_storage* addr2, int net2, socklen_t addrlen);
+
+/**
+ * Put address into string, works for IPv4 and IPv6.
+ * @param addr: address
+ * @param addrlen: length of address
+ * @param buf: result string stored here
+ * @param len: length of buf.
+ * On failure a string with "error" is stored inside.
+ */
+void addr_to_str(struct sockaddr_storage* addr, socklen_t addrlen,
+	char* buf, size_t len);
+
+/**
+ * See if sockaddr is an ipv6 mapped ipv4 address, "::ffff:0.0.0.0"
+ * @param addr: address
+ * @param addrlen: length of address
+ * @return true if so
+ */
+int addr_is_ip4mapped(struct sockaddr_storage* addr, socklen_t addrlen);
+
+/**
+ * See if sockaddr is 255.255.255.255.
+ * @param addr: address
+ * @param addrlen: length of address
+ * @return true if so
+ */
+int addr_is_broadcast(struct sockaddr_storage* addr, socklen_t addrlen);
+
+/**
+ * See if sockaddr is 0.0.0.0 or ::0.
+ * @param addr: address
+ * @param addrlen: length of address
+ * @return true if so
+ */
+int addr_is_any(struct sockaddr_storage* addr, socklen_t addrlen);
+
+/**
+ * Insert new socket list item. If fails logs error.
+ * @param list: pointer to pointer to first item.
+ * @param addr: address or NULL if 'cache'.
+ * @param len: length of addr, or 0 if 'cache'.
+ * @param region: where to allocate
+ */
+void sock_list_insert(struct sock_list** list, struct sockaddr_storage* addr,
+	socklen_t len, struct regional* region);
+
+/**
+ * Append one list to another.  Must both be from same qstate(regional).
+ * @param list: pointer to result list that is modified.
+ * @param add: item(s) to add.  They are prepended to list.
+ */
+void sock_list_prepend(struct sock_list** list, struct sock_list* add);
+
+/**
+ * Find addr in list.
+ * @param list: to search in
+ * @param addr: address to look for.
+ * @param len: length. Can be 0, look for 'cache entry'.
+ * @return true if found.
+ */
+int sock_list_find(struct sock_list* list, struct sockaddr_storage* addr,
+        socklen_t len);
+
+/**
+ * Merge socklist into another socket list.  Allocates the new entries
+ * freshly and copies them over, so also performs a region switchover.
+ * Allocation failures are logged.
+ * @param list: the destination list (checked for duplicates)
+ * @param region: where to allocate
+ * @param add: the list of entries to add.
+ */
+void sock_list_merge(struct sock_list** list, struct regional* region,
+	struct sock_list* add);
+
+/**
+ * Log libcrypto error with descriptive string. Calls log_err().
+ * @param str: what failed.
+ */
+void log_crypto_err(const char* str);
+
+/** 
+ * create SSL listen context
+ * @param key: private key file.
+ * @param pem: public key cert.
+ * @param verifypem: if nonNULL, verifylocation file.
+ * return SSL_CTX* or NULL on failure (logged).
+ */
+void* listen_sslctx_create(char* key, char* pem, char* verifypem);
+
+/**
+ * create SSL connect context
+ * @param key: if nonNULL (also pem nonNULL), the client private key.
+ * @param pem: client public key (or NULL if key is NULL).
+ * @param verifypem: if nonNULL used for verifylocation file.
+ * @return SSL_CTX* or NULL on failure (logged).
+ */
+void* connect_sslctx_create(char* key, char* pem, char* verifypem);
+
+/**
+ * accept a new fd and wrap it in a BIO in SSL
+ * @param sslctx: the SSL_CTX to use (from listen_sslctx_create()).
+ * @param fd: from accept, nonblocking.
+ * @return SSL or NULL on alloc failure.
+ */
+void* incoming_ssl_fd(void* sslctx, int fd);
+
+/**
+ * connect a new fd and wrap it in a BIO in SSL
+ * @param sslctx: the SSL_CTX to use (from connect_sslctx_create())
+ * @param fd: from connect.
+ * @return SSL or NULL on alloc failure
+ */
+void* outgoing_ssl_fd(void* sslctx, int fd);
+
+#endif /* NET_HELP_H */
diff --git a/3rdParty/Unbound/src/src/util/netevent.c b/3rdParty/Unbound/src/src/util/netevent.c
new file mode 100644
index 0000000..c6f5279
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/netevent.c
@@ -0,0 +1,2065 @@
+/*
+ * util/netevent.c - event notification
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains event notification functions.
+ */
+#include "config.h"
+#include <ldns/wire2host.h>
+#include "util/netevent.h"
+#include "util/log.h"
+#include "util/net_help.h"
+#include "util/fptr_wlist.h"
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+/* -------- Start of local definitions -------- */
+/** if CMSG_ALIGN is not defined on this platform, a workaround */
+#ifndef CMSG_ALIGN
+#  ifdef _CMSG_DATA_ALIGN
+#    define CMSG_ALIGN _CMSG_DATA_ALIGN
+#  else
+#    define CMSG_ALIGN(len) (((len)+sizeof(long)-1) & ~(sizeof(long)-1))
+#  endif
+#endif
+
+/** if CMSG_LEN is not defined on this platform, a workaround */
+#ifndef CMSG_LEN
+#  define CMSG_LEN(len) (CMSG_ALIGN(sizeof(struct cmsghdr))+(len))
+#endif
+
+/** if CMSG_SPACE is not defined on this platform, a workaround */
+#ifndef CMSG_SPACE
+#  ifdef _CMSG_HDR_ALIGN
+#    define CMSG_SPACE(l) (CMSG_ALIGN(l)+_CMSG_HDR_ALIGN(sizeof(struct cmsghdr)))
+#  else
+#    define CMSG_SPACE(l) (CMSG_ALIGN(l)+CMSG_ALIGN(sizeof(struct cmsghdr)))
+#  endif
+#endif
+
+/** The TCP reading or writing query timeout in seconds */
+#define TCP_QUERY_TIMEOUT 120 
+
+#ifndef NONBLOCKING_IS_BROKEN
+/** number of UDP reads to perform per read indication from select */
+#define NUM_UDP_PER_SELECT 100
+#else
+#define NUM_UDP_PER_SELECT 1
+#endif
+
+/* We define libevent structures here to hide the libevent stuff. */
+
+#ifdef USE_MINI_EVENT
+#  ifdef USE_WINSOCK
+#    include "util/winsock_event.h"
+#  else
+#    include "util/mini_event.h"
+#  endif /* USE_WINSOCK */
+#else /* USE_MINI_EVENT */
+   /* we use libevent */
+#  include <event.h>
+#endif /* USE_MINI_EVENT */
+
+/**
+ * The internal event structure for keeping libevent info for the event.
+ * Possibly other structures (list, tree) this is part of.
+ */
+struct internal_event {
+	/** the comm base */
+	struct comm_base* base;
+	/** libevent event type, alloced here */
+	struct event ev;
+};
+
+/**
+ * Internal base structure, so that every thread has its own events.
+ */
+struct internal_base {
+	/** libevent event_base type. */
+	struct event_base* base;
+	/** seconds time pointer points here */
+	uint32_t secs;
+	/** timeval with current time */
+	struct timeval now;
+};
+
+/**
+ * Internal timer structure, to store timer event in.
+ */
+struct internal_timer {
+	/** the comm base */
+	struct comm_base* base;
+	/** libevent event type, alloced here */
+	struct event ev;
+	/** is timer enabled */
+	uint8_t enabled;
+};
+
+/**
+ * Internal signal structure, to store signal event in.
+ */
+struct internal_signal {
+	/** libevent event type, alloced here */
+	struct event ev;
+	/** next in signal list */
+	struct internal_signal* next;
+};
+
+/** create a tcp handler with a parent */
+static struct comm_point* comm_point_create_tcp_handler(
+	struct comm_base *base, struct comm_point* parent, size_t bufsize,
+        comm_point_callback_t* callback, void* callback_arg);
+
+/* -------- End of local definitions -------- */
+
+#ifdef USE_MINI_EVENT
+/** minievent updates the time when it blocks. */
+#define comm_base_now(x) /* nothing to do */
+#else /* !USE_MINI_EVENT */
+/** fillup the time values in the event base */
+static void
+comm_base_now(struct comm_base* b)
+{
+	if(gettimeofday(&b->eb->now, NULL) < 0) {
+		log_err("gettimeofday: %s", strerror(errno));
+	}
+	b->eb->secs = (uint32_t)b->eb->now.tv_sec;
+}
+#endif /* USE_MINI_EVENT */
+
+struct comm_base* 
+comm_base_create(int sigs)
+{
+	struct comm_base* b = (struct comm_base*)calloc(1,
+		sizeof(struct comm_base));
+	if(!b)
+		return NULL;
+	b->eb = (struct internal_base*)calloc(1, sizeof(struct internal_base));
+	if(!b->eb) {
+		free(b);
+		return NULL;
+	}
+#ifdef USE_MINI_EVENT
+	(void)sigs;
+	/* use mini event time-sharing feature */
+	b->eb->base = event_init(&b->eb->secs, &b->eb->now);
+#else
+#  if defined(HAVE_EV_LOOP) || defined(HAVE_EV_DEFAULT_LOOP)
+	/* libev */
+	if(sigs)
+		b->eb->base=(struct event_base *)ev_default_loop(EVFLAG_AUTO);
+	else
+		b->eb->base=(struct event_base *)ev_loop_new(EVFLAG_AUTO);
+#  else
+	(void)sigs;
+#    ifdef HAVE_EVENT_BASE_NEW
+	b->eb->base = event_base_new();
+#    else
+	b->eb->base = event_init();
+#    endif
+#  endif
+#endif
+	if(!b->eb->base) {
+		free(b->eb);
+		free(b);
+		return NULL;
+	}
+	comm_base_now(b);
+	/* avoid event_get_method call which causes crashes even when
+	 * not printing, because its result is passed */
+	verbose(VERB_ALGO, 
+#if defined(HAVE_EV_LOOP) || defined(HAVE_EV_DEFAULT_LOOP)
+		"libev"
+#elif defined(USE_MINI_EVENT)
+		"event "
+#else
+		"libevent "
+#endif
+		"%s uses %s method.", 
+		event_get_version(), 
+#ifdef HAVE_EVENT_BASE_GET_METHOD
+		event_base_get_method(b->eb->base)
+#else
+		"not_obtainable"
+#endif
+	);
+	return b;
+}
+
+void 
+comm_base_delete(struct comm_base* b)
+{
+	if(!b)
+		return;
+#ifdef USE_MINI_EVENT
+	event_base_free(b->eb->base);
+#elif defined(HAVE_EVENT_BASE_FREE) && defined(HAVE_EVENT_BASE_ONCE)
+	/* only libevent 1.2+ has it, but in 1.2 it is broken - 
+	   assertion fails on signal handling ev that is not deleted
+ 	   in libevent 1.3c (event_base_once appears) this is fixed. */
+	event_base_free(b->eb->base);
+#endif /* HAVE_EVENT_BASE_FREE and HAVE_EVENT_BASE_ONCE */
+	b->eb->base = NULL;
+	free(b->eb);
+	free(b);
+}
+
+void 
+comm_base_timept(struct comm_base* b, uint32_t** tt, struct timeval** tv)
+{
+	*tt = &b->eb->secs;
+	*tv = &b->eb->now;
+}
+
+void 
+comm_base_dispatch(struct comm_base* b)
+{
+	int retval;
+	retval = event_base_dispatch(b->eb->base);
+	if(retval != 0) {
+		fatal_exit("event_dispatch returned error %d, "
+			"errno is %s", retval, strerror(errno));
+	}
+}
+
+void comm_base_exit(struct comm_base* b)
+{
+	if(event_base_loopexit(b->eb->base, NULL) != 0) {
+		log_err("Could not loopexit");
+	}
+}
+
+struct event_base* comm_base_internal(struct comm_base* b)
+{
+	return b->eb->base;
+}
+
+/** see if errno for udp has to be logged or not uses globals */
+static int
+udp_send_errno_needs_log(struct sockaddr* addr, socklen_t addrlen)
+{
+	/* do not log transient errors (unless high verbosity) */
+#if defined(ENETUNREACH) || defined(EHOSTDOWN) || defined(EHOSTUNREACH) || defined(ENETDOWN)
+	switch(errno) {
+#  ifdef ENETUNREACH
+		case ENETUNREACH:
+#  endif
+#  ifdef EHOSTDOWN
+		case EHOSTDOWN:
+#  endif
+#  ifdef EHOSTUNREACH
+		case EHOSTUNREACH:
+#  endif
+#  ifdef ENETDOWN
+		case ENETDOWN:
+#  endif
+			if(verbosity < VERB_ALGO)
+				return 0;
+		default:
+			break;
+	}
+#endif
+	/* squelch errors where people deploy AAAA ::ffff:bla for
+	 * authority servers, which we try for intranets. */
+	if(errno == EINVAL && addr_is_ip4mapped(
+		(struct sockaddr_storage*)addr, addrlen) &&
+		verbosity < VERB_DETAIL)
+		return 0;
+	/* SO_BROADCAST sockopt can give access to 255.255.255.255,
+	 * but a dns cache does not need it. */
+	if(errno == EACCES && addr_is_broadcast(
+		(struct sockaddr_storage*)addr, addrlen) &&
+		verbosity < VERB_DETAIL)
+		return 0;
+	return 1;
+}
+
+/* send a UDP reply */
+int
+comm_point_send_udp_msg(struct comm_point *c, ldns_buffer* packet,
+	struct sockaddr* addr, socklen_t addrlen) 
+{
+	ssize_t sent;
+	log_assert(c->fd != -1);
+#ifdef UNBOUND_DEBUG
+	if(ldns_buffer_remaining(packet) == 0)
+		log_err("error: send empty UDP packet");
+#endif
+	log_assert(addr && addrlen > 0);
+	sent = sendto(c->fd, (void*)ldns_buffer_begin(packet), 
+		ldns_buffer_remaining(packet), 0,
+		addr, addrlen);
+	if(sent == -1) {
+		if(!udp_send_errno_needs_log(addr, addrlen))
+			return 0;
+#ifndef USE_WINSOCK
+		verbose(VERB_OPS, "sendto failed: %s", strerror(errno));
+#else
+		verbose(VERB_OPS, "sendto failed: %s", 
+			wsa_strerror(WSAGetLastError()));
+#endif
+		log_addr(VERB_OPS, "remote address is", 
+			(struct sockaddr_storage*)addr, addrlen);
+		return 0;
+	} else if((size_t)sent != ldns_buffer_remaining(packet)) {
+		log_err("sent %d in place of %d bytes", 
+			(int)sent, (int)ldns_buffer_remaining(packet));
+		return 0;
+	}
+	return 1;
+}
+
+#if defined(AF_INET6) && defined(IPV6_PKTINFO) && (defined(HAVE_RECVMSG) || defined(HAVE_SENDMSG))
+/** print debug ancillary info */
+static void p_ancil(const char* str, struct comm_reply* r)
+{
+	if(r->srctype != 4 && r->srctype != 6) {
+		log_info("%s: unknown srctype %d", str, r->srctype);
+		return;
+	}
+	if(r->srctype == 6) {
+		char buf[1024];
+		if(inet_ntop(AF_INET6, &r->pktinfo.v6info.ipi6_addr, 
+			buf, (socklen_t)sizeof(buf)) == 0) {
+			strncpy(buf, "(inet_ntop error)", sizeof(buf));
+		}
+		buf[sizeof(buf)-1]=0;
+		log_info("%s: %s %d", str, buf, r->pktinfo.v6info.ipi6_ifindex);
+	} else if(r->srctype == 4) {
+#ifdef IP_PKTINFO
+		char buf1[1024], buf2[1024];
+		if(inet_ntop(AF_INET, &r->pktinfo.v4info.ipi_addr, 
+			buf1, (socklen_t)sizeof(buf1)) == 0) {
+			strncpy(buf1, "(inet_ntop error)", sizeof(buf1));
+		}
+		buf1[sizeof(buf1)-1]=0;
+		if(inet_ntop(AF_INET, &r->pktinfo.v4info.ipi_spec_dst, 
+			buf2, (socklen_t)sizeof(buf2)) == 0) {
+			strncpy(buf2, "(inet_ntop error)", sizeof(buf2));
+		}
+		buf2[sizeof(buf2)-1]=0;
+		log_info("%s: %d %s %s", str, r->pktinfo.v4info.ipi_ifindex,
+			buf1, buf2);
+#elif defined(IP_RECVDSTADDR)
+		char buf1[1024];
+		if(inet_ntop(AF_INET, &r->pktinfo.v4addr, 
+			buf1, (socklen_t)sizeof(buf1)) == 0) {
+			strncpy(buf1, "(inet_ntop error)", sizeof(buf1));
+		}
+		buf1[sizeof(buf1)-1]=0;
+		log_info("%s: %s", str, buf1);
+#endif /* IP_PKTINFO or PI_RECVDSTDADDR */
+	}
+}
+#endif /* AF_INET6 && IPV6_PKTINFO && HAVE_RECVMSG||HAVE_SENDMSG */
+
+/** send a UDP reply over specified interface*/
+static int
+comm_point_send_udp_msg_if(struct comm_point *c, ldns_buffer* packet,
+	struct sockaddr* addr, socklen_t addrlen, struct comm_reply* r) 
+{
+#if defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(HAVE_SENDMSG)
+	ssize_t sent;
+	struct msghdr msg;
+	struct iovec iov[1];
+	char control[256];
+#ifndef S_SPLINT_S
+	struct cmsghdr *cmsg;
+#endif /* S_SPLINT_S */
+
+	log_assert(c->fd != -1);
+#ifdef UNBOUND_DEBUG
+	if(ldns_buffer_remaining(packet) == 0)
+		log_err("error: send empty UDP packet");
+#endif
+	log_assert(addr && addrlen > 0);
+
+	msg.msg_name = addr;
+	msg.msg_namelen = addrlen;
+	iov[0].iov_base = ldns_buffer_begin(packet);
+	iov[0].iov_len = ldns_buffer_remaining(packet);
+	msg.msg_iov = iov;
+	msg.msg_iovlen = 1;
+	msg.msg_control = control;
+#ifndef S_SPLINT_S
+	msg.msg_controllen = sizeof(control);
+#endif /* S_SPLINT_S */
+	msg.msg_flags = 0;
+
+#ifndef S_SPLINT_S
+	cmsg = CMSG_FIRSTHDR(&msg);
+	if(r->srctype == 4) {
+#ifdef IP_PKTINFO
+		msg.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
+		log_assert(msg.msg_controllen <= sizeof(control));
+		cmsg->cmsg_level = IPPROTO_IP;
+		cmsg->cmsg_type = IP_PKTINFO;
+		memmove(CMSG_DATA(cmsg), &r->pktinfo.v4info,
+			sizeof(struct in_pktinfo));
+		cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+#elif defined(IP_SENDSRCADDR)
+		msg.msg_controllen = CMSG_SPACE(sizeof(struct in_addr));
+		log_assert(msg.msg_controllen <= sizeof(control));
+		cmsg->cmsg_level = IPPROTO_IP;
+		cmsg->cmsg_type = IP_SENDSRCADDR;
+		memmove(CMSG_DATA(cmsg), &r->pktinfo.v4addr,
+			sizeof(struct in_addr));
+		cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
+#else
+		verbose(VERB_ALGO, "no IP_PKTINFO or IP_SENDSRCADDR");
+		msg.msg_control = NULL;
+#endif /* IP_PKTINFO or IP_SENDSRCADDR */
+	} else if(r->srctype == 6) {
+		msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
+		log_assert(msg.msg_controllen <= sizeof(control));
+		cmsg->cmsg_level = IPPROTO_IPV6;
+		cmsg->cmsg_type = IPV6_PKTINFO;
+		memmove(CMSG_DATA(cmsg), &r->pktinfo.v6info,
+			sizeof(struct in6_pktinfo));
+		cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+	} else {
+		/* try to pass all 0 to use default route */
+		msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
+		log_assert(msg.msg_controllen <= sizeof(control));
+		cmsg->cmsg_level = IPPROTO_IPV6;
+		cmsg->cmsg_type = IPV6_PKTINFO;
+		memset(CMSG_DATA(cmsg), 0, sizeof(struct in6_pktinfo));
+		cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+	}
+#endif /* S_SPLINT_S */
+	if(verbosity >= VERB_ALGO)
+		p_ancil("send_udp over interface", r);
+	sent = sendmsg(c->fd, &msg, 0);
+	if(sent == -1) {
+		if(!udp_send_errno_needs_log(addr, addrlen))
+			return 0;
+		verbose(VERB_OPS, "sendmsg failed: %s", strerror(errno));
+		log_addr(VERB_OPS, "remote address is", 
+			(struct sockaddr_storage*)addr, addrlen);
+		return 0;
+	} else if((size_t)sent != ldns_buffer_remaining(packet)) {
+		log_err("sent %d in place of %d bytes", 
+			(int)sent, (int)ldns_buffer_remaining(packet));
+		return 0;
+	}
+	return 1;
+#else
+	(void)c;
+	(void)packet;
+	(void)addr;
+	(void)addrlen;
+	(void)r;
+	log_err("sendmsg: IPV6_PKTINFO not supported");
+	return 0;
+#endif /* AF_INET6 && IPV6_PKTINFO && HAVE_SENDMSG */
+}
+
+void 
+comm_point_udp_ancil_callback(int fd, short event, void* arg)
+{
+#if defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(HAVE_RECVMSG)
+	struct comm_reply rep;
+	struct msghdr msg;
+	struct iovec iov[1];
+	ssize_t rcv;
+	char ancil[256];
+	int i;
+#ifndef S_SPLINT_S
+	struct cmsghdr* cmsg;
+#endif /* S_SPLINT_S */
+
+	rep.c = (struct comm_point*)arg;
+	log_assert(rep.c->type == comm_udp);
+
+	if(!(event&EV_READ))
+		return;
+	log_assert(rep.c && rep.c->buffer && rep.c->fd == fd);
+	comm_base_now(rep.c->ev->base);
+	for(i=0; i<NUM_UDP_PER_SELECT; i++) {
+		ldns_buffer_clear(rep.c->buffer);
+		rep.addrlen = (socklen_t)sizeof(rep.addr);
+		log_assert(fd != -1);
+		log_assert(ldns_buffer_remaining(rep.c->buffer) > 0);
+		msg.msg_name = &rep.addr;
+		msg.msg_namelen = (socklen_t)sizeof(rep.addr);
+		iov[0].iov_base = ldns_buffer_begin(rep.c->buffer);
+		iov[0].iov_len = ldns_buffer_remaining(rep.c->buffer);
+		msg.msg_iov = iov;
+		msg.msg_iovlen = 1;
+		msg.msg_control = ancil;
+#ifndef S_SPLINT_S
+		msg.msg_controllen = sizeof(ancil);
+#endif /* S_SPLINT_S */
+		msg.msg_flags = 0;
+		rcv = recvmsg(fd, &msg, 0);
+		if(rcv == -1) {
+			if(errno != EAGAIN && errno != EINTR) {
+				log_err("recvmsg failed: %s", strerror(errno));
+			}
+			return;
+		}
+		rep.addrlen = msg.msg_namelen;
+		ldns_buffer_skip(rep.c->buffer, rcv);
+		ldns_buffer_flip(rep.c->buffer);
+		rep.srctype = 0;
+#ifndef S_SPLINT_S
+		for(cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+			cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+			if( cmsg->cmsg_level == IPPROTO_IPV6 &&
+				cmsg->cmsg_type == IPV6_PKTINFO) {
+				rep.srctype = 6;
+				memmove(&rep.pktinfo.v6info, CMSG_DATA(cmsg),
+					sizeof(struct in6_pktinfo));
+				break;
+#ifdef IP_PKTINFO
+			} else if( cmsg->cmsg_level == IPPROTO_IP &&
+				cmsg->cmsg_type == IP_PKTINFO) {
+				rep.srctype = 4;
+				memmove(&rep.pktinfo.v4info, CMSG_DATA(cmsg),
+					sizeof(struct in_pktinfo));
+				break;
+#elif defined(IP_RECVDSTADDR)
+			} else if( cmsg->cmsg_level == IPPROTO_IP &&
+				cmsg->cmsg_type == IP_RECVDSTADDR) {
+				rep.srctype = 4;
+				memmove(&rep.pktinfo.v4addr, CMSG_DATA(cmsg),
+					sizeof(struct in_addr));
+				break;
+#endif /* IP_PKTINFO or IP_RECVDSTADDR */
+			}
+		}
+		if(verbosity >= VERB_ALGO)
+			p_ancil("receive_udp on interface", &rep);
+#endif /* S_SPLINT_S */
+		fptr_ok(fptr_whitelist_comm_point(rep.c->callback));
+		if((*rep.c->callback)(rep.c, rep.c->cb_arg, NETEVENT_NOERROR, &rep)) {
+			/* send back immediate reply */
+			(void)comm_point_send_udp_msg_if(rep.c, rep.c->buffer,
+				(struct sockaddr*)&rep.addr, rep.addrlen, &rep);
+		}
+		if(rep.c->fd == -1) /* commpoint closed */
+			break;
+	}
+#else
+	(void)fd;
+	(void)event;
+	(void)arg;
+	fatal_exit("recvmsg: No support for IPV6_PKTINFO. "
+		"Please disable interface-automatic");
+#endif /* AF_INET6 && IPV6_PKTINFO && HAVE_RECVMSG */
+}
+
+void 
+comm_point_udp_callback(int fd, short event, void* arg)
+{
+	struct comm_reply rep;
+	ssize_t rcv;
+	int i;
+
+	rep.c = (struct comm_point*)arg;
+	log_assert(rep.c->type == comm_udp);
+
+	if(!(event&EV_READ))
+		return;
+	log_assert(rep.c && rep.c->buffer && rep.c->fd == fd);
+	comm_base_now(rep.c->ev->base);
+	for(i=0; i<NUM_UDP_PER_SELECT; i++) {
+		ldns_buffer_clear(rep.c->buffer);
+		rep.addrlen = (socklen_t)sizeof(rep.addr);
+		log_assert(fd != -1);
+		log_assert(ldns_buffer_remaining(rep.c->buffer) > 0);
+		rcv = recvfrom(fd, (void*)ldns_buffer_begin(rep.c->buffer), 
+			ldns_buffer_remaining(rep.c->buffer), 0, 
+			(struct sockaddr*)&rep.addr, &rep.addrlen);
+		if(rcv == -1) {
+#ifndef USE_WINSOCK
+			if(errno != EAGAIN && errno != EINTR)
+				log_err("recvfrom %d failed: %s", 
+					fd, strerror(errno));
+#else
+			if(WSAGetLastError() != WSAEINPROGRESS &&
+				WSAGetLastError() != WSAECONNRESET &&
+				WSAGetLastError()!= WSAEWOULDBLOCK)
+				log_err("recvfrom failed: %s",
+					wsa_strerror(WSAGetLastError()));
+#endif
+			return;
+		}
+		ldns_buffer_skip(rep.c->buffer, rcv);
+		ldns_buffer_flip(rep.c->buffer);
+		rep.srctype = 0;
+		fptr_ok(fptr_whitelist_comm_point(rep.c->callback));
+		if((*rep.c->callback)(rep.c, rep.c->cb_arg, NETEVENT_NOERROR, &rep)) {
+			/* send back immediate reply */
+			(void)comm_point_send_udp_msg(rep.c, rep.c->buffer,
+				(struct sockaddr*)&rep.addr, rep.addrlen);
+		}
+		if(rep.c->fd != fd) /* commpoint closed to -1 or reused for
+		another UDP port. Note rep.c cannot be reused with TCP fd. */
+			break;
+	}
+}
+
+/** Use a new tcp handler for new query fd, set to read query */
+static void
+setup_tcp_handler(struct comm_point* c, int fd) 
+{
+	log_assert(c->type == comm_tcp);
+	log_assert(c->fd == -1);
+	ldns_buffer_clear(c->buffer);
+	c->tcp_is_reading = 1;
+	c->tcp_byte_count = 0;
+	comm_point_start_listening(c, fd, TCP_QUERY_TIMEOUT);
+}
+
+int comm_point_perform_accept(struct comm_point* c,
+	struct sockaddr_storage* addr, socklen_t* addrlen)
+{
+	int new_fd;
+	*addrlen = (socklen_t)sizeof(*addr);
+	new_fd = accept(c->fd, (struct sockaddr*)addr, addrlen);
+	if(new_fd == -1) {
+#ifndef USE_WINSOCK
+		/* EINTR is signal interrupt. others are closed connection. */
+		if(	errno == EINTR || errno == EAGAIN
+#ifdef EWOULDBLOCK
+			|| errno == EWOULDBLOCK 
+#endif
+#ifdef ECONNABORTED
+			|| errno == ECONNABORTED 
+#endif
+#ifdef EPROTO
+			|| errno == EPROTO
+#endif /* EPROTO */
+			)
+			return -1;
+		log_err("accept failed: %s", strerror(errno));
+#else /* USE_WINSOCK */
+		if(WSAGetLastError() == WSAEINPROGRESS ||
+			WSAGetLastError() == WSAECONNRESET)
+			return -1;
+		if(WSAGetLastError() == WSAEWOULDBLOCK) {
+			winsock_tcp_wouldblock(&c->ev->ev, EV_READ);
+			return -1;
+		}
+		log_err("accept failed: %s", wsa_strerror(WSAGetLastError()));
+#endif
+		log_addr(0, "remote address is", addr, *addrlen);
+		return -1;
+	}
+	fd_set_nonblock(new_fd);
+	return new_fd;
+}
+
+#ifdef USE_WINSOCK
+static long win_bio_cb(BIO *b, int oper, const char* ATTR_UNUSED(argp),
+        int ATTR_UNUSED(argi), long argl, long retvalue)
+{
+	verbose(VERB_ALGO, "bio_cb %d, %s %s %s", oper,
+		(oper&BIO_CB_RETURN)?"return":"before",
+		(oper&BIO_CB_READ)?"read":((oper&BIO_CB_WRITE)?"write":"other"),
+		WSAGetLastError()==WSAEWOULDBLOCK?"wsawb":"");
+	/* on windows, check if previous operation caused EWOULDBLOCK */
+	if( (oper == (BIO_CB_READ|BIO_CB_RETURN) && argl == 0) ||
+		(oper == (BIO_CB_GETS|BIO_CB_RETURN) && argl == 0)) {
+		if(WSAGetLastError() == WSAEWOULDBLOCK)
+			winsock_tcp_wouldblock((struct event*)
+				BIO_get_callback_arg(b), EV_READ);
+	}
+	if( (oper == (BIO_CB_WRITE|BIO_CB_RETURN) && argl == 0) ||
+		(oper == (BIO_CB_PUTS|BIO_CB_RETURN) && argl == 0)) {
+		if(WSAGetLastError() == WSAEWOULDBLOCK)
+			winsock_tcp_wouldblock((struct event*)
+				BIO_get_callback_arg(b), EV_WRITE);
+	}
+	/* return original return value */
+	return retvalue;
+}
+
+/** set win bio callbacks for nonblocking operations */
+void
+comm_point_tcp_win_bio_cb(struct comm_point* c, void* thessl)
+{
+	SSL* ssl = (SSL*)thessl;
+	/* set them both just in case, but usually they are the same BIO */
+	BIO_set_callback(SSL_get_rbio(ssl), &win_bio_cb);
+	BIO_set_callback_arg(SSL_get_rbio(ssl), (char*)&c->ev->ev);
+	BIO_set_callback(SSL_get_wbio(ssl), &win_bio_cb);
+	BIO_set_callback_arg(SSL_get_wbio(ssl), (char*)&c->ev->ev);
+}
+#endif
+
+void 
+comm_point_tcp_accept_callback(int fd, short event, void* arg)
+{
+	struct comm_point* c = (struct comm_point*)arg, *c_hdl;
+	int new_fd;
+	log_assert(c->type == comm_tcp_accept);
+	if(!(event & EV_READ)) {
+		log_info("ignoring tcp accept event %d", (int)event);
+		return;
+	}
+	comm_base_now(c->ev->base);
+	/* find free tcp handler. */
+	if(!c->tcp_free) {
+		log_warn("accepted too many tcp, connections full");
+		return;
+	}
+	/* accept incoming connection. */
+	c_hdl = c->tcp_free;
+	log_assert(fd != -1);
+	new_fd = comm_point_perform_accept(c, &c_hdl->repinfo.addr,
+		&c_hdl->repinfo.addrlen);
+	if(new_fd == -1)
+		return;
+	if(c->ssl) {
+		c_hdl->ssl = incoming_ssl_fd(c->ssl, new_fd);
+		if(!c_hdl->ssl) {
+			c_hdl->fd = new_fd;
+			comm_point_close(c_hdl);
+			return;
+		}
+		c_hdl->ssl_shake_state = comm_ssl_shake_read;
+#ifdef USE_WINSOCK
+		comm_point_tcp_win_bio_cb(c_hdl, c_hdl->ssl);
+#endif
+	}
+
+	/* grab the tcp handler buffers */
+	c->tcp_free = c_hdl->tcp_free;
+	if(!c->tcp_free) {
+		/* stop accepting incoming queries for now. */
+		comm_point_stop_listening(c);
+	}
+	/* addr is dropped. Not needed for tcp reply. */
+	setup_tcp_handler(c_hdl, new_fd);
+}
+
+/** Make tcp handler free for next assignment */
+static void
+reclaim_tcp_handler(struct comm_point* c)
+{
+	log_assert(c->type == comm_tcp);
+	if(c->ssl) {
+		SSL_shutdown(c->ssl);
+		SSL_free(c->ssl);
+		c->ssl = NULL;
+	}
+	comm_point_close(c);
+	if(c->tcp_parent) {
+		c->tcp_free = c->tcp_parent->tcp_free;
+		c->tcp_parent->tcp_free = c;
+		if(!c->tcp_free) {
+			/* re-enable listening on accept socket */
+			comm_point_start_listening(c->tcp_parent, -1, -1);
+		}
+	}
+}
+
+/** do the callback when writing is done */
+static void
+tcp_callback_writer(struct comm_point* c)
+{
+	log_assert(c->type == comm_tcp);
+	ldns_buffer_clear(c->buffer);
+	if(c->tcp_do_toggle_rw)
+		c->tcp_is_reading = 1;
+	c->tcp_byte_count = 0;
+	/* switch from listening(write) to listening(read) */
+	comm_point_stop_listening(c);
+	comm_point_start_listening(c, -1, -1);
+}
+
+/** do the callback when reading is done */
+static void
+tcp_callback_reader(struct comm_point* c)
+{
+	log_assert(c->type == comm_tcp || c->type == comm_local);
+	ldns_buffer_flip(c->buffer);
+	if(c->tcp_do_toggle_rw)
+		c->tcp_is_reading = 0;
+	c->tcp_byte_count = 0;
+	if(c->type == comm_tcp)
+		comm_point_stop_listening(c);
+	fptr_ok(fptr_whitelist_comm_point(c->callback));
+	if( (*c->callback)(c, c->cb_arg, NETEVENT_NOERROR, &c->repinfo) ) {
+		comm_point_start_listening(c, -1, TCP_QUERY_TIMEOUT);
+	}
+}
+
+/** continue ssl handshake */
+static int
+ssl_handshake(struct comm_point* c)
+{
+	int r;
+	if(c->ssl_shake_state == comm_ssl_shake_hs_read) {
+		/* read condition satisfied back to writing */
+		comm_point_listen_for_rw(c, 1, 1);
+		c->ssl_shake_state = comm_ssl_shake_none;
+		return 1;
+	}
+	if(c->ssl_shake_state == comm_ssl_shake_hs_write) {
+		/* write condition satisfied, back to reading */
+		comm_point_listen_for_rw(c, 1, 0);
+		c->ssl_shake_state = comm_ssl_shake_none;
+		return 1;
+	}
+
+	ERR_clear_error();
+	r = SSL_do_handshake(c->ssl);
+	if(r != 1) {
+		int want = SSL_get_error(c->ssl, r);
+		if(want == SSL_ERROR_WANT_READ) {
+			if(c->ssl_shake_state == comm_ssl_shake_read)
+				return 1;
+			c->ssl_shake_state = comm_ssl_shake_read;
+			comm_point_listen_for_rw(c, 1, 0);
+			return 1;
+		} else if(want == SSL_ERROR_WANT_WRITE) {
+			if(c->ssl_shake_state == comm_ssl_shake_write)
+				return 1;
+			c->ssl_shake_state = comm_ssl_shake_write;
+			comm_point_listen_for_rw(c, 0, 1);
+			return 1;
+		} else if(r == 0) {
+			return 0; /* closed */
+		} else if(want == SSL_ERROR_SYSCALL) {
+			/* SYSCALL and errno==0 means closed uncleanly */
+			if(errno != 0)
+				log_err("SSL_handshake syscall: %s",
+					strerror(errno));
+			return 0;
+		} else {
+			log_crypto_err("ssl handshake failed");
+			log_addr(1, "ssl handshake failed", &c->repinfo.addr,
+				c->repinfo.addrlen);
+			return 0;
+		}
+	}
+	/* this is where peer verification could take place */
+	log_addr(VERB_ALGO, "SSL DNS connection", &c->repinfo.addr,
+		c->repinfo.addrlen);
+
+	/* setup listen rw correctly */
+	if(c->tcp_is_reading) {
+		if(c->ssl_shake_state != comm_ssl_shake_read)
+			comm_point_listen_for_rw(c, 1, 0);
+	} else {
+		comm_point_listen_for_rw(c, 1, 1);
+	}
+	c->ssl_shake_state = comm_ssl_shake_none;
+	return 1;
+}
+
+/** ssl read callback on TCP */
+static int
+ssl_handle_read(struct comm_point* c)
+{
+	int r;
+	if(c->ssl_shake_state != comm_ssl_shake_none) {
+		if(!ssl_handshake(c))
+			return 0;
+		if(c->ssl_shake_state != comm_ssl_shake_none)
+			return 1;
+	}
+	if(c->tcp_byte_count < sizeof(uint16_t)) {
+		/* read length bytes */
+		ERR_clear_error();
+		if((r=SSL_read(c->ssl, (void*)ldns_buffer_at(c->buffer,
+			c->tcp_byte_count), (int)(sizeof(uint16_t) -
+			c->tcp_byte_count))) <= 0) {
+			int want = SSL_get_error(c->ssl, r);
+			if(want == SSL_ERROR_ZERO_RETURN) {
+				return 0; /* shutdown, closed */
+			} else if(want == SSL_ERROR_WANT_READ) {
+				return 1; /* read more later */
+			} else if(want == SSL_ERROR_WANT_WRITE) {
+				c->ssl_shake_state = comm_ssl_shake_hs_write;
+				comm_point_listen_for_rw(c, 0, 1);
+				return 1;
+			} else if(want == SSL_ERROR_SYSCALL) {
+				if(errno != 0)
+					log_err("SSL_read syscall: %s",
+						strerror(errno));
+				return 0;
+			}
+			log_crypto_err("could not SSL_read");
+			return 0;
+		}
+		c->tcp_byte_count += r;
+		if(c->tcp_byte_count != sizeof(uint16_t))
+			return 1;
+		if(ldns_buffer_read_u16_at(c->buffer, 0) >
+			ldns_buffer_capacity(c->buffer)) {
+			verbose(VERB_QUERY, "ssl: dropped larger than buffer");
+			return 0;
+		}
+		ldns_buffer_set_limit(c->buffer,
+			ldns_buffer_read_u16_at(c->buffer, 0));
+		if(ldns_buffer_limit(c->buffer) < LDNS_HEADER_SIZE) {
+			verbose(VERB_QUERY, "ssl: dropped bogus too short.");
+			return 0;
+		}
+		verbose(VERB_ALGO, "Reading ssl tcp query of length %d",
+			(int)ldns_buffer_limit(c->buffer));
+	}
+	log_assert(ldns_buffer_remaining(c->buffer) > 0);
+	ERR_clear_error();
+	r = SSL_read(c->ssl, (void*)ldns_buffer_current(c->buffer),
+		(int)ldns_buffer_remaining(c->buffer));
+	if(r <= 0) {
+		int want = SSL_get_error(c->ssl, r);
+		if(want == SSL_ERROR_ZERO_RETURN) {
+			return 0; /* shutdown, closed */
+		} else if(want == SSL_ERROR_WANT_READ) {
+			return 1; /* read more later */
+		} else if(want == SSL_ERROR_WANT_WRITE) {
+			c->ssl_shake_state = comm_ssl_shake_hs_write;
+			comm_point_listen_for_rw(c, 0, 1);
+			return 1;
+		} else if(want == SSL_ERROR_SYSCALL) {
+			if(errno != 0)
+				log_err("SSL_read syscall: %s",
+					strerror(errno));
+			return 0;
+		}
+		log_crypto_err("could not SSL_read");
+		return 0;
+	}
+	ldns_buffer_skip(c->buffer, (ssize_t)r);
+	if(ldns_buffer_remaining(c->buffer) <= 0) {
+		tcp_callback_reader(c);
+	}
+	return 1;
+}
+
+/** ssl write callback on TCP */
+static int
+ssl_handle_write(struct comm_point* c)
+{
+	int r;
+	if(c->ssl_shake_state != comm_ssl_shake_none) {
+		if(!ssl_handshake(c))
+			return 0;
+		if(c->ssl_shake_state != comm_ssl_shake_none)
+			return 1;
+	}
+	/* ignore return, if fails we may simply block */
+	(void)SSL_set_mode(c->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
+	if(c->tcp_byte_count < sizeof(uint16_t)) {
+		uint16_t len = htons(ldns_buffer_limit(c->buffer));
+		ERR_clear_error();
+		r = SSL_write(c->ssl,
+			(void*)(((uint8_t*)&len)+c->tcp_byte_count),
+			(int)(sizeof(uint16_t)-c->tcp_byte_count));
+		if(r <= 0) {
+			int want = SSL_get_error(c->ssl, r);
+			if(want == SSL_ERROR_ZERO_RETURN) {
+				return 0; /* closed */
+			} else if(want == SSL_ERROR_WANT_READ) {
+				c->ssl_shake_state = comm_ssl_shake_read;
+				comm_point_listen_for_rw(c, 1, 0);
+				return 1; /* wait for read condition */
+			} else if(want == SSL_ERROR_WANT_WRITE) {
+				return 1; /* write more later */
+			} else if(want == SSL_ERROR_SYSCALL) {
+				if(errno != 0)
+					log_err("SSL_write syscall: %s",
+						strerror(errno));
+				return 0;
+			}
+			log_crypto_err("could not SSL_write");
+			return 0;
+		}
+		c->tcp_byte_count += r;
+		if(c->tcp_byte_count < sizeof(uint16_t))
+			return 1;
+		ldns_buffer_set_position(c->buffer, c->tcp_byte_count -
+			sizeof(uint16_t));
+		if(ldns_buffer_remaining(c->buffer) == 0) {
+			tcp_callback_writer(c);
+			return 1;
+		}
+	}
+	log_assert(ldns_buffer_remaining(c->buffer) > 0);
+	ERR_clear_error();
+	r = SSL_write(c->ssl, (void*)ldns_buffer_current(c->buffer),
+		(int)ldns_buffer_remaining(c->buffer));
+	if(r <= 0) {
+		int want = SSL_get_error(c->ssl, r);
+		if(want == SSL_ERROR_ZERO_RETURN) {
+			return 0; /* closed */
+		} else if(want == SSL_ERROR_WANT_READ) {
+			c->ssl_shake_state = comm_ssl_shake_read;
+			comm_point_listen_for_rw(c, 1, 0);
+			return 1; /* wait for read condition */
+		} else if(want == SSL_ERROR_WANT_WRITE) {
+			return 1; /* write more later */
+		} else if(want == SSL_ERROR_SYSCALL) {
+			if(errno != 0)
+				log_err("SSL_write syscall: %s",
+					strerror(errno));
+			return 0;
+		}
+		log_crypto_err("could not SSL_write");
+		return 0;
+	}
+	ldns_buffer_skip(c->buffer, (ssize_t)r);
+
+	if(ldns_buffer_remaining(c->buffer) == 0) {
+		tcp_callback_writer(c);
+	}
+	return 1;
+}
+
+/** handle ssl tcp connection with dns contents */
+static int
+ssl_handle_it(struct comm_point* c)
+{
+	if(c->tcp_is_reading)
+		return ssl_handle_read(c);
+	return ssl_handle_write(c);
+}
+
+/** Handle tcp reading callback. 
+ * @param fd: file descriptor of socket.
+ * @param c: comm point to read from into buffer.
+ * @param short_ok: if true, very short packets are OK (for comm_local).
+ * @return: 0 on error 
+ */
+static int
+comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok)
+{
+	ssize_t r;
+	log_assert(c->type == comm_tcp || c->type == comm_local);
+	if(c->ssl)
+		return ssl_handle_it(c);
+	if(!c->tcp_is_reading)
+		return 0;
+
+	log_assert(fd != -1);
+	if(c->tcp_byte_count < sizeof(uint16_t)) {
+		/* read length bytes */
+		r = recv(fd,(void*)ldns_buffer_at(c->buffer,c->tcp_byte_count),
+			sizeof(uint16_t)-c->tcp_byte_count, 0);
+		if(r == 0)
+			return 0;
+		else if(r == -1) {
+#ifndef USE_WINSOCK
+			if(errno == EINTR || errno == EAGAIN)
+				return 1;
+#ifdef ECONNRESET
+			if(errno == ECONNRESET && verbosity < 2)
+				return 0; /* silence reset by peer */
+#endif
+			log_err("read (in tcp s): %s", strerror(errno));
+#else /* USE_WINSOCK */
+			if(WSAGetLastError() == WSAECONNRESET)
+				return 0;
+			if(WSAGetLastError() == WSAEINPROGRESS)
+				return 1;
+			if(WSAGetLastError() == WSAEWOULDBLOCK) {
+				winsock_tcp_wouldblock(&c->ev->ev, EV_READ);
+				return 1;
+			}
+			log_err("read (in tcp s): %s", 
+				wsa_strerror(WSAGetLastError()));
+#endif
+			log_addr(0, "remote address is", &c->repinfo.addr,
+				c->repinfo.addrlen);
+			return 0;
+		} 
+		c->tcp_byte_count += r;
+		if(c->tcp_byte_count != sizeof(uint16_t))
+			return 1;
+		if(ldns_buffer_read_u16_at(c->buffer, 0) >
+			ldns_buffer_capacity(c->buffer)) {
+			verbose(VERB_QUERY, "tcp: dropped larger than buffer");
+			return 0;
+		}
+		ldns_buffer_set_limit(c->buffer, 
+			ldns_buffer_read_u16_at(c->buffer, 0));
+		if(!short_ok && 
+			ldns_buffer_limit(c->buffer) < LDNS_HEADER_SIZE) {
+			verbose(VERB_QUERY, "tcp: dropped bogus too short.");
+			return 0;
+		}
+		verbose(VERB_ALGO, "Reading tcp query of length %d", 
+			(int)ldns_buffer_limit(c->buffer));
+	}
+
+	log_assert(ldns_buffer_remaining(c->buffer) > 0);
+	r = recv(fd, (void*)ldns_buffer_current(c->buffer), 
+		ldns_buffer_remaining(c->buffer), 0);
+	if(r == 0) {
+		return 0;
+	} else if(r == -1) {
+#ifndef USE_WINSOCK
+		if(errno == EINTR || errno == EAGAIN)
+			return 1;
+		log_err("read (in tcp r): %s", strerror(errno));
+#else /* USE_WINSOCK */
+		if(WSAGetLastError() == WSAECONNRESET)
+			return 0;
+		if(WSAGetLastError() == WSAEINPROGRESS)
+			return 1;
+		if(WSAGetLastError() == WSAEWOULDBLOCK) {
+			winsock_tcp_wouldblock(&c->ev->ev, EV_READ);
+			return 1;
+		}
+		log_err("read (in tcp r): %s", 
+			wsa_strerror(WSAGetLastError()));
+#endif
+		log_addr(0, "remote address is", &c->repinfo.addr,
+			c->repinfo.addrlen);
+		return 0;
+	}
+	ldns_buffer_skip(c->buffer, r);
+	if(ldns_buffer_remaining(c->buffer) <= 0) {
+		tcp_callback_reader(c);
+	}
+	return 1;
+}
+
+/** 
+ * Handle tcp writing callback. 
+ * @param fd: file descriptor of socket.
+ * @param c: comm point to write buffer out of.
+ * @return: 0 on error
+ */
+static int
+comm_point_tcp_handle_write(int fd, struct comm_point* c)
+{
+	ssize_t r;
+	log_assert(c->type == comm_tcp);
+	if(c->tcp_is_reading && !c->ssl)
+		return 0;
+	log_assert(fd != -1);
+	if(c->tcp_byte_count == 0 && c->tcp_check_nb_connect) {
+		/* check for pending error from nonblocking connect */
+		/* from Stevens, unix network programming, vol1, 3rd ed, p450*/
+		int error = 0;
+		socklen_t len = (socklen_t)sizeof(error);
+		if(getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&error, 
+			&len) < 0){
+#ifndef USE_WINSOCK
+			error = errno; /* on solaris errno is error */
+#else /* USE_WINSOCK */
+			error = WSAGetLastError();
+#endif
+		}
+#ifndef USE_WINSOCK
+#if defined(EINPROGRESS) && defined(EWOULDBLOCK)
+		if(error == EINPROGRESS || error == EWOULDBLOCK)
+			return 1; /* try again later */
+#endif
+		else if(error != 0 && verbosity < 2)
+			return 0; /* silence lots of chatter in the logs */
+                else if(error != 0) {
+			log_err("tcp connect: %s", strerror(error));
+#else /* USE_WINSOCK */
+		/* examine error */
+		if(error == WSAEINPROGRESS)
+			return 1;
+		else if(error == WSAEWOULDBLOCK) {
+			winsock_tcp_wouldblock(&c->ev->ev, EV_WRITE);
+			return 1;
+		} else if(error != 0 && verbosity < 2)
+			return 0;
+		else if(error != 0) {
+			log_err("tcp connect: %s", wsa_strerror(error));
+#endif /* USE_WINSOCK */
+			log_addr(0, "remote address is", &c->repinfo.addr, 
+				c->repinfo.addrlen);
+			return 0;
+		}
+	}
+	if(c->ssl)
+		return ssl_handle_it(c);
+
+	if(c->tcp_byte_count < sizeof(uint16_t)) {
+		uint16_t len = htons(ldns_buffer_limit(c->buffer));
+#ifdef HAVE_WRITEV
+		struct iovec iov[2];
+		iov[0].iov_base = (uint8_t*)&len + c->tcp_byte_count;
+		iov[0].iov_len = sizeof(uint16_t) - c->tcp_byte_count;
+		iov[1].iov_base = ldns_buffer_begin(c->buffer);
+		iov[1].iov_len = ldns_buffer_limit(c->buffer);
+		log_assert(iov[0].iov_len > 0);
+		log_assert(iov[1].iov_len > 0);
+		r = writev(fd, iov, 2);
+#else /* HAVE_WRITEV */
+		r = send(fd, (void*)(((uint8_t*)&len)+c->tcp_byte_count),
+			sizeof(uint16_t)-c->tcp_byte_count, 0);
+#endif /* HAVE_WRITEV */
+		if(r == -1) {
+#ifndef USE_WINSOCK
+#ifdef EPIPE
+                	if(errno == EPIPE && verbosity < 2)
+                        	return 0; /* silence 'broken pipe' */
+#endif
+			if(errno == EINTR || errno == EAGAIN)
+				return 1;
+			log_err("tcp writev: %s", strerror(errno));
+#else
+			if(WSAGetLastError() == WSAENOTCONN)
+				return 1;
+			if(WSAGetLastError() == WSAEINPROGRESS)
+				return 1;
+			if(WSAGetLastError() == WSAEWOULDBLOCK) {
+				winsock_tcp_wouldblock(&c->ev->ev, EV_WRITE);
+				return 1; 
+			}
+			log_err("tcp send s: %s", 
+				wsa_strerror(WSAGetLastError()));
+#endif
+			log_addr(0, "remote address is", &c->repinfo.addr,
+				c->repinfo.addrlen);
+			return 0;
+		}
+		c->tcp_byte_count += r;
+		if(c->tcp_byte_count < sizeof(uint16_t))
+			return 1;
+		ldns_buffer_set_position(c->buffer, c->tcp_byte_count - 
+			sizeof(uint16_t));
+		if(ldns_buffer_remaining(c->buffer) == 0) {
+			tcp_callback_writer(c);
+			return 1;
+		}
+	}
+	log_assert(ldns_buffer_remaining(c->buffer) > 0);
+	r = send(fd, (void*)ldns_buffer_current(c->buffer), 
+		ldns_buffer_remaining(c->buffer), 0);
+	if(r == -1) {
+#ifndef USE_WINSOCK
+		if(errno == EINTR || errno == EAGAIN)
+			return 1;
+		log_err("tcp send r: %s", strerror(errno));
+#else
+		if(WSAGetLastError() == WSAEINPROGRESS)
+			return 1;
+		if(WSAGetLastError() == WSAEWOULDBLOCK) {
+			winsock_tcp_wouldblock(&c->ev->ev, EV_WRITE);
+			return 1; 
+		}
+		log_err("tcp send r: %s", 
+			wsa_strerror(WSAGetLastError()));
+#endif
+		log_addr(0, "remote address is", &c->repinfo.addr,
+			c->repinfo.addrlen);
+		return 0;
+	}
+	ldns_buffer_skip(c->buffer, r);
+
+	if(ldns_buffer_remaining(c->buffer) == 0) {
+		tcp_callback_writer(c);
+	}
+	
+	return 1;
+}
+
+void 
+comm_point_tcp_handle_callback(int fd, short event, void* arg)
+{
+	struct comm_point* c = (struct comm_point*)arg;
+	log_assert(c->type == comm_tcp);
+	comm_base_now(c->ev->base);
+
+	if(event&EV_READ) {
+		if(!comm_point_tcp_handle_read(fd, c, 0)) {
+			reclaim_tcp_handler(c);
+			if(!c->tcp_do_close) {
+				fptr_ok(fptr_whitelist_comm_point(
+					c->callback));
+				(void)(*c->callback)(c, c->cb_arg, 
+					NETEVENT_CLOSED, NULL);
+			}
+		}
+		return;
+	}
+	if(event&EV_WRITE) {
+		if(!comm_point_tcp_handle_write(fd, c)) {
+			reclaim_tcp_handler(c);
+			if(!c->tcp_do_close) {
+				fptr_ok(fptr_whitelist_comm_point(
+					c->callback));
+				(void)(*c->callback)(c, c->cb_arg, 
+					NETEVENT_CLOSED, NULL);
+			}
+		}
+		return;
+	}
+	if(event&EV_TIMEOUT) {
+		verbose(VERB_QUERY, "tcp took too long, dropped");
+		reclaim_tcp_handler(c);
+		if(!c->tcp_do_close) {
+			fptr_ok(fptr_whitelist_comm_point(c->callback));
+			(void)(*c->callback)(c, c->cb_arg,
+				NETEVENT_TIMEOUT, NULL);
+		}
+		return;
+	}
+	log_err("Ignored event %d for tcphdl.", event);
+}
+
+void comm_point_local_handle_callback(int fd, short event, void* arg)
+{
+	struct comm_point* c = (struct comm_point*)arg;
+	log_assert(c->type == comm_local);
+	comm_base_now(c->ev->base);
+
+	if(event&EV_READ) {
+		if(!comm_point_tcp_handle_read(fd, c, 1)) {
+			fptr_ok(fptr_whitelist_comm_point(c->callback));
+			(void)(*c->callback)(c, c->cb_arg, NETEVENT_CLOSED, 
+				NULL);
+		}
+		return;
+	}
+	log_err("Ignored event %d for localhdl.", event);
+}
+
+void comm_point_raw_handle_callback(int ATTR_UNUSED(fd), 
+	short event, void* arg)
+{
+	struct comm_point* c = (struct comm_point*)arg;
+	int err = NETEVENT_NOERROR;
+	log_assert(c->type == comm_raw);
+	comm_base_now(c->ev->base);
+	
+	if(event&EV_TIMEOUT)
+		err = NETEVENT_TIMEOUT;
+	fptr_ok(fptr_whitelist_comm_point_raw(c->callback));
+	(void)(*c->callback)(c, c->cb_arg, err, NULL);
+}
+
+struct comm_point* 
+comm_point_create_udp(struct comm_base *base, int fd, ldns_buffer* buffer,
+	comm_point_callback_t* callback, void* callback_arg)
+{
+	struct comm_point* c = (struct comm_point*)calloc(1,
+		sizeof(struct comm_point));
+	short evbits;
+	if(!c)
+		return NULL;
+	c->ev = (struct internal_event*)calloc(1,
+		sizeof(struct internal_event));
+	if(!c->ev) {
+		free(c);
+		return NULL;
+	}
+	c->ev->base = base;
+	c->fd = fd;
+	c->buffer = buffer;
+	c->timeout = NULL;
+	c->tcp_is_reading = 0;
+	c->tcp_byte_count = 0;
+	c->tcp_parent = NULL;
+	c->max_tcp_count = 0;
+	c->tcp_handlers = NULL;
+	c->tcp_free = NULL;
+	c->type = comm_udp;
+	c->tcp_do_close = 0;
+	c->do_not_close = 0;
+	c->tcp_do_toggle_rw = 0;
+	c->tcp_check_nb_connect = 0;
+	c->inuse = 0;
+	c->callback = callback;
+	c->cb_arg = callback_arg;
+	evbits = EV_READ | EV_PERSIST;
+	/* libevent stuff */
+	event_set(&c->ev->ev, c->fd, evbits, comm_point_udp_callback, c);
+	if(event_base_set(base->eb->base, &c->ev->ev) != 0) {
+		log_err("could not baseset udp event");
+		comm_point_delete(c);
+		return NULL;
+	}
+	if(fd!=-1 && event_add(&c->ev->ev, c->timeout) != 0 ) {
+		log_err("could not add udp event");
+		comm_point_delete(c);
+		return NULL;
+	}
+	return c;
+}
+
+struct comm_point* 
+comm_point_create_udp_ancil(struct comm_base *base, int fd, 
+	ldns_buffer* buffer, 
+	comm_point_callback_t* callback, void* callback_arg)
+{
+	struct comm_point* c = (struct comm_point*)calloc(1,
+		sizeof(struct comm_point));
+	short evbits;
+	if(!c)
+		return NULL;
+	c->ev = (struct internal_event*)calloc(1,
+		sizeof(struct internal_event));
+	if(!c->ev) {
+		free(c);
+		return NULL;
+	}
+	c->ev->base = base;
+	c->fd = fd;
+	c->buffer = buffer;
+	c->timeout = NULL;
+	c->tcp_is_reading = 0;
+	c->tcp_byte_count = 0;
+	c->tcp_parent = NULL;
+	c->max_tcp_count = 0;
+	c->tcp_handlers = NULL;
+	c->tcp_free = NULL;
+	c->type = comm_udp;
+	c->tcp_do_close = 0;
+	c->do_not_close = 0;
+	c->inuse = 0;
+	c->tcp_do_toggle_rw = 0;
+	c->tcp_check_nb_connect = 0;
+	c->callback = callback;
+	c->cb_arg = callback_arg;
+	evbits = EV_READ | EV_PERSIST;
+	/* libevent stuff */
+	event_set(&c->ev->ev, c->fd, evbits, comm_point_udp_ancil_callback, c);
+	if(event_base_set(base->eb->base, &c->ev->ev) != 0) {
+		log_err("could not baseset udp event");
+		comm_point_delete(c);
+		return NULL;
+	}
+	if(fd!=-1 && event_add(&c->ev->ev, c->timeout) != 0 ) {
+		log_err("could not add udp event");
+		comm_point_delete(c);
+		return NULL;
+	}
+	return c;
+}
+
+static struct comm_point* 
+comm_point_create_tcp_handler(struct comm_base *base, 
+	struct comm_point* parent, size_t bufsize,
+        comm_point_callback_t* callback, void* callback_arg)
+{
+	struct comm_point* c = (struct comm_point*)calloc(1,
+		sizeof(struct comm_point));
+	short evbits;
+	if(!c)
+		return NULL;
+	c->ev = (struct internal_event*)calloc(1,
+		sizeof(struct internal_event));
+	if(!c->ev) {
+		free(c);
+		return NULL;
+	}
+	c->ev->base = base;
+	c->fd = -1;
+	c->buffer = ldns_buffer_new(bufsize);
+	if(!c->buffer) {
+		free(c->ev);
+		free(c);
+		return NULL;
+	}
+	c->timeout = (struct timeval*)malloc(sizeof(struct timeval));
+	if(!c->timeout) {
+		ldns_buffer_free(c->buffer);
+		free(c->ev);
+		free(c);
+		return NULL;
+	}
+	c->tcp_is_reading = 0;
+	c->tcp_byte_count = 0;
+	c->tcp_parent = parent;
+	c->max_tcp_count = 0;
+	c->tcp_handlers = NULL;
+	c->tcp_free = NULL;
+	c->type = comm_tcp;
+	c->tcp_do_close = 0;
+	c->do_not_close = 0;
+	c->tcp_do_toggle_rw = 1;
+	c->tcp_check_nb_connect = 0;
+	c->repinfo.c = c;
+	c->callback = callback;
+	c->cb_arg = callback_arg;
+	/* add to parent free list */
+	c->tcp_free = parent->tcp_free;
+	parent->tcp_free = c;
+	/* libevent stuff */
+	evbits = EV_PERSIST | EV_READ | EV_TIMEOUT;
+	event_set(&c->ev->ev, c->fd, evbits, comm_point_tcp_handle_callback, c);
+	if(event_base_set(base->eb->base, &c->ev->ev) != 0)
+	{
+		log_err("could not basetset tcphdl event");
+		parent->tcp_free = c->tcp_free;
+		free(c->ev);
+		free(c);
+		return NULL;
+	}
+	return c;
+}
+
+struct comm_point* 
+comm_point_create_tcp(struct comm_base *base, int fd, int num, size_t bufsize,
+        comm_point_callback_t* callback, void* callback_arg)
+{
+	struct comm_point* c = (struct comm_point*)calloc(1,
+		sizeof(struct comm_point));
+	short evbits;
+	int i;
+	/* first allocate the TCP accept listener */
+	if(!c)
+		return NULL;
+	c->ev = (struct internal_event*)calloc(1,
+		sizeof(struct internal_event));
+	if(!c->ev) {
+		free(c);
+		return NULL;
+	}
+	c->ev->base = base;
+	c->fd = fd;
+	c->buffer = NULL;
+	c->timeout = NULL;
+	c->tcp_is_reading = 0;
+	c->tcp_byte_count = 0;
+	c->tcp_parent = NULL;
+	c->max_tcp_count = num;
+	c->tcp_handlers = (struct comm_point**)calloc((size_t)num,
+		sizeof(struct comm_point*));
+	if(!c->tcp_handlers) {
+		free(c->ev);
+		free(c);
+		return NULL;
+	}
+	c->tcp_free = NULL;
+	c->type = comm_tcp_accept;
+	c->tcp_do_close = 0;
+	c->do_not_close = 0;
+	c->tcp_do_toggle_rw = 0;
+	c->tcp_check_nb_connect = 0;
+	c->callback = NULL;
+	c->cb_arg = NULL;
+	evbits = EV_READ | EV_PERSIST;
+	/* libevent stuff */
+	event_set(&c->ev->ev, c->fd, evbits, comm_point_tcp_accept_callback, c);
+	if(event_base_set(base->eb->base, &c->ev->ev) != 0 ||
+		event_add(&c->ev->ev, c->timeout) != 0 )
+	{
+		log_err("could not add tcpacc event");
+		comm_point_delete(c);
+		return NULL;
+	}
+
+	/* now prealloc the tcp handlers */
+	for(i=0; i<num; i++) {
+		c->tcp_handlers[i] = comm_point_create_tcp_handler(base,
+			c, bufsize, callback, callback_arg);
+		if(!c->tcp_handlers[i]) {
+			comm_point_delete(c);
+			return NULL;
+		}
+	}
+	
+	return c;
+}
+
+struct comm_point* 
+comm_point_create_tcp_out(struct comm_base *base, size_t bufsize,
+        comm_point_callback_t* callback, void* callback_arg)
+{
+	struct comm_point* c = (struct comm_point*)calloc(1,
+		sizeof(struct comm_point));
+	short evbits;
+	if(!c)
+		return NULL;
+	c->ev = (struct internal_event*)calloc(1,
+		sizeof(struct internal_event));
+	if(!c->ev) {
+		free(c);
+		return NULL;
+	}
+	c->ev->base = base;
+	c->fd = -1;
+	c->buffer = ldns_buffer_new(bufsize);
+	if(!c->buffer) {
+		free(c->ev);
+		free(c);
+		return NULL;
+	}
+	c->timeout = NULL;
+	c->tcp_is_reading = 0;
+	c->tcp_byte_count = 0;
+	c->tcp_parent = NULL;
+	c->max_tcp_count = 0;
+	c->tcp_handlers = NULL;
+	c->tcp_free = NULL;
+	c->type = comm_tcp;
+	c->tcp_do_close = 0;
+	c->do_not_close = 0;
+	c->tcp_do_toggle_rw = 1;
+	c->tcp_check_nb_connect = 1;
+	c->repinfo.c = c;
+	c->callback = callback;
+	c->cb_arg = callback_arg;
+	evbits = EV_PERSIST | EV_WRITE;
+	event_set(&c->ev->ev, c->fd, evbits, comm_point_tcp_handle_callback, c);
+	if(event_base_set(base->eb->base, &c->ev->ev) != 0)
+	{
+		log_err("could not basetset tcpout event");
+		ldns_buffer_free(c->buffer);
+		free(c->ev);
+		free(c);
+		return NULL;
+	}
+
+	return c;
+}
+
+struct comm_point* 
+comm_point_create_local(struct comm_base *base, int fd, size_t bufsize,
+        comm_point_callback_t* callback, void* callback_arg)
+{
+	struct comm_point* c = (struct comm_point*)calloc(1,
+		sizeof(struct comm_point));
+	short evbits;
+	if(!c)
+		return NULL;
+	c->ev = (struct internal_event*)calloc(1,
+		sizeof(struct internal_event));
+	if(!c->ev) {
+		free(c);
+		return NULL;
+	}
+	c->ev->base = base;
+	c->fd = fd;
+	c->buffer = ldns_buffer_new(bufsize);
+	if(!c->buffer) {
+		free(c->ev);
+		free(c);
+		return NULL;
+	}
+	c->timeout = NULL;
+	c->tcp_is_reading = 1;
+	c->tcp_byte_count = 0;
+	c->tcp_parent = NULL;
+	c->max_tcp_count = 0;
+	c->tcp_handlers = NULL;
+	c->tcp_free = NULL;
+	c->type = comm_local;
+	c->tcp_do_close = 0;
+	c->do_not_close = 1;
+	c->tcp_do_toggle_rw = 0;
+	c->tcp_check_nb_connect = 0;
+	c->callback = callback;
+	c->cb_arg = callback_arg;
+	/* libevent stuff */
+	evbits = EV_PERSIST | EV_READ;
+	event_set(&c->ev->ev, c->fd, evbits, comm_point_local_handle_callback, 
+		c);
+	if(event_base_set(base->eb->base, &c->ev->ev) != 0 ||
+		event_add(&c->ev->ev, c->timeout) != 0 )
+	{
+		log_err("could not add localhdl event");
+		free(c->ev);
+		free(c);
+		return NULL;
+	}
+	return c;
+}
+
+struct comm_point* 
+comm_point_create_raw(struct comm_base* base, int fd, int writing, 
+	comm_point_callback_t* callback, void* callback_arg)
+{
+	struct comm_point* c = (struct comm_point*)calloc(1,
+		sizeof(struct comm_point));
+	short evbits;
+	if(!c)
+		return NULL;
+	c->ev = (struct internal_event*)calloc(1,
+		sizeof(struct internal_event));
+	if(!c->ev) {
+		free(c);
+		return NULL;
+	}
+	c->ev->base = base;
+	c->fd = fd;
+	c->buffer = NULL;
+	c->timeout = NULL;
+	c->tcp_is_reading = 0;
+	c->tcp_byte_count = 0;
+	c->tcp_parent = NULL;
+	c->max_tcp_count = 0;
+	c->tcp_handlers = NULL;
+	c->tcp_free = NULL;
+	c->type = comm_raw;
+	c->tcp_do_close = 0;
+	c->do_not_close = 1;
+	c->tcp_do_toggle_rw = 0;
+	c->tcp_check_nb_connect = 0;
+	c->callback = callback;
+	c->cb_arg = callback_arg;
+	/* libevent stuff */
+	if(writing)
+		evbits = EV_PERSIST | EV_WRITE;
+	else 	evbits = EV_PERSIST | EV_READ;
+	event_set(&c->ev->ev, c->fd, evbits, comm_point_raw_handle_callback, 
+		c);
+	if(event_base_set(base->eb->base, &c->ev->ev) != 0 ||
+		event_add(&c->ev->ev, c->timeout) != 0 )
+	{
+		log_err("could not add rawhdl event");
+		free(c->ev);
+		free(c);
+		return NULL;
+	}
+	return c;
+}
+
+void 
+comm_point_close(struct comm_point* c)
+{
+	if(!c)
+		return;
+	if(c->fd != -1)
+		if(event_del(&c->ev->ev) != 0) {
+			log_err("could not event_del on close");
+		}
+	/* close fd after removing from event lists, or epoll.. is messed up */
+	if(c->fd != -1 && !c->do_not_close) {
+		verbose(VERB_ALGO, "close fd %d", c->fd);
+#ifndef USE_WINSOCK
+		close(c->fd);
+#else
+		closesocket(c->fd);
+#endif
+	}
+	c->fd = -1;
+}
+
+void 
+comm_point_delete(struct comm_point* c)
+{
+	if(!c) 
+		return;
+	if(c->type == comm_tcp && c->ssl) {
+		SSL_shutdown(c->ssl);
+		SSL_free(c->ssl);
+	}
+	comm_point_close(c);
+	if(c->tcp_handlers) {
+		int i;
+		for(i=0; i<c->max_tcp_count; i++)
+			comm_point_delete(c->tcp_handlers[i]);
+		free(c->tcp_handlers);
+	}
+	free(c->timeout);
+	if(c->type == comm_tcp || c->type == comm_local)
+		ldns_buffer_free(c->buffer);
+	free(c->ev);
+	free(c);
+}
+
+void 
+comm_point_send_reply(struct comm_reply *repinfo)
+{
+	log_assert(repinfo && repinfo->c);
+	if(repinfo->c->type == comm_udp) {
+		if(repinfo->srctype)
+			comm_point_send_udp_msg_if(repinfo->c, 
+			repinfo->c->buffer, (struct sockaddr*)&repinfo->addr, 
+			repinfo->addrlen, repinfo);
+		else
+			comm_point_send_udp_msg(repinfo->c, repinfo->c->buffer,
+			(struct sockaddr*)&repinfo->addr, repinfo->addrlen);
+	} else {
+		comm_point_start_listening(repinfo->c, -1, TCP_QUERY_TIMEOUT);
+	}
+}
+
+void 
+comm_point_drop_reply(struct comm_reply* repinfo)
+{
+	if(!repinfo)
+		return;
+	log_assert(repinfo && repinfo->c);
+	log_assert(repinfo->c->type != comm_tcp_accept);
+	if(repinfo->c->type == comm_udp)
+		return;
+	reclaim_tcp_handler(repinfo->c);
+}
+
+void 
+comm_point_stop_listening(struct comm_point* c)
+{
+	verbose(VERB_ALGO, "comm point stop listening %d", c->fd);
+	if(event_del(&c->ev->ev) != 0) {
+		log_err("event_del error to stoplisten");
+	}
+}
+
+void 
+comm_point_start_listening(struct comm_point* c, int newfd, int sec)
+{
+	verbose(VERB_ALGO, "comm point start listening %d", 
+		c->fd==-1?newfd:c->fd);
+	if(c->type == comm_tcp_accept && !c->tcp_free) {
+		/* no use to start listening no free slots. */
+		return;
+	}
+	if(sec != -1 && sec != 0) {
+		if(!c->timeout) {
+			c->timeout = (struct timeval*)malloc(sizeof(
+				struct timeval));
+			if(!c->timeout) {
+				log_err("cpsl: malloc failed. No net read.");
+				return;
+			}
+		}
+		c->ev->ev.ev_events |= EV_TIMEOUT;
+#ifndef S_SPLINT_S /* splint fails on struct timeval. */
+		c->timeout->tv_sec = sec;
+		c->timeout->tv_usec = 0;
+#endif /* S_SPLINT_S */
+	}
+	if(c->type == comm_tcp) {
+		c->ev->ev.ev_events &= ~(EV_READ|EV_WRITE);
+		if(c->tcp_is_reading)
+			c->ev->ev.ev_events |= EV_READ;
+		else	c->ev->ev.ev_events |= EV_WRITE;
+	}
+	if(newfd != -1) {
+		if(c->fd != -1) {
+#ifndef USE_WINSOCK
+			close(c->fd);
+#else
+			closesocket(c->fd);
+#endif
+		}
+		c->fd = newfd;
+		c->ev->ev.ev_fd = c->fd;
+	}
+	if(event_add(&c->ev->ev, sec==0?NULL:c->timeout) != 0) {
+		log_err("event_add failed. in cpsl.");
+	}
+}
+
+void comm_point_listen_for_rw(struct comm_point* c, int rd, int wr)
+{
+	verbose(VERB_ALGO, "comm point listen_for_rw %d %d", c->fd, wr);
+	if(event_del(&c->ev->ev) != 0) {
+		log_err("event_del error to cplf");
+	}
+	c->ev->ev.ev_events &= ~(EV_READ|EV_WRITE);
+	if(rd) c->ev->ev.ev_events |= EV_READ;
+	if(wr) c->ev->ev.ev_events |= EV_WRITE;
+	if(event_add(&c->ev->ev, c->timeout) != 0) {
+		log_err("event_add failed. in cplf.");
+	}
+}
+
+size_t comm_point_get_mem(struct comm_point* c)
+{
+	size_t s;
+	if(!c) 
+		return 0;
+	s = sizeof(*c) + sizeof(*c->ev);
+	if(c->timeout) 
+		s += sizeof(*c->timeout);
+	if(c->type == comm_tcp || c->type == comm_local)
+		s += sizeof(*c->buffer) + ldns_buffer_capacity(c->buffer);
+	if(c->type == comm_tcp_accept) {
+		int i;
+		for(i=0; i<c->max_tcp_count; i++)
+			s += comm_point_get_mem(c->tcp_handlers[i]);
+	}
+	return s;
+}
+
+struct comm_timer* 
+comm_timer_create(struct comm_base* base, void (*cb)(void*), void* cb_arg)
+{
+	struct comm_timer *tm = (struct comm_timer*)calloc(1,
+		sizeof(struct comm_timer));
+	if(!tm)
+		return NULL;
+	tm->ev_timer = (struct internal_timer*)calloc(1,
+		sizeof(struct internal_timer));
+	if(!tm->ev_timer) {
+		log_err("malloc failed");
+		free(tm);
+		return NULL;
+	}
+	tm->ev_timer->base = base;
+	tm->callback = cb;
+	tm->cb_arg = cb_arg;
+	event_set(&tm->ev_timer->ev, -1, EV_TIMEOUT, 
+		comm_timer_callback, tm);
+	if(event_base_set(base->eb->base, &tm->ev_timer->ev) != 0) {
+		log_err("timer_create: event_base_set failed.");
+		free(tm->ev_timer);
+		free(tm);
+		return NULL;
+	}
+	return tm;
+}
+
+void 
+comm_timer_disable(struct comm_timer* timer)
+{
+	if(!timer)
+		return;
+	evtimer_del(&timer->ev_timer->ev);
+	timer->ev_timer->enabled = 0;
+}
+
+void 
+comm_timer_set(struct comm_timer* timer, struct timeval* tv)
+{
+	log_assert(tv);
+	if(timer->ev_timer->enabled)
+		comm_timer_disable(timer);
+	event_set(&timer->ev_timer->ev, -1, EV_TIMEOUT,
+		comm_timer_callback, timer);
+	if(event_base_set(timer->ev_timer->base->eb->base, 
+		&timer->ev_timer->ev) != 0)
+		log_err("comm_timer_set: set_base failed.");
+	if(evtimer_add(&timer->ev_timer->ev, tv) != 0)
+		log_err("comm_timer_set: evtimer_add failed.");
+	timer->ev_timer->enabled = 1;
+}
+
+void 
+comm_timer_delete(struct comm_timer* timer)
+{
+	if(!timer)
+		return;
+	comm_timer_disable(timer);
+	free(timer->ev_timer);
+	free(timer);
+}
+
+void 
+comm_timer_callback(int ATTR_UNUSED(fd), short event, void* arg)
+{
+	struct comm_timer* tm = (struct comm_timer*)arg;
+	if(!(event&EV_TIMEOUT))
+		return;
+	comm_base_now(tm->ev_timer->base);
+	tm->ev_timer->enabled = 0;
+	fptr_ok(fptr_whitelist_comm_timer(tm->callback));
+	(*tm->callback)(tm->cb_arg);
+}
+
+int 
+comm_timer_is_set(struct comm_timer* timer)
+{
+	return (int)timer->ev_timer->enabled;
+}
+
+size_t 
+comm_timer_get_mem(struct comm_timer* timer)
+{
+	return sizeof(*timer) + sizeof(struct internal_timer);
+}
+
+struct comm_signal* 
+comm_signal_create(struct comm_base* base,
+        void (*callback)(int, void*), void* cb_arg)
+{
+	struct comm_signal* com = (struct comm_signal*)malloc(
+		sizeof(struct comm_signal));
+	if(!com) {
+		log_err("malloc failed");
+		return NULL;
+	}
+	com->base = base;
+	com->callback = callback;
+	com->cb_arg = cb_arg;
+	com->ev_signal = NULL;
+	return com;
+}
+
+void 
+comm_signal_callback(int sig, short event, void* arg)
+{
+	struct comm_signal* comsig = (struct comm_signal*)arg;
+	if(!(event & EV_SIGNAL))
+		return;
+	comm_base_now(comsig->base);
+	fptr_ok(fptr_whitelist_comm_signal(comsig->callback));
+	(*comsig->callback)(sig, comsig->cb_arg);
+}
+
+int 
+comm_signal_bind(struct comm_signal* comsig, int sig)
+{
+	struct internal_signal* entry = (struct internal_signal*)calloc(1, 
+		sizeof(struct internal_signal));
+	if(!entry) {
+		log_err("malloc failed");
+		return 0;
+	}
+	log_assert(comsig);
+	/* add signal event */
+	signal_set(&entry->ev, sig, comm_signal_callback, comsig);
+	if(event_base_set(comsig->base->eb->base, &entry->ev) != 0) {
+		log_err("Could not set signal base");
+		free(entry);
+		return 0;
+	}
+	if(signal_add(&entry->ev, NULL) != 0) {
+		log_err("Could not add signal handler");
+		free(entry);
+		return 0;
+	}
+	/* link into list */
+	entry->next = comsig->ev_signal;
+	comsig->ev_signal = entry;
+	return 1;
+}
+
+void 
+comm_signal_delete(struct comm_signal* comsig)
+{
+	struct internal_signal* p, *np;
+	if(!comsig)
+		return;
+	p=comsig->ev_signal;
+	while(p) {
+		np = p->next;
+		signal_del(&p->ev);
+		free(p);
+		p = np;
+	}
+	free(comsig);
+}
diff --git a/3rdParty/Unbound/src/src/util/netevent.h b/3rdParty/Unbound/src/src/util/netevent.h
new file mode 100644
index 0000000..8ce62e7
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/netevent.h
@@ -0,0 +1,649 @@
+/*
+ * util/netevent.h - event notification
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains event notification functions.
+ *
+ * There are three types of communication points
+ *    o UDP socket - perthread buffer.
+ *    o TCP-accept socket - array of TCP-sockets, socketcount.
+ *    o TCP socket - own buffer, parent-TCPaccept, read/write state,
+ *                   number of bytes read/written, timeout.
+ *
+ * There are sockets aimed towards our clients and towards the internet.
+ *    o frontside - aimed towards our clients, queries come in, answers back.
+ *    o behind - aimed towards internet, to the authoritative DNS servers.
+ *
+ * Several event types are available:
+ *    o comm_base - for thread safety of the comm points, one per thread.
+ *    o comm_point - udp and tcp networking, with callbacks.
+ *    o comm_timer - a timeout with callback.
+ *    o comm_signal - callbacks when signal is caught.
+ *    o comm_reply - holds reply info during networking callback.
+ *
+ */
+
+#ifndef NET_EVENT_H
+#define NET_EVENT_H
+
+#include <ldns/buffer.h>
+struct comm_point;
+struct comm_reply;
+struct event_base;
+
+/* internal event notification data storage structure. */
+struct internal_event;
+struct internal_base;
+struct internal_timer;
+
+/** callback from communication point function type */
+typedef int comm_point_callback_t(struct comm_point*, void*, int, 
+	struct comm_reply*);
+
+/** to pass no_error to callback function */
+#define NETEVENT_NOERROR 0
+/** to pass closed connection to callback function */
+#define NETEVENT_CLOSED -1
+/** to pass timeout happened to callback function */
+#define NETEVENT_TIMEOUT -2 
+/** to pass fallback from capsforID to callback function; 0x20 failed */
+#define NETEVENT_CAPSFAIL -3
+
+/**
+ * A communication point dispatcher. Thread specific.
+ */
+struct comm_base {
+	/** behind the scenes structure. with say libevent info. alloced */
+	struct internal_base* eb;
+};
+
+/**
+ * Reply information for a communication point.
+ */
+struct comm_reply {
+	/** the comm_point with fd to send reply on to. */
+	struct comm_point* c;
+	/** the address (for UDP based communication) */
+	struct sockaddr_storage addr;
+	/** length of address */
+	socklen_t addrlen;
+	/** return type 0 (none), 4(IP4), 6(IP6) */
+	int srctype;
+	/** the return source interface data */
+	union {
+#ifdef IPV6_PKTINFO
+		struct in6_pktinfo v6info;
+#endif
+#ifdef IP_PKTINFO
+		struct in_pktinfo v4info;
+#elif defined(IP_RECVDSTADDR)
+		struct in_addr v4addr;
+#endif
+	} 	
+		/** variable with return source data */
+		pktinfo;
+};
+
+/** 
+ * Communication point to the network 
+ * These behaviours can be accomplished by setting the flags
+ * and passing return values from the callback.
+ *    udp frontside: called after readdone. sendafter.
+ *    tcp frontside: called readdone, sendafter. close.
+ *    udp behind: called after readdone. No send after.
+ *    tcp behind: write done, read done, then called. No send after.
+ */
+struct comm_point {
+	/** behind the scenes structure, with say libevent info. alloced. */
+	struct internal_event* ev;
+
+	/** file descriptor for communication point */
+	int fd;
+
+	/** timeout (NULL if it does not). Malloced. */
+	struct timeval* timeout;
+
+	/** buffer pointer. Either to perthread, or own buffer or NULL */
+	ldns_buffer* buffer;
+
+	/* -------- TCP Handler -------- */
+	/** Read/Write state for TCP */
+	int tcp_is_reading;
+	/** The current read/write count for TCP */
+	size_t tcp_byte_count;
+	/** parent communication point (for TCP sockets) */
+	struct comm_point* tcp_parent;
+	/** sockaddr from peer, for TCP handlers */
+	struct comm_reply repinfo;
+
+	/* -------- TCP Accept -------- */
+	/** the number of TCP handlers for this tcp-accept socket */
+	int max_tcp_count;
+	/** malloced array of tcp handlers for a tcp-accept, 
+	    of size max_tcp_count. */
+	struct comm_point** tcp_handlers;
+	/** linked list of free tcp_handlers to use for new queries.
+	    For tcp_accept the first entry, for tcp_handlers the next one. */
+	struct comm_point* tcp_free;
+
+	/* -------- SSL TCP DNS ------- */
+	/** the SSL object with rw bio (owned) or for commaccept ctx ref */
+	void* ssl;
+	/** handshake state for init and renegotiate */
+	enum {
+		/** no handshake, it has been done */
+		comm_ssl_shake_none = 0,
+		/** ssl initial handshake wants to read */
+		comm_ssl_shake_read,
+		/** ssl initial handshake wants to write */
+		comm_ssl_shake_write,
+		/** ssl_write wants to read */
+		comm_ssl_shake_hs_read,
+		/** ssl_read wants to write */
+		comm_ssl_shake_hs_write
+	} ssl_shake_state;
+
+	/** is this a UDP, TCP-accept or TCP socket. */
+	enum comm_point_type {
+		/** UDP socket - handle datagrams. */
+		comm_udp, 
+		/** TCP accept socket - only creates handlers if readable. */
+		comm_tcp_accept, 
+		/** TCP handler socket - handle byteperbyte readwrite. */
+		comm_tcp,
+		/** AF_UNIX socket - for internal commands. */
+		comm_local,
+		/** raw - not DNS format - for pipe readers and writers */
+		comm_raw
+	} 
+		/** variable with type of socket, UDP,TCP-accept,TCP,pipe */
+		type;
+
+	/* ---------- Behaviour ----------- */
+	/** if set the connection is NOT closed on delete. */
+	int do_not_close;
+
+	/** if set, the connection is closed on error, on timeout, 
+	    and after read/write completes. No callback is done. */
+	int tcp_do_close;
+
+	/** if set, read/write completes:
+		read/write state of tcp is toggled.
+		buffer reset/bytecount reset.
+		this flag cleared.
+	    So that when that is done the callback is called. */
+	int tcp_do_toggle_rw;
+
+	/** if set, checks for pending error from nonblocking connect() call.*/
+	int tcp_check_nb_connect;
+
+	/** number of queries outstanding on this socket, used by
+	 * outside network for udp ports */
+	int inuse;
+
+	/** callback when done.
+	    tcp_accept does not get called back, is NULL then.
+	    If a timeout happens, callback with timeout=1 is called.
+	    If an error happens, callback is called with error set 
+	    nonzero. If not NETEVENT_NOERROR, it is an errno value.
+	    If the connection is closed (by remote end) then the
+	    callback is called with error set to NETEVENT_CLOSED=-1.
+	    If a timeout happens on the connection, the error is set to 
+	    NETEVENT_TIMEOUT=-2.
+	    The reply_info can be copied if the reply needs to happen at a
+	    later time. It consists of a struct with commpoint and address.
+	    It can be passed to a msg send routine some time later.
+	    Note the reply information is temporary and must be copied.
+	    NULL is passed for_reply info, in cases where error happened.
+
+	    declare as: 
+	    int my_callback(struct comm_point* c, void* my_arg, int error,
+		struct comm_reply *reply_info);
+
+	    if the routine returns 0, nothing is done.
+	    Notzero, the buffer will be sent back to client.
+	    		For UDP this is done without changing the commpoint.
+			In TCP it sets write state.
+	*/
+	comm_point_callback_t* callback;
+	/** argument to pass to callback. */
+	void *cb_arg;
+};
+
+/**
+ * Structure only for making timeout events.
+ */
+struct comm_timer {
+	/** the internal event stuff */
+	struct internal_timer* ev_timer;
+
+	/** callback function, takes user arg only */
+	void (*callback)(void*);
+
+	/** callback user argument */
+	void* cb_arg;
+};
+
+/**
+ * Structure only for signal events.
+ */
+struct comm_signal {
+	/** the communication base */
+	struct comm_base* base;
+
+	/** the internal event stuff */
+	struct internal_signal* ev_signal;
+
+	/** callback function, takes signal number and user arg */
+	void (*callback)(int, void*);
+
+	/** callback user argument */
+	void* cb_arg;
+};
+
+/**
+ * Create a new comm base.
+ * @param sigs: if true it attempts to create a default loop for 
+ *   signal handling.
+ * @return: the new comm base. NULL on error.
+ */
+struct comm_base* comm_base_create(int sigs);
+
+/**
+ * Destroy a comm base.
+ * All comm points must have been deleted.
+ * @param b: the base to delete.
+ */
+void comm_base_delete(struct comm_base* b);
+
+/**
+ * Obtain two pointers. The pointers never change (until base_delete()).
+ * The pointers point to time values that are updated regularly.
+ * @param b: the communication base that will update the time values.
+ * @param tt: pointer to time in seconds is returned.
+ * @param tv: pointer to time in microseconds is returned.
+ */
+void comm_base_timept(struct comm_base* b, uint32_t** tt, struct timeval** tv);
+
+/**
+ * Dispatch the comm base events.
+ * @param b: the communication to perform.
+ */
+void comm_base_dispatch(struct comm_base* b);
+
+/**
+ * Exit from dispatch loop.
+ * @param b: the communication base that is in dispatch().
+ */
+void comm_base_exit(struct comm_base* b);
+
+/**
+ * Access internal data structure (for util/tube.c on windows)
+ * @param b: comm base
+ * @return event_base. Could be libevent, or internal event handler.
+ */
+struct event_base* comm_base_internal(struct comm_base* b);
+
+/**
+ * Create an UDP comm point. Calls malloc.
+ * setups the structure with the parameters you provide.
+ * @param base: in which base to alloc the commpoint.
+ * @param fd : file descriptor of open UDP socket.
+ * @param buffer: shared buffer by UDP sockets from this thread.
+ * @param callback: callback function pointer.
+ * @param callback_arg: will be passed to your callback function.
+ * @return: returns the allocated communication point. NULL on error.
+ * Sets timeout to NULL. Turns off TCP options.
+ */
+struct comm_point* comm_point_create_udp(struct comm_base* base,
+	int fd, ldns_buffer* buffer, 
+	comm_point_callback_t* callback, void* callback_arg);
+
+/**
+ * Create an UDP with ancillary data comm point. Calls malloc.
+ * Uses recvmsg instead of recv to get udp message.
+ * setups the structure with the parameters you provide.
+ * @param base: in which base to alloc the commpoint.
+ * @param fd : file descriptor of open UDP socket.
+ * @param buffer: shared buffer by UDP sockets from this thread.
+ * @param callback: callback function pointer.
+ * @param callback_arg: will be passed to your callback function.
+ * @return: returns the allocated communication point. NULL on error.
+ * Sets timeout to NULL. Turns off TCP options.
+ */
+struct comm_point* comm_point_create_udp_ancil(struct comm_base* base,
+	int fd, ldns_buffer* buffer, 
+	comm_point_callback_t* callback, void* callback_arg);
+
+/**
+ * Create a TCP listener comm point. Calls malloc.
+ * Setups the structure with the parameters you provide.
+ * Also Creates TCP Handlers, pre allocated for you.
+ * Uses the parameters you provide.
+ * @param base: in which base to alloc the commpoint.
+ * @param fd: file descriptor of open TCP socket set to listen nonblocking.
+ * @param num: becomes max_tcp_count, the routine allocates that
+ *	many tcp handler commpoints.
+ * @param bufsize: size of buffer to create for handlers.
+ * @param callback: callback function pointer for TCP handlers.
+ * @param callback_arg: will be passed to your callback function.
+ * @return: returns the TCP listener commpoint. You can find the
+ *  	TCP handlers in the array inside the listener commpoint.
+ *	returns NULL on error.
+ * Inits timeout to NULL. All handlers are on the free list.
+ */
+struct comm_point* comm_point_create_tcp(struct comm_base* base,
+	int fd, int num, size_t bufsize, 
+	comm_point_callback_t* callback, void* callback_arg);
+
+/**
+ * Create an outgoing TCP commpoint. No file descriptor is opened, left at -1.
+ * @param base: in which base to alloc the commpoint.
+ * @param bufsize: size of buffer to create for handlers.
+ * @param callback: callback function pointer for the handler.
+ * @param callback_arg: will be passed to your callback function.
+ * @return: the commpoint or NULL on error.
+ */
+struct comm_point* comm_point_create_tcp_out(struct comm_base* base,
+	size_t bufsize, comm_point_callback_t* callback, void* callback_arg);
+
+/**
+ * Create commpoint to listen to a local domain file descriptor.
+ * @param base: in which base to alloc the commpoint.
+ * @param fd: file descriptor of open AF_UNIX socket set to listen nonblocking.
+ * @param bufsize: size of buffer to create for handlers.
+ * @param callback: callback function pointer for the handler.
+ * @param callback_arg: will be passed to your callback function.
+ * @return: the commpoint or NULL on error.
+ */
+struct comm_point* comm_point_create_local(struct comm_base* base,
+	int fd, size_t bufsize, 
+	comm_point_callback_t* callback, void* callback_arg);
+
+/**
+ * Create commpoint to listen to a local domain pipe descriptor.
+ * @param base: in which base to alloc the commpoint.
+ * @param fd: file descriptor.
+ * @param writing: true if you want to listen to writes, false for reads.
+ * @param callback: callback function pointer for the handler.
+ * @param callback_arg: will be passed to your callback function.
+ * @return: the commpoint or NULL on error.
+ */
+struct comm_point* comm_point_create_raw(struct comm_base* base,
+	int fd, int writing, 
+	comm_point_callback_t* callback, void* callback_arg);
+
+/**
+ * Close a comm point fd.
+ * @param c: comm point to close.
+ */
+void comm_point_close(struct comm_point* c);
+
+/**
+ * Close and deallocate (free) the comm point. If the comm point is
+ * a tcp-accept point, also its tcp-handler points are deleted.
+ * @param c: comm point to delete.
+ */
+void comm_point_delete(struct comm_point* c);
+
+/**
+ * Send reply. Put message into commpoint buffer.
+ * @param repinfo: The reply info copied from a commpoint callback call.
+ */
+void comm_point_send_reply(struct comm_reply* repinfo);
+
+/**
+ * Drop reply. Cleans up.
+ * @param repinfo: The reply info copied from a commpoint callback call.
+ */
+void comm_point_drop_reply(struct comm_reply* repinfo);
+
+/**
+ * Send an udp message over a commpoint.
+ * @param c: commpoint to send it from.
+ * @param packet: what to send.
+ * @param addr: where to send it to.
+ * @param addrlen: length of addr.
+ * @return: false on a failure.
+ */
+int comm_point_send_udp_msg(struct comm_point* c, ldns_buffer* packet,
+	struct sockaddr* addr, socklen_t addrlen);
+
+/**
+ * Stop listening for input on the commpoint. No callbacks will happen.
+ * @param c: commpoint to disable. The fd is not closed.
+ */
+void comm_point_stop_listening(struct comm_point* c);
+
+/**
+ * Start listening again for input on the comm point.
+ * @param c: commpoint to enable again.
+ * @param newfd: new fd, or -1 to leave fd be.
+ * @param sec: timeout in seconds, or -1 for no (change to the) timeout.
+ */
+void comm_point_start_listening(struct comm_point* c, int newfd, int sec);
+
+/**
+ * Stop listening and start listening again for reading or writing.
+ * @param c: commpoint
+ * @param rd: if true, listens for reading.
+ * @param wr: if true, listens for writing.
+ */
+void comm_point_listen_for_rw(struct comm_point* c, int rd, int wr);
+
+/**
+ * Get size of memory used by comm point.
+ * For TCP handlers this includes subhandlers.
+ * For UDP handlers, this does not include the (shared) UDP buffer.
+ * @param c: commpoint.
+ * @return size in bytes.
+ */
+size_t comm_point_get_mem(struct comm_point* c);
+
+/**
+ * create timer. Not active upon creation.
+ * @param base: event handling base.
+ * @param cb: callback function: void myfunc(void* myarg);
+ * @param cb_arg: user callback argument.
+ * @return: the new timer or NULL on error.
+ */
+struct comm_timer* comm_timer_create(struct comm_base* base, 
+	void (*cb)(void*), void* cb_arg);
+
+/**
+ * disable timer. Stops callbacks from happening.
+ * @param timer: to disable.
+ */
+void comm_timer_disable(struct comm_timer* timer);
+
+/**
+ * reset timevalue for timer.
+ * @param timer: timer to (re)set.
+ * @param tv: when the timer should activate. if NULL timer is disabled.
+ */
+void comm_timer_set(struct comm_timer* timer, struct timeval* tv);
+
+/**
+ * delete timer.
+ * @param timer: to delete.
+ */
+void comm_timer_delete(struct comm_timer* timer);
+
+/**
+ * see if timeout has been set to a value.
+ * @param timer: the timer to examine.
+ * @return: false if disabled or not set.
+ */
+int comm_timer_is_set(struct comm_timer* timer);
+
+/**
+ * Get size of memory used by comm timer.
+ * @param timer: the timer to examine.
+ * @return size in bytes.
+ */
+size_t comm_timer_get_mem(struct comm_timer* timer);
+
+/**
+ * Create a signal handler. Call signal_bind() later to bind to a signal.
+ * @param base: communication base to use.
+ * @param callback: called when signal is caught.
+ * @param cb_arg: user argument to callback
+ * @return: the signal struct or NULL on error.
+ */
+struct comm_signal* comm_signal_create(struct comm_base* base,
+	void (*callback)(int, void*), void* cb_arg);
+
+/**
+ * Bind signal struct to catch a signal. A signle comm_signal can be bound
+ * to multiple signals, calling comm_signal_bind multiple times.
+ * @param comsig: the communication point, with callback information.
+ * @param sig: signal number.
+ * @return: true on success. false on error.
+ */
+int comm_signal_bind(struct comm_signal* comsig, int sig);
+
+/**
+ * Delete the signal communication point.
+ * @param comsig: to delete.
+ */
+void comm_signal_delete(struct comm_signal* comsig);
+
+/**
+ * perform accept(2) with error checking.
+ * @param c: commpoint with accept fd.
+ * @param addr: remote end returned here.
+ * @param addrlen: length of remote end returned here.
+ * @return new fd, or -1 on error.
+ *	if -1, error message has been printed if necessary, simply drop
+ *	out of the reading handler.
+ */
+int comm_point_perform_accept(struct comm_point* c, 
+	struct sockaddr_storage* addr, socklen_t* addrlen);
+
+/**** internal routines ****/
+
+/**
+ * This routine is published for checks and tests, and is only used internally.
+ * handle libevent callback for udp comm point.
+ * @param fd: file descriptor.
+ * @param event: event bits from libevent: 
+ *	EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT.
+ * @param arg: the comm_point structure.
+ */
+void comm_point_udp_callback(int fd, short event, void* arg);
+
+/**
+ * This routine is published for checks and tests, and is only used internally.
+ * handle libevent callback for udp ancillary data comm point.
+ * @param fd: file descriptor.
+ * @param event: event bits from libevent: 
+ *	EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT.
+ * @param arg: the comm_point structure.
+ */
+void comm_point_udp_ancil_callback(int fd, short event, void* arg);
+
+/**
+ * This routine is published for checks and tests, and is only used internally.
+ * handle libevent callback for tcp accept comm point
+ * @param fd: file descriptor.
+ * @param event: event bits from libevent: 
+ *	EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT.
+ * @param arg: the comm_point structure.
+ */
+void comm_point_tcp_accept_callback(int fd, short event, void* arg);
+
+/**
+ * This routine is published for checks and tests, and is only used internally.
+ * handle libevent callback for tcp data comm point
+ * @param fd: file descriptor.
+ * @param event: event bits from libevent: 
+ *	EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT.
+ * @param arg: the comm_point structure.
+ */
+void comm_point_tcp_handle_callback(int fd, short event, void* arg);
+
+/**
+ * This routine is published for checks and tests, and is only used internally.
+ * handle libevent callback for timer comm.
+ * @param fd: file descriptor (always -1).
+ * @param event: event bits from libevent: 
+ *	EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT.
+ * @param arg: the comm_timer structure.
+ */
+void comm_timer_callback(int fd, short event, void* arg);
+
+/**
+ * This routine is published for checks and tests, and is only used internally.
+ * handle libevent callback for signal comm.
+ * @param fd: file descriptor (used for the signal number).
+ * @param event: event bits from libevent: 
+ *	EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT.
+ * @param arg: the internal commsignal structure.
+ */
+void comm_signal_callback(int fd, short event, void* arg);
+
+/**
+ * This routine is published for checks and tests, and is only used internally.
+ * libevent callback for AF_UNIX fds
+ * @param fd: file descriptor.
+ * @param event: event bits from libevent: 
+ *	EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT.
+ * @param arg: the comm_point structure.
+ */
+void comm_point_local_handle_callback(int fd, short event, void* arg);
+
+/**
+ * This routine is published for checks and tests, and is only used internally.
+ * libevent callback for raw fd access.
+ * @param fd: file descriptor.
+ * @param event: event bits from libevent: 
+ *	EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT.
+ * @param arg: the comm_point structure.
+ */
+void comm_point_raw_handle_callback(int fd, short event, void* arg);
+
+#ifdef USE_WINSOCK
+/**
+ * Callback for openssl BIO to on windows detect WSAEWOULDBLOCK and notify
+ * the winsock_event of this for proper TCP nonblocking implementation.
+ * @param c: comm_point, fd must be set its struct event is registered.
+ * @param ssl: openssl SSL, fd must be set so it has a bio.
+ */
+void comm_point_tcp_win_bio_cb(struct comm_point* c, void* ssl);
+#endif
+
+#endif /* NET_EVENT_H */
diff --git a/3rdParty/Unbound/src/src/util/random.c b/3rdParty/Unbound/src/src/util/random.c
new file mode 100644
index 0000000..1458104
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/random.c
@@ -0,0 +1,198 @@
+/*
+ * util/random.c - thread safe random generator, which is reasonably secure.
+ * 
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ * 
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ * Thread safe random functions. Similar to arc4random() with an explicit
+ * initialisation routine.
+ *
+ * The code in this file is based on arc4random from
+ * openssh-4.0p1/openbsd-compat/bsd-arc4random.c
+ * That code is also BSD licensed. Here is their statement:
+ *
+ * Copyright (c) 1996, David Mazieres <dm@uun.org>
+ * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include "config.h"
+#include "util/random.h"
+#include "util/log.h"
+#include <openssl/rand.h>
+#include <openssl/rc4.h>
+#include <openssl/err.h>
+
+/**
+ * Struct with per-thread random state.
+ * Keeps SSL types away from the header file.
+ */
+struct ub_randstate {
+	/** key used for arc4random generation */
+	RC4_KEY rc4;
+	/** keeps track of key usage */
+	int rc4_ready;
+};
+
+/** Size of key to use */
+#define SEED_SIZE 20
+
+/** 
+ * Max random value.  Similar to RAND_MAX, but more portable
+ * (mingw uses only 15 bits random).
+ */
+#define MAX_VALUE 0x7fffffff
+
+/** Number of bytes to reseed after */
+#define REKEY_BYTES	(1 << 24)
+
+/* (re)setup system seed */
+void
+ub_systemseed(unsigned int seed)
+{
+	/* RAND_ is threadsafe, by the way */
+	if(!RAND_status()) {
+		/* try to seed it */
+		unsigned char buf[256];
+		unsigned int v = seed;
+		size_t i;
+		for(i=0; i<256/sizeof(seed); i++) {
+			memmove(buf+i*sizeof(seed), &v, sizeof(seed));
+			v = v*seed + (unsigned int)i;
+		}
+		RAND_seed(buf, 256);
+		if(!RAND_status()) {
+			log_err("Random generator has no entropy "
+				"(error %ld)", ERR_get_error());
+		} else {
+			verbose(VERB_OPS, "openssl has no entropy, "
+				"seeding with time and pid");
+		}
+	}
+}
+
+/** reseed random generator */
+static void
+ub_arc4random_stir(struct ub_randstate* s, struct ub_randstate* from)
+{
+	unsigned char rand_buf[SEED_SIZE];
+	int i;
+
+	memset(&s->rc4, 0, sizeof(s->rc4));
+	memset(rand_buf, 0xc, sizeof(rand_buf));
+	if (from) {
+		for(i=0; i<SEED_SIZE; i++)
+			rand_buf[i] = (unsigned char)ub_random(from);
+	} else {
+		if(!RAND_status())
+			ub_systemseed((unsigned)getpid()^(unsigned)time(NULL));
+		if (RAND_bytes(rand_buf, (int)sizeof(rand_buf)) <= 0) {
+			/* very unlikely that this happens, since we seeded
+			 * above, if it does; complain and keep going */
+			log_err("Couldn't obtain random bytes (error %ld)",
+				    ERR_get_error());
+			s->rc4_ready = 256;
+			return;
+		}
+	}
+	RC4_set_key(&s->rc4, SEED_SIZE, rand_buf);
+
+	/*
+	 * Discard early keystream, as per recommendations in:
+	 * http://www.wisdom.weizmann.ac.il/~itsik/RC4/Papers/Rc4_ksa.ps
+	 */
+	for(i = 0; i <= 256; i += sizeof(rand_buf))
+		RC4(&s->rc4, sizeof(rand_buf), rand_buf, rand_buf);
+
+	memset(rand_buf, 0, sizeof(rand_buf));
+
+	s->rc4_ready = REKEY_BYTES;
+}
+
+struct ub_randstate* 
+ub_initstate(unsigned int seed, struct ub_randstate* from)
+{
+	struct ub_randstate* s = (struct ub_randstate*)calloc(1, sizeof(*s));
+	if(!s) {
+		log_err("malloc failure in random init");
+		return NULL;
+	}
+	ub_systemseed(seed);
+	ub_arc4random_stir(s, from);
+	return s;
+}
+
+long int 
+ub_random(struct ub_randstate* s)
+{
+	unsigned int r = 0;
+	if (s->rc4_ready <= 0) {
+		ub_arc4random_stir(s, NULL);
+	}
+
+	RC4(&s->rc4, sizeof(r), 
+		(unsigned char *)&r, (unsigned char *)&r);
+	s->rc4_ready -= sizeof(r);
+	return (long int)((r) % (((unsigned)MAX_VALUE + 1)));
+}
+
+long int
+ub_random_max(struct ub_randstate* state, long int x)
+{
+	/* make sure we fetch in a range that is divisible by x. ignore
+	 * values from d .. MAX_VALUE, instead draw a new number */
+	long int d = MAX_VALUE - (MAX_VALUE % x); /* d is divisible by x */
+	long int v = ub_random(state);
+	while(d <= v)
+		v = ub_random(state);
+	return (v % x);
+}
+
+void 
+ub_randfree(struct ub_randstate* s)
+{
+	if(s)
+		free(s);
+	/* user app must do RAND_cleanup(); */
+}
diff --git a/3rdParty/Unbound/src/src/util/random.h b/3rdParty/Unbound/src/src/util/random.h
new file mode 100644
index 0000000..99879dc
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/random.h
@@ -0,0 +1,93 @@
+/*
+ * util/random.h - thread safe random generator, which is reasonably secure.
+ * 
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ * 
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+#ifndef UTIL_RANDOM_H
+#define UTIL_RANDOM_H
+
+/**
+ * \file
+ * Thread safe random functions. Similar to arc4random() with an explicit
+ * initialisation routine.
+ */
+
+/**
+ * random state structure.
+ */
+struct ub_randstate;
+
+/**
+ * Initialize the system randomness.  Obtains entropy from the system
+ * before a chroot or privilege makes it unavailable. 
+ * You do not have to call this, otherwise ub_initstate does so.
+ * @param seed: seed value to create state (if no good entropy is found).
+ */
+void ub_systemseed(unsigned int seed);
+
+/**
+ * Initialize a random generator state for use 
+ * @param seed: seed value to create state contents.
+ *	(ignored for arc4random).
+ * @param from: if not NULL, the seed is taken from this random structure.
+ * 	can be used to seed random states via a parent-random-state that
+ * 	is itself seeded with entropy.
+ * @return new state or NULL alloc failure.
+ */
+struct ub_randstate* ub_initstate(unsigned int seed, 
+	struct ub_randstate* from);
+
+/**
+ * Generate next random number from the state passed along.
+ * Thread safe, so random numbers are repeatable.
+ * @param state: must have been initialised with ub_initstate.
+ * @return: random 31 bit value.
+ */
+long int ub_random(struct ub_randstate* state);
+
+/**
+ * Generate random number between 0 and x-1.  No modulo bias.
+ * @param state: must have been initialised with ub_initstate.
+ * @param x: an upper limit. not (negative or zero). must be smaller than 2**31.
+ * @return: random value between 0..x-1. Possibly more than one
+ * random number is picked from the random stream to satisfy this.
+ */
+long int ub_random_max(struct ub_randstate* state, long int x);
+
+/**
+ * Delete the random state.
+ * @param state: to delete.
+ */
+void ub_randfree(struct ub_randstate* state);
+
+#endif /* UTIL_RANDOM_H */
diff --git a/3rdParty/Unbound/src/src/util/rbtree.c b/3rdParty/Unbound/src/src/util/rbtree.c
new file mode 100644
index 0000000..d31afd9
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/rbtree.c
@@ -0,0 +1,620 @@
+/*
+ * rbtree.c -- generic red black tree
+ *
+ * Copyright (c) 2001-2007, NLnet Labs. All rights reserved.
+ * 
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ *
+ */
+
+/**
+ * \file
+ * Implementation of a redblack tree.
+ */
+
+#include "config.h"
+#include "log.h"
+#include "fptr_wlist.h"
+#include "util/rbtree.h"
+
+/** Node colour black */
+#define	BLACK	0
+/** Node colour red */
+#define	RED	1
+
+/** the NULL node, global alloc */
+rbnode_t	rbtree_null_node = {
+	RBTREE_NULL,		/* Parent.  */
+	RBTREE_NULL,		/* Left.  */
+	RBTREE_NULL,		/* Right.  */
+	NULL,			/* Key.  */
+	BLACK			/* Color.  */
+};
+
+/** rotate subtree left (to preserve redblack property) */
+static void rbtree_rotate_left(rbtree_t *rbtree, rbnode_t *node);
+/** rotate subtree right (to preserve redblack property) */
+static void rbtree_rotate_right(rbtree_t *rbtree, rbnode_t *node);
+/** Fixup node colours when insert happened */
+static void rbtree_insert_fixup(rbtree_t *rbtree, rbnode_t *node);
+/** Fixup node colours when delete happened */
+static void rbtree_delete_fixup(rbtree_t* rbtree, rbnode_t* child, rbnode_t* child_parent);
+
+/*
+ * Creates a new red black tree, intializes and returns a pointer to it.
+ *
+ * Return NULL on failure.
+ *
+ */
+rbtree_t *
+rbtree_create (int (*cmpf)(const void *, const void *))
+{
+	rbtree_t *rbtree;
+
+	/* Allocate memory for it */
+	rbtree = (rbtree_t *) malloc(sizeof(rbtree_t));
+	if (!rbtree) {
+		return NULL;
+	}
+
+	/* Initialize it */
+	rbtree_init(rbtree, cmpf);
+
+	return rbtree;
+}
+
+void 
+rbtree_init(rbtree_t *rbtree, int (*cmpf)(const void *, const void *))
+{
+	/* Initialize it */
+	rbtree->root = RBTREE_NULL;
+	rbtree->count = 0;
+	rbtree->cmp = cmpf;
+}
+
+/*
+ * Rotates the node to the left.
+ *
+ */
+static void
+rbtree_rotate_left(rbtree_t *rbtree, rbnode_t *node)
+{
+	rbnode_t *right = node->right;
+	node->right = right->left;
+	if (right->left != RBTREE_NULL)
+		right->left->parent = node;
+
+	right->parent = node->parent;
+
+	if (node->parent != RBTREE_NULL) {
+		if (node == node->parent->left) {
+			node->parent->left = right;
+		} else  {
+			node->parent->right = right;
+		}
+	} else {
+		rbtree->root = right;
+	}
+	right->left = node;
+	node->parent = right;
+}
+
+/*
+ * Rotates the node to the right.
+ *
+ */
+static void
+rbtree_rotate_right(rbtree_t *rbtree, rbnode_t *node)
+{
+	rbnode_t *left = node->left;
+	node->left = left->right;
+	if (left->right != RBTREE_NULL)
+		left->right->parent = node;
+
+	left->parent = node->parent;
+
+	if (node->parent != RBTREE_NULL) {
+		if (node == node->parent->right) {
+			node->parent->right = left;
+		} else  {
+			node->parent->left = left;
+		}
+	} else {
+		rbtree->root = left;
+	}
+	left->right = node;
+	node->parent = left;
+}
+
+static void
+rbtree_insert_fixup(rbtree_t *rbtree, rbnode_t *node)
+{
+	rbnode_t	*uncle;
+
+	/* While not at the root and need fixing... */
+	while (node != rbtree->root && node->parent->color == RED) {
+		/* If our parent is left child of our grandparent... */
+		if (node->parent == node->parent->parent->left) {
+			uncle = node->parent->parent->right;
+
+			/* If our uncle is red... */
+			if (uncle->color == RED) {
+				/* Paint the parent and the uncle black... */
+				node->parent->color = BLACK;
+				uncle->color = BLACK;
+
+				/* And the grandparent red... */
+				node->parent->parent->color = RED;
+
+				/* And continue fixing the grandparent */
+				node = node->parent->parent;
+			} else {				/* Our uncle is black... */
+				/* Are we the right child? */
+				if (node == node->parent->right) {
+					node = node->parent;
+					rbtree_rotate_left(rbtree, node);
+				}
+				/* Now we're the left child, repaint and rotate... */
+				node->parent->color = BLACK;
+				node->parent->parent->color = RED;
+				rbtree_rotate_right(rbtree, node->parent->parent);
+			}
+		} else {
+			uncle = node->parent->parent->left;
+
+			/* If our uncle is red... */
+			if (uncle->color == RED) {
+				/* Paint the parent and the uncle black... */
+				node->parent->color = BLACK;
+				uncle->color = BLACK;
+
+				/* And the grandparent red... */
+				node->parent->parent->color = RED;
+
+				/* And continue fixing the grandparent */
+				node = node->parent->parent;
+			} else {				/* Our uncle is black... */
+				/* Are we the right child? */
+				if (node == node->parent->left) {
+					node = node->parent;
+					rbtree_rotate_right(rbtree, node);
+				}
+				/* Now we're the right child, repaint and rotate... */
+				node->parent->color = BLACK;
+				node->parent->parent->color = RED;
+				rbtree_rotate_left(rbtree, node->parent->parent);
+			}
+		}
+	}
+	rbtree->root->color = BLACK;
+}
+
+
+/*
+ * Inserts a node into a red black tree.
+ *
+ * Returns NULL on failure or the pointer to the newly added node
+ * otherwise.
+ */
+rbnode_t *
+rbtree_insert (rbtree_t *rbtree, rbnode_t *data)
+{
+	/* XXX Not necessary, but keeps compiler quiet... */
+	int r = 0;
+
+	/* We start at the root of the tree */
+	rbnode_t	*node = rbtree->root;
+	rbnode_t	*parent = RBTREE_NULL;
+
+	fptr_ok(fptr_whitelist_rbtree_cmp(rbtree->cmp));
+	/* Lets find the new parent... */
+	while (node != RBTREE_NULL) {
+		/* Compare two keys, do we have a duplicate? */
+		if ((r = rbtree->cmp(data->key, node->key)) == 0) {
+			return NULL;
+		}
+		parent = node;
+
+		if (r < 0) {
+			node = node->left;
+		} else {
+			node = node->right;
+		}
+	}
+
+	/* Initialize the new node */
+	data->parent = parent;
+	data->left = data->right = RBTREE_NULL;
+	data->color = RED;
+	rbtree->count++;
+
+	/* Insert it into the tree... */
+	if (parent != RBTREE_NULL) {
+		if (r < 0) {
+			parent->left = data;
+		} else {
+			parent->right = data;
+		}
+	} else {
+		rbtree->root = data;
+	}
+
+	/* Fix up the red-black properties... */
+	rbtree_insert_fixup(rbtree, data);
+
+	return data;
+}
+
+/*
+ * Searches the red black tree, returns the data if key is found or NULL otherwise.
+ *
+ */
+rbnode_t *
+rbtree_search (rbtree_t *rbtree, const void *key)
+{
+	rbnode_t *node;
+
+	if (rbtree_find_less_equal(rbtree, key, &node)) {
+		return node;
+	} else {
+		return NULL;
+	}
+}
+
+/** helpers for delete: swap node colours */
+static void swap_int8(uint8_t* x, uint8_t* y) 
+{ 
+	uint8_t t = *x; *x = *y; *y = t; 
+}
+
+/** helpers for delete: swap node pointers */
+static void swap_np(rbnode_t** x, rbnode_t** y) 
+{
+	rbnode_t* t = *x; *x = *y; *y = t; 
+}
+
+/** Update parent pointers of child trees of 'parent' */
+static void change_parent_ptr(rbtree_t* rbtree, rbnode_t* parent, rbnode_t* old, rbnode_t* new)
+{
+	if(parent == RBTREE_NULL)
+	{
+		log_assert(rbtree->root == old);
+		if(rbtree->root == old) rbtree->root = new;
+		return;
+	}
+	log_assert(parent->left == old || parent->right == old
+		|| parent->left == new || parent->right == new);
+	if(parent->left == old) parent->left = new;
+	if(parent->right == old) parent->right = new;
+}
+/** Update parent pointer of a node 'child' */
+static void change_child_ptr(rbnode_t* child, rbnode_t* old, rbnode_t* new)
+{
+	if(child == RBTREE_NULL) return;
+	log_assert(child->parent == old || child->parent == new);
+	if(child->parent == old) child->parent = new;
+}
+
+rbnode_t* 
+rbtree_delete(rbtree_t *rbtree, const void *key)
+{
+	rbnode_t *to_delete;
+	rbnode_t *child;
+	if((to_delete = rbtree_search(rbtree, key)) == 0) return 0;
+	rbtree->count--;
+
+	/* make sure we have at most one non-leaf child */
+	if(to_delete->left != RBTREE_NULL && to_delete->right != RBTREE_NULL)
+	{
+		/* swap with smallest from right subtree (or largest from left) */
+		rbnode_t *smright = to_delete->right;
+		while(smright->left != RBTREE_NULL)
+			smright = smright->left;
+		/* swap the smright and to_delete elements in the tree,
+		 * but the rbnode_t is first part of user data struct
+		 * so cannot just swap the keys and data pointers. Instead
+		 * readjust the pointers left,right,parent */
+
+		/* swap colors - colors are tied to the position in the tree */
+		swap_int8(&to_delete->color, &smright->color);
+
+		/* swap child pointers in parents of smright/to_delete */
+		change_parent_ptr(rbtree, to_delete->parent, to_delete, smright);
+		if(to_delete->right != smright)
+			change_parent_ptr(rbtree, smright->parent, smright, to_delete);
+
+		/* swap parent pointers in children of smright/to_delete */
+		change_child_ptr(smright->left, smright, to_delete);
+		change_child_ptr(smright->left, smright, to_delete);
+		change_child_ptr(smright->right, smright, to_delete);
+		change_child_ptr(smright->right, smright, to_delete);
+		change_child_ptr(to_delete->left, to_delete, smright);
+		if(to_delete->right != smright)
+			change_child_ptr(to_delete->right, to_delete, smright);
+		if(to_delete->right == smright)
+		{
+			/* set up so after swap they work */
+			to_delete->right = to_delete;
+			smright->parent = smright;
+		}
+
+		/* swap pointers in to_delete/smright nodes */
+		swap_np(&to_delete->parent, &smright->parent);
+		swap_np(&to_delete->left, &smright->left);
+		swap_np(&to_delete->right, &smright->right);
+
+		/* now delete to_delete (which is at the location where the smright previously was) */
+	}
+	log_assert(to_delete->left == RBTREE_NULL || to_delete->right == RBTREE_NULL);
+
+	if(to_delete->left != RBTREE_NULL) child = to_delete->left;
+	else child = to_delete->right;
+
+	/* unlink to_delete from the tree, replace to_delete with child */
+	change_parent_ptr(rbtree, to_delete->parent, to_delete, child);
+	change_child_ptr(child, to_delete, to_delete->parent);
+
+	if(to_delete->color == RED)
+	{
+		/* if node is red then the child (black) can be swapped in */
+	}
+	else if(child->color == RED)
+	{
+		/* change child to BLACK, removing a RED node is no problem */
+		if(child!=RBTREE_NULL) child->color = BLACK;
+	}
+	else rbtree_delete_fixup(rbtree, child, to_delete->parent);
+
+	/* unlink completely */
+	to_delete->parent = RBTREE_NULL;
+	to_delete->left = RBTREE_NULL;
+	to_delete->right = RBTREE_NULL;
+	to_delete->color = BLACK;
+	return to_delete;
+}
+
+static void rbtree_delete_fixup(rbtree_t* rbtree, rbnode_t* child, rbnode_t* child_parent)
+{
+	rbnode_t* sibling;
+	int go_up = 1;
+
+	/* determine sibling to the node that is one-black short */
+	if(child_parent->right == child) sibling = child_parent->left;
+	else sibling = child_parent->right;
+
+	while(go_up)
+	{
+		if(child_parent == RBTREE_NULL)
+		{
+			/* removed parent==black from root, every path, so ok */
+			return;
+		}
+
+		if(sibling->color == RED)
+		{	/* rotate to get a black sibling */
+			child_parent->color = RED;
+			sibling->color = BLACK;
+			if(child_parent->right == child)
+				rbtree_rotate_right(rbtree, child_parent);
+			else	rbtree_rotate_left(rbtree, child_parent);
+			/* new sibling after rotation */
+			if(child_parent->right == child) sibling = child_parent->left;
+			else sibling = child_parent->right;
+		}
+
+		if(child_parent->color == BLACK 
+			&& sibling->color == BLACK
+			&& sibling->left->color == BLACK
+			&& sibling->right->color == BLACK)
+		{	/* fixup local with recolor of sibling */
+			if(sibling != RBTREE_NULL)
+				sibling->color = RED;
+
+			child = child_parent;
+			child_parent = child_parent->parent;
+			/* prepare to go up, new sibling */
+			if(child_parent->right == child) sibling = child_parent->left;
+			else sibling = child_parent->right;
+		}
+		else go_up = 0;
+	}
+
+	if(child_parent->color == RED
+		&& sibling->color == BLACK
+		&& sibling->left->color == BLACK
+		&& sibling->right->color == BLACK) 
+	{
+		/* move red to sibling to rebalance */
+		if(sibling != RBTREE_NULL)
+			sibling->color = RED;
+		child_parent->color = BLACK;
+		return;
+	}
+	log_assert(sibling != RBTREE_NULL);
+
+	/* get a new sibling, by rotating at sibling. See which child
+	   of sibling is red */
+	if(child_parent->right == child
+		&& sibling->color == BLACK
+		&& sibling->right->color == RED
+		&& sibling->left->color == BLACK)
+	{
+		sibling->color = RED;
+		sibling->right->color = BLACK;
+		rbtree_rotate_left(rbtree, sibling);
+		/* new sibling after rotation */
+		if(child_parent->right == child) sibling = child_parent->left;
+		else sibling = child_parent->right;
+	}
+	else if(child_parent->left == child
+		&& sibling->color == BLACK
+		&& sibling->left->color == RED
+		&& sibling->right->color == BLACK)
+	{
+		sibling->color = RED;
+		sibling->left->color = BLACK;
+		rbtree_rotate_right(rbtree, sibling);
+		/* new sibling after rotation */
+		if(child_parent->right == child) sibling = child_parent->left;
+		else sibling = child_parent->right;
+	}
+
+	/* now we have a black sibling with a red child. rotate and exchange colors. */
+	sibling->color = child_parent->color;
+	child_parent->color = BLACK;
+	if(child_parent->right == child)
+	{
+		log_assert(sibling->left->color == RED);
+		sibling->left->color = BLACK;
+		rbtree_rotate_right(rbtree, child_parent);
+	}
+	else
+	{
+		log_assert(sibling->right->color == RED);
+		sibling->right->color = BLACK;
+		rbtree_rotate_left(rbtree, child_parent);
+	}
+}
+
+int
+rbtree_find_less_equal(rbtree_t *rbtree, const void *key, rbnode_t **result)
+{
+	int r;
+	rbnode_t *node;
+
+	log_assert(result);
+	
+	/* We start at root... */
+	node = rbtree->root;
+
+	*result = NULL;
+	fptr_ok(fptr_whitelist_rbtree_cmp(rbtree->cmp));
+
+	/* While there are children... */
+	while (node != RBTREE_NULL) {
+		r = rbtree->cmp(key, node->key);
+		if (r == 0) {
+			/* Exact match */
+			*result = node;
+			return 1;
+		} 
+		if (r < 0) {
+			node = node->left;
+		} else {
+			/* Temporary match */
+			*result = node;
+			node = node->right;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Finds the first element in the red black tree
+ *
+ */
+rbnode_t *
+rbtree_first (rbtree_t *rbtree)
+{
+	rbnode_t *node;
+
+	for (node = rbtree->root; node->left != RBTREE_NULL; node = node->left);
+	return node;
+}
+
+rbnode_t *
+rbtree_last (rbtree_t *rbtree)
+{
+	rbnode_t *node;
+
+	for (node = rbtree->root; node->right != RBTREE_NULL; node = node->right);
+	return node;
+}
+
+/*
+ * Returns the next node...
+ *
+ */
+rbnode_t *
+rbtree_next (rbnode_t *node)
+{
+	rbnode_t *parent;
+
+	if (node->right != RBTREE_NULL) {
+		/* One right, then keep on going left... */
+		for (node = node->right; node->left != RBTREE_NULL; node = node->left);
+	} else {
+		parent = node->parent;
+		while (parent != RBTREE_NULL && node == parent->right) {
+			node = parent;
+			parent = parent->parent;
+		}
+		node = parent;
+	}
+	return node;
+}
+
+rbnode_t *
+rbtree_previous(rbnode_t *node)
+{
+	rbnode_t *parent;
+
+	if (node->left != RBTREE_NULL) {
+		/* One left, then keep on going right... */
+		for (node = node->left; node->right != RBTREE_NULL; node = node->right);
+	} else {
+		parent = node->parent;
+		while (parent != RBTREE_NULL && node == parent->left) {
+			node = parent;
+			parent = parent->parent;
+		}
+		node = parent;
+	}
+	return node;
+}
+
+/** recursive descent traverse */
+static void 
+traverse_post(void (*func)(rbnode_t*, void*), void* arg, rbnode_t* node)
+{
+	if(!node || node == RBTREE_NULL)
+		return;
+	/* recurse */
+	traverse_post(func, arg, node->left);
+	traverse_post(func, arg, node->right);
+	/* call user func */
+	(*func)(node, arg);
+}
+
+void 
+traverse_postorder(rbtree_t* tree, void (*func)(rbnode_t*, void*), void* arg)
+{
+	traverse_post(func, arg, tree->root);
+}
diff --git a/3rdParty/Unbound/src/src/util/rbtree.h b/3rdParty/Unbound/src/src/util/rbtree.h
new file mode 100644
index 0000000..879804d
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/rbtree.h
@@ -0,0 +1,192 @@
+/*
+ * rbtree.h -- generic red-black tree
+ *
+ * Copyright (c) 2001-2007, NLnet Labs. All rights reserved.
+ * 
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ *
+ */
+
+/**
+ * \file
+ * Red black tree. Implementation taken from NSD 3.0.5, adjusted for use
+ * in unbound (memory allocation, logging and so on).
+ */
+
+#ifndef UTIL_RBTREE_H_
+#define	UTIL_RBTREE_H_
+
+/**
+ * This structure must be the first member of the data structure in
+ * the rbtree.  This allows easy casting between an rbnode_t and the
+ * user data (poor man's inheritance).
+ */
+typedef struct rbnode_t rbnode_t;
+/**
+ * The rbnode_t struct definition.
+ */
+struct rbnode_t {
+	/** parent in rbtree, RBTREE_NULL for root */
+	rbnode_t   *parent;
+	/** left node (smaller items) */
+	rbnode_t   *left;
+	/** right node (larger items) */
+	rbnode_t   *right;
+	/** pointer to sorting key */
+	const void *key;
+	/** colour of this node */
+	uint8_t	    color;
+};
+
+/** The nullpointer, points to empty node */
+#define	RBTREE_NULL &rbtree_null_node
+/** the global empty node */
+extern	rbnode_t	rbtree_null_node;
+
+/** An entire red black tree */
+typedef struct rbtree_t rbtree_t;
+/** definition for tree struct */
+struct rbtree_t {
+	/** The root of the red-black tree */
+	rbnode_t    *root;
+
+	/** The number of the nodes in the tree */
+	size_t       count;
+
+	/** 
+	 * Key compare function. <0,0,>0 like strcmp. 
+	 * Return 0 on two NULL ptrs. 
+	 */
+	int (*cmp) (const void *, const void *);
+};
+
+/** 
+ * Create new tree (malloced) with given key compare function. 
+ * @param cmpf: compare function (like strcmp) takes pointers to two keys.
+ * @return: new tree, empty.
+ */
+rbtree_t *rbtree_create(int (*cmpf)(const void *, const void *));
+
+/** 
+ * Init a new tree (malloced by caller) with given key compare function. 
+ * @param rbtree: uninitialised memory for new tree, returned empty.
+ * @param cmpf: compare function (like strcmp) takes pointers to two keys.
+ */
+void rbtree_init(rbtree_t *rbtree, int (*cmpf)(const void *, const void *));
+
+/** 
+ * Insert data into the tree. 
+ * @param rbtree: tree to insert to.
+ * @param data: element to insert. 
+ * @return: data ptr or NULL if key already present. 
+ */
+rbnode_t *rbtree_insert(rbtree_t *rbtree, rbnode_t *data);
+
+/**
+ * Delete element from tree.
+ * @param rbtree: tree to delete from.
+ * @param key: key of item to delete.
+ * @return: node that is now unlinked from the tree. User to delete it. 
+ * returns 0 if node not present 
+ */
+rbnode_t *rbtree_delete(rbtree_t *rbtree, const void *key);
+
+/**
+ * Find key in tree. Returns NULL if not found.
+ * @param rbtree: tree to find in.
+ * @param key: key that must match.
+ * @return: node that fits or NULL.
+ */
+rbnode_t *rbtree_search(rbtree_t *rbtree, const void *key);
+
+/**
+ * Find, but match does not have to be exact.
+ * @param rbtree: tree to find in.
+ * @param key: key to find position of.
+ * @param result: set to the exact node if present, otherwise to element that
+ *   precedes the position of key in the tree. NULL if no smaller element.
+ * @return: true if exact match in result. Else result points to <= element,
+ * or NULL if key is smaller than the smallest key. 
+ */
+int rbtree_find_less_equal(rbtree_t *rbtree, const void *key, 
+	rbnode_t **result);
+
+/**
+ * Returns first (smallest) node in the tree
+ * @param rbtree: tree
+ * @return: smallest element or NULL if tree empty.
+ */
+rbnode_t *rbtree_first(rbtree_t *rbtree);
+
+/**
+ * Returns last (largest) node in the tree
+ * @param rbtree: tree
+ * @return: largest element or NULL if tree empty.
+ */
+rbnode_t *rbtree_last(rbtree_t *rbtree);
+
+/**
+ * Returns next larger node in the tree
+ * @param rbtree: tree
+ * @return: next larger element or NULL if no larger in tree.
+ */
+rbnode_t *rbtree_next(rbnode_t *rbtree);
+
+/**
+ * Returns previous smaller node in the tree
+ * @param rbtree: tree
+ * @return: previous smaller element or NULL if no previous in tree.
+ */
+rbnode_t *rbtree_previous(rbnode_t *rbtree);
+
+/**
+ * Call with node=variable of struct* with rbnode_t as first element.
+ * with type is the type of a pointer to that struct. 
+ */
+#define RBTREE_FOR(node, type, rbtree) \
+	for(node=(type)rbtree_first(rbtree); \
+		(rbnode_t*)node != RBTREE_NULL; \
+		node = (type)rbtree_next((rbnode_t*)node))
+
+/**
+ * Call function for all elements in the redblack tree, such that
+ * leaf elements are called before parent elements. So that all
+ * elements can be safely free()d.
+ * Note that your function must not remove the nodes from the tree.
+ * Since that may trigger rebalances of the rbtree.
+ * @param tree: the tree
+ * @param func: function called with element and user arg.
+ * 	The function must not alter the rbtree.
+ * @param arg: user argument.
+ */
+void traverse_postorder(rbtree_t* tree, void (*func)(rbnode_t*, void*),
+	void* arg);
+
+#endif /* UTIL_RBTREE_H_ */
diff --git a/3rdParty/Unbound/src/src/util/regional.c b/3rdParty/Unbound/src/src/util/regional.c
new file mode 100644
index 0000000..8b1fcdb
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/regional.c
@@ -0,0 +1,223 @@
+/*
+ * regional.c -- region based memory allocator.
+ *
+ * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ * 
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ * Regional allocator. Allocates small portions of of larger chunks.
+ */
+
+#include "config.h"
+#include "util/log.h"
+#include "util/regional.h"
+
+#ifdef ALIGNMENT
+#  undef ALIGNMENT
+#endif
+/** increase size until it fits alignment of s bytes */
+#define ALIGN_UP(x, s)     (((x) + s - 1) & (~(s - 1)))
+/** what size to align on; make sure a char* fits in it. */
+#define ALIGNMENT          (sizeof(uint64_t))
+
+/** Default reasonable size for chunks */
+#define REGIONAL_CHUNK_SIZE         8192
+#ifdef UNBOUND_ALLOC_NONREGIONAL
+/** All objects allocated outside of chunks, for debug */
+#define REGIONAL_LARGE_OBJECT_SIZE  0
+#else
+/** Default size for large objects - allocated outside of chunks. */
+#define REGIONAL_LARGE_OBJECT_SIZE  2048
+#endif
+
+struct regional* 
+regional_create(void)
+{
+	return regional_create_custom(REGIONAL_CHUNK_SIZE);
+}
+
+/** init regional struct with first block */
+static void
+regional_init(struct regional* r)
+{
+	size_t a = ALIGN_UP(sizeof(struct regional), ALIGNMENT);
+	r->data = (char*)r + a;
+	r->available = r->first_size - a;
+	r->next = NULL;
+	r->large_list = NULL;
+	r->total_large = 0;
+}
+
+struct regional* 
+regional_create_custom(size_t size)
+{
+	struct regional* r = (struct regional*)malloc(size);
+	log_assert(sizeof(struct regional) <= size);
+	if(!r) return NULL;
+	r->first_size = size;
+	regional_init(r);
+	return r;
+}
+
+void 
+regional_free_all(struct regional *r)
+{
+	char* p = r->next, *np;
+	while(p) {
+		np = *(char**)p;
+		free(p);
+		p = np;
+	}
+	p = r->large_list;
+	while(p) {
+		np = *(char**)p;
+		free(p);
+		p = np;
+	}
+	regional_init(r);
+}
+
+void 
+regional_destroy(struct regional *r)
+{
+	if(!r) return;
+	regional_free_all(r);
+	free(r);
+}
+
+void *
+regional_alloc(struct regional *r, size_t size)
+{
+	size_t a = ALIGN_UP(size, ALIGNMENT);
+	void *s;
+	/* large objects */
+	if(a > REGIONAL_LARGE_OBJECT_SIZE) {
+		s = malloc(ALIGNMENT + size);
+		if(!s) return NULL;
+		r->total_large += ALIGNMENT+size;
+		*(char**)s = r->large_list;
+		r->large_list = (char*)s;
+		return (char*)s+ALIGNMENT;
+	}
+	/* create a new chunk */
+	if(a > r->available) {
+		s = malloc(REGIONAL_CHUNK_SIZE);
+		if(!s) return NULL;
+		*(char**)s = r->next;
+		r->next = (char*)s;
+		r->data = (char*)s + ALIGNMENT;
+		r->available = REGIONAL_CHUNK_SIZE - ALIGNMENT;
+	}
+	/* put in this chunk */
+	r->available -= a;
+	s = r->data;
+	r->data += a;
+	return s;
+}
+
+void *
+regional_alloc_init(struct regional* r, const void *init, size_t size)
+{
+	void *s = regional_alloc(r, size);
+	if(!s) return NULL;
+	memcpy(s, init, size);
+	return s;
+}
+
+void *
+regional_alloc_zero(struct regional *r, size_t size)
+{
+	void *s = regional_alloc(r, size);
+	if(!s) return NULL;
+	memset(s, 0, size);
+	return s;
+}
+
+char *
+regional_strdup(struct regional *r, const char *string)
+{
+	return (char*)regional_alloc_init(r, string, strlen(string)+1);
+}
+
+/**
+ * reasonably slow, but stats and get_mem are not supposed to be fast
+ * count the number of chunks in use
+ */
+static size_t
+count_chunks(struct regional* r)
+{
+	size_t c = 1;
+	char* p = r->next;
+	while(p) {
+		c++;
+		p = *(char**)p;
+	}
+	return c;
+}
+
+/**
+ * also reasonably slow, counts the number of large objects
+ */
+static size_t
+count_large(struct regional* r)
+{
+	size_t c = 0;
+	char* p = r->large_list;
+	while(p) {
+		c++;
+		p = *(char**)p;
+	}
+	return c;
+}
+
+void 
+regional_log_stats(struct regional *r)
+{
+	/* some basic assertions put here (non time critical code) */
+	log_assert(ALIGNMENT >= sizeof(char*));
+	log_assert(REGIONAL_CHUNK_SIZE > ALIGNMENT);
+	log_assert(REGIONAL_CHUNK_SIZE-ALIGNMENT > REGIONAL_LARGE_OBJECT_SIZE);
+	log_assert(REGIONAL_CHUNK_SIZE >= sizeof(struct regional));
+	/* debug print */
+	log_info("regional %u chunks, %u large",
+		(unsigned)count_chunks(r), (unsigned)count_large(r));
+}
+
+size_t 
+regional_get_mem(struct regional* r)
+{
+	return r->first_size + (count_chunks(r)-1)*REGIONAL_CHUNK_SIZE 
+		+ r->total_large;
+}
diff --git a/3rdParty/Unbound/src/src/util/regional.h b/3rdParty/Unbound/src/src/util/regional.h
new file mode 100644
index 0000000..250523a
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/regional.h
@@ -0,0 +1,150 @@
+/*
+ * regional.h -- region based memory allocator.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ * 
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ * Regional allocator. Allocates small portions of of larger chunks.
+ * Based on region-allocator from NSD, but rewritten to be light.
+ *
+ * Different from (nsd) region-allocator.h
+ * 	o does not have recycle bin
+ * 	o does not collect stats; just enough to answer get_mem() in use.
+ * 	o does not keep cleanup list
+ * 	o does not have function pointers to setup
+ * 	o allocs the regional struct inside the first block.
+ * 	o can take a block to create regional from.
+ * 	o blocks and large allocations are kept on singly linked lists.
+ */
+
+#ifndef UTIL_REGIONAL_H_
+#define UTIL_REGIONAL_H_
+
+/** 
+ * the regional* is the first block*.
+ * every block has a ptr to the next in first bytes.
+ * and so does the regional struct, which is the first block.
+ */
+struct regional
+{
+	/** 
+	 * next chunk. NULL if first chunk is the only chunk. 
+	 * first inside that chunk is the char* next pointer. 
+	 * When regional_free_all() has been called this value is NULL.
+	 */
+	char* next;
+	/** first large object, cast to char** to obtain next ptr */
+	char* large_list;
+	/** total large size */
+	size_t total_large;
+	/** initial chunk size */
+	size_t first_size;
+	/** number of bytes available in the current chunk. */
+	size_t available;
+	/** current chunk data position. */
+	char* data;
+};
+
+/**
+ * Create a new regional.
+ * @return: newly allocated regional.
+ */
+struct regional* regional_create(void);
+
+/**
+ * Create a new region, with custom settings.
+ * @param size: length of first block.
+ * @return: newly allocated regional.
+ */
+struct regional* regional_create_custom(size_t size);
+	
+/**
+ * Free all memory associated with regional. Only keeps the first block with
+ * the regional inside it.
+ * @param r: the region.
+ */
+void regional_free_all(struct regional *r);
+
+/**
+ * Destroy regional.  All memory associated with regional is freed as if
+ * regional_free_all was called, as well as destroying the regional struct.
+ * @param r: to delete.
+ */
+void regional_destroy(struct regional *r);
+
+/**
+ * Allocate size bytes of memory inside regional.  The memory is
+ * deallocated when region_free_all is called for this region.
+ * @param r: the region.
+ * @param size: number of bytes.
+ * @return: pointer to memory allocated.
+ */
+void *regional_alloc(struct regional *r, size_t size);
+
+/**
+ * Allocate size bytes of memory inside regional and copy INIT into it.
+ * The memory is deallocated when region_free_all is called for this
+ * region.
+ * @param r: the region.
+ * @param init: to copy.
+ * @param size: number of bytes.
+ * @return: pointer to memory allocated.
+ */
+void *regional_alloc_init(struct regional* r, const void *init, size_t size);
+
+/**
+ * Allocate size bytes of memory inside regional that are initialized to
+ * 0.  The memory is deallocated when region_free_all is called for
+ * this region.
+ * @param r: the region.
+ * @param size: number of bytes.
+ * @return: pointer to memory allocated.
+ */
+void *regional_alloc_zero(struct regional *r, size_t size);
+
+/**
+ * Duplicate string and allocate the result in regional.
+ * @param r: the region.
+ * @param string: null terminated string.
+ * @return: pointer to memory allocated.
+ */
+char *regional_strdup(struct regional *r, const char *string);
+
+/** Debug print regional statistics to log */
+void regional_log_stats(struct regional *r);
+
+/** get total memory size in use by region */
+size_t regional_get_mem(struct regional* r);
+
+#endif /* UTIL_REGIONAL_H_ */
diff --git a/3rdParty/Unbound/src/src/util/rtt.c b/3rdParty/Unbound/src/src/util/rtt.c
new file mode 100644
index 0000000..df1d437
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/rtt.c
@@ -0,0 +1,120 @@
+/*
+ * util/rtt.c - UDP round trip time estimator for resend timeouts.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains a data type and functions to help estimate good
+ * round trip times for UDP resend timeout values.
+ */
+#include "config.h"
+#include "util/rtt.h"
+#include "util/log.h"
+
+/** calculate RTO from rtt information */
+static int
+calc_rto(const struct rtt_info* rtt)
+{
+	/* From Stevens, Unix Network Programming, Vol1, 3rd ed., p.598 */
+	int rto = rtt->srtt + 4*rtt->rttvar;
+	if(rto < RTT_MIN_TIMEOUT)
+		rto = RTT_MIN_TIMEOUT;
+	if(rto > RTT_MAX_TIMEOUT)
+		rto = RTT_MAX_TIMEOUT;
+	return rto;
+}
+
+void 
+rtt_init(struct rtt_info* rtt)
+{
+	rtt->srtt = 0;
+	rtt->rttvar = 94;
+	rtt->rto = calc_rto(rtt);
+	/* default value from the book is 0 + 4*0.75 = 3 seconds */
+	/* first RTO is 0 + 4*0.094 = 0.376 seconds */
+}
+
+int 
+rtt_timeout(const struct rtt_info* rtt)
+{
+	return rtt->rto;
+}
+
+int 
+rtt_unclamped(const struct rtt_info* rtt)
+{
+	if(calc_rto(rtt) != rtt->rto) {
+		/* timeout fallback has happened */
+		return rtt->rto;
+	}
+	/* return unclamped value */
+	return rtt->srtt + 4*rtt->rttvar;
+}
+
+void 
+rtt_update(struct rtt_info* rtt, int ms)
+{
+	int delta = ms - rtt->srtt;
+	rtt->srtt += delta / 8; /* g = 1/8 */
+	if(delta < 0)
+		delta = -delta; /* |delta| */
+	rtt->rttvar += (delta - rtt->rttvar) / 4; /* h = 1/4 */
+	rtt->rto = calc_rto(rtt);
+}
+
+void 
+rtt_lost(struct rtt_info* rtt, int orig)
+{
+	/* exponential backoff */
+
+	/* if a query succeeded and put down the rto meanwhile, ignore this */
+	if(rtt->rto < orig)
+		return;
+
+	/* the original rto is doubled, not the current one to make sure
+	 * that the values in the cache are not increased by lots of
+	 * queries simultaneously as they time out at the same time */
+	orig *= 2;
+	if(rtt->rto <= orig) {
+		rtt->rto = orig;
+		if(rtt->rto > RTT_MAX_TIMEOUT)
+			rtt->rto = RTT_MAX_TIMEOUT;
+	}
+}
+
+int rtt_notimeout(const struct rtt_info* rtt)
+{
+	return calc_rto(rtt);
+}
diff --git a/3rdParty/Unbound/src/src/util/rtt.h b/3rdParty/Unbound/src/src/util/rtt.h
new file mode 100644
index 0000000..1af5484
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/rtt.h
@@ -0,0 +1,107 @@
+/*
+ * util/rtt.h - UDP round trip time estimator for resend timeouts.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains a data type and functions to help estimate good
+ * round trip times for UDP resend timeout values.
+ */
+
+#ifndef UTIL_RTT_H
+#define UTIL_RTT_H
+
+/**
+ * RTT information. Keeps packet Round Trip Time.
+ */
+struct rtt_info {
+	/** smoothed rtt estimator, in milliseconds */
+	int srtt;
+	/** smoothed mean deviation, in milliseconds */
+	int rttvar;
+	/** current RTO in use, in milliseconds */
+	int rto;
+};
+
+/** min retransmit timeout value, in milliseconds */
+#define RTT_MIN_TIMEOUT	50
+/** max retransmit timeout value, in milliseconds */
+#define RTT_MAX_TIMEOUT 120000
+
+/**
+ * Initialize RTT estimators.
+ * @param rtt: The structure. Caller is responsible for allocation of it.
+ */
+void rtt_init(struct rtt_info* rtt);
+
+/** 
+ * Get timeout to use for sending a UDP packet.
+ * @param rtt: round trip statistics structure.
+ * @return: timeout to use in milliseconds. Relative time value.
+ */
+int rtt_timeout(const struct rtt_info* rtt);
+
+/** 
+ * Get unclamped timeout to use for server selection.
+ * Recent timeouts are reflected in the returned value.
+ * @param rtt: round trip statistics structure.
+ * @return: value to use in milliseconds. 
+ */
+int rtt_unclamped(const struct rtt_info* rtt);
+
+/**
+ * RTT for valid responses. Without timeouts.
+ * @param rtt: round trip statistics structure.
+ * @return: value in msec.
+ */
+int rtt_notimeout(const struct rtt_info* rtt);
+
+/**
+ * Update the statistics with a new roundtrip estimate observation.
+ * @param rtt: round trip statistics structure.
+ * @param ms: estimate of roundtrip time in milliseconds.
+ */
+void rtt_update(struct rtt_info* rtt, int ms);
+
+/**
+ * Update the statistics with a new timout expired observation.
+ * @param rtt: round trip statistics structure.
+ * @param orig: original rtt time given for the query that timed out.
+ * 	Used to calculate the maximum responsible backed off time that
+ * 	can reasonably be applied.
+ */
+void rtt_lost(struct rtt_info* rtt, int orig);
+
+#endif /* UTIL_RTT_H */
diff --git a/3rdParty/Unbound/src/src/util/storage/dnstree.c b/3rdParty/Unbound/src/src/util/storage/dnstree.c
new file mode 100644
index 0000000..003e8af
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/storage/dnstree.c
@@ -0,0 +1,282 @@
+/*
+ * util/storage/dnstree.c - support for rbtree types suitable for DNS code.
+ *
+ * Copyright (c) 2008, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains structures combining types and functions to
+ * manipulate those structures that help building DNS lookup trees.
+ */
+#include "config.h"
+#include "util/storage/dnstree.h"
+#include "util/data/dname.h"
+#include "util/net_help.h"
+
+int name_tree_compare(const void* k1, const void* k2)
+{
+        struct name_tree_node* x = (struct name_tree_node*)k1;
+        struct name_tree_node* y = (struct name_tree_node*)k2;
+        int m;
+        if(x->dclass != y->dclass) {
+                if(x->dclass < y->dclass)
+                        return -1;
+                return 1;
+        }
+        return dname_lab_cmp(x->name, x->labs, y->name, y->labs, &m);
+}
+
+int addr_tree_compare(const void* k1, const void* k2)
+{
+        struct addr_tree_node* n1 = (struct addr_tree_node*)k1;
+        struct addr_tree_node* n2 = (struct addr_tree_node*)k2;
+        int r = sockaddr_cmp_addr(&n1->addr, n1->addrlen, &n2->addr,
+                n2->addrlen);
+        if(r != 0) return r;
+        if(n1->net < n2->net)
+                return -1;
+        if(n1->net > n2->net)
+                return 1;
+        return 0;
+}
+
+void name_tree_init(rbtree_t* tree)
+{
+	rbtree_init(tree, &name_tree_compare);
+}
+
+void addr_tree_init(rbtree_t* tree)
+{
+	rbtree_init(tree, &addr_tree_compare);
+}
+
+int name_tree_insert(rbtree_t* tree, struct name_tree_node* node, 
+        uint8_t* name, size_t len, int labs, uint16_t dclass)
+{
+	node->node.key = node;
+	node->name = name;
+	node->len = len;
+	node->labs = labs;
+	node->dclass = dclass;
+	node->parent = NULL;
+	return rbtree_insert(tree, &node->node) != NULL;
+}
+
+int addr_tree_insert(rbtree_t* tree, struct addr_tree_node* node,
+        struct sockaddr_storage* addr, socklen_t addrlen, int net)
+{
+	node->node.key = node;
+	memcpy(&node->addr, addr, addrlen);
+	node->addrlen = addrlen;
+	node->net = net;
+	node->parent = NULL;
+	return rbtree_insert(tree, &node->node) != NULL;
+}
+
+void addr_tree_init_parents(rbtree_t* tree)
+{
+        struct addr_tree_node* node, *prev = NULL, *p;
+        int m;
+        RBTREE_FOR(node, struct addr_tree_node*, tree) {
+                node->parent = NULL;
+                if(!prev || prev->addrlen != node->addrlen) {
+                        prev = node;
+                        continue;
+                }
+                m = addr_in_common(&prev->addr, prev->net, &node->addr,
+                        node->net, node->addrlen);
+                /* sort order like: ::/0, 1::/2, 1::/4, ... 2::/2 */
+                /* find the previous, or parent-parent-parent */
+                for(p = prev; p; p = p->parent)
+                        if(p->net <= m) {
+                                /* ==: since prev matched m, this is closest*/
+                                /* <: prev matches more, but is not a parent,
+				 * this one is a (grand)parent */
+                                node->parent = p;
+                                break;
+                        }
+                prev = node;
+        }
+}
+
+void name_tree_init_parents(rbtree_t* tree)
+{
+        struct name_tree_node* node, *prev = NULL, *p;
+        int m;
+        RBTREE_FOR(node, struct name_tree_node*, tree) {
+                node->parent = NULL;
+                if(!prev || prev->dclass != node->dclass) {
+                        prev = node;
+                        continue;
+                }
+                (void)dname_lab_cmp(prev->name, prev->labs, node->name,
+                        node->labs, &m); /* we know prev is smaller */
+		/* sort order like: . com. bla.com. zwb.com. net. */
+                /* find the previous, or parent-parent-parent */
+                for(p = prev; p; p = p->parent)
+                        if(p->labs <= m) {
+                                /* ==: since prev matched m, this is closest*/
+                                /* <: prev matches more, but is not a parent,
+				 * this one is a (grand)parent */
+                                node->parent = p;
+                                break;
+                        }
+                prev = node;
+        }
+}
+
+struct name_tree_node* name_tree_find(rbtree_t* tree, uint8_t* name, 
+        size_t len, int labs, uint16_t dclass)
+{
+	struct name_tree_node key;
+	key.node.key = &key;
+	key.name = name;
+	key.len = len;
+	key.labs = labs;
+	key.dclass = dclass;
+	return (struct name_tree_node*)rbtree_search(tree, &key);
+}
+
+struct name_tree_node* name_tree_lookup(rbtree_t* tree, uint8_t* name,
+        size_t len, int labs, uint16_t dclass)
+{
+        rbnode_t* res = NULL;
+        struct name_tree_node *result;
+        struct name_tree_node key;
+        key.node.key = &key;
+        key.name = name;
+        key.len = len;
+        key.labs = labs;
+        key.dclass = dclass;
+        if(rbtree_find_less_equal(tree, &key, &res)) {
+                /* exact */
+                result = (struct name_tree_node*)res;
+        } else {
+                /* smaller element (or no element) */
+                int m;
+                result = (struct name_tree_node*)res;
+                if(!result || result->dclass != dclass)
+                        return NULL;
+                /* count number of labels matched */
+                (void)dname_lab_cmp(result->name, result->labs, key.name,
+                        key.labs, &m);
+                while(result) { /* go up until qname is subdomain of stub */
+                        if(result->labs <= m)
+                                break;
+                        result = result->parent;
+                }
+        }
+	return result;
+}
+
+struct addr_tree_node* addr_tree_lookup(rbtree_t* tree, 
+        struct sockaddr_storage* addr, socklen_t addrlen)
+{
+        rbnode_t* res = NULL;
+        struct addr_tree_node* result;
+        struct addr_tree_node key;
+        key.node.key = &key;
+        memcpy(&key.addr, addr, addrlen);
+        key.addrlen = addrlen;
+        key.net = (addr_is_ip6(addr, addrlen)?128:32);
+        if(rbtree_find_less_equal(tree, &key, &res)) {
+                /* exact */
+                return (struct addr_tree_node*)res;
+        } else {
+                /* smaller element (or no element) */
+                int m;
+                result = (struct addr_tree_node*)res;
+                if(!result || result->addrlen != addrlen)
+                        return 0;
+                /* count number of bits matched */
+                m = addr_in_common(&result->addr, result->net, addr,
+                        key.net, addrlen);
+                while(result) { /* go up until addr is inside netblock */
+                        if(result->net <= m)
+                                break;
+                        result = result->parent;
+                }
+        }
+        return result;
+}
+
+int
+name_tree_next_root(rbtree_t* tree, uint16_t* dclass)
+{
+	struct name_tree_node key;
+	rbnode_t* n;
+	struct name_tree_node* p;
+	if(*dclass == 0) {
+		/* first root item is first item in tree */
+		n = rbtree_first(tree);
+		if(n == RBTREE_NULL)
+			return 0;
+		p = (struct name_tree_node*)n;
+		if(dname_is_root(p->name)) {
+			*dclass = p->dclass;
+			return 1;
+		}
+		/* root not first item? search for higher items */
+		*dclass = p->dclass + 1;
+		return name_tree_next_root(tree, dclass);
+	}
+	/* find class n in tree, we may get a direct hit, or if we don't
+	 * this is the last item of the previous class so rbtree_next() takes
+	 * us to the next root (if any) */
+	key.node.key = &key;
+	key.name = (uint8_t*)"\000";
+	key.len = 1;
+	key.labs = 0;
+	key.dclass = *dclass;
+	n = NULL;
+	if(rbtree_find_less_equal(tree, &key, &n)) {
+		/* exact */
+		return 1;
+	} else {
+		/* smaller element */
+		if(!n || n == RBTREE_NULL)
+			return 0; /* nothing found */
+		n = rbtree_next(n);
+		if(n == RBTREE_NULL)
+			return 0; /* no higher */
+		p = (struct name_tree_node*)n;
+		if(dname_is_root(p->name)) {
+			*dclass = p->dclass;
+			return 1;
+		}
+		/* not a root node, return next higher item */
+		*dclass = p->dclass+1;
+		return name_tree_next_root(tree, dclass);
+	}
+}
diff --git a/3rdParty/Unbound/src/src/util/storage/dnstree.h b/3rdParty/Unbound/src/src/util/storage/dnstree.h
new file mode 100644
index 0000000..3ecbd12
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/storage/dnstree.h
@@ -0,0 +1,192 @@
+/*
+ * util/storage/dnstree.h - support for rbtree types suitable for DNS code.
+ *
+ * Copyright (c) 2008, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains structures combining types and functions to
+ * manipulate those structures that help building DNS lookup trees.
+ */
+
+#ifndef UTIL_STORAGE_DNSTREE_H
+#define UTIL_STORAGE_DNSTREE_H
+#include "util/rbtree.h"
+
+/**
+ * Tree of domain names.  Sorted first by class then by name.
+ * This is not sorted canonically, but fast.
+ * This can be looked up to obtain a closest encloser parent name.
+ *
+ * The tree itself is a rbtree_t.
+ * This is the element node put as first entry in the client structure.
+ */
+struct name_tree_node {
+	/** rbtree node, key is this struct : dclass and name */
+	rbnode_t node;
+	/** parent in tree */
+	struct name_tree_node* parent;
+	/** name in uncompressed wireformat */
+	uint8_t* name;
+	/** length of name */
+	size_t len;
+	/** labels in name */
+	int labs;
+	/** the class of the name (host order) */
+	uint16_t dclass;
+};
+
+/**
+ * Tree of IP addresses.  Sorted first by protocol, then by bits.
+ * This can be looked up to obtain the enclosing subnet.
+ *
+ * The tree itself is a rbtree_t.
+ * This is the element node put as first entry in the client structure.
+ */
+struct addr_tree_node {
+	/** rbtree node, key is this struct : proto and subnet */
+	rbnode_t node;
+	/** parent in tree */
+	struct addr_tree_node* parent;
+	/** address */
+	struct sockaddr_storage addr;
+	/** length of addr */
+	socklen_t addrlen;
+	/** netblock size */
+	int net;
+};
+
+/**
+ * Init a name tree to be empty
+ * @param tree: to init.
+ */
+void name_tree_init(rbtree_t* tree);
+
+/**
+ * insert element into name tree.
+ * @param tree: name tree
+ * @param node: node element (at start of a structure that caller
+ *	has allocated).
+ * @param name: name to insert (wireformat)
+ *	this node has been allocated by the caller and it itself inserted.
+ * @param len: length of name
+ * @param labs: labels in name
+ * @param dclass: class of name
+ * @return false on error (duplicate element).
+ */
+int name_tree_insert(rbtree_t* tree, struct name_tree_node* node, 
+	uint8_t* name, size_t len, int labs, uint16_t dclass);
+
+/**
+ * Initialize parent pointers in name tree.
+ * Should be performed after insertions are done, before lookups
+ * @param tree: name tree
+ */
+void name_tree_init_parents(rbtree_t* tree);
+
+/**
+ * Lookup exact match in name tree
+ * @param tree: name tree
+ * @param name: wireformat name
+ * @param len: length of name
+ * @param labs: labels in name
+ * @param dclass: class of name
+ * @return node or NULL if not found.
+ */
+struct name_tree_node* name_tree_find(rbtree_t* tree, uint8_t* name, 
+	size_t len, int labs, uint16_t dclass);
+
+/**
+ * Lookup closest encloser in name tree.
+ * @param tree: name tree
+ * @param name: wireformat name
+ * @param len: length of name
+ * @param labs: labels in name
+ * @param dclass: class of name
+ * @return closest enclosing node (could be equal) or NULL if not found.
+ */
+struct name_tree_node* name_tree_lookup(rbtree_t* tree, uint8_t* name, 
+	size_t len, int labs, uint16_t dclass);
+
+/**
+ * Find next root item in name tree.
+ * @param tree: the nametree.
+ * @param dclass: the class to look for next (or higher).
+ * @return false if no classes found, true means class put into c.
+ */
+int name_tree_next_root(rbtree_t* tree, uint16_t* dclass);
+
+/**
+ * Init addr tree to be empty.
+ * @param tree: to init.
+ */
+void addr_tree_init(rbtree_t* tree);
+
+/**
+ * insert element into addr tree.
+ * @param tree: addr tree
+ * @param node: node element (at start of a structure that caller
+ *	has allocated).
+ * @param addr: to insert (copied).
+ * @param addrlen: length of addr
+ * @param net: size of subnet. 
+ * @return false on error (duplicate element).
+ */
+int addr_tree_insert(rbtree_t* tree, struct addr_tree_node* node, 
+	struct sockaddr_storage* addr, socklen_t addrlen, int net);
+
+/**
+ * Initialize parent pointers in addr tree.
+ * Should be performed after insertions are done, before lookups
+ * @param tree: addr tree
+ */
+void addr_tree_init_parents(rbtree_t* tree);
+
+/**
+ * Lookup closest encloser in addr tree.
+ * @param tree: addr tree
+ * @param addr: to lookup.
+ * @param addrlen: length of addr
+ * @return closest enclosing node (could be equal) or NULL if not found.
+ */
+struct addr_tree_node* addr_tree_lookup(rbtree_t* tree, 
+	struct sockaddr_storage* addr, socklen_t addrlen);
+
+/** compare name tree nodes */
+int name_tree_compare(const void* k1, const void* k2);
+
+/** compare addr tree nodes */
+int addr_tree_compare(const void* k1, const void* k2);
+
+#endif /* UTIL_STORAGE_DNSTREE_H */
diff --git a/3rdParty/Unbound/src/src/util/storage/lookup3.c b/3rdParty/Unbound/src/src/util/storage/lookup3.c
new file mode 100644
index 0000000..65e0ad2
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/storage/lookup3.c
@@ -0,0 +1,1011 @@
+/*
+  January 2012(Wouter) added randomised initial value, fallout from 28c3.
+  March 2007(Wouter) adapted from lookup3.c original, add config.h include.
+     added #ifdef VALGRIND to remove 298,384,660 'unused variable k8' warnings.
+     added include of lookup3.h to check definitions match declarations.
+     removed include of stdint - config.h takes care of platform independence.
+  url http://burtleburtle.net/bob/hash/index.html.
+*/
+/*
+-------------------------------------------------------------------------------
+lookup3.c, by Bob Jenkins, May 2006, Public Domain.
+
+These are functions for producing 32-bit hashes for hash table lookup.
+hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() 
+are externally useful functions.  Routines to test the hash are included 
+if SELF_TEST is defined.  You can use this free for any purpose.  It's in
+the public domain.  It has no warranty.
+
+You probably want to use hashlittle().  hashlittle() and hashbig()
+hash byte arrays.  hashlittle() is is faster than hashbig() on
+little-endian machines.  Intel and AMD are little-endian machines.
+On second thought, you probably want hashlittle2(), which is identical to
+hashlittle() except it returns two 32-bit hashes for the price of one.  
+You could implement hashbig2() if you wanted but I haven't bothered here.
+
+If you want to find a hash of, say, exactly 7 integers, do
+  a = i1;  b = i2;  c = i3;
+  mix(a,b,c);
+  a += i4; b += i5; c += i6;
+  mix(a,b,c);
+  a += i7;
+  final(a,b,c);
+then use c as the hash value.  If you have a variable length array of
+4-byte integers to hash, use hashword().  If you have a byte array (like
+a character string), use hashlittle().  If you have several byte arrays, or
+a mix of things, see the comments above hashlittle().  
+
+Why is this so big?  I read 12 bytes at a time into 3 4-byte integers, 
+then mix those integers.  This is fast (you can do a lot more thorough
+mixing with 12*3 instructions on 3 integers than you can with 3 instructions
+on 1 byte), but shoehorning those bytes into integers efficiently is messy.
+-------------------------------------------------------------------------------
+*/
+/*#define SELF_TEST 1*/
+
+#include "config.h"
+#include "util/storage/lookup3.h"
+#include <stdio.h>      /* defines printf for tests */
+#include <time.h>       /* defines time_t for timings in the test */
+/*#include <stdint.h>     defines uint32_t etc  (from config.h) */
+#include <sys/param.h>  /* attempt to define endianness */
+#ifdef linux
+# include <endian.h>    /* attempt to define endianness */
+#endif
+
+/* random initial value */
+static uint32_t raninit = 0xdeadbeef;
+
+void
+hash_set_raninit(uint32_t v)
+{
+	raninit = v;
+}
+
+/*
+ * My best guess at if you are big-endian or little-endian.  This may
+ * need adjustment.
+ */
+#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
+     __BYTE_ORDER == __LITTLE_ENDIAN) || \
+    (defined(i386) || defined(__i386__) || defined(__i486__) || \
+     defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL))
+# define HASH_LITTLE_ENDIAN 1
+# define HASH_BIG_ENDIAN 0
+#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \
+       __BYTE_ORDER == __BIG_ENDIAN) || \
+      (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel))
+# define HASH_LITTLE_ENDIAN 0
+# define HASH_BIG_ENDIAN 1
+#else
+# define HASH_LITTLE_ENDIAN 0
+# define HASH_BIG_ENDIAN 0
+#endif
+
+#define hashsize(n) ((uint32_t)1<<(n))
+#define hashmask(n) (hashsize(n)-1)
+#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
+
+/*
+-------------------------------------------------------------------------------
+mix -- mix 3 32-bit values reversibly.
+
+This is reversible, so any information in (a,b,c) before mix() is
+still in (a,b,c) after mix().
+
+If four pairs of (a,b,c) inputs are run through mix(), or through
+mix() in reverse, there are at least 32 bits of the output that
+are sometimes the same for one pair and different for another pair.
+This was tested for:
+* pairs that differed by one bit, by two bits, in any combination
+  of top bits of (a,b,c), or in any combination of bottom bits of
+  (a,b,c).
+* "differ" is defined as +, -, ^, or ~^.  For + and -, I transformed
+  the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
+  is commonly produced by subtraction) look like a single 1-bit
+  difference.
+* the base values were pseudorandom, all zero but one bit set, or 
+  all zero plus a counter that starts at zero.
+
+Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that
+satisfy this are
+    4  6  8 16 19  4
+    9 15  3 18 27 15
+   14  9  3  7 17  3
+Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
+for "differ" defined as + with a one-bit base and a two-bit delta.  I
+used http://burtleburtle.net/bob/hash/avalanche.html to choose 
+the operations, constants, and arrangements of the variables.
+
+This does not achieve avalanche.  There are input bits of (a,b,c)
+that fail to affect some output bits of (a,b,c), especially of a.  The
+most thoroughly mixed value is c, but it doesn't really even achieve
+avalanche in c.
+
+This allows some parallelism.  Read-after-writes are good at doubling
+the number of bits affected, so the goal of mixing pulls in the opposite
+direction as the goal of parallelism.  I did what I could.  Rotates
+seem to cost as much as shifts on every machine I could lay my hands
+on, and rotates are much kinder to the top and bottom bits, so I used
+rotates.
+-------------------------------------------------------------------------------
+*/
+#define mix(a,b,c) \
+{ \
+  a -= c;  a ^= rot(c, 4);  c += b; \
+  b -= a;  b ^= rot(a, 6);  a += c; \
+  c -= b;  c ^= rot(b, 8);  b += a; \
+  a -= c;  a ^= rot(c,16);  c += b; \
+  b -= a;  b ^= rot(a,19);  a += c; \
+  c -= b;  c ^= rot(b, 4);  b += a; \
+}
+
+/*
+-------------------------------------------------------------------------------
+final -- final mixing of 3 32-bit values (a,b,c) into c
+
+Pairs of (a,b,c) values differing in only a few bits will usually
+produce values of c that look totally different.  This was tested for
+* pairs that differed by one bit, by two bits, in any combination
+  of top bits of (a,b,c), or in any combination of bottom bits of
+  (a,b,c).
+* "differ" is defined as +, -, ^, or ~^.  For + and -, I transformed
+  the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
+  is commonly produced by subtraction) look like a single 1-bit
+  difference.
+* the base values were pseudorandom, all zero but one bit set, or 
+  all zero plus a counter that starts at zero.
+
+These constants passed:
+ 14 11 25 16 4 14 24
+ 12 14 25 16 4 14 24
+and these came close:
+  4  8 15 26 3 22 24
+ 10  8 15 26 3 22 24
+ 11  8 15 26 3 22 24
+-------------------------------------------------------------------------------
+*/
+#define final(a,b,c) \
+{ \
+  c ^= b; c -= rot(b,14); \
+  a ^= c; a -= rot(c,11); \
+  b ^= a; b -= rot(a,25); \
+  c ^= b; c -= rot(b,16); \
+  a ^= c; a -= rot(c,4);  \
+  b ^= a; b -= rot(a,14); \
+  c ^= b; c -= rot(b,24); \
+}
+
+/*
+--------------------------------------------------------------------
+ This works on all machines.  To be useful, it requires
+ -- that the key be an array of uint32_t's, and
+ -- that the length be the number of uint32_t's in the key
+
+ The function hashword() is identical to hashlittle() on little-endian
+ machines, and identical to hashbig() on big-endian machines,
+ except that the length has to be measured in uint32_ts rather than in
+ bytes.  hashlittle() is more complicated than hashword() only because
+ hashlittle() has to dance around fitting the key bytes into registers.
+--------------------------------------------------------------------
+*/
+uint32_t hashword(
+const uint32_t *k,                   /* the key, an array of uint32_t values */
+size_t          length,               /* the length of the key, in uint32_ts */
+uint32_t        initval)         /* the previous hash, or an arbitrary value */
+{
+  uint32_t a,b,c;
+
+  /* Set up the internal state */
+  a = b = c = raninit + (((uint32_t)length)<<2) + initval;
+
+  /*------------------------------------------------- handle most of the key */
+  while (length > 3)
+  {
+    a += k[0];
+    b += k[1];
+    c += k[2];
+    mix(a,b,c);
+    length -= 3;
+    k += 3;
+  }
+
+  /*------------------------------------------- handle the last 3 uint32_t's */
+  switch(length)                     /* all the case statements fall through */
+  { 
+  case 3 : c+=k[2];
+  case 2 : b+=k[1];
+  case 1 : a+=k[0];
+    final(a,b,c);
+  case 0:     /* case 0: nothing left to add */
+    break;
+  }
+  /*------------------------------------------------------ report the result */
+  return c;
+}
+
+
+#ifdef SELF_TEST
+
+/*
+--------------------------------------------------------------------
+hashword2() -- same as hashword(), but take two seeds and return two
+32-bit values.  pc and pb must both be nonnull, and *pc and *pb must
+both be initialized with seeds.  If you pass in (*pb)==0, the output 
+(*pc) will be the same as the return value from hashword().
+--------------------------------------------------------------------
+*/
+void hashword2 (
+const uint32_t *k,                   /* the key, an array of uint32_t values */
+size_t          length,               /* the length of the key, in uint32_ts */
+uint32_t       *pc,                      /* IN: seed OUT: primary hash value */
+uint32_t       *pb)               /* IN: more seed OUT: secondary hash value */
+{
+  uint32_t a,b,c;
+
+  /* Set up the internal state */
+  a = b = c = raninit + ((uint32_t)(length<<2)) + *pc;
+  c += *pb;
+
+  /*------------------------------------------------- handle most of the key */
+  while (length > 3)
+  {
+    a += k[0];
+    b += k[1];
+    c += k[2];
+    mix(a,b,c);
+    length -= 3;
+    k += 3;
+  }
+
+  /*------------------------------------------- handle the last 3 uint32_t's */
+  switch(length)                     /* all the case statements fall through */
+  { 
+  case 3 : c+=k[2];
+  case 2 : b+=k[1];
+  case 1 : a+=k[0];
+    final(a,b,c);
+  case 0:     /* case 0: nothing left to add */
+    break;
+  }
+  /*------------------------------------------------------ report the result */
+  *pc=c; *pb=b;
+}
+
+#endif /* SELF_TEST */
+
+/*
+-------------------------------------------------------------------------------
+hashlittle() -- hash a variable-length key into a 32-bit value
+  k       : the key (the unaligned variable-length array of bytes)
+  length  : the length of the key, counting by bytes
+  initval : can be any 4-byte value
+Returns a 32-bit value.  Every bit of the key affects every bit of
+the return value.  Two keys differing by one or two bits will have
+totally different hash values.
+
+The best hash table sizes are powers of 2.  There is no need to do
+mod a prime (mod is sooo slow!).  If you need less than 32 bits,
+use a bitmask.  For example, if you need only 10 bits, do
+  h = (h & hashmask(10));
+In which case, the hash table should have hashsize(10) elements.
+
+If you are hashing n strings (uint8_t **)k, do it like this:
+  for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h);
+
+By Bob Jenkins, 2006.  bob_jenkins@burtleburtle.net.  You may use this
+code any way you wish, private, educational, or commercial.  It's free.
+
+Use for hash table lookup, or anything where one collision in 2^^32 is
+acceptable.  Do NOT use for cryptographic purposes.
+-------------------------------------------------------------------------------
+*/
+
+uint32_t hashlittle( const void *key, size_t length, uint32_t initval)
+{
+  uint32_t a,b,c;                                          /* internal state */
+  union { const void *ptr; size_t i; } u;     /* needed for Mac Powerbook G4 */
+
+  /* Set up the internal state */
+  a = b = c = raninit + ((uint32_t)length) + initval;
+
+  u.ptr = key;
+  if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
+    const uint32_t *k = (const uint32_t *)key;         /* read 32-bit chunks */
+#ifdef VALGRIND
+    const uint8_t  *k8;
+#endif
+
+    /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
+    while (length > 12)
+    {
+      a += k[0];
+      b += k[1];
+      c += k[2];
+      mix(a,b,c);
+      length -= 12;
+      k += 3;
+    }
+
+    /*----------------------------- handle the last (probably partial) block */
+    /* 
+     * "k[2]&0xffffff" actually reads beyond the end of the string, but
+     * then masks off the part it's not allowed to read.  Because the
+     * string is aligned, the masked-off tail is in the same word as the
+     * rest of the string.  Every machine with memory protection I've seen
+     * does it on word boundaries, so is OK with this.  But VALGRIND will
+     * still catch it and complain.  The masking trick does make the hash
+     * noticably faster for short strings (like English words).
+     */
+#ifndef VALGRIND
+
+    switch(length)
+    {
+    case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+    case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
+    case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
+    case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
+    case 8 : b+=k[1]; a+=k[0]; break;
+    case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
+    case 6 : b+=k[1]&0xffff; a+=k[0]; break;
+    case 5 : b+=k[1]&0xff; a+=k[0]; break;
+    case 4 : a+=k[0]; break;
+    case 3 : a+=k[0]&0xffffff; break;
+    case 2 : a+=k[0]&0xffff; break;
+    case 1 : a+=k[0]&0xff; break;
+    case 0 : return c;              /* zero length strings require no mixing */
+    }
+
+#else /* make valgrind happy */
+
+    k8 = (const uint8_t *)k;
+    switch(length)
+    {
+    case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+    case 11: c+=((uint32_t)k8[10])<<16;  /* fall through */
+    case 10: c+=((uint32_t)k8[9])<<8;    /* fall through */
+    case 9 : c+=k8[8];                   /* fall through */
+    case 8 : b+=k[1]; a+=k[0]; break;
+    case 7 : b+=((uint32_t)k8[6])<<16;   /* fall through */
+    case 6 : b+=((uint32_t)k8[5])<<8;    /* fall through */
+    case 5 : b+=k8[4];                   /* fall through */
+    case 4 : a+=k[0]; break;
+    case 3 : a+=((uint32_t)k8[2])<<16;   /* fall through */
+    case 2 : a+=((uint32_t)k8[1])<<8;    /* fall through */
+    case 1 : a+=k8[0]; break;
+    case 0 : return c;
+    }
+
+#endif /* !valgrind */
+
+  } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
+    const uint16_t *k = (const uint16_t *)key;         /* read 16-bit chunks */
+    const uint8_t  *k8;
+
+    /*--------------- all but last block: aligned reads and different mixing */
+    while (length > 12)
+    {
+      a += k[0] + (((uint32_t)k[1])<<16);
+      b += k[2] + (((uint32_t)k[3])<<16);
+      c += k[4] + (((uint32_t)k[5])<<16);
+      mix(a,b,c);
+      length -= 12;
+      k += 6;
+    }
+
+    /*----------------------------- handle the last (probably partial) block */
+    k8 = (const uint8_t *)k;
+    switch(length)
+    {
+    case 12: c+=k[4]+(((uint32_t)k[5])<<16);
+             b+=k[2]+(((uint32_t)k[3])<<16);
+             a+=k[0]+(((uint32_t)k[1])<<16);
+             break;
+    case 11: c+=((uint32_t)k8[10])<<16;     /* fall through */
+    case 10: c+=k[4];
+             b+=k[2]+(((uint32_t)k[3])<<16);
+             a+=k[0]+(((uint32_t)k[1])<<16);
+             break;
+    case 9 : c+=k8[8];                      /* fall through */
+    case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
+             a+=k[0]+(((uint32_t)k[1])<<16);
+             break;
+    case 7 : b+=((uint32_t)k8[6])<<16;      /* fall through */
+    case 6 : b+=k[2];
+             a+=k[0]+(((uint32_t)k[1])<<16);
+             break;
+    case 5 : b+=k8[4];                      /* fall through */
+    case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
+             break;
+    case 3 : a+=((uint32_t)k8[2])<<16;      /* fall through */
+    case 2 : a+=k[0];
+             break;
+    case 1 : a+=k8[0];
+             break;
+    case 0 : return c;                     /* zero length requires no mixing */
+    }
+
+  } else {                        /* need to read the key one byte at a time */
+    const uint8_t *k = (const uint8_t *)key;
+
+    /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
+    while (length > 12)
+    {
+      a += k[0];
+      a += ((uint32_t)k[1])<<8;
+      a += ((uint32_t)k[2])<<16;
+      a += ((uint32_t)k[3])<<24;
+      b += k[4];
+      b += ((uint32_t)k[5])<<8;
+      b += ((uint32_t)k[6])<<16;
+      b += ((uint32_t)k[7])<<24;
+      c += k[8];
+      c += ((uint32_t)k[9])<<8;
+      c += ((uint32_t)k[10])<<16;
+      c += ((uint32_t)k[11])<<24;
+      mix(a,b,c);
+      length -= 12;
+      k += 12;
+    }
+
+    /*-------------------------------- last block: affect all 32 bits of (c) */
+    switch(length)                   /* all the case statements fall through */
+    {
+    case 12: c+=((uint32_t)k[11])<<24;
+    case 11: c+=((uint32_t)k[10])<<16;
+    case 10: c+=((uint32_t)k[9])<<8;
+    case 9 : c+=k[8];
+    case 8 : b+=((uint32_t)k[7])<<24;
+    case 7 : b+=((uint32_t)k[6])<<16;
+    case 6 : b+=((uint32_t)k[5])<<8;
+    case 5 : b+=k[4];
+    case 4 : a+=((uint32_t)k[3])<<24;
+    case 3 : a+=((uint32_t)k[2])<<16;
+    case 2 : a+=((uint32_t)k[1])<<8;
+    case 1 : a+=k[0];
+             break;
+    case 0 : return c;
+    }
+  }
+
+  final(a,b,c);
+  return c;
+}
+
+#ifdef SELF_TEST
+
+/*
+ * hashlittle2: return 2 32-bit hash values
+ *
+ * This is identical to hashlittle(), except it returns two 32-bit hash
+ * values instead of just one.  This is good enough for hash table
+ * lookup with 2^^64 buckets, or if you want a second hash if you're not
+ * happy with the first, or if you want a probably-unique 64-bit ID for
+ * the key.  *pc is better mixed than *pb, so use *pc first.  If you want
+ * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)".
+ */
+void hashlittle2( 
+  const void *key,       /* the key to hash */
+  size_t      length,    /* length of the key */
+  uint32_t   *pc,        /* IN: primary initval, OUT: primary hash */
+  uint32_t   *pb)        /* IN: secondary initval, OUT: secondary hash */
+{
+  uint32_t a,b,c;                                          /* internal state */
+  union { const void *ptr; size_t i; } u;     /* needed for Mac Powerbook G4 */
+
+  /* Set up the internal state */
+  a = b = c = raninit + ((uint32_t)length) + *pc;
+  c += *pb;
+
+  u.ptr = key;
+  if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
+    const uint32_t *k = (const uint32_t *)key;         /* read 32-bit chunks */
+#ifdef VALGRIND
+    const uint8_t  *k8;
+#endif
+
+    /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
+    while (length > 12)
+    {
+      a += k[0];
+      b += k[1];
+      c += k[2];
+      mix(a,b,c);
+      length -= 12;
+      k += 3;
+    }
+
+    /*----------------------------- handle the last (probably partial) block */
+    /* 
+     * "k[2]&0xffffff" actually reads beyond the end of the string, but
+     * then masks off the part it's not allowed to read.  Because the
+     * string is aligned, the masked-off tail is in the same word as the
+     * rest of the string.  Every machine with memory protection I've seen
+     * does it on word boundaries, so is OK with this.  But VALGRIND will
+     * still catch it and complain.  The masking trick does make the hash
+     * noticably faster for short strings (like English words).
+     */
+#ifndef VALGRIND
+
+    switch(length)
+    {
+    case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+    case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
+    case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
+    case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
+    case 8 : b+=k[1]; a+=k[0]; break;
+    case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
+    case 6 : b+=k[1]&0xffff; a+=k[0]; break;
+    case 5 : b+=k[1]&0xff; a+=k[0]; break;
+    case 4 : a+=k[0]; break;
+    case 3 : a+=k[0]&0xffffff; break;
+    case 2 : a+=k[0]&0xffff; break;
+    case 1 : a+=k[0]&0xff; break;
+    case 0 : *pc=c; *pb=b; return;  /* zero length strings require no mixing */
+    }
+
+#else /* make valgrind happy */
+
+    k8 = (const uint8_t *)k;
+    switch(length)
+    {
+    case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+    case 11: c+=((uint32_t)k8[10])<<16;  /* fall through */
+    case 10: c+=((uint32_t)k8[9])<<8;    /* fall through */
+    case 9 : c+=k8[8];                   /* fall through */
+    case 8 : b+=k[1]; a+=k[0]; break;
+    case 7 : b+=((uint32_t)k8[6])<<16;   /* fall through */
+    case 6 : b+=((uint32_t)k8[5])<<8;    /* fall through */
+    case 5 : b+=k8[4];                   /* fall through */
+    case 4 : a+=k[0]; break;
+    case 3 : a+=((uint32_t)k8[2])<<16;   /* fall through */
+    case 2 : a+=((uint32_t)k8[1])<<8;    /* fall through */
+    case 1 : a+=k8[0]; break;
+    case 0 : *pc=c; *pb=b; return;  /* zero length strings require no mixing */
+    }
+
+#endif /* !valgrind */
+
+  } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
+    const uint16_t *k = (const uint16_t *)key;         /* read 16-bit chunks */
+    const uint8_t  *k8;
+
+    /*--------------- all but last block: aligned reads and different mixing */
+    while (length > 12)
+    {
+      a += k[0] + (((uint32_t)k[1])<<16);
+      b += k[2] + (((uint32_t)k[3])<<16);
+      c += k[4] + (((uint32_t)k[5])<<16);
+      mix(a,b,c);
+      length -= 12;
+      k += 6;
+    }
+
+    /*----------------------------- handle the last (probably partial) block */
+    k8 = (const uint8_t *)k;
+    switch(length)
+    {
+    case 12: c+=k[4]+(((uint32_t)k[5])<<16);
+             b+=k[2]+(((uint32_t)k[3])<<16);
+             a+=k[0]+(((uint32_t)k[1])<<16);
+             break;
+    case 11: c+=((uint32_t)k8[10])<<16;     /* fall through */
+    case 10: c+=k[4];
+             b+=k[2]+(((uint32_t)k[3])<<16);
+             a+=k[0]+(((uint32_t)k[1])<<16);
+             break;
+    case 9 : c+=k8[8];                      /* fall through */
+    case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
+             a+=k[0]+(((uint32_t)k[1])<<16);
+             break;
+    case 7 : b+=((uint32_t)k8[6])<<16;      /* fall through */
+    case 6 : b+=k[2];
+             a+=k[0]+(((uint32_t)k[1])<<16);
+             break;
+    case 5 : b+=k8[4];                      /* fall through */
+    case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
+             break;
+    case 3 : a+=((uint32_t)k8[2])<<16;      /* fall through */
+    case 2 : a+=k[0];
+             break;
+    case 1 : a+=k8[0];
+             break;
+    case 0 : *pc=c; *pb=b; return;  /* zero length strings require no mixing */
+    }
+
+  } else {                        /* need to read the key one byte at a time */
+    const uint8_t *k = (const uint8_t *)key;
+
+    /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
+    while (length > 12)
+    {
+      a += k[0];
+      a += ((uint32_t)k[1])<<8;
+      a += ((uint32_t)k[2])<<16;
+      a += ((uint32_t)k[3])<<24;
+      b += k[4];
+      b += ((uint32_t)k[5])<<8;
+      b += ((uint32_t)k[6])<<16;
+      b += ((uint32_t)k[7])<<24;
+      c += k[8];
+      c += ((uint32_t)k[9])<<8;
+      c += ((uint32_t)k[10])<<16;
+      c += ((uint32_t)k[11])<<24;
+      mix(a,b,c);
+      length -= 12;
+      k += 12;
+    }
+
+    /*-------------------------------- last block: affect all 32 bits of (c) */
+    switch(length)                   /* all the case statements fall through */
+    {
+    case 12: c+=((uint32_t)k[11])<<24;
+    case 11: c+=((uint32_t)k[10])<<16;
+    case 10: c+=((uint32_t)k[9])<<8;
+    case 9 : c+=k[8];
+    case 8 : b+=((uint32_t)k[7])<<24;
+    case 7 : b+=((uint32_t)k[6])<<16;
+    case 6 : b+=((uint32_t)k[5])<<8;
+    case 5 : b+=k[4];
+    case 4 : a+=((uint32_t)k[3])<<24;
+    case 3 : a+=((uint32_t)k[2])<<16;
+    case 2 : a+=((uint32_t)k[1])<<8;
+    case 1 : a+=k[0];
+             break;
+    case 0 : *pc=c; *pb=b; return;  /* zero length strings require no mixing */
+    }
+  }
+
+  final(a,b,c);
+  *pc=c; *pb=b;
+}
+
+#endif /* SELF_TEST */
+
+#if 0	/* currently not used */
+
+/*
+ * hashbig():
+ * This is the same as hashword() on big-endian machines.  It is different
+ * from hashlittle() on all machines.  hashbig() takes advantage of
+ * big-endian byte ordering. 
+ */
+uint32_t hashbig( const void *key, size_t length, uint32_t initval)
+{
+  uint32_t a,b,c;
+  union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */
+
+  /* Set up the internal state */
+  a = b = c = raninit + ((uint32_t)length) + initval;
+
+  u.ptr = key;
+  if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) {
+    const uint32_t *k = (const uint32_t *)key;         /* read 32-bit chunks */
+#ifdef VALGRIND
+    const uint8_t  *k8;
+#endif
+
+    /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
+    while (length > 12)
+    {
+      a += k[0];
+      b += k[1];
+      c += k[2];
+      mix(a,b,c);
+      length -= 12;
+      k += 3;
+    }
+
+    /*----------------------------- handle the last (probably partial) block */
+    /* 
+     * "k[2]<<8" actually reads beyond the end of the string, but
+     * then shifts out the part it's not allowed to read.  Because the
+     * string is aligned, the illegal read is in the same word as the
+     * rest of the string.  Every machine with memory protection I've seen
+     * does it on word boundaries, so is OK with this.  But VALGRIND will
+     * still catch it and complain.  The masking trick does make the hash
+     * noticably faster for short strings (like English words).
+     */
+#ifndef VALGRIND
+
+    switch(length)
+    {
+    case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+    case 11: c+=k[2]&0xffffff00; b+=k[1]; a+=k[0]; break;
+    case 10: c+=k[2]&0xffff0000; b+=k[1]; a+=k[0]; break;
+    case 9 : c+=k[2]&0xff000000; b+=k[1]; a+=k[0]; break;
+    case 8 : b+=k[1]; a+=k[0]; break;
+    case 7 : b+=k[1]&0xffffff00; a+=k[0]; break;
+    case 6 : b+=k[1]&0xffff0000; a+=k[0]; break;
+    case 5 : b+=k[1]&0xff000000; a+=k[0]; break;
+    case 4 : a+=k[0]; break;
+    case 3 : a+=k[0]&0xffffff00; break;
+    case 2 : a+=k[0]&0xffff0000; break;
+    case 1 : a+=k[0]&0xff000000; break;
+    case 0 : return c;              /* zero length strings require no mixing */
+    }
+
+#else  /* make valgrind happy */
+
+    k8 = (const uint8_t *)k;
+    switch(length)                   /* all the case statements fall through */
+    {
+    case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+    case 11: c+=((uint32_t)k8[10])<<8;  /* fall through */
+    case 10: c+=((uint32_t)k8[9])<<16;  /* fall through */
+    case 9 : c+=((uint32_t)k8[8])<<24;  /* fall through */
+    case 8 : b+=k[1]; a+=k[0]; break;
+    case 7 : b+=((uint32_t)k8[6])<<8;   /* fall through */
+    case 6 : b+=((uint32_t)k8[5])<<16;  /* fall through */
+    case 5 : b+=((uint32_t)k8[4])<<24;  /* fall through */
+    case 4 : a+=k[0]; break;
+    case 3 : a+=((uint32_t)k8[2])<<8;   /* fall through */
+    case 2 : a+=((uint32_t)k8[1])<<16;  /* fall through */
+    case 1 : a+=((uint32_t)k8[0])<<24; break;
+    case 0 : return c;
+    }
+
+#endif /* !VALGRIND */
+
+  } else {                        /* need to read the key one byte at a time */
+    const uint8_t *k = (const uint8_t *)key;
+
+    /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
+    while (length > 12)
+    {
+      a += ((uint32_t)k[0])<<24;
+      a += ((uint32_t)k[1])<<16;
+      a += ((uint32_t)k[2])<<8;
+      a += ((uint32_t)k[3]);
+      b += ((uint32_t)k[4])<<24;
+      b += ((uint32_t)k[5])<<16;
+      b += ((uint32_t)k[6])<<8;
+      b += ((uint32_t)k[7]);
+      c += ((uint32_t)k[8])<<24;
+      c += ((uint32_t)k[9])<<16;
+      c += ((uint32_t)k[10])<<8;
+      c += ((uint32_t)k[11]);
+      mix(a,b,c);
+      length -= 12;
+      k += 12;
+    }
+
+    /*-------------------------------- last block: affect all 32 bits of (c) */
+    switch(length)                   /* all the case statements fall through */
+    {
+    case 12: c+=k[11];
+    case 11: c+=((uint32_t)k[10])<<8;
+    case 10: c+=((uint32_t)k[9])<<16;
+    case 9 : c+=((uint32_t)k[8])<<24;
+    case 8 : b+=k[7];
+    case 7 : b+=((uint32_t)k[6])<<8;
+    case 6 : b+=((uint32_t)k[5])<<16;
+    case 5 : b+=((uint32_t)k[4])<<24;
+    case 4 : a+=k[3];
+    case 3 : a+=((uint32_t)k[2])<<8;
+    case 2 : a+=((uint32_t)k[1])<<16;
+    case 1 : a+=((uint32_t)k[0])<<24;
+             break;
+    case 0 : return c;
+    }
+  }
+
+  final(a,b,c);
+  return c;
+}
+
+#endif /* 0 == currently not used */
+
+#ifdef SELF_TEST
+
+/* used for timings */
+void driver1()
+{
+  uint8_t buf[256];
+  uint32_t i;
+  uint32_t h=0;
+  time_t a,z;
+
+  time(&a);
+  for (i=0; i<256; ++i) buf[i] = 'x';
+  for (i=0; i<1; ++i) 
+  {
+    h = hashlittle(&buf[0],1,h);
+  }
+  time(&z);
+  if (z-a > 0) printf("time %d %.8x\n", z-a, h);
+}
+
+/* check that every input bit changes every output bit half the time */
+#define HASHSTATE 1
+#define HASHLEN   1
+#define MAXPAIR 60
+#define MAXLEN  70
+void driver2()
+{
+  uint8_t qa[MAXLEN+1], qb[MAXLEN+2], *a = &qa[0], *b = &qb[1];
+  uint32_t c[HASHSTATE], d[HASHSTATE], i=0, j=0, k, l, m=0, z;
+  uint32_t e[HASHSTATE],f[HASHSTATE],g[HASHSTATE],h[HASHSTATE];
+  uint32_t x[HASHSTATE],y[HASHSTATE];
+  uint32_t hlen;
+
+  printf("No more than %d trials should ever be needed \n",MAXPAIR/2);
+  for (hlen=0; hlen < MAXLEN; ++hlen)
+  {
+    z=0;
+    for (i=0; i<hlen; ++i)  /*----------------------- for each input byte, */
+    {
+      for (j=0; j<8; ++j)   /*------------------------ for each input bit, */
+      {
+	for (m=1; m<8; ++m) /*------------ for serveral possible initvals, */
+	{
+	  for (l=0; l<HASHSTATE; ++l)
+	    e[l]=f[l]=g[l]=h[l]=x[l]=y[l]=~((uint32_t)0);
+
+      	  /*---- check that every output bit is affected by that input bit */
+	  for (k=0; k<MAXPAIR; k+=2)
+	  { 
+	    uint32_t finished=1;
+	    /* keys have one bit different */
+	    for (l=0; l<hlen+1; ++l) {a[l] = b[l] = (uint8_t)0;}
+	    /* have a and b be two keys differing in only one bit */
+	    a[i] ^= (k<<j);
+	    a[i] ^= (k>>(8-j));
+	     c[0] = hashlittle(a, hlen, m);
+	    b[i] ^= ((k+1)<<j);
+	    b[i] ^= ((k+1)>>(8-j));
+	     d[0] = hashlittle(b, hlen, m);
+	    /* check every bit is 1, 0, set, and not set at least once */
+	    for (l=0; l<HASHSTATE; ++l)
+	    {
+	      e[l] &= (c[l]^d[l]);
+	      f[l] &= ~(c[l]^d[l]);
+	      g[l] &= c[l];
+	      h[l] &= ~c[l];
+	      x[l] &= d[l];
+	      y[l] &= ~d[l];
+	      if (e[l]|f[l]|g[l]|h[l]|x[l]|y[l]) finished=0;
+	    }
+	    if (finished) break;
+	  }
+	  if (k>z) z=k;
+	  if (k==MAXPAIR) 
+	  {
+	     printf("Some bit didn't change: ");
+	     printf("%.8x %.8x %.8x %.8x %.8x %.8x  ",
+	            e[0],f[0],g[0],h[0],x[0],y[0]);
+	     printf("i %d j %d m %d len %d\n", i, j, m, hlen);
+	  }
+	  if (z==MAXPAIR) goto done;
+	}
+      }
+    }
+   done:
+    if (z < MAXPAIR)
+    {
+      printf("Mix success  %2d bytes  %2d initvals  ",i,m);
+      printf("required  %d  trials\n", z/2);
+    }
+  }
+  printf("\n");
+}
+
+/* Check for reading beyond the end of the buffer and alignment problems */
+void driver3()
+{
+  uint8_t buf[MAXLEN+20], *b;
+  uint32_t len;
+  uint8_t q[] = "This is the time for all good men to come to the aid of their country...";
+  uint32_t h;
+  uint8_t qq[] = "xThis is the time for all good men to come to the aid of their country...";
+  uint32_t i;
+  uint8_t qqq[] = "xxThis is the time for all good men to come to the aid of their country...";
+  uint32_t j;
+  uint8_t qqqq[] = "xxxThis is the time for all good men to come to the aid of their country...";
+  uint32_t ref,x,y;
+  uint8_t *p;
+
+  printf("Endianness.  These lines should all be the same (for values filled in):\n");
+  printf("%.8x                            %.8x                            %.8x\n",
+         hashword((const uint32_t *)q, (sizeof(q)-1)/4, 13),
+         hashword((const uint32_t *)q, (sizeof(q)-5)/4, 13),
+         hashword((const uint32_t *)q, (sizeof(q)-9)/4, 13));
+  p = q;
+  printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
+         hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13),
+         hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13),
+         hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13),
+         hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13),
+         hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13),
+         hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13));
+  p = &qq[1];
+  printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
+         hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13),
+         hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13),
+         hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13),
+         hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13),
+         hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13),
+         hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13));
+  p = &qqq[2];
+  printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
+         hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13),
+         hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13),
+         hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13),
+         hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13),
+         hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13),
+         hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13));
+  p = &qqqq[3];
+  printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
+         hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13),
+         hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13),
+         hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13),
+         hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13),
+         hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13),
+         hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13));
+  printf("\n");
+
+  /* check that hashlittle2 and hashlittle produce the same results */
+  i=47; j=0;
+  hashlittle2(q, sizeof(q), &i, &j);
+  if (hashlittle(q, sizeof(q), 47) != i)
+    printf("hashlittle2 and hashlittle mismatch\n");
+
+  /* check that hashword2 and hashword produce the same results */
+  len = raninit;
+  i=47, j=0;
+  hashword2(&len, 1, &i, &j);
+  if (hashword(&len, 1, 47) != i)
+    printf("hashword2 and hashword mismatch %x %x\n", 
+	   i, hashword(&len, 1, 47));
+
+  /* check hashlittle doesn't read before or after the ends of the string */
+  for (h=0, b=buf+1; h<8; ++h, ++b)
+  {
+    for (i=0; i<MAXLEN; ++i)
+    {
+      len = i;
+      for (j=0; j<i; ++j) *(b+j)=0;
+
+      /* these should all be equal */
+      ref = hashlittle(b, len, (uint32_t)1);
+      *(b+i)=(uint8_t)~0;
+      *(b-1)=(uint8_t)~0;
+      x = hashlittle(b, len, (uint32_t)1);
+      y = hashlittle(b, len, (uint32_t)1);
+      if ((ref != x) || (ref != y)) 
+      {
+	printf("alignment error: %.8x %.8x %.8x %d %d\n",ref,x,y,
+               h, i);
+      }
+    }
+  }
+}
+
+/* check for problems with nulls */
+ void driver4()
+{
+  uint8_t buf[1];
+  uint32_t h,i,state[HASHSTATE];
+
+
+  buf[0] = ~0;
+  for (i=0; i<HASHSTATE; ++i) state[i] = 1;
+  printf("These should all be different\n");
+  for (i=0, h=0; i<8; ++i)
+  {
+    h = hashlittle(buf, 0, h);
+    printf("%2ld  0-byte strings, hash is  %.8x\n", i, h);
+  }
+}
+
+
+int main()
+{
+  driver1();   /* test that the key is hashed: used for timings */
+  driver2();   /* test that whole key is hashed thoroughly */
+  driver3();   /* test that nothing but the key is hashed */
+  driver4();   /* test hashing multiple buffers (all buffers are null) */
+  return 1;
+}
+
+#endif  /* SELF_TEST */
diff --git a/3rdParty/Unbound/src/src/util/storage/lookup3.h b/3rdParty/Unbound/src/src/util/storage/lookup3.h
new file mode 100644
index 0000000..06211fd
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/storage/lookup3.h
@@ -0,0 +1,71 @@
+/*
+ * util/storage/lookup3.h - header file for hashing functions.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains header definitions for the hash functions we use.
+ * The hash functions are public domain (see lookup3.c).
+ */
+
+#ifndef UTIL_STORAGE_LOOKUP3_H
+#define UTIL_STORAGE_LOOKUP3_H
+
+/**
+ * Hash key made of 4byte chunks.
+ * @param k: the key, an array of uint32_t values
+ * @param length: the length of the key, in uint32_ts
+ * @param initval: the previous hash, or an arbitrary value
+ * @return: hash value.
+ */
+uint32_t hashword(const uint32_t *k, size_t length, uint32_t initval);
+
+/**
+ * Hash key data.
+ * @param k: the key, array of uint8_t
+ * @param length: the length of the key, in uint8_ts
+ * @param initval: the previous hash, or an arbitrary value
+ * @return: hash value.
+ */
+uint32_t hashlittle(const void *k, size_t length, uint32_t initval);
+
+/**
+ * Set the randomisation initial value, set this before threads start,
+ * and before hashing stuff (because it changes subsequent results).
+ * @param v: value
+ */
+void hash_set_raninit(uint32_t v);
+
+#endif /* UTIL_STORAGE_LOOKUP3_H */
diff --git a/3rdParty/Unbound/src/src/util/storage/lruhash.c b/3rdParty/Unbound/src/src/util/storage/lruhash.c
new file mode 100644
index 0000000..c22278d
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/storage/lruhash.c
@@ -0,0 +1,544 @@
+/*
+ * util/storage/lruhash.c - hashtable, hash function, LRU keeping.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains a hashtable with LRU keeping of entries.
+ *
+ */
+
+#include "config.h"
+#include "util/storage/lruhash.h"
+#include "util/fptr_wlist.h"
+
+void
+bin_init(struct lruhash_bin* array, size_t size)
+{
+	size_t i;
+#ifdef THREADS_DISABLED
+	(void)array;
+#endif
+	for(i=0; i<size; i++) {
+		lock_quick_init(&array[i].lock);
+		lock_protect(&array[i].lock, &array[i], 
+			sizeof(struct lruhash_bin));
+	}
+}
+
+struct lruhash* 
+lruhash_create(size_t start_size, size_t maxmem, lruhash_sizefunc_t sizefunc, 
+	lruhash_compfunc_t compfunc, lruhash_delkeyfunc_t delkeyfunc, 
+	lruhash_deldatafunc_t deldatafunc, void* arg)
+{
+	struct lruhash* table = (struct lruhash*)calloc(1, 
+		sizeof(struct lruhash));
+	if(!table)
+		return NULL;
+	lock_quick_init(&table->lock);
+	table->sizefunc = sizefunc;
+	table->compfunc = compfunc;
+	table->delkeyfunc = delkeyfunc;
+	table->deldatafunc = deldatafunc;
+	table->cb_arg = arg;
+	table->size = start_size;
+	table->size_mask = (int)(start_size-1);
+	table->lru_start = NULL;
+	table->lru_end = NULL;
+	table->num = 0;
+	table->space_used = 0;
+	table->space_max = maxmem;
+	table->array = calloc(table->size, sizeof(struct lruhash_bin));
+	if(!table->array) {
+		lock_quick_destroy(&table->lock);
+		free(table);
+		return NULL;
+	}
+	bin_init(table->array, table->size);
+	lock_protect(&table->lock, table, sizeof(*table));
+	lock_protect(&table->lock, table->array, 
+		table->size*sizeof(struct lruhash_bin));
+	return table;
+}
+
+void 
+bin_delete(struct lruhash* table, struct lruhash_bin* bin)
+{
+	struct lruhash_entry* p, *np;
+	void *d;
+	if(!bin)
+		return;
+	lock_quick_destroy(&bin->lock);
+	p = bin->overflow_list;
+	bin->overflow_list = NULL;
+	while(p) {
+		np = p->overflow_next;
+		d = p->data;
+		(*table->delkeyfunc)(p->key, table->cb_arg);
+		(*table->deldatafunc)(d, table->cb_arg);
+		p = np;
+	}
+}
+
+void 
+bin_split(struct lruhash* table, struct lruhash_bin* newa, 
+	int newmask)
+{
+	size_t i;
+	struct lruhash_entry *p, *np;
+	struct lruhash_bin* newbin;
+	/* move entries to new table. Notice that since hash x is mapped to
+	 * bin x & mask, and new mask uses one more bit, so all entries in
+	 * one bin will go into the old bin or bin | newbit */
+#ifndef THREADS_DISABLED
+	int newbit = newmask - table->size_mask;
+#endif
+	/* so, really, this task could also be threaded, per bin. */
+	/* LRU list is not changed */
+	for(i=0; i<table->size; i++)
+	{
+		lock_quick_lock(&table->array[i].lock);
+		p = table->array[i].overflow_list;
+		/* lock both destination bins */
+		lock_quick_lock(&newa[i].lock);
+		lock_quick_lock(&newa[newbit|i].lock);
+		while(p) {
+			np = p->overflow_next;
+			/* link into correct new bin */
+			newbin = &newa[p->hash & newmask];
+			p->overflow_next = newbin->overflow_list;
+			newbin->overflow_list = p;
+			p=np;
+		}
+		lock_quick_unlock(&newa[i].lock);
+		lock_quick_unlock(&newa[newbit|i].lock);
+		lock_quick_unlock(&table->array[i].lock);
+	}
+}
+
+void 
+lruhash_delete(struct lruhash* table)
+{
+	size_t i;
+	if(!table)
+		return;
+	/* delete lock on hashtable to force check its OK */
+	lock_quick_destroy(&table->lock);
+	for(i=0; i<table->size; i++)
+		bin_delete(table, &table->array[i]);
+	free(table->array);
+	free(table);
+}
+
+void 
+bin_overflow_remove(struct lruhash_bin* bin, struct lruhash_entry* entry)
+{
+	struct lruhash_entry* p = bin->overflow_list;
+	struct lruhash_entry** prevp = &bin->overflow_list;
+	while(p) {
+		if(p == entry) {
+			*prevp = p->overflow_next;
+			return;
+		}
+		prevp = &p->overflow_next;
+		p = p->overflow_next;
+	}
+}
+
+void 
+reclaim_space(struct lruhash* table, struct lruhash_entry** list)
+{
+	struct lruhash_entry* d;
+	struct lruhash_bin* bin;
+	log_assert(table);
+	/* does not delete MRU entry, so table will not be empty. */
+	while(table->num > 1 && table->space_used > table->space_max) {
+		/* notice that since we hold the hashtable lock, nobody
+		   can change the lru chain. So it cannot be deleted underneath
+		   us. We still need the hashbin and entry write lock to make 
+		   sure we flush all users away from the entry. 
+		   which is unlikely, since it is LRU, if someone got a rdlock
+		   it would be moved to front, but to be sure. */
+		d = table->lru_end;
+		/* specialised, delete from end of double linked list,
+		   and we know num>1, so there is a previous lru entry. */
+		log_assert(d && d->lru_prev);
+		table->lru_end = d->lru_prev;
+		d->lru_prev->lru_next = NULL;
+		/* schedule entry for deletion */
+		bin = &table->array[d->hash & table->size_mask];
+		table->num --;
+		lock_quick_lock(&bin->lock);
+		bin_overflow_remove(bin, d);
+		d->overflow_next = *list;
+		*list = d;
+		lock_rw_wrlock(&d->lock);
+		table->space_used -= table->sizefunc(d->key, d->data);
+		if(table->markdelfunc)
+			(*table->markdelfunc)(d->key);
+		lock_rw_unlock(&d->lock);
+		lock_quick_unlock(&bin->lock);
+	}
+}
+
+struct lruhash_entry* 
+bin_find_entry(struct lruhash* table, 
+	struct lruhash_bin* bin, hashvalue_t hash, void* key)
+{
+	struct lruhash_entry* p = bin->overflow_list;
+	while(p) {
+		if(p->hash == hash && table->compfunc(p->key, key) == 0)
+			return p;
+		p = p->overflow_next;
+	}
+	return NULL;
+}
+
+void 
+table_grow(struct lruhash* table)
+{
+	struct lruhash_bin* newa;
+	int newmask;
+	size_t i;
+	if(table->size_mask == (int)(((size_t)-1)>>1)) {
+		log_err("hash array malloc: size_t too small");
+		return;
+	}
+	/* try to allocate new array, if not fail */
+	newa = calloc(table->size*2, sizeof(struct lruhash_bin));
+	if(!newa) {
+		log_err("hash grow: malloc failed");
+		/* continue with smaller array. Though its slower. */
+		return;
+	}
+	bin_init(newa, table->size*2);
+	newmask = (table->size_mask << 1) | 1;
+	bin_split(table, newa, newmask);
+	/* delete the old bins */
+	lock_unprotect(&table->lock, table->array);
+	for(i=0; i<table->size; i++) {
+		lock_quick_destroy(&table->array[i].lock);
+	}
+	free(table->array);
+	
+	table->size *= 2;
+	table->size_mask = newmask;
+	table->array = newa;
+	lock_protect(&table->lock, table->array, 
+		table->size*sizeof(struct lruhash_bin));
+	return;
+}
+
+void 
+lru_front(struct lruhash* table, struct lruhash_entry* entry)
+{
+	entry->lru_prev = NULL;
+	entry->lru_next = table->lru_start;
+	if(!table->lru_start)
+		table->lru_end = entry;
+	else	table->lru_start->lru_prev = entry;
+	table->lru_start = entry;
+}
+
+void 
+lru_remove(struct lruhash* table, struct lruhash_entry* entry)
+{
+	if(entry->lru_prev)
+		entry->lru_prev->lru_next = entry->lru_next;
+	else	table->lru_start = entry->lru_next;
+	if(entry->lru_next)
+		entry->lru_next->lru_prev = entry->lru_prev;
+	else	table->lru_end = entry->lru_prev;
+}
+
+void 
+lru_touch(struct lruhash* table, struct lruhash_entry* entry)
+{
+	log_assert(table && entry);
+	if(entry == table->lru_start)
+		return; /* nothing to do */
+	/* remove from current lru position */
+	lru_remove(table, entry);
+	/* add at front */
+	lru_front(table, entry);
+}
+
+void 
+lruhash_insert(struct lruhash* table, hashvalue_t hash,
+        struct lruhash_entry* entry, void* data, void* cb_arg)
+{
+	struct lruhash_bin* bin;
+	struct lruhash_entry* found, *reclaimlist=NULL;
+	size_t need_size;
+	fptr_ok(fptr_whitelist_hash_sizefunc(table->sizefunc));
+	fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc));
+	fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc));
+	fptr_ok(fptr_whitelist_hash_compfunc(table->compfunc));
+	fptr_ok(fptr_whitelist_hash_markdelfunc(table->markdelfunc));
+	need_size = table->sizefunc(entry->key, data);
+	if(cb_arg == NULL) cb_arg = table->cb_arg;
+
+	/* find bin */
+	lock_quick_lock(&table->lock);
+	bin = &table->array[hash & table->size_mask];
+	lock_quick_lock(&bin->lock);
+
+	/* see if entry exists already */
+	if(!(found=bin_find_entry(table, bin, hash, entry->key))) {
+		/* if not: add to bin */
+		entry->overflow_next = bin->overflow_list;
+		bin->overflow_list = entry;
+		lru_front(table, entry);
+		table->num++;
+		table->space_used += need_size;
+	} else {
+		/* if so: update data - needs a writelock */
+		table->space_used += need_size -
+			(*table->sizefunc)(found->key, found->data);
+		(*table->delkeyfunc)(entry->key, cb_arg);
+		lru_touch(table, found);
+		lock_rw_wrlock(&found->lock);
+		(*table->deldatafunc)(found->data, cb_arg);
+		found->data = data;
+		lock_rw_unlock(&found->lock);
+	}
+	lock_quick_unlock(&bin->lock);
+	if(table->space_used > table->space_max)
+		reclaim_space(table, &reclaimlist);
+	if(table->num >= table->size)
+		table_grow(table);
+	lock_quick_unlock(&table->lock);
+
+	/* finish reclaim if any (outside of critical region) */
+	while(reclaimlist) {
+		struct lruhash_entry* n = reclaimlist->overflow_next;
+		void* d = reclaimlist->data;
+		(*table->delkeyfunc)(reclaimlist->key, cb_arg);
+		(*table->deldatafunc)(d, cb_arg);
+		reclaimlist = n;
+	}
+}
+
+struct lruhash_entry* 
+lruhash_lookup(struct lruhash* table, hashvalue_t hash, void* key, int wr)
+{
+	struct lruhash_entry* entry;
+	struct lruhash_bin* bin;
+	fptr_ok(fptr_whitelist_hash_compfunc(table->compfunc));
+
+	lock_quick_lock(&table->lock);
+	bin = &table->array[hash & table->size_mask];
+	lock_quick_lock(&bin->lock);
+	if((entry=bin_find_entry(table, bin, hash, key)))
+		lru_touch(table, entry);
+	lock_quick_unlock(&table->lock);
+
+	if(entry) {
+		if(wr)	{ lock_rw_wrlock(&entry->lock); }
+		else	{ lock_rw_rdlock(&entry->lock); }
+	}
+	lock_quick_unlock(&bin->lock);
+	return entry;
+}
+
+void 
+lruhash_remove(struct lruhash* table, hashvalue_t hash, void* key)
+{
+	struct lruhash_entry* entry;
+	struct lruhash_bin* bin;
+	void *d;
+	fptr_ok(fptr_whitelist_hash_sizefunc(table->sizefunc));
+	fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc));
+	fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc));
+	fptr_ok(fptr_whitelist_hash_compfunc(table->compfunc));
+	fptr_ok(fptr_whitelist_hash_markdelfunc(table->markdelfunc));
+
+	lock_quick_lock(&table->lock);
+	bin = &table->array[hash & table->size_mask];
+	lock_quick_lock(&bin->lock);
+	if((entry=bin_find_entry(table, bin, hash, key))) {
+		bin_overflow_remove(bin, entry);
+		lru_remove(table, entry);
+	} else {
+		lock_quick_unlock(&table->lock);
+		lock_quick_unlock(&bin->lock);
+		return;
+	}
+	table->num--;
+	table->space_used -= (*table->sizefunc)(entry->key, entry->data);
+	lock_quick_unlock(&table->lock);
+	lock_rw_wrlock(&entry->lock);
+	if(table->markdelfunc)
+		(*table->markdelfunc)(entry->key);
+	lock_rw_unlock(&entry->lock);
+	lock_quick_unlock(&bin->lock);
+	/* finish removal */
+	d = entry->data;
+	(*table->delkeyfunc)(entry->key, table->cb_arg);
+	(*table->deldatafunc)(d, table->cb_arg);
+}
+
+/** clear bin, respecting locks, does not do space, LRU */
+static void
+bin_clear(struct lruhash* table, struct lruhash_bin* bin)
+{
+	struct lruhash_entry* p, *np;
+	void *d;
+	lock_quick_lock(&bin->lock);
+	p = bin->overflow_list; 
+	while(p) {
+		lock_rw_wrlock(&p->lock);
+		np = p->overflow_next;
+		d = p->data;
+		if(table->markdelfunc)
+			(*table->markdelfunc)(p->key);
+		lock_rw_unlock(&p->lock);
+		(*table->delkeyfunc)(p->key, table->cb_arg);
+		(*table->deldatafunc)(d, table->cb_arg);
+		p = np;
+	}
+	bin->overflow_list = NULL;
+	lock_quick_unlock(&bin->lock);
+}
+
+void
+lruhash_clear(struct lruhash* table)
+{
+	size_t i;
+	if(!table)
+		return;
+	fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc));
+	fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc));
+	fptr_ok(fptr_whitelist_hash_markdelfunc(table->markdelfunc));
+
+	lock_quick_lock(&table->lock);
+	for(i=0; i<table->size; i++) {
+		bin_clear(table, &table->array[i]);
+	}
+	table->lru_start = NULL;
+	table->lru_end = NULL;
+	table->num = 0;
+	table->space_used = 0;
+	lock_quick_unlock(&table->lock);
+}
+
+void 
+lruhash_status(struct lruhash* table, const char* id, int extended)
+{
+	lock_quick_lock(&table->lock);
+	log_info("%s: %u entries, memory %u / %u",
+		id, (unsigned)table->num, (unsigned)table->space_used,
+		(unsigned)table->space_max);
+	log_info("  itemsize %u, array %u, mask %d",
+		(unsigned)(table->num? table->space_used/table->num : 0),
+		(unsigned)table->size, table->size_mask);
+	if(extended) {
+		size_t i;
+		int min=(int)table->size*2, max=-2;
+		for(i=0; i<table->size; i++) {
+			int here = 0;
+			struct lruhash_entry *en;
+			lock_quick_lock(&table->array[i].lock);
+			en = table->array[i].overflow_list;
+			while(en) {
+				here ++;
+				en = en->overflow_next;
+			}
+			lock_quick_unlock(&table->array[i].lock);
+			if(extended >= 2)
+				log_info("bin[%d] %d", (int)i, here);
+			if(here > max) max = here;
+			if(here < min) min = here;
+		}
+		log_info("  bin min %d, avg %.2lf, max %d", min, 
+			(double)table->num/(double)table->size, max);
+	}
+	lock_quick_unlock(&table->lock);
+}
+
+size_t
+lruhash_get_mem(struct lruhash* table)
+{
+	size_t s;
+	lock_quick_lock(&table->lock);
+	s = sizeof(struct lruhash) + table->space_used;
+#ifdef USE_THREAD_DEBUG
+	if(table->size != 0) {
+		size_t i;
+		for(i=0; i<table->size; i++)
+			s += sizeof(struct lruhash_bin) + 
+				lock_get_mem(&table->array[i].lock);
+	}
+#else /* no THREAD_DEBUG */
+	if(table->size != 0)
+		s += (table->size)*(sizeof(struct lruhash_bin) + 
+			lock_get_mem(&table->array[0].lock));
+#endif
+	lock_quick_unlock(&table->lock);
+	s += lock_get_mem(&table->lock);
+	return s;
+}
+
+void 
+lruhash_setmarkdel(struct lruhash* table, lruhash_markdelfunc_t md)
+{
+	lock_quick_lock(&table->lock);
+	table->markdelfunc = md;
+	lock_quick_unlock(&table->lock);
+}
+
+void 
+lruhash_traverse(struct lruhash* h, int wr, 
+	void (*func)(struct lruhash_entry*, void*), void* arg)
+{
+	size_t i;
+	struct lruhash_entry* e;
+
+	lock_quick_lock(&h->lock);
+	for(i=0; i<h->size; i++) {
+		lock_quick_lock(&h->array[i].lock);
+		for(e = h->array[i].overflow_list; e; e = e->overflow_next) {
+			if(wr) {
+				lock_rw_wrlock(&e->lock);
+			} else {
+				lock_rw_rdlock(&e->lock);
+			}
+			(*func)(e, arg);
+			lock_rw_unlock(&e->lock);
+		}
+		lock_quick_unlock(&h->array[i].lock);
+	}
+	lock_quick_unlock(&h->lock);
+}
diff --git a/3rdParty/Unbound/src/src/util/storage/lruhash.h b/3rdParty/Unbound/src/src/util/storage/lruhash.h
new file mode 100644
index 0000000..d0efe2d
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/storage/lruhash.h
@@ -0,0 +1,414 @@
+/*
+ * util/storage/lruhash.h - hashtable, hash function, LRU keeping.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains a hashtable with LRU keeping of entries.
+ *
+ * The hash table keeps a maximum memory size. Old entries are removed
+ * to make space for new entries.
+ *
+ * The locking strategy is as follows:
+ * 	o since (almost) every read also implies a LRU update, the
+ *	  hashtable lock is a spinlock, not rwlock.
+ *	o the idea is to move every thread through the hash lock quickly,
+ *	  so that the next thread can access the lookup table.
+ *	o User performs hash function.
+ *
+ * For read:
+ *	o lock hashtable.
+ *		o lookup hash bin.
+ *		o lock hash bin.
+ *			o find entry (if failed, unlock hash, unl bin, exit).
+ *			o swizzle pointers for LRU update.
+ *		o unlock hashtable.
+ *		o lock entry (rwlock).
+ *		o unlock hash bin.
+ *		o work on entry.
+ *	o unlock entry.
+ *
+ * To update an entry, gain writelock and change the entry.
+ * (the entry must keep the same hashvalue, so a data update.)
+ * (you cannot upgrade a readlock to a writelock, because the item may
+ *  be deleted, it would cause race conditions. So instead, unlock and
+ *  relookup it in the hashtable.)
+ *
+ * To delete an entry:
+ *	o unlock the entry if you hold the lock already.
+ *	o lock hashtable.
+ *		o lookup hash bin.
+ *		o lock hash bin.
+ *			o find entry (if failed, unlock hash, unl bin, exit).
+ *			o remove entry from hashtable bin overflow chain.
+ *		o unlock hashtable.
+ *		o lock entry (writelock).
+ *		o unlock hash bin.
+ *	o unlock entry (nobody else should be waiting for this lock,
+ *	  since you removed it from hashtable, and you got writelock while
+ *	  holding the hashbinlock so you are the only one.)
+ * 	  Note you are only allowed to obtain a lock while holding hashbinlock.
+ *	o delete entry.
+ *
+ * The above sequence is:
+ *	o race free, works with read, write and delete.
+ *	o but has a queue, imagine someone needing a writelock on an item.
+ *	  but there are still readlocks. The writelocker waits, but holds
+ *	  the hashbinlock. The next thread that comes in and needs the same
+ * 	  hashbin will wait for the lock while holding the hashtable lock.
+ *	  thus halting the entire system on hashtable.
+ *	  This is because of the delete protection. 
+ *	  Readlocks will be easier on the rwlock on entries.
+ *	  While the writer is holding writelock, similar problems happen with
+ *	  a reader or writer needing the same item.
+ *	  the scenario requires more than three threads.
+ * 	o so the queue length is 3 threads in a bad situation. The fourth is
+ *	  unable to use the hashtable.
+ *
+ * If you need to acquire locks on multiple items from the hashtable.
+ *	o you MUST release all locks on items from the hashtable before
+ *	  doing the next lookup/insert/delete/whatever.
+ *	o To acquire multiple items you should use a special routine that
+ *	  obtains the locks on those multiple items in one go.
+ */
+
+#ifndef UTIL_STORAGE_LRUHASH_H
+#define UTIL_STORAGE_LRUHASH_H
+#include "util/locks.h"
+struct lruhash_bin;
+struct lruhash_entry;
+
+/** default start size for hash arrays */
+#define HASH_DEFAULT_STARTARRAY		1024 /* entries in array */
+/** default max memory for hash arrays */
+#define HASH_DEFAULT_MAXMEM		4*1024*1024 /* bytes */
+
+/** the type of a hash value */
+typedef uint32_t hashvalue_t;
+
+/** 
+ * Type of function that calculates the size of an entry.
+ * Result must include the size of struct lruhash_entry. 
+ * Keys that are identical must also calculate to the same size.
+ * size = func(key, data).
+ */
+typedef size_t (*lruhash_sizefunc_t)(void*, void*);
+
+/** type of function that compares two keys. return 0 if equal. */
+typedef int (*lruhash_compfunc_t)(void*, void*);
+
+/** old keys are deleted. 
+ * The RRset type has to revoke its ID number, markdel() is used first.
+ * This function is called: func(key, userarg) */
+typedef void (*lruhash_delkeyfunc_t)(void*, void*);
+
+/** old data is deleted. This function is called: func(data, userarg). */
+typedef void (*lruhash_deldatafunc_t)(void*, void*);
+
+/** mark a key as pending to be deleted (and not to be used by anyone). 
+ * called: func(key) */
+typedef void (*lruhash_markdelfunc_t)(void*);
+
+/**
+ * Hash table that keeps LRU list of entries.
+ */
+struct lruhash {
+	/** lock for exclusive access, to the lookup array */
+	lock_quick_t lock;
+	/** the size function for entries in this table */
+	lruhash_sizefunc_t sizefunc;
+	/** the compare function for entries in this table. */
+	lruhash_compfunc_t compfunc;
+	/** how to delete keys. */
+	lruhash_delkeyfunc_t delkeyfunc;
+	/** how to delete data. */
+	lruhash_deldatafunc_t deldatafunc;
+	/** how to mark a key pending deletion */
+	lruhash_markdelfunc_t markdelfunc;
+	/** user argument for user functions */
+	void* cb_arg;
+
+	/** the size of the lookup array */
+	size_t size;
+	/** size bitmask - since size is a power of 2 */
+	int size_mask;
+	/** lookup array of bins */
+	struct lruhash_bin* array;
+
+	/** the lru list, start and end, noncyclical double linked list. */
+	struct lruhash_entry* lru_start;
+	/** lru list end item (least recently used) */
+	struct lruhash_entry* lru_end;
+
+	/** the number of entries in the hash table. */
+	size_t num;
+	/** the amount of space used, roughly the number of bytes in use. */
+	size_t space_used;
+	/** the amount of space the hash table is maximally allowed to use. */
+	size_t space_max;
+};
+
+/**
+ * A single bin with a linked list of entries in it.
+ */
+struct lruhash_bin {
+	/** 
+	 * Lock for exclusive access to the linked list
+	 * This lock makes deletion of items safe in this overflow list.
+	 */
+	lock_quick_t lock;
+	/** linked list of overflow entries */
+	struct lruhash_entry* overflow_list;
+};
+
+/**
+ * An entry into the hash table.
+ * To change overflow_next you need to hold the bin lock.
+ * To change the lru items you need to hold the hashtable lock.
+ * This structure is designed as part of key struct. And key pointer helps
+ * to get the surrounding structure. Data should be allocated on its own.
+ */
+struct lruhash_entry {
+	/** 
+	 * rwlock for access to the contents of the entry
+	 * Note that it does _not_ cover the lru_ and overflow_ ptrs.
+	 * Even with a writelock, you cannot change hash and key.
+	 * You need to delete it to change hash or key.
+	 */
+	lock_rw_t lock;
+	/** next entry in overflow chain. Covered by hashlock and binlock. */
+	struct lruhash_entry* overflow_next;
+	/** next entry in lru chain. covered by hashlock. */
+	struct lruhash_entry* lru_next;
+	/** prev entry in lru chain. covered by hashlock. */
+	struct lruhash_entry* lru_prev;
+	/** hash value of the key. It may not change, until entry deleted. */
+	hashvalue_t hash;
+	/** key */
+	void* key;
+	/** data */
+	void* data;
+};
+
+/**
+ * Create new hash table.
+ * @param start_size: size of hashtable array at start, must be power of 2.
+ * @param maxmem: maximum amount of memory this table is allowed to use.
+ * @param sizefunc: calculates memory usage of entries.
+ * @param compfunc: compares entries, 0 on equality.
+ * @param delkeyfunc: deletes key.
+ *   Calling both delkey and deldata will also free the struct lruhash_entry.
+ *   Make it part of the key structure and delete it in delkeyfunc.
+ * @param deldatafunc: deletes data. 
+ * @param arg: user argument that is passed to user function calls.
+ * @return: new hash table or NULL on malloc failure.
+ */
+struct lruhash* lruhash_create(size_t start_size, size_t maxmem,
+	lruhash_sizefunc_t sizefunc, lruhash_compfunc_t compfunc,
+	lruhash_delkeyfunc_t delkeyfunc, lruhash_deldatafunc_t deldatafunc, 
+	void* arg);
+
+/**
+ * Delete hash table. Entries are all deleted.
+ * @param table: to delete.
+ */
+void lruhash_delete(struct lruhash* table);
+
+/**
+ * Clear hash table. Entries are all deleted, while locking them before 
+ * doing so. At end the table is empty.
+ * @param table: to make empty.
+ */
+void lruhash_clear(struct lruhash* table);
+
+/**
+ * Insert a new element into the hashtable. 
+ * If key is already present data pointer in that entry is updated.
+ * The space calculation function is called with the key, data.
+ * If necessary the least recently used entries are deleted to make space.
+ * If necessary the hash array is grown up.
+ *
+ * @param table: hash table.
+ * @param hash: hash value. User calculates the hash.
+ * @param entry: identifies the entry.
+ * 	If key already present, this entry->key is deleted immediately.
+ *	But entry->data is set to NULL before deletion, and put into
+ * 	the existing entry. The data is then freed.
+ * @param data: the data.
+ * @param cb_override: if not null overrides the cb_arg for the deletefunc.
+ */
+void lruhash_insert(struct lruhash* table, hashvalue_t hash, 
+	struct lruhash_entry* entry, void* data, void* cb_override);
+
+/**
+ * Lookup an entry in the hashtable.
+ * At the end of the function you hold a (read/write)lock on the entry.
+ * The LRU is updated for the entry (if found).
+ * @param table: hash table.
+ * @param hash: hash of key.
+ * @param key: what to look for, compared against entries in overflow chain.
+ *    the hash value must be set, and must work with compare function.
+ * @param wr: set to true if you desire a writelock on the entry.
+ *    with a writelock you can update the data part.
+ * @return: pointer to the entry or NULL. The entry is locked.
+ *    The user must unlock the entry when done.
+ */
+struct lruhash_entry* lruhash_lookup(struct lruhash* table, hashvalue_t hash, 
+	void* key, int wr);
+
+/**
+ * Touch entry, so it becomes the most recently used in the LRU list.
+ * Caller must hold hash table lock. The entry must be inserted already.
+ * @param table: hash table.
+ * @param entry: entry to make first in LRU.
+ */
+void lru_touch(struct lruhash* table, struct lruhash_entry* entry);
+
+/**
+ * Set the markdelfunction (or NULL)
+ */
+void lruhash_setmarkdel(struct lruhash* table, lruhash_markdelfunc_t md);
+
+/************************* Internal functions ************************/
+/*** these are only exposed for unit tests. ***/
+
+/**
+ * Remove entry from hashtable. Does nothing if not found in hashtable.
+ * Delfunc is called for the entry.
+ * @param table: hash table.
+ * @param hash: hash of key.
+ * @param key: what to look for. 
+ */
+void lruhash_remove(struct lruhash* table, hashvalue_t hash, void* key);
+
+/** init the hash bins for the table */
+void bin_init(struct lruhash_bin* array, size_t size);
+
+/** delete the hash bin and entries inside it */
+void bin_delete(struct lruhash* table, struct lruhash_bin* bin);
+
+/** 
+ * Find entry in hash bin. You must have locked the bin.
+ * @param table: hash table with function pointers.
+ * @param bin: hash bin to look into.
+ * @param hash: hash value to look for.
+ * @param key: key to look for.
+ * @return: the entry or NULL if not found.
+ */
+struct lruhash_entry* bin_find_entry(struct lruhash* table, 
+	struct lruhash_bin* bin, hashvalue_t hash, void* key);
+
+/**
+ * Remove entry from bin overflow chain.
+ * You must have locked the bin.
+ * @param bin: hash bin to look into.
+ * @param entry: entry ptr that needs removal.
+ */
+void bin_overflow_remove(struct lruhash_bin* bin, 
+	struct lruhash_entry* entry);
+
+/**
+ * Split hash bin into two new ones. Based on increased size_mask.
+ * Caller must hold hash table lock.
+ * At the end the routine acquires all hashbin locks (in the old array).
+ * This makes it wait for other threads to finish with the bins.
+ * So the bins are ready to be deleted after this function.
+ * @param table: hash table with function pointers.
+ * @param newa: new increased array.
+ * @param newmask: new lookup mask.
+ */
+void bin_split(struct lruhash* table, struct lruhash_bin* newa, 
+	int newmask);
+
+/** 
+ * Try to make space available by deleting old entries.
+ * Assumes that the lock on the hashtable is being held by caller.
+ * Caller must not hold bin locks.
+ * @param table: hash table.
+ * @param list: list of entries that are to be deleted later.
+ *	Entries have been removed from the hash table and writelock is held.
+ */
+void reclaim_space(struct lruhash* table, struct lruhash_entry** list);
+
+/**
+ * Grow the table lookup array. Becomes twice as large.
+ * Caller must hold the hash table lock. Must not hold any bin locks.
+ * Tries to grow, on malloc failure, nothing happened.
+ * @param table: hash table.
+ */
+void table_grow(struct lruhash* table);
+
+/**
+ * Put entry at front of lru. entry must be unlinked from lru.
+ * Caller must hold hash table lock.
+ * @param table: hash table with lru head and tail.
+ * @param entry: entry to make most recently used.
+ */
+void lru_front(struct lruhash* table, struct lruhash_entry* entry);
+
+/**
+ * Remove entry from lru list.
+ * Caller must hold hash table lock.
+ * @param table: hash table with lru head and tail.
+ * @param entry: entry to remove from lru.
+ */
+void lru_remove(struct lruhash* table, struct lruhash_entry* entry);
+
+/**
+ * Output debug info to the log as to state of the hash table.
+ * @param table: hash table.
+ * @param id: string printed with table to identify the hash table.
+ * @param extended: set to true to print statistics on overflow bin lengths.
+ */
+void lruhash_status(struct lruhash* table, const char* id, int extended);
+
+/**
+ * Get memory in use now by the lruhash table.
+ * @param table: hash table. Will be locked before use. And unlocked after.
+ * @return size in bytes.
+ */
+size_t lruhash_get_mem(struct lruhash* table);
+
+/**
+ * Traverse a lruhash. Call back for every element in the table.
+ * @param h: hash table.  Locked before use.
+ * @param wr: if true writelock is obtained on element, otherwise readlock.
+ * @param func: function for every element. Do not lock or unlock elements.
+ * @param arg: user argument to func.
+ */
+void lruhash_traverse(struct lruhash* h, int wr,
+        void (*func)(struct lruhash_entry*, void*), void* arg);
+
+#endif /* UTIL_STORAGE_LRUHASH_H */
diff --git a/3rdParty/Unbound/src/src/util/storage/slabhash.c b/3rdParty/Unbound/src/src/util/storage/slabhash.c
new file mode 100644
index 0000000..9c0c507
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/storage/slabhash.c
@@ -0,0 +1,219 @@
+/*
+ * util/storage/slabhash.c - hashtable consisting of several smaller tables.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * Implementation of hash table that consists of smaller hash tables.
+ * This results in a partitioned lruhash table.
+ * It cannot grow, but that gives it the ability to have multiple
+ * locks. Also this means there are multiple LRU lists.
+ */
+
+#include "config.h"
+#include "util/storage/slabhash.h"
+
+struct slabhash* slabhash_create(size_t numtables, size_t start_size, 
+	size_t maxmem, lruhash_sizefunc_t sizefunc, 
+	lruhash_compfunc_t compfunc, lruhash_delkeyfunc_t delkeyfunc, 
+	lruhash_deldatafunc_t deldatafunc, void* arg)
+{
+	size_t i;
+	struct slabhash* sl = (struct slabhash*)calloc(1, 
+		sizeof(struct slabhash));
+	if(!sl) return NULL;
+	sl->size = numtables;
+	log_assert(sl->size > 0);
+	sl->array = (struct lruhash**)calloc(sl->size, sizeof(struct lruhash*));
+	if(!sl->array) {
+		free(sl);
+		return NULL;
+	}
+	sl->mask = (uint32_t)(sl->size - 1);
+	if(sl->mask == 0) {
+		sl->shift = 0;
+	} else {
+		log_assert( (sl->size & sl->mask) == 0 
+			/* size must be power of 2 */ );
+		sl->shift = 0;
+		while(!(sl->mask & 0x80000000)) {
+			sl->mask <<= 1;
+			sl->shift ++;
+		}
+	}
+	for(i=0; i<sl->size; i++) {
+		sl->array[i] = lruhash_create(start_size, maxmem / sl->size,
+			sizefunc, compfunc, delkeyfunc, deldatafunc, arg);
+		if(!sl->array[i]) {
+			slabhash_delete(sl);
+			return NULL;
+		}
+	}
+	return sl;
+}
+
+void slabhash_delete(struct slabhash* sl)
+{
+	if(!sl)
+		return;
+	if(sl->array) {
+		size_t i;
+		for(i=0; i<sl->size; i++)
+			lruhash_delete(sl->array[i]);
+		free(sl->array);
+	}
+	free(sl);
+}
+
+void slabhash_clear(struct slabhash* sl)
+{
+	size_t i;
+	if(!sl)
+		return;
+	for(i=0; i<sl->size; i++)
+		lruhash_clear(sl->array[i]);
+}
+
+/** helper routine to calculate the slabhash index */
+static unsigned int
+slab_idx(struct slabhash* sl, hashvalue_t hash)
+{
+	return ((hash & sl->mask) >> sl->shift);
+}
+
+void slabhash_insert(struct slabhash* sl, hashvalue_t hash, 
+	struct lruhash_entry* entry, void* data, void* arg)
+{
+	lruhash_insert(sl->array[slab_idx(sl, hash)], hash, entry, data, arg);
+}
+
+struct lruhash_entry* slabhash_lookup(struct slabhash* sl, 
+	hashvalue_t hash, void* key, int wr)
+{
+	return lruhash_lookup(sl->array[slab_idx(sl, hash)], hash, key, wr);
+}
+
+void slabhash_remove(struct slabhash* sl, hashvalue_t hash, void* key)
+{
+	lruhash_remove(sl->array[slab_idx(sl, hash)], hash, key);
+}
+
+void slabhash_status(struct slabhash* sl, const char* id, int extended)
+{
+	size_t i;
+	char num[17];
+	log_info("Slabhash %s: %u tables mask=%x shift=%d", 
+		id, (unsigned)sl->size, (unsigned)sl->mask, sl->shift);
+	for(i=0; i<sl->size; i++) {
+		snprintf(num, sizeof(num), "table %u", (unsigned)i);
+		lruhash_status(sl->array[i], num, extended);
+	}
+}
+
+size_t slabhash_get_size(struct slabhash* sl)
+{
+	size_t i, total = 0;
+	for(i=0; i<sl->size; i++) {
+		lock_quick_lock(&sl->array[i]->lock);
+		total += sl->array[i]->space_max;
+		lock_quick_unlock(&sl->array[i]->lock);
+	}
+	return total;
+}
+
+size_t slabhash_get_mem(struct slabhash* sl)
+{	
+	size_t i, total = sizeof(*sl);
+	total += sizeof(struct lruhash*)*sl->size;
+	for(i=0; i<sl->size; i++) {
+		total += lruhash_get_mem(sl->array[i]);
+	}
+	return total;
+}
+
+struct lruhash* slabhash_gettable(struct slabhash* sl, hashvalue_t hash)
+{
+	return sl->array[slab_idx(sl, hash)];
+}
+
+/* test code, here to avoid linking problems with fptr_wlist */
+/** delete key */
+static void delkey(struct slabhash_testkey* k) {
+	lock_rw_destroy(&k->entry.lock); free(k);}
+/** delete data */
+static void deldata(struct slabhash_testdata* d) {free(d);}
+
+size_t test_slabhash_sizefunc(void* ATTR_UNUSED(key), void* ATTR_UNUSED(data))
+{
+	return sizeof(struct slabhash_testkey) + 
+		sizeof(struct slabhash_testdata);
+}
+
+int test_slabhash_compfunc(void* key1, void* key2)
+{
+	struct slabhash_testkey* k1 = (struct slabhash_testkey*)key1;
+	struct slabhash_testkey* k2 = (struct slabhash_testkey*)key2;
+	if(k1->id == k2->id)
+		return 0;
+	if(k1->id > k2->id)
+		return 1;
+	return -1;
+}
+
+void test_slabhash_delkey(void* key, void* ATTR_UNUSED(arg))
+{
+	delkey((struct slabhash_testkey*)key);
+}
+
+void test_slabhash_deldata(void* data, void* ATTR_UNUSED(arg))
+{
+	deldata((struct slabhash_testdata*)data);
+}
+
+void slabhash_setmarkdel(struct slabhash* sl, lruhash_markdelfunc_t md)
+{
+	size_t i;
+	for(i=0; i<sl->size; i++) {
+		lruhash_setmarkdel(sl->array[i], md);
+	}
+}
+
+void slabhash_traverse(struct slabhash* sh, int wr,
+        void (*func)(struct lruhash_entry*, void*), void* arg)
+{
+	size_t i;
+	for(i=0; i<sh->size; i++)
+		lruhash_traverse(sh->array[i], wr, func, arg);
+}
diff --git a/3rdParty/Unbound/src/src/util/storage/slabhash.h b/3rdParty/Unbound/src/src/util/storage/slabhash.h
new file mode 100644
index 0000000..93228fe
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/storage/slabhash.h
@@ -0,0 +1,211 @@
+/*
+ * util/storage/slabhash.h - hashtable consisting of several smaller tables.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * Hash table that consists of smaller hash tables.
+ * It cannot grow, but that gives it the ability to have multiple
+ * locks. Also this means there are multiple LRU lists.
+ */
+
+#ifndef UTIL_STORAGE_SLABHASH_H
+#define UTIL_STORAGE_SLABHASH_H
+#include "util/storage/lruhash.h"
+
+/** default number of slabs */
+#define HASH_DEFAULT_SLABS 4
+
+/**
+ * Hash table formed from several smaller ones. 
+ * This results in a partitioned lruhash table, a 'slashtable'.
+ * None of the data inside the slabhash may be altered.
+ * Therefore, no locks are needed to access the structure.
+ */
+struct slabhash {
+	/** the size of the array - must be power of 2 */
+	size_t size;
+	/** size bitmask - uses high bits. */
+	uint32_t mask;
+	/** shift right this many bits to get index into array. */
+	unsigned int shift;
+	/** lookup array of hash tables */
+	struct lruhash** array;
+};
+
+/**
+ * Create new slabbed hash table.
+ * @param numtables: number of hash tables to use, other parameters used to
+ *	initialize these smaller hashtables.
+ * @param start_size: size of hashtable array at start, must be power of 2.
+ * @param maxmem: maximum amount of memory this table is allowed to use.
+ *	so every table gets maxmem/numtables to use for itself.
+ * @param sizefunc: calculates memory usage of entries.
+ * @param compfunc: compares entries, 0 on equality.
+ * @param delkeyfunc: deletes key.
+ * @param deldatafunc: deletes data. 
+ * @param arg: user argument that is passed to user function calls.
+ * @return: new hash table or NULL on malloc failure.
+ */
+struct slabhash* slabhash_create(size_t numtables, size_t start_size, 
+	size_t maxmem, lruhash_sizefunc_t sizefunc, 
+	lruhash_compfunc_t compfunc, lruhash_delkeyfunc_t delkeyfunc, 
+	lruhash_deldatafunc_t deldatafunc, void* arg);
+
+/**
+ * Delete hash table. Entries are all deleted.
+ * @param table: to delete.
+ */
+void slabhash_delete(struct slabhash* table);
+
+/**
+ * Clear hash table. Entries are all deleted.
+ * @param table: to make empty.
+ */
+void slabhash_clear(struct slabhash* table);
+
+/**
+ * Insert a new element into the hashtable, uses lruhash_insert. 
+ * If key is already present data pointer in that entry is updated.
+ *
+ * @param table: hash table.
+ * @param hash: hash value. User calculates the hash.
+ * @param entry: identifies the entry.
+ * 	If key already present, this entry->key is deleted immediately.
+ *	But entry->data is set to NULL before deletion, and put into
+ * 	the existing entry. The data is then freed.
+ * @param data: the data.
+ * @param cb_override: if not NULL overrides the cb_arg for deletfunc.
+ */
+void slabhash_insert(struct slabhash* table, hashvalue_t hash, 
+	struct lruhash_entry* entry, void* data, void* cb_override);
+
+/**
+ * Lookup an entry in the hashtable. Uses lruhash_lookup.
+ * At the end of the function you hold a (read/write)lock on the entry.
+ * The LRU is updated for the entry (if found).
+ * @param table: hash table.
+ * @param hash: hash of key.
+ * @param key: what to look for, compared against entries in overflow chain.
+ *    the hash value must be set, and must work with compare function.
+ * @param wr: set to true if you desire a writelock on the entry.
+ *    with a writelock you can update the data part.
+ * @return: pointer to the entry or NULL. The entry is locked.
+ *    The user must unlock the entry when done.
+ */
+struct lruhash_entry* slabhash_lookup(struct slabhash* table, 
+	hashvalue_t hash, void* key, int wr);
+
+/**
+ * Remove entry from hashtable. Does nothing if not found in hashtable.
+ * Delfunc is called for the entry. Uses lruhash_remove.
+ * @param table: hash table.
+ * @param hash: hash of key.
+ * @param key: what to look for. 
+ */
+void slabhash_remove(struct slabhash* table, hashvalue_t hash, void* key);
+
+/**
+ * Output debug info to the log as to state of the hash table.
+ * @param table: hash table.
+ * @param id: string printed with table to identify the hash table.
+ * @param extended: set to true to print statistics on overflow bin lengths.
+ */
+void slabhash_status(struct slabhash* table, const char* id, int extended);
+
+/**
+ * Retrieve slab hash total size.
+ * @param table: hash table.
+ * @return size configured as max.
+ */
+size_t slabhash_get_size(struct slabhash* table);
+
+/**
+ * Retrieve slab hash current memory use.
+ * @param table: hash table.
+ * @return memory in use.
+ */
+size_t slabhash_get_mem(struct slabhash* table);
+
+/**
+ * Get lruhash table for a given hash value
+ * @param table: slabbed hash table.
+ * @param hash: hash value.
+ * @return the lru hash table.
+ */
+struct lruhash* slabhash_gettable(struct slabhash* table, hashvalue_t hash);
+
+/**
+ * Set markdel function
+ * @param table: slabbed hash table.
+ * @param md: markdel function ptr.
+ */
+void slabhash_setmarkdel(struct slabhash* table, lruhash_markdelfunc_t md);
+
+/**
+ * Traverse a slabhash.
+ * @param table: slabbed hash table.
+ * @param wr: if true, writelock is obtained, otherwise readlock.
+ * @param func: function to call for every element.
+ * @param arg: user argument to function.
+ */
+void slabhash_traverse(struct slabhash* table, int wr,
+        void (*func)(struct lruhash_entry*, void*), void* arg);
+
+/* --- test representation --- */
+/** test structure contains test key */
+struct slabhash_testkey {
+	/** the key id */
+	int id;
+	/** the entry */
+	struct lruhash_entry entry;
+};
+/** test structure contains test data */
+struct slabhash_testdata {
+	/** data value */
+	int data;
+};
+
+/** test sizefunc for lruhash */
+size_t test_slabhash_sizefunc(void*, void*);
+/** test comparefunc for lruhash */
+int test_slabhash_compfunc(void*, void*);
+/** test delkey for lruhash */
+void test_slabhash_delkey(void*, void*);
+/** test deldata for lruhash */
+void test_slabhash_deldata(void*, void*);
+/* --- end test representation --- */
+
+#endif /* UTIL_STORAGE_SLABHASH_H */
diff --git a/3rdParty/Unbound/src/src/util/timehist.c b/3rdParty/Unbound/src/src/util/timehist.c
new file mode 100644
index 0000000..98d8db1
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/timehist.c
@@ -0,0 +1,246 @@
+/*
+ * util/timehist.c - make histogram of time values.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to make a histogram of time values.
+ */
+#include "config.h"
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+#include <sys/time.h>
+#include "util/timehist.h"
+#include "util/log.h"
+
+/** special timestwo operation for time values in histogram setup */
+static void
+timestwo(struct timeval* v)
+{
+#ifndef S_SPLINT_S
+	if(v->tv_sec == 0 && v->tv_usec == 0) {
+		v->tv_usec = 1;
+		return;
+	}
+	v->tv_sec *= 2;
+	v->tv_usec *= 2;
+	if(v->tv_usec == 1024*1024) {
+		/* nice values and easy to compute */
+		v->tv_sec = 1;
+		v->tv_usec = 0;
+	}
+#endif
+}
+
+/** do setup exponentially */
+static void
+dosetup(struct timehist* hist)
+{
+	struct timeval last;
+	size_t i;
+	memset(&last, 0, sizeof(last));
+	for(i=0; i<hist->num; i++) {
+		hist->buckets[i].lower = last;
+		timestwo(&last);
+		hist->buckets[i].upper = last;
+		hist->buckets[i].count = 0;
+	}
+}
+
+struct timehist* timehist_setup(void)
+{
+	struct timehist* hist = (struct timehist*)calloc(1, 
+		sizeof(struct timehist));
+	if(!hist)
+		return NULL;
+	hist->num = NUM_BUCKETS_HIST;
+	hist->buckets = (struct th_buck*)calloc(hist->num, 
+		sizeof(struct th_buck));
+	if(!hist->buckets) {
+		free(hist);
+		return NULL;
+	}
+	/* setup the buckets */
+	dosetup(hist);
+	return hist;
+}
+
+void timehist_delete(struct timehist* hist)
+{
+	if(!hist)
+		return;
+	free(hist->buckets);
+	free(hist);
+}
+
+void timehist_clear(struct timehist* hist)
+{
+	size_t i;
+	for(i=0; i<hist->num; i++)
+		hist->buckets[i].count = 0;
+}
+
+/** histogram compare of time values */
+static int
+timeval_smaller(const struct timeval* x, const struct timeval* y)
+{
+#ifndef S_SPLINT_S
+	if(x->tv_sec < y->tv_sec)
+		return 1;
+	else if(x->tv_sec == y->tv_sec) {
+		if(x->tv_usec <= y->tv_usec)
+			return 1;
+		else	return 0;
+	}
+	else	return 0;
+#endif
+}
+
+
+void timehist_insert(struct timehist* hist, struct timeval* tv)
+{
+	size_t i;
+	for(i=0; i<hist->num; i++) {
+		if(timeval_smaller(tv, &hist->buckets[i].upper)) {
+			hist->buckets[i].count++;
+			return;
+		}
+	}
+	/* dump in last bucket */
+	hist->buckets[hist->num-1].count++;
+}
+
+void timehist_print(struct timehist* hist)
+{
+#ifndef S_SPLINT_S
+	size_t i;
+	for(i=0; i<hist->num; i++) {
+		if(hist->buckets[i].count != 0) {
+			printf("%4d.%6.6d %4d.%6.6d %u\n",
+				(int)hist->buckets[i].lower.tv_sec,
+				(int)hist->buckets[i].lower.tv_usec,
+				(int)hist->buckets[i].upper.tv_sec,
+				(int)hist->buckets[i].upper.tv_usec,
+				(unsigned)hist->buckets[i].count);
+		}
+	}
+#endif
+}
+
+void timehist_log(struct timehist* hist, const char* name)
+{
+#ifndef S_SPLINT_S
+	size_t i;
+	log_info("[25%%]=%g median[50%%]=%g [75%%]=%g",
+		timehist_quartile(hist, 0.25),
+		timehist_quartile(hist, 0.50),
+		timehist_quartile(hist, 0.75));
+	/*        0000.000000 0000.000000 0 */
+	log_info("lower(secs) upper(secs) %s", name);
+	for(i=0; i<hist->num; i++) {
+		if(hist->buckets[i].count != 0) {
+			log_info("%4d.%6.6d %4d.%6.6d %u",
+				(int)hist->buckets[i].lower.tv_sec,
+				(int)hist->buckets[i].lower.tv_usec,
+				(int)hist->buckets[i].upper.tv_sec,
+				(int)hist->buckets[i].upper.tv_usec,
+				(unsigned)hist->buckets[i].count);
+		}
+	}
+#endif
+}
+
+/** total number in histogram */
+static size_t
+timehist_count(struct timehist* hist)
+{
+	size_t i, res = 0;
+	for(i=0; i<hist->num; i++)
+		res += hist->buckets[i].count;
+	return res;
+}
+
+double 
+timehist_quartile(struct timehist* hist, double q)
+{
+	double lookfor, passed, res;
+	double low = 0, up = 0;
+	size_t i;
+	if(!hist || hist->num == 0)
+		return 0.;
+	/* look for i'th element, interpolated */
+	lookfor = (double)timehist_count(hist);
+	if(lookfor < 4)
+		return 0.; /* not enough elements for a good estimate */
+	lookfor *= q;
+	passed = 0;
+	i = 0;
+	while(i+1 < hist->num && 
+		passed+(double)hist->buckets[i].count < lookfor) {
+		passed += (double)hist->buckets[i++].count;
+	}
+	/* got the right bucket */
+#ifndef S_SPLINT_S
+	low = (double)hist->buckets[i].lower.tv_sec + 
+		(double)hist->buckets[i].lower.tv_usec/1000000.;
+	up = (double)hist->buckets[i].upper.tv_sec + 
+		(double)hist->buckets[i].upper.tv_usec/1000000.;
+#endif
+	res = (lookfor - passed)*(up-low)/((double)hist->buckets[i].count);
+	return low+res;
+}
+
+void 
+timehist_export(struct timehist* hist, size_t* array, size_t sz)
+{
+	size_t i;
+	if(!hist) return;
+	if(sz > hist->num)
+		sz = hist->num;
+	for(i=0; i<sz; i++)
+		array[i] = hist->buckets[i].count;
+}
+
+void 
+timehist_import(struct timehist* hist, size_t* array, size_t sz)
+{
+	size_t i;
+	if(!hist) return;
+	if(sz > hist->num)
+		sz = hist->num;
+	for(i=0; i<sz; i++)
+		hist->buckets[i].count = array[i];
+}
diff --git a/3rdParty/Unbound/src/src/util/timehist.h b/3rdParty/Unbound/src/src/util/timehist.h
new file mode 100644
index 0000000..d594483
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/timehist.h
@@ -0,0 +1,134 @@
+/*
+ * util/timehist.h - make histogram of time values.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to make a histogram of time values.
+ */
+
+#ifndef UTIL_TIMEHIST_H
+#define UTIL_TIMEHIST_H
+
+/** Number of buckets in a histogram */
+#define NUM_BUCKETS_HIST 40
+
+/**
+ * Bucket of time history information
+ */
+struct th_buck {
+	/** lower bound */
+	struct timeval lower;
+	/** upper bound */
+	struct timeval upper;
+	/** number of items */
+	size_t count;
+};
+
+/**
+ * Keep histogram of time values.
+ */
+struct timehist {
+	/** number of buckets */
+	size_t num;
+	/** bucket array */
+	struct th_buck* buckets;
+};
+
+/** 
+ * Setup a histogram, default
+ * @return histogram or NULL on malloc failure.
+ */
+struct timehist* timehist_setup(void);
+
+/**
+ * Delete histogram
+ * @param hist: to delete
+ */
+void timehist_delete(struct timehist* hist);
+
+/**
+ * Clear histogram
+ * @param hist: to clear all data from
+ */
+void timehist_clear(struct timehist* hist);
+
+/**
+ * Add time value to histogram.
+ * @param hist: histogram
+ * @param tv: time value
+ */
+void timehist_insert(struct timehist* hist, struct timeval* tv);
+
+/**
+ * Find time value for given quartile, such as 0.25, 0.50, 0.75.
+ * The looks up the value for the i-th element in the sorted list of time 
+ * values, as approximated using the histogram.
+ * @param hist: histogram. Interpolated information is used from it.
+ * @param q: quartile, 0.50 results in the median. Must be >0 and <1.
+ * @return: the time in seconds for that percentage.
+ */
+double timehist_quartile(struct timehist* hist, double q);
+
+/**
+ * Printout histogram
+ * @param hist: histogram
+ */
+void timehist_print(struct timehist* hist);
+
+/**
+ * Log histogram, print it to the logfile.
+ * @param hist: histogram
+ * @param name: the name of the value column
+ */
+void timehist_log(struct timehist* hist, const char* name);
+
+/**
+ * Export histogram to an array.
+ * @param hist: histogram
+ * @param array: the array to export to.
+ * @param sz: number of items in array.
+ */
+void timehist_export(struct timehist* hist, size_t* array, size_t sz);
+
+/**
+ * Import histogram from an array.
+ * @param hist: histogram
+ * @param array: the array to import from.
+ * @param sz: number of items in array.
+ */
+void timehist_import(struct timehist* hist, size_t* array, size_t sz);
+
+#endif /* UTIL_TIMEHIST_H */
diff --git a/3rdParty/Unbound/src/src/util/tube.c b/3rdParty/Unbound/src/src/util/tube.c
new file mode 100644
index 0000000..2fad1ca
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/tube.c
@@ -0,0 +1,726 @@
+/*
+ * util/tube.c - pipe service
+ *
+ * Copyright (c) 2008, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains pipe service functions.
+ */
+#include "config.h"
+#include "util/tube.h"
+#include "util/log.h"
+#include "util/net_help.h"
+#include "util/netevent.h"
+#include "util/fptr_wlist.h"
+
+#ifndef USE_WINSOCK
+/* on unix */
+
+#ifndef HAVE_SOCKETPAIR
+/** no socketpair() available, like on Minix 3.1.7, use pipe */
+#define socketpair(f, t, p, sv) pipe(sv) 
+#endif /* HAVE_SOCKETPAIR */
+
+struct tube* tube_create(void)
+{
+	struct tube* tube = (struct tube*)calloc(1, sizeof(*tube));
+	int sv[2];
+	if(!tube) {
+		int err = errno;
+		log_err("tube_create: out of memory");
+		errno = err;
+		return NULL;
+	}
+	tube->sr = -1;
+	tube->sw = -1;
+	if(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) {
+		int err = errno;
+		log_err("socketpair: %s", strerror(errno));
+		free(tube);
+		errno = err;
+		return NULL;
+	}
+	tube->sr = sv[0];
+	tube->sw = sv[1];
+	if(!fd_set_nonblock(tube->sr) || !fd_set_nonblock(tube->sw)) {
+		int err = errno;
+		log_err("tube: cannot set nonblocking");
+		tube_delete(tube);
+		errno = err;
+		return NULL;
+	}
+	return tube;
+}
+
+void tube_delete(struct tube* tube)
+{
+	if(!tube) return;
+	tube_remove_bg_listen(tube);
+	tube_remove_bg_write(tube);
+	/* close fds after deleting commpoints, to be sure.
+	 *            Also epoll does not like closing fd before event_del */
+	tube_close_read(tube);
+	tube_close_write(tube);
+	free(tube);
+}
+
+void tube_close_read(struct tube* tube)
+{
+	if(tube->sr != -1) {
+		close(tube->sr);
+		tube->sr = -1;
+	}
+}
+
+void tube_close_write(struct tube* tube)
+{
+	if(tube->sw != -1) {
+		close(tube->sw);
+		tube->sw = -1;
+	}
+}
+
+void tube_remove_bg_listen(struct tube* tube)
+{
+	if(tube->listen_com) {
+		comm_point_delete(tube->listen_com);
+		tube->listen_com = NULL;
+	}
+	if(tube->cmd_msg) {
+		free(tube->cmd_msg);
+		tube->cmd_msg = NULL;
+	}
+}
+
+void tube_remove_bg_write(struct tube* tube)
+{
+	if(tube->res_com) {
+		comm_point_delete(tube->res_com);
+		tube->res_com = NULL;
+	}
+	if(tube->res_list) {
+		struct tube_res_list* np, *p = tube->res_list;
+		tube->res_list = NULL;
+		tube->res_last = NULL;
+		while(p) {
+			np = p->next;
+			free(p->buf);
+			free(p);
+			p = np;
+		}
+	}
+}
+
+int
+tube_handle_listen(struct comm_point* c, void* arg, int error,
+        struct comm_reply* ATTR_UNUSED(reply_info))
+{
+	struct tube* tube = (struct tube*)arg;
+	ssize_t r;
+	if(error != NETEVENT_NOERROR) {
+		fptr_ok(fptr_whitelist_tube_listen(tube->listen_cb));
+		(*tube->listen_cb)(tube, NULL, 0, error, tube->listen_arg);
+		return 0;
+	}
+
+	if(tube->cmd_read < sizeof(tube->cmd_len)) {
+		/* complete reading the length of control msg */
+		r = read(c->fd, ((uint8_t*)&tube->cmd_len) + tube->cmd_read,
+			sizeof(tube->cmd_len) - tube->cmd_read);
+		if(r==0) {
+			/* error has happened or */
+			/* parent closed pipe, must have exited somehow */
+			fptr_ok(fptr_whitelist_tube_listen(tube->listen_cb));
+			(*tube->listen_cb)(tube, NULL, 0, NETEVENT_CLOSED, 
+				tube->listen_arg);
+			return 0;
+		}
+		if(r==-1) {
+			if(errno != EAGAIN && errno != EINTR) {
+				log_err("rpipe error: %s", strerror(errno));
+			}
+			/* nothing to read now, try later */
+			return 0;
+		}
+		tube->cmd_read += r;
+		if(tube->cmd_read < sizeof(tube->cmd_len)) {
+			/* not complete, try later */
+			return 0;
+		}
+		tube->cmd_msg = (uint8_t*)calloc(1, tube->cmd_len);
+		if(!tube->cmd_msg) {
+			log_err("malloc failure");
+			tube->cmd_read = 0;
+			return 0;
+		}
+	}
+	/* cmd_len has been read, read remainder */
+	r = read(c->fd, tube->cmd_msg+tube->cmd_read-sizeof(tube->cmd_len),
+		tube->cmd_len - (tube->cmd_read - sizeof(tube->cmd_len)));
+	if(r==0) {
+		/* error has happened or */
+		/* parent closed pipe, must have exited somehow */
+		fptr_ok(fptr_whitelist_tube_listen(tube->listen_cb));
+		(*tube->listen_cb)(tube, NULL, 0, NETEVENT_CLOSED, 
+			tube->listen_arg);
+		return 0;
+	}
+	if(r==-1) {
+		/* nothing to read now, try later */
+		if(errno != EAGAIN && errno != EINTR) {
+			log_err("rpipe error: %s", strerror(errno));
+		}
+		return 0;
+	}
+	tube->cmd_read += r;
+	if(tube->cmd_read < sizeof(tube->cmd_len) + tube->cmd_len) {
+		/* not complete, try later */
+		return 0;
+	}
+	tube->cmd_read = 0;
+
+	fptr_ok(fptr_whitelist_tube_listen(tube->listen_cb));
+	(*tube->listen_cb)(tube, tube->cmd_msg, tube->cmd_len, 
+		NETEVENT_NOERROR, tube->listen_arg);
+		/* also frees the buf */
+	tube->cmd_msg = NULL;
+	return 0;
+}
+
+int
+tube_handle_write(struct comm_point* c, void* arg, int error,
+        struct comm_reply* ATTR_UNUSED(reply_info))
+{
+	struct tube* tube = (struct tube*)arg;
+	struct tube_res_list* item = tube->res_list;
+	ssize_t r;
+	if(error != NETEVENT_NOERROR) {
+		log_err("tube_handle_write net error %d", error);
+		return 0;
+	}
+
+	if(!item) {
+		comm_point_stop_listening(c);
+		return 0;
+	}
+
+	if(tube->res_write < sizeof(item->len)) {
+		r = write(c->fd, ((uint8_t*)&item->len) + tube->res_write,
+			sizeof(item->len) - tube->res_write);
+		if(r == -1) {
+			if(errno != EAGAIN && errno != EINTR) {
+				log_err("wpipe error: %s", strerror(errno));
+			}
+			return 0; /* try again later */
+		}
+		if(r == 0) {
+			/* error on pipe, must have exited somehow */
+			/* cannot signal this to pipe user */
+			return 0;
+		}
+		tube->res_write += r;
+		if(tube->res_write < sizeof(item->len))
+			return 0;
+	}
+	r = write(c->fd, item->buf + tube->res_write - sizeof(item->len),
+		item->len - (tube->res_write - sizeof(item->len)));
+	if(r == -1) {
+		if(errno != EAGAIN && errno != EINTR) {
+			log_err("wpipe error: %s", strerror(errno));
+		}
+		return 0; /* try again later */
+	}
+	if(r == 0) {
+		/* error on pipe, must have exited somehow */
+		/* cannot signal this to pipe user */
+		return 0;
+	}
+	tube->res_write += r;
+	if(tube->res_write < sizeof(item->len) + item->len)
+		return 0;
+	/* done this result, remove it */
+	free(item->buf);
+	item->buf = NULL;
+	tube->res_list = tube->res_list->next;
+	free(item);
+	if(!tube->res_list) {
+		tube->res_last = NULL;
+		comm_point_stop_listening(c);
+	}
+	tube->res_write = 0;
+	return 0;
+}
+
+int tube_write_msg(struct tube* tube, uint8_t* buf, uint32_t len, 
+        int nonblock)
+{
+	ssize_t r, d;
+	int fd = tube->sw;
+
+	/* test */
+	if(nonblock) {
+		r = write(fd, &len, sizeof(len));
+		if(r == -1) {
+			if(errno==EINTR || errno==EAGAIN)
+				return -1;
+			log_err("tube msg write failed: %s", strerror(errno));
+			return -1; /* can still continue, perhaps */
+		}
+	} else r = 0;
+	if(!fd_set_block(fd))
+		return 0;
+	/* write remainder */
+	d = r;
+	while(d != (ssize_t)sizeof(len)) {
+		if((r=write(fd, ((char*)&len)+d, sizeof(len)-d)) == -1) {
+			log_err("tube msg write failed: %s", strerror(errno));
+			(void)fd_set_nonblock(fd);
+			return 0;
+		}
+		d += r;
+	}
+	d = 0;
+	while(d != (ssize_t)len) {
+		if((r=write(fd, buf+d, len-d)) == -1) {
+			log_err("tube msg write failed: %s", strerror(errno));
+			(void)fd_set_nonblock(fd);
+			return 0;
+		}
+		d += r;
+	}
+	if(!fd_set_nonblock(fd))
+		return 0;
+	return 1;
+}
+
+int tube_read_msg(struct tube* tube, uint8_t** buf, uint32_t* len, 
+        int nonblock)
+{
+	ssize_t r, d;
+	int fd = tube->sr;
+
+	/* test */
+	*len = 0;
+	if(nonblock) {
+		r = read(fd, len, sizeof(*len));
+		if(r == -1) {
+			if(errno==EINTR || errno==EAGAIN)
+				return -1;
+			log_err("tube msg read failed: %s", strerror(errno));
+			return -1; /* we can still continue, perhaps */
+		}
+		if(r == 0) /* EOF */
+			return 0;
+	} else r = 0;
+	if(!fd_set_block(fd))
+		return 0;
+	/* read remainder */
+	d = r;
+	while(d != (ssize_t)sizeof(*len)) {
+		if((r=read(fd, ((char*)len)+d, sizeof(*len)-d)) == -1) {
+			log_err("tube msg read failed: %s", strerror(errno));
+			(void)fd_set_nonblock(fd);
+			return 0;
+		}
+		if(r == 0) /* EOF */ {
+			(void)fd_set_nonblock(fd);
+			return 0;
+		}
+		d += r;
+	}
+	*buf = (uint8_t*)malloc(*len);
+	if(!*buf) {
+		log_err("tube read out of memory");
+		(void)fd_set_nonblock(fd);
+		return 0;
+	}
+	d = 0;
+	while(d != (ssize_t)*len) {
+		if((r=read(fd, (*buf)+d, (size_t)((ssize_t)*len)-d)) == -1) {
+			log_err("tube msg read failed: %s", strerror(errno));
+			(void)fd_set_nonblock(fd);
+			free(*buf);
+			return 0;
+		}
+		if(r == 0) { /* EOF */
+			(void)fd_set_nonblock(fd);
+			free(*buf);
+			return 0;
+		}
+		d += r;
+	}
+	if(!fd_set_nonblock(fd)) {
+		free(*buf);
+		return 0;
+	}
+	return 1;
+}
+
+/** perform a select() on the fd */
+static int
+pollit(int fd, struct timeval* t)
+{
+	fd_set r;
+#ifndef S_SPLINT_S
+	FD_ZERO(&r);
+	FD_SET(FD_SET_T fd, &r);
+#endif
+	if(select(fd+1, &r, NULL, NULL, t) == -1) {
+		return 0;
+	}
+	errno = 0;
+	return (int)(FD_ISSET(fd, &r));
+}
+
+int tube_poll(struct tube* tube)
+{
+	struct timeval t;
+	memset(&t, 0, sizeof(t));
+	return pollit(tube->sr, &t);
+}
+
+int tube_wait(struct tube* tube)
+{
+	return pollit(tube->sr, NULL);
+}
+
+int tube_read_fd(struct tube* tube)
+{
+	return tube->sr;
+}
+
+int tube_setup_bg_listen(struct tube* tube, struct comm_base* base,
+        tube_callback_t* cb, void* arg)
+{
+	tube->listen_cb = cb;
+	tube->listen_arg = arg;
+	if(!(tube->listen_com = comm_point_create_raw(base, tube->sr, 
+		0, tube_handle_listen, tube))) {
+		int err = errno;
+		log_err("tube_setup_bg_l: commpoint creation failed");
+		errno = err;
+		return 0;
+	}
+	return 1;
+}
+
+int tube_setup_bg_write(struct tube* tube, struct comm_base* base)
+{
+	if(!(tube->res_com = comm_point_create_raw(base, tube->sw, 
+		1, tube_handle_write, tube))) {
+		int err = errno;
+		log_err("tube_setup_bg_w: commpoint creation failed");
+		errno = err;
+		return 0;
+	}
+	return 1;
+}
+
+int tube_queue_item(struct tube* tube, uint8_t* msg, size_t len)
+{
+	struct tube_res_list* item = 
+		(struct tube_res_list*)malloc(sizeof(*item));
+	if(!item) {
+		free(msg);
+		log_err("out of memory for async answer");
+		return 0;
+	}
+	item->buf = msg;
+	item->len = len;
+	item->next = NULL;
+	/* add at back of list, since the first one may be partially written */
+	if(tube->res_last)
+		tube->res_last->next = item;
+	else    tube->res_list = item;
+	tube->res_last = item;
+	if(tube->res_list == tube->res_last) {
+		/* first added item, start the write process */
+		comm_point_start_listening(tube->res_com, -1, -1);
+	}
+	return 1;
+}
+
+void tube_handle_signal(int ATTR_UNUSED(fd), short ATTR_UNUSED(events), 
+	void* ATTR_UNUSED(arg))
+{
+	log_assert(0);
+}
+
+#else /* USE_WINSOCK */
+/* on windows */
+
+
+struct tube* tube_create(void)
+{
+	/* windows does not have forks like unix, so we only support
+	 * threads on windows. And thus the pipe need only connect
+	 * threads. We use a mutex and a list of datagrams. */
+	struct tube* tube = (struct tube*)calloc(1, sizeof(*tube));
+	if(!tube) {
+		int err = errno;
+		log_err("tube_create: out of memory");
+		errno = err;
+		return NULL;
+	}
+	tube->event = WSACreateEvent();
+	if(tube->event == WSA_INVALID_EVENT) {
+		free(tube);
+		log_err("WSACreateEvent: %s", wsa_strerror(WSAGetLastError()));
+	}
+	if(!WSAResetEvent(tube->event)) {
+		log_err("WSAResetEvent: %s", wsa_strerror(WSAGetLastError()));
+	}
+	lock_basic_init(&tube->res_lock);
+	verbose(VERB_ALGO, "tube created");
+	return tube;
+}
+
+void tube_delete(struct tube* tube)
+{
+	if(!tube) return;
+	tube_remove_bg_listen(tube);
+	tube_remove_bg_write(tube);
+	tube_close_read(tube);
+	tube_close_write(tube);
+	if(!WSACloseEvent(tube->event))
+		log_err("WSACloseEvent: %s", wsa_strerror(WSAGetLastError()));
+	lock_basic_destroy(&tube->res_lock);
+	verbose(VERB_ALGO, "tube deleted");
+	free(tube);
+}
+
+void tube_close_read(struct tube* ATTR_UNUSED(tube))
+{
+	verbose(VERB_ALGO, "tube close_read");
+}
+
+void tube_close_write(struct tube* ATTR_UNUSED(tube))
+{
+	verbose(VERB_ALGO, "tube close_write");
+	/* wake up waiting reader with an empty queue */
+	if(!WSASetEvent(tube->event)) {
+		log_err("WSASetEvent: %s", wsa_strerror(WSAGetLastError()));
+	}
+}
+
+void tube_remove_bg_listen(struct tube* tube)
+{
+	verbose(VERB_ALGO, "tube remove_bg_listen");
+	winsock_unregister_wsaevent(&tube->ev_listen);
+}
+
+void tube_remove_bg_write(struct tube* tube)
+{
+	verbose(VERB_ALGO, "tube remove_bg_write");
+	if(tube->res_list) {
+		struct tube_res_list* np, *p = tube->res_list;
+		tube->res_list = NULL;
+		tube->res_last = NULL;
+		while(p) {
+			np = p->next;
+			free(p->buf);
+			free(p);
+			p = np;
+		}
+	}
+}
+
+int tube_write_msg(struct tube* tube, uint8_t* buf, uint32_t len, 
+        int ATTR_UNUSED(nonblock))
+{
+	uint8_t* a;
+	verbose(VERB_ALGO, "tube write_msg len %d", (int)len);
+	a = (uint8_t*)memdup(buf, len);
+	if(!a) {
+		log_err("out of memory in tube_write_msg");
+		return 0;
+	}
+	/* always nonblocking, this pipe cannot get full */
+	return tube_queue_item(tube, a, len);
+}
+
+int tube_read_msg(struct tube* tube, uint8_t** buf, uint32_t* len, 
+        int nonblock)
+{
+	struct tube_res_list* item = NULL;
+	verbose(VERB_ALGO, "tube read_msg %s", nonblock?"nonblock":"blocking");
+	*buf = NULL;
+	if(!tube_poll(tube)) {
+		verbose(VERB_ALGO, "tube read_msg nodata");
+		/* nothing ready right now, wait if we want to */
+		if(nonblock)
+			return -1; /* would block waiting for items */
+		if(!tube_wait(tube))
+			return 0;
+	}
+	lock_basic_lock(&tube->res_lock);
+	if(tube->res_list) {
+		item = tube->res_list;
+		tube->res_list = item->next;
+		if(tube->res_last == item) {
+			/* the list is now empty */
+			tube->res_last = NULL;
+			verbose(VERB_ALGO, "tube read_msg lastdata");
+			if(!WSAResetEvent(tube->event)) {
+				log_err("WSAResetEvent: %s", 
+					wsa_strerror(WSAGetLastError()));
+			}
+		}
+	}
+	lock_basic_unlock(&tube->res_lock);
+	if(!item)
+		return 0; /* would block waiting for items */
+	*buf = item->buf;
+	*len = item->len;
+	free(item);
+	verbose(VERB_ALGO, "tube read_msg len %d", (int)*len);
+	return 1;
+}
+
+int tube_poll(struct tube* tube)
+{
+	struct tube_res_list* item = NULL;
+	lock_basic_lock(&tube->res_lock);
+	item = tube->res_list;
+	lock_basic_unlock(&tube->res_lock);
+	if(item)
+		return 1;
+	return 0;
+}
+
+int tube_wait(struct tube* tube)
+{
+	/* block on eventhandle */
+	DWORD res = WSAWaitForMultipleEvents(
+		1 /* one event in array */, 
+		&tube->event /* the event to wait for, our pipe signal */, 
+		0 /* wait for all events is false */, 
+		WSA_INFINITE /* wait, no timeout */,
+		0 /* we are not alertable for IO completion routines */
+		);
+	if(res == WSA_WAIT_TIMEOUT) {
+		return 0;
+	}
+	if(res == WSA_WAIT_IO_COMPLETION) {
+		/* a bit unexpected, since we were not alertable */
+		return 0;
+	}
+	return 1;
+}
+
+int tube_read_fd(struct tube* ATTR_UNUSED(tube))
+{
+	/* nothing sensible on Windows */
+	return -1;
+}
+
+int
+tube_handle_listen(struct comm_point* ATTR_UNUSED(c), void* ATTR_UNUSED(arg), 
+	int ATTR_UNUSED(error), struct comm_reply* ATTR_UNUSED(reply_info))
+{
+	log_assert(0);
+	return 0;
+}
+
+int
+tube_handle_write(struct comm_point* ATTR_UNUSED(c), void* ATTR_UNUSED(arg), 
+	int ATTR_UNUSED(error), struct comm_reply* ATTR_UNUSED(reply_info))
+{
+	log_assert(0);
+	return 0;
+}
+
+int tube_setup_bg_listen(struct tube* tube, struct comm_base* base,
+        tube_callback_t* cb, void* arg)
+{
+	tube->listen_cb = cb;
+	tube->listen_arg = arg;
+	if(!comm_base_internal(base))
+		return 1; /* ignore when no comm base - testing */
+	return winsock_register_wsaevent(comm_base_internal(base), 
+		&tube->ev_listen, tube->event, &tube_handle_signal, tube);
+}
+
+int tube_setup_bg_write(struct tube* ATTR_UNUSED(tube), 
+	struct comm_base* ATTR_UNUSED(base))
+{
+	/* the queue item routine performs the signaling */
+	return 1;
+}
+
+int tube_queue_item(struct tube* tube, uint8_t* msg, size_t len)
+{
+	struct tube_res_list* item = 
+		(struct tube_res_list*)malloc(sizeof(*item));
+	verbose(VERB_ALGO, "tube queue_item len %d", (int)len);
+	if(!item) {
+		free(msg);
+		log_err("out of memory for async answer");
+		return 0;
+	}
+	item->buf = msg;
+	item->len = len;
+	item->next = NULL;
+	lock_basic_lock(&tube->res_lock);
+	/* add at back of list, since the first one may be partially written */
+	if(tube->res_last)
+		tube->res_last->next = item;
+	else    tube->res_list = item;
+	tube->res_last = item;
+	/* signal the eventhandle */
+	if(!WSASetEvent(tube->event)) {
+		log_err("WSASetEvent: %s", wsa_strerror(WSAGetLastError()));
+	}
+	lock_basic_unlock(&tube->res_lock);
+	return 1;
+}
+
+void tube_handle_signal(int ATTR_UNUSED(fd), short ATTR_UNUSED(events), 
+	void* arg)
+{
+	struct tube* tube = (struct tube*)arg;
+	uint8_t* buf;
+	uint32_t len;
+	verbose(VERB_ALGO, "tube handle_signal");
+	while(tube_poll(tube)) {
+		if(tube_read_msg(tube, &buf, &len, 1)) {
+			fptr_ok(fptr_whitelist_tube_listen(tube->listen_cb));
+			(*tube->listen_cb)(tube, buf, len, NETEVENT_NOERROR, 
+				tube->listen_arg);
+		}
+	}
+}
+
+#endif /* USE_WINSOCK */
diff --git a/3rdParty/Unbound/src/src/util/tube.h b/3rdParty/Unbound/src/src/util/tube.h
new file mode 100644
index 0000000..2e70411
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/tube.h
@@ -0,0 +1,273 @@
+/*
+ * util/tube.h - pipe service
+ *
+ * Copyright (c) 2008, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains pipe service functions.
+ */
+
+#ifndef UTIL_TUBE_H
+#define UTIL_TUBE_H
+struct comm_reply;
+struct comm_point;
+struct comm_base;
+struct tube;
+struct tube_res_list;
+#ifdef USE_WINSOCK
+#include "util/locks.h"
+#include "util/winsock_event.h"
+#endif
+
+/**
+ * Callback from pipe listen function
+ * void mycallback(tube, msg, len, error, user_argument);
+ * if error is true (NETEVENT_*), msg is probably NULL.
+ */
+typedef void tube_callback_t(struct tube*, uint8_t*, size_t, int, void*);
+
+/**
+ * A pipe
+ */
+struct tube {
+#ifndef USE_WINSOCK
+	/** pipe end to read from */
+	int sr;
+	/** pipe end to write on */
+	int sw;
+
+	/** listen commpoint */
+	struct comm_point* listen_com;
+	/** listen callback */
+	tube_callback_t* listen_cb;
+	/** listen callback user arg */
+	void* listen_arg;
+	/** are we currently reading a command, 0 if not, else bytecount */
+	size_t cmd_read;
+	/** size of current read command, may be partially read */
+	uint32_t cmd_len;
+	/** the current read command content, malloced, can be partially read*/
+	uint8_t* cmd_msg;
+
+	/** background write queue, commpoint to write results back */
+	struct comm_point* res_com;
+	/** are we curently writing a result, 0 if not, else bytecount into
+	 * the res_list first entry. */
+	size_t res_write;
+	/** list of outstanding results to be written back */
+	struct tube_res_list* res_list;
+	/** last in list */
+	struct tube_res_list* res_last;
+
+#else /* USE_WINSOCK */
+	/** listen callback */
+	tube_callback_t* listen_cb;
+	/** listen callback user arg */
+	void* listen_arg;
+	/** the windows sockets event (signaled if items in pipe) */
+	WSAEVENT event;
+	/** winsock event storage when registered with event base */
+	struct event ev_listen;
+
+	/** lock on the list of outstanding items */
+	lock_basic_t res_lock;
+	/** list of outstanding results on pipe */
+	struct tube_res_list* res_list;
+	/** last in list */
+	struct tube_res_list* res_last;
+#endif /* USE_WINSOCK */
+};
+
+/**
+ * List of results (arbitrary command serializations) to write back
+ */
+struct tube_res_list {
+	/** next in list */
+	struct tube_res_list* next;
+	/** serialized buffer to write */
+	uint8_t* buf;
+	/** length to write */
+	uint32_t len;
+};
+
+/**
+ * Create a pipe
+ * @return: new tube struct or NULL on error.
+ */
+struct tube* tube_create(void);
+
+/**
+ * Delete and destroy a pipe
+ * @param tube: to delete
+ */
+void tube_delete(struct tube* tube);
+
+/**
+ * Write length bytes followed by message.
+ * @param tube: the tube to write on.
+ *     If that tube is a pipe, its write fd is used as
+ *     the socket to write on. Is nonblocking.
+ *      Set to blocking by the function,
+ *      and back to non-blocking at exit of function.
+ * @param buf: the message.
+ * @param len: length of message.
+ * @param nonblock: if set to true, the first write is nonblocking.
+ *      If the first write fails the function returns -1.
+ *      If set false, the first write is blocking.
+ * @return: all remainder writes are nonblocking.
+ *      return 0 on error, in that case blocking/nonblocking of socket is
+ *              unknown.
+ *      return 1 if all OK.
+ */
+int tube_write_msg(struct tube* tube, uint8_t* buf, uint32_t len, 
+	int nonblock);
+
+/**
+ * Read length bytes followed by message.
+ * @param tube: The tube to read on.
+ *     If that tube is a pipe, its read fd is used as
+ *     the socket to read on. Is nonblocking.
+ *      Set to blocking by the function,
+ *      and back to non-blocking at exit of function.
+ * @param buf: the message, malloced.
+ * @param len: length of message, returned.
+ * @param nonblock: if set to true, the first read is nonblocking.
+ *      If the first read fails the function returns -1.
+ *      If set false, the first read is blocking.
+ * @return: all remainder reads are nonblocking.
+ *      return 0 on error, in that case blocking/nonblocking of socket is 
+ *              unknown. On EOF 0 is returned.
+ *      return 1 if all OK.
+ */
+int tube_read_msg(struct tube* tube, uint8_t** buf, uint32_t* len, 
+	int nonblock);
+
+/**
+ * Close read part of the pipe.
+ * The tube can no longer be read from.
+ * @param tube: tube to operate on.
+ */
+void tube_close_read(struct tube* tube);
+
+/**
+ * Close write part of the pipe.
+ * The tube can no longer be written to.
+ * @param tube: tube to operate on.
+ */
+void tube_close_write(struct tube* tube);
+
+/**
+ * See if data is ready for reading on the tube without blocking.
+ * @param tube: tube to check for readable items
+ * @return true if readable items are present. False if not (or error).
+ *     true on pipe_closed.
+ */
+int tube_poll(struct tube* tube);
+
+/**
+ * Wait for data to be ready for reading on the tube. is blocking.
+ * No timeout.
+ * @param tube: the tube to wait on.
+ * @return: if there was something to read (false on error).
+ *     true on pipe_closed.
+ */
+int tube_wait(struct tube* tube);
+
+/**
+ * Get FD that is readable when new information arrives.
+ * @param tube
+ * @return file descriptor.
+ */
+int tube_read_fd(struct tube* tube);
+
+/**
+ * Start listening for information over the pipe.
+ * Background registration of a read listener, callback when read completed.
+ * Do not mix with tube_read_msg style direct reads from the pipe.
+ * @param tube: tube to listen on
+ * @param base: what base to register event callback.
+ * @param cb: callback routine.
+ * @param arg: user argument for callback routine.
+ * @return true if successful, false on error.
+ */
+int tube_setup_bg_listen(struct tube* tube, struct comm_base* base,
+	tube_callback_t* cb, void* arg);
+
+/**
+ * Remove bg listen setup from event base.
+ * @param tube: what tube to cleanup
+ */
+void tube_remove_bg_listen(struct tube* tube);
+
+/**
+ * Start background write handler for the pipe.
+ * Do not mix with tube_write_msg style direct writes to the pipe.
+ * @param tube: tube to write on
+ * @param base: what base to register event handler on.
+ * @return true if successful, false on error.
+ */
+int tube_setup_bg_write(struct tube* tube, struct comm_base* base);
+
+/**
+ * Remove bg write setup from event base.
+ * @param tube: what tube to cleanup
+ */
+void tube_remove_bg_write(struct tube* tube);
+
+
+/**
+ * Append data item to background list of writes.
+ * Mallocs a list entry behind the scenes.
+ * Not locked behind the scenes, call from one thread or lock on outside.
+ * @param tube: what tube to queue on.
+ * @param msg: memory message to send. Is free()d after use.
+ * 	Put at the end of the to-send queue.
+ * @param len: length of item.
+ * @return 0 on failure (msg freed).
+ */
+int tube_queue_item(struct tube* tube, uint8_t* msg, size_t len);
+
+/** for fptr wlist, callback function */
+int tube_handle_listen(struct comm_point* c, void* arg, int error, 
+	struct comm_reply* reply_info);
+
+/** for fptr wlist, callback function */
+int tube_handle_write(struct comm_point* c, void* arg, int error, 
+	struct comm_reply* reply_info);
+
+/** for fptr wlist, winsock signal event callback function */
+void tube_handle_signal(int fd, short events, void* arg);
+
+#endif /* UTIL_TUBE_H */
diff --git a/3rdParty/Unbound/src/src/util/winsock_event.c b/3rdParty/Unbound/src/src/util/winsock_event.c
new file mode 100644
index 0000000..ff5c9b0
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/winsock_event.c
@@ -0,0 +1,692 @@
+/*
+ * util/winsock_event.c - implementation of the unbound winsock event handler. 
+ *
+ * Copyright (c) 2008, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+/**
+ * \file
+ * Implementation of the unbound WinSock2 API event notification handler
+ * for the Windows port.
+ */
+
+#include "config.h"
+#ifdef USE_WINSOCK
+#include <signal.h>
+#include "util/winsock_event.h"
+#include "util/fptr_wlist.h"
+
+int mini_ev_cmp(const void* a, const void* b)
+{
+        const struct event *e = (const struct event*)a;
+        const struct event *f = (const struct event*)b;
+        if(e->ev_timeout.tv_sec < f->ev_timeout.tv_sec)
+                return -1;
+        if(e->ev_timeout.tv_sec > f->ev_timeout.tv_sec)
+                return 1;
+        if(e->ev_timeout.tv_usec < f->ev_timeout.tv_usec)
+                return -1;
+        if(e->ev_timeout.tv_usec > f->ev_timeout.tv_usec)
+                return 1;
+        if(e < f)
+                return -1;
+        if(e > f)
+                return 1;
+	return 0;
+}
+
+/** set time */
+static int
+settime(struct event_base* base)
+{
+        if(gettimeofday(base->time_tv, NULL) < 0) {
+                return -1;
+        }
+#ifndef S_SPLINT_S
+        *base->time_secs = (uint32_t)base->time_tv->tv_sec;
+#endif
+        return 0;
+}
+
+#ifdef UNBOUND_DEBUG
+/**
+ * Find a fd in the list of items.
+ * Note that not all items have a fd associated (those are -1).
+ * Signals are stored separately, and not searched.
+ * @param base: event base to look in.
+ * @param fd: what socket to look for.
+ * @return the index in the array, or -1 on failure.
+ */
+static int
+find_fd(struct event_base* base, int fd)
+{
+	int i;
+	for(i=0; i<base->max; i++) {
+		if(base->items[i]->ev_fd == fd)
+			return i;
+	}
+	return -1;
+}
+#endif
+
+/** Find ptr in base array */
+static void
+zero_waitfor(WSAEVENT waitfor[], WSAEVENT x)
+{
+	int i;
+	for(i=0; i<WSK_MAX_ITEMS; i++) {
+		if(waitfor[i] == x)
+			waitfor[i] = 0;
+	}
+}
+
+void *event_init(uint32_t* time_secs, struct timeval* time_tv)
+{
+        struct event_base* base = (struct event_base*)malloc(
+		sizeof(struct event_base));
+        if(!base)
+                return NULL;
+        memset(base, 0, sizeof(*base));
+        base->time_secs = time_secs;
+        base->time_tv = time_tv;
+        if(settime(base) < 0) {
+                event_base_free(base);
+                return NULL;
+        }
+	base->items = (struct event**)calloc(WSK_MAX_ITEMS, 
+		sizeof(struct event*));
+	if(!base->items) {
+                event_base_free(base);
+                return NULL;
+	}
+	base->cap = WSK_MAX_ITEMS;
+	base->max = 0;
+        base->times = rbtree_create(mini_ev_cmp);
+        if(!base->times) {
+                event_base_free(base);
+                return NULL;
+        }
+        base->signals = (struct event**)calloc(MAX_SIG, sizeof(struct event*));
+        if(!base->signals) {
+                event_base_free(base);
+                return NULL;
+        }
+	base->tcp_stickies = 0;
+	base->tcp_reinvigorated = 0;
+	verbose(VERB_CLIENT, "winsock_event inited");
+        return base;
+}
+
+const char *event_get_version(void)
+{
+	return "winsock-event-"PACKAGE_VERSION;
+}
+
+const char *event_get_method(void)
+{
+	return "WSAWaitForMultipleEvents";
+}
+
+/** call timeouts handlers, and return how long to wait for next one or -1 */
+static void handle_timeouts(struct event_base* base, struct timeval* now,
+        struct timeval* wait)
+{
+        struct event* p;
+#ifndef S_SPLINT_S
+        wait->tv_sec = (time_t)-1;
+#endif
+	verbose(VERB_CLIENT, "winsock_event handle_timeouts");
+
+        while((rbnode_t*)(p = (struct event*)rbtree_first(base->times))
+                !=RBTREE_NULL) {
+#ifndef S_SPLINT_S
+                if(p->ev_timeout.tv_sec > now->tv_sec ||
+                        (p->ev_timeout.tv_sec==now->tv_sec &&
+                        p->ev_timeout.tv_usec > now->tv_usec)) {
+                        /* there is a next larger timeout. wait for it */
+                        wait->tv_sec = p->ev_timeout.tv_sec - now->tv_sec;
+                        if(now->tv_usec > p->ev_timeout.tv_usec) {
+                                wait->tv_sec--;
+                                wait->tv_usec = 1000000 - (now->tv_usec -
+                                        p->ev_timeout.tv_usec);
+                        } else {
+                                wait->tv_usec = p->ev_timeout.tv_usec
+                                        - now->tv_usec;
+                        }
+			verbose(VERB_CLIENT, "winsock_event wait=%d.%6.6d",
+				(int)wait->tv_sec, (int)wait->tv_usec);
+                        return;
+                }
+#endif
+                /* event times out, remove it */
+                (void)rbtree_delete(base->times, p);
+                p->ev_events &= ~EV_TIMEOUT;
+                fptr_ok(fptr_whitelist_event(p->ev_callback));
+                (*p->ev_callback)(p->ev_fd, EV_TIMEOUT, p->ev_arg);
+        }
+	verbose(VERB_CLIENT, "winsock_event wait=(-1)");
+}
+
+/** handle is_signal events and see if signalled */
+static void handle_signal(struct event* ev)
+{
+	DWORD ret;
+	log_assert(ev->is_signal && ev->hEvent);
+	/* see if the event is signalled */
+	ret = WSAWaitForMultipleEvents(1, &ev->hEvent, 0 /* any object */,
+		0 /* return immediately */, 0 /* not alertable for IOcomple*/);
+	if(ret == WSA_WAIT_IO_COMPLETION || ret == WSA_WAIT_FAILED) {
+		log_err("WSAWaitForMultipleEvents(signal) failed: %s",
+			wsa_strerror(WSAGetLastError()));
+		return;
+	}
+	if(ret == WSA_WAIT_TIMEOUT) {
+		/* not signalled */
+		return;
+	}
+
+	/* reset the signal */
+	if(!WSAResetEvent(ev->hEvent))
+		log_err("WSAResetEvent failed: %s",
+			wsa_strerror(WSAGetLastError()));
+	/* do the callback (which may set the signal again) */
+	fptr_ok(fptr_whitelist_event(ev->ev_callback));
+	(*ev->ev_callback)(ev->ev_fd, ev->ev_events, ev->ev_arg);
+}
+
+/** call select and callbacks for that */
+static int handle_select(struct event_base* base, struct timeval* wait)
+{
+	DWORD timeout = 0; /* in milliseconds */	
+	DWORD ret;
+	struct event* eventlist[WSK_MAX_ITEMS];
+	WSANETWORKEVENTS netev;
+	int i, numwait = 0, startidx = 0, was_timeout = 0;
+	int newstickies = 0;
+	struct timeval nultm;
+
+	verbose(VERB_CLIENT, "winsock_event handle_select");
+
+#ifndef S_SPLINT_S
+        if(wait->tv_sec==(time_t)-1)
+                wait = NULL;
+	if(wait)
+		timeout = wait->tv_sec*1000 + wait->tv_usec/1000;
+	if(base->tcp_stickies) {
+		wait = &nultm;
+		nultm.tv_sec = 0;
+		nultm.tv_usec = 0;
+		timeout = 0; /* no waiting, we have sticky events */
+	}
+#endif
+
+	/* prepare event array */
+	for(i=0; i<base->max; i++) {
+		if(base->items[i]->ev_fd == -1 && !base->items[i]->is_signal)
+			continue; /* skip timer only events */
+		eventlist[numwait] = base->items[i];
+		base->waitfor[numwait++] = base->items[i]->hEvent;
+		if(numwait == WSK_MAX_ITEMS)
+			break; /* sanity check */
+	}
+	log_assert(numwait <= WSA_MAXIMUM_WAIT_EVENTS);
+	verbose(VERB_CLIENT, "winsock_event bmax=%d numwait=%d wait=%x "
+		"timeout=%d", base->max, numwait, (int)wait, (int)timeout);
+
+	/* do the wait */
+	if(numwait == 0) {
+		/* WSAWaitFor.. doesn't like 0 event objects */
+		if(wait) {
+			Sleep(timeout);
+		}
+		was_timeout = 1;
+	} else {
+		ret = WSAWaitForMultipleEvents(numwait, base->waitfor,
+			0 /* do not wait for all, just one will do */,
+			wait?timeout:WSA_INFINITE,
+			0); /* we are not alertable (IO completion events) */
+		if(ret == WSA_WAIT_IO_COMPLETION) {
+			log_err("WSAWaitForMultipleEvents failed: WSA_WAIT_IO_COMPLETION");
+			return -1;
+		} else if(ret == WSA_WAIT_FAILED) {
+			log_err("WSAWaitForMultipleEvents failed: %s", 
+				wsa_strerror(WSAGetLastError()));
+			return -1;
+		} else if(ret == WSA_WAIT_TIMEOUT) {
+			was_timeout = 1;
+		} else
+			startidx = ret - WSA_WAIT_EVENT_0;
+	}
+	verbose(VERB_CLIENT, "winsock_event wake was_timeout=%d startidx=%d", 
+		was_timeout, startidx);
+
+	/* get new time after wait */
+        if(settime(base) < 0)
+               return -1;
+
+	/* callbacks */
+	if(base->tcp_stickies)
+		startidx = 0; /* process all events, some are sticky */
+	for(i=startidx; i<numwait; i++)
+		eventlist[i]->just_checked = 1;
+
+	verbose(VERB_CLIENT, "winsock_event signals");
+	for(i=startidx; i<numwait; i++) {
+		if(!base->waitfor[i])
+			continue; /* was deleted */
+		if(eventlist[i]->is_signal) {
+			eventlist[i]->just_checked = 0;
+			handle_signal(eventlist[i]);
+		}
+	}
+	/* early exit - do not process network, exit quickly */
+	if(base->need_to_exit)
+		return 0;
+
+	verbose(VERB_CLIENT, "winsock_event net");
+	for(i=startidx; i<numwait; i++) {
+		short bits = 0;
+		/* eventlist[i] fired */
+		/* see if eventlist[i] is still valid and just checked from
+		 * WSAWaitForEvents */
+		if(!base->waitfor[i])
+			continue; /* was deleted */
+		if(!eventlist[i]->just_checked)
+			continue; /* added by other callback */
+		if(eventlist[i]->is_signal)
+			continue; /* not a network event at all */
+		eventlist[i]->just_checked = 0;
+
+		if(WSAEnumNetworkEvents(eventlist[i]->ev_fd, 
+			base->waitfor[i], /* reset the event handle */
+			/*NULL,*/ /* do not reset the event handle */
+			&netev) != 0) {
+			log_err("WSAEnumNetworkEvents failed: %s", 
+				wsa_strerror(WSAGetLastError()));
+			return -1;
+		}
+		if((netev.lNetworkEvents & FD_READ)) {
+			if(netev.iErrorCode[FD_READ_BIT] != 0)
+				verbose(VERB_ALGO, "FD_READ_BIT error: %s",
+				wsa_strerror(netev.iErrorCode[FD_READ_BIT]));
+			bits |= EV_READ;
+		}
+		if((netev.lNetworkEvents & FD_WRITE)) {
+			if(netev.iErrorCode[FD_WRITE_BIT] != 0)
+				verbose(VERB_ALGO, "FD_WRITE_BIT error: %s",
+				wsa_strerror(netev.iErrorCode[FD_WRITE_BIT]));
+			bits |= EV_WRITE;
+		}
+		if((netev.lNetworkEvents & FD_CONNECT)) {
+			if(netev.iErrorCode[FD_CONNECT_BIT] != 0)
+				verbose(VERB_ALGO, "FD_CONNECT_BIT error: %s",
+				wsa_strerror(netev.iErrorCode[FD_CONNECT_BIT]));
+			bits |= EV_READ;
+			bits |= EV_WRITE;
+		}
+		if((netev.lNetworkEvents & FD_ACCEPT)) {
+			if(netev.iErrorCode[FD_ACCEPT_BIT] != 0)
+				verbose(VERB_ALGO, "FD_ACCEPT_BIT error: %s",
+				wsa_strerror(netev.iErrorCode[FD_ACCEPT_BIT]));
+			bits |= EV_READ;
+		}
+		if((netev.lNetworkEvents & FD_CLOSE)) {
+			if(netev.iErrorCode[FD_CLOSE_BIT] != 0)
+				verbose(VERB_ALGO, "FD_CLOSE_BIT error: %s",
+				wsa_strerror(netev.iErrorCode[FD_CLOSE_BIT]));
+			bits |= EV_READ;
+			bits |= EV_WRITE;
+		}
+		if(eventlist[i]->is_tcp && eventlist[i]->stick_events) {
+			verbose(VERB_ALGO, "winsock %d pass sticky %s%s",
+				eventlist[i]->ev_fd,
+				(eventlist[i]->old_events&EV_READ)?"EV_READ":"",
+				(eventlist[i]->old_events&EV_WRITE)?"EV_WRITE":"");
+			bits |= eventlist[i]->old_events;
+		}
+		if(eventlist[i]->is_tcp && bits) {
+			eventlist[i]->old_events = bits;
+			eventlist[i]->stick_events = 1;
+			if((eventlist[i]->ev_events & bits)) {
+				newstickies = 1;
+			}
+			verbose(VERB_ALGO, "winsock %d store sticky %s%s",
+				eventlist[i]->ev_fd,
+				(eventlist[i]->old_events&EV_READ)?"EV_READ":"",
+				(eventlist[i]->old_events&EV_WRITE)?"EV_WRITE":"");
+		}
+		if((bits & eventlist[i]->ev_events)) {
+			verbose(VERB_ALGO, "winsock event callback %p fd=%d "
+				"%s%s%s%s%s ; %s%s%s", 
+				eventlist[i], eventlist[i]->ev_fd,
+				(netev.lNetworkEvents&FD_READ)?" FD_READ":"",
+				(netev.lNetworkEvents&FD_WRITE)?" FD_WRITE":"",
+				(netev.lNetworkEvents&FD_CONNECT)?
+					" FD_CONNECT":"",
+				(netev.lNetworkEvents&FD_ACCEPT)?
+					" FD_ACCEPT":"",
+				(netev.lNetworkEvents&FD_CLOSE)?" FD_CLOSE":"",
+				(bits&EV_READ)?" EV_READ":"",
+				(bits&EV_WRITE)?" EV_WRITE":"",
+				(bits&EV_TIMEOUT)?" EV_TIMEOUT":"");
+				
+                        fptr_ok(fptr_whitelist_event(
+                                eventlist[i]->ev_callback));
+                        (*eventlist[i]->ev_callback)(eventlist[i]->ev_fd,
+                                bits & eventlist[i]->ev_events, 
+				eventlist[i]->ev_arg);
+		}
+		if(eventlist[i]->is_tcp && bits)
+			verbose(VERB_ALGO, "winsock %d got sticky %s%s",
+				eventlist[i]->ev_fd,
+				(eventlist[i]->old_events&EV_READ)?"EV_READ":"",
+				(eventlist[i]->old_events&EV_WRITE)?"EV_WRITE":"");
+	}
+	verbose(VERB_CLIENT, "winsock_event net");
+	if(base->tcp_reinvigorated) {
+		verbose(VERB_CLIENT, "winsock_event reinvigorated");
+		base->tcp_reinvigorated = 0;
+		newstickies = 1;
+	}
+	base->tcp_stickies = newstickies;
+	verbose(VERB_CLIENT, "winsock_event handle_select end");
+        return 0;
+}
+
+int event_base_dispatch(struct event_base *base)
+{
+        struct timeval wait;
+        if(settime(base) < 0)
+                return -1;
+        while(!base->need_to_exit)
+        {
+                /* see if timeouts need handling */
+                handle_timeouts(base, base->time_tv, &wait);
+                if(base->need_to_exit)
+                        return 0;
+                /* do select */
+                if(handle_select(base, &wait) < 0) {
+                        if(base->need_to_exit)
+                                return 0;
+                        return -1;
+                }
+        }
+        return 0;
+}
+
+int event_base_loopexit(struct event_base *base, 
+	struct timeval * ATTR_UNUSED(tv))
+{
+	verbose(VERB_CLIENT, "winsock_event loopexit");
+        base->need_to_exit = 1;
+        return 0;
+}
+
+void event_base_free(struct event_base *base)
+{
+	verbose(VERB_CLIENT, "winsock_event event_base_free");
+        if(!base)
+                return;
+	if(base->items)
+		free(base->items);
+        if(base->times)
+                free(base->times);
+        if(base->signals)
+                free(base->signals);
+        free(base);
+}
+
+void event_set(struct event *ev, int fd, short bits, 
+	void (*cb)(int, short, void *), void *arg)
+{
+        ev->node.key = ev;
+        ev->ev_fd = fd;
+        ev->ev_events = bits;
+        ev->ev_callback = cb;
+        fptr_ok(fptr_whitelist_event(ev->ev_callback));
+        ev->ev_arg = arg;
+	ev->just_checked = 0;
+        ev->added = 0;
+}
+
+int event_base_set(struct event_base *base, struct event *ev)
+{
+        ev->ev_base = base;
+	ev->old_events = 0;
+	ev->stick_events = 0;
+        ev->added = 0;
+        return 0;
+}
+
+int event_add(struct event *ev, struct timeval *tv)
+{
+	verbose(VERB_ALGO, "event_add %p added=%d fd=%d tv=%d %s%s%s", 
+		ev, ev->added, ev->ev_fd, 
+		(tv?(int)tv->tv_sec*1000+(int)tv->tv_usec/1000:-1),
+		(ev->ev_events&EV_READ)?" EV_READ":"",
+		(ev->ev_events&EV_WRITE)?" EV_WRITE":"",
+		(ev->ev_events&EV_TIMEOUT)?" EV_TIMEOUT":"");
+        if(ev->added)
+                event_del(ev);
+	log_assert(ev->ev_fd==-1 || find_fd(ev->ev_base, ev->ev_fd) == -1);
+	ev->is_tcp = 0;
+	ev->is_signal = 0;
+	ev->just_checked = 0;
+
+        if((ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
+		BOOL b=0;
+		int t, l;
+		long events = 0;
+
+		if(ev->ev_base->max == ev->ev_base->cap)
+			return -1;
+		ev->idx = ev->ev_base->max++;
+		ev->ev_base->items[ev->idx] = ev;
+
+		if( (ev->ev_events&EV_READ) )
+			events |= FD_READ;
+		if( (ev->ev_events&EV_WRITE) )
+			events |= FD_WRITE;
+		l = sizeof(t);
+		if(getsockopt(ev->ev_fd, SOL_SOCKET, SO_TYPE,
+			(void*)&t, &l) != 0)
+			log_err("getsockopt(SO_TYPE) failed: %s",
+				wsa_strerror(WSAGetLastError()));
+		if(t == SOCK_STREAM) {
+			/* TCP socket */
+			ev->is_tcp = 1;
+			events |= FD_CLOSE;
+			if( (ev->ev_events&EV_WRITE) )
+				events |= FD_CONNECT;
+			l = sizeof(b);
+			if(getsockopt(ev->ev_fd, SOL_SOCKET, SO_ACCEPTCONN,
+				(void*)&b, &l) != 0)
+				log_err("getsockopt(SO_ACCEPTCONN) failed: %s",
+					wsa_strerror(WSAGetLastError()));
+			if(b) /* TCP accept socket */
+				events |= FD_ACCEPT;
+		}
+		ev->hEvent = WSACreateEvent();
+		if(ev->hEvent == WSA_INVALID_EVENT)
+			log_err("WSACreateEvent failed: %s",
+				wsa_strerror(WSAGetLastError()));
+		/* automatically sets fd to nonblocking mode.
+		 * nonblocking cannot be disabled, until wsaES(fd, NULL, 0) */
+		if(WSAEventSelect(ev->ev_fd, ev->hEvent, events) != 0) {
+			log_err("WSAEventSelect failed: %s",
+				wsa_strerror(WSAGetLastError()));
+		}
+		if(ev->is_tcp && ev->stick_events && 
+			(ev->ev_events & ev->old_events)) {
+			/* go to processing the sticky event right away */
+			ev->ev_base->tcp_reinvigorated = 1;
+		}
+	}
+
+	if(tv && (ev->ev_events&EV_TIMEOUT)) {
+#ifndef S_SPLINT_S
+                struct timeval *now = ev->ev_base->time_tv;
+                ev->ev_timeout.tv_sec = tv->tv_sec + now->tv_sec;
+                ev->ev_timeout.tv_usec = tv->tv_usec + now->tv_usec;
+                while(ev->ev_timeout.tv_usec > 1000000) {
+                        ev->ev_timeout.tv_usec -= 1000000;
+                        ev->ev_timeout.tv_sec++;
+                }
+#endif
+                (void)rbtree_insert(ev->ev_base->times, &ev->node);
+        }
+        ev->added = 1;
+	return 0;
+}
+
+int event_del(struct event *ev)
+{
+	verbose(VERB_ALGO, "event_del %p added=%d fd=%d tv=%d %s%s%s", 
+		ev, ev->added, ev->ev_fd, 
+		(ev->ev_events&EV_TIMEOUT)?(int)ev->ev_timeout.tv_sec*1000+
+		(int)ev->ev_timeout.tv_usec/1000:-1,
+		(ev->ev_events&EV_READ)?" EV_READ":"",
+		(ev->ev_events&EV_WRITE)?" EV_WRITE":"",
+		(ev->ev_events&EV_TIMEOUT)?" EV_TIMEOUT":"");
+	if(!ev->added)
+		return 0;
+	log_assert(ev->added);
+        if((ev->ev_events&EV_TIMEOUT))
+                (void)rbtree_delete(ev->ev_base->times, &ev->node);
+        if((ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
+		log_assert(ev->ev_base->max > 0);
+		/* remove item and compact the list */
+		ev->ev_base->items[ev->idx] = 
+			ev->ev_base->items[ev->ev_base->max-1];
+		ev->ev_base->items[ev->ev_base->max-1] = NULL;
+		ev->ev_base->max--;
+		if(ev->idx < ev->ev_base->max)
+			ev->ev_base->items[ev->idx]->idx = ev->idx;
+		zero_waitfor(ev->ev_base->waitfor, ev->hEvent);
+
+		if(WSAEventSelect(ev->ev_fd, ev->hEvent, 0) != 0)
+			log_err("WSAEventSelect(disable) failed: %s",
+				wsa_strerror(WSAGetLastError()));
+		if(!WSACloseEvent(ev->hEvent))
+			log_err("WSACloseEvent failed: %s",
+				wsa_strerror(WSAGetLastError()));
+	}
+	ev->just_checked = 0;
+        ev->added = 0;
+        return 0;
+}
+
+/** which base gets to handle signals */
+static struct event_base* signal_base = NULL;
+/** signal handler */
+static RETSIGTYPE sigh(int sig)
+{
+        struct event* ev;
+        if(!signal_base || sig < 0 || sig >= MAX_SIG)
+                return;
+        ev = signal_base->signals[sig];
+        if(!ev)
+                return;
+        fptr_ok(fptr_whitelist_event(ev->ev_callback));
+        (*ev->ev_callback)(sig, EV_SIGNAL, ev->ev_arg);
+}
+
+int signal_add(struct event *ev, struct timeval * ATTR_UNUSED(tv))
+{
+        if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
+                return -1;
+        signal_base = ev->ev_base;
+        ev->ev_base->signals[ev->ev_fd] = ev;
+        ev->added = 1;
+        if(signal(ev->ev_fd, sigh) == SIG_ERR) {
+                return -1;
+        }
+        return 0;
+}
+
+int signal_del(struct event *ev)
+{
+        if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
+                return -1;
+        ev->ev_base->signals[ev->ev_fd] = NULL;
+        ev->added = 0;
+        return 0;
+}
+
+void winsock_tcp_wouldblock(struct event* ev, int eventbits)
+{
+	verbose(VERB_ALGO, "winsock: tcp wouldblock %s", 
+		eventbits==EV_READ?"EV_READ":"EV_WRITE");
+	ev->old_events &= (~eventbits);
+	if(ev->old_events == 0)
+		ev->stick_events = 0;
+		/* in case this is the last sticky event, we could
+		 * possibly run an empty handler loop to reset the base
+		 * tcp_stickies variable 
+		 */
+}
+
+int winsock_register_wsaevent(struct event_base* base, struct event* ev,
+	WSAEVENT wsaevent, void (*cb)(int, short, void*), void* arg)
+{
+	if(base->max == base->cap)
+		return 0;
+	memset(ev, 0, sizeof(*ev));
+	ev->ev_fd = -1;
+	ev->ev_events = EV_READ;
+	ev->ev_callback = cb;
+	ev->ev_arg = arg;
+	ev->is_signal = 1;
+	ev->hEvent = wsaevent;
+	ev->added = 1;
+	ev->ev_base = base;
+	ev->idx = ev->ev_base->max++;
+	ev->ev_base->items[ev->idx] = ev;
+	return 1;
+}
+
+void winsock_unregister_wsaevent(struct event* ev)
+{
+	if(!ev || !ev->added) return;
+	log_assert(ev->added && ev->ev_base->max > 0)
+	/* remove item and compact the list */
+	ev->ev_base->items[ev->idx] = ev->ev_base->items[ev->ev_base->max-1];
+	ev->ev_base->items[ev->ev_base->max-1] = NULL;
+	ev->ev_base->max--;
+	if(ev->idx < ev->ev_base->max)
+		ev->ev_base->items[ev->idx]->idx = ev->idx;
+	ev->added = 0;
+}
+
+#else /* USE_WINSOCK */
+/** symbol so this codefile defines symbols. pleasing ranlib on OSX 10.5 */
+int winsock_unused_symbol = 1;
+#endif /* USE_WINSOCK */
diff --git a/3rdParty/Unbound/src/src/util/winsock_event.h b/3rdParty/Unbound/src/src/util/winsock_event.h
new file mode 100644
index 0000000..088283e
--- /dev/null
+++ b/3rdParty/Unbound/src/src/util/winsock_event.h
@@ -0,0 +1,264 @@
+/*
+ * util/winsock_event.h - unbound event handling for winsock on windows
+ *
+ * Copyright (c) 2008, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains interface functions with the WinSock2 API on Windows.
+ * It uses the winsock WSAWaitForMultipleEvents interface on a number of
+ * sockets.
+ *
+ * Note that windows can only wait for max 64 events at one time.
+ * 
+ * Also, file descriptors cannot be waited for.
+ *
+ * Named pipes are not easily available (and are not usable in select() ).
+ * For interprocess communication, it is possible to wait for a hEvent to
+ * be signaled by another thread.
+ *
+ * When a socket becomes readable, then it will not be flagged as 
+ * readable again until you have gotten WOULDBLOCK from a recv routine.
+ * That means the event handler must store the readability (edge notify)
+ * and process the incoming data until it blocks. 
+ * The function performing recv then has to inform the event handler that
+ * the socket has blocked, and the event handler can mark it as such.
+ * Thus, this file transforms the edge notify from windows to a level notify
+ * that is compatible with UNIX.
+ * The WSAEventSelect page says that it does do level notify, as long
+ * as you call a recv/write/accept at least once when it is signalled.
+ * This last bit is not true, even though documented in server2008 api docs
+ * from microsoft, it does not happen at all. Instead you have to test for
+ * WSAEWOULDBLOCK on a tcp stream, and only then retest the socket.
+ * And before that remember the previous result as still valid.
+ *
+ * To stay 'fair', instead of emptying a socket completely, the event handler
+ * can test the other (marked as blocking) sockets for new events.
+ *
+ * Additionally, TCP accept sockets get special event support.
+ *
+ * Socket numbers are not starting small, they can be any number (say 33060).
+ * Therefore, bitmaps are not used, but arrays.
+ *
+ * on winsock, you must use recv() and send() for TCP reads and writes,
+ * not read() and write(), those work only on files.
+ *
+ * Also fseek and fseeko do not work if a FILE is not fopen-ed in binary mode.
+ *
+ * When under a high load windows gives out lots of errors, from recvfrom
+ * on udp sockets for example (WSAECONNRESET). Even though the udp socket
+ * has no connection per se.
+ */
+
+#ifndef UTIL_WINSOCK_EVENT_H
+#define UTIL_WINSOCK_EVENT_H
+
+#ifdef USE_WINSOCK
+
+#ifndef HAVE_EVENT_BASE_FREE
+#define HAVE_EVENT_BASE_FREE
+#endif
+
+/** event timeout */
+#define EV_TIMEOUT      0x01
+/** event fd readable */
+#define EV_READ         0x02
+/** event fd writable */
+#define EV_WRITE        0x04
+/** event signal */
+#define EV_SIGNAL       0x08
+/** event must persist */
+#define EV_PERSIST      0x10
+
+/* needs our redblack tree */
+#include "rbtree.h"
+
+/** max number of signals to support */
+#define MAX_SIG 32
+
+/** The number of items that the winsock event handler can service.
+ * Windows cannot handle more anyway */
+#define WSK_MAX_ITEMS 64
+
+/**
+ * event base for winsock event handler
+ */
+struct event_base
+{
+	/** sorted by timeout (absolute), ptr */
+	rbtree_t* times;
+	/** array (first part in use) of handles to work on */
+	struct event** items;
+	/** number of items in use in array */
+	int max;
+	/** capacity of array, size of array in items */
+	int cap;
+	/** array of 0 - maxsig of ptr to event for it */
+        struct event** signals;
+	/** if we need to exit */
+	int need_to_exit;
+	/** where to store time in seconds */
+	uint32_t* time_secs;
+	/** where to store time in microseconds */
+	struct timeval* time_tv;
+	/** 
+	 * TCP streams have sticky events to them, these are not
+	 * reported by the windows event system anymore, we have to
+	 * keep reporting those events as present until wouldblock() is
+	 * signalled by the handler back to use.
+	 */
+	int tcp_stickies;
+	/**
+	 * should next cycle process reinvigorated stickies,
+	 * these are stickies that have been stored, but due to a new
+	 * event_add a sudden interest in the event has incepted.
+	 */
+	int tcp_reinvigorated;
+	/** The list of events that is currently being processed. */
+	WSAEVENT waitfor[WSK_MAX_ITEMS];
+};
+
+/**
+ * Event structure. Has some of the event elements.
+ */
+struct event {
+        /** node in timeout rbtree */
+        rbnode_t node;
+        /** is event already added */
+        int added;
+
+        /** event base it belongs to */
+        struct event_base *ev_base;
+        /** fd to poll or -1 for timeouts. signal number for sigs. */
+        int ev_fd;
+        /** what events this event is interested in, see EV_.. above. */
+        short ev_events;
+        /** timeout value */
+        struct timeval ev_timeout;
+
+        /** callback to call: fd, eventbits, userarg */
+        void (*ev_callback)(int, short, void *);
+        /** callback user arg */
+        void *ev_arg;
+
+	/* ----- nonpublic part, for winsock_event only ----- */
+	/** index of this event in the items array (if added) */
+	int idx;
+	/** the event handle to wait for new events to become ready */
+	WSAEVENT hEvent;
+	/** true if this filedes is a TCP socket and needs special attention */
+	int is_tcp;
+	/** remembered EV_ values */
+	short old_events;
+	/** should remembered EV_ values be used for TCP streams. 
+	 * Reset after WOULDBLOCK is signaled using the function. */
+	int stick_events;
+
+	/** true if this event is a signaling WSAEvent by the user. 
+	 * User created and user closed WSAEvent. Only signaled/unsigneled,
+	 * no read/write/distinctions needed. */
+	int is_signal;
+	/** used during callbacks to see which events were just checked */
+	int just_checked;
+};
+
+/** create event base */
+void *event_init(uint32_t* time_secs, struct timeval* time_tv);
+/** get version */
+const char *event_get_version(void);
+/** get polling method (select,epoll) */
+const char *event_get_method(void);
+/** run select in a loop */
+int event_base_dispatch(struct event_base *);
+/** exit that loop */
+int event_base_loopexit(struct event_base *, struct timeval *);
+/** free event base. Free events yourself */
+void event_base_free(struct event_base *);
+/** set content of event */
+void event_set(struct event *, int, short, void (*)(int, short, void *), void *);
+
+/** add event to a base. You *must* call this for every event. */
+int event_base_set(struct event_base *, struct event *);
+/** add event to make it active. You may not change it with event_set anymore */
+int event_add(struct event *, struct timeval *);
+/** remove event. You may change it again */
+int event_del(struct event *);
+
+#define evtimer_add(ev, tv)             event_add(ev, tv)
+#define evtimer_del(ev)                 event_del(ev)
+
+/* uses different implementation. Cannot mix fd/timeouts and signals inside
+ * the same struct event. create several event structs for that.  */
+/** install signal handler */
+int signal_add(struct event *, struct timeval *);
+/** set signal event contents */
+#define signal_set(ev, x, cb, arg)      \
+        event_set(ev, x, EV_SIGNAL|EV_PERSIST, cb, arg)
+/** remove signal handler */
+int signal_del(struct event *);
+
+/** compare events in tree, based on timevalue, ptr for uniqueness */
+int mini_ev_cmp(const void* a, const void* b);
+
+/**
+ * Routine for windows only, where the handling layer can signal that
+ * a TCP stream encountered WSAEWOULDBLOCK for a stream and thus needs
+ * retesting the event.
+ * Pass if EV_READ or EV_WRITE gave wouldblock.
+ */
+void winsock_tcp_wouldblock(struct event* ev, int eventbit);
+
+/**
+ * Routine for windows only. where you pass a signal WSAEvent that
+ * you wait for. When the event is signaled, the callback gets called.
+ * The callback has to WSAResetEvent to disable the signal. 
+ * @param base: the event base.
+ * @param ev: the event structure for data storage
+ * 	can be passed uninitialised.
+ * @param wsaevent: the WSAEvent that gets signaled.
+ * @param cb: callback routine.
+ * @param arg: user argument to callback routine.
+ * @return false on error.
+ */
+int winsock_register_wsaevent(struct event_base* base, struct event* ev,
+	WSAEVENT wsaevent, void (*cb)(int, short, void*), void* arg);
+
+/**
+ * Unregister a wsaevent. User has to close the WSAEVENT itself.
+ * @param ev: event data storage.
+ */
+void winsock_unregister_wsaevent(struct event* ev);
+
+#endif /* USE_WINSOCK */
+#endif /* UTIL_WINSOCK_EVENT_H */
diff --git a/3rdParty/Unbound/src/src/validator/autotrust.c b/3rdParty/Unbound/src/src/validator/autotrust.c
new file mode 100644
index 0000000..8c3a7c6
--- /dev/null
+++ b/3rdParty/Unbound/src/src/validator/autotrust.c
@@ -0,0 +1,2200 @@
+/*
+ * validator/autotrust.c - RFC5011 trust anchor management for unbound.
+ *
+ * Copyright (c) 2009, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * Contains autotrust implementation. The implementation was taken from 
+ * the autotrust daemon (BSD licensed), written by Matthijs Mekking.
+ * It was modified to fit into unbound. The state table process is the same.
+ */
+#include "config.h"
+#include <ldns/ldns.h>
+#include "validator/autotrust.h"
+#include "validator/val_anchor.h"
+#include "validator/val_utils.h"
+#include "validator/val_sigcrypt.h"
+#include "util/data/dname.h"
+#include "util/data/packed_rrset.h"
+#include "util/log.h"
+#include "util/module.h"
+#include "util/net_help.h"
+#include "util/config_file.h"
+#include "util/regional.h"
+#include "util/random.h"
+#include "util/data/msgparse.h"
+#include "services/mesh.h"
+#include "services/cache/rrset.h"
+#include "validator/val_kcache.h"
+
+/** number of times a key must be seen before it can become valid */
+#define MIN_PENDINGCOUNT 2
+
+/** Event: Revoked */
+static void do_revoked(struct module_env* env, struct autr_ta* anchor, int* c);
+
+struct autr_global_data* autr_global_create(void)
+{
+	struct autr_global_data* global;
+	global = (struct autr_global_data*)malloc(sizeof(*global));
+	if(!global) 
+		return NULL;
+	rbtree_init(&global->probe, &probetree_cmp);
+	return global;
+}
+
+void autr_global_delete(struct autr_global_data* global)
+{
+	if(!global)
+		return;
+	/* elements deleted by parent */
+	memset(global, 0, sizeof(*global));
+	free(global);
+}
+
+int probetree_cmp(const void* x, const void* y)
+{
+	struct trust_anchor* a = (struct trust_anchor*)x;
+	struct trust_anchor* b = (struct trust_anchor*)y;
+	log_assert(a->autr && b->autr);
+	if(a->autr->next_probe_time < b->autr->next_probe_time)
+		return -1;
+	if(a->autr->next_probe_time > b->autr->next_probe_time)
+		return 1;
+	/* time is equal, sort on trust point identity */
+	return anchor_cmp(x, y);
+}
+
+size_t 
+autr_get_num_anchors(struct val_anchors* anchors)
+{
+	size_t res = 0;
+	if(!anchors)
+		return 0;
+	lock_basic_lock(&anchors->lock);
+	if(anchors->autr)
+		res = anchors->autr->probe.count;
+	lock_basic_unlock(&anchors->lock);
+	return res;
+}
+
+/** Position in string */
+static int
+position_in_string(char *str, const char* sub)
+{
+	char* pos = strstr(str, sub);
+	if(pos)
+		return (int)(pos-str)+(int)strlen(sub);
+	return -1;
+}
+
+/** Debug routine to print pretty key information */
+static void
+verbose_key(struct autr_ta* ta, enum verbosity_value level, 
+	const char* format, ...) ATTR_FORMAT(printf, 3, 4);
+
+/** 
+ * Implementation of debug pretty key print 
+ * @param ta: trust anchor key with DNSKEY data.
+ * @param level: verbosity level to print at.
+ * @param format: printf style format string.
+ */
+static void
+verbose_key(struct autr_ta* ta, enum verbosity_value level, 
+	const char* format, ...) 
+{
+	va_list args;
+	va_start(args, format);
+	if(verbosity >= level) {
+		char* str = ldns_rdf2str(ldns_rr_owner(ta->rr));
+		int keytag = (int)ldns_calc_keytag(ta->rr);
+		char msg[MAXSYSLOGMSGLEN];
+		vsnprintf(msg, sizeof(msg), format, args);
+		verbose(level, "%s key %d %s", str?str:"??", keytag, msg);
+		free(str);
+	}
+	va_end(args);
+}
+
+/** 
+ * Parse comments 
+ * @param str: to parse
+ * @param ta: trust key autotrust metadata
+ * @return false on failure.
+ */
+static int
+parse_comments(char* str, struct autr_ta* ta)
+{
+        int len = (int)strlen(str), pos = 0, timestamp = 0;
+        char* comment = (char*) malloc(sizeof(char)*len+1);
+        char* comments = comment;
+	if(!comment) {
+		log_err("malloc failure in parse");
+                return 0;
+	}
+	/* skip over whitespace and data at start of line */
+        while (*str != '\0' && *str != ';')
+                str++;
+        if (*str == ';')
+                str++;
+        /* copy comments */
+        while (*str != '\0')
+        {
+                *comments = *str;
+                comments++;
+                str++;
+        }
+        *comments = '\0';
+
+        comments = comment;
+
+        /* read state */
+        pos = position_in_string(comments, "state=");
+        if (pos >= (int) strlen(comments))
+        {
+		log_err("parse error");
+                free(comment);
+                return 0;
+        }
+        if (pos <= 0)
+                ta->s = AUTR_STATE_VALID;
+        else
+        {
+                int s = (int) comments[pos] - '0';
+                switch(s)
+                {
+                        case AUTR_STATE_START:
+                        case AUTR_STATE_ADDPEND:
+                        case AUTR_STATE_VALID:
+                        case AUTR_STATE_MISSING:
+                        case AUTR_STATE_REVOKED:
+                        case AUTR_STATE_REMOVED:
+                                ta->s = s;
+                                break;
+                        default:
+				verbose_key(ta, VERB_OPS, "has undefined "
+					"state, considered NewKey");
+                                ta->s = AUTR_STATE_START;
+                                break;
+                }
+        }
+        /* read pending count */
+        pos = position_in_string(comments, "count=");
+        if (pos >= (int) strlen(comments))
+        {
+		log_err("parse error");
+                free(comment);
+                return 0;
+        }
+        if (pos <= 0)
+                ta->pending_count = 0;
+        else
+        {
+                comments += pos;
+                ta->pending_count = (uint8_t)atoi(comments);
+        }
+
+        /* read last change */
+        pos = position_in_string(comments, "lastchange=");
+        if (pos >= (int) strlen(comments))
+        {
+		log_err("parse error");
+                free(comment);
+                return 0;
+        }
+        if (pos >= 0)
+        {
+                comments += pos;
+                timestamp = atoi(comments);
+        }
+        if (pos < 0 || !timestamp)
+		ta->last_change = 0;
+        else
+                ta->last_change = (uint32_t)timestamp;
+
+        free(comment);
+        return 1;
+}
+
+/** Check if a line contains data (besides comments) */
+static int
+str_contains_data(char* str, char comment)
+{
+        while (*str != '\0') {
+                if (*str == comment || *str == '\n')
+                        return 0;
+                if (*str != ' ' && *str != '\t')
+                        return 1;
+                str++;
+        }
+        return 0;
+}
+
+/** Get DNSKEY flags */
+static int
+dnskey_flags(ldns_rr* rr)
+{
+	if(ldns_rr_get_type(rr) != LDNS_RR_TYPE_DNSKEY)
+		return 0;
+	return (int)ldns_read_uint16(ldns_rdf_data(ldns_rr_dnskey_flags(rr)));
+}
+
+
+/** Check if KSK DNSKEY */
+static int
+rr_is_dnskey_sep(ldns_rr* rr)
+{
+	return (dnskey_flags(rr)&DNSKEY_BIT_SEP);
+}
+
+/** Check if REVOKED DNSKEY */
+static int
+rr_is_dnskey_revoked(ldns_rr* rr)
+{
+	return (dnskey_flags(rr)&LDNS_KEY_REVOKE_KEY);
+}
+
+/** create ta */
+static struct autr_ta*
+autr_ta_create(ldns_rr* rr)
+{
+	struct autr_ta* ta = (struct autr_ta*)calloc(1, sizeof(*ta));
+	if(!ta) {
+		ldns_rr_free(rr);
+		return NULL;
+	}
+	ta->rr = rr;
+	return ta;
+}
+
+/** create tp */
+static struct trust_anchor*
+autr_tp_create(struct val_anchors* anchors, ldns_rdf* own, uint16_t dc)
+{
+	struct trust_anchor* tp = (struct trust_anchor*)calloc(1, sizeof(*tp));
+	if(!tp) return NULL;
+	tp->name = memdup(ldns_rdf_data(own), ldns_rdf_size(own));
+	if(!tp->name) {
+		free(tp);
+		return NULL;
+	}
+	tp->namelen = ldns_rdf_size(own);
+	tp->namelabs = dname_count_labels(tp->name);
+	tp->node.key = tp;
+	tp->dclass = dc;
+	tp->autr = (struct autr_point_data*)calloc(1, sizeof(*tp->autr));
+	if(!tp->autr) {
+		free(tp->name);
+		free(tp);
+		return NULL;
+	}
+	tp->autr->pnode.key = tp;
+
+	lock_basic_lock(&anchors->lock);
+	if(!rbtree_insert(anchors->tree, &tp->node)) {
+		lock_basic_unlock(&anchors->lock);
+		log_err("trust anchor presented twice");
+		free(tp->name);
+		free(tp->autr);
+		free(tp);
+		return NULL;
+	}
+	if(!rbtree_insert(&anchors->autr->probe, &tp->autr->pnode)) {
+		(void)rbtree_delete(anchors->tree, tp);
+		lock_basic_unlock(&anchors->lock);
+		log_err("trust anchor in probetree twice");
+		free(tp->name);
+		free(tp->autr);
+		free(tp);
+		return NULL;
+	}
+	lock_basic_unlock(&anchors->lock);
+	lock_basic_init(&tp->lock);
+	lock_protect(&tp->lock, tp, sizeof(*tp));
+	lock_protect(&tp->lock, tp->autr, sizeof(*tp->autr));
+	return tp;
+}
+
+/** delete assembled rrsets */
+static void
+autr_rrset_delete(struct ub_packed_rrset_key* r)
+{
+	if(r) {
+		free(r->rk.dname);
+		free(r->entry.data);
+		free(r);
+	}
+}
+
+void autr_point_delete(struct trust_anchor* tp)
+{
+	if(!tp)
+		return;
+	lock_unprotect(&tp->lock, tp);
+	lock_unprotect(&tp->lock, tp->autr);
+	lock_basic_destroy(&tp->lock);
+	autr_rrset_delete(tp->ds_rrset);
+	autr_rrset_delete(tp->dnskey_rrset);
+	if(tp->autr) {
+		struct autr_ta* p = tp->autr->keys, *np;
+		while(p) {
+			np = p->next;
+			ldns_rr_free(p->rr);
+			free(p);
+			p = np;
+		}
+		free(tp->autr->file);
+		free(tp->autr);
+	}
+	free(tp->name);
+	free(tp);
+}
+
+/** find or add a new trust point for autotrust */
+static struct trust_anchor*
+find_add_tp(struct val_anchors* anchors, ldns_rr* rr)
+{
+	struct trust_anchor* tp;
+	ldns_rdf* own = ldns_rr_owner(rr);
+	tp = anchor_find(anchors, ldns_rdf_data(own), 
+		dname_count_labels(ldns_rdf_data(own)),
+		ldns_rdf_size(own), ldns_rr_get_class(rr));
+	if(tp) {
+		if(!tp->autr) {
+			log_err("anchor cannot be with and without autotrust");
+			lock_basic_unlock(&tp->lock);
+			return NULL;
+		}
+		return tp;
+	}
+	tp = autr_tp_create(anchors, ldns_rr_owner(rr), ldns_rr_get_class(rr));
+	lock_basic_lock(&tp->lock);
+	return tp;
+}
+
+/** Add trust anchor from RR */
+static struct autr_ta*
+add_trustanchor_frm_rr(struct val_anchors* anchors, ldns_rr* rr, 
+	struct trust_anchor** tp)
+{
+	struct autr_ta* ta = autr_ta_create(rr);
+	if(!ta) 
+		return NULL;
+	*tp = find_add_tp(anchors, rr);
+	if(!*tp) {
+		ldns_rr_free(ta->rr);
+		free(ta);
+		return NULL;
+	}
+	/* add ta to tp */
+	ta->next = (*tp)->autr->keys;
+	(*tp)->autr->keys = ta;
+	lock_basic_unlock(&(*tp)->lock);
+	return ta;
+}
+
+/**
+ * Add new trust anchor from a string in file.
+ * @param anchors: all anchors
+ * @param str: string with anchor and comments, if any comments.
+ * @param tp: trust point returned.
+ * @param origin: what to use for @
+ * @param prev: previous rr name
+ * @param skip: if true, the result is NULL, but not an error, skip it.
+ * @return new key in trust point.
+ */
+static struct autr_ta*
+add_trustanchor_frm_str(struct val_anchors* anchors, char* str, 
+	struct trust_anchor** tp, ldns_rdf* origin, ldns_rdf** prev, int* skip)
+{
+        ldns_rr* rr;
+	ldns_status lstatus;
+        if (!str_contains_data(str, ';')) {
+		*skip = 1;
+                return NULL; /* empty line */
+	}
+        if (LDNS_STATUS_OK !=
+                (lstatus = ldns_rr_new_frm_str(&rr, str, 0, origin, prev)))
+        {
+        	log_err("ldns error while converting string to RR: %s",
+			ldns_get_errorstr_by_id(lstatus));
+                return NULL;
+        }
+	if(ldns_rr_get_type(rr) != LDNS_RR_TYPE_DNSKEY &&
+		ldns_rr_get_type(rr) != LDNS_RR_TYPE_DS) {
+		ldns_rr_free(rr);
+		*skip = 1;
+		return NULL; /* only DS and DNSKEY allowed */
+	}
+        return add_trustanchor_frm_rr(anchors, rr, tp);
+}
+
+/** 
+ * Load single anchor 
+ * @param anchors: all points.
+ * @param str: comments line
+ * @param fname: filename
+ * @param origin: $ORIGIN.
+ * @param prev: passed to ldns.
+ * @param skip: if true, the result is NULL, but not an error, skip it.
+ * @return false on failure, otherwise the tp read.
+ */
+static struct trust_anchor*
+load_trustanchor(struct val_anchors* anchors, char* str, const char* fname,
+	ldns_rdf* origin, ldns_rdf** prev, int* skip)
+{
+        struct autr_ta* ta = NULL;
+        struct trust_anchor* tp = NULL;
+
+        ta = add_trustanchor_frm_str(anchors, str, &tp, origin, prev, skip);
+	if(!ta)
+		return NULL;
+	lock_basic_lock(&tp->lock);
+	if(!parse_comments(str, ta)) {
+		lock_basic_unlock(&tp->lock);
+		return NULL;
+	}
+	if(!tp->autr->file) {
+		tp->autr->file = strdup(fname);
+		if(!tp->autr->file) {
+			lock_basic_unlock(&tp->lock);
+			log_err("malloc failure");
+			return NULL;
+		}
+	}
+	lock_basic_unlock(&tp->lock);
+        return tp;
+}
+
+/**
+ * Assemble the trust anchors into DS and DNSKEY packed rrsets.
+ * Uses only VALID and MISSING DNSKEYs.
+ * Read the ldns_rrs and builds packed rrsets
+ * @param tp: the trust point. Must be locked.
+ * @return false on malloc failure.
+ */
+static int 
+autr_assemble(struct trust_anchor* tp)
+{
+	ldns_rr_list* ds, *dnskey;
+	struct autr_ta* ta;
+	struct ub_packed_rrset_key* ubds=NULL, *ubdnskey=NULL;
+
+	ds = ldns_rr_list_new();
+	dnskey = ldns_rr_list_new();
+	if(!ds || !dnskey) {
+		ldns_rr_list_free(ds);
+		ldns_rr_list_free(dnskey);
+		return 0;
+	}
+	for(ta = tp->autr->keys; ta; ta = ta->next) {
+		if(ldns_rr_get_type(ta->rr) == LDNS_RR_TYPE_DS) {
+			if(!ldns_rr_list_push_rr(ds, ta->rr)) {
+				ldns_rr_list_free(ds);
+				ldns_rr_list_free(dnskey);
+				return 0;
+			}
+		} else if(ta->s == AUTR_STATE_VALID || 
+			ta->s == AUTR_STATE_MISSING) {
+			if(!ldns_rr_list_push_rr(dnskey, ta->rr)) {
+				ldns_rr_list_free(ds);
+				ldns_rr_list_free(dnskey);
+				return 0;
+			}
+		}
+	}
+
+	/* make packed rrset keys - malloced with no ID number, they
+	 * are not in the cache */
+	/* make packed rrset data (if there is a key) */
+
+	if(ldns_rr_list_rr_count(ds) > 0) {
+		ubds = ub_packed_rrset_heap_key(ds);
+		if(!ubds) 
+			goto error_cleanup;
+		ubds->entry.data = packed_rrset_heap_data(ds);
+		if(!ubds->entry.data)
+			goto error_cleanup;
+	}
+	if(ldns_rr_list_rr_count(dnskey) > 0) {
+		ubdnskey = ub_packed_rrset_heap_key(dnskey);
+		if(!ubdnskey)
+			goto error_cleanup;
+		ubdnskey->entry.data = packed_rrset_heap_data(dnskey);
+		if(!ubdnskey->entry.data) {
+		error_cleanup:
+			autr_rrset_delete(ubds);
+			autr_rrset_delete(ubdnskey);
+			ldns_rr_list_free(ds);
+			ldns_rr_list_free(dnskey);
+			return 0;
+		}
+	}
+	/* we have prepared the new keys so nothing can go wrong any more.
+	 * And we are sure we cannot be left without trustanchor after
+	 * any errors. Put in the new keys and remove old ones. */
+
+	/* free the old data */
+	autr_rrset_delete(tp->ds_rrset);
+	autr_rrset_delete(tp->dnskey_rrset);
+
+	/* assign the data to replace the old */
+	tp->ds_rrset = ubds;
+	tp->dnskey_rrset = ubdnskey;
+	tp->numDS = ldns_rr_list_rr_count(ds);
+	tp->numDNSKEY = ldns_rr_list_rr_count(dnskey);
+
+	ldns_rr_list_free(ds);
+	ldns_rr_list_free(dnskey);
+	return 1;
+}
+
+/** parse integer */
+static unsigned int
+parse_int(char* line, int* ret)
+{
+	char *e;
+	unsigned int x = (unsigned int)strtol(line, &e, 10);
+	if(line == e) {
+		*ret = -1; /* parse error */
+		return 0; 
+	}
+	*ret = 1; /* matched */
+	return x;
+}
+
+/** parse id sequence for anchor */
+static struct trust_anchor*
+parse_id(struct val_anchors* anchors, char* line)
+{
+	struct trust_anchor *tp;
+	int r;
+	ldns_rdf* rdf;
+	uint16_t dclass;
+	/* read the owner name */
+	char* next = strchr(line, ' ');
+	if(!next)
+		return NULL;
+	next[0] = 0;
+	rdf = ldns_dname_new_frm_str(line);
+	if(!rdf)
+		return NULL;
+
+	/* read the class */
+	dclass = parse_int(next+1, &r);
+	if(r == -1) {
+		ldns_rdf_deep_free(rdf);
+		return NULL;
+	}
+
+	/* find the trust point */
+	tp = autr_tp_create(anchors, rdf, dclass);
+	ldns_rdf_deep_free(rdf);
+	return tp;
+}
+
+/** 
+ * Parse variable from trustanchor header 
+ * @param line: to parse
+ * @param anchors: the anchor is added to this, if "id:" is seen.
+ * @param anchor: the anchor as result value or previously returned anchor
+ * 	value to read the variable lines into.
+ * @return: 0 no match, -1 failed syntax error, +1 success line read.
+ * 	+2 revoked trust anchor file.
+ */
+static int
+parse_var_line(char* line, struct val_anchors* anchors, 
+	struct trust_anchor** anchor)
+{
+	struct trust_anchor* tp = *anchor;
+	int r = 0;
+	if(strncmp(line, ";;id: ", 6) == 0) {
+		*anchor = parse_id(anchors, line+6);
+		if(!*anchor) return -1;
+		else return 1;
+	} else if(strncmp(line, ";;REVOKED", 9) == 0) {
+		if(tp) {
+			log_err("REVOKED statement must be at start of file");
+			return -1;
+		}
+		return 2;
+	} else if(strncmp(line, ";;last_queried: ", 16) == 0) {
+		if(!tp) return -1;
+		lock_basic_lock(&tp->lock);
+		tp->autr->last_queried = (time_t)parse_int(line+16, &r);
+		lock_basic_unlock(&tp->lock);
+	} else if(strncmp(line, ";;last_success: ", 16) == 0) {
+		if(!tp) return -1;
+		lock_basic_lock(&tp->lock);
+		tp->autr->last_success = (time_t)parse_int(line+16, &r);
+		lock_basic_unlock(&tp->lock);
+	} else if(strncmp(line, ";;next_probe_time: ", 19) == 0) {
+		if(!tp) return -1;
+		lock_basic_lock(&anchors->lock);
+		lock_basic_lock(&tp->lock);
+		(void)rbtree_delete(&anchors->autr->probe, tp);
+		tp->autr->next_probe_time = (time_t)parse_int(line+19, &r);
+		(void)rbtree_insert(&anchors->autr->probe, &tp->autr->pnode);
+		lock_basic_unlock(&tp->lock);
+		lock_basic_unlock(&anchors->lock);
+	} else if(strncmp(line, ";;query_failed: ", 16) == 0) {
+		if(!tp) return -1;
+		lock_basic_lock(&tp->lock);
+		tp->autr->query_failed = (uint8_t)parse_int(line+16, &r);
+		lock_basic_unlock(&tp->lock);
+	} else if(strncmp(line, ";;query_interval: ", 18) == 0) {
+		if(!tp) return -1;
+		lock_basic_lock(&tp->lock);
+		tp->autr->query_interval = (uint32_t)parse_int(line+18, &r);
+		lock_basic_unlock(&tp->lock);
+	} else if(strncmp(line, ";;retry_time: ", 14) == 0) {
+		if(!tp) return -1;
+		lock_basic_lock(&tp->lock);
+		tp->autr->retry_time = (uint32_t)parse_int(line+14, &r);
+		lock_basic_unlock(&tp->lock);
+	}
+	return r;
+}
+
+/** handle origin lines */
+static int
+handle_origin(char* line, ldns_rdf** origin)
+{
+	while(isspace((int)*line))
+		line++;
+	if(strncmp(line, "$ORIGIN", 7) != 0)
+		return 0;
+	ldns_rdf_deep_free(*origin);
+	line += 7;
+	while(isspace((int)*line))
+		line++;
+	*origin = ldns_dname_new_frm_str(line);
+	if(!*origin)
+		log_warn("malloc failure or parse error in $ORIGIN");
+	return 1;
+}
+
+/** Read one line and put multiline RRs onto one line string */
+static int
+read_multiline(char* buf, size_t len, FILE* in, int* linenr)
+{
+	char* pos = buf;
+	size_t left = len;
+	int depth = 0;
+	buf[len-1] = 0;
+	while(left > 0 && fgets(pos, (int)left, in) != NULL) {
+		size_t i, poslen = strlen(pos);
+		(*linenr)++;
+
+		/* check what the new depth is after the line */
+		/* this routine cannot handle braces inside quotes,
+		   say for TXT records, but this routine only has to read keys */
+		for(i=0; i<poslen; i++) {
+			if(pos[i] == '(') {
+				depth++;
+			} else if(pos[i] == ')') {
+				if(depth == 0) {
+					log_err("mismatch: too many ')'");
+					return -1;
+				}
+				depth--;
+			} else if(pos[i] == ';') {
+				break;
+			}
+		}
+
+		/* normal oneline or last line: keeps newline and comments */
+		if(depth == 0) {
+			return 1;
+		}
+
+		/* more lines expected, snip off comments and newline */
+		if(poslen>0) 
+			pos[poslen-1] = 0; /* strip newline */
+		if(strchr(pos, ';')) 
+			strchr(pos, ';')[0] = 0; /* strip comments */
+
+		/* move to paste other lines behind this one */
+		poslen = strlen(pos);
+		pos += poslen;
+		left -= poslen;
+		/* the newline is changed into a space */
+		if(left <= 2 /* space and eos */) {
+			log_err("line too long");
+			return -1;
+		}
+		pos[0] = ' ';
+		pos[1] = 0;
+		pos += 1;
+		left -= 1;
+	}
+	if(depth != 0) {
+		log_err("mismatch: too many '('");
+		return -1;
+	}
+	if(pos != buf)
+		return 1;
+	return 0;
+}
+
+int autr_read_file(struct val_anchors* anchors, const char* nm)
+{
+        /* the file descriptor */
+        FILE* fd;
+        /* keep track of line numbers */
+        int line_nr = 0;
+        /* single line */
+        char line[10240];
+	/* trust point being read */
+	struct trust_anchor *tp = NULL, *tp2;
+	int r;
+	/* for $ORIGIN parsing */
+	ldns_rdf *origin=NULL, *prev=NULL;
+
+        if (!(fd = fopen(nm, "r"))) {
+                log_err("unable to open %s for reading: %s", 
+			nm, strerror(errno));
+                return 0;
+        }
+        verbose(VERB_ALGO, "reading autotrust anchor file %s", nm);
+        while ( (r=read_multiline(line, sizeof(line), fd, &line_nr)) != 0) {
+		if(r == -1 || (r = parse_var_line(line, anchors, &tp)) == -1) {
+			log_err("could not parse auto-trust-anchor-file "
+				"%s line %d", nm, line_nr);
+			fclose(fd);
+			ldns_rdf_deep_free(origin);
+			ldns_rdf_deep_free(prev);
+			return 0;
+		} else if(r == 1) {
+			continue;
+		} else if(r == 2) {
+			log_warn("trust anchor %s has been revoked", nm);
+			fclose(fd);
+			ldns_rdf_deep_free(origin);
+			ldns_rdf_deep_free(prev);
+			return 1;
+		}
+        	if (!str_contains_data(line, ';'))
+                	continue; /* empty lines allowed */
+ 		if(handle_origin(line, &origin))
+			continue;
+		r = 0;
+                if(!(tp2=load_trustanchor(anchors, line, nm, origin, &prev, 
+			&r))) {
+			if(!r) log_err("failed to load trust anchor from %s "
+				"at line %i, skipping", nm, line_nr);
+                        /* try to do the rest */
+			continue;
+                }
+		if(tp && tp != tp2) {
+			log_err("file %s has mismatching data inside: "
+				"the file may only contain keys for one name, "
+				"remove keys for other domain names", nm);
+        		fclose(fd);
+			ldns_rdf_deep_free(origin);
+			ldns_rdf_deep_free(prev);
+			return 0;
+		}
+		tp = tp2;
+        }
+        fclose(fd);
+	ldns_rdf_deep_free(origin);
+	ldns_rdf_deep_free(prev);
+	if(!tp) {
+		log_err("failed to read %s", nm);
+		return 0;
+	}
+
+	/* now assemble the data into DNSKEY and DS packed rrsets */
+	lock_basic_lock(&tp->lock);
+	if(!autr_assemble(tp)) {
+		lock_basic_unlock(&tp->lock);
+		log_err("malloc failure assembling %s", nm);
+		return 0;
+	}
+	lock_basic_unlock(&tp->lock);
+	return 1;
+}
+
+/** string for a trustanchor state */
+static const char*
+trustanchor_state2str(autr_state_t s)
+{
+        switch (s) {
+                case AUTR_STATE_START:       return "  START  ";
+                case AUTR_STATE_ADDPEND:     return " ADDPEND ";
+                case AUTR_STATE_VALID:       return "  VALID  ";
+                case AUTR_STATE_MISSING:     return " MISSING ";
+                case AUTR_STATE_REVOKED:     return " REVOKED ";
+                case AUTR_STATE_REMOVED:     return " REMOVED ";
+        }
+        return " UNKNOWN ";
+}
+
+/** print ID to file */
+static int
+print_id(FILE* out, char* fname, struct module_env* env, 
+	uint8_t* nm, size_t nmlen, uint16_t dclass)
+{
+	ldns_rdf rdf;
+#ifdef UNBOUND_DEBUG
+	ldns_status s;
+#endif
+
+	memset(&rdf, 0, sizeof(rdf));
+	ldns_rdf_set_data(&rdf, nm);
+	ldns_rdf_set_size(&rdf, nmlen);
+	ldns_rdf_set_type(&rdf, LDNS_RDF_TYPE_DNAME);
+
+	ldns_buffer_clear(env->scratch_buffer);
+#ifdef UNBOUND_DEBUG
+	s =
+#endif
+	ldns_rdf2buffer_str_dname(env->scratch_buffer, &rdf);
+	log_assert(s == LDNS_STATUS_OK);
+	ldns_buffer_write_u8(env->scratch_buffer, 0);
+	ldns_buffer_flip(env->scratch_buffer);
+	if(fprintf(out, ";;id: %s %d\n", 
+		(char*)ldns_buffer_begin(env->scratch_buffer),
+		(int)dclass) < 0) {
+		log_err("could not write to %s: %s", fname, strerror(errno));
+		return 0;
+	}
+	return 1;
+}
+
+static int
+autr_write_contents(FILE* out, char* fn, struct module_env* env,
+	struct trust_anchor* tp)
+{
+	char tmi[32];
+	struct autr_ta* ta;
+	char* str;
+
+	/* write pretty header */
+	if(fprintf(out, "; autotrust trust anchor file\n") < 0) {
+		log_err("could not write to %s: %s", fn, strerror(errno));
+		return 0;
+	}
+	if(tp->autr->revoked) {
+		if(fprintf(out, ";;REVOKED\n") < 0 ||
+		   fprintf(out, "; The zone has all keys revoked, and is\n"
+			"; considered as if it has no trust anchors.\n"
+			"; the remainder of the file is the last probe.\n"
+			"; to restart the trust anchor, overwrite this file.\n"
+			"; with one containing valid DNSKEYs or DSes.\n") < 0) {
+		   log_err("could not write to %s: %s", fn, strerror(errno));
+		   return 0;
+		}
+	}
+	if(!print_id(out, fn, env, tp->name, tp->namelen, tp->dclass)) {
+		return 0;
+	}
+	if(fprintf(out, ";;last_queried: %u ;;%s", 
+		(unsigned int)tp->autr->last_queried, 
+		ctime_r(&(tp->autr->last_queried), tmi)) < 0 ||
+	   fprintf(out, ";;last_success: %u ;;%s", 
+		(unsigned int)tp->autr->last_success,
+		ctime_r(&(tp->autr->last_success), tmi)) < 0 ||
+	   fprintf(out, ";;next_probe_time: %u ;;%s", 
+		(unsigned int)tp->autr->next_probe_time,
+		ctime_r(&(tp->autr->next_probe_time), tmi)) < 0 ||
+	   fprintf(out, ";;query_failed: %d\n", (int)tp->autr->query_failed)<0
+	   || fprintf(out, ";;query_interval: %d\n", 
+	   (int)tp->autr->query_interval) < 0 ||
+	   fprintf(out, ";;retry_time: %d\n", (int)tp->autr->retry_time) < 0) {
+		log_err("could not write to %s: %s", fn, strerror(errno));
+		return 0;
+	}
+
+	/* write anchors */
+	for(ta=tp->autr->keys; ta; ta=ta->next) {
+		/* by default do not store START and REMOVED keys */
+		if(ta->s == AUTR_STATE_START)
+			continue;
+		if(ta->s == AUTR_STATE_REMOVED)
+			continue;
+		/* only store keys */
+		if(ldns_rr_get_type(ta->rr) != LDNS_RR_TYPE_DNSKEY)
+			continue;
+		str = ldns_rr2str(ta->rr);
+		if(!str || !str[0]) {
+			free(str);
+			log_err("malloc failure writing %s", fn);
+			return 0;
+		}
+		str[strlen(str)-1] = 0; /* remove newline */
+		if(fprintf(out, "%s ;;state=%d [%s] ;;count=%d "
+			";;lastchange=%u ;;%s", str, (int)ta->s, 
+			trustanchor_state2str(ta->s), (int)ta->pending_count,
+			(unsigned int)ta->last_change, 
+			ctime_r(&(ta->last_change), tmi)) < 0) {
+		   log_err("could not write to %s: %s", fn, strerror(errno));
+		   free(str);
+		   return 0;
+		}
+		free(str);
+	}
+	return 1;
+}
+
+void autr_write_file(struct module_env* env, struct trust_anchor* tp)
+{
+	FILE* out;
+	char* fname = tp->autr->file;
+	char tempf[2048];
+	log_assert(tp->autr);
+	/* unique name with pid number and thread number */
+	snprintf(tempf, sizeof(tempf), "%s.%d-%d", fname, (int)getpid(),
+		env&&env->worker?*(int*)env->worker:0);
+	verbose(VERB_ALGO, "autotrust: write to disk: %s", tempf);
+	out = fopen(tempf, "w");
+	if(!out) {
+		log_err("could not open autotrust file for writing, %s: %s",
+			tempf, strerror(errno));
+		return;
+	}
+	if(!autr_write_contents(out, tempf, env, tp)) {
+		/* failed to write contents (completely) */
+		fclose(out);
+		unlink(tempf);
+		log_err("could not completely write: %s", fname);
+		return;
+	}
+	/* success; overwrite actual file */
+	fclose(out);
+	verbose(VERB_ALGO, "autotrust: replaced %s", fname);
+	if(rename(tempf, fname) < 0) {
+		log_err("rename(%s to %s): %s", tempf, fname, strerror(errno));
+	}
+}
+
+/** 
+ * Verify if dnskey works for trust point 
+ * @param env: environment (with time) for verification
+ * @param ve: validator environment (with options) for verification.
+ * @param tp: trust point to verify with
+ * @param rrset: DNSKEY rrset to verify.
+ * @return false on failure, true if verification successful.
+ */
+static int
+verify_dnskey(struct module_env* env, struct val_env* ve,
+        struct trust_anchor* tp, struct ub_packed_rrset_key* rrset)
+{
+	char* reason = NULL;
+	uint8_t sigalg[ALGO_NEEDS_MAX+1];
+	int downprot = 1;
+	enum sec_status sec = val_verify_DNSKEY_with_TA(env, ve, rrset,
+		tp->ds_rrset, tp->dnskey_rrset, downprot?sigalg:NULL, &reason);
+	/* sigalg is ignored, it returns algorithms signalled to exist, but
+	 * in 5011 there are no other rrsets to check.  if downprot is
+	 * enabled, then it checks that the DNSKEY is signed with all
+	 * algorithms available in the trust store. */
+	verbose(VERB_ALGO, "autotrust: validate DNSKEY with anchor: %s",
+		sec_status_to_string(sec));
+	return sec == sec_status_secure;
+}
+
+/** Find minimum expiration interval from signatures */
+static uint32_t
+min_expiry(struct module_env* env, ldns_rr_list* rrset)
+{
+	size_t i;
+	uint32_t t, r = 15 * 24 * 3600; /* 15 days max */
+	for(i=0; i<ldns_rr_list_rr_count(rrset); i++) {
+		ldns_rr* rr = ldns_rr_list_rr(rrset, i);
+		if(ldns_rr_get_type(rr) != LDNS_RR_TYPE_RRSIG)
+			continue;
+		t = ldns_rdf2native_int32(ldns_rr_rrsig_expiration(rr));
+		if(t - *env->now > 0) {
+			t -= *env->now;
+			if(t < r)
+				r = t;
+		}
+	}
+	return r;
+}
+
+/** Is rr self-signed revoked key */
+static int
+rr_is_selfsigned_revoked(struct module_env* env, struct val_env* ve,
+	struct ub_packed_rrset_key* dnskey_rrset, size_t i)
+{
+	enum sec_status sec;
+	char* reason = NULL;
+	verbose(VERB_ALGO, "seen REVOKE flag, check self-signed, rr %d",
+		(int)i);
+	/* no algorithm downgrade protection necessary, if it is selfsigned
+	 * revoked it can be removed. */
+	sec = dnskey_verify_rrset(env, ve, dnskey_rrset, dnskey_rrset, i, 
+		&reason);
+	return (sec == sec_status_secure);
+}
+
+/** Set fetched value */
+static void
+seen_trustanchor(struct autr_ta* ta, uint8_t seen)
+{
+	ta->fetched = seen;
+	if(ta->pending_count < 250) /* no numerical overflow, please */
+		ta->pending_count++;
+}
+
+/** set revoked value */
+static void
+seen_revoked_trustanchor(struct autr_ta* ta, uint8_t revoked)
+{
+	ta->revoked = revoked;
+}
+
+/** revoke a trust anchor */
+static void
+revoke_dnskey(struct autr_ta* ta, int off)
+{
+        ldns_rdf* rdf;
+        uint16_t flags;
+	log_assert(ta && ta->rr);
+	if(ldns_rr_get_type(ta->rr) != LDNS_RR_TYPE_DNSKEY)
+		return;
+	rdf = ldns_rr_dnskey_flags(ta->rr);
+	flags = ldns_read_uint16(ldns_rdf_data(rdf));
+
+	if (off && (flags&LDNS_KEY_REVOKE_KEY))
+		flags ^= LDNS_KEY_REVOKE_KEY; /* flip */
+	else
+		flags |= LDNS_KEY_REVOKE_KEY;
+	ldns_write_uint16(ldns_rdf_data(rdf), flags);
+}
+
+/** Compare two RR buffers skipping the REVOKED bit */
+static int
+ldns_rr_compare_wire_skip_revbit(ldns_buffer* rr1_buf, ldns_buffer* rr2_buf)
+{
+	size_t rr1_len, rr2_len, min_len, i, offset;
+	rr1_len = ldns_buffer_capacity(rr1_buf);
+	rr2_len = ldns_buffer_capacity(rr2_buf);
+	/* jump past dname (checked in earlier part) and especially past TTL */
+	offset = 0;
+	while (offset < rr1_len && *ldns_buffer_at(rr1_buf, offset) != 0)
+		offset += *ldns_buffer_at(rr1_buf, offset) + 1;
+	/* jump to rdata section (PAST the rdata length field) */
+	offset += 11; /* 0-dname-end + type + class + ttl + rdatalen */
+	min_len = (rr1_len < rr2_len) ? rr1_len : rr2_len;
+	/* compare RRs RDATA byte for byte. */
+	for(i = offset; i < min_len; i++)
+	{
+		uint8_t *rdf1, *rdf2;
+		rdf1 = ldns_buffer_at(rr1_buf, i);
+		rdf2 = ldns_buffer_at(rr2_buf, i);
+		if (i==(offset+1))
+		{
+			/* this is the second part of the flags field */
+			*rdf1 = *rdf1 | LDNS_KEY_REVOKE_KEY;
+			*rdf2 = *rdf2 | LDNS_KEY_REVOKE_KEY;
+		}
+		if (*rdf1 < *rdf2)	return -1;
+		else if (*rdf1 > *rdf2)	return 1;
+        }
+	return 0;
+}
+
+/** Compare two RRs skipping the REVOKED bit */
+static int
+ldns_rr_compare_skip_revbit(const ldns_rr* rr1, const ldns_rr* rr2, int* result)
+{
+	size_t rr1_len, rr2_len;
+	ldns_buffer* rr1_buf;
+	ldns_buffer* rr2_buf;
+
+	*result = ldns_rr_compare_no_rdata(rr1, rr2);
+	if (*result == 0)
+	{
+		rr1_len = ldns_rr_uncompressed_size(rr1);
+		rr2_len = ldns_rr_uncompressed_size(rr2);
+		rr1_buf = ldns_buffer_new(rr1_len);
+		rr2_buf = ldns_buffer_new(rr2_len);
+		if(!rr1_buf || !rr2_buf) {
+			ldns_buffer_free(rr1_buf);
+			ldns_buffer_free(rr2_buf);
+			return 0;
+		}
+		if (ldns_rr2buffer_wire_canonical(rr1_buf, rr1,
+			LDNS_SECTION_ANY) != LDNS_STATUS_OK)
+		{
+			ldns_buffer_free(rr1_buf);
+			ldns_buffer_free(rr2_buf);
+			return 0;
+		}
+		if (ldns_rr2buffer_wire_canonical(rr2_buf, rr2,
+			LDNS_SECTION_ANY) != LDNS_STATUS_OK) {
+			ldns_buffer_free(rr1_buf);
+			ldns_buffer_free(rr2_buf);
+			return 0;
+		}
+		*result = ldns_rr_compare_wire_skip_revbit(rr1_buf, rr2_buf);
+		ldns_buffer_free(rr1_buf);
+		ldns_buffer_free(rr2_buf);
+	}
+	return 1;
+}
+
+
+/** compare two trust anchors */
+static int
+ta_compare(ldns_rr* a, ldns_rr* b, int* result)
+{
+	if (!a && !b)	*result = 0;
+	else if (!a)	*result = -1;
+	else if (!b)	*result = 1;
+	else if (ldns_rr_get_type(a) != ldns_rr_get_type(b))
+		*result = (int)ldns_rr_get_type(a) - (int)ldns_rr_get_type(b);
+	else if (ldns_rr_get_type(a) == LDNS_RR_TYPE_DNSKEY) {
+		if(!ldns_rr_compare_skip_revbit(a, b, result))
+			return 0;
+	}
+	else if (ldns_rr_get_type(a) == LDNS_RR_TYPE_DS)
+		*result = ldns_rr_compare(a, b);
+	else    *result = -1;
+	return 1;
+}
+
+/** 
+ * Find key
+ * @param tp: to search in
+ * @param rr: to look for
+ * @param result: returns NULL or the ta key looked for.
+ * @return false on malloc failure during search. if true examine result.
+ */
+static int
+find_key(struct trust_anchor* tp, ldns_rr* rr, struct autr_ta** result)
+{
+	struct autr_ta* ta;
+	int ret;
+	if(!tp || !rr)
+		return 0;
+	for(ta=tp->autr->keys; ta; ta=ta->next) {
+		if(!ta_compare(ta->rr, rr, &ret))
+			return 0;
+		if(ret == 0) {
+			*result = ta;
+			return 1;
+		}
+	}
+	*result = NULL;
+	return 1;
+}
+
+/** add key and clone RR and tp already locked */
+static struct autr_ta*
+add_key(struct trust_anchor* tp, ldns_rr* rr)
+{
+	ldns_rr* c;
+	struct autr_ta* ta;
+	c = ldns_rr_clone(rr);
+	if(!c) return NULL;
+	ta = autr_ta_create(c);
+	if(!ta) {
+		ldns_rr_free(c);
+		return NULL;
+	}
+	/* link in, tp already locked */
+	ta->next = tp->autr->keys;
+	tp->autr->keys = ta;
+	return ta;
+}
+
+/** get TTL from DNSKEY rrset */
+static uint32_t
+key_ttl(struct ub_packed_rrset_key* k)
+{
+	struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data;
+	return d->ttl;
+}
+
+/** update the time values for the trustpoint */
+static void
+set_tp_times(struct trust_anchor* tp, uint32_t rrsig_exp_interval, 
+	uint32_t origttl, int* changed)
+{
+	uint32_t x, qi = tp->autr->query_interval, rt = tp->autr->retry_time;
+	
+	/* x = MIN(15days, ttl/2, expire/2) */
+	x = 15 * 24 * 3600;
+	if(origttl/2 < x)
+		x = origttl/2;
+	if(rrsig_exp_interval/2 < x)
+		x = rrsig_exp_interval/2;
+	/* MAX(1hr, x) */
+	if(x < 3600)
+		tp->autr->query_interval = 3600;
+	else	tp->autr->query_interval = x;
+
+	/* x= MIN(1day, ttl/10, expire/10) */
+	x = 24 * 3600;
+	if(origttl/10 < x)
+		x = origttl/10;
+	if(rrsig_exp_interval/10 < x)
+		x = rrsig_exp_interval/10;
+	/* MAX(1hr, x) */
+	if(x < 3600)
+		tp->autr->retry_time = 3600;
+	else	tp->autr->retry_time = x;
+
+	if(qi != tp->autr->query_interval || rt != tp->autr->retry_time) {
+		*changed = 1;
+		verbose(VERB_ALGO, "orig_ttl is %d", (int)origttl);
+		verbose(VERB_ALGO, "rrsig_exp_interval is %d", 
+			(int)rrsig_exp_interval);
+		verbose(VERB_ALGO, "query_interval: %d, retry_time: %d",
+			(int)tp->autr->query_interval, 
+			(int)tp->autr->retry_time);
+	}
+}
+
+/** init events to zero */
+static void
+init_events(struct trust_anchor* tp)
+{
+	struct autr_ta* ta;
+	for(ta=tp->autr->keys; ta; ta=ta->next) {
+		ta->fetched = 0;
+	}
+}
+
+/** check for revoked keys without trusting any other information */
+static void
+check_contains_revoked(struct module_env* env, struct val_env* ve,
+	struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset,
+	int* changed)
+{
+	ldns_rr_list* r = packed_rrset_to_rr_list(dnskey_rrset, 
+		env->scratch_buffer);
+	size_t i;
+	if(!r) {
+		log_err("malloc failure");
+		return;
+	}
+	for(i=0; i<ldns_rr_list_rr_count(r); i++) {
+		ldns_rr* rr = ldns_rr_list_rr(r, i);
+		struct autr_ta* ta = NULL;
+		if(ldns_rr_get_type(rr) != LDNS_RR_TYPE_DNSKEY)
+			continue;
+		if(!rr_is_dnskey_sep(rr) || !rr_is_dnskey_revoked(rr))
+			continue; /* not a revoked KSK */
+		if(!find_key(tp, rr, &ta)) {
+			log_err("malloc failure");
+			continue; /* malloc fail in compare*/
+		}
+		if(!ta)
+			continue; /* key not found */
+		if(rr_is_selfsigned_revoked(env, ve, dnskey_rrset, i)) {
+			/* checked if there is an rrsig signed by this key. */
+			log_assert(dnskey_calc_keytag(dnskey_rrset, i) ==
+				ldns_calc_keytag(rr)); /* checks conversion*/
+			verbose_key(ta, VERB_ALGO, "is self-signed revoked");
+			if(!ta->revoked) 
+				*changed = 1;
+			seen_revoked_trustanchor(ta, 1);
+			do_revoked(env, ta, changed);
+		}
+	}
+	ldns_rr_list_deep_free(r);
+}
+
+/** See if a DNSKEY is verified by one of the DSes */
+static int
+key_matches_a_ds(struct module_env* env, struct val_env* ve,
+	struct ub_packed_rrset_key* dnskey_rrset, size_t key_idx,
+	struct ub_packed_rrset_key* ds_rrset)
+{
+	struct packed_rrset_data* dd = (struct packed_rrset_data*)
+	                ds_rrset->entry.data;
+	size_t ds_idx, num = dd->count;
+	int d = val_favorite_ds_algo(ds_rrset);
+	char* reason = "";
+	for(ds_idx=0; ds_idx<num; ds_idx++) {
+		if(!ds_digest_algo_is_supported(ds_rrset, ds_idx) ||
+			!ds_key_algo_is_supported(ds_rrset, ds_idx) ||
+			ds_get_digest_algo(ds_rrset, ds_idx) != d)
+			continue;
+		if(ds_get_key_algo(ds_rrset, ds_idx)
+		   != dnskey_get_algo(dnskey_rrset, key_idx)
+		   || dnskey_calc_keytag(dnskey_rrset, key_idx)
+		   != ds_get_keytag(ds_rrset, ds_idx)) {
+			continue;
+		}
+		if(!ds_digest_match_dnskey(env, dnskey_rrset, key_idx,
+			ds_rrset, ds_idx)) {
+			verbose(VERB_ALGO, "DS match attempt failed");
+			continue;
+		}
+		if(dnskey_verify_rrset(env, ve, dnskey_rrset, 
+			dnskey_rrset, key_idx, &reason) == sec_status_secure) {
+			return 1;
+		} else {
+			verbose(VERB_ALGO, "DS match failed because the key "
+				"does not verify the keyset: %s", reason);
+		}
+	}
+	return 0;
+}
+
+/** Set update events */
+static int
+update_events(struct module_env* env, struct val_env* ve, 
+	struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset, 
+	int* changed)
+{
+	ldns_rr_list* r = packed_rrset_to_rr_list(dnskey_rrset, 
+		env->scratch_buffer);
+	size_t i;
+	if(!r) 
+		return 0;
+	init_events(tp);
+	for(i=0; i<ldns_rr_list_rr_count(r); i++) {
+		ldns_rr* rr = ldns_rr_list_rr(r, i);
+		struct autr_ta* ta = NULL;
+		if(ldns_rr_get_type(rr) != LDNS_RR_TYPE_DNSKEY)
+			continue;
+		if(!rr_is_dnskey_sep(rr))
+			continue;
+		if(rr_is_dnskey_revoked(rr)) {
+			/* self-signed revoked keys already detected before,
+			 * other revoked keys are not 'added' again */
+			continue;
+		}
+		/* is a key of this type supported?. Note rr_list and
+		 * packed_rrset are in the same order. */
+		if(!dnskey_algo_is_supported(dnskey_rrset, i)) {
+			/* skip unknown algorithm key, it is useless to us */
+			log_nametypeclass(VERB_DETAIL, "trust point has "
+				"unsupported algorithm at", 
+				tp->name, LDNS_RR_TYPE_DNSKEY, tp->dclass);
+			continue;
+		}
+
+		/* is it new? if revocation bit set, find the unrevoked key */
+		if(!find_key(tp, rr, &ta)) {
+			ldns_rr_list_deep_free(r); /* malloc fail in compare*/
+			return 0;
+		}
+		if(!ta) {
+			ta = add_key(tp, rr);
+			*changed = 1;
+			/* first time seen, do we have DSes? if match: VALID */
+			if(ta && tp->ds_rrset && key_matches_a_ds(env, ve,
+				dnskey_rrset, i, tp->ds_rrset)) {
+				verbose_key(ta, VERB_ALGO, "verified by DS");
+				ta->s = AUTR_STATE_VALID;
+			}
+		}
+		if(!ta) {
+			ldns_rr_list_deep_free(r);
+			return 0;
+		}
+		seen_trustanchor(ta, 1);
+		verbose_key(ta, VERB_ALGO, "in DNS response");
+	}
+	set_tp_times(tp, min_expiry(env, r), key_ttl(dnskey_rrset), changed);
+	ldns_rr_list_deep_free(r);
+	return 1;
+}
+
+/**
+ * Check if the holddown time has already exceeded
+ * setting: add-holddown: add holddown timer
+ * setting: del-holddown: del holddown timer
+ * @param env: environment with current time
+ * @param ta: trust anchor to check for.
+ * @param holddown: the timer value
+ * @return number of seconds the holddown has passed.
+ */
+static int
+check_holddown(struct module_env* env, struct autr_ta* ta, 
+	unsigned int holddown)
+{
+        unsigned int elapsed;
+	if((unsigned)*env->now < (unsigned)ta->last_change) {
+		log_warn("time goes backwards. delaying key holddown");
+		return 0;
+	}
+	elapsed = (unsigned)*env->now - (unsigned)ta->last_change;
+        if (elapsed > holddown) {
+                return (int) (elapsed-holddown);
+        }
+	verbose_key(ta, VERB_ALGO, "holddown time %d seconds to go",
+		(int) (holddown-elapsed));
+        return 0;
+}
+
+
+/** Set last_change to now */
+static void
+reset_holddown(struct module_env* env, struct autr_ta* ta, int* changed)
+{
+	ta->last_change = *env->now;
+	*changed = 1;
+}
+
+/** Set the state for this trust anchor */
+static void
+set_trustanchor_state(struct module_env* env, struct autr_ta* ta, int* changed,
+	autr_state_t s)
+{
+	verbose_key(ta, VERB_ALGO, "update: %s to %s",
+		trustanchor_state2str(ta->s), trustanchor_state2str(s));
+	ta->s = s;
+	reset_holddown(env, ta, changed);
+}
+
+
+/** Event: NewKey */
+static void
+do_newkey(struct module_env* env, struct autr_ta* anchor, int* c)
+{
+	if (anchor->s == AUTR_STATE_START)
+		set_trustanchor_state(env, anchor, c, AUTR_STATE_ADDPEND);
+}
+
+/** Event: AddTime */
+static void
+do_addtime(struct module_env* env, struct autr_ta* anchor, int* c)
+{
+	/* This not according to RFC, this is 30 days, but the RFC demands 
+	 * MAX(30days, TTL expire time of first DNSKEY set with this key),
+	 * The value may be too small if a very large TTL was used. */
+	int exceeded = check_holddown(env, anchor, env->cfg->add_holddown);
+	if (exceeded && anchor->s == AUTR_STATE_ADDPEND) {
+		verbose_key(anchor, VERB_ALGO, "add-holddown time exceeded "
+			"%d seconds ago, and pending-count %d", exceeded,
+			anchor->pending_count);
+		if(anchor->pending_count >= MIN_PENDINGCOUNT) {
+			set_trustanchor_state(env, anchor, c, AUTR_STATE_VALID);
+			anchor->pending_count = 0;
+			return;
+		}
+		verbose_key(anchor, VERB_ALGO, "add-holddown time sanity check "
+			"failed (pending count: %d)", anchor->pending_count);
+	}
+}
+
+/** Event: RemTime */
+static void
+do_remtime(struct module_env* env, struct autr_ta* anchor, int* c)
+{
+	int exceeded = check_holddown(env, anchor, env->cfg->del_holddown);
+	if(exceeded && anchor->s == AUTR_STATE_REVOKED) {
+		verbose_key(anchor, VERB_ALGO, "del-holddown time exceeded "
+			"%d seconds ago", exceeded);
+		set_trustanchor_state(env, anchor, c, AUTR_STATE_REMOVED);
+	}
+}
+
+/** Event: KeyRem */
+static void
+do_keyrem(struct module_env* env, struct autr_ta* anchor, int* c)
+{
+	if(anchor->s == AUTR_STATE_ADDPEND) {
+		set_trustanchor_state(env, anchor, c, AUTR_STATE_START);
+		anchor->pending_count = 0;
+	} else if(anchor->s == AUTR_STATE_VALID)
+		set_trustanchor_state(env, anchor, c, AUTR_STATE_MISSING);
+}
+
+/** Event: KeyPres */
+static void
+do_keypres(struct module_env* env, struct autr_ta* anchor, int* c)
+{
+	if(anchor->s == AUTR_STATE_MISSING)
+		set_trustanchor_state(env, anchor, c, AUTR_STATE_VALID);
+}
+
+/* Event: Revoked */
+static void
+do_revoked(struct module_env* env, struct autr_ta* anchor, int* c)
+{
+	if(anchor->s == AUTR_STATE_VALID || anchor->s == AUTR_STATE_MISSING) {
+                set_trustanchor_state(env, anchor, c, AUTR_STATE_REVOKED);
+		verbose_key(anchor, VERB_ALGO, "old id, prior to revocation");
+                revoke_dnskey(anchor, 0);
+		verbose_key(anchor, VERB_ALGO, "new id, after revocation");
+	}
+}
+
+/** Do statestable transition matrix for anchor */
+static void
+anchor_state_update(struct module_env* env, struct autr_ta* anchor, int* c)
+{
+	log_assert(anchor);
+	switch(anchor->s) {
+	/* START */
+	case AUTR_STATE_START:
+		/* NewKey: ADDPEND */
+		if (anchor->fetched)
+			do_newkey(env, anchor, c);
+		break;
+	/* ADDPEND */
+	case AUTR_STATE_ADDPEND:
+		/* KeyRem: START */
+		if (!anchor->fetched)
+			do_keyrem(env, anchor, c);
+		/* AddTime: VALID */
+		else	do_addtime(env, anchor, c);
+		break;
+	/* VALID */
+	case AUTR_STATE_VALID:
+		/* RevBit: REVOKED */
+		if (anchor->revoked)
+			do_revoked(env, anchor, c);
+		/* KeyRem: MISSING */
+		else if (!anchor->fetched)
+			do_keyrem(env, anchor, c);
+		else if(!anchor->last_change) {
+			verbose_key(anchor, VERB_ALGO, "first seen");
+			reset_holddown(env, anchor, c);
+		}
+		break;
+	/* MISSING */
+	case AUTR_STATE_MISSING:
+		/* RevBit: REVOKED */
+		if (anchor->revoked)
+			do_revoked(env, anchor, c);
+		/* KeyPres */
+		else if (anchor->fetched)
+			do_keypres(env, anchor, c);
+		break;
+	/* REVOKED */
+	case AUTR_STATE_REVOKED:
+		if (anchor->fetched)
+			reset_holddown(env, anchor, c);
+		/* RemTime: REMOVED */
+		else	do_remtime(env, anchor, c);
+		break;
+	/* REMOVED */
+	case AUTR_STATE_REMOVED:
+	default:
+		break;
+	}
+}
+
+/** if ZSK init then trust KSKs */
+static int
+init_zsk_to_ksk(struct module_env* env, struct trust_anchor* tp, int* changed)
+{
+	/* search for VALID ZSKs */
+	struct autr_ta* anchor;
+	int validzsk = 0;
+	int validksk = 0;
+	for(anchor = tp->autr->keys; anchor; anchor = anchor->next) {
+		/* last_change test makes sure it was manually configured */
+                if (ldns_rr_get_type(anchor->rr) == LDNS_RR_TYPE_DNSKEY &&
+			anchor->last_change == 0 && 
+			!rr_is_dnskey_sep(anchor->rr) &&
+			anchor->s == AUTR_STATE_VALID)
+                        validzsk++;
+	}
+	if(validzsk == 0)
+		return 0;
+	for(anchor = tp->autr->keys; anchor; anchor = anchor->next) {
+                if (rr_is_dnskey_sep(anchor->rr) && 
+			anchor->s == AUTR_STATE_ADDPEND) {
+			verbose_key(anchor, VERB_ALGO, "trust KSK from "
+				"ZSK(config)");
+			set_trustanchor_state(env, anchor, changed, 
+				AUTR_STATE_VALID);
+			validksk++;
+		}
+	}
+	return validksk;
+}
+
+/** Remove missing trustanchors so the list does not grow forever */
+static void
+remove_missing_trustanchors(struct module_env* env, struct trust_anchor* tp,
+	int* changed)
+{
+	struct autr_ta* anchor;
+	int exceeded;
+	int valid = 0;
+	/* see if we have anchors that are valid */
+	for(anchor = tp->autr->keys; anchor; anchor = anchor->next) {
+		/* Only do KSKs */
+                if (!rr_is_dnskey_sep(anchor->rr))
+                        continue;
+                if (anchor->s == AUTR_STATE_VALID)
+                        valid++;
+	}
+	/* if there are no SEP Valid anchors, see if we started out with
+	 * a ZSK (last-change=0) anchor, which is VALID and there are KSKs
+	 * now that can be made valid.  Do this immediately because there
+	 * is no guarantee that the ZSKs get announced long enough.  Usually
+	 * this is immediately after init with a ZSK trusted, unless the domain
+	 * was not advertising any KSKs at all.  In which case we perfectly
+	 * track the zero number of KSKs. */
+	if(valid == 0) {
+		valid = init_zsk_to_ksk(env, tp, changed);
+		if(valid == 0)
+			return;
+	}
+	
+	for(anchor = tp->autr->keys; anchor; anchor = anchor->next) {
+		/* ignore ZSKs if newly added */
+		if(anchor->s == AUTR_STATE_START)
+			continue;
+		/* remove ZSKs if a KSK is present */
+                if (!rr_is_dnskey_sep(anchor->rr)) {
+			if(valid > 0) {
+				verbose_key(anchor, VERB_ALGO, "remove ZSK "
+					"[%d key(s) VALID]", valid);
+				set_trustanchor_state(env, anchor, changed, 
+					AUTR_STATE_REMOVED);
+			}
+                        continue;
+		}
+                /* Only do MISSING keys */
+                if (anchor->s != AUTR_STATE_MISSING)
+                        continue;
+		if(env->cfg->keep_missing == 0)
+			continue; /* keep forever */
+
+		exceeded = check_holddown(env, anchor, env->cfg->keep_missing);
+		/* If keep_missing has exceeded and we still have more than 
+		 * one valid KSK: remove missing trust anchor */
+                if (exceeded && valid > 0) {
+			verbose_key(anchor, VERB_ALGO, "keep-missing time "
+				"exceeded %d seconds ago, [%d key(s) VALID]",
+				exceeded, valid);
+			set_trustanchor_state(env, anchor, changed, 
+				AUTR_STATE_REMOVED);
+		}
+	}
+}
+
+/** Do the statetable from RFC5011 transition matrix */
+static int
+do_statetable(struct module_env* env, struct trust_anchor* tp, int* changed)
+{
+	struct autr_ta* anchor;
+	for(anchor = tp->autr->keys; anchor; anchor = anchor->next) {
+		/* Only do KSKs */
+		if(!rr_is_dnskey_sep(anchor->rr))
+			continue;
+		anchor_state_update(env, anchor, changed);
+	}
+	remove_missing_trustanchors(env, tp, changed);
+	return 1;
+}
+
+/** See if time alone makes ADDPEND to VALID transition */
+static void
+autr_holddown_exceed(struct module_env* env, struct trust_anchor* tp, int* c)
+{
+	struct autr_ta* anchor;
+	for(anchor = tp->autr->keys; anchor; anchor = anchor->next) {
+		if(rr_is_dnskey_sep(anchor->rr) && 
+			anchor->s == AUTR_STATE_ADDPEND)
+			do_addtime(env, anchor, c);
+	}
+}
+
+/** cleanup key list */
+static void
+autr_cleanup_keys(struct trust_anchor* tp)
+{
+	struct autr_ta* p, **prevp;
+	prevp = &tp->autr->keys;
+	p = tp->autr->keys;
+	while(p) {
+		/* do we want to remove this key? */
+		if(p->s == AUTR_STATE_START || p->s == AUTR_STATE_REMOVED ||
+			ldns_rr_get_type(p->rr) != LDNS_RR_TYPE_DNSKEY) {
+			struct autr_ta* np = p->next;
+			/* remove */
+			ldns_rr_free(p->rr);
+			free(p);
+			/* snip and go to next item */
+			*prevp = np;
+			p = np;
+			continue;
+		}
+		/* remove pending counts if no longer pending */
+		if(p->s != AUTR_STATE_ADDPEND)
+			p->pending_count = 0;
+		prevp = &p->next;
+		p = p->next;
+	}
+}
+
+/** calculate next probe time */
+static time_t
+calc_next_probe(struct module_env* env, uint32_t wait)
+{
+	/* make it random, 90-100% */
+	uint32_t rnd, rest;
+	if(wait < 3600)
+		wait = 3600;
+	rnd = wait/10;
+	rest = wait-rnd;
+	rnd = (uint32_t)ub_random_max(env->rnd, (long int)rnd);
+	return (time_t)(*env->now + rest + rnd);
+}
+
+/** what is first probe time (anchors must be locked) */
+static time_t
+wait_probe_time(struct val_anchors* anchors)
+{
+	rbnode_t* t = rbtree_first(&anchors->autr->probe);
+	if(t != RBTREE_NULL) 
+		return ((struct trust_anchor*)t->key)->autr->next_probe_time;
+	return 0;
+}
+
+/** reset worker timer */
+static void
+reset_worker_timer(struct module_env* env)
+{
+	struct timeval tv;
+#ifndef S_SPLINT_S
+	uint32_t next = (uint32_t)wait_probe_time(env->anchors);
+	/* in case this is libunbound, no timer */
+	if(!env->probe_timer)
+		return;
+	if(next > *env->now)
+		tv.tv_sec = (time_t)(next - *env->now);
+	else	tv.tv_sec = 0;
+#endif
+	tv.tv_usec = 0;
+	comm_timer_set(env->probe_timer, &tv);
+	verbose(VERB_ALGO, "scheduled next probe in %d sec", (int)tv.tv_sec);
+}
+
+/** set next probe for trust anchor */
+static int
+set_next_probe(struct module_env* env, struct trust_anchor* tp,
+	struct ub_packed_rrset_key* dnskey_rrset)
+{
+	struct trust_anchor key, *tp2;
+	time_t mold, mnew;
+	/* use memory allocated in rrset for temporary name storage */
+	key.node.key = &key;
+	key.name = dnskey_rrset->rk.dname;
+	key.namelen = dnskey_rrset->rk.dname_len;
+	key.namelabs = dname_count_labels(key.name);
+	key.dclass = tp->dclass;
+	lock_basic_unlock(&tp->lock);
+
+	/* fetch tp again and lock anchors, so that we can modify the trees */
+	lock_basic_lock(&env->anchors->lock);
+	tp2 = (struct trust_anchor*)rbtree_search(env->anchors->tree, &key);
+	if(!tp2) {
+		verbose(VERB_ALGO, "trustpoint was deleted in set_next_probe");
+		lock_basic_unlock(&env->anchors->lock);
+		return 0;
+	}
+	log_assert(tp == tp2);
+	lock_basic_lock(&tp->lock);
+
+	/* schedule */
+	mold = wait_probe_time(env->anchors);
+	(void)rbtree_delete(&env->anchors->autr->probe, tp);
+	tp->autr->next_probe_time = calc_next_probe(env, 
+		tp->autr->query_interval);
+	(void)rbtree_insert(&env->anchors->autr->probe, &tp->autr->pnode);
+	mnew = wait_probe_time(env->anchors);
+
+	lock_basic_unlock(&env->anchors->lock);
+	verbose(VERB_ALGO, "next probe set in %d seconds", 
+		(int)tp->autr->next_probe_time - (int)*env->now);
+	if(mold != mnew) {
+		reset_worker_timer(env);
+	}
+	return 1;
+}
+
+/** Revoke and Delete a trust point */
+static void
+autr_tp_remove(struct module_env* env, struct trust_anchor* tp,
+	struct ub_packed_rrset_key* dnskey_rrset)
+{
+	struct trust_anchor key;
+	struct autr_point_data pd;
+	time_t mold, mnew;
+
+	log_nametypeclass(VERB_OPS, "trust point was revoked",
+		tp->name, LDNS_RR_TYPE_DNSKEY, tp->dclass);
+	tp->autr->revoked = 1;
+
+	/* use space allocated for dnskey_rrset to save name of anchor */
+	memset(&key, 0, sizeof(key));
+	memset(&pd, 0, sizeof(pd));
+	key.autr = &pd;
+	key.node.key = &key;
+	pd.pnode.key = &key;
+	pd.next_probe_time = tp->autr->next_probe_time;
+	key.name = dnskey_rrset->rk.dname;
+	key.namelen = tp->namelen;
+	key.namelabs = tp->namelabs;
+	key.dclass = tp->dclass;
+
+	/* unlock */
+	lock_basic_unlock(&tp->lock);
+
+	/* take from tree. It could be deleted by someone else,hence (void). */
+	lock_basic_lock(&env->anchors->lock);
+	(void)rbtree_delete(env->anchors->tree, &key);
+	mold = wait_probe_time(env->anchors);
+	(void)rbtree_delete(&env->anchors->autr->probe, &key);
+	mnew = wait_probe_time(env->anchors);
+	anchors_init_parents_locked(env->anchors);
+	lock_basic_unlock(&env->anchors->lock);
+
+	/* save on disk */
+	tp->autr->next_probe_time = 0; /* no more probing for it */
+	autr_write_file(env, tp);
+
+	/* delete */
+	autr_point_delete(tp);
+	if(mold != mnew) {
+		reset_worker_timer(env);
+	}
+}
+
+int autr_process_prime(struct module_env* env, struct val_env* ve,
+	struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset)
+{
+	int changed = 0;
+	log_assert(tp && tp->autr);
+	/* autotrust update trust anchors */
+	/* the tp is locked, and stays locked unless it is deleted */
+
+	/* we could just catch the anchor here while another thread
+	 * is busy deleting it. Just unlock and let the other do its job */
+	if(tp->autr->revoked) {
+		log_nametypeclass(VERB_ALGO, "autotrust not processed, "
+			"trust point revoked", tp->name, 
+			LDNS_RR_TYPE_DNSKEY, tp->dclass);
+		lock_basic_unlock(&tp->lock);
+		return 0; /* it is revoked */
+	}
+
+	/* query_dnskeys(): */
+	tp->autr->last_queried = *env->now;
+
+	log_nametypeclass(VERB_ALGO, "autotrust process for",
+		tp->name, LDNS_RR_TYPE_DNSKEY, tp->dclass);
+	/* see if time alone makes some keys valid */
+	autr_holddown_exceed(env, tp, &changed);
+	if(changed) {
+		verbose(VERB_ALGO, "autotrust: morekeys, reassemble");
+		if(!autr_assemble(tp)) {
+			log_err("malloc failure assembling autotrust keys");
+			return 1; /* unchanged */
+		}
+	}
+	/* did we get any data? */
+	if(!dnskey_rrset) {
+		verbose(VERB_ALGO, "autotrust: no dnskey rrset");
+		/* no update of query_failed, because then we would have
+		 * to write to disk. But we cannot because we maybe are
+		 * still 'initialising' with DS records, that we cannot write
+		 * in the full format (which only contains KSKs). */
+		return 1; /* trust point exists */
+	}
+	/* check for revoked keys to remove immediately */
+	check_contains_revoked(env, ve, tp, dnskey_rrset, &changed);
+	if(changed) {
+		verbose(VERB_ALGO, "autotrust: revokedkeys, reassemble");
+		if(!autr_assemble(tp)) {
+			log_err("malloc failure assembling autotrust keys");
+			return 1; /* unchanged */
+		}
+		if(!tp->ds_rrset && !tp->dnskey_rrset) {
+			/* no more keys, all are revoked */
+			/* this is a success for this probe attempt */
+			tp->autr->last_success = *env->now;
+			autr_tp_remove(env, tp, dnskey_rrset);
+			return 0; /* trust point removed */
+		}
+	}
+	/* verify the dnskey rrset and see if it is valid. */
+	if(!verify_dnskey(env, ve, tp, dnskey_rrset)) {
+		verbose(VERB_ALGO, "autotrust: dnskey did not verify.");
+		/* only increase failure count if this is not the first prime,
+		 * this means there was a previous succesful probe */
+		if(tp->autr->last_success) {
+			tp->autr->query_failed += 1;
+			autr_write_file(env, tp);
+		}
+		return 1; /* trust point exists */
+	}
+
+	tp->autr->last_success = *env->now;
+	tp->autr->query_failed = 0;
+
+	/* Add new trust anchors to the data structure
+	 * - note which trust anchors are seen this probe.
+	 * Set trustpoint query_interval and retry_time.
+	 * - find minimum rrsig expiration interval
+	 */
+	if(!update_events(env, ve, tp, dnskey_rrset, &changed)) {
+		log_err("malloc failure in autotrust update_events. "
+			"trust point unchanged.");
+		return 1; /* trust point unchanged, so exists */
+	}
+
+	/* - for every SEP key do the 5011 statetable.
+	 * - remove missing trustanchors (if veryold and we have new anchors).
+	 */
+	if(!do_statetable(env, tp, &changed)) {
+		log_err("malloc failure in autotrust do_statetable. "
+			"trust point unchanged.");
+		return 1; /* trust point unchanged, so exists */
+	}
+
+	autr_cleanup_keys(tp);
+	if(!set_next_probe(env, tp, dnskey_rrset))
+		return 0; /* trust point does not exist */
+	autr_write_file(env, tp);
+	if(changed) {
+		verbose(VERB_ALGO, "autotrust: changed, reassemble");
+		if(!autr_assemble(tp)) {
+			log_err("malloc failure assembling autotrust keys");
+			return 1; /* unchanged */
+		}
+		if(!tp->ds_rrset && !tp->dnskey_rrset) {
+			/* no more keys, all are revoked */
+			autr_tp_remove(env, tp, dnskey_rrset);
+			return 0; /* trust point removed */
+		}
+	} else verbose(VERB_ALGO, "autotrust: no changes");
+	
+	return 1; /* trust point exists */
+}
+
+/** debug print a trust anchor key */
+static void 
+autr_debug_print_ta(struct autr_ta* ta)
+{
+	char buf[32];
+	char* str = ldns_rr2str(ta->rr);
+	if(!str) {
+		log_info("out of memory in debug_print_ta");
+		return;
+	}
+	if(str && str[0]) str[strlen(str)-1]=0; /* remove newline */
+	ctime_r(&ta->last_change, buf);
+	if(buf[0]) buf[strlen(buf)-1]=0; /* remove newline */
+	log_info("[%s] %s ;;state:%d ;;pending_count:%d%s%s last:%s",
+		trustanchor_state2str(ta->s), str, ta->s, ta->pending_count,
+		ta->fetched?" fetched":"", ta->revoked?" revoked":"", buf);
+	free(str);
+}
+
+/** debug print a trust point */
+static void 
+autr_debug_print_tp(struct trust_anchor* tp)
+{
+	struct autr_ta* ta;
+	char buf[257];
+	if(!tp->autr)
+		return;
+	dname_str(tp->name, buf);
+	log_info("trust point %s : %d", buf, (int)tp->dclass);
+	log_info("assembled %d DS and %d DNSKEYs", 
+		(int)tp->numDS, (int)tp->numDNSKEY);
+	if(0) { /* turned off because it prints to stderr */
+		ldns_buffer* bf = ldns_buffer_new(70000);
+		ldns_rr_list* list;
+		if(tp->ds_rrset) {
+			list = packed_rrset_to_rr_list(tp->ds_rrset, bf);
+			ldns_rr_list_print(stderr, list);
+			ldns_rr_list_deep_free(list);
+		}
+		if(tp->dnskey_rrset) {
+			list = packed_rrset_to_rr_list(tp->dnskey_rrset, bf);
+			ldns_rr_list_print(stderr, list);
+			ldns_rr_list_deep_free(list);
+		}
+		ldns_buffer_free(bf);
+	}
+	log_info("file %s", tp->autr->file);
+	ctime_r(&tp->autr->last_queried, buf);
+	if(buf[0]) buf[strlen(buf)-1]=0; /* remove newline */
+	log_info("last_queried: %u %s", (unsigned)tp->autr->last_queried, buf);
+	ctime_r(&tp->autr->last_success, buf);
+	if(buf[0]) buf[strlen(buf)-1]=0; /* remove newline */
+	log_info("last_success: %u %s", (unsigned)tp->autr->last_success, buf);
+	ctime_r(&tp->autr->next_probe_time, buf);
+	if(buf[0]) buf[strlen(buf)-1]=0; /* remove newline */
+	log_info("next_probe_time: %u %s", (unsigned)tp->autr->next_probe_time,
+		buf);
+	log_info("query_interval: %u", (unsigned)tp->autr->query_interval);
+	log_info("retry_time: %u", (unsigned)tp->autr->retry_time);
+	log_info("query_failed: %u", (unsigned)tp->autr->query_failed);
+		
+	for(ta=tp->autr->keys; ta; ta=ta->next) {
+		autr_debug_print_ta(ta);
+	}
+}
+
+void 
+autr_debug_print(struct val_anchors* anchors)
+{
+	struct trust_anchor* tp;
+	lock_basic_lock(&anchors->lock);
+	RBTREE_FOR(tp, struct trust_anchor*, anchors->tree) {
+		lock_basic_lock(&tp->lock);
+		autr_debug_print_tp(tp);
+		lock_basic_unlock(&tp->lock);
+	}
+	lock_basic_unlock(&anchors->lock);
+}
+
+void probe_answer_cb(void* arg, int ATTR_UNUSED(rcode), 
+	ldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(sec),
+	char* ATTR_UNUSED(why_bogus))
+{
+	/* retry was set before the query was done,
+	 * re-querytime is set when query succeeded, but that may not
+	 * have reset this timer because the query could have been
+	 * handled by another thread. In that case, this callback would
+	 * get called after the original timeout is done. 
+	 * By not resetting the timer, it may probe more often, but not
+	 * less often.
+	 * Unless the new lookup resulted in smaller TTLs and thus smaller
+	 * timeout values. In that case one old TTL could be mistakenly done.
+	 */
+	struct module_env* env = (struct module_env*)arg;
+	verbose(VERB_ALGO, "autotrust probe answer cb");
+	reset_worker_timer(env);
+}
+
+/** probe a trust anchor DNSKEY and unlocks tp */
+static void
+probe_anchor(struct module_env* env, struct trust_anchor* tp)
+{
+	struct query_info qinfo;
+	uint16_t qflags = BIT_RD;
+	struct edns_data edns;
+	ldns_buffer* buf = env->scratch_buffer;
+	qinfo.qname = regional_alloc_init(env->scratch, tp->name, tp->namelen);
+	if(!qinfo.qname) {
+		log_err("out of memory making 5011 probe");
+		return;
+	}
+	qinfo.qname_len = tp->namelen;
+	qinfo.qtype = LDNS_RR_TYPE_DNSKEY;
+	qinfo.qclass = tp->dclass;
+	log_query_info(VERB_ALGO, "autotrust probe", &qinfo);
+	verbose(VERB_ALGO, "retry probe set in %d seconds", 
+		(int)tp->autr->next_probe_time - (int)*env->now);
+	edns.edns_present = 1;
+	edns.ext_rcode = 0;
+	edns.edns_version = 0;
+	edns.bits = EDNS_DO;
+	if(ldns_buffer_capacity(buf) < 65535)
+		edns.udp_size = (uint16_t)ldns_buffer_capacity(buf);
+	else	edns.udp_size = 65535;
+
+	/* can't hold the lock while mesh_run is processing */
+	lock_basic_unlock(&tp->lock);
+
+	/* delete the DNSKEY from rrset and key cache so an active probe
+	 * is done. First the rrset so another thread does not use it
+	 * to recreate the key entry in a race condition. */
+	rrset_cache_remove(env->rrset_cache, qinfo.qname, qinfo.qname_len,
+		qinfo.qtype, qinfo.qclass, 0);
+	key_cache_remove(env->key_cache, qinfo.qname, qinfo.qname_len, 
+		qinfo.qclass);
+
+	if(!mesh_new_callback(env->mesh, &qinfo, qflags, &edns, buf, 0, 
+		&probe_answer_cb, env)) {
+		log_err("out of memory making 5011 probe");
+	}
+}
+
+/** fetch first to-probe trust-anchor and lock it and set retrytime */
+static struct trust_anchor*
+todo_probe(struct module_env* env, uint32_t* next)
+{
+	struct trust_anchor* tp;
+	rbnode_t* el;
+	/* get first one */
+	lock_basic_lock(&env->anchors->lock);
+	if( (el=rbtree_first(&env->anchors->autr->probe)) == RBTREE_NULL) {
+		/* in case of revoked anchors */
+		lock_basic_unlock(&env->anchors->lock);
+		return NULL;
+	}
+	tp = (struct trust_anchor*)el->key;
+	lock_basic_lock(&tp->lock);
+
+	/* is it eligible? */
+	if((uint32_t)tp->autr->next_probe_time > *env->now) {
+		/* no more to probe */
+		*next = (uint32_t)tp->autr->next_probe_time - *env->now;
+		lock_basic_unlock(&tp->lock);
+		lock_basic_unlock(&env->anchors->lock);
+		return NULL;
+	}
+
+	/* reset its next probe time */
+	(void)rbtree_delete(&env->anchors->autr->probe, tp);
+	tp->autr->next_probe_time = calc_next_probe(env, tp->autr->retry_time);
+	(void)rbtree_insert(&env->anchors->autr->probe, &tp->autr->pnode);
+	lock_basic_unlock(&env->anchors->lock);
+
+	return tp;
+}
+
+uint32_t 
+autr_probe_timer(struct module_env* env)
+{
+	struct trust_anchor* tp;
+	uint32_t next_probe = 3600;
+	int num = 0;
+	verbose(VERB_ALGO, "autotrust probe timer callback");
+	/* while there are still anchors to probe */
+	while( (tp = todo_probe(env, &next_probe)) ) {
+		/* make a probe for this anchor */
+		probe_anchor(env, tp);
+		num++;
+	}
+	regional_free_all(env->scratch);
+	if(num == 0)
+		return 0; /* no trust points to probe */
+	verbose(VERB_ALGO, "autotrust probe timer %d callbacks done", num);
+	return next_probe;
+}
diff --git a/3rdParty/Unbound/src/src/validator/autotrust.h b/3rdParty/Unbound/src/src/validator/autotrust.h
new file mode 100644
index 0000000..4e88ed3
--- /dev/null
+++ b/3rdParty/Unbound/src/src/validator/autotrust.h
@@ -0,0 +1,205 @@
+/*
+ * validator/autotrust.h - RFC5011 trust anchor management for unbound.
+ *
+ * Copyright (c) 2009, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * Contains autotrust definitions.
+ */
+
+#ifndef VALIDATOR_AUTOTRUST_H
+#define VALIDATOR_AUTOTRUST_H
+#include "util/rbtree.h"
+#include "util/data/packed_rrset.h"
+struct val_anchors;
+struct trust_anchor;
+struct ub_packed_rrset_key;
+struct module_env;
+struct val_env;
+
+/** Autotrust anchor states */
+typedef enum {
+	AUTR_STATE_START   = 0,
+	AUTR_STATE_ADDPEND = 1,
+	AUTR_STATE_VALID   = 2,
+	AUTR_STATE_MISSING = 3,
+	AUTR_STATE_REVOKED = 4,
+	AUTR_STATE_REMOVED = 5
+} autr_state_t;
+
+/** 
+ * Autotrust metadata for one trust anchor key.
+ */
+struct autr_ta {
+	/** next key */
+	struct autr_ta* next;
+	/** the RR */
+	ldns_rr* rr;
+	/** last update of key state (new pending count keeps date the same) */
+	time_t last_change;
+	/** 5011 state */
+	autr_state_t s;
+	/** pending count */
+	uint8_t pending_count;
+	/** fresh TA was seen */
+	uint8_t fetched;
+	/** revoked TA was seen */
+	uint8_t revoked;
+};
+
+/** 
+ * Autotrust metadata for a trust point.
+ * This is part of the struct trust_anchor data.
+ */
+struct autr_point_data {
+	/** file to store the trust point in. chrootdir already applied. */
+	char* file;
+	/** rbtree node for probe sort, key is struct trust_anchor */
+	rbnode_t pnode;
+
+	/** the keys */
+	struct autr_ta* keys;
+
+	/** last queried DNSKEY set 
+	 * Not all failures are captured in this entry.
+	 * If the validator did not even start (e.g. timeout or localservfail),
+	 * then the last_queried and query_failed values are not updated.
+	 */
+	time_t last_queried;
+	/** last successful DNSKEY set */
+	time_t last_success;
+	/** next probe time */
+	time_t next_probe_time;
+
+	/** when to query if !failed */
+	uint32_t query_interval;
+	/** when to retry if failed */
+	uint32_t retry_time;
+
+	/** 
+	 * How many times did it fail. diagnostic only (has no effect).
+	 * Only updated if there was a dnskey rrset that failed to verify.
+	 */
+	uint8_t query_failed;
+	/** true if the trust point has been revoked */
+	uint8_t revoked;
+};
+
+/** 
+ * Autotrust global metadata.
+ */
+struct autr_global_data {
+	/** rbtree of autotrust anchors sorted by next probe time.
+	 * When time is equal, sorted by anchor class, name. */
+	rbtree_t probe;
+};
+
+/**
+ * Create new global 5011 data structure.
+ * @return new structure or NULL on malloc failure.
+ */
+struct autr_global_data* autr_global_create(void);
+
+/**
+ * Delete global 5011 data structure.
+ * @param global: global autotrust state to delete.
+ */
+void autr_global_delete(struct autr_global_data* global);
+
+/**
+ * See if autotrust anchors are configured and how many.
+ * @param anchors: the trust anchors structure.
+ * @return number of autotrust trust anchors
+ */
+size_t autr_get_num_anchors(struct val_anchors* anchors);
+
+/**
+ * Process probe timer.  Add new probes if needed.
+ * @param env: module environment with time, with anchors and with the mesh.
+ * @return time of next probe (in seconds from now).
+ * 	If 0, then there is no next probe anymore (trust points deleted).
+ */
+uint32_t autr_probe_timer(struct module_env* env);
+
+/** probe tree compare function */
+int probetree_cmp(const void* x, const void* y);
+
+/**
+ * Read autotrust file.
+ * @param anchors: the anchors structure.
+ * @param nm: name of the file (copied).
+ * @return false on failure.
+ */
+int autr_read_file(struct val_anchors* anchors, const char* nm);
+
+/**
+ * Write autotrust file.
+ * @param env: environment with scratch space.
+ * @param tp: trust point to write.
+ */
+void autr_write_file(struct module_env* env, struct trust_anchor* tp);
+
+/**
+ * Delete autr anchor, deletes the autr data but does not do
+ * unlinking from trees, caller does that.
+ * @param tp: trust point to delete.
+ */
+void autr_point_delete(struct trust_anchor* tp);
+
+/**
+ * Perform autotrust processing.
+ * @param env: qstate environment with the anchors structure.
+ * @param ve: validator environment for verification of rrsigs.
+ * @param tp: trust anchor to process.
+ * @param dnskey_rrset: DNSKEY rrset probed (can be NULL if bad prime result).
+ * 	allocated in a region. Has not been validated yet.
+ * @return false if trust anchor was revoked completely.
+ * 	Otherwise logs errors to log, does not change return value.
+ * 	On errors, likely the trust point has been unchanged.
+ */
+int autr_process_prime(struct module_env* env, struct val_env* ve,
+	struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset);
+
+/**
+ * Debug printout of rfc5011 tracked anchors
+ * @param anchors: all the anchors.
+ */
+void autr_debug_print(struct val_anchors* anchors);
+
+/** callback for query answer to 5011 probe */
+void probe_answer_cb(void* arg, int rcode, ldns_buffer* buf, 
+	enum sec_status sec, char* errinf);
+
+#endif /* VALIDATOR_AUTOTRUST_H */
diff --git a/3rdParty/Unbound/src/src/validator/val_anchor.c b/3rdParty/Unbound/src/src/validator/val_anchor.c
new file mode 100644
index 0000000..72338b0
--- /dev/null
+++ b/3rdParty/Unbound/src/src/validator/val_anchor.c
@@ -0,0 +1,1150 @@
+/*
+ * validator/val_anchor.c - validator trust anchor storage.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains storage for the trust anchors for the validator.
+ */
+#include "config.h"
+#include <ctype.h>
+#include <ldns/dname.h>
+#include <ldns/host2wire.h>
+#include "validator/val_anchor.h"
+#include "validator/val_sigcrypt.h"
+#include "validator/autotrust.h"
+#include "util/data/packed_rrset.h"
+#include "util/data/dname.h"
+#include "util/log.h"
+#include "util/net_help.h"
+#include "util/regional.h"
+#include "util/config_file.h"
+#ifdef HAVE_GLOB_H
+#include <glob.h>
+#endif
+
+int
+anchor_cmp(const void* k1, const void* k2)
+{
+	int m;
+	struct trust_anchor* n1 = (struct trust_anchor*)k1;
+	struct trust_anchor* n2 = (struct trust_anchor*)k2;
+	/* no need to ntohs(class) because sort order is irrelevant */
+	if(n1->dclass != n2->dclass) {
+		if(n1->dclass < n2->dclass)
+			return -1;
+		return 1;
+	}
+	return dname_lab_cmp(n1->name, n1->namelabs, n2->name, n2->namelabs, 
+		&m);
+}
+
+struct val_anchors* 
+anchors_create(void)
+{
+	struct val_anchors* a = (struct val_anchors*)calloc(1, sizeof(*a));
+	if(!a)
+		return NULL;
+	a->region = regional_create();
+	if(!a->region) {
+		free(a);
+		return NULL;
+	}
+	a->tree = rbtree_create(anchor_cmp);
+	if(!a->tree) {
+		anchors_delete(a);
+		return NULL;
+	}
+	a->autr = autr_global_create();
+	if(!a->autr) {
+		anchors_delete(a);
+		return NULL;
+	}
+	lock_basic_init(&a->lock);
+	lock_protect(&a->lock, a, sizeof(*a));
+	lock_protect(&a->lock, a->autr, sizeof(*a->autr));
+	return a;
+}
+
+/** destroy locks in tree and delete autotrust anchors */
+static void
+anchors_delfunc(rbnode_t* elem, void* ATTR_UNUSED(arg))
+{
+	struct trust_anchor* ta = (struct trust_anchor*)elem;
+	if(ta->autr) {
+		autr_point_delete(ta);
+	} else {
+		lock_basic_destroy(&ta->lock);
+	}
+}
+
+void 
+anchors_delete(struct val_anchors* anchors)
+{
+	if(!anchors)
+		return;
+	lock_unprotect(&anchors->lock, anchors->autr);
+	lock_unprotect(&anchors->lock, anchors);
+	lock_basic_destroy(&anchors->lock);
+	traverse_postorder(anchors->tree, anchors_delfunc, NULL);
+	free(anchors->tree);
+	regional_destroy(anchors->region);
+	autr_global_delete(anchors->autr);
+	free(anchors);
+}
+
+void
+anchors_init_parents_locked(struct val_anchors* anchors)
+{
+	struct trust_anchor* node, *prev = NULL, *p;
+	int m; 
+	/* nobody else can grab locks because we hold the main lock.
+	 * Thus the previous items, after unlocked, are not deleted */
+	RBTREE_FOR(node, struct trust_anchor*, anchors->tree) {
+		lock_basic_lock(&node->lock);
+		node->parent = NULL;
+		if(!prev || prev->dclass != node->dclass) {
+			prev = node;
+			lock_basic_unlock(&node->lock);
+			continue;
+		}
+		(void)dname_lab_cmp(prev->name, prev->namelabs, node->name, 
+			node->namelabs, &m); /* we know prev is smaller */
+		/* sort order like: . com. bla.com. zwb.com. net. */
+		/* find the previous, or parent-parent-parent */
+		for(p = prev; p; p = p->parent)
+			/* looking for name with few labels, a parent */
+			if(p->namelabs <= m) {
+				/* ==: since prev matched m, this is closest*/
+				/* <: prev matches more, but is not a parent,
+			 	* this one is a (grand)parent */
+				node->parent = p;
+				break;
+			}
+		lock_basic_unlock(&node->lock);
+		prev = node;
+	}
+}
+
+/** initialise parent pointers in the tree */
+static void
+init_parents(struct val_anchors* anchors)
+{
+	lock_basic_lock(&anchors->lock);
+	anchors_init_parents_locked(anchors);
+	lock_basic_unlock(&anchors->lock);
+}
+
+struct trust_anchor*
+anchor_find(struct val_anchors* anchors, uint8_t* name, int namelabs,
+	size_t namelen, uint16_t dclass)
+{
+	struct trust_anchor key;
+	rbnode_t* n;
+	if(!name) return NULL;
+	key.node.key = &key;
+	key.name = name;
+	key.namelabs = namelabs;
+	key.namelen = namelen;
+	key.dclass = dclass;
+	lock_basic_lock(&anchors->lock);
+	n = rbtree_search(anchors->tree, &key);
+	if(n) {
+		lock_basic_lock(&((struct trust_anchor*)n->key)->lock);
+	}
+	lock_basic_unlock(&anchors->lock);
+	if(!n)
+		return NULL;
+	return (struct trust_anchor*)n->key;
+}
+
+/** create new trust anchor object */
+static struct trust_anchor*
+anchor_new_ta(struct val_anchors* anchors, uint8_t* name, int namelabs,
+	size_t namelen, uint16_t dclass)
+{
+#ifdef UNBOUND_DEBUG
+	rbnode_t* r;
+#endif
+	struct trust_anchor* ta = (struct trust_anchor*)regional_alloc(
+		anchors->region, sizeof(struct trust_anchor));
+	if(!ta)
+		return NULL;
+	memset(ta, 0, sizeof(*ta));
+	ta->node.key = ta;
+	ta->name = regional_alloc_init(anchors->region, name, namelen);
+	if(!ta->name)
+		return NULL;
+	ta->namelabs = namelabs;
+	ta->namelen = namelen;
+	ta->dclass = dclass;
+	lock_basic_init(&ta->lock);
+	lock_basic_lock(&anchors->lock);
+#ifdef UNBOUND_DEBUG
+	r =
+#endif
+	rbtree_insert(anchors->tree, &ta->node);
+	lock_basic_unlock(&anchors->lock);
+	log_assert(r != NULL);
+	return ta;
+}
+
+/** find trustanchor key by exact data match */
+static struct ta_key*
+anchor_find_key(struct trust_anchor* ta, uint8_t* rdata, size_t rdata_len,
+	uint16_t type)
+{
+	struct ta_key* k;
+	for(k = ta->keylist; k; k = k->next) {
+		if(k->type == type && k->len == rdata_len &&
+			memcmp(k->data, rdata, rdata_len) == 0)
+			return k;
+	}
+	return NULL;
+}
+	
+/** create new trustanchor key */
+static struct ta_key*
+anchor_new_ta_key(struct val_anchors* anchors, uint8_t* rdata, size_t rdata_len,
+	uint16_t type)
+{
+	struct ta_key* k = (struct ta_key*)regional_alloc(anchors->region,
+		sizeof(*k));
+	if(!k)
+		return NULL;
+	memset(k, 0, sizeof(*k));
+	k->data = regional_alloc_init(anchors->region, rdata, rdata_len);
+	if(!k->data)
+		return NULL;
+	k->len = rdata_len;
+	k->type = type;
+	return k;
+}
+
+/**
+ * This routine adds a new RR to a trust anchor. The trust anchor may not
+ * exist yet, and is created if not. The RR can be DS or DNSKEY.
+ * This routine will also remove duplicates; storing them only once.
+ * @param anchors: anchor storage.
+ * @param name: name of trust anchor (wireformat)
+ * @param type: type or RR
+ * @param dclass: class of RR
+ * @param rdata: rdata wireformat, starting with rdlength.
+ *	If NULL, nothing is stored, but an entry is created.
+ * @param rdata_len: length of rdata including rdlength.
+ * @return: NULL on error, else the trust anchor.
+ */
+static struct trust_anchor*
+anchor_store_new_key(struct val_anchors* anchors, uint8_t* name, uint16_t type,
+	uint16_t dclass, uint8_t* rdata, size_t rdata_len)
+{
+	struct ta_key* k;
+	struct trust_anchor* ta;
+	int namelabs;
+	size_t namelen;
+	namelabs = dname_count_size_labels(name, &namelen);
+	if(type != LDNS_RR_TYPE_DS && type != LDNS_RR_TYPE_DNSKEY) {
+		log_err("Bad type for trust anchor");
+		return 0;
+	}
+	/* lookup or create trustanchor */
+	ta = anchor_find(anchors, name, namelabs, namelen, dclass);
+	if(!ta) {
+		ta = anchor_new_ta(anchors, name, namelabs, namelen, dclass);
+		if(!ta)
+			return NULL;
+		lock_basic_lock(&ta->lock);
+	}
+	if(!rdata) {
+		lock_basic_unlock(&ta->lock);
+		return ta;
+	}
+	/* look for duplicates */
+	if(anchor_find_key(ta, rdata, rdata_len, type)) {
+		lock_basic_unlock(&ta->lock);
+		return ta;
+	}
+	k = anchor_new_ta_key(anchors, rdata, rdata_len, type);
+	if(!k) {
+		lock_basic_unlock(&ta->lock);
+		return NULL;
+	}
+	/* add new key */
+	if(type == LDNS_RR_TYPE_DS)
+		ta->numDS++;
+	else	ta->numDNSKEY++;
+	k->next = ta->keylist;
+	ta->keylist = k;
+	lock_basic_unlock(&ta->lock);
+	return ta;
+}
+
+/**
+ * Add new RR. It converts ldns RR to wire format.
+ * @param anchors: anchor storage.
+ * @param buffer: parsing buffer.
+ * @param rr: the rr (allocated by caller).
+ * @return NULL on error, else the trust anchor.
+ */
+static struct trust_anchor*
+anchor_store_new_rr(struct val_anchors* anchors, ldns_buffer* buffer, 
+	ldns_rr* rr)
+{
+	struct trust_anchor* ta;
+	ldns_rdf* owner = ldns_rr_owner(rr);
+	ldns_status status;
+	ldns_buffer_clear(buffer);
+	ldns_buffer_skip(buffer, 2); /* skip rdatalen */
+	status = ldns_rr_rdata2buffer_wire(buffer, rr);
+	if(status != LDNS_STATUS_OK) {
+		log_err("error converting trustanchor to wireformat: %s", 
+			ldns_get_errorstr_by_id(status));
+		return NULL;
+	}
+	ldns_buffer_flip(buffer);
+	ldns_buffer_write_u16_at(buffer, 0, ldns_buffer_limit(buffer) - 2);
+
+	if(!(ta=anchor_store_new_key(anchors, ldns_rdf_data(owner), 
+		ldns_rr_get_type(rr), ldns_rr_get_class(rr),
+		ldns_buffer_begin(buffer), ldns_buffer_limit(buffer)))) {
+		return NULL;
+	}
+	log_nametypeclass(VERB_QUERY, "adding trusted key",
+		ldns_rdf_data(owner), 
+		ldns_rr_get_type(rr), ldns_rr_get_class(rr));
+	return ta;
+}
+
+/**
+ * Insert insecure anchor
+ * @param anchors: anchor storage.
+ * @param str: the domain name.
+ * @return NULL on error, Else last trust anchor point
+ */
+static struct trust_anchor*
+anchor_insert_insecure(struct val_anchors* anchors, const char* str)
+{
+	struct trust_anchor* ta;
+	ldns_rdf* nm = ldns_dname_new_frm_str(str);
+	if(!nm) {
+		log_err("parse error in domain name '%s'", str);
+		return NULL;
+	}
+	ta = anchor_store_new_key(anchors, ldns_rdf_data(nm), LDNS_RR_TYPE_DS,
+		LDNS_RR_CLASS_IN, NULL, 0);
+	ldns_rdf_deep_free(nm);
+	return ta;
+}
+
+struct trust_anchor*
+anchor_store_str(struct val_anchors* anchors, ldns_buffer* buffer,
+	const char* str)
+{
+	struct trust_anchor* ta;
+	ldns_rr* rr = NULL;
+	ldns_status status = ldns_rr_new_frm_str(&rr, str, 0, NULL, NULL);
+	if(status != LDNS_STATUS_OK) {
+		log_err("error parsing trust anchor: %s", 
+			ldns_get_errorstr_by_id(status));
+		ldns_rr_free(rr);
+		return NULL;
+	}
+	if(!(ta=anchor_store_new_rr(anchors, buffer, rr))) {
+		log_err("out of memory");
+		ldns_rr_free(rr);
+		return NULL;
+	}
+	ldns_rr_free(rr);
+	return ta;
+}
+
+/**
+ * Read a file with trust anchors
+ * @param anchors: anchor storage.
+ * @param buffer: parsing buffer.
+ * @param fname: string.
+ * @param onlyone: only one trust anchor allowed in file.
+ * @return NULL on error. Else last trust-anchor point.
+ */
+static struct trust_anchor*
+anchor_read_file(struct val_anchors* anchors, ldns_buffer* buffer,
+	const char* fname, int onlyone)
+{
+	struct trust_anchor* ta = NULL, *tanew;
+	uint32_t default_ttl = 3600;
+	ldns_rdf* origin = NULL, *prev = NULL;
+	int line_nr = 1;
+	ldns_status status;
+	ldns_rr* rr;
+	int ok = 1;
+	FILE* in = fopen(fname, "r");
+	if(!in) {
+		log_err("error opening file %s: %s", fname, strerror(errno));
+		return 0;
+	}
+	while(!feof(in)) {
+		rr = NULL;
+		status = ldns_rr_new_frm_fp_l(&rr, in, &default_ttl, &origin,
+			&prev, &line_nr);
+		if(status == LDNS_STATUS_SYNTAX_EMPTY /* empty line */
+			|| status == LDNS_STATUS_SYNTAX_TTL /* $TTL */
+			|| status == LDNS_STATUS_SYNTAX_ORIGIN /* $ORIGIN */)
+			continue;
+		if(status != LDNS_STATUS_OK) {
+			log_err("parse error in %s:%d : %s", fname, line_nr,
+				ldns_get_errorstr_by_id(status));
+			ldns_rr_free(rr);
+			ok = 0;
+			break;
+		}
+		if(ldns_rr_get_type(rr) != LDNS_RR_TYPE_DS && 
+			ldns_rr_get_type(rr) != LDNS_RR_TYPE_DNSKEY) {
+			ldns_rr_free(rr);
+			continue;
+		}
+		if(!(tanew=anchor_store_new_rr(anchors, buffer, rr))) {
+			log_err("error at %s line %d", fname, line_nr);
+			ldns_rr_free(rr);
+			ok = 0;
+			break;
+		}
+		if(onlyone && ta && ta != tanew) {
+			log_err("error at %s line %d: no multiple anchor "
+				"domains allowed (you can have multiple "
+				"keys, but they must have the same name).", 
+				fname, line_nr);
+			ldns_rr_free(rr);
+			ok = 0;
+			break;
+		}
+		ta = tanew;
+		ldns_rr_free(rr);
+	}
+	ldns_rdf_deep_free(origin);
+	ldns_rdf_deep_free(prev);
+	fclose(in);
+	if(!ok) return NULL;
+	/* empty file is OK when multiple anchors are allowed */
+	if(!onlyone && !ta) return (struct trust_anchor*)1;
+	return ta;
+}
+
+/** skip file to end of line */
+static void
+skip_to_eol(FILE* in)
+{
+	int c;
+	while((c = getc(in)) != EOF ) {
+		if(c == '\n')
+			return;
+	}
+}
+
+/** true for special characters in bind configs */
+static int
+is_bind_special(int c)
+{
+	switch(c) {
+		case '{':
+		case '}':
+		case '"':
+		case ';':
+			return 1;
+	}
+	return 0;
+}
+
+/** 
+ * Read a keyword skipping bind comments; spaces, specials, restkeywords. 
+ * The file is split into the following tokens:
+ *	* special characters, on their own, rdlen=1, { } doublequote ;
+ *	* whitespace becomes a single ' ' or tab. Newlines become spaces.
+ *	* other words ('keywords')
+ *	* comments are skipped if desired
+ *		/ / C++ style comment to end of line
+ *		# to end of line
+ *		/ * C style comment * /
+ * @param in: file to read from.
+ * @param buf: buffer, what is read is stored after current buffer position.
+ *	Space is left in the buffer to write a terminating 0.
+ * @param line: line number is increased per line, for error reports.
+ * @param comments: if 0, comments are not possible and become text.
+ *	if 1, comments are skipped entirely.
+ *	In BIND files, this is when reading quoted strings, for example
+ *	" base 64 text with / / in there "
+ * @return the number of character written to the buffer. 
+ *	0 on end of file.
+ */
+static int
+readkeyword_bindfile(FILE* in, ldns_buffer* buf, int* line, int comments)
+{
+	int c;
+	int numdone = 0;
+	while((c = getc(in)) != EOF ) {
+		if(comments && c == '#') {	/*   # blabla   */
+			skip_to_eol(in);
+			(*line)++;
+			continue;
+		} else if(comments && c=='/' && numdone>0 && /* /_/ bla*/
+			ldns_buffer_read_u8_at(buf, 
+			ldns_buffer_position(buf)-1) == '/') {
+			ldns_buffer_skip(buf, -1);
+			numdone--;
+			skip_to_eol(in);
+			(*line)++;
+			continue;
+		} else if(comments && c=='*' && numdone>0 && /* /_* bla *_/ */
+			ldns_buffer_read_u8_at(buf, 
+			ldns_buffer_position(buf)-1) == '/') {
+			ldns_buffer_skip(buf, -1);
+			numdone--;
+			/* skip to end of comment */
+			while(c != EOF && (c=getc(in)) != EOF ) {
+				if(c == '*') {
+					if((c=getc(in)) == '/')
+						break;
+				}
+				if(c == '\n')
+					(*line)++;
+			}
+			continue;
+		}
+		/* not a comment, complete the keyword */
+		if(numdone > 0) {
+			/* check same type */
+			if(isspace(c)) {
+				ungetc(c, in);
+				return numdone;
+			}
+			if(is_bind_special(c)) {
+				ungetc(c, in);
+				return numdone;
+			}
+		}
+		if(c == '\n') {
+			c = ' ';
+			(*line)++;
+		}
+		/* space for 1 char + 0 string terminator */
+		if(ldns_buffer_remaining(buf) < 2) {
+			fatal_exit("trusted-keys, %d, string too long", *line);
+		}
+		ldns_buffer_write_u8(buf, (uint8_t)c);
+		numdone++;
+		if(isspace(c)) {
+			/* collate whitespace into ' ' */
+			while((c = getc(in)) != EOF ) {
+				if(c == '\n')
+					(*line)++;
+				if(!isspace(c)) {
+					ungetc(c, in);
+					break;
+				}
+			}
+			return numdone;
+		}
+		if(is_bind_special(c))
+			return numdone;
+	}
+	return numdone;
+}
+
+/** skip through file to { or ; */
+static int 
+skip_to_special(FILE* in, ldns_buffer* buf, int* line, int spec) 
+{
+	int rdlen;
+	ldns_buffer_clear(buf);
+	while((rdlen=readkeyword_bindfile(in, buf, line, 1))) {
+		if(rdlen == 1 && isspace((int)*ldns_buffer_begin(buf))) {
+			ldns_buffer_clear(buf);
+			continue;
+		}
+		if(rdlen != 1 || *ldns_buffer_begin(buf) != (uint8_t)spec) {
+			ldns_buffer_write_u8(buf, 0);
+			log_err("trusted-keys, line %d, expected %c", 
+				*line, spec);
+			return 0;
+		}
+		return 1;
+	}
+	log_err("trusted-keys, line %d, expected %c got EOF", *line, spec);
+	return 0;
+}
+
+/** 
+ * read contents of trusted-keys{ ... ; clauses and insert keys into storage.
+ * @param anchors: where to store keys
+ * @param buf: buffer to use
+ * @param line: line number in file
+ * @param in: file to read from.
+ * @return 0 on error.
+ */
+static int
+process_bind_contents(struct val_anchors* anchors, ldns_buffer* buf, 
+	int* line, FILE* in)
+{
+	/* loop over contents, collate strings before ; */
+	/* contents is (numbered): 0   1    2  3 4   5  6 7 8    */
+	/*                           name. 257 3 5 base64 base64 */
+	/* quoted value:           0 "111"  0  0 0   0  0 0 0    */
+	/* comments value:         1 "000"  1  1  1 "0  0 0 0"  1 */
+	int contnum = 0;
+	int quoted = 0;
+	int comments = 1;
+	int rdlen;
+	char* str = 0;
+	ldns_buffer_clear(buf);
+	while((rdlen=readkeyword_bindfile(in, buf, line, comments))) {
+		if(rdlen == 1 && ldns_buffer_position(buf) == 1
+			&& isspace((int)*ldns_buffer_begin(buf))) {
+			/* starting whitespace is removed */
+			ldns_buffer_clear(buf);
+			continue;
+		} else if(rdlen == 1 && ldns_buffer_current(buf)[-1] == '"') {
+			/* remove " from the string */
+			if(contnum == 0) {
+				quoted = 1;
+				comments = 0;
+			}
+			ldns_buffer_skip(buf, -1);
+			if(contnum > 0 && quoted) {
+				if(ldns_buffer_remaining(buf) < 8+1) {
+					log_err("line %d, too long", *line);
+					return 0;
+				}
+				ldns_buffer_write(buf, " DNSKEY ", 8);
+				quoted = 0;
+				comments = 1;
+			} else if(contnum > 0)
+				comments = !comments;
+			continue;
+		} else if(rdlen == 1 && ldns_buffer_current(buf)[-1] == ';') {
+
+			if(contnum < 5) {
+				ldns_buffer_write_u8(buf, 0);
+				log_err("line %d, bad key", *line);
+				return 0;
+			}
+			ldns_buffer_skip(buf, -1);
+			ldns_buffer_write_u8(buf, 0);
+			str = strdup((char*)ldns_buffer_begin(buf));
+			if(!str) {
+				log_err("line %d, allocation failure", *line);
+				return 0;
+			}
+			if(!anchor_store_str(anchors, buf, str)) {
+				log_err("line %d, bad key", *line);
+				free(str);
+				return 0;
+			}
+			free(str);
+			ldns_buffer_clear(buf);
+			contnum = 0;
+			quoted = 0;
+			comments = 1;
+			continue;
+		} else if(rdlen == 1 && ldns_buffer_current(buf)[-1] == '}') {
+			if(contnum > 0) {
+				ldns_buffer_write_u8(buf, 0);
+				log_err("line %d, bad key before }", *line);
+				return 0;
+			}
+			return 1;
+		} else if(rdlen == 1 && 
+			isspace((int)ldns_buffer_current(buf)[-1])) {
+			/* leave whitespace here */
+		} else {
+			/* not space or whatnot, so actual content */
+			contnum ++;
+			if(contnum == 1 && !quoted) {
+				if(ldns_buffer_remaining(buf) < 8+1) {
+					log_err("line %d, too long", *line);
+					return 0;
+				}	
+				ldns_buffer_write(buf, " DNSKEY ", 8);
+			}
+		}
+	}
+
+	log_err("line %d, EOF before }", *line);
+	return 0;
+}
+
+/**
+ * Read a BIND9 like file with trust anchors in named.conf format.
+ * @param anchors: anchor storage.
+ * @param buffer: parsing buffer.
+ * @param fname: string.
+ * @return false on error.
+ */
+static int
+anchor_read_bind_file(struct val_anchors* anchors, ldns_buffer* buffer,
+	const char* fname)
+{
+	int line_nr = 1;
+	FILE* in = fopen(fname, "r");
+	int rdlen = 0;
+	if(!in) {
+		log_err("error opening file %s: %s", fname, strerror(errno));
+		return 0;
+	}
+	verbose(VERB_QUERY, "reading in bind-compat-mode: '%s'", fname);
+	/* scan for  trusted-keys  keyword, ignore everything else */
+	ldns_buffer_clear(buffer);
+	while((rdlen=readkeyword_bindfile(in, buffer, &line_nr, 1)) != 0) {
+		if(rdlen != 12 || strncmp((char*)ldns_buffer_begin(buffer),
+			"trusted-keys", 12) != 0) {
+			ldns_buffer_clear(buffer);
+			/* ignore everything but trusted-keys */
+			continue;
+		}
+		if(!skip_to_special(in, buffer, &line_nr, '{')) {
+			log_err("error in trusted key: \"%s\"", fname);
+			fclose(in);
+			return 0;
+		}
+		/* process contents */
+		if(!process_bind_contents(anchors, buffer, &line_nr, in)) {
+			log_err("error in trusted key: \"%s\"", fname);
+			fclose(in);
+			return 0;
+		}
+		if(!skip_to_special(in, buffer, &line_nr, ';')) {
+			log_err("error in trusted key: \"%s\"", fname);
+			fclose(in);
+			return 0;
+		}
+		ldns_buffer_clear(buffer);
+	}
+	fclose(in);
+	return 1;
+}
+
+/**
+ * Read a BIND9 like files with trust anchors in named.conf format.
+ * Performs wildcard processing of name.
+ * @param anchors: anchor storage.
+ * @param buffer: parsing buffer.
+ * @param pat: pattern string. (can be wildcarded)
+ * @return false on error.
+ */
+static int
+anchor_read_bind_file_wild(struct val_anchors* anchors, ldns_buffer* buffer,
+	const char* pat)
+{
+#ifdef HAVE_GLOB
+	glob_t g;
+	size_t i;
+	int r, flags;
+	if(!strchr(pat, '*') && !strchr(pat, '?') && !strchr(pat, '[') && 
+		!strchr(pat, '{') && !strchr(pat, '~')) {
+		return anchor_read_bind_file(anchors, buffer, pat);
+	}
+	verbose(VERB_QUERY, "wildcard found, processing %s", pat);
+	flags = 0 
+#ifdef GLOB_ERR
+		| GLOB_ERR
+#endif
+#ifdef GLOB_NOSORT
+		| GLOB_NOSORT
+#endif
+#ifdef GLOB_BRACE
+		| GLOB_BRACE
+#endif
+#ifdef GLOB_TILDE
+		| GLOB_TILDE
+#endif
+	;
+	memset(&g, 0, sizeof(g));
+	r = glob(pat, flags, NULL, &g);
+	if(r) {
+		/* some error */
+		if(r == GLOB_NOMATCH) {
+			verbose(VERB_QUERY, "trusted-keys-file: "
+				"no matches for %s", pat);
+			return 1;
+		} else if(r == GLOB_NOSPACE) {
+			log_err("wildcard trusted-keys-file %s: "
+				"pattern out of memory", pat);
+		} else if(r == GLOB_ABORTED) {
+			log_err("wildcard trusted-keys-file %s: expansion "
+				"aborted (%s)", pat, strerror(errno));
+		} else {
+			log_err("wildcard trusted-keys-file %s: expansion "
+				"failed (%s)", pat, strerror(errno));
+		}
+		return 0;
+	}
+	/* process files found, if any */
+	for(i=0; i<(size_t)g.gl_pathc; i++) {
+		if(!anchor_read_bind_file(anchors, buffer, g.gl_pathv[i])) {
+			log_err("error reading wildcard "
+				"trusted-keys-file: %s", g.gl_pathv[i]);
+			globfree(&g);
+			return 0;
+		}
+	}
+	globfree(&g);
+	return 1;
+#else /* not HAVE_GLOB */
+	return anchor_read_bind_file(anchors, buffer, pat);
+#endif /* HAVE_GLOB */
+}
+
+/** 
+ * Assemble an rrset structure for the type 
+ * @param region: allocated in this region.
+ * @param ta: trust anchor.
+ * @param num: number of items to fetch from list.
+ * @param type: fetch only items of this type.
+ * @return rrset or NULL on error.
+ */
+static struct ub_packed_rrset_key*
+assemble_it(struct regional* region, struct trust_anchor* ta, size_t num, 
+	uint16_t type)
+{
+	struct ub_packed_rrset_key* pkey = (struct ub_packed_rrset_key*)
+		regional_alloc(region, sizeof(*pkey));
+	struct packed_rrset_data* pd;
+	struct ta_key* tk;
+	size_t i;
+	if(!pkey)
+		return NULL;
+	memset(pkey, 0, sizeof(*pkey));
+	pkey->rk.dname = regional_alloc_init(region, ta->name, ta->namelen);
+	if(!pkey->rk.dname)
+		return NULL;
+	
+	pkey->rk.dname_len = ta->namelen;
+	pkey->rk.type = htons(type);
+	pkey->rk.rrset_class = htons(ta->dclass);
+	/* The rrset is build in an uncompressed way. This means it
+	 * cannot be copied in the normal way. */
+	pd = (struct packed_rrset_data*)regional_alloc(region, sizeof(*pd));
+	if(!pd)
+		return NULL;
+	memset(pd, 0, sizeof(*pd));
+	pd->count = num;
+	pd->trust = rrset_trust_ultimate;
+	pd->rr_len = (size_t*)regional_alloc(region, num*sizeof(size_t));
+	if(!pd->rr_len)
+		return NULL;
+	pd->rr_ttl = (uint32_t*)regional_alloc(region, num*sizeof(uint32_t));
+	if(!pd->rr_ttl)
+		return NULL;
+	pd->rr_data = (uint8_t**)regional_alloc(region, num*sizeof(uint8_t*));
+	if(!pd->rr_data)
+		return NULL;
+	/* fill in rrs */
+	i=0;
+	for(tk = ta->keylist; tk; tk = tk->next) {
+		if(tk->type != type)
+			continue;
+		pd->rr_len[i] = tk->len;
+		/* reuse data ptr to allocation in region */
+		pd->rr_data[i] = tk->data;
+		pd->rr_ttl[i] = 0;
+		i++;
+	}
+	pkey->entry.data = (void*)pd;
+	return pkey;
+}
+
+/**
+ * Assemble structures for the trust DS and DNSKEY rrsets.
+ * @param anchors: trust anchor storage.
+ * @param ta: trust anchor
+ * @return: false on error.
+ */
+static int
+anchors_assemble(struct val_anchors* anchors, struct trust_anchor* ta)
+{
+	if(ta->numDS > 0) {
+		ta->ds_rrset = assemble_it(anchors->region, ta,
+			ta->numDS, LDNS_RR_TYPE_DS);
+		if(!ta->ds_rrset)
+			return 0;
+	}
+	if(ta->numDNSKEY > 0) {
+		ta->dnskey_rrset = assemble_it(anchors->region, ta,
+			ta->numDNSKEY, LDNS_RR_TYPE_DNSKEY);
+		if(!ta->dnskey_rrset)
+			return 0;
+	}
+	return 1;
+}
+
+/**
+ * Check DS algos for support, warn if not.
+ * @param ta: trust anchor
+ * @return number of DS anchors with unsupported algorithms.
+ */
+static size_t
+anchors_ds_unsupported(struct trust_anchor* ta)
+{
+	size_t i, num = 0;
+	for(i=0; i<ta->numDS; i++) {
+		if(!ds_digest_algo_is_supported(ta->ds_rrset, i) || 
+			!ds_key_algo_is_supported(ta->ds_rrset, i))
+			num++;
+	}
+	return num;
+}
+
+/**
+ * Check DNSKEY algos for support, warn if not.
+ * @param ta: trust anchor
+ * @return number of DNSKEY anchors with unsupported algorithms.
+ */
+static size_t
+anchors_dnskey_unsupported(struct trust_anchor* ta)
+{
+	size_t i, num = 0;
+	for(i=0; i<ta->numDNSKEY; i++) {
+		if(!dnskey_algo_is_supported(ta->dnskey_rrset, i))
+			num++;
+	}
+	return num;
+}
+
+/**
+ * Assemble the rrsets in the anchors, ready for use by validator.
+ * @param anchors: trust anchor storage.
+ * @return: false on error.
+ */
+static int
+anchors_assemble_rrsets(struct val_anchors* anchors)
+{
+	struct trust_anchor* ta;
+	struct trust_anchor* next;
+	size_t nods, nokey;
+	lock_basic_lock(&anchors->lock);
+	ta=(struct trust_anchor*)rbtree_first(anchors->tree);
+	while((rbnode_t*)ta != RBTREE_NULL) {
+		next = (struct trust_anchor*)rbtree_next(&ta->node);
+		lock_basic_lock(&ta->lock);
+		if(ta->autr || (ta->numDS == 0 && ta->numDNSKEY == 0)) {
+			lock_basic_unlock(&ta->lock);
+			ta = next; /* skip */
+			continue;
+		}
+		if(!anchors_assemble(anchors, ta)) {
+			log_err("out of memory");
+			lock_basic_unlock(&ta->lock);
+			lock_basic_unlock(&anchors->lock);
+			return 0;
+		}
+		nods = anchors_ds_unsupported(ta);
+		nokey = anchors_dnskey_unsupported(ta);
+		if(nods) {
+			log_nametypeclass(0, "warning: unsupported "
+				"algorithm for trust anchor", 
+				ta->name, LDNS_RR_TYPE_DS, ta->dclass);
+		}
+		if(nokey) {
+			log_nametypeclass(0, "warning: unsupported "
+				"algorithm for trust anchor", 
+				ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass);
+		}
+		if(nods == ta->numDS && nokey == ta->numDNSKEY) {
+			char b[257];
+			dname_str(ta->name, b);
+			log_warn("trust anchor %s has no supported algorithms,"
+				" the anchor is ignored (check if you need to"
+				" upgrade unbound and openssl)", b);
+			(void)rbtree_delete(anchors->tree, &ta->node);
+			lock_basic_unlock(&ta->lock);
+			lock_basic_destroy(&ta->lock);
+			ta = next;
+			continue;
+		}
+		lock_basic_unlock(&ta->lock);
+		ta = next;
+	}
+	lock_basic_unlock(&anchors->lock);
+	return 1;
+}
+
+int 
+anchors_apply_cfg(struct val_anchors* anchors, struct config_file* cfg)
+{
+	struct config_strlist* f;
+	char* nm;
+	ldns_buffer* parsebuf = ldns_buffer_new(65535);
+	for(f = cfg->domain_insecure; f; f = f->next) {
+		if(!f->str || f->str[0] == 0) /* empty "" */
+			continue;
+		if(!anchor_insert_insecure(anchors, f->str)) {
+			log_err("error in domain-insecure: %s", f->str);
+			ldns_buffer_free(parsebuf);
+			return 0;
+		}
+	}
+	for(f = cfg->trust_anchor_file_list; f; f = f->next) {
+		if(!f->str || f->str[0] == 0) /* empty "" */
+			continue;
+		nm = f->str;
+		if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(nm,
+			cfg->chrootdir, strlen(cfg->chrootdir)) == 0)
+			nm += strlen(cfg->chrootdir);
+		if(!anchor_read_file(anchors, parsebuf, nm, 0)) {
+			log_err("error reading trust-anchor-file: %s", f->str);
+			ldns_buffer_free(parsebuf);
+			return 0;
+		}
+	}
+	for(f = cfg->trusted_keys_file_list; f; f = f->next) {
+		if(!f->str || f->str[0] == 0) /* empty "" */
+			continue;
+		nm = f->str;
+		if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(nm,
+			cfg->chrootdir, strlen(cfg->chrootdir)) == 0)
+			nm += strlen(cfg->chrootdir);
+		if(!anchor_read_bind_file_wild(anchors, parsebuf, nm)) {
+			log_err("error reading trusted-keys-file: %s", f->str);
+			ldns_buffer_free(parsebuf);
+			return 0;
+		}
+	}
+	for(f = cfg->trust_anchor_list; f; f = f->next) {
+		if(!f->str || f->str[0] == 0) /* empty "" */
+			continue;
+		if(!anchor_store_str(anchors, parsebuf, f->str)) {
+			log_err("error in trust-anchor: \"%s\"", f->str);
+			ldns_buffer_free(parsebuf);
+			return 0;
+		}
+	}
+	if(cfg->dlv_anchor_file && cfg->dlv_anchor_file[0] != 0) {
+		struct trust_anchor* dlva;
+		nm = cfg->dlv_anchor_file;
+		if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(nm,
+			cfg->chrootdir, strlen(cfg->chrootdir)) == 0)
+			nm += strlen(cfg->chrootdir);
+		if(!(dlva = anchor_read_file(anchors, parsebuf,
+			nm, 1))) {
+			log_err("error reading dlv-anchor-file: %s", 
+				cfg->dlv_anchor_file);
+			ldns_buffer_free(parsebuf);
+			return 0;
+		}
+		lock_basic_lock(&anchors->lock);
+		anchors->dlv_anchor = dlva;
+		lock_basic_unlock(&anchors->lock);
+	}
+	for(f = cfg->dlv_anchor_list; f; f = f->next) {
+		struct trust_anchor* dlva;
+		if(!f->str || f->str[0] == 0) /* empty "" */
+			continue;
+		if(!(dlva = anchor_store_str(
+			anchors, parsebuf, f->str))) {
+			log_err("error in dlv-anchor: \"%s\"", f->str);
+			ldns_buffer_free(parsebuf);
+			return 0;
+		}
+		lock_basic_lock(&anchors->lock);
+		anchors->dlv_anchor = dlva;
+		lock_basic_unlock(&anchors->lock);
+	}
+	/* do autr last, so that it sees what anchors are filled by other
+	 * means can can print errors about double config for the name */
+	for(f = cfg->auto_trust_anchor_file_list; f; f = f->next) {
+		if(!f->str || f->str[0] == 0) /* empty "" */
+			continue;
+		nm = f->str;
+		if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(nm,
+			cfg->chrootdir, strlen(cfg->chrootdir)) == 0)
+			nm += strlen(cfg->chrootdir);
+		if(!autr_read_file(anchors, nm)) {
+			log_err("error reading auto-trust-anchor-file: %s", 
+				f->str);
+			ldns_buffer_free(parsebuf);
+			return 0;
+		}
+	}
+	/* first assemble, since it may delete useless anchors */
+	anchors_assemble_rrsets(anchors);
+	init_parents(anchors);
+	ldns_buffer_free(parsebuf);
+	if(verbosity >= VERB_ALGO) autr_debug_print(anchors);
+	return 1;
+}
+
+struct trust_anchor* 
+anchors_lookup(struct val_anchors* anchors,
+        uint8_t* qname, size_t qname_len, uint16_t qclass)
+{
+	struct trust_anchor key;
+	struct trust_anchor* result;
+	rbnode_t* res = NULL;
+	key.node.key = &key;
+	key.name = qname;
+	key.namelabs = dname_count_labels(qname);
+	key.namelen = qname_len;
+	key.dclass = qclass;
+	lock_basic_lock(&anchors->lock);
+	if(rbtree_find_less_equal(anchors->tree, &key, &res)) {
+		/* exact */
+		result = (struct trust_anchor*)res;
+	} else {
+		/* smaller element (or no element) */
+		int m;
+		result = (struct trust_anchor*)res;
+		if(!result || result->dclass != qclass) {
+			lock_basic_unlock(&anchors->lock);
+			return NULL;
+		}
+		/* count number of labels matched */
+		(void)dname_lab_cmp(result->name, result->namelabs, key.name,
+			key.namelabs, &m);
+		while(result) { /* go up until qname is subdomain of stub */
+			if(result->namelabs <= m)
+				break;
+			result = result->parent;
+		}
+	}
+	if(result) {
+		lock_basic_lock(&result->lock);
+	}
+	lock_basic_unlock(&anchors->lock);
+	return result;
+}
+
+size_t 
+anchors_get_mem(struct val_anchors* anchors)
+{
+	return sizeof(*anchors) + regional_get_mem(anchors->region);
+}
diff --git a/3rdParty/Unbound/src/src/validator/val_anchor.h b/3rdParty/Unbound/src/src/validator/val_anchor.h
new file mode 100644
index 0000000..d2f3afc
--- /dev/null
+++ b/3rdParty/Unbound/src/src/validator/val_anchor.h
@@ -0,0 +1,206 @@
+/*
+ * validator/val_anchor.h - validator trust anchor storage.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains storage for the trust anchors for the validator.
+ */
+
+#ifndef VALIDATOR_VAL_ANCHOR_H
+#define VALIDATOR_VAL_ANCHOR_H
+#include "util/rbtree.h"
+#include "util/locks.h"
+struct regional;
+struct trust_anchor;
+struct config_file;
+struct ub_packed_rrset_key;
+struct autr_point_data;
+struct autr_global_data;
+
+/**
+ * Trust anchor store.
+ * The tree must be locked, while no other locks (from trustanchors) are held.
+ * And then an anchor searched for.  Which can be locked or deleted.  Then
+ * the tree can be unlocked again.  This means you have to release the lock
+ * on a trust anchor and look it up again to delete it.
+ */
+struct val_anchors {
+	/** lock on trees */
+	lock_basic_t lock;
+	/** 
+	 * region where trust anchors are allocated.
+	 * Autotrust anchors are malloced so they can be updated. 
+	 */
+	struct regional* region;
+	/**
+	 * Anchors are store in this tree. Sort order is chosen, so that
+	 * dnames are in nsec-like order. A lookup on class, name will return
+	 * an exact match of the closest match, with the ancestor needed.
+	 * contents of type trust_anchor.
+	 */
+	rbtree_t* tree;
+	/** The DLV trust anchor (if one is configured, else NULL) */
+	struct trust_anchor* dlv_anchor;
+	/** Autotrust global data, anchors sorted by next probe time */
+	struct autr_global_data* autr;
+};
+
+/**
+ * Trust anchor key
+ */
+struct ta_key {
+	/** next in list */
+	struct ta_key* next;
+	/** rdata, in wireformat of the key RR. starts with rdlength. */
+	uint8_t* data;
+	/** length of the rdata (including rdlength). */
+	size_t len;
+	/** DNS type (host format) of the key, DS or DNSKEY */
+	uint16_t type;
+};
+
+/**
+ * A trust anchor in the trust anchor store.
+ * Unique by name, class.
+ */
+struct trust_anchor {
+	/** rbtree node, key is this structure */
+	rbnode_t node;
+	/** lock on the entire anchor and its keys; for autotrust changes */
+	lock_basic_t lock;
+	/** name of this trust anchor */
+	uint8_t* name;
+	/** length of name */
+	size_t namelen;
+	/** number of labels in name of rrset */
+	int namelabs;
+	/** the ancestor in the trustanchor tree */
+	struct trust_anchor* parent;
+	/** 
+	 * List of DS or DNSKEY rrs that form the trust anchor.
+	 * It is allocated in the region.
+	 */
+	struct ta_key* keylist;
+	/** Autotrust anchor point data, or NULL */
+	struct autr_point_data* autr;
+	/** number of DSs in the keylist */
+	size_t numDS;
+	/** number of DNSKEYs in the keylist */
+	size_t numDNSKEY;
+	/** the DS RRset */
+	struct ub_packed_rrset_key* ds_rrset;
+	/** The DNSKEY RRset */
+	struct ub_packed_rrset_key* dnskey_rrset;
+	/** class of the trust anchor */
+	uint16_t dclass;
+};
+
+/**
+ * Create trust anchor storage
+ * @return new storage or NULL on error.
+ */
+struct val_anchors* anchors_create(void);
+
+/**
+ * Delete trust anchor storage.
+ * @param anchors: to delete.
+ */
+void anchors_delete(struct val_anchors* anchors);
+
+/**
+ * Process trust anchor config.
+ * @param anchors: struct anchor storage
+ * @param cfg: config options.
+ * @return 0 on error.
+ */
+int anchors_apply_cfg(struct val_anchors* anchors, struct config_file* cfg);
+
+/**
+ * Recalculate parent pointers.  The caller must hold the lock on the
+ * anchors structure (say after removing an item from the rbtree).
+ * Caller must not hold any locks on trust anchors.
+ * After the call is complete the parent pointers are updated and an item
+ * just removed is no longer referenced in parent pointers.
+ * @param anchors: the structure to update.
+ */
+void anchors_init_parents_locked(struct val_anchors* anchors);
+
+/**
+ * Given a qname/qclass combination, find the trust anchor closest above it.
+ * Or return NULL if none exists.
+ *
+ * @param anchors: struct anchor storage
+ * @param qname: query name, uncompressed wireformat.
+ * @param qname_len: length of qname.
+ * @param qclass: class to query for.
+ * @return the trust anchor or NULL if none is found. The anchor is locked.
+ */
+struct trust_anchor* anchors_lookup(struct val_anchors* anchors,
+	uint8_t* qname, size_t qname_len, uint16_t qclass);
+
+/**
+ * Find a trust anchor. Exact matching.
+ * @param anchors: anchor storage.
+ * @param name: name of trust anchor (wireformat)
+ * @param namelabs: labels in name
+ * @param namelen: length of name
+ * @param dclass: class of trust anchor
+ * @return NULL if not found. The anchor is locked.
+ */
+struct trust_anchor* anchor_find(struct val_anchors* anchors, 
+	uint8_t* name, int namelabs, size_t namelen, uint16_t dclass);
+
+/**
+ * Store one string as trust anchor RR.
+ * @param anchors: anchor storage.
+ * @param buffer: parsing buffer, to generate the RR wireformat in.
+ * @param str: string.
+ * @return NULL on error.
+ */
+struct trust_anchor* anchor_store_str(struct val_anchors* anchors, 
+	ldns_buffer* buffer, const char* str);
+
+/**
+ * Get memory in use by the trust anchor storage
+ * @param anchors: anchor storage.
+ * @return memory in use in bytes.
+ */
+size_t anchors_get_mem(struct val_anchors* anchors);
+
+/** compare two trust anchors */
+int anchor_cmp(const void* k1, const void* k2);
+
+#endif /* VALIDATOR_VAL_ANCHOR_H */
diff --git a/3rdParty/Unbound/src/src/validator/val_kcache.c b/3rdParty/Unbound/src/src/validator/val_kcache.c
new file mode 100644
index 0000000..68e8c3f
--- /dev/null
+++ b/3rdParty/Unbound/src/src/validator/val_kcache.c
@@ -0,0 +1,172 @@
+/*
+ * validator/val_kcache.c - validator key shared cache with validated keys
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions for dealing with the validator key cache.
+ */
+#include "config.h"
+#include "validator/val_kcache.h"
+#include "validator/val_kentry.h"
+#include "util/log.h"
+#include "util/config_file.h"
+#include "util/data/dname.h"
+#include "util/module.h"
+
+struct key_cache* 
+key_cache_create(struct config_file* cfg)
+{
+	struct key_cache* kcache = (struct key_cache*)calloc(1, 
+		sizeof(*kcache));
+	size_t numtables, start_size, maxmem;
+	if(!kcache) {
+		log_err("malloc failure");
+		return NULL;
+	}
+	numtables = cfg->key_cache_slabs;
+	start_size = HASH_DEFAULT_STARTARRAY;
+	maxmem = cfg->key_cache_size;
+	kcache->slab = slabhash_create(numtables, start_size, maxmem,
+		&key_entry_sizefunc, &key_entry_compfunc,
+		&key_entry_delkeyfunc, &key_entry_deldatafunc, NULL);
+	if(!kcache->slab) {
+		log_err("malloc failure");
+		free(kcache);
+		return NULL;
+	}
+	return kcache;
+}
+
+void 
+key_cache_delete(struct key_cache* kcache)
+{
+	if(!kcache)
+		return;
+	slabhash_delete(kcache->slab);
+	free(kcache);
+}
+
+void 
+key_cache_insert(struct key_cache* kcache, struct key_entry_key* kkey,
+	struct module_qstate* qstate)
+{
+	struct key_entry_key* k = key_entry_copy(kkey);
+	if(!k)
+		return;
+	if(key_entry_isbad(k) && qstate->errinf &&
+		qstate->env->cfg->val_log_level >= 2) {
+		/* on malloc failure there is simply no reason string */
+		key_entry_set_reason(k, errinf_to_str(qstate));
+	}
+	key_entry_hash(k);
+	slabhash_insert(kcache->slab, k->entry.hash, &k->entry, 
+		k->entry.data, NULL);
+}
+
+/**
+ * Lookup exactly in the key cache. Returns pointer to locked entry.
+ * Caller must unlock it after use.
+ * @param kcache: the key cache.
+ * @param name: for what name to look; uncompressed wireformat
+ * @param namelen: length of the name.
+ * @param key_class: class of the key.
+ * @param wr: set true to get a writelock.
+ * @return key entry, locked, or NULL if not found. No TTL checking is
+ * 	performed.
+ */
+static struct key_entry_key*
+key_cache_search(struct key_cache* kcache, uint8_t* name, size_t namelen, 
+	uint16_t key_class, int wr)
+{
+	struct lruhash_entry* e;
+	struct key_entry_key lookfor;
+	lookfor.entry.key = &lookfor;
+	lookfor.name = name;
+	lookfor.namelen = namelen;
+	lookfor.key_class = key_class;
+	key_entry_hash(&lookfor);
+	e = slabhash_lookup(kcache->slab, lookfor.entry.hash, &lookfor, wr);
+	if(!e) 
+		return NULL;
+	return (struct key_entry_key*)e->key;
+}
+
+struct key_entry_key* 
+key_cache_obtain(struct key_cache* kcache, uint8_t* name, size_t namelen, 
+	uint16_t key_class, struct regional* region, uint32_t now)
+{
+	/* keep looking until we find a nonexpired entry */
+	while(1) {
+		struct key_entry_key* k = key_cache_search(kcache, name, 
+			namelen, key_class, 0);
+		if(k) {
+			/* see if TTL is OK */
+			struct key_entry_data* d = (struct key_entry_data*)
+				k->entry.data;
+			if(now <= d->ttl) {
+				/* copy and return it */
+				struct key_entry_key* retkey =
+					key_entry_copy_toregion(k, region);
+				lock_rw_unlock(&k->entry.lock);
+				return retkey;
+			}
+			lock_rw_unlock(&k->entry.lock);
+		}
+		/* snip off first label to continue */
+		if(dname_is_root(name))
+			break;
+		dname_remove_label(&name, &namelen);
+	}
+	return NULL;
+}
+
+size_t 
+key_cache_get_mem(struct key_cache* kcache)
+{
+	return sizeof(*kcache) + slabhash_get_mem(kcache->slab);
+}
+
+void key_cache_remove(struct key_cache* kcache,
+	uint8_t* name, size_t namelen, uint16_t key_class)
+{
+	struct key_entry_key lookfor;
+	lookfor.entry.key = &lookfor;
+	lookfor.name = name;
+	lookfor.namelen = namelen;
+	lookfor.key_class = key_class;
+	key_entry_hash(&lookfor);
+	slabhash_remove(kcache->slab, lookfor.entry.hash, &lookfor);
+}
diff --git a/3rdParty/Unbound/src/src/validator/val_kcache.h b/3rdParty/Unbound/src/src/validator/val_kcache.h
new file mode 100644
index 0000000..c37cf1e
--- /dev/null
+++ b/3rdParty/Unbound/src/src/validator/val_kcache.h
@@ -0,0 +1,118 @@
+/*
+ * validator/val_kcache.h - validator key shared cache with validated keys
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions for caching validated key entries. 
+ */
+
+#ifndef VALIDATOR_VAL_KCACHE_H
+#define VALIDATOR_VAL_KCACHE_H
+#include "util/storage/slabhash.h"
+struct key_entry_key;
+struct key_entry_data;
+struct config_file;
+struct regional;
+struct module_qstate;
+
+/**
+ * Key cache
+ */
+struct key_cache {
+	/** uses slabhash for storage, type key_entry_key, key_entry_data */
+	struct slabhash* slab;
+};
+
+/**
+ * Create the key cache
+ * @param cfg: config settings for the key cache.
+ * @return new key cache or NULL on malloc failure.
+ */
+struct key_cache* key_cache_create(struct config_file* cfg);
+
+/**
+ * Delete the key cache
+ * @param kcache: to delete
+ */
+void key_cache_delete(struct key_cache* kcache);
+
+/**
+ * Insert or update a key cache entry. Note that the insert may silently
+ * fail if there is not enough memory.
+ *
+ * @param kcache: the key cache.
+ * @param kkey: key entry key, assumed malloced in a region, is copied
+ * 	to perform update or insertion. Its data pointer is also copied.
+ * @param qstate: store errinf reason in case its bad.
+ */
+void key_cache_insert(struct key_cache* kcache, struct key_entry_key* kkey,
+	struct module_qstate* qstate);
+
+/**
+ * Remove an entry from the key cache.
+ * @param kcache: the key cache.
+ * @param name: for what name to look; uncompressed wireformat
+ * @param namelen: length of the name.
+ * @param key_class: class of the key.
+ */
+void key_cache_remove(struct key_cache* kcache,
+	uint8_t* name, size_t namelen, uint16_t key_class);
+
+/**
+ * Lookup key entry in the cache. Looks up the closest key entry above the
+ * given name.
+ * @param kcache: the key cache.
+ * @param name: for what name to look; uncompressed wireformat
+ * @param namelen: length of the name.
+ * @param key_class: class of the key.
+ * @param region: a copy of the key_entry is allocated in this region.
+ * @param now: current time.
+ * @return pointer to a newly allocated key_entry copy in the region, if
+ * 	a key entry could be found, and allocation succeeded and TTL was OK.
+ * 	Otherwise, NULL is returned.
+ */
+struct key_entry_key* key_cache_obtain(struct key_cache* kcache,
+	uint8_t* name, size_t namelen, uint16_t key_class, 
+	struct regional* region, uint32_t now);
+
+/**
+ * Get memory in use by the key cache.
+ * @param kcache: the key cache.
+ * @return memory in use in bytes.
+ */
+size_t key_cache_get_mem(struct key_cache* kcache);
+
+#endif /* VALIDATOR_VAL_KCACHE_H */
diff --git a/3rdParty/Unbound/src/src/validator/val_kentry.c b/3rdParty/Unbound/src/src/validator/val_kentry.c
new file mode 100644
index 0000000..ddac140
--- /dev/null
+++ b/3rdParty/Unbound/src/src/validator/val_kentry.c
@@ -0,0 +1,412 @@
+/*
+ * validator/val_kentry.c - validator key entry definition.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions for dealing with validator key entries.
+ */
+#include "config.h"
+#include <ldns/ldns.h>
+#include "validator/val_kentry.h"
+#include "util/data/packed_rrset.h"
+#include "util/data/dname.h"
+#include "util/storage/lookup3.h"
+#include "util/regional.h"
+#include "util/net_help.h"
+
+size_t 
+key_entry_sizefunc(void* key, void* data)
+{
+	struct key_entry_key* kk = (struct key_entry_key*)key;
+	struct key_entry_data* kd = (struct key_entry_data*)data;
+	size_t s = sizeof(*kk) + kk->namelen;
+	s += sizeof(*kd) + lock_get_mem(&kk->entry.lock);
+	if(kd->rrset_data)
+		s += packed_rrset_sizeof(kd->rrset_data);
+	if(kd->reason)
+		s += strlen(kd->reason)+1;
+	if(kd->algo)
+		s += strlen((char*)kd->algo)+1;
+	return s;
+}
+
+int 
+key_entry_compfunc(void* k1, void* k2)
+{
+	struct key_entry_key* n1 = (struct key_entry_key*)k1;
+	struct key_entry_key* n2 = (struct key_entry_key*)k2;
+	if(n1->key_class != n2->key_class) {
+		if(n1->key_class < n2->key_class)
+			return -1;
+		return 1;
+	}
+	return query_dname_compare(n1->name, n2->name);
+}
+
+void 
+key_entry_delkeyfunc(void* key, void* ATTR_UNUSED(userarg))
+{
+	struct key_entry_key* kk = (struct key_entry_key*)key;
+	if(!key)
+		return;
+	lock_rw_destroy(&kk->entry.lock);
+	free(kk->name);
+	free(kk);
+}
+
+void 
+key_entry_deldatafunc(void* data, void* ATTR_UNUSED(userarg))
+{
+	struct key_entry_data* kd = (struct key_entry_data*)data;
+	free(kd->reason);
+	free(kd->rrset_data);
+	free(kd->algo);
+	free(kd);
+}
+
+void 
+key_entry_hash(struct key_entry_key* kk)
+{
+	kk->entry.hash = 0x654;
+	kk->entry.hash = hashlittle(&kk->key_class, sizeof(kk->key_class), 
+		kk->entry.hash);
+	kk->entry.hash = dname_query_hash(kk->name, kk->entry.hash);
+}
+
+struct key_entry_key* 
+key_entry_copy_toregion(struct key_entry_key* kkey, struct regional* region)
+{
+	struct key_entry_key* newk;
+	newk = regional_alloc_init(region, kkey, sizeof(*kkey));
+	if(!newk)
+		return NULL;
+	newk->name = regional_alloc_init(region, kkey->name, kkey->namelen);
+	if(!newk->name)
+		return NULL;
+	newk->entry.key = newk;
+	if(newk->entry.data) {
+		/* copy data element */
+		struct key_entry_data *d = (struct key_entry_data*)
+			kkey->entry.data;
+		struct key_entry_data *newd;
+		newd = regional_alloc_init(region, d, sizeof(*d));
+		if(!newd)
+			return NULL;
+		/* copy rrset */
+		if(d->rrset_data) {
+			newd->rrset_data = regional_alloc_init(region,
+				d->rrset_data, 
+				packed_rrset_sizeof(d->rrset_data));
+			if(!newd->rrset_data)
+				return NULL;
+			packed_rrset_ptr_fixup(newd->rrset_data);
+		}
+		if(d->reason) {
+			newd->reason = regional_strdup(region, d->reason);
+			if(!newd->reason)
+				return NULL;
+		}
+		if(d->algo) {
+			newd->algo = (uint8_t*)regional_strdup(region,
+				(char*)d->algo);
+			if(!newd->algo)
+				return NULL;
+		}
+		newk->entry.data = newd;
+	}
+	return newk;
+}
+
+struct key_entry_key* 
+key_entry_copy(struct key_entry_key* kkey)
+{
+	struct key_entry_key* newk;
+	if(!kkey)
+		return NULL;
+	newk = memdup(kkey, sizeof(*kkey));
+	if(!newk)
+		return NULL;
+	newk->name = memdup(kkey->name, kkey->namelen);
+	if(!newk->name) {
+		free(newk);
+		return NULL;
+	}
+	lock_rw_init(&newk->entry.lock);
+	newk->entry.key = newk;
+	if(newk->entry.data) {
+		/* copy data element */
+		struct key_entry_data *d = (struct key_entry_data*)
+			kkey->entry.data;
+		struct key_entry_data *newd;
+		newd = memdup(d, sizeof(*d));
+		if(!newd) {
+			free(newk->name);
+			free(newk);
+			return NULL;
+		}
+		/* copy rrset */
+		if(d->rrset_data) {
+			newd->rrset_data = memdup(d->rrset_data, 
+				packed_rrset_sizeof(d->rrset_data));
+			if(!newd->rrset_data) {
+				free(newd);
+				free(newk->name);
+				free(newk);
+				return NULL;
+			}
+			packed_rrset_ptr_fixup(newd->rrset_data);
+		}
+		if(d->reason) {
+			newd->reason = strdup(d->reason);
+			if(!newd->reason) {
+				free(newd->rrset_data);
+				free(newd);
+				free(newk->name);
+				free(newk);
+				return NULL;
+			}
+		}
+		if(d->algo) {
+			newd->algo = (uint8_t*)strdup((char*)d->algo);
+			if(!newd->algo) {
+				free(newd->rrset_data);
+				free(newd->reason);
+				free(newd);
+				free(newk->name);
+				free(newk);
+				return NULL;
+			}
+		}
+		newk->entry.data = newd;
+	}
+	return newk;
+}
+
+int 
+key_entry_isnull(struct key_entry_key* kkey)
+{
+	struct key_entry_data* d = (struct key_entry_data*)kkey->entry.data;
+	return (!d->isbad && d->rrset_data == NULL);
+}
+
+int 
+key_entry_isgood(struct key_entry_key* kkey)
+{
+	struct key_entry_data* d = (struct key_entry_data*)kkey->entry.data;
+	return (!d->isbad && d->rrset_data != NULL);
+}
+
+int 
+key_entry_isbad(struct key_entry_key* kkey)
+{
+	struct key_entry_data* d = (struct key_entry_data*)kkey->entry.data;
+	return (int)(d->isbad);
+}
+
+void
+key_entry_set_reason(struct key_entry_key* kkey, char* reason)
+{
+	struct key_entry_data* d = (struct key_entry_data*)kkey->entry.data;
+	d->reason = reason;
+}
+
+char*
+key_entry_get_reason(struct key_entry_key* kkey)
+{
+	struct key_entry_data* d = (struct key_entry_data*)kkey->entry.data;
+	return d->reason;
+}
+
+/** setup key entry in region */
+static int
+key_entry_setup(struct regional* region,
+	uint8_t* name, size_t namelen, uint16_t dclass, 
+	struct key_entry_key** k, struct key_entry_data** d)
+{
+	*k = regional_alloc(region, sizeof(**k));
+	if(!*k)
+		return 0;
+	memset(*k, 0, sizeof(**k));
+	(*k)->entry.key = *k;
+	(*k)->name = regional_alloc_init(region, name, namelen);
+	if(!(*k)->name)
+		return 0;
+	(*k)->namelen = namelen;
+	(*k)->key_class = dclass;
+	*d = regional_alloc(region, sizeof(**d));
+	if(!*d)
+		return 0;
+	(*k)->entry.data = *d;
+	return 1;
+}
+
+struct key_entry_key* 
+key_entry_create_null(struct regional* region,
+	uint8_t* name, size_t namelen, uint16_t dclass, uint32_t ttl,
+	uint32_t now)
+{
+	struct key_entry_key* k;
+	struct key_entry_data* d;
+	if(!key_entry_setup(region, name, namelen, dclass, &k, &d))
+		return NULL;
+	d->ttl = now + ttl;
+	d->isbad = 0;
+	d->reason = NULL;
+	d->rrset_type = LDNS_RR_TYPE_DNSKEY;
+	d->rrset_data = NULL;
+	d->algo = NULL;
+	return k;
+}
+
+struct key_entry_key* 
+key_entry_create_rrset(struct regional* region,
+	uint8_t* name, size_t namelen, uint16_t dclass,
+	struct ub_packed_rrset_key* rrset, uint8_t* sigalg, uint32_t now)
+{
+	struct key_entry_key* k;
+	struct key_entry_data* d;
+	struct packed_rrset_data* rd = (struct packed_rrset_data*)
+		rrset->entry.data;
+	if(!key_entry_setup(region, name, namelen, dclass, &k, &d))
+		return NULL;
+	d->ttl = rd->ttl + now;
+	d->isbad = 0;
+	d->reason = NULL;
+	d->rrset_type = ntohs(rrset->rk.type);
+	d->rrset_data = (struct packed_rrset_data*)regional_alloc_init(region,
+		rd, packed_rrset_sizeof(rd));
+	if(!d->rrset_data)
+		return NULL;
+	if(sigalg) {
+		d->algo = (uint8_t*)regional_strdup(region, (char*)sigalg);
+		if(!d->algo)
+			return NULL;
+	} else d->algo = NULL;
+	packed_rrset_ptr_fixup(d->rrset_data);
+	return k;
+}
+
+struct key_entry_key* 
+key_entry_create_bad(struct regional* region,
+	uint8_t* name, size_t namelen, uint16_t dclass, uint32_t ttl, 
+	uint32_t now)
+{
+	struct key_entry_key* k;
+	struct key_entry_data* d;
+	if(!key_entry_setup(region, name, namelen, dclass, &k, &d))
+		return NULL;
+	d->ttl = now + ttl;
+	d->isbad = 1;
+	d->reason = NULL;
+	d->rrset_type = LDNS_RR_TYPE_DNSKEY;
+	d->rrset_data = NULL;
+	d->algo = NULL;
+	return k;
+}
+
+struct ub_packed_rrset_key* 
+key_entry_get_rrset(struct key_entry_key* kkey, struct regional* region)
+{
+	struct key_entry_data* d = (struct key_entry_data*)kkey->entry.data;
+	struct ub_packed_rrset_key* rrk;
+	struct packed_rrset_data* rrd;
+	if(!d || !d->rrset_data)
+		return NULL;
+	rrk = regional_alloc(region, sizeof(*rrk));
+	if(!rrk)
+		return NULL;
+	memset(rrk, 0, sizeof(*rrk));
+	rrk->rk.dname = regional_alloc_init(region, kkey->name, kkey->namelen);
+	if(!rrk->rk.dname)
+		return NULL;
+	rrk->rk.dname_len = kkey->namelen;
+	rrk->rk.type = htons(d->rrset_type);
+	rrk->rk.rrset_class = htons(kkey->key_class);
+	rrk->entry.key = rrk;
+	rrd = regional_alloc_init(region, d->rrset_data, 
+		packed_rrset_sizeof(d->rrset_data));
+	if(!rrd)
+		return NULL;
+	rrk->entry.data = rrd;
+	packed_rrset_ptr_fixup(rrd);
+	return rrk;
+}
+
+/** Get size of key in keyset */
+static size_t
+dnskey_get_keysize(struct packed_rrset_data* data, size_t idx)
+{
+	unsigned char* pk;
+	unsigned int pklen = 0;
+	int algo;
+	if(data->rr_len[idx] < 2+5)
+		return 0;
+	algo = (int)data->rr_data[idx][2+3];
+	pk = (unsigned char*)data->rr_data[idx]+2+4;
+	pklen = (unsigned)data->rr_len[idx]-2-4;
+	return ldns_rr_dnskey_key_size_raw(pk, pklen, algo);
+}
+
+/** get dnskey flags from data */
+static uint16_t
+kd_get_flags(struct packed_rrset_data* data, size_t idx)
+{
+	uint16_t f;
+	if(data->rr_len[idx] < 2+2)
+		return 0;
+	memmove(&f, data->rr_data[idx]+2, 2);
+	f = ntohs(f);
+	return f;
+}
+
+size_t 
+key_entry_keysize(struct key_entry_key* kkey)
+{
+	struct packed_rrset_data* d;
+	/* compute size of smallest ZSK key in the rrset */
+	size_t i;
+	size_t bits = 0;
+	if(!key_entry_isgood(kkey))
+		return 0;
+	d = ((struct key_entry_data*)kkey->entry.data)->rrset_data;
+	for(i=0; i<d->count; i++) {
+		if(!(kd_get_flags(d, i) & DNSKEY_BIT_ZSK))
+			continue;
+		if(i==0 || dnskey_get_keysize(d, i) < bits)
+			bits = dnskey_get_keysize(d, i);
+	}
+	return bits;
+}
diff --git a/3rdParty/Unbound/src/src/validator/val_kentry.h b/3rdParty/Unbound/src/src/validator/val_kentry.h
new file mode 100644
index 0000000..d14ffe5
--- /dev/null
+++ b/3rdParty/Unbound/src/src/validator/val_kentry.h
@@ -0,0 +1,220 @@
+/*
+ * validator/val_kentry.h - validator key entry definition.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions for dealing with validator key entries.
+ */
+
+#ifndef VALIDATOR_VAL_KENTRY_H
+#define VALIDATOR_VAL_KENTRY_H
+struct packed_rrset_data;
+struct regional;
+struct ub_packed_rrset_key;
+#include "util/storage/lruhash.h"
+
+/**
+ * A key entry for the validator.
+ * This may or may not be a trusted key.
+ * This is what is stored in the key cache.
+ * This is the key part for the cache; the key entry key.
+ */
+struct key_entry_key {
+	/** lru hash entry */
+	struct lruhash_entry entry;
+	/** name of the key */
+	uint8_t* name;
+	/** length of name */
+	size_t namelen;
+	/** class of the key, host byteorder */
+	uint16_t key_class;
+};
+
+/**
+ * Key entry for the validator.
+ * Contains key status.
+ * This is the data part for the cache, the key entry data.
+ *
+ * Can be in three basic states:
+ * 	isbad=0:		good key
+ * 	isbad=1:		bad key
+ * 	isbad=0 && rrset=0:	insecure space.
+ */
+struct key_entry_data {
+	/** the TTL of this entry (absolute time) */
+	uint32_t ttl;
+	/** the key rrdata. can be NULL to signal keyless name. */
+	struct packed_rrset_data* rrset_data;
+	/** not NULL sometimes to give reason why bogus */
+	char* reason;
+	/** list of algorithms signalled, ends with 0, or NULL */
+	uint8_t* algo;
+	/** DNS RR type of the rrset data (host order) */
+	uint16_t rrset_type;
+	/** if the key is bad: Bogus or malformed */
+	uint8_t isbad;
+};
+
+/** function for lruhash operation */
+size_t key_entry_sizefunc(void* key, void* data);
+
+/** function for lruhash operation */
+int key_entry_compfunc(void* k1, void* k2);
+
+/** function for lruhash operation */
+void key_entry_delkeyfunc(void* key, void* userarg);
+
+/** function for lruhash operation */
+void key_entry_deldatafunc(void* data, void* userarg);
+
+/** calculate hash for key entry 
+ * @param kk: key entry. The lruhash entry.hash value is filled in.
+ */
+void key_entry_hash(struct key_entry_key* kk);
+
+/**
+ * Copy a key entry, to be region-allocated.
+ * @param kkey: the key entry key (and data pointer) to copy.
+ * @param region: where to allocate it
+ * @return newly region-allocated entry or NULL on a failure to allocate.
+ */
+struct key_entry_key* key_entry_copy_toregion(struct key_entry_key* kkey, 
+	struct regional* region);
+
+/**
+ * Copy a key entry, malloced.
+ * @param kkey: the key entry key (and data pointer) to copy.
+ * @return newly allocated entry or NULL on a failure to allocate memory.
+ */
+struct key_entry_key* key_entry_copy(struct key_entry_key* kkey);
+
+/**
+ * See if this is a null entry. Does not do locking.
+ * @param kkey: must have data pointer set correctly
+ * @return true if it is a NULL rrset entry.
+ */
+int key_entry_isnull(struct key_entry_key* kkey);
+
+/**
+ * See if this entry is good. Does not do locking.
+ * @param kkey: must have data pointer set correctly
+ * @return true if it is good.
+ */
+int key_entry_isgood(struct key_entry_key* kkey);
+
+/**
+ * See if this entry is bad. Does not do locking.
+ * @param kkey: must have data pointer set correctly
+ * @return true if it is bad.
+ */
+int key_entry_isbad(struct key_entry_key* kkey);
+
+/**
+ * Set reason why a key is bad.
+ * @param kkey: bad key.
+ * @param reason: string to attach, you must allocate it.
+ *    Not safe to call twice unless you deallocate it yourself.
+ */
+void key_entry_set_reason(struct key_entry_key* kkey, char* reason);
+
+/**
+ * Get reason why a key is bad.
+ * @param kkey: bad key
+ * @return pointer to string.
+ *    String is part of key entry and is deleted with it.
+ */
+char* key_entry_get_reason(struct key_entry_key* kkey);
+
+/**
+ * Create a null entry, in the given region.
+ * @param region: where to allocate
+ * @param name: the key name
+ * @param namelen: length of name
+ * @param dclass: class of key entry. (host order);
+ * @param ttl: what ttl should the key have. relative.
+ * @param now: current time (added to ttl).
+ * @return new key entry or NULL on alloc failure
+ */
+struct key_entry_key* key_entry_create_null(struct regional* region,
+	uint8_t* name, size_t namelen, uint16_t dclass, uint32_t ttl, 
+	uint32_t now);
+
+/**
+ * Create a key entry from an rrset, in the given region.
+ * @param region: where to allocate.
+ * @param name: the key name
+ * @param namelen: length of name
+ * @param dclass: class of key entry. (host order);
+ * @param rrset: data for key entry. This is copied to the region.
+ * @param sigalg: signalled algorithm list (or NULL).
+ * @param now: current time (added to ttl of rrset)
+ * @return new key entry or NULL on alloc failure
+ */
+struct key_entry_key* key_entry_create_rrset(struct regional* region,
+        uint8_t* name, size_t namelen, uint16_t dclass, 
+	struct ub_packed_rrset_key* rrset, uint8_t* sigalg, uint32_t now);
+
+/**
+ * Create a bad entry, in the given region.
+ * @param region: where to allocate
+ * @param name: the key name
+ * @param namelen: length of name
+ * @param dclass: class of key entry. (host order);
+ * @param ttl: what ttl should the key have. relative.
+ * @param now: current time (added to ttl).
+ * @return new key entry or NULL on alloc failure
+ */
+struct key_entry_key* key_entry_create_bad(struct regional* region,
+	uint8_t* name, size_t namelen, uint16_t dclass, uint32_t ttl,
+	uint32_t now);
+
+/**
+ * Obtain rrset from a key entry, allocated in region.
+ * @param kkey: key entry to convert to a rrset.
+ * @param region: where to allocate rrset
+ * @return rrset copy; if no rrset or alloc error returns NULL.
+ */
+struct ub_packed_rrset_key* key_entry_get_rrset(struct key_entry_key* kkey,
+	struct regional* region);
+
+/**
+ * Get keysize of the keyentry.
+ * @param kkey: key, must be a good key, with contents.
+ * @return size in bits of the key.
+ */
+size_t key_entry_keysize(struct key_entry_key* kkey);
+
+#endif /* VALIDATOR_VAL_KENTRY_H */
diff --git a/3rdParty/Unbound/src/src/validator/val_neg.c b/3rdParty/Unbound/src/src/validator/val_neg.c
new file mode 100644
index 0000000..60434db
--- /dev/null
+++ b/3rdParty/Unbound/src/src/validator/val_neg.c
@@ -0,0 +1,1455 @@
+/*
+ * validator/val_neg.c - validator aggressive negative caching functions.
+ *
+ * Copyright (c) 2008, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains helper functions for the validator module.
+ * The functions help with aggressive negative caching.
+ * This creates new denials of existance, and proofs for absence of types
+ * from cached NSEC records.
+ */
+#include "config.h"
+#ifdef HAVE_OPENSSL_SSL_H
+#include "openssl/ssl.h"
+#endif
+#include "validator/val_neg.h"
+#include "validator/val_nsec.h"
+#include "validator/val_nsec3.h"
+#include "validator/val_utils.h"
+#include "util/data/dname.h"
+#include "util/data/msgreply.h"
+#include "util/log.h"
+#include "util/net_help.h"
+#include "util/config_file.h"
+#include "services/cache/rrset.h"
+#include "services/cache/dns.h"
+
+int val_neg_data_compare(const void* a, const void* b)
+{
+	struct val_neg_data* x = (struct val_neg_data*)a;
+	struct val_neg_data* y = (struct val_neg_data*)b;
+	int m;
+	return dname_canon_lab_cmp(x->name, x->labs, y->name, y->labs, &m);
+}
+
+int val_neg_zone_compare(const void* a, const void* b)
+{
+	struct val_neg_zone* x = (struct val_neg_zone*)a;
+	struct val_neg_zone* y = (struct val_neg_zone*)b;
+	int m;
+	if(x->dclass != y->dclass) {
+		if(x->dclass < y->dclass)
+			return -1;
+		return 1;
+	}
+	return dname_canon_lab_cmp(x->name, x->labs, y->name, y->labs, &m);
+}
+
+struct val_neg_cache* val_neg_create(struct config_file* cfg, size_t maxiter)
+{
+	struct val_neg_cache* neg = (struct val_neg_cache*)calloc(1, 
+		sizeof(*neg));
+	if(!neg) {
+		log_err("Could not create neg cache: out of memory");
+		return NULL;
+	}
+	neg->nsec3_max_iter = maxiter;
+	neg->max = 1024*1024; /* 1 M is thousands of entries */
+	if(cfg) neg->max = cfg->neg_cache_size;
+	rbtree_init(&neg->tree, &val_neg_zone_compare);
+	lock_basic_init(&neg->lock);
+	lock_protect(&neg->lock, neg, sizeof(*neg));
+	return neg;
+}
+
+size_t val_neg_get_mem(struct val_neg_cache* neg)
+{
+	size_t result;
+	lock_basic_lock(&neg->lock);
+	result = sizeof(*neg) + neg->use;
+	lock_basic_unlock(&neg->lock);
+	return result;
+}
+
+/** clear datas on cache deletion */
+static void
+neg_clear_datas(rbnode_t* n, void* ATTR_UNUSED(arg))
+{
+	struct val_neg_data* d = (struct val_neg_data*)n;
+	free(d->name);
+	free(d);
+}
+
+/** clear zones on cache deletion */
+static void
+neg_clear_zones(rbnode_t* n, void* ATTR_UNUSED(arg))
+{
+	struct val_neg_zone* z = (struct val_neg_zone*)n;
+	/* delete all the rrset entries in the tree */
+	traverse_postorder(&z->tree, &neg_clear_datas, NULL);
+	free(z->nsec3_salt);
+	free(z->name);
+	free(z);
+}
+
+void neg_cache_delete(struct val_neg_cache* neg)
+{
+	if(!neg) return;
+	lock_basic_destroy(&neg->lock);
+	/* delete all the zones in the tree */
+	traverse_postorder(&neg->tree, &neg_clear_zones, NULL);
+	free(neg);
+}
+
+/**
+ * Put data element at the front of the LRU list.
+ * @param neg: negative cache with LRU start and end.
+ * @param data: this data is fronted.
+ */
+static void neg_lru_front(struct val_neg_cache* neg, 
+	struct val_neg_data* data)
+{
+	data->prev = NULL;
+	data->next = neg->first;
+	if(!neg->first)
+		neg->last = data;
+	else	neg->first->prev = data;
+	neg->first = data;
+}
+
+/**
+ * Remove data element from LRU list.
+ * @param neg: negative cache with LRU start and end.
+ * @param data: this data is removed from the list.
+ */
+static void neg_lru_remove(struct val_neg_cache* neg, 
+	struct val_neg_data* data)
+{
+	if(data->prev)
+		data->prev->next = data->next;
+	else	neg->first = data->next;
+	if(data->next)
+		data->next->prev = data->prev;
+	else	neg->last = data->prev;
+}
+
+/**
+ * Touch LRU for data element, put it at the start of the LRU list.
+ * @param neg: negative cache with LRU start and end.
+ * @param data: this data is used.
+ */
+static void neg_lru_touch(struct val_neg_cache* neg, 
+	struct val_neg_data* data)
+{
+	if(data == neg->first)
+		return; /* nothing to do */
+	/* remove from current lru position */
+	neg_lru_remove(neg, data);
+	/* add at front */
+	neg_lru_front(neg, data);
+}
+
+/**
+ * Delete a zone element from the negative cache.
+ * May delete other zone elements to keep tree coherent, or
+ * only mark the element as 'not in use'.
+ * @param neg: negative cache.
+ * @param z: zone element to delete.
+ */
+static void neg_delete_zone(struct val_neg_cache* neg, struct val_neg_zone* z)
+{
+	struct val_neg_zone* p, *np;
+	if(!z) return;
+	log_assert(z->in_use);
+	log_assert(z->count > 0);
+	z->in_use = 0;
+
+	/* go up the tree and reduce counts */
+	p = z;
+	while(p) {
+		log_assert(p->count > 0);
+		p->count --;
+		p = p->parent;
+	}
+
+	/* remove zones with zero count */
+	p = z;
+	while(p && p->count == 0) {
+		np = p->parent;
+		(void)rbtree_delete(&neg->tree, &p->node);
+		neg->use -= p->len + sizeof(*p);
+		free(p->nsec3_salt);
+		free(p->name);
+		free(p);
+		p = np;
+	}
+}
+	
+void neg_delete_data(struct val_neg_cache* neg, struct val_neg_data* el)
+{
+	struct val_neg_zone* z;
+	struct val_neg_data* p, *np;
+	if(!el) return;
+	z = el->zone;
+	log_assert(el->in_use);
+	log_assert(el->count > 0);
+	el->in_use = 0;
+
+	/* remove it from the lru list */
+	neg_lru_remove(neg, el);
+	
+	/* go up the tree and reduce counts */
+	p = el;
+	while(p) {
+		log_assert(p->count > 0);
+		p->count --;
+		p = p->parent;
+	}
+
+	/* delete 0 count items from tree */
+	p = el;
+	while(p && p->count == 0) {
+		np = p->parent;
+		(void)rbtree_delete(&z->tree, &p->node);
+		neg->use -= p->len + sizeof(*p);
+		free(p->name);
+		free(p);
+		p = np;
+	}
+
+	/* check if the zone is now unused */
+	if(z->tree.count == 0) {
+		neg_delete_zone(neg, z);
+	}
+}
+
+/**
+ * Create more space in negative cache
+ * The oldest elements are deleted until enough space is present.
+ * Empty zones are deleted.
+ * @param neg: negative cache.
+ * @param need: how many bytes are needed.
+ */
+static void neg_make_space(struct val_neg_cache* neg, size_t need)
+{
+	/* delete elements until enough space or its empty */
+	while(neg->last && neg->max < neg->use + need) {
+		neg_delete_data(neg, neg->last);
+	}
+}
+
+struct val_neg_zone* neg_find_zone(struct val_neg_cache* neg, 
+	uint8_t* nm, size_t len, uint16_t dclass)
+{
+	struct val_neg_zone lookfor;
+	struct val_neg_zone* result;
+	lookfor.node.key = &lookfor;
+	lookfor.name = nm;
+	lookfor.len = len;
+	lookfor.labs = dname_count_labels(lookfor.name);
+	lookfor.dclass = dclass;
+
+	result = (struct val_neg_zone*)
+		rbtree_search(&neg->tree, lookfor.node.key);
+	return result;
+}
+
+/**
+ * Find the given data
+ * @param zone: negative zone
+ * @param nm: what to look for.
+ * @param len: length of nm
+ * @param labs: labels in nm
+ * @return data or NULL if not found.
+ */
+static struct val_neg_data* neg_find_data(struct val_neg_zone* zone, 
+	uint8_t* nm, size_t len, int labs)
+{
+	struct val_neg_data lookfor;
+	struct val_neg_data* result;
+	lookfor.node.key = &lookfor;
+	lookfor.name = nm;
+	lookfor.len = len;
+	lookfor.labs = labs;
+
+	result = (struct val_neg_data*)
+		rbtree_search(&zone->tree, lookfor.node.key);
+	return result;
+}
+
+/**
+ * Calculate space needed for the data and all its parents
+ * @param rep: NSEC entries.
+ * @return size.
+ */
+static size_t calc_data_need(struct reply_info* rep)
+{
+	uint8_t* d;
+	size_t i, len, res = 0;
+
+	for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep->ns_numrrsets; i++) {
+		if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC) {
+			d = rep->rrsets[i]->rk.dname;
+			len = rep->rrsets[i]->rk.dname_len;
+			res = sizeof(struct val_neg_data) + len;
+			while(!dname_is_root(d)) {
+				log_assert(len > 1); /* not root label */
+				dname_remove_label(&d, &len);
+				res += sizeof(struct val_neg_data) + len;
+			}
+		}
+	}
+	return res;
+}
+
+/**
+ * Calculate space needed for zone and all its parents
+ * @param d: name of zone
+ * @param len: length of name
+ * @return size.
+ */
+static size_t calc_zone_need(uint8_t* d, size_t len)
+{
+	size_t res = sizeof(struct val_neg_zone) + len;
+	while(!dname_is_root(d)) {
+		log_assert(len > 1); /* not root label */
+		dname_remove_label(&d, &len);
+		res += sizeof(struct val_neg_zone) + len;
+	}
+	return res;
+}
+
+/**
+ * Find closest existing parent zone of the given name.
+ * @param neg: negative cache.
+ * @param nm: name to look for
+ * @param nm_len: length of nm
+ * @param labs: labelcount of nm.
+ * @param qclass: class.
+ * @return the zone or NULL if none found.
+ */
+static struct val_neg_zone* neg_closest_zone_parent(struct val_neg_cache* neg,
+	uint8_t* nm, size_t nm_len, int labs, uint16_t qclass)
+{
+	struct val_neg_zone key;
+	struct val_neg_zone* result;
+	rbnode_t* res = NULL;
+	key.node.key = &key;
+	key.name = nm;
+	key.len = nm_len;
+	key.labs = labs;
+	key.dclass = qclass;
+	if(rbtree_find_less_equal(&neg->tree, &key, &res)) {
+		/* exact match */
+		result = (struct val_neg_zone*)res;
+	} else {
+		/* smaller element (or no element) */
+		int m;
+		result = (struct val_neg_zone*)res;
+		if(!result || result->dclass != qclass)
+			return NULL;
+		/* count number of labels matched */
+		(void)dname_lab_cmp(result->name, result->labs, key.name,
+			key.labs, &m);
+		while(result) { /* go up until qname is subdomain of stub */
+			if(result->labs <= m)
+				break;
+			result = result->parent;
+		}
+	}
+	return result;
+}
+
+/**
+ * Find closest existing parent data for the given name.
+ * @param zone: to look in.
+ * @param nm: name to look for
+ * @param nm_len: length of nm
+ * @param labs: labelcount of nm.
+ * @return the data or NULL if none found.
+ */
+static struct val_neg_data* neg_closest_data_parent(
+	struct val_neg_zone* zone, uint8_t* nm, size_t nm_len, int labs)
+{
+	struct val_neg_data key;
+	struct val_neg_data* result;
+	rbnode_t* res = NULL;
+	key.node.key = &key;
+	key.name = nm;
+	key.len = nm_len;
+	key.labs = labs;
+	if(rbtree_find_less_equal(&zone->tree, &key, &res)) {
+		/* exact match */
+		result = (struct val_neg_data*)res;
+	} else {
+		/* smaller element (or no element) */
+		int m;
+		result = (struct val_neg_data*)res;
+		if(!result)
+			return NULL;
+		/* count number of labels matched */
+		(void)dname_lab_cmp(result->name, result->labs, key.name,
+			key.labs, &m);
+		while(result) { /* go up until qname is subdomain of stub */
+			if(result->labs <= m)
+				break;
+			result = result->parent;
+		}
+	}
+	return result;
+}
+
+/**
+ * Create a single zone node
+ * @param nm: name for zone (copied)
+ * @param nm_len: length of name
+ * @param labs: labels in name.
+ * @param dclass: class of zone, host order.
+ * @return new zone or NULL on failure
+ */
+static struct val_neg_zone* neg_setup_zone_node(
+	uint8_t* nm, size_t nm_len, int labs, uint16_t dclass)
+{
+	struct val_neg_zone* zone = 
+		(struct val_neg_zone*)calloc(1, sizeof(*zone));
+	if(!zone) {
+		return NULL;
+	}
+	zone->node.key = zone;
+	zone->name = memdup(nm, nm_len);
+	if(!zone->name) {
+		free(zone);
+		return NULL;
+	}
+	zone->len = nm_len;
+	zone->labs = labs;
+	zone->dclass = dclass;
+
+	rbtree_init(&zone->tree, &val_neg_data_compare);
+	return zone;
+}
+
+/**
+ * Create a linked list of parent zones, starting at longname ending on
+ * the parent (can be NULL, creates to the root).
+ * @param nm: name for lowest in chain
+ * @param nm_len: length of name
+ * @param labs: labels in name.
+ * @param dclass: class of zone.
+ * @param parent: NULL for to root, else so it fits under here.
+ * @return zone; a chain of zones and their parents up to the parent.
+ *  	or NULL on malloc failure
+ */
+static struct val_neg_zone* neg_zone_chain(
+	uint8_t* nm, size_t nm_len, int labs, uint16_t dclass,
+	struct val_neg_zone* parent)
+{
+	int i;
+	int tolabs = parent?parent->labs:0;
+	struct val_neg_zone* zone, *prev = NULL, *first = NULL;
+
+	/* create the new subtree, i is labelcount of current creation */
+	/* this creates a 'first' to z->parent=NULL list of zones */
+	for(i=labs; i!=tolabs; i--) {
+		/* create new item */
+		zone = neg_setup_zone_node(nm, nm_len, i, dclass);
+		if(!zone) {
+			/* need to delete other allocations in this routine!*/
+			struct val_neg_zone* p=first, *np;
+			while(p) {
+				np = p->parent;
+				free(p);
+				free(p->name);
+				p = np;
+			}
+			return NULL;
+		}
+		if(i == labs) {
+			first = zone;
+		} else {
+			prev->parent = zone;
+		}
+		/* prepare for next name */
+		prev = zone;
+		dname_remove_label(&nm, &nm_len);
+	}
+	return first;
+}	
+
+void val_neg_zone_take_inuse(struct val_neg_zone* zone)
+{
+	if(!zone->in_use) {
+		struct val_neg_zone* p;
+		zone->in_use = 1;
+		/* increase usage count of all parents */
+		for(p=zone; p; p = p->parent) {
+			p->count++;
+		}
+	}
+}
+
+struct val_neg_zone* neg_create_zone(struct val_neg_cache* neg,
+	uint8_t* nm, size_t nm_len, uint16_t dclass)
+{
+	struct val_neg_zone* zone;
+	struct val_neg_zone* parent;
+	struct val_neg_zone* p, *np;
+	int labs = dname_count_labels(nm);
+
+	/* find closest enclosing parent zone that (still) exists */
+	parent = neg_closest_zone_parent(neg, nm, nm_len, labs, dclass);
+	if(parent && query_dname_compare(parent->name, nm) == 0)
+		return parent; /* already exists, weird */
+	/* if parent exists, it is in use */
+	log_assert(!parent || parent->count > 0);
+	zone = neg_zone_chain(nm, nm_len, labs, dclass, parent);
+	if(!zone) {
+		return NULL;
+	}
+
+	/* insert the list of zones into the tree */
+	p = zone;
+	while(p) {
+		np = p->parent;
+		/* mem use */
+		neg->use += sizeof(struct val_neg_zone) + p->len;
+		/* insert in tree */
+		(void)rbtree_insert(&neg->tree, &p->node);
+		/* last one needs proper parent pointer */
+		if(np == NULL)
+			p->parent = parent;
+		p = np;
+	}
+	return zone;
+}
+
+/** find zone name of message, returns the SOA record */
+static struct ub_packed_rrset_key* reply_find_soa(struct reply_info* rep)
+{
+	size_t i;
+	for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){
+		if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_SOA)
+			return rep->rrsets[i];
+	}
+	return NULL;
+}
+
+/** see if the reply has NSEC records worthy of caching */
+static int reply_has_nsec(struct reply_info* rep)
+{
+	size_t i;
+	struct packed_rrset_data* d;
+	if(rep->security != sec_status_secure)
+		return 0;
+	for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){
+		if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC) {
+			d = (struct packed_rrset_data*)rep->rrsets[i]->
+				entry.data;
+			if(d->security == sec_status_secure)
+				return 1;
+		}
+	}
+	return 0;
+}
+
+
+/**
+ * Create single node of data element.
+ * @param nm: name (copied)
+ * @param nm_len: length of name
+ * @param labs: labels in name.
+ * @return element with name nm, or NULL malloc failure.
+ */
+static struct val_neg_data* neg_setup_data_node(
+	uint8_t* nm, size_t nm_len, int labs)
+{
+	struct val_neg_data* el;
+	el = (struct val_neg_data*)calloc(1, sizeof(*el));
+	if(!el) {
+		return NULL;
+	}
+	el->node.key = el;
+	el->name = memdup(nm, nm_len);
+	if(!el->name) {
+		free(el);
+		return NULL;
+	}
+	el->len = nm_len;
+	el->labs = labs;
+	return el;
+}
+
+/**
+ * Create chain of data element and parents
+ * @param nm: name
+ * @param nm_len: length of name
+ * @param labs: labels in name.
+ * @param parent: up to where to make, if NULL up to root label.
+ * @return lowest element with name nm, or NULL malloc failure.
+ */
+static struct val_neg_data* neg_data_chain(
+	uint8_t* nm, size_t nm_len, int labs, struct val_neg_data* parent)
+{
+	int i;
+	int tolabs = parent?parent->labs:0;
+	struct val_neg_data* el, *first = NULL, *prev = NULL;
+
+	/* create the new subtree, i is labelcount of current creation */
+	/* this creates a 'first' to z->parent=NULL list of zones */
+	for(i=labs; i!=tolabs; i--) {
+		/* create new item */
+		el = neg_setup_data_node(nm, nm_len, i);
+		if(!el) {
+			/* need to delete other allocations in this routine!*/
+			struct val_neg_data* p = first, *np;
+			while(p) {
+				np = p->parent;
+				free(p);
+				free(p->name);
+				p = np;
+			}
+			return NULL;
+		}
+		if(i == labs) {
+			first = el;
+		} else {
+			prev->parent = el;
+		}
+
+		/* prepare for next name */
+		prev = el;
+		dname_remove_label(&nm, &nm_len);
+	}
+	return first;
+}
+
+/**
+ * Remove NSEC records between start and end points.
+ * By walking the tree, the tree is sorted canonically.
+ * @param neg: negative cache.
+ * @param zone: the zone
+ * @param el: element to start walking at.
+ * @param nsec: the nsec record with the end point
+ */
+static void wipeout(struct val_neg_cache* neg, struct val_neg_zone* zone, 
+	struct val_neg_data* el, struct ub_packed_rrset_key* nsec)
+{
+	struct packed_rrset_data* d = (struct packed_rrset_data*)nsec->
+		entry.data;
+	uint8_t* end;
+	size_t end_len;
+	int end_labs, m;
+	rbnode_t* walk, *next;
+	struct val_neg_data* cur;
+	uint8_t buf[257];
+	/* get endpoint */
+	if(!d || d->count == 0 || d->rr_len[0] < 2+1)
+		return;
+	if(ntohs(nsec->rk.type) == LDNS_RR_TYPE_NSEC) {
+		end = d->rr_data[0]+2;
+		end_len = dname_valid(end, d->rr_len[0]-2);
+		end_labs = dname_count_labels(end);
+	} else {
+		/* NSEC3 */
+		if(!nsec3_get_nextowner_b32(nsec, 0, buf, sizeof(buf)))
+			return;
+		end = buf;
+		end_labs = dname_count_size_labels(end, &end_len);
+	}
+
+	/* sanity check, both owner and end must be below the zone apex */
+	if(!dname_subdomain_c(el->name, zone->name) || 
+		!dname_subdomain_c(end, zone->name))
+		return;
+
+	/* detect end of zone NSEC ; wipe until the end of zone */
+	if(query_dname_compare(end, zone->name) == 0) {
+		end = NULL;
+	}
+
+	walk = rbtree_next(&el->node);
+	while(walk && walk != RBTREE_NULL) {
+		cur = (struct val_neg_data*)walk;
+		/* sanity check: must be larger than start */
+		if(dname_canon_lab_cmp(cur->name, cur->labs, 
+			el->name, el->labs, &m) <= 0) {
+			/* r == 0 skip original record. */
+			/* r < 0  too small! */
+			walk = rbtree_next(walk);
+			continue;
+		}
+		/* stop at endpoint, also data at empty nonterminals must be
+		 * removed (no NSECs there) so everything between 
+		 * start and end */
+		if(end && dname_canon_lab_cmp(cur->name, cur->labs,
+			end, end_labs, &m) >= 0) {
+			break;
+		}
+		/* this element has to be deleted, but we cannot do it
+		 * now, because we are walking the tree still ... */
+		/* get the next element: */
+		next = rbtree_next(walk);
+		/* now delete the original element, this may trigger
+		 * rbtree rebalances, but really, the next element is
+		 * the one we need.
+		 * But it may trigger delete of other data and the
+		 * entire zone. However, if that happens, this is done
+		 * by deleting the *parents* of the element for deletion,
+		 * and maybe also the entire zone if it is empty. 
+		 * But parents are smaller in canonical compare, thus,
+		 * if a larger element exists, then it is not a parent,
+		 * it cannot get deleted, the zone cannot get empty.
+		 * If the next==NULL, then zone can be empty. */
+		if(cur->in_use)
+			neg_delete_data(neg, cur);
+		walk = next;
+	}
+}
+
+void neg_insert_data(struct val_neg_cache* neg, 
+	struct val_neg_zone* zone, struct ub_packed_rrset_key* nsec)
+{
+	struct packed_rrset_data* d;
+	struct val_neg_data* parent;
+	struct val_neg_data* el;
+	uint8_t* nm = nsec->rk.dname;
+	size_t nm_len = nsec->rk.dname_len;
+	int labs = dname_count_labels(nsec->rk.dname);
+
+	d = (struct packed_rrset_data*)nsec->entry.data;
+	if( !(d->security == sec_status_secure ||
+		(d->security == sec_status_unchecked && d->rrsig_count > 0)))
+		return;
+	log_nametypeclass(VERB_ALGO, "negcache rr", 
+		nsec->rk.dname, ntohs(nsec->rk.type), 
+		ntohs(nsec->rk.rrset_class));
+
+	/* find closest enclosing parent data that (still) exists */
+	parent = neg_closest_data_parent(zone, nm, nm_len, labs);
+	if(parent && query_dname_compare(parent->name, nm) == 0) {
+		/* perfect match already exists */
+		log_assert(parent->count > 0);
+		el = parent;
+	} else { 
+		struct val_neg_data* p, *np;
+
+		/* create subtree for perfect match */
+		/* if parent exists, it is in use */
+		log_assert(!parent || parent->count > 0);
+
+		el = neg_data_chain(nm, nm_len, labs, parent);
+		if(!el) {
+			log_err("out of memory inserting NSEC negative cache");
+			return;
+		}
+		el->in_use = 0; /* set on below */
+
+		/* insert the list of zones into the tree */
+		p = el;
+		while(p) {
+			np = p->parent;
+			/* mem use */
+			neg->use += sizeof(struct val_neg_data) + p->len;
+			/* insert in tree */
+			p->zone = zone;
+			(void)rbtree_insert(&zone->tree, &p->node);
+			/* last one needs proper parent pointer */
+			if(np == NULL)
+				p->parent = parent;
+			p = np;
+		}
+	}
+
+	if(!el->in_use) {
+		struct val_neg_data* p;
+
+		el->in_use = 1;
+		/* increase usage count of all parents */
+		for(p=el; p; p = p->parent) {
+			p->count++;
+		}
+
+		neg_lru_front(neg, el);
+	} else {
+		/* in use, bring to front, lru */
+		neg_lru_touch(neg, el);
+	}
+
+	/* if nsec3 store last used parameters */
+	if(ntohs(nsec->rk.type) == LDNS_RR_TYPE_NSEC3) {
+		int h;
+		uint8_t* s;
+		size_t slen, it;
+		if(nsec3_get_params(nsec, 0, &h, &it, &s, &slen) &&
+			it <= neg->nsec3_max_iter &&
+			(h != zone->nsec3_hash || it != zone->nsec3_iter ||
+			slen != zone->nsec3_saltlen || 
+			memcmp(zone->nsec3_salt, s, slen) != 0)) {
+			uint8_t* sa = memdup(s, slen);
+			if(sa) {
+				free(zone->nsec3_salt);
+				zone->nsec3_salt = sa;
+				zone->nsec3_saltlen = slen;
+				zone->nsec3_hash = h;
+				zone->nsec3_iter = it;
+			}
+		}
+	}
+
+	/* wipe out the cache items between NSEC start and end */
+	wipeout(neg, zone, el, nsec);
+}
+
+void val_neg_addreply(struct val_neg_cache* neg, struct reply_info* rep)
+{
+	size_t i, need;
+	struct ub_packed_rrset_key* soa;
+	struct val_neg_zone* zone;
+	/* see if secure nsecs inside */
+	if(!reply_has_nsec(rep))
+		return;
+	/* find the zone name in message */
+	soa = reply_find_soa(rep);
+	if(!soa)
+		return;
+
+	log_nametypeclass(VERB_ALGO, "negcache insert for zone",
+		soa->rk.dname, LDNS_RR_TYPE_SOA, ntohs(soa->rk.rrset_class));
+	
+	/* ask for enough space to store all of it */
+	need = calc_data_need(rep) + 
+		calc_zone_need(soa->rk.dname, soa->rk.dname_len);
+	lock_basic_lock(&neg->lock);
+	neg_make_space(neg, need);
+
+	/* find or create the zone entry */
+	zone = neg_find_zone(neg, soa->rk.dname, soa->rk.dname_len,
+		ntohs(soa->rk.rrset_class));
+	if(!zone) {
+		if(!(zone = neg_create_zone(neg, soa->rk.dname, 
+			soa->rk.dname_len, ntohs(soa->rk.rrset_class)))) {
+			lock_basic_unlock(&neg->lock);
+			log_err("out of memory adding negative zone");
+			return;
+		}
+	}
+	val_neg_zone_take_inuse(zone);
+
+	/* insert the NSECs */
+	for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){
+		if(ntohs(rep->rrsets[i]->rk.type) != LDNS_RR_TYPE_NSEC)
+			continue;
+		if(!dname_subdomain_c(rep->rrsets[i]->rk.dname, 
+			zone->name)) continue;
+		/* insert NSEC into this zone's tree */
+		neg_insert_data(neg, zone, rep->rrsets[i]);
+	}
+	if(zone->tree.count == 0) {
+		/* remove empty zone if inserts failed */
+		neg_delete_zone(neg, zone);
+	}
+	lock_basic_unlock(&neg->lock);
+}
+
+/**
+ * Lookup closest data record. For NSEC denial.
+ * @param zone: zone to look in
+ * @param qname: name to look for.
+ * @param len: length of name
+ * @param labs: labels in name
+ * @param data: data element, exact or smaller or NULL
+ * @return true if exact match.
+ */
+static int neg_closest_data(struct val_neg_zone* zone,
+	uint8_t* qname, size_t len, int labs, struct val_neg_data** data)
+{
+	struct val_neg_data key;
+	rbnode_t* r;
+	key.node.key = &key;
+	key.name = qname;
+	key.len = len;
+	key.labs = labs;
+	if(rbtree_find_less_equal(&zone->tree, &key, &r)) {
+		/* exact match */
+		*data = (struct val_neg_data*)r;
+		return 1;
+	} else {
+		/* smaller match */
+		*data = (struct val_neg_data*)r;
+		return 0;
+	}
+}
+
+int val_neg_dlvlookup(struct val_neg_cache* neg, uint8_t* qname, size_t len,
+        uint16_t qclass, struct rrset_cache* rrset_cache, uint32_t now)
+{
+	/* lookup closest zone */
+	struct val_neg_zone* zone;
+	struct val_neg_data* data;
+	int labs;
+	struct ub_packed_rrset_key* nsec;
+	struct packed_rrset_data* d;
+	uint32_t flags;
+	uint8_t* wc;
+	struct query_info qinfo;
+	if(!neg) return 0;
+
+	log_nametypeclass(VERB_ALGO, "negcache dlvlookup", qname, 
+		LDNS_RR_TYPE_DLV, qclass);
+	
+	labs = dname_count_labels(qname);
+	lock_basic_lock(&neg->lock);
+	zone = neg_closest_zone_parent(neg, qname, len, labs, qclass);
+	while(zone && !zone->in_use)
+		zone = zone->parent;
+	if(!zone) {
+		lock_basic_unlock(&neg->lock);
+		return 0;
+	}
+	log_nametypeclass(VERB_ALGO, "negcache zone", zone->name, 0, 
+		zone->dclass);
+
+	/* DLV is defined to use NSEC only */
+	if(zone->nsec3_hash) {
+		lock_basic_unlock(&neg->lock);
+		return 0;
+	}
+
+	/* lookup closest data record */
+	(void)neg_closest_data(zone, qname, len, labs, &data);
+	while(data && !data->in_use)
+		data = data->parent;
+	if(!data) {
+		lock_basic_unlock(&neg->lock);
+		return 0;
+	}
+	log_nametypeclass(VERB_ALGO, "negcache rr", data->name, 
+		LDNS_RR_TYPE_NSEC, zone->dclass);
+
+	/* lookup rrset in rrset cache */
+	flags = 0;
+	if(query_dname_compare(data->name, zone->name) == 0)
+		flags = PACKED_RRSET_NSEC_AT_APEX;
+	nsec = rrset_cache_lookup(rrset_cache, data->name, data->len,
+		LDNS_RR_TYPE_NSEC, zone->dclass, flags, now, 0);
+
+	/* check if secure and TTL ok */
+	if(!nsec) {
+		lock_basic_unlock(&neg->lock);
+		return 0;
+	}
+	d = (struct packed_rrset_data*)nsec->entry.data;
+	if(!d || now > d->ttl) {
+		lock_rw_unlock(&nsec->entry.lock);
+		/* delete data record if expired */
+		neg_delete_data(neg, data);
+		lock_basic_unlock(&neg->lock);
+		return 0;
+	}
+	if(d->security != sec_status_secure) {
+		lock_rw_unlock(&nsec->entry.lock);
+		neg_delete_data(neg, data);
+		lock_basic_unlock(&neg->lock);
+		return 0;
+	}
+	verbose(VERB_ALGO, "negcache got secure rrset");
+
+	/* check NSEC security */
+	/* check if NSEC proves no DLV type exists */
+	/* check if NSEC proves NXDOMAIN for qname */
+	qinfo.qname = qname;
+	qinfo.qtype = LDNS_RR_TYPE_DLV;
+	qinfo.qclass = qclass;
+	if(!nsec_proves_nodata(nsec, &qinfo, &wc) &&
+		!val_nsec_proves_name_error(nsec, qname)) {
+		/* the NSEC is not a denial for the DLV */
+		lock_rw_unlock(&nsec->entry.lock);
+		lock_basic_unlock(&neg->lock);
+		verbose(VERB_ALGO, "negcache not proven");
+		return 0;
+	}
+	/* so the NSEC was a NODATA proof, or NXDOMAIN proof. */
+
+	/* no need to check for wildcard NSEC; no wildcards in DLV repos */
+	/* no need to lookup SOA record for client; no response message */
+
+	lock_rw_unlock(&nsec->entry.lock);
+	/* if OK touch the LRU for neg_data element */
+	neg_lru_touch(neg, data);
+	lock_basic_unlock(&neg->lock);
+	verbose(VERB_ALGO, "negcache DLV denial proven");
+	return 1;
+}
+
+/** see if the reply has signed NSEC records and return the signer */
+static uint8_t* reply_nsec_signer(struct reply_info* rep, size_t* signer_len,
+	uint16_t* dclass)
+{
+	size_t i;
+	struct packed_rrset_data* d;
+	uint8_t* s;
+	for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){
+		if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC ||
+			ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC3) {
+			d = (struct packed_rrset_data*)rep->rrsets[i]->
+				entry.data;
+			/* return first signer name of first NSEC */
+			if(d->rrsig_count != 0) {
+				val_find_rrset_signer(rep->rrsets[i],
+					&s, signer_len);
+				if(s && *signer_len) {
+					*dclass = ntohs(rep->rrsets[i]->
+						rk.rrset_class);
+					return s;
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+void val_neg_addreferral(struct val_neg_cache* neg, struct reply_info* rep,
+	uint8_t* zone_name)
+{
+	size_t i, need;
+	uint8_t* signer;
+	size_t signer_len;
+	uint16_t dclass;
+	struct val_neg_zone* zone;
+	/* no SOA in this message, find RRSIG over NSEC's signer name.
+	 * note the NSEC records are maybe not validated yet */
+	signer = reply_nsec_signer(rep, &signer_len, &dclass);
+	if(!signer) 
+		return;
+	if(!dname_subdomain_c(signer, zone_name)) {
+		/* the signer is not in the bailiwick, throw it out */
+		return;
+	}
+
+	log_nametypeclass(VERB_ALGO, "negcache insert referral ",
+		signer, LDNS_RR_TYPE_NS, dclass);
+	
+	/* ask for enough space to store all of it */
+	need = calc_data_need(rep) + calc_zone_need(signer, signer_len);
+	lock_basic_lock(&neg->lock);
+	neg_make_space(neg, need);
+
+	/* find or create the zone entry */
+	zone = neg_find_zone(neg, signer, signer_len, dclass);
+	if(!zone) {
+		if(!(zone = neg_create_zone(neg, signer, signer_len, 
+			dclass))) {
+			lock_basic_unlock(&neg->lock);
+			log_err("out of memory adding negative zone");
+			return;
+		}
+	}
+	val_neg_zone_take_inuse(zone);
+
+	/* insert the NSECs */
+	for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){
+		if(ntohs(rep->rrsets[i]->rk.type) != LDNS_RR_TYPE_NSEC &&
+			ntohs(rep->rrsets[i]->rk.type) != LDNS_RR_TYPE_NSEC3)
+			continue;
+		if(!dname_subdomain_c(rep->rrsets[i]->rk.dname, 
+			zone->name)) continue;
+		/* insert NSEC into this zone's tree */
+		neg_insert_data(neg, zone, rep->rrsets[i]);
+	}
+	if(zone->tree.count == 0) {
+		/* remove empty zone if inserts failed */
+		neg_delete_zone(neg, zone);
+	}
+	lock_basic_unlock(&neg->lock);
+}
+
+/**
+ * Check that an NSEC3 rrset does not have a type set.
+ * None of the nsec3s in a hash-collision are allowed to have the type.
+ * (since we do not know which one is the nsec3 looked at, flags, ..., we
+ * ignore the cached item and let it bypass negative caching).
+ * @param k: the nsec3 rrset to check.
+ * @param t: type to check
+ * @return true if no RRs have the type.
+ */
+static int nsec3_no_type(struct ub_packed_rrset_key* k, uint16_t t)
+{
+	int count = (int)((struct packed_rrset_data*)k->entry.data)->count;
+	int i;
+	for(i=0; i<count; i++)
+		if(nsec3_has_type(k, i, t))
+			return 0;
+	return 1;
+}
+
+/**
+ * See if rrset exists in rrset cache.
+ * If it does, the bit is checked, and if not expired, it is returned
+ * allocated in region.
+ * @param rrset_cache: rrset cache
+ * @param qname: to lookup rrset name
+ * @param qname_len: length of qname.
+ * @param qtype: type of rrset to lookup, host order
+ * @param qclass: class of rrset to lookup, host order
+ * @param flags: flags for rrset to lookup
+ * @param region: where to alloc result
+ * @param checkbit: if true, a bit in the nsec typemap is checked for absence.
+ * @param checktype: which bit to check
+ * @param now: to check ttl against
+ * @return rrset or NULL
+ */
+static struct ub_packed_rrset_key*
+grab_nsec(struct rrset_cache* rrset_cache, uint8_t* qname, size_t qname_len,
+	uint16_t qtype, uint16_t qclass, uint32_t flags, 
+	struct regional* region, int checkbit, uint16_t checktype, 
+	uint32_t now)
+{
+	struct ub_packed_rrset_key* r, *k = rrset_cache_lookup(rrset_cache,
+		qname, qname_len, qtype, qclass, flags, now, 0);
+	struct packed_rrset_data* d;
+	if(!k) return NULL;
+	d = (struct packed_rrset_data*)k->entry.data;
+	if(d->ttl < now) {
+		lock_rw_unlock(&k->entry.lock);
+		return NULL;
+	}
+	/* only secure or unchecked records that have signatures. */
+	if( ! ( d->security == sec_status_secure ||
+		(d->security == sec_status_unchecked &&
+		d->rrsig_count > 0) ) ) {
+		lock_rw_unlock(&k->entry.lock);
+		return NULL;
+	}
+	/* check if checktype is absent */
+	if(checkbit && (
+		(qtype == LDNS_RR_TYPE_NSEC && nsec_has_type(k, checktype)) ||
+		(qtype == LDNS_RR_TYPE_NSEC3 && !nsec3_no_type(k, checktype))
+		)) {
+		lock_rw_unlock(&k->entry.lock);
+		return NULL;
+	}
+	/* looks OK! copy to region and return it */
+	r = packed_rrset_copy_region(k, region, now);
+	/* if it failed, we return the NULL */
+	lock_rw_unlock(&k->entry.lock);
+	return r;
+}
+
+/** find nsec3 closest encloser in neg cache */
+static struct val_neg_data*
+neg_find_nsec3_ce(struct val_neg_zone* zone, uint8_t* qname, size_t qname_len,
+		int qlabs, ldns_buffer* buf, uint8_t* hashnc, size_t* nclen)
+{
+	struct val_neg_data* data;
+	uint8_t hashce[SHA_DIGEST_LENGTH];
+	uint8_t b32[257];
+	size_t celen, b32len;
+
+	*nclen = 0;
+	while(qlabs > 0) {
+		/* hash */
+		if(!(celen=nsec3_get_hashed(buf, qname, qname_len, 
+			zone->nsec3_hash, zone->nsec3_iter, zone->nsec3_salt, 
+			zone->nsec3_saltlen, hashce, sizeof(hashce))))
+			return NULL;
+		if(!(b32len=nsec3_hash_to_b32(hashce, celen, zone->name,
+			zone->len, b32, sizeof(b32))))
+			return NULL;
+
+		/* lookup (exact match only) */
+		data = neg_find_data(zone, b32, b32len, zone->labs+1);
+		if(data && data->in_use) {
+			/* found ce match! */
+			return data;
+		}
+
+		*nclen = celen;
+		memmove(hashnc, hashce, celen);
+		dname_remove_label(&qname, &qname_len);
+		qlabs --;
+	}
+	return NULL;
+}
+
+/** check nsec3 parameters on nsec3 rrset with current zone values */
+static int
+neg_params_ok(struct val_neg_zone* zone, struct ub_packed_rrset_key* rrset)
+{
+	int h;
+	uint8_t* s;
+	size_t slen, it;
+	if(!nsec3_get_params(rrset, 0, &h, &it, &s, &slen))
+		return 0;
+	return (h == zone->nsec3_hash && it == zone->nsec3_iter &&
+		slen == zone->nsec3_saltlen &&
+		memcmp(zone->nsec3_salt, s, slen) == 0);
+}
+
+/** get next closer for nsec3 proof */
+static struct ub_packed_rrset_key*
+neg_nsec3_getnc(struct val_neg_zone* zone, uint8_t* hashnc, size_t nclen,
+	struct rrset_cache* rrset_cache, struct regional* region, 
+	uint32_t now, uint8_t* b32, size_t maxb32)
+{
+	struct ub_packed_rrset_key* nc_rrset;
+	struct val_neg_data* data;
+	size_t b32len;
+
+	if(!(b32len=nsec3_hash_to_b32(hashnc, nclen, zone->name,
+		zone->len, b32, maxb32)))
+		return NULL;
+	(void)neg_closest_data(zone, b32, b32len, zone->labs+1, &data);
+	if(!data && zone->tree.count != 0) {
+		/* could be before the first entry ; return the last
+		 * entry (possibly the rollover nsec3 at end) */
+		data = (struct val_neg_data*)rbtree_last(&zone->tree);
+	}
+	while(data && !data->in_use)
+		data = data->parent;
+	if(!data)
+		return NULL;
+	/* got a data element in tree, grab it */
+	nc_rrset = grab_nsec(rrset_cache, data->name, data->len, 
+		LDNS_RR_TYPE_NSEC3, zone->dclass, 0, region, 0, 0, now);
+	if(!nc_rrset)
+		return NULL;
+	if(!neg_params_ok(zone, nc_rrset))
+		return NULL;
+	return nc_rrset;
+}
+
+/** neg cache nsec3 proof procedure*/
+static struct dns_msg*
+neg_nsec3_proof_ds(struct val_neg_zone* zone, uint8_t* qname, size_t qname_len,
+		int qlabs, ldns_buffer* buf, struct rrset_cache* rrset_cache,
+		struct regional* region, uint32_t now, uint8_t* topname)
+{
+	struct dns_msg* msg;
+	struct val_neg_data* data;
+	uint8_t hashnc[SHA_DIGEST_LENGTH];
+	size_t nclen;
+	struct ub_packed_rrset_key* ce_rrset, *nc_rrset;
+	struct nsec3_cached_hash c;
+	uint8_t nc_b32[257];
+
+	/* for NSEC3 ; determine the closest encloser for which we
+	 * can find an exact match. Remember the hashed lower name,
+	 * since that is the one we need a closest match for. 
+	 * If we find a match straight away, then it becomes NODATA.
+	 * Otherwise, NXDOMAIN or if OPTOUT, an insecure delegation.
+	 * Also check that parameters are the same on closest encloser
+	 * and on closest match.
+	 */
+	if(!zone->nsec3_hash) 
+		return NULL; /* not nsec3 zone */
+
+	if(!(data=neg_find_nsec3_ce(zone, qname, qname_len, qlabs, buf,
+		hashnc, &nclen))) {
+		return NULL;
+	}
+
+	/* grab the ce rrset */
+	ce_rrset = grab_nsec(rrset_cache, data->name, data->len, 
+		LDNS_RR_TYPE_NSEC3, zone->dclass, 0, region, 1, 
+		LDNS_RR_TYPE_DS, now);
+	if(!ce_rrset)
+		return NULL;
+	if(!neg_params_ok(zone, ce_rrset))
+		return NULL;
+
+	if(nclen == 0) {
+		/* exact match, just check the type bits */
+		/* need: -SOA, -DS, +NS */
+		if(nsec3_has_type(ce_rrset, 0, LDNS_RR_TYPE_SOA) ||
+			nsec3_has_type(ce_rrset, 0, LDNS_RR_TYPE_DS) ||
+			!nsec3_has_type(ce_rrset, 0, LDNS_RR_TYPE_NS))
+			return NULL;
+		if(!(msg = dns_msg_create(qname, qname_len, 
+			LDNS_RR_TYPE_DS, zone->dclass, region, 1))) 
+			return NULL;
+		/* TTL reduced in grab_nsec */
+		if(!dns_msg_authadd(msg, region, ce_rrset, 0)) 
+			return NULL;
+		return msg;
+	}
+
+	/* optout is not allowed without knowing the trust-anchor in use,
+	 * otherwise the optout could spoof away that anchor */
+	if(!topname)
+		return NULL;
+
+	/* if there is no exact match, it must be in an optout span
+	 * (an existing DS implies an NSEC3 must exist) */
+	nc_rrset = neg_nsec3_getnc(zone, hashnc, nclen, rrset_cache, 
+		region, now, nc_b32, sizeof(nc_b32));
+	if(!nc_rrset) 
+		return NULL;
+	if(!neg_params_ok(zone, nc_rrset))
+		return NULL;
+	if(!nsec3_has_optout(nc_rrset, 0))
+		return NULL;
+	c.hash = hashnc;
+	c.hash_len = nclen;
+	c.b32 = nc_b32+1;
+	c.b32_len = (size_t)nc_b32[0];
+	if(nsec3_covers(zone->name, &c, nc_rrset, 0, buf)) {
+		/* nc_rrset covers the next closer name.
+		 * ce_rrset equals a closer encloser.
+		 * nc_rrset is optout.
+		 * No need to check wildcard for type DS */
+		/* capacity=3: ce + nc + soa(if needed) */
+		if(!(msg = dns_msg_create(qname, qname_len, 
+			LDNS_RR_TYPE_DS, zone->dclass, region, 3))) 
+			return NULL;
+		/* now=0 because TTL was reduced in grab_nsec */
+		if(!dns_msg_authadd(msg, region, ce_rrset, 0)) 
+			return NULL;
+		if(!dns_msg_authadd(msg, region, nc_rrset, 0)) 
+			return NULL;
+		return msg;
+	}
+	return NULL;
+}
+
+/**
+ * Add SOA record for external responses.
+ * @param rrset_cache: to look into.
+ * @param now: current time.
+ * @param region: where to perform the allocation
+ * @param msg: current msg with NSEC.
+ * @param zone: val_neg_zone if we have one.
+ * @return false on lookup or alloc failure.
+ */
+static int add_soa(struct rrset_cache* rrset_cache, uint32_t now,
+	struct regional* region, struct dns_msg* msg, struct val_neg_zone* zone)
+{
+	struct ub_packed_rrset_key* soa;
+	uint8_t* nm;
+	size_t nmlen;
+	uint16_t dclass;
+	if(zone) {
+		nm = zone->name;
+		nmlen = zone->len;
+		dclass = zone->dclass;
+	} else {
+		/* Assumes the signer is the zone SOA to add */
+		nm = reply_nsec_signer(msg->rep, &nmlen, &dclass);
+		if(!nm) 
+			return 0;
+	}
+	soa = rrset_cache_lookup(rrset_cache, nm, nmlen, LDNS_RR_TYPE_SOA, 
+		dclass, PACKED_RRSET_SOA_NEG, now, 0);
+	if(!soa)
+		return 0;
+	if(!dns_msg_authadd(msg, region, soa, now)) {
+		lock_rw_unlock(&soa->entry.lock);
+		return 0;
+	}
+	lock_rw_unlock(&soa->entry.lock);
+	return 1;
+}
+
+struct dns_msg* 
+val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo, 
+	struct regional* region, struct rrset_cache* rrset_cache, 
+	ldns_buffer* buf, uint32_t now, int addsoa, uint8_t* topname)
+{
+	struct dns_msg* msg;
+	struct ub_packed_rrset_key* rrset;
+	uint8_t* zname;
+	size_t zname_len;
+	int zname_labs;
+	struct val_neg_zone* zone;
+
+	/* only for DS queries */
+	if(qinfo->qtype != LDNS_RR_TYPE_DS)
+		return NULL;
+	log_assert(!topname || dname_subdomain_c(qinfo->qname, topname));
+
+	/* see if info from neg cache is available 
+	 * For NSECs, because there is no optout; a DS next to a delegation
+	 * always has exactly an NSEC for it itself; check its DS bit.
+	 * flags=0 (not the zone apex).
+	 */
+	rrset = grab_nsec(rrset_cache, qinfo->qname, qinfo->qname_len,
+		LDNS_RR_TYPE_NSEC, qinfo->qclass, 0, region, 1, 
+		qinfo->qtype, now);
+	if(rrset) {
+		/* return msg with that rrset */
+		if(!(msg = dns_msg_create(qinfo->qname, qinfo->qname_len, 
+			qinfo->qtype, qinfo->qclass, region, 2))) 
+			return NULL;
+		/* TTL already subtracted in grab_nsec */
+		if(!dns_msg_authadd(msg, region, rrset, 0)) 
+			return NULL;
+		if(addsoa && !add_soa(rrset_cache, now, region, msg, NULL))
+			return NULL;
+		return msg;
+	}
+
+	/* check NSEC3 neg cache for type DS */
+	/* need to look one zone higher for DS type */
+	zname = qinfo->qname;
+	zname_len = qinfo->qname_len;
+	dname_remove_label(&zname, &zname_len);
+	zname_labs = dname_count_labels(zname);
+
+	/* lookup closest zone */
+	lock_basic_lock(&neg->lock);
+	zone = neg_closest_zone_parent(neg, zname, zname_len, zname_labs, 
+		qinfo->qclass);
+	while(zone && !zone->in_use)
+		zone = zone->parent;
+	/* check that the zone is not too high up so that we do not pick data
+	 * out of a zone that is above the last-seen key (or trust-anchor). */
+	if(zone && topname) {
+		if(!dname_subdomain_c(zone->name, topname))
+			zone = NULL;
+	}
+	if(!zone) {
+		lock_basic_unlock(&neg->lock);
+		return NULL;
+	}
+
+	msg = neg_nsec3_proof_ds(zone, qinfo->qname, qinfo->qname_len, 
+		zname_labs+1, buf, rrset_cache, region, now, topname);
+	if(msg && addsoa && !add_soa(rrset_cache, now, region, msg, zone)) {
+		lock_basic_unlock(&neg->lock);
+		return NULL;
+	}
+	lock_basic_unlock(&neg->lock);
+	return msg;
+}
diff --git a/3rdParty/Unbound/src/src/validator/val_neg.h b/3rdParty/Unbound/src/src/validator/val_neg.h
new file mode 100644
index 0000000..01b423e
--- /dev/null
+++ b/3rdParty/Unbound/src/src/validator/val_neg.h
@@ -0,0 +1,314 @@
+/*
+ * validator/val_neg.h - validator aggressive negative caching functions.
+ *
+ * Copyright (c) 2008, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains helper functions for the validator module.
+ * The functions help with aggressive negative caching.
+ * This creates new denials of existance, and proofs for absence of types
+ * from cached NSEC records.
+ */
+
+#ifndef VALIDATOR_VAL_NEG_H
+#define VALIDATOR_VAL_NEG_H
+#include "util/locks.h"
+#include "util/rbtree.h"
+struct val_neg_data;
+struct config_file;
+struct reply_info;
+struct rrset_cache;
+struct regional;
+struct query_info;
+struct dns_msg;
+struct ub_packed_rrset_key;
+
+/**
+ * The negative cache.  It is shared between the threads, so locked. 
+ * Kept as validator-environ-state.  It refers back to the rrset cache for
+ * data elements.  It can be out of date and contain conflicting data 
+ * from zone content changes.  
+ * It contains a tree of zones, every zone has a tree of data elements.
+ * The data elements are part of one big LRU list, with one memory counter.
+ */
+struct val_neg_cache {
+	/** the big lock on the negative cache.  Because we use a rbtree 
+	 * for the data (quick lookup), we need a big lock */
+	lock_basic_t lock;
+	/** The zone rbtree. contents sorted canonical, type val_neg_zone */
+	rbtree_t tree;
+	/** the first in linked list of LRU of val_neg_data */
+	struct val_neg_data* first;
+	/** last in lru (least recently used element) */
+	struct val_neg_data* last;
+	/** current memory in use (bytes) */
+	size_t use;
+	/** max memory to use (bytes) */
+	size_t max;
+	/** max nsec3 iterations allowed */
+	size_t nsec3_max_iter;
+};
+
+/**
+ * Per Zone aggressive negative caching data.
+ */
+struct val_neg_zone {
+	/** rbtree node element, key is this struct: the name, class */
+	rbnode_t node;
+	/** name; the key */
+	uint8_t* name;
+	/** length of name */
+	size_t len;
+	/** labels in name */
+	int labs;
+
+	/** pointer to parent zone in the negative cache */
+	struct val_neg_zone* parent;
+
+	/** the number of elements, including this one and the ones whose
+	 * parents (-parents) include this one, that are in_use 
+	 * No elements have a count of zero, those are removed. */
+	int count;
+
+	/** if 0: NSEC zone, else NSEC3 hash algorithm in use */
+	int nsec3_hash;
+	/** nsec3 iteration count in use */
+	size_t nsec3_iter;
+	/** nsec3 salt in use */
+	uint8_t* nsec3_salt;
+	/** length of salt in bytes */
+	size_t nsec3_saltlen;
+
+	/** tree of NSEC data for this zone, sorted canonical 
+	 * by NSEC owner name */
+	rbtree_t tree;
+
+	/** class of node; host order */
+	uint16_t dclass;
+	/** if this element is in use, boolean */
+	uint8_t in_use;
+};
+
+/**
+ * Data element for aggressive negative caching.
+ * The tree of these elements acts as an index onto the rrset cache.
+ * It shows the NSEC records that (may) exist and are (possibly) secure.
+ * The rbtree allows for logN search for a covering NSEC record.
+ * To make tree insertion and deletion logN too, all the parent (one label
+ * less than the name) data elements are also in the rbtree, with a usage
+ * count for every data element.
+ * There is no actual data stored in this data element, if it is in_use,
+ * then the data can (possibly) be found in the rrset cache.
+ */
+struct val_neg_data {
+	/** rbtree node element, key is this struct: the name */
+	rbnode_t node;
+	/** name; the key */
+	uint8_t* name;
+	/** length of name */
+	size_t len;
+	/** labels in name */
+	int labs;
+
+	/** pointer to parent node in the negative cache */
+	struct val_neg_data* parent;
+
+	/** the number of elements, including this one and the ones whose
+	 * parents (-parents) include this one, that are in use 
+	 * No elements have a count of zero, those are removed. */
+	int count;
+
+	/** the zone that this denial is part of */
+	struct val_neg_zone* zone;
+
+	/** previous in LRU */
+	struct val_neg_data* prev;
+	/** next in LRU (next element was less recently used) */
+	struct val_neg_data* next;
+
+	/** if this element is in use, boolean */
+	uint8_t in_use;
+};
+
+/**
+ * Create negative cache
+ * @param cfg: config options.
+ * @param maxiter: max nsec3 iterations allowed.
+ * @return neg cache, empty or NULL on failure.
+ */
+struct val_neg_cache* val_neg_create(struct config_file* cfg, size_t maxiter);
+
+/**
+ * see how much memory is in use by the negative cache.
+ * @param neg: negative cache
+ * @return number of bytes in use.
+ */
+size_t val_neg_get_mem(struct val_neg_cache* neg);
+
+/**
+ * Destroy negative cache. There must no longer be any other threads.
+ * @param neg: negative cache.
+ */
+void neg_cache_delete(struct val_neg_cache* neg);
+
+/** 
+ * Comparison function for rbtree val neg data elements
+ */
+int val_neg_data_compare(const void* a, const void* b);
+
+/** 
+ * Comparison function for rbtree val neg zone elements
+ */
+int val_neg_zone_compare(const void* a, const void* b);
+
+/**
+ * Insert NSECs from this message into the negative cache for reference.
+ * @param neg: negative cache
+ * @param rep: reply with NSECs.
+ * Errors are ignored, means that storage is omitted.
+ */
+void val_neg_addreply(struct val_neg_cache* neg, struct reply_info* rep);
+
+/**
+ * Insert NSECs from this referral into the negative cache for reference.
+ * @param neg: negative cache
+ * @param rep: referral reply with NS, NSECs.
+ * @param zone: bailiwick for the referral.
+ * Errors are ignored, means that storage is omitted.
+ */
+void val_neg_addreferral(struct val_neg_cache* neg, struct reply_info* rep,
+	uint8_t* zone);
+
+/**
+ * Perform a DLV style lookup
+ * During the lookup, we could find out that data has expired. In that
+ * case the neg_cache entries are removed, and lookup fails.
+ *
+ * @param neg: negative cache.
+ * @param qname: name to look for
+ * @param len: length of qname.
+ * @param qclass: class to look in.
+ * @param rrset_cache: the rrset cache, for NSEC lookups.
+ * @param now: current time for ttl checks.
+ * @return 
+ *	0 on error
+ *	0 if no proof of negative
+ *	1 if indeed negative was proven
+ *	  thus, qname DLV qclass does not exist.
+ */
+int val_neg_dlvlookup(struct val_neg_cache* neg, uint8_t* qname, size_t len,
+	uint16_t qclass, struct rrset_cache* rrset_cache, uint32_t now);
+
+/**
+ * For the given query, try to get a reply out of the negative cache.
+ * The reply still needs to be validated.
+ * @param neg: negative cache.
+ * @param qinfo: query
+ * @param region: where to allocate reply.
+ * @param rrset_cache: rrset cache.
+ * @param buf: temporary buffer.
+ * @param now: to check TTLs against.
+ * @param addsoa: if true, produce result for external consumption.
+ *	if false, do not add SOA - for unbound-internal consumption.
+ * @param topname: do not look higher than this name, 
+ * 	so that the result cannot be taken from a zone above the current
+ * 	trust anchor.  Which could happen with multiple islands of trust.
+ * 	if NULL, then no trust anchor is used, but also the algorithm becomes
+ * 	more conservative, especially for opt-out zones, since the receiver
+ * 	may have a trust-anchor below the optout and thus the optout cannot
+ * 	be used to create a proof from the negative cache.
+ * @return a reply message if something was found. 
+ * 	This reply may still need validation.
+ * 	NULL if nothing found (or out of memory).
+ */
+struct dns_msg* val_neg_getmsg(struct val_neg_cache* neg, 
+	struct query_info* qinfo, struct regional* region, 
+	struct rrset_cache* rrset_cache, ldns_buffer* buf, uint32_t now,
+	int addsoa, uint8_t* topname);
+
+
+/**** functions exposed for unit test ****/
+/**
+ * Insert data into the data tree of a zone
+ * Does not do locking.
+ * @param neg: negative cache
+ * @param zone: zone to insert into
+ * @param nsec: record to insert.
+ */
+void neg_insert_data(struct val_neg_cache* neg,
+        struct val_neg_zone* zone, struct ub_packed_rrset_key* nsec);
+
+/**
+ * Delete a data element from the negative cache.
+ * May delete other data elements to keep tree coherent, or
+ * only mark the element as 'not in use'.
+ * Does not do locking.
+ * @param neg: negative cache.
+ * @param el: data element to delete.
+ */
+void neg_delete_data(struct val_neg_cache* neg, struct val_neg_data* el);
+
+/**
+ * Find the given zone, from the SOA owner name and class
+ * Does not do locking.
+ * @param neg: negative cache
+ * @param nm: what to look for.
+ * @param len: length of nm
+ * @param dclass: class to look for.
+ * @return zone or NULL if not found.
+ */
+struct val_neg_zone* neg_find_zone(struct val_neg_cache* neg,
+        uint8_t* nm, size_t len, uint16_t dclass);
+
+/**
+ * Create a new zone.
+ * Does not do locking.
+ * @param neg: negative cache
+ * @param nm: what to look for.
+ * @param nm_len: length of name.
+ * @param dclass: class of zone, host order.
+ * @return zone or NULL if out of memory.
+ */
+struct val_neg_zone* neg_create_zone(struct val_neg_cache* neg,
+        uint8_t* nm, size_t nm_len, uint16_t dclass);
+
+/**
+ * take a zone into use. increases counts of parents.
+ * Does not do locking.
+ * @param zone: zone to take into use.
+ */
+void val_neg_zone_take_inuse(struct val_neg_zone* zone);
+
+#endif /* VALIDATOR_VAL_NEG_H */
diff --git a/3rdParty/Unbound/src/src/validator/val_nsec.c b/3rdParty/Unbound/src/src/validator/val_nsec.c
new file mode 100644
index 0000000..6406870
--- /dev/null
+++ b/3rdParty/Unbound/src/src/validator/val_nsec.c
@@ -0,0 +1,603 @@
+/*
+ * validator/val_nsec.c - validator NSEC denial of existance functions.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains helper functions for the validator module.
+ * The functions help with NSEC checking, the different NSEC proofs
+ * for denial of existance, and proofs for presence of types.
+ */
+#include "config.h"
+#include <ldns/packet.h>
+#include "validator/val_nsec.h"
+#include "validator/val_utils.h"
+#include "util/data/msgreply.h"
+#include "util/data/dname.h"
+#include "util/net_help.h"
+#include "util/module.h"
+#include "services/cache/rrset.h"
+
+/** get ttl of rrset */
+static uint32_t 
+rrset_get_ttl(struct ub_packed_rrset_key* k)
+{
+	struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data;
+	return d->ttl;
+}
+
+int
+nsecbitmap_has_type_rdata(uint8_t* bitmap, size_t len, uint16_t type)
+{
+	/* Check type present in NSEC typemap with bitmap arg */
+	/* bitmasks for determining type-lowerbits presence */
+	uint8_t masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
+	uint8_t type_window = type>>8;
+	uint8_t type_low = type&0xff;
+	uint8_t win, winlen;
+	/* read each of the type bitmap windows and see if the searched
+	 * type is amongst it */
+	while(len > 0) {
+		if(len < 3) /* bad window, at least window# winlen bitmap */
+			return 0;
+		win = *bitmap++;
+		winlen = *bitmap++;
+		len -= 2;
+		if(len < winlen || winlen < 1 || winlen > 32) 
+			return 0;	/* bad window length */
+		if(win == type_window) {
+			/* search window bitmap for the correct byte */
+			/* mybyte is 0 if we need the first byte */
+			size_t mybyte = type_low>>3;
+			if(winlen <= mybyte)
+				return 0; /* window too short */
+			return (int)(bitmap[mybyte] & masks[type_low&0x7]);
+		} else {
+			/* not the window we are looking for */
+			bitmap += winlen;
+			len -= winlen;
+		}
+	}
+	/* end of bitmap reached, no type found */
+	return 0;
+}
+
+int
+nsec_has_type(struct ub_packed_rrset_key* nsec, uint16_t type)
+{
+	struct packed_rrset_data* d = (struct packed_rrset_data*)nsec->
+		entry.data;
+	size_t len;
+	if(!d || d->count == 0 || d->rr_len[0] < 2+1)
+		return 0;
+	len = dname_valid(d->rr_data[0]+2, d->rr_len[0]-2);
+	if(!len)
+		return 0;
+	return nsecbitmap_has_type_rdata(d->rr_data[0]+2+len, 
+		d->rr_len[0]-2-len, type);
+}
+
+/**
+ * Get next owner name from nsec record
+ * @param nsec: the nsec RRset.
+ *	If there are multiple RRs, then this will only return one of them.
+ * @param nm: the next name is returned.
+ * @param ln: length of nm is returned.
+ * @return false on a bad NSEC RR (too short, malformed dname).
+ */
+static int 
+nsec_get_next(struct ub_packed_rrset_key* nsec, uint8_t** nm, size_t* ln)
+{
+	struct packed_rrset_data* d = (struct packed_rrset_data*)nsec->
+		entry.data;
+	if(!d || d->count == 0 || d->rr_len[0] < 2+1) {
+		*nm = 0;
+		*ln = 0;
+		return 0;
+	}
+	*nm = d->rr_data[0]+2;
+	*ln = dname_valid(*nm, d->rr_len[0]-2);
+	if(!*ln) {
+		*nm = 0;
+		*ln = 0;
+		return 0;
+	}
+	return 1;
+}
+
+/**
+ * For an NSEC that matches the DS queried for, check absence of DS type.
+ *
+ * @param nsec: NSEC for proof, must be trusted.
+ * @param qinfo: what is queried for.
+ * @return if secure the nsec proves that no DS is present, or 
+ *	insecure if it proves it is not a delegation point.
+ *	or bogus if something was wrong.
+ */
+static enum sec_status 
+val_nsec_proves_no_ds(struct ub_packed_rrset_key* nsec, 
+	struct query_info* qinfo)
+{
+	log_assert(qinfo->qtype == LDNS_RR_TYPE_DS);
+	log_assert(ntohs(nsec->rk.type) == LDNS_RR_TYPE_NSEC);
+
+	if(nsec_has_type(nsec, LDNS_RR_TYPE_SOA) && qinfo->qname_len != 1) {
+		/* SOA present means that this is the NSEC from the child, 
+		 * not the parent (so it is the wrong one). */
+		return sec_status_bogus;
+	}
+	if(nsec_has_type(nsec, LDNS_RR_TYPE_DS)) {
+		/* DS present means that there should have been a positive 
+		 * response to the DS query, so there is something wrong. */
+		return sec_status_bogus;
+	}
+
+	if(!nsec_has_type(nsec, LDNS_RR_TYPE_NS)) {
+		/* If there is no NS at this point at all, then this 
+		 * doesn't prove anything one way or the other. */
+		return sec_status_insecure;
+	}
+	/* Otherwise, this proves no DS. */
+	return sec_status_secure;
+}
+
+/** check security status from cache or verify rrset, returns true if secure */
+static int
+nsec_verify_rrset(struct module_env* env, struct val_env* ve, 
+	struct ub_packed_rrset_key* nsec, struct key_entry_key* kkey, 
+	char** reason)
+{
+	struct packed_rrset_data* d = (struct packed_rrset_data*)
+		nsec->entry.data;
+	if(d->security == sec_status_secure)
+		return 1;
+	rrset_check_sec_status(env->rrset_cache, nsec, *env->now);
+	if(d->security == sec_status_secure)
+		return 1;
+	d->security = val_verify_rrset_entry(env, ve, nsec, kkey, reason);
+	if(d->security == sec_status_secure) {
+		rrset_update_sec_status(env->rrset_cache, nsec, *env->now);
+		return 1;
+	}
+	return 0;
+}
+
+enum sec_status 
+val_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve, 
+	struct query_info* qinfo, struct reply_info* rep, 
+	struct key_entry_key* kkey, uint32_t* proof_ttl, char** reason)
+{
+	struct ub_packed_rrset_key* nsec = reply_find_rrset_section_ns(
+		rep, qinfo->qname, qinfo->qname_len, LDNS_RR_TYPE_NSEC, 
+		qinfo->qclass);
+	enum sec_status sec;
+	size_t i;
+	uint8_t* wc = NULL, *ce = NULL;
+	int valid_nsec = 0;
+	struct ub_packed_rrset_key* wc_nsec = NULL;
+
+	/* If we have a NSEC at the same name, it must prove one 
+	 * of two things
+	 * --
+	 * 1) this is a delegation point and there is no DS
+	 * 2) this is not a delegation point */
+	if(nsec) {
+		if(!nsec_verify_rrset(env, ve, nsec, kkey, reason)) {
+			verbose(VERB_ALGO, "NSEC RRset for the "
+				"referral did not verify.");
+			return sec_status_bogus;
+		}
+		sec = val_nsec_proves_no_ds(nsec, qinfo);
+		if(sec == sec_status_bogus) {
+			/* something was wrong. */
+			*reason = "NSEC does not prove absence of DS";
+			return sec;
+		} else if(sec == sec_status_insecure) {
+			/* this wasn't a delegation point. */
+			return sec;
+		} else if(sec == sec_status_secure) {
+			/* this proved no DS. */
+			*proof_ttl = ub_packed_rrset_ttl(nsec);
+			return sec;
+		}
+		/* if unchecked, fall through to next proof */
+	}
+
+	/* Otherwise, there is no NSEC at qname. This could be an ENT. 
+	 * (ENT=empty non terminal). If not, this is broken. */
+	
+	/* verify NSEC rrsets in auth section */
+	for(i=rep->an_numrrsets; i < rep->an_numrrsets+rep->ns_numrrsets; 
+		i++) {
+		if(rep->rrsets[i]->rk.type != htons(LDNS_RR_TYPE_NSEC))
+			continue;
+		if(!nsec_verify_rrset(env, ve, rep->rrsets[i], kkey, reason)) {
+			verbose(VERB_ALGO, "NSEC for empty non-terminal "
+				"did not verify.");
+			return sec_status_bogus;
+		}
+		if(nsec_proves_nodata(rep->rrsets[i], qinfo, &wc)) {
+			verbose(VERB_ALGO, "NSEC for empty non-terminal "
+				"proved no DS.");
+			*proof_ttl = rrset_get_ttl(rep->rrsets[i]);
+			if(wc && dname_is_wild(rep->rrsets[i]->rk.dname)) 
+				wc_nsec = rep->rrsets[i];
+			valid_nsec = 1;
+		}
+		if(val_nsec_proves_name_error(rep->rrsets[i], qinfo->qname)) {
+			ce = nsec_closest_encloser(qinfo->qname, 
+				rep->rrsets[i]);
+		}
+	}
+	if(wc && !ce)
+		valid_nsec = 0;
+	else if(wc && ce) {
+		/* ce and wc must match */
+		if(query_dname_compare(wc, ce) != 0) 
+			valid_nsec = 0;
+		else if(!wc_nsec)
+			valid_nsec = 0;
+	}
+	if(valid_nsec) {
+		if(wc) {
+			/* check if this is a delegation */
+			*reason = "NSEC for wildcard does not prove absence of DS";
+			return val_nsec_proves_no_ds(wc_nsec, qinfo);
+		}
+		/* valid nsec proves empty nonterminal */
+		return sec_status_insecure;
+	}
+
+	/* NSEC proof did not conlusively point to DS or no DS */
+	return sec_status_unchecked;
+}
+
+int nsec_proves_nodata(struct ub_packed_rrset_key* nsec, 
+	struct query_info* qinfo, uint8_t** wc)
+{
+	log_assert(wc);
+	if(query_dname_compare(nsec->rk.dname, qinfo->qname) != 0) {
+		uint8_t* nm;
+		size_t ln;
+
+		/* empty-non-terminal checking. 
+		 * Done before wildcard, because this is an exact match,
+		 * and would prevent a wildcard from matching. */
+
+		/* If the nsec is proving that qname is an ENT, the nsec owner 
+		 * will be less than qname, and the next name will be a child 
+		 * domain of the qname. */
+		if(!nsec_get_next(nsec, &nm, &ln))
+			return 0; /* bad nsec */
+		if(dname_strict_subdomain_c(nm, qinfo->qname) &&
+			dname_canonical_compare(nsec->rk.dname, 
+				qinfo->qname) < 0) {
+			return 1; /* proves ENT */
+		}
+
+		/* wildcard checking. */
+
+		/* If this is a wildcard NSEC, make sure that a) it was 
+		 * possible to have generated qname from the wildcard and 
+		 * b) the type map does not contain qtype. Note that this 
+		 * does NOT prove that this wildcard was the applicable 
+		 * wildcard. */
+		if(dname_is_wild(nsec->rk.dname)) {
+			/* the purported closest encloser. */
+			uint8_t* ce = nsec->rk.dname;
+			size_t ce_len = nsec->rk.dname_len;
+			dname_remove_label(&ce, &ce_len);
+
+			/* The qname must be a strict subdomain of the 
+			 * closest encloser, for the wildcard to apply 
+			 */
+			if(dname_strict_subdomain_c(qinfo->qname, ce)) {
+				/* here we have a matching NSEC for the qname,
+				 * perform matching NSEC checks */
+				if(nsec_has_type(nsec, LDNS_RR_TYPE_CNAME)) {
+				   /* should have gotten the wildcard CNAME */
+					return 0;
+				}
+				if(nsec_has_type(nsec, LDNS_RR_TYPE_NS) && 
+				   !nsec_has_type(nsec, LDNS_RR_TYPE_SOA)) {
+				   /* wrong parentside (wildcard) NSEC used */
+					return 0;
+				}
+				if(nsec_has_type(nsec, qinfo->qtype)) {
+					return 0;
+				}
+				*wc = ce;
+				return 1;
+			}
+		}
+
+		/* Otherwise, this NSEC does not prove ENT and is not a 
+		 * wildcard, so it does not prove NODATA. */
+		return 0;
+	}
+
+	/* If the qtype exists, then we should have gotten it. */
+	if(nsec_has_type(nsec, qinfo->qtype)) {
+		return 0;
+	}
+
+	/* if the name is a CNAME node, then we should have gotten the CNAME*/
+	if(nsec_has_type(nsec, LDNS_RR_TYPE_CNAME)) {
+		return 0;
+	}
+
+	/* If an NS set exists at this name, and NOT a SOA (so this is a 
+	 * zone cut, not a zone apex), then we should have gotten a 
+	 * referral (or we just got the wrong NSEC). 
+	 * The reverse of this check is used when qtype is DS, since that
+	 * must use the NSEC from above the zone cut. */
+	if(qinfo->qtype != LDNS_RR_TYPE_DS &&
+		nsec_has_type(nsec, LDNS_RR_TYPE_NS) && 
+		!nsec_has_type(nsec, LDNS_RR_TYPE_SOA)) {
+		return 0;
+	} else if(qinfo->qtype == LDNS_RR_TYPE_DS &&
+		nsec_has_type(nsec, LDNS_RR_TYPE_SOA &&
+		!dname_is_root(qinfo->qname))) {
+		return 0;
+	}
+
+	return 1;
+}
+
+int 
+val_nsec_proves_name_error(struct ub_packed_rrset_key* nsec, uint8_t* qname)
+{
+	uint8_t* owner = nsec->rk.dname;
+	uint8_t* next;
+	size_t nlen;
+	if(!nsec_get_next(nsec, &next, &nlen))
+		return 0;
+
+	/* If NSEC owner == qname, then this NSEC proves that qname exists. */
+	if(query_dname_compare(qname, owner) == 0) {
+		return 0;
+	}
+
+	/* If NSEC is a parent of qname, we need to check the type map
+	 * If the parent name has a DNAME or is a delegation point, then 
+	 * this NSEC is being misused. */
+	if(dname_subdomain_c(qname, owner) && 
+		(nsec_has_type(nsec, LDNS_RR_TYPE_DNAME) ||
+		(nsec_has_type(nsec, LDNS_RR_TYPE_NS) 
+			&& !nsec_has_type(nsec, LDNS_RR_TYPE_SOA))
+		)) {
+		return 0;
+	}
+
+	if(query_dname_compare(owner, next) == 0) {
+		/* this nsec is the only nsec */
+		/* zone.name NSEC zone.name, disproves everything else */
+		/* but only for subdomains of that zone */
+		if(dname_strict_subdomain_c(qname, next))
+			return 1;
+	}
+	else if(dname_canonical_compare(owner, next) > 0) {
+		/* this is the last nsec, ....(bigger) NSEC zonename(smaller) */
+		/* the names after the last (owner) name do not exist 
+		 * there are no names before the zone name in the zone 
+		 * but the qname must be a subdomain of the zone name(next). */
+		if(dname_canonical_compare(owner, qname) < 0 &&
+			dname_strict_subdomain_c(qname, next))
+			return 1;
+	} else {
+		/* regular NSEC, (smaller) NSEC (larger) */
+		if(dname_canonical_compare(owner, qname) < 0 &&
+		   dname_canonical_compare(qname, next) < 0) {
+			return 1;
+		}
+	}
+	return 0;
+}
+
+int val_nsec_proves_insecuredelegation(struct ub_packed_rrset_key* nsec, 
+	struct query_info* qinfo)
+{
+	if(nsec_has_type(nsec, LDNS_RR_TYPE_NS) &&
+		!nsec_has_type(nsec, LDNS_RR_TYPE_DS) &&
+		!nsec_has_type(nsec, LDNS_RR_TYPE_SOA)) {
+		/* see if nsec signals an insecure delegation */
+		if(qinfo->qtype == LDNS_RR_TYPE_DS) {
+			/* if type is DS and qname is equal to nsec, then it
+			 * is an exact match nsec, result not insecure */
+			if(dname_strict_subdomain_c(qinfo->qname,
+				nsec->rk.dname))
+				return 1;
+		} else {
+			if(dname_subdomain_c(qinfo->qname, nsec->rk.dname))
+				return 1;
+		}
+	}
+	return 0;
+}
+
+uint8_t* 
+nsec_closest_encloser(uint8_t* qname, struct ub_packed_rrset_key* nsec)
+{
+	uint8_t* next;
+	size_t nlen;
+	uint8_t* common1, *common2;
+	if(!nsec_get_next(nsec, &next, &nlen))
+		return NULL;
+	/* longest common with owner or next name */
+	common1 = dname_get_shared_topdomain(nsec->rk.dname, qname);
+	common2 = dname_get_shared_topdomain(next, qname);
+	if(dname_count_labels(common1) > dname_count_labels(common2))
+		return common1;
+	return common2;
+}
+
+int val_nsec_proves_positive_wildcard(struct ub_packed_rrset_key* nsec, 
+	struct query_info* qinf, uint8_t* wc)
+{
+	uint8_t* ce;
+	/*  1) prove that qname doesn't exist and 
+	 *  2) that the correct wildcard was used
+	 *  nsec has been verified already. */
+	if(!val_nsec_proves_name_error(nsec, qinf->qname))
+		return 0;
+	/* check wildcard name */
+	ce = nsec_closest_encloser(qinf->qname, nsec);
+	if(!ce)
+		return 0;
+	if(query_dname_compare(wc, ce) != 0) {
+		return 0;
+	}
+	return 1;
+}
+
+int 
+val_nsec_proves_no_wc(struct ub_packed_rrset_key* nsec, uint8_t* qname, 
+	size_t qnamelen)
+{
+	/* Determine if a NSEC record proves the non-existence of a 
+	 * wildcard that could have produced qname. */
+	int labs;
+	int i;
+	uint8_t* ce = nsec_closest_encloser(qname, nsec);
+	uint8_t* strip;
+	size_t striplen;
+	uint8_t buf[LDNS_MAX_DOMAINLEN+3];
+	if(!ce)
+		return 0;
+	/* we can subtract the closest encloser count - since that is the
+	 * largest shared topdomain with owner and next NSEC name,
+	 * because the NSEC is no proof for names shorter than the owner 
+	 * and next names. */
+	labs = dname_count_labels(qname) - dname_count_labels(ce);
+
+	for(i=labs; i>0; i--) {
+		/* i is number of labels to strip off qname, prepend * wild */
+		strip = qname;
+		striplen = qnamelen;
+		dname_remove_labels(&strip, &striplen, i);
+		if(striplen > LDNS_MAX_DOMAINLEN-2)
+			continue; /* too long to prepend wildcard */
+		buf[0] = 1;
+		buf[1] = (uint8_t)'*';
+		memmove(buf+2, strip, striplen);
+		if(val_nsec_proves_name_error(nsec, buf)) {
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/**
+ * Find shared topdomain that exists
+ */
+static void
+dlv_topdomain(struct ub_packed_rrset_key* nsec, uint8_t* qname,
+	uint8_t** nm, size_t* nm_len)
+{
+	/* make sure reply is part of nm */
+	/* take shared topdomain with left of NSEC. */
+
+	/* because, if empty nonterminal, then right is subdomain of qname.
+	 * and any shared topdomain would be empty nonterminals.
+	 * 
+	 * If nxdomain, then the right is bigger, and could have an 
+	 * interesting shared topdomain, but if it does have one, it is
+	 * an empty nonterminal. An empty nonterminal shared with the left
+	 * one. */
+	int n;
+	uint8_t* common = dname_get_shared_topdomain(qname, nsec->rk.dname);
+	n = dname_count_labels(*nm) - dname_count_labels(common);
+	dname_remove_labels(nm, nm_len, n);
+}
+
+int val_nsec_check_dlv(struct query_info* qinfo,
+        struct reply_info* rep, uint8_t** nm, size_t* nm_len)
+{
+	uint8_t* next;
+	size_t i, nlen;
+	int c;
+	/* we should now have a NOERROR/NODATA or NXDOMAIN message */
+	if(rep->an_numrrsets != 0) {
+		return 0;
+	}
+	/* is this NOERROR ? */
+	if(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NOERROR) {
+		/* it can be a plain NSEC match - go up one more level. */
+		/* or its an empty nonterminal - go up to nonempty level */
+		for(i=0; i<rep->ns_numrrsets; i++) {
+			if(htons(rep->rrsets[i]->rk.type)!=LDNS_RR_TYPE_NSEC ||
+				!nsec_get_next(rep->rrsets[i], &next, &nlen))
+				continue;
+			c = dname_canonical_compare(
+				rep->rrsets[i]->rk.dname, qinfo->qname);
+			if(c == 0) {
+				/* plain match */
+				if(nsec_has_type(rep->rrsets[i],
+					LDNS_RR_TYPE_DLV))
+					return 0;
+				dname_remove_label(nm, nm_len);
+				return 1;
+			} else if(c < 0 && 
+				dname_strict_subdomain_c(next, qinfo->qname)) {
+				/* ENT */
+				dlv_topdomain(rep->rrsets[i], qinfo->qname,
+					nm, nm_len);
+				return 1;
+			}
+		}
+		return 0;
+	}
+
+	/* is this NXDOMAIN ? */
+	if(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NXDOMAIN) {
+		/* find the qname denial NSEC record. It can tell us
+		 * a closest encloser name; or that we not need bother */
+		for(i=0; i<rep->ns_numrrsets; i++) {
+			if(htons(rep->rrsets[i]->rk.type) != LDNS_RR_TYPE_NSEC)
+				continue;
+			if(val_nsec_proves_name_error(rep->rrsets[i], 
+				qinfo->qname)) {
+				log_nametypeclass(VERB_ALGO, "topdomain on",
+					rep->rrsets[i]->rk.dname, 
+					ntohs(rep->rrsets[i]->rk.type), 0);
+				dlv_topdomain(rep->rrsets[i], qinfo->qname,
+					nm, nm_len);
+				return 1;
+			}
+		}
+		return 0;
+	}
+	return 0;
+}
diff --git a/3rdParty/Unbound/src/src/validator/val_nsec.h b/3rdParty/Unbound/src/src/validator/val_nsec.h
new file mode 100644
index 0000000..34f7f63
--- /dev/null
+++ b/3rdParty/Unbound/src/src/validator/val_nsec.h
@@ -0,0 +1,182 @@
+/*
+ * validator/val_nsec.h - validator NSEC denial of existance functions.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains helper functions for the validator module.
+ * The functions help with NSEC checking, the different NSEC proofs
+ * for denial of existance, and proofs for presence of types.
+ */
+
+#ifndef VALIDATOR_VAL_NSEC_H
+#define VALIDATOR_VAL_NSEC_H
+#include "util/data/packed_rrset.h"
+struct val_env;
+struct module_env;
+struct ub_packed_rrset_key;
+struct reply_info;
+struct query_info;
+struct key_entry_key;
+
+/**
+ * Check DS absence.
+ * There is a NODATA reply to a DS that needs checking.
+ * NSECs can prove this is not a delegation point, or sucessfully prove
+ * that there is no DS. Or this fails.
+ *
+ * @param env: module env for rrsig verification routines.
+ * @param ve: validator env for rrsig verification routines.
+ * @param qinfo: the DS queried for.
+ * @param rep: reply received.
+ * @param kkey: key entry to use for verification of signatures.
+ * @param proof_ttl: if secure, the TTL of how long this proof lasts.
+ * @param reason: string explaining why bogus.
+ * @return security status.
+ *	SECURE: proved absence of DS.
+ *	INSECURE: proved that this was not a delegation point.
+ *	BOGUS: crypto bad, or no absence of DS proven. 
+ *	UNCHECKED: there was no way to prove anything (no NSECs, unknown algo).
+ */
+enum sec_status val_nsec_prove_nodata_dsreply(struct module_env* env,
+	struct val_env* ve, struct query_info* qinfo, 
+	struct reply_info* rep, struct key_entry_key* kkey,
+	uint32_t* proof_ttl, char** reason);
+
+/** 
+ * nsec typemap check, takes an NSEC-type bitmap as argument, checks for type.
+ * @param bitmap: pointer to the bitmap part of wireformat rdata.
+ * @param len: length of the bitmap, in bytes.
+ * @param type: the type (in host order) to check for.
+ * @return true if the type bit was set in the bitmap. false if not, or
+ * 	if the bitmap was malformed in some way.
+ */
+int nsecbitmap_has_type_rdata(uint8_t* bitmap, size_t len, uint16_t type);
+
+/**
+ * Check if type is present in the NSEC typemap
+ * @param nsec: the nsec RRset.
+ *	If there are multiple RRs, then each must have the same typemap,
+ *	since the typemap represents the types at this domain node.
+ * @param type: type to check for, host order.
+ * @return true if present
+ */
+int nsec_has_type(struct ub_packed_rrset_key* nsec, uint16_t type);
+
+/**
+ * Determine if a NSEC proves the NOERROR/NODATA conditions. This will also
+ * handle the empty non-terminal (ENT) case and partially handle the
+ * wildcard case. If the ownername of 'nsec' is a wildcard, the validator
+ * must still be provided proof that qname did not directly exist and that
+ * the wildcard is, in fact, *.closest_encloser.
+ *
+ * @param nsec: the nsec record to check against.
+ * @param qinfo: the query info.
+ * @param wc: if the nodata is proven for a wildcard match, the wildcard
+ * 	closest encloser is returned, else NULL (wc is unchanged).
+ * 	This closest encloser must then match the nameerror given for the
+ * 	nextcloser of qname.
+ * @return true if NSEC proves this.
+ */
+int nsec_proves_nodata(struct ub_packed_rrset_key* nsec, 
+	struct query_info* qinfo, uint8_t** wc);
+
+/**
+ * Determine if the given NSEC proves a NameError (NXDOMAIN) for a given
+ * qname.
+ *
+ * @param nsec: the nsec to check
+ * @param qname: what was queried.
+ * @return true if proven.
+ */
+int val_nsec_proves_name_error(struct ub_packed_rrset_key* nsec, 
+	uint8_t* qname);
+
+/**
+ * Determine if the given NSEC proves a positive wildcard response.
+ * @param nsec: the nsec to check
+ * @param qinf: what was queried.
+ * @param wc: wildcard (without *. label)
+ * @return true if proven.
+ */
+int val_nsec_proves_positive_wildcard(struct ub_packed_rrset_key* nsec, 
+	struct query_info* qinf, uint8_t* wc);
+
+/**
+ * Determine closest encloser of a query name and the NSEC that covers it
+ * (and thus disproved it). 
+ * A name error must have been proven already, otherwise this will be invalid.
+ * @param qname: the name queried for.
+ * @param nsec: the nsec RRset.
+ * @return closest encloser dname or NULL on error (bad nsec RRset).
+ */
+uint8_t* nsec_closest_encloser(uint8_t* qname, 
+	struct ub_packed_rrset_key* nsec);
+
+/**
+ * Determine if the given NSEC proves that a wildcard match does not exist.
+ *
+ * @param nsec: the nsec RRset.
+ * @param qname: the name queried for.
+ * @param qnamelen: length of qname.
+ * @return true if proven.
+ */
+int val_nsec_proves_no_wc(struct ub_packed_rrset_key* nsec, uint8_t* qname, 
+	size_t qnamelen);
+
+/**
+ * Determine the DLV result, what to do with NSEC DLV reply.
+ * @param qinfo: what was queried for.
+ * @param rep: the nonpositive reply.
+ * @param nm: dlv lookup name, to adjust for new lookup name (if needed).
+ * @param nm_len: length of lookup name.
+ * @return 0 on error, 1 if a higher point is found.
+ * 	If the higher point is above the dlv repo anchor, the qname does 
+ * 	not exist.
+ */
+int val_nsec_check_dlv(struct query_info* qinfo,
+	struct reply_info* rep, uint8_t** nm, size_t* nm_len);
+
+/**
+ * Determine if an nsec proves an insecure delegation towards the qname.
+ * @param nsec: nsec rrset.
+ * @param qinfo: what was queries for.
+ * @return 0 if not, 1 if an NSEC that signals an insecure delegation to
+ * 	the qname.
+ */
+int val_nsec_proves_insecuredelegation(struct ub_packed_rrset_key* nsec,
+        struct query_info* qinfo);
+
+#endif /* VALIDATOR_VAL_NSEC_H */
diff --git a/3rdParty/Unbound/src/src/validator/val_nsec3.c b/3rdParty/Unbound/src/src/validator/val_nsec3.c
new file mode 100644
index 0000000..a18e3ab
--- /dev/null
+++ b/3rdParty/Unbound/src/src/validator/val_nsec3.c
@@ -0,0 +1,1446 @@
+/*
+ * validator/val_nsec3.c - validator NSEC3 denial of existance functions.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains helper functions for the validator module.
+ * The functions help with NSEC3 checking, the different NSEC3 proofs
+ * for denial of existance, and proofs for presence of types.
+ */
+#include "config.h"
+#include <ctype.h>
+#ifdef HAVE_OPENSSL_SSL_H
+#include "openssl/ssl.h"
+#endif
+#include "validator/val_nsec3.h"
+#include "validator/validator.h"
+#include "validator/val_kentry.h"
+#include "services/cache/rrset.h"
+#include "util/regional.h"
+#include "util/rbtree.h"
+#include "util/module.h"
+#include "util/net_help.h"
+#include "util/data/packed_rrset.h"
+#include "util/data/dname.h"
+#include "util/data/msgreply.h"
+/* we include nsec.h for the bitmap_has_type function */
+#include "validator/val_nsec.h"
+
+/** 
+ * This function we get from ldns-compat or from base system 
+ * it returns the number of data bytes stored at the target, or <0 on error.
+ */
+int ldns_b32_ntop_extended_hex(uint8_t const *src, size_t srclength,
+	char *target, size_t targsize);
+/** 
+ * This function we get from ldns-compat or from base system 
+ * it returns the number of data bytes stored at the target, or <0 on error.
+ */
+int ldns_b32_pton_extended_hex(char const *src, size_t hashed_owner_str_len, 
+	uint8_t *target, size_t targsize);
+
+/**
+ * Closest encloser (ce) proof results
+ * Contains the ce and the next-closer (nc) proof.
+ */
+struct ce_response {
+	/** the closest encloser name */
+	uint8_t* ce;
+	/** length of ce */
+	size_t ce_len;
+	/** NSEC3 record that proved ce. rrset */
+	struct ub_packed_rrset_key* ce_rrset;
+	/** NSEC3 record that proved ce. rr number */
+	int ce_rr;
+	/** NSEC3 record that proved nc. rrset */
+	struct ub_packed_rrset_key* nc_rrset;
+	/** NSEC3 record that proved nc. rr*/
+	int nc_rr;
+};
+
+/**
+ * Filter conditions for NSEC3 proof
+ * Used to iterate over the applicable NSEC3 RRs.
+ */
+struct nsec3_filter {
+	/** Zone name, only NSEC3 records for this zone are considered */
+	uint8_t* zone;
+	/** length of the zonename */
+	size_t zone_len;
+	/** the list of NSEC3s to filter; array */
+	struct ub_packed_rrset_key** list;
+	/** number of rrsets in list */
+	size_t num;
+	/** class of records for the NSEC3, only this class applies */
+	uint16_t fclass;
+};
+
+/** return number of rrs in an rrset */
+static size_t
+rrset_get_count(struct ub_packed_rrset_key* rrset)
+{
+        struct packed_rrset_data* d = (struct packed_rrset_data*)
+	        rrset->entry.data;
+        if(!d) return 0;
+        return d->count;
+}
+
+/** return if nsec3 RR has unknown flags */
+static int
+nsec3_unknown_flags(struct ub_packed_rrset_key* rrset, int r)
+{
+        struct packed_rrset_data* d = (struct packed_rrset_data*)
+	        rrset->entry.data;
+	log_assert(d && r < (int)d->count);
+	if(d->rr_len[r] < 2+2)
+		return 0; /* malformed */
+	return (int)(d->rr_data[r][2+1] & NSEC3_UNKNOWN_FLAGS);
+}
+
+int
+nsec3_has_optout(struct ub_packed_rrset_key* rrset, int r)
+{
+        struct packed_rrset_data* d = (struct packed_rrset_data*)
+	        rrset->entry.data;
+	log_assert(d && r < (int)d->count);
+	if(d->rr_len[r] < 2+2)
+		return 0; /* malformed */
+	return (int)(d->rr_data[r][2+1] & NSEC3_OPTOUT);
+}
+
+/** return nsec3 RR algorithm */
+static int
+nsec3_get_algo(struct ub_packed_rrset_key* rrset, int r)
+{
+        struct packed_rrset_data* d = (struct packed_rrset_data*)
+	        rrset->entry.data;
+	log_assert(d && r < (int)d->count);
+	if(d->rr_len[r] < 2+1)
+		return 0; /* malformed */
+	return (int)(d->rr_data[r][2+0]);
+}
+
+/** return if nsec3 RR has known algorithm */
+static int
+nsec3_known_algo(struct ub_packed_rrset_key* rrset, int r)
+{
+        struct packed_rrset_data* d = (struct packed_rrset_data*)
+	        rrset->entry.data;
+	log_assert(d && r < (int)d->count);
+	if(d->rr_len[r] < 2+1)
+		return 0; /* malformed */
+	switch(d->rr_data[r][2+0]) {
+		case NSEC3_HASH_SHA1:
+			return 1;
+	}
+	return 0;
+}
+
+/** return nsec3 RR iteration count */
+static size_t
+nsec3_get_iter(struct ub_packed_rrset_key* rrset, int r)
+{
+	uint16_t i;
+        struct packed_rrset_data* d = (struct packed_rrset_data*)
+	        rrset->entry.data;
+	log_assert(d && r < (int)d->count);
+	if(d->rr_len[r] < 2+4)
+		return 0; /* malformed */
+	memmove(&i, d->rr_data[r]+2+2, sizeof(i));
+	i = ntohs(i);
+	return (size_t)i;
+}
+
+/** return nsec3 RR salt */
+static int
+nsec3_get_salt(struct ub_packed_rrset_key* rrset, int r,
+	uint8_t** salt, size_t* saltlen)
+{
+        struct packed_rrset_data* d = (struct packed_rrset_data*)
+	        rrset->entry.data;
+	log_assert(d && r < (int)d->count);
+	if(d->rr_len[r] < 2+5) {
+		*salt = 0;
+		*saltlen = 0;
+		return 0; /* malformed */
+	}
+	*saltlen = (size_t)d->rr_data[r][2+4];
+	if(d->rr_len[r] < 2+5+(size_t)*saltlen) {
+		*salt = 0;
+		*saltlen = 0;
+		return 0; /* malformed */
+	}
+	*salt = d->rr_data[r]+2+5;
+	return 1;
+}
+
+int nsec3_get_params(struct ub_packed_rrset_key* rrset, int r,
+	int* algo, size_t* iter, uint8_t** salt, size_t* saltlen)
+{
+	if(!nsec3_known_algo(rrset, r) || nsec3_unknown_flags(rrset, r))
+		return 0;
+	if(!nsec3_get_salt(rrset, r, salt, saltlen))
+		return 0;
+	*algo = nsec3_get_algo(rrset, r);
+	*iter = nsec3_get_iter(rrset, r);
+	return 1;
+}
+
+int
+nsec3_get_nextowner(struct ub_packed_rrset_key* rrset, int r,
+	uint8_t** next, size_t* nextlen)
+{
+	size_t saltlen;
+        struct packed_rrset_data* d = (struct packed_rrset_data*)
+	        rrset->entry.data;
+	log_assert(d && r < (int)d->count);
+	if(d->rr_len[r] < 2+5) {
+		*next = 0;
+		*nextlen = 0;
+		return 0; /* malformed */
+	}
+	saltlen = (size_t)d->rr_data[r][2+4];
+	if(d->rr_len[r] < 2+5+saltlen+1) {
+		*next = 0;
+		*nextlen = 0;
+		return 0; /* malformed */
+	}
+	*nextlen = (size_t)d->rr_data[r][2+5+saltlen];
+	if(d->rr_len[r] < 2+5+saltlen+1+*nextlen) {
+		*next = 0;
+		*nextlen = 0;
+		return 0; /* malformed */
+	}
+	*next = d->rr_data[r]+2+5+saltlen+1;
+	return 1;
+}
+
+size_t nsec3_hash_to_b32(uint8_t* hash, size_t hashlen, uint8_t* zone,
+	size_t zonelen, uint8_t* buf, size_t max)
+{
+	/* write b32 of name, leave one for length */
+	int ret;
+	if(max < hashlen*2+1) /* quick approx of b32, as if hexb16 */
+		return 0;
+	ret = ldns_b32_ntop_extended_hex(hash, hashlen, (char*)buf+1, max-1);
+	if(ret < 1) 
+		return 0;
+	buf[0] = (uint8_t)ret; /* length of b32 label */
+	ret++;
+	if(max - ret < zonelen)
+		return 0;
+	memmove(buf+ret, zone, zonelen);
+	return zonelen+(size_t)ret;
+}
+
+size_t nsec3_get_nextowner_b32(struct ub_packed_rrset_key* rrset, int r,
+	uint8_t* buf, size_t max)
+{
+	uint8_t* nm, *zone;
+	size_t nmlen, zonelen;
+	if(!nsec3_get_nextowner(rrset, r, &nm, &nmlen))
+		return 0;
+	/* append zone name; the owner name must be <b32>.zone */
+	zone = rrset->rk.dname;
+	zonelen = rrset->rk.dname_len;
+	dname_remove_label(&zone, &zonelen);
+	return nsec3_hash_to_b32(nm, nmlen, zone, zonelen, buf, max);
+}
+
+int
+nsec3_has_type(struct ub_packed_rrset_key* rrset, int r, uint16_t type)
+{
+	uint8_t* bitmap;
+	size_t bitlen, skiplen;
+        struct packed_rrset_data* d = (struct packed_rrset_data*)
+	        rrset->entry.data;
+	log_assert(d && r < (int)d->count);
+	skiplen = 2+4;
+	/* skip salt */
+	if(d->rr_len[r] < skiplen+1)
+		return 0; /* malformed, too short */
+	skiplen += 1+(size_t)d->rr_data[r][skiplen]; 
+	/* skip next hashed owner */
+	if(d->rr_len[r] < skiplen+1)
+		return 0; /* malformed, too short */
+	skiplen += 1+(size_t)d->rr_data[r][skiplen]; 
+	if(d->rr_len[r] < skiplen)
+		return 0; /* malformed, too short */
+	bitlen = d->rr_len[r] - skiplen;
+	bitmap = d->rr_data[r]+skiplen;
+	return nsecbitmap_has_type_rdata(bitmap, bitlen, type);
+}
+	
+/** 
+ * Iterate through NSEC3 list, per RR 
+ * This routine gives the next RR in the list (or sets rrset null). 
+ * Usage:
+ *
+ * size_t rrsetnum;
+ * int rrnum;
+ * struct ub_packed_rrset_key* rrset;
+ * for(rrset=filter_first(filter, &rrsetnum, &rrnum); rrset; 
+ *	rrset=filter_next(filter, &rrsetnum, &rrnum))
+ *		do_stuff;
+ * 
+ * Also filters out 
+ * 	o unknown flag NSEC3s
+ * 	o unknown algorithm NSEC3s.
+ * @param filter: nsec3 filter structure.
+ * @param rrsetnum: in/out rrset number to look at.
+ * @param rrnum: in/out rr number in rrset to look at.
+ * @returns ptr to the next rrset (or NULL at end).
+ */
+static struct ub_packed_rrset_key*
+filter_next(struct nsec3_filter* filter, size_t* rrsetnum, int* rrnum)
+{
+	size_t i;
+	int r;
+	uint8_t* nm;
+	size_t nmlen;
+	if(!filter->zone) /* empty list */
+		return NULL;
+	for(i=*rrsetnum; i<filter->num; i++) {
+		/* see if RRset qualifies */
+		if(ntohs(filter->list[i]->rk.type) != LDNS_RR_TYPE_NSEC3 ||
+			ntohs(filter->list[i]->rk.rrset_class) != 
+			filter->fclass) 
+			continue;
+		/* check RRset zone */
+		nm = filter->list[i]->rk.dname;
+		nmlen = filter->list[i]->rk.dname_len;
+		dname_remove_label(&nm, &nmlen);
+		if(query_dname_compare(nm, filter->zone) != 0)
+			continue;
+		if(i == *rrsetnum)
+			r = (*rrnum) + 1; /* continue at next RR */
+		else	r = 0;		/* new RRset start at first RR */
+		for(; r < (int)rrset_get_count(filter->list[i]); r++) {
+			/* skip unknown flags, algo */
+			if(nsec3_unknown_flags(filter->list[i], r) ||
+				!nsec3_known_algo(filter->list[i], r))
+				continue;
+			/* this one is a good target */
+			*rrsetnum = i;
+			*rrnum = r;
+			return filter->list[i];
+		}
+	}
+	return NULL;
+}
+
+/**
+ * Start iterating over NSEC3 records.
+ * @param filter: the filter structure, must have been filter_init-ed.
+ * @param rrsetnum: can be undefined on call, inited.
+ * @param rrnum: can be undefined on call, inited.
+ * @return first rrset of an NSEC3, together with rrnum this points to
+ *	the first RR to examine. Is NULL on empty list.
+ */
+static struct ub_packed_rrset_key*
+filter_first(struct nsec3_filter* filter, size_t* rrsetnum, int* rrnum)
+{
+	*rrsetnum = 0;
+	*rrnum = -1;
+	return filter_next(filter, rrsetnum, rrnum);
+}
+
+/** see if at least one RR is known (flags, algo) */
+static int
+nsec3_rrset_has_known(struct ub_packed_rrset_key* s)
+{
+	int r;
+	for(r=0; r < (int)rrset_get_count(s); r++) {
+		if(!nsec3_unknown_flags(s, r) && nsec3_known_algo(s, r))
+			return 1;
+	}
+	return 0;
+}
+
+/** 
+ * Initialize the filter structure.
+ * Finds the zone by looking at available NSEC3 records and best match.
+ * 	(skips the unknown flag and unknown algo NSEC3s).
+ *
+ * @param filter: nsec3 filter structure.
+ * @param list: list of rrsets, an array of them.
+ * @param num: number of rrsets in list.
+ * @param qinfo: 
+ *	query name to match a zone for.
+ *	query type (if DS a higher zone must be chosen)
+ *	qclass, to filter NSEC3s with.
+ */
+static void
+filter_init(struct nsec3_filter* filter, struct ub_packed_rrset_key** list,
+	size_t num, struct query_info* qinfo)
+{
+	size_t i;
+	uint8_t* nm;
+	size_t nmlen;
+	filter->zone = NULL;
+	filter->zone_len = 0;
+	filter->list = list;
+	filter->num = num;
+	filter->fclass = qinfo->qclass;
+	for(i=0; i<num; i++) {
+		/* ignore other stuff in the list */
+		if(ntohs(list[i]->rk.type) != LDNS_RR_TYPE_NSEC3 ||
+			ntohs(list[i]->rk.rrset_class) != qinfo->qclass) 
+			continue;
+		/* skip unknown flags, algo */
+		if(!nsec3_rrset_has_known(list[i]))
+			continue;
+
+		/* since NSEC3s are base32.zonename, we can find the zone
+		 * name by stripping off the first label of the record */
+		nm = list[i]->rk.dname;
+		nmlen = list[i]->rk.dname_len;
+		dname_remove_label(&nm, &nmlen);
+		/* if we find a domain that can prove about the qname,
+		 * and if this domain is closer to the qname */
+		if(dname_subdomain_c(qinfo->qname, nm) && (!filter->zone ||
+			dname_subdomain_c(nm, filter->zone))) {
+			/* for a type DS do not accept a zone equal to qname*/
+			if(qinfo->qtype == LDNS_RR_TYPE_DS && 
+				query_dname_compare(qinfo->qname, nm) == 0 &&
+				!dname_is_root(qinfo->qname))
+				continue;
+			filter->zone = nm;
+			filter->zone_len = nmlen;
+		}
+	}
+}
+
+/**
+ * Find max iteration count using config settings and key size
+ * @param ve: validator environment with iteration count config settings.
+ * @param bits: key size
+ * @return max iteration count
+ */
+static size_t
+get_max_iter(struct val_env* ve, size_t bits)
+{
+	int i;
+	log_assert(ve->nsec3_keyiter_count > 0);
+	/* round up to nearest config keysize, linear search, keep it small */
+	for(i=0; i<ve->nsec3_keyiter_count; i++) {
+		if(bits <= ve->nsec3_keysize[i])
+			return ve->nsec3_maxiter[i];
+	}
+	/* else, use value for biggest key */
+	return ve->nsec3_maxiter[ve->nsec3_keyiter_count-1];
+}
+
+/** 
+ * Determine if any of the NSEC3 rrs iteration count is too high, from key.
+ * @param ve: validator environment with iteration count config settings.
+ * @param filter: what NSEC3s to loop over.
+ * @param kkey: key entry used for verification; used for iteration counts.
+ * @return 1 if some nsec3s are above the max iteration count.
+ */
+static int
+nsec3_iteration_count_high(struct val_env* ve, struct nsec3_filter* filter, 
+	struct key_entry_key* kkey)
+{
+	size_t rrsetnum;
+	int rrnum;
+	struct ub_packed_rrset_key* rrset;
+	/* first determine the max number of iterations */
+	size_t bits = key_entry_keysize(kkey);
+	size_t max_iter = get_max_iter(ve, bits);
+	verbose(VERB_ALGO, "nsec3: keysize %d bits, max iterations %d",
+		(int)bits, (int)max_iter);
+
+	for(rrset=filter_first(filter, &rrsetnum, &rrnum); rrset; 
+		rrset=filter_next(filter, &rrsetnum, &rrnum)) {
+		if(nsec3_get_iter(rrset, rrnum) > max_iter)
+			return 1;
+	}
+	return 0;
+}
+
+/* nsec3_cache_compare for rbtree */
+int
+nsec3_hash_cmp(const void* c1, const void* c2) 
+{
+	struct nsec3_cached_hash* h1 = (struct nsec3_cached_hash*)c1;
+	struct nsec3_cached_hash* h2 = (struct nsec3_cached_hash*)c2;
+	uint8_t* s1, *s2;
+	size_t s1len, s2len;
+	int c = query_dname_compare(h1->dname, h2->dname);
+	if(c != 0)
+		return c;
+	/* compare parameters */
+	/* if both malformed, its equal, robustness */
+	if(nsec3_get_algo(h1->nsec3, h1->rr) !=
+		nsec3_get_algo(h2->nsec3, h2->rr)) {
+		if(nsec3_get_algo(h1->nsec3, h1->rr) <
+			nsec3_get_algo(h2->nsec3, h2->rr))
+			return -1;
+		return 1;
+	}
+	if(nsec3_get_iter(h1->nsec3, h1->rr) !=
+		nsec3_get_iter(h2->nsec3, h2->rr)) {
+		if(nsec3_get_iter(h1->nsec3, h1->rr) <
+			nsec3_get_iter(h2->nsec3, h2->rr))
+			return -1;
+		return 1;
+	}
+	(void)nsec3_get_salt(h1->nsec3, h1->rr, &s1, &s1len);
+	(void)nsec3_get_salt(h2->nsec3, h2->rr, &s2, &s2len);
+	if(s1len != s2len) {
+		if(s1len < s2len)
+			return -1;
+		return 1;
+	}
+	return memcmp(s1, s2, s1len);
+}
+
+size_t
+nsec3_get_hashed(ldns_buffer* buf, uint8_t* nm, size_t nmlen, int algo, 
+	size_t iter, uint8_t* salt, size_t saltlen, uint8_t* res, size_t max)
+{
+	size_t i, hash_len;
+	/* prepare buffer for first iteration */
+	ldns_buffer_clear(buf);
+	ldns_buffer_write(buf, nm, nmlen);
+	query_dname_tolower(ldns_buffer_begin(buf));
+	ldns_buffer_write(buf, salt, saltlen);
+	ldns_buffer_flip(buf);
+	switch(algo) {
+#ifdef HAVE_EVP_SHA1
+		case NSEC3_HASH_SHA1:
+			hash_len = SHA_DIGEST_LENGTH;
+			if(hash_len > max)
+				return 0;
+			(void)SHA1((unsigned char*)ldns_buffer_begin(buf),
+				(unsigned long)ldns_buffer_limit(buf),
+				(unsigned char*)res);
+			for(i=0; i<iter; i++) {
+				ldns_buffer_clear(buf);
+				ldns_buffer_write(buf, res, hash_len);
+				ldns_buffer_write(buf, salt, saltlen);
+				ldns_buffer_flip(buf);
+				(void)SHA1(
+					(unsigned char*)ldns_buffer_begin(buf),
+					(unsigned long)ldns_buffer_limit(buf),
+					(unsigned char*)res);
+			}
+			break;
+#endif /* HAVE_EVP_SHA1 */
+		default:
+			log_err("nsec3 hash of unknown algo %d", algo);
+			return 0;
+	}
+	return hash_len;
+}
+
+/** perform hash of name */
+static int
+nsec3_calc_hash(struct regional* region, ldns_buffer* buf, 
+	struct nsec3_cached_hash* c)
+{
+	int algo = nsec3_get_algo(c->nsec3, c->rr);
+	size_t iter = nsec3_get_iter(c->nsec3, c->rr);
+	uint8_t* salt;
+	size_t saltlen, i;
+	if(!nsec3_get_salt(c->nsec3, c->rr, &salt, &saltlen))
+		return -1;
+	/* prepare buffer for first iteration */
+	ldns_buffer_clear(buf);
+	ldns_buffer_write(buf, c->dname, c->dname_len);
+	query_dname_tolower(ldns_buffer_begin(buf));
+	ldns_buffer_write(buf, salt, saltlen);
+	ldns_buffer_flip(buf);
+	switch(algo) {
+#ifdef HAVE_EVP_SHA1
+		case NSEC3_HASH_SHA1:
+			c->hash_len = SHA_DIGEST_LENGTH;
+			c->hash = (uint8_t*)regional_alloc(region, 
+				c->hash_len);
+			if(!c->hash)
+				return 0;
+			(void)SHA1((unsigned char*)ldns_buffer_begin(buf),
+				(unsigned long)ldns_buffer_limit(buf),
+				(unsigned char*)c->hash);
+			for(i=0; i<iter; i++) {
+				ldns_buffer_clear(buf);
+				ldns_buffer_write(buf, c->hash, c->hash_len);
+				ldns_buffer_write(buf, salt, saltlen);
+				ldns_buffer_flip(buf);
+				(void)SHA1(
+					(unsigned char*)ldns_buffer_begin(buf),
+					(unsigned long)ldns_buffer_limit(buf),
+					(unsigned char*)c->hash);
+			}
+			break;
+#endif /* HAVE_EVP_SHA1 */
+		default:
+			log_err("nsec3 hash of unknown algo %d", algo);
+			return -1;
+	}
+	return 1;
+}
+
+/** perform b32 encoding of hash */
+static int
+nsec3_calc_b32(struct regional* region, ldns_buffer* buf, 
+	struct nsec3_cached_hash* c)
+{
+	int r;
+	ldns_buffer_clear(buf);
+	r = ldns_b32_ntop_extended_hex(c->hash, c->hash_len,
+		(char*)ldns_buffer_begin(buf), ldns_buffer_limit(buf));
+	if(r < 1) {
+		log_err("b32_ntop_extended_hex: error in encoding: %d", r);
+		return 0;
+	}
+	c->b32_len = (size_t)r;
+	c->b32 = regional_alloc_init(region, ldns_buffer_begin(buf), 
+		c->b32_len);
+	if(!c->b32)
+		return 0;
+	return 1;
+}
+
+int
+nsec3_hash_name(rbtree_t* table, struct regional* region, ldns_buffer* buf,
+	struct ub_packed_rrset_key* nsec3, int rr, uint8_t* dname, 
+	size_t dname_len, struct nsec3_cached_hash** hash)
+{
+	struct nsec3_cached_hash* c;
+	struct nsec3_cached_hash looki;
+#ifdef UNBOUND_DEBUG
+	rbnode_t* n;
+#endif
+	int r;
+	looki.node.key = &looki;
+	looki.nsec3 = nsec3;
+	looki.rr = rr;
+	looki.dname = dname;
+	looki.dname_len = dname_len;
+	/* lookup first in cache */
+	c = (struct nsec3_cached_hash*)rbtree_search(table, &looki);
+	if(c) {
+		*hash = c;
+		return 1;
+	}
+	/* create a new entry */
+	c = (struct nsec3_cached_hash*)regional_alloc(region, sizeof(*c));
+	if(!c) return 0;
+	c->node.key = c;
+	c->nsec3 = nsec3;
+	c->rr = rr;
+	c->dname = dname;
+	c->dname_len = dname_len;
+	r = nsec3_calc_hash(region, buf, c);
+	if(r != 1)
+		return r;
+	r = nsec3_calc_b32(region, buf, c);
+	if(r != 1)
+		return r;
+#ifdef UNBOUND_DEBUG
+	n =
+#endif
+	rbtree_insert(table, &c->node);
+	log_assert(n); /* cannot be duplicate, just did lookup */
+	*hash = c;
+	return 1;
+}
+
+/**
+ * compare a label lowercased
+ */
+static int
+label_compare_lower(uint8_t* lab1, uint8_t* lab2, size_t lablen)
+{
+	size_t i;
+	for(i=0; i<lablen; i++) {
+		if(tolower((int)*lab1) != tolower((int)*lab2)) {
+			if(tolower((int)*lab1) < tolower((int)*lab2))
+				return -1;
+			return 1;
+		}
+		lab1++;
+		lab2++;
+	}
+	return 0;
+}
+
+/**
+ * Compare a hashed name with the owner name of an NSEC3 RRset.
+ * @param flt: filter with zone name.
+ * @param hash: the hashed name.
+ * @param s: rrset with owner name.
+ * @return true if matches exactly, false if not.
+ */
+static int
+nsec3_hash_matches_owner(struct nsec3_filter* flt, 
+	struct nsec3_cached_hash* hash, struct ub_packed_rrset_key* s)
+{
+	uint8_t* nm = s->rk.dname;
+	/* compare, does hash of name based on params in this NSEC3
+	 * match the owner name of this NSEC3? 
+	 * name must be: <hashlength>base32 . zone name 
+	 * so; first label must not be root label (not zero length),
+	 * and match the b32 encoded hash length, 
+	 * and the label content match the b32 encoded hash
+	 * and the rest must be the zone name.
+	 */
+	if(hash->b32_len != 0 && (size_t)nm[0] == hash->b32_len &&
+		label_compare_lower(nm+1, hash->b32, hash->b32_len) == 0 &&
+		query_dname_compare(nm+(size_t)nm[0]+1, flt->zone) == 0) {
+		return 1;
+	}
+	return 0;
+}
+
+/**
+ * Find matching NSEC3
+ * Find the NSEC3Record that matches a hash of a name.
+ * @param env: module environment with temporary region and buffer.
+ * @param flt: the NSEC3 RR filter, contains zone name and RRs.
+ * @param ct: cached hashes table.
+ * @param nm: name to look for.
+ * @param nmlen: length of name.
+ * @param rrset: nsec3 that matches is returned here.
+ * @param rr: rr number in nsec3 rrset that matches.
+ * @return true if a matching NSEC3 is found, false if not.
+ */
+static int
+find_matching_nsec3(struct module_env* env, struct nsec3_filter* flt,
+	rbtree_t* ct, uint8_t* nm, size_t nmlen, 
+	struct ub_packed_rrset_key** rrset, int* rr)
+{
+	size_t i_rs;
+	int i_rr;
+	struct ub_packed_rrset_key* s;
+	struct nsec3_cached_hash* hash;
+	int r;
+
+	/* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */
+	for(s=filter_first(flt, &i_rs, &i_rr); s; 
+		s=filter_next(flt, &i_rs, &i_rr)) {
+		/* get name hashed for this NSEC3 RR */
+		r = nsec3_hash_name(ct, env->scratch, env->scratch_buffer,
+			s, i_rr, nm, nmlen, &hash);
+		if(r == 0) {
+			log_err("nsec3: malloc failure");
+			break; /* alloc failure */
+		} else if(r < 0)
+			continue; /* malformed NSEC3 */
+		else if(nsec3_hash_matches_owner(flt, hash, s)) {
+			*rrset = s; /* rrset with this name */
+			*rr = i_rr; /* matches hash with these parameters */
+			return 1;
+		}
+	}
+	*rrset = NULL;
+	*rr = 0;
+	return 0;
+}
+
+int
+nsec3_covers(uint8_t* zone, struct nsec3_cached_hash* hash,
+	struct ub_packed_rrset_key* rrset, int rr, ldns_buffer* buf)
+{
+	uint8_t* next, *owner;
+	size_t nextlen;
+	int len;
+	if(!nsec3_get_nextowner(rrset, rr, &next, &nextlen))
+		return 0; /* malformed RR proves nothing */
+
+	/* check the owner name is a hashed value . apex
+	 * base32 encoded values must have equal length. 
+	 * hash_value and next hash value must have equal length. */
+	if(nextlen != hash->hash_len || hash->hash_len==0||hash->b32_len==0|| 
+		(size_t)*rrset->rk.dname != hash->b32_len ||
+		query_dname_compare(rrset->rk.dname+1+
+			(size_t)*rrset->rk.dname, zone) != 0)
+		return 0; /* bad lengths or owner name */
+
+	/* This is the "normal case: owner < next and owner < hash < next */
+	if(label_compare_lower(rrset->rk.dname+1, hash->b32, 
+		hash->b32_len) < 0 && 
+		memcmp(hash->hash, next, nextlen) < 0)
+		return 1;
+
+	/* convert owner name from text to binary */
+	ldns_buffer_clear(buf);
+	owner = ldns_buffer_begin(buf);
+	len = ldns_b32_pton_extended_hex((char*)rrset->rk.dname+1, 
+		hash->b32_len, owner, ldns_buffer_limit(buf));
+	if(len<1)
+		return 0; /* bad owner name in some way */
+	if((size_t)len != hash->hash_len || (size_t)len != nextlen)
+		return 0; /* wrong length */
+
+	/* this is the end of zone case: next <= owner && 
+	 * 	(hash > owner || hash < next) 
+	 * this also covers the only-apex case of next==owner.
+	 */
+	if(memcmp(next, owner, nextlen) <= 0 &&
+		( memcmp(hash->hash, owner, nextlen) > 0 ||
+		  memcmp(hash->hash, next, nextlen) < 0)) {
+		return 1;
+	}
+	return 0;
+}
+
+/**
+ * findCoveringNSEC3
+ * Given a name, find a covering NSEC3 from among a list of NSEC3s.
+ *
+ * @param env: module environment with temporary region and buffer.
+ * @param flt: the NSEC3 RR filter, contains zone name and RRs.
+ * @param ct: cached hashes table.
+ * @param nm: name to check if covered.
+ * @param nmlen: length of name.
+ * @param rrset: covering NSEC3 rrset is returned here.
+ * @param rr: rr of cover is returned here.
+ * @return true if a covering NSEC3 is found, false if not.
+ */
+static int
+find_covering_nsec3(struct module_env* env, struct nsec3_filter* flt,
+        rbtree_t* ct, uint8_t* nm, size_t nmlen, 
+	struct ub_packed_rrset_key** rrset, int* rr)
+{
+	size_t i_rs;
+	int i_rr;
+	struct ub_packed_rrset_key* s;
+	struct nsec3_cached_hash* hash;
+	int r;
+
+	/* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */
+	for(s=filter_first(flt, &i_rs, &i_rr); s; 
+		s=filter_next(flt, &i_rs, &i_rr)) {
+		/* get name hashed for this NSEC3 RR */
+		r = nsec3_hash_name(ct, env->scratch, env->scratch_buffer,
+			s, i_rr, nm, nmlen, &hash);
+		if(r == 0) {
+			log_err("nsec3: malloc failure");
+			break; /* alloc failure */
+		} else if(r < 0)
+			continue; /* malformed NSEC3 */
+		else if(nsec3_covers(flt->zone, hash, s, i_rr, 
+			env->scratch_buffer)) {
+			*rrset = s; /* rrset with this name */
+			*rr = i_rr; /* covers hash with these parameters */
+			return 1;
+		}
+	}
+	*rrset = NULL;
+	*rr = 0;
+	return 0;
+}
+
+/**
+ * findClosestEncloser
+ * Given a name and a list of NSEC3s, find the candidate closest encloser.
+ * This will be the first ancestor of 'name' (including itself) to have a
+ * matching NSEC3 RR.
+ * @param env: module environment with temporary region and buffer.
+ * @param flt: the NSEC3 RR filter, contains zone name and RRs.
+ * @param ct: cached hashes table.
+ * @param qinfo: query that is verified for.
+ * @param ce: closest encloser information is returned in here.
+ * @return true if a closest encloser candidate is found, false if not.
+ */
+static int
+nsec3_find_closest_encloser(struct module_env* env, struct nsec3_filter* flt, 
+	rbtree_t* ct, struct query_info* qinfo, struct ce_response* ce)
+{
+	uint8_t* nm = qinfo->qname;
+	size_t nmlen = qinfo->qname_len;
+
+	/* This scans from longest name to shortest, so the first match 
+	 * we find is the only viable candidate. */
+
+	/* (David:) FIXME: modify so that the NSEC3 matching the zone apex need 
+	 * not be present. (Mark Andrews idea).
+	 * (Wouter:) But make sure you check for DNAME bit in zone apex,
+	 * if the NSEC3 you find is the only NSEC3 in the zone, then this
+	 * may be the case. */
+
+	while(dname_subdomain_c(nm, flt->zone)) {
+		if(find_matching_nsec3(env, flt, ct, nm, nmlen, 
+			&ce->ce_rrset, &ce->ce_rr)) {
+			ce->ce = nm;
+			ce->ce_len = nmlen;
+			return 1;
+		}
+		dname_remove_label(&nm, &nmlen);
+	}
+	return 0;
+}
+
+/**
+ * Given a qname and its proven closest encloser, calculate the "next
+ * closest" name. Basically, this is the name that is one label longer than
+ * the closest encloser that is still a subdomain of qname.
+ *
+ * @param qname: query name.
+ * @param qnamelen: length of qname.
+ * @param ce: closest encloser
+ * @param nm: result name.
+ * @param nmlen: length of nm.
+ */
+static void
+next_closer(uint8_t* qname, size_t qnamelen, uint8_t* ce, 
+	uint8_t** nm, size_t* nmlen)
+{
+	int strip = dname_count_labels(qname) - dname_count_labels(ce) -1;
+	*nm = qname;
+	*nmlen = qnamelen;
+	if(strip>0)
+		dname_remove_labels(nm, nmlen, strip);
+}
+
+/**
+ * proveClosestEncloser
+ * Given a List of nsec3 RRs, find and prove the closest encloser to qname.
+ * @param env: module environment with temporary region and buffer.
+ * @param flt: the NSEC3 RR filter, contains zone name and RRs.
+ * @param ct: cached hashes table.
+ * @param qinfo: query that is verified for.
+ * @param prove_does_not_exist: If true, then if the closest encloser 
+ * 	turns out to be qname, then null is returned.
+ * 	If set true, and the return value is true, then you can be 
+ * 	certain that the ce.nc_rrset and ce.nc_rr are set properly.
+ * @param ce: closest encloser information is returned in here.
+ * @return bogus if no closest encloser could be proven.
+ * 	secure if a closest encloser could be proven, ce is set.
+ * 	insecure if the closest-encloser candidate turns out to prove
+ * 		that an insecure delegation exists above the qname.
+ */
+static enum sec_status
+nsec3_prove_closest_encloser(struct module_env* env, struct nsec3_filter* flt, 
+	rbtree_t* ct, struct query_info* qinfo, int prove_does_not_exist,
+	struct ce_response* ce)
+{
+	uint8_t* nc;
+	size_t nc_len;
+	/* robust: clean out ce, in case it gets abused later */
+	memset(ce, 0, sizeof(*ce));
+
+	if(!nsec3_find_closest_encloser(env, flt, ct, qinfo, ce)) {
+		verbose(VERB_ALGO, "nsec3 proveClosestEncloser: could "
+			"not find a candidate for the closest encloser.");
+		return sec_status_bogus;
+	}
+	log_nametypeclass(VERB_ALGO, "ce candidate", ce->ce, 0, 0);
+
+	if(query_dname_compare(ce->ce, qinfo->qname) == 0) {
+		if(prove_does_not_exist) {
+			verbose(VERB_ALGO, "nsec3 proveClosestEncloser: "
+				"proved that qname existed, bad");
+			return sec_status_bogus;
+		}
+		/* otherwise, we need to nothing else to prove that qname 
+		 * is its own closest encloser. */
+		return sec_status_secure;
+	}
+
+	/* If the closest encloser is actually a delegation, then the 
+	 * response should have been a referral. If it is a DNAME, then 
+	 * it should have been a DNAME response. */
+	if(nsec3_has_type(ce->ce_rrset, ce->ce_rr, LDNS_RR_TYPE_NS) &&
+		!nsec3_has_type(ce->ce_rrset, ce->ce_rr, LDNS_RR_TYPE_SOA)) {
+		if(!nsec3_has_type(ce->ce_rrset, ce->ce_rr, LDNS_RR_TYPE_DS)) {
+			verbose(VERB_ALGO, "nsec3 proveClosestEncloser: "
+				"closest encloser is insecure delegation");
+			return sec_status_insecure;
+		}
+		verbose(VERB_ALGO, "nsec3 proveClosestEncloser: closest "
+			"encloser was a delegation, bad");
+		return sec_status_bogus;
+	}
+	if(nsec3_has_type(ce->ce_rrset, ce->ce_rr, LDNS_RR_TYPE_DNAME)) {
+		verbose(VERB_ALGO, "nsec3 proveClosestEncloser: closest "
+			"encloser was a DNAME, bad");
+		return sec_status_bogus;
+	}
+	
+	/* Otherwise, we need to show that the next closer name is covered. */
+	next_closer(qinfo->qname, qinfo->qname_len, ce->ce, &nc, &nc_len);
+	if(!find_covering_nsec3(env, flt, ct, nc, nc_len, 
+		&ce->nc_rrset, &ce->nc_rr)) {
+		verbose(VERB_ALGO, "nsec3: Could not find proof that the "
+		          "candidate encloser was the closest encloser");
+		return sec_status_bogus;
+	}
+	return sec_status_secure;
+}
+
+/** allocate a wildcard for the closest encloser */
+static uint8_t*
+nsec3_ce_wildcard(struct regional* region, uint8_t* ce, size_t celen,
+	size_t* len)
+{
+	uint8_t* nm;
+	if(celen > LDNS_MAX_DOMAINLEN - 2)
+		return 0; /* too long */
+	nm = (uint8_t*)regional_alloc(region, celen+2);
+	if(!nm) {
+		log_err("nsec3 wildcard: out of memory");
+		return 0; /* alloc failure */
+	}
+	nm[0] = 1;
+	nm[1] = (uint8_t)'*'; /* wildcard label */
+	memmove(nm+2, ce, celen);
+	*len = celen+2;
+	return nm;
+}
+
+/** Do the name error proof */
+static enum sec_status
+nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt, 
+	rbtree_t* ct, struct query_info* qinfo)
+{
+	struct ce_response ce;
+	uint8_t* wc;
+	size_t wclen;
+	struct ub_packed_rrset_key* wc_rrset;
+	int wc_rr;
+	enum sec_status sec;
+
+	/* First locate and prove the closest encloser to qname. We will 
+	 * use the variant that fails if the closest encloser turns out 
+	 * to be qname. */
+	sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce);
+	if(sec != sec_status_secure) {
+		if(sec == sec_status_bogus)
+			verbose(VERB_ALGO, "nsec3 nameerror proof: failed "
+				"to prove a closest encloser");
+		else 	verbose(VERB_ALGO, "nsec3 nameerror proof: closest "
+				"nsec3 is an insecure delegation");
+		return sec;
+	}
+	log_nametypeclass(VERB_ALGO, "nsec3 namerror: proven ce=", ce.ce,0,0);
+
+	/* At this point, we know that qname does not exist. Now we need 
+	 * to prove that the wildcard does not exist. */
+	log_assert(ce.ce);
+	wc = nsec3_ce_wildcard(env->scratch, ce.ce, ce.ce_len, &wclen);
+	if(!wc || !find_covering_nsec3(env, flt, ct, wc, wclen, 
+		&wc_rrset, &wc_rr)) {
+		verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove "
+			"that the applicable wildcard did not exist.");
+		return sec_status_bogus;
+	}
+
+	if(ce.nc_rrset && nsec3_has_optout(ce.nc_rrset, ce.nc_rr)) {
+		verbose(VERB_ALGO, "nsec3 nameerror proof: nc has optout");
+		return sec_status_insecure;
+	}
+	return sec_status_secure;
+}
+
+enum sec_status
+nsec3_prove_nameerror(struct module_env* env, struct val_env* ve,
+	struct ub_packed_rrset_key** list, size_t num,
+	struct query_info* qinfo, struct key_entry_key* kkey)
+{
+	rbtree_t ct;
+	struct nsec3_filter flt;
+
+	if(!list || num == 0 || !kkey || !key_entry_isgood(kkey))
+		return sec_status_bogus; /* no valid NSEC3s, bogus */
+	rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */
+	filter_init(&flt, list, num, qinfo); /* init RR iterator */
+	if(!flt.zone)
+		return sec_status_bogus; /* no RRs */
+	if(nsec3_iteration_count_high(ve, &flt, kkey))
+		return sec_status_insecure; /* iteration count too high */
+	log_nametypeclass(VERB_ALGO, "start nsec3 nameerror proof, zone", 
+		flt.zone, 0, 0);
+	return nsec3_do_prove_nameerror(env, &flt, &ct, qinfo);
+}
+
+/* 
+ * No code to handle qtype=NSEC3 specially. 
+ * This existed in early drafts, but was later (-05) removed.
+ */
+
+/** Do the nodata proof */
+static enum sec_status
+nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, 
+	rbtree_t* ct, struct query_info* qinfo)
+{
+	struct ce_response ce;
+	uint8_t* wc;
+	size_t wclen;
+	struct ub_packed_rrset_key* rrset;
+	int rr;
+	enum sec_status sec;
+
+	if(find_matching_nsec3(env, flt, ct, qinfo->qname, qinfo->qname_len, 
+		&rrset, &rr)) {
+		/* cases 1 and 2 */
+		if(nsec3_has_type(rrset, rr, qinfo->qtype)) {
+			verbose(VERB_ALGO, "proveNodata: Matching NSEC3 "
+				"proved that type existed, bogus");
+			return sec_status_bogus;
+		} else if(nsec3_has_type(rrset, rr, LDNS_RR_TYPE_CNAME)) {
+			verbose(VERB_ALGO, "proveNodata: Matching NSEC3 "
+				"proved that a CNAME existed, bogus");
+			return sec_status_bogus;
+		}
+
+		/* 
+		 * If type DS: filter_init zone find already found a parent
+		 *   zone, so this nsec3 is from a parent zone. 
+		 *   o can be not a delegation (unusual query for normal name,
+		 *   	no DS anyway, but we can verify that).
+		 *   o can be a delegation (which is the usual DS check).
+		 *   o may not have the SOA bit set (only the top of the
+		 *   	zone, which must have been above the name, has that).
+		 *   	Except for the root; which is checked by itself.
+		 *
+		 * If not type DS: matching nsec3 must not be a delegation.
+		 */
+		if(qinfo->qtype == LDNS_RR_TYPE_DS && qinfo->qname_len != 1 
+			&& nsec3_has_type(rrset, rr, LDNS_RR_TYPE_SOA &&
+			!dname_is_root(qinfo->qname))) {
+			verbose(VERB_ALGO, "proveNodata: apex NSEC3 "
+				"abused for no DS proof, bogus");
+			return sec_status_bogus;
+		} else if(qinfo->qtype != LDNS_RR_TYPE_DS && 
+			nsec3_has_type(rrset, rr, LDNS_RR_TYPE_NS) &&
+			!nsec3_has_type(rrset, rr, LDNS_RR_TYPE_SOA)) {
+			if(!nsec3_has_type(rrset, rr, LDNS_RR_TYPE_DS)) {
+				verbose(VERB_ALGO, "proveNodata: matching "
+					"NSEC3 is insecure delegation");
+				return sec_status_insecure;
+			}
+			verbose(VERB_ALGO, "proveNodata: matching "
+				"NSEC3 is a delegation, bogus");
+			return sec_status_bogus;
+		}
+		return sec_status_secure;
+	}
+
+	/* For cases 3 - 5, we need the proven closest encloser, and it 
+	 * can't match qname. Although, at this point, we know that it 
+	 * won't since we just checked that. */
+	sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce);
+	if(sec == sec_status_bogus) {
+		verbose(VERB_ALGO, "proveNodata: did not match qname, "
+		          "nor found a proven closest encloser.");
+		return sec_status_bogus;
+	} else if(sec==sec_status_insecure && qinfo->qtype!=LDNS_RR_TYPE_DS){
+		verbose(VERB_ALGO, "proveNodata: closest nsec3 is insecure "
+		          "delegation.");
+		return sec_status_insecure;
+	}
+
+	/* Case 3: removed */
+
+	/* Case 4: */
+	log_assert(ce.ce);
+	wc = nsec3_ce_wildcard(env->scratch, ce.ce, ce.ce_len, &wclen);
+	if(wc && find_matching_nsec3(env, flt, ct, wc, wclen, &rrset, &rr)) {
+		/* found wildcard */
+		if(nsec3_has_type(rrset, rr, qinfo->qtype)) {
+			verbose(VERB_ALGO, "nsec3 nodata proof: matching "
+				"wildcard had qtype, bogus");
+			return sec_status_bogus;
+		} else if(nsec3_has_type(rrset, rr, LDNS_RR_TYPE_CNAME)) {
+			verbose(VERB_ALGO, "nsec3 nodata proof: matching "
+				"wildcard had a CNAME, bogus");
+			return sec_status_bogus;
+		}
+		if(qinfo->qtype == LDNS_RR_TYPE_DS && qinfo->qname_len != 1 
+			&& nsec3_has_type(rrset, rr, LDNS_RR_TYPE_SOA)) {
+			verbose(VERB_ALGO, "nsec3 nodata proof: matching "
+				"wildcard for no DS proof has a SOA, bogus");
+			return sec_status_bogus;
+		} else if(qinfo->qtype != LDNS_RR_TYPE_DS && 
+			nsec3_has_type(rrset, rr, LDNS_RR_TYPE_NS) &&
+			!nsec3_has_type(rrset, rr, LDNS_RR_TYPE_SOA)) {
+			verbose(VERB_ALGO, "nsec3 nodata proof: matching "
+				"wilcard is a delegation, bogus");
+			return sec_status_bogus;
+		}
+		/* everything is peachy keen, except for optout spans */
+		if(ce.nc_rrset && nsec3_has_optout(ce.nc_rrset, ce.nc_rr)) {
+			verbose(VERB_ALGO, "nsec3 nodata proof: matching "
+				"wildcard is in optout range, insecure");
+			return sec_status_insecure;
+		}
+		return sec_status_secure;
+	}
+
+	/* Case 5: */
+	/* Due to forwarders, cnames, and other collating effects, we
+	 * can see the ordinary unsigned data from a zone beneath an
+	 * insecure delegation under an optout here */
+	if(!ce.nc_rrset) {
+		verbose(VERB_ALGO, "nsec3 nodata proof: no next closer nsec3");
+		return sec_status_bogus;
+	}
+
+	/* We need to make sure that the covering NSEC3 is opt-out. */
+	log_assert(ce.nc_rrset);
+	if(!nsec3_has_optout(ce.nc_rrset, ce.nc_rr)) {
+		if(qinfo->qtype == LDNS_RR_TYPE_DS)
+		  verbose(VERB_ALGO, "proveNodata: covering NSEC3 was not "
+			"opt-out in an opt-out DS NOERROR/NODATA case.");
+		else verbose(VERB_ALGO, "proveNodata: could not find matching "
+			"NSEC3, nor matching wildcard, nor optout NSEC3 "
+			"-- no more options, bogus.");
+		return sec_status_bogus;
+	}
+	/* RFC5155 section 9.2: if nc has optout then no AD flag set */
+	return sec_status_insecure;
+}
+
+enum sec_status
+nsec3_prove_nodata(struct module_env* env, struct val_env* ve,
+	struct ub_packed_rrset_key** list, size_t num,
+	struct query_info* qinfo, struct key_entry_key* kkey)
+{
+	rbtree_t ct;
+	struct nsec3_filter flt;
+
+	if(!list || num == 0 || !kkey || !key_entry_isgood(kkey))
+		return sec_status_bogus; /* no valid NSEC3s, bogus */
+	rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */
+	filter_init(&flt, list, num, qinfo); /* init RR iterator */
+	if(!flt.zone)
+		return sec_status_bogus; /* no RRs */
+	if(nsec3_iteration_count_high(ve, &flt, kkey))
+		return sec_status_insecure; /* iteration count too high */
+	return nsec3_do_prove_nodata(env, &flt, &ct, qinfo);
+}
+
+enum sec_status
+nsec3_prove_wildcard(struct module_env* env, struct val_env* ve,
+        struct ub_packed_rrset_key** list, size_t num,
+	struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc)
+{
+	rbtree_t ct;
+	struct nsec3_filter flt;
+	struct ce_response ce;
+	uint8_t* nc;
+	size_t nc_len;
+	size_t wclen;
+	(void)dname_count_size_labels(wc, &wclen);
+
+	if(!list || num == 0 || !kkey || !key_entry_isgood(kkey))
+		return sec_status_bogus; /* no valid NSEC3s, bogus */
+	rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */
+	filter_init(&flt, list, num, qinfo); /* init RR iterator */
+	if(!flt.zone)
+		return sec_status_bogus; /* no RRs */
+	if(nsec3_iteration_count_high(ve, &flt, kkey))
+		return sec_status_insecure; /* iteration count too high */
+
+	/* We know what the (purported) closest encloser is by just 
+	 * looking at the supposed generating wildcard. 
+	 * The *. has already been removed from the wc name.
+	 */
+	memset(&ce, 0, sizeof(ce));
+	ce.ce = wc;
+	ce.ce_len = wclen;
+
+	/* Now we still need to prove that the original data did not exist.
+	 * Otherwise, we need to show that the next closer name is covered. */
+	next_closer(qinfo->qname, qinfo->qname_len, ce.ce, &nc, &nc_len);
+	if(!find_covering_nsec3(env, &flt, &ct, nc, nc_len, 
+		&ce.nc_rrset, &ce.nc_rr)) {
+		verbose(VERB_ALGO, "proveWildcard: did not find a covering "
+			"NSEC3 that covered the next closer name.");
+		return sec_status_bogus;
+	}
+	if(ce.nc_rrset && nsec3_has_optout(ce.nc_rrset, ce.nc_rr)) {
+		verbose(VERB_ALGO, "proveWildcard: NSEC3 optout");
+		return sec_status_insecure;
+	}
+	return sec_status_secure;
+}
+
+/** test if list is all secure */
+static int
+list_is_secure(struct module_env* env, struct val_env* ve, 
+	struct ub_packed_rrset_key** list, size_t num,
+	struct key_entry_key* kkey, char** reason)
+{
+	struct packed_rrset_data* d;
+	size_t i;
+	for(i=0; i<num; i++) {
+		d = (struct packed_rrset_data*)list[i]->entry.data;
+		if(list[i]->rk.type != htons(LDNS_RR_TYPE_NSEC3))
+			continue;
+		if(d->security == sec_status_secure)
+			continue;
+		rrset_check_sec_status(env->rrset_cache, list[i], *env->now);
+		if(d->security == sec_status_secure)
+			continue;
+		d->security = val_verify_rrset_entry(env, ve, list[i], kkey,
+			reason);
+		if(d->security != sec_status_secure) {
+			verbose(VERB_ALGO, "NSEC3 did not verify");
+			return 0;
+		}
+		rrset_update_sec_status(env->rrset_cache, list[i], *env->now);
+	}
+	return 1;
+}
+
+enum sec_status
+nsec3_prove_nods(struct module_env* env, struct val_env* ve,
+	struct ub_packed_rrset_key** list, size_t num,
+	struct query_info* qinfo, struct key_entry_key* kkey, char** reason)
+{
+	rbtree_t ct;
+	struct nsec3_filter flt;
+	struct ce_response ce;
+	struct ub_packed_rrset_key* rrset;
+	int rr;
+	log_assert(qinfo->qtype == LDNS_RR_TYPE_DS);
+
+	if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) {
+		*reason = "no valid NSEC3s";
+		return sec_status_bogus; /* no valid NSEC3s, bogus */
+	}
+	if(!list_is_secure(env, ve, list, num, kkey, reason))
+		return sec_status_bogus; /* not all NSEC3 records secure */
+	rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */
+	filter_init(&flt, list, num, qinfo); /* init RR iterator */
+	if(!flt.zone) {
+		*reason = "no NSEC3 records";
+		return sec_status_bogus; /* no RRs */
+	}
+	if(nsec3_iteration_count_high(ve, &flt, kkey))
+		return sec_status_insecure; /* iteration count too high */
+
+	/* Look for a matching NSEC3 to qname -- this is the normal 
+	 * NODATA case. */
+	if(find_matching_nsec3(env, &flt, &ct, qinfo->qname, qinfo->qname_len, 
+		&rrset, &rr)) {
+		/* If the matching NSEC3 has the SOA bit set, it is from 
+		 * the wrong zone (the child instead of the parent). If 
+		 * it has the DS bit set, then we were lied to. */
+		if(nsec3_has_type(rrset, rr, LDNS_RR_TYPE_SOA) && 
+			qinfo->qname_len != 1) {
+			verbose(VERB_ALGO, "nsec3 provenods: NSEC3 is from"
+				" child zone, bogus");
+			*reason = "NSEC3 from child zone";
+			return sec_status_bogus;
+		} else if(nsec3_has_type(rrset, rr, LDNS_RR_TYPE_DS)) {
+			verbose(VERB_ALGO, "nsec3 provenods: NSEC3 has qtype"
+				" DS, bogus");
+			*reason = "NSEC3 has DS in bitmap";
+			return sec_status_bogus;
+		}
+		/* If the NSEC3 RR doesn't have the NS bit set, then 
+		 * this wasn't a delegation point. */
+		if(!nsec3_has_type(rrset, rr, LDNS_RR_TYPE_NS))
+			return sec_status_indeterminate;
+		/* Otherwise, this proves no DS. */
+		return sec_status_secure;
+	}
+
+	/* Otherwise, we are probably in the opt-out case. */
+	if(nsec3_prove_closest_encloser(env, &flt, &ct, qinfo, 1, &ce)
+		!= sec_status_secure) {
+		/* an insecure delegation *above* the qname does not prove
+		 * anything about this qname exactly, and bogus is bogus */
+		verbose(VERB_ALGO, "nsec3 provenods: did not match qname, "
+		          "nor found a proven closest encloser.");
+		*reason = "no NSEC3 closest encloser";
+		return sec_status_bogus;
+	}
+
+	/* robust extra check */
+	if(!ce.nc_rrset) {
+		verbose(VERB_ALGO, "nsec3 nods proof: no next closer nsec3");
+		*reason = "no NSEC3 next closer";
+		return sec_status_bogus;
+	}
+
+	/* we had the closest encloser proof, then we need to check that the
+	 * covering NSEC3 was opt-out -- the proveClosestEncloser step already
+	 * checked to see if the closest encloser was a delegation or DNAME.
+	 */
+	log_assert(ce.nc_rrset);
+	if(!nsec3_has_optout(ce.nc_rrset, ce.nc_rr)) {
+		verbose(VERB_ALGO, "nsec3 provenods: covering NSEC3 was not "
+			"opt-out in an opt-out DS NOERROR/NODATA case.");
+		*reason = "covering NSEC3 was not opt-out in an opt-out "
+			"DS NOERROR/NODATA case";
+		return sec_status_bogus;
+	}
+	/* RFC5155 section 9.2: if nc has optout then no AD flag set */
+	return sec_status_insecure;
+}
+
+enum sec_status
+nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve,
+	struct ub_packed_rrset_key** list, size_t num, 
+	struct query_info* qinfo, struct key_entry_key* kkey, int* nodata)
+{
+	enum sec_status sec, secnx;
+	rbtree_t ct;
+	struct nsec3_filter flt;
+	*nodata = 0;
+
+	if(!list || num == 0 || !kkey || !key_entry_isgood(kkey))
+		return sec_status_bogus; /* no valid NSEC3s, bogus */
+	rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */
+	filter_init(&flt, list, num, qinfo); /* init RR iterator */
+	if(!flt.zone)
+		return sec_status_bogus; /* no RRs */
+	if(nsec3_iteration_count_high(ve, &flt, kkey))
+		return sec_status_insecure; /* iteration count too high */
+
+	/* try nxdomain and nodata after another, while keeping the
+	 * hash cache intact */
+
+	secnx = nsec3_do_prove_nameerror(env, &flt, &ct, qinfo);
+	if(secnx==sec_status_secure)
+		return sec_status_secure;
+	sec = nsec3_do_prove_nodata(env, &flt, &ct, qinfo);
+	if(sec==sec_status_secure) {
+		*nodata = 1;
+	} else if(sec == sec_status_insecure) {
+		*nodata = 1;
+	} else if(secnx == sec_status_insecure) {
+		sec = sec_status_insecure;
+	}
+	return sec;
+}
diff --git a/3rdParty/Unbound/src/src/validator/val_nsec3.h b/3rdParty/Unbound/src/src/validator/val_nsec3.h
new file mode 100644
index 0000000..ae4326d
--- /dev/null
+++ b/3rdParty/Unbound/src/src/validator/val_nsec3.h
@@ -0,0 +1,378 @@
+/*
+ * validator/val_nsec3.h - validator NSEC3 denial of existance functions.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains helper functions for the validator module.
+ * The functions help with NSEC3 checking, the different NSEC3 proofs
+ * for denial of existance, and proofs for presence of types.
+ *
+ * NSEC3
+ *                      1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |   Hash Alg.   |     Flags     |          Iterations           |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |  Salt Length  |                     Salt                      /
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |  Hash Length  |             Next Hashed Owner Name            /
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * /                         Type Bit Maps                         /
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * NSEC3PARAM
+ *                      1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |   Hash Alg.   |     Flags     |          Iterations           |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |  Salt Length  |                     Salt                      /
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+#ifndef VALIDATOR_VAL_NSEC3_H
+#define VALIDATOR_VAL_NSEC3_H
+#include "util/rbtree.h"
+#include "util/data/packed_rrset.h"
+struct val_env;
+struct regional;
+struct module_env;
+struct ub_packed_rrset_key;
+struct reply_info;
+struct query_info;
+struct key_entry_key;
+
+/**
+ *     0 1 2 3 4 5 6 7
+ *    +-+-+-+-+-+-+-+-+
+ *    |             |O|
+ *    +-+-+-+-+-+-+-+-+
+ * The OPT-OUT bit in the NSEC3 flags field.
+ * If enabled, there can be zero or more unsigned delegations in the span.
+ * If disabled, there are zero unsigned delegations in the span.
+ */
+#define NSEC3_OPTOUT	0x01
+/**
+ * The unknown flags in the NSEC3 flags field.
+ * They must be zero, or the NSEC3 is ignored.
+ */
+#define NSEC3_UNKNOWN_FLAGS 0xFE
+
+/** The SHA1 hash algorithm for NSEC3 */
+#define NSEC3_HASH_SHA1	0x01
+
+/**
+ * Determine if the set of NSEC3 records provided with a response prove NAME
+ * ERROR. This means that the NSEC3s prove a) the closest encloser exists,
+ * b) the direct child of the closest encloser towards qname doesn't exist,
+ * and c) *.closest encloser does not exist.
+ *
+ * @param env: module environment with temporary region and buffer.
+ * @param ve: validator environment, with iteration count settings.
+ * @param list: array of RRsets, some of which are NSEC3s.
+ * @param num: number of RRsets in the array to examine.
+ * @param qinfo: query that is verified for.
+ * @param kkey: key entry that signed the NSEC3s.
+ * @return:
+ * 	sec_status SECURE of the Name Error is proven by the NSEC3 RRs, 
+ * 	BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored.
+ */
+enum sec_status
+nsec3_prove_nameerror(struct module_env* env, struct val_env* ve,
+	struct ub_packed_rrset_key** list, size_t num, 
+	struct query_info* qinfo, struct key_entry_key* kkey);
+
+/**
+ * Determine if the NSEC3s provided in a response prove the NOERROR/NODATA
+ * status. There are a number of different variants to this:
+ * 
+ * 1) Normal NODATA -- qname is matched to an NSEC3 record, type is not
+ * present.
+ * 
+ * 2) ENT NODATA -- because there must be NSEC3 record for
+ * empty-non-terminals, this is the same as #1.
+ * 
+ * 3) NSEC3 ownername NODATA -- qname matched an existing, lone NSEC3
+ * ownername, but qtype was not NSEC3. NOTE: as of nsec-05, this case no
+ * longer exists.
+ * 
+ * 4) Wildcard NODATA -- A wildcard matched the name, but not the type.
+ * 
+ * 5) Opt-In DS NODATA -- the qname is covered by an opt-in span and qtype ==
+ * DS. (or maybe some future record with the same parent-side-only property)
+ *
+ * @param env: module environment with temporary region and buffer.
+ * @param ve: validator environment, with iteration count settings.
+ * @param list: array of RRsets, some of which are NSEC3s.
+ * @param num: number of RRsets in the array to examine.
+ * @param qinfo: query that is verified for.
+ * @param kkey: key entry that signed the NSEC3s.
+ * @return:
+ * 	sec_status SECURE of the proposition is proven by the NSEC3 RRs, 
+ * 	BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored.
+ */
+enum sec_status
+nsec3_prove_nodata(struct module_env* env, struct val_env* ve,
+	struct ub_packed_rrset_key** list, size_t num, 
+	struct query_info* qinfo, struct key_entry_key* kkey);
+
+
+/**
+ * Prove that a positive wildcard match was appropriate (no direct match
+ * RRset).
+ *
+ * @param env: module environment with temporary region and buffer.
+ * @param ve: validator environment, with iteration count settings.
+ * @param list: array of RRsets, some of which are NSEC3s.
+ * @param num: number of RRsets in the array to examine.
+ * @param qinfo: query that is verified for.
+ * @param kkey: key entry that signed the NSEC3s.
+ * @param wc: The purported wildcard that matched. This is the wildcard name
+ * 	as *.wildcard.name., with the *. label already removed.
+ * @return:
+ * 	sec_status SECURE of the proposition is proven by the NSEC3 RRs, 
+ * 	BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored.
+ */
+enum sec_status
+nsec3_prove_wildcard(struct module_env* env, struct val_env* ve,
+	struct ub_packed_rrset_key** list, size_t num, 
+	struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc);
+
+/**
+ * Prove that a DS response either had no DS, or wasn't a delegation point.
+ *
+ * Fundamentally there are two cases here: normal NODATA and Opt-In NODATA.
+ *
+ * @param env: module environment with temporary region and buffer.
+ * @param ve: validator environment, with iteration count settings.
+ * @param list: array of RRsets, some of which are NSEC3s.
+ * @param num: number of RRsets in the array to examine.
+ * @param qinfo: query that is verified for.
+ * @param kkey: key entry that signed the NSEC3s.
+ * @param reason: string for bogus result.
+ * @return:
+ * 	sec_status SECURE of the proposition is proven by the NSEC3 RRs, 
+ * 	BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored.
+ * 	or if there was no DS in an insecure (i.e., opt-in) way,
+ * 	INDETERMINATE if it was clear that this wasn't a delegation point.
+ */
+enum sec_status
+nsec3_prove_nods(struct module_env* env, struct val_env* ve,
+	struct ub_packed_rrset_key** list, size_t num, 
+	struct query_info* qinfo, struct key_entry_key* kkey, char** reason);
+
+/**
+ * Prove NXDOMAIN or NODATA.
+ *
+ * @param env: module environment with temporary region and buffer.
+ * @param ve: validator environment, with iteration count settings.
+ * @param list: array of RRsets, some of which are NSEC3s.
+ * @param num: number of RRsets in the array to examine.
+ * @param qinfo: query that is verified for.
+ * @param kkey: key entry that signed the NSEC3s.
+ * @param nodata: if return value is secure, this indicates if nodata or
+ * 	nxdomain was proven.
+ * @return:
+ * 	sec_status SECURE of the proposition is proven by the NSEC3 RRs, 
+ * 	BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored.
+ */
+enum sec_status
+nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve,
+	struct ub_packed_rrset_key** list, size_t num, 
+	struct query_info* qinfo, struct key_entry_key* kkey, int* nodata);
+
+/**
+ * The NSEC3 hash result storage.
+ * Consists of an rbtree, with these nodes in it.
+ * The nodes detail how a set of parameters (from nsec3 rr) plus
+ * a dname result in a hash.
+ */
+struct nsec3_cached_hash {
+	/** rbtree node, key is this structure */
+	rbnode_t node;
+	/** where are the parameters for conversion, in this rrset data */
+	struct ub_packed_rrset_key* nsec3;
+	/** where are the parameters for conversion, this RR number in data */
+	int rr;
+	/** the name to convert */
+	uint8_t* dname;
+	/** length of the dname */
+	size_t dname_len;
+	/** the hash result (not base32 encoded) */
+	uint8_t* hash;
+	/** length of hash in bytes */
+	size_t hash_len;
+	/** the hash result in base32 encoding */
+	uint8_t* b32;
+	/** length of base32 encoding (as a label) */
+	size_t b32_len;
+};
+
+/**
+ * Rbtree for hash cache comparison function.
+ * @param c1: key 1.
+ * @param c2: key 2.
+ * @return: comparison code, -1, 0, 1, of the keys.
+ */
+int nsec3_hash_cmp(const void* c1, const void* c2);
+
+/**
+ * Obtain the hash of an owner name.
+ * Used internally by the nsec3 proof functions in this file.
+ * published to enable unit testing of hash algorithms and cache.
+ *
+ * @param table: the cache table. Must be inited at start.
+ * @param region: scratch region to use for allocation.
+ * 	This region holds the tree, if you wipe the region, reinit the tree.
+ * @param buf: temporary buffer.
+ * @param nsec3: the rrset with parameters
+ * @param rr: rr number from d that has the NSEC3 parameters to hash to.
+ * @param dname: name to hash
+ * 	This pointer is used inside the tree, assumed region-alloced.
+ * @param dname_len: the length of the name.
+ * @param hash: the hash node is returned on success.
+ * @return:
+ * 	1 on success, either from cache or newly hashed hash is returned.
+ * 	0 on a malloc failure.
+ * 	-1 if the NSEC3 rr was badly formatted (i.e. formerr).
+ */
+int nsec3_hash_name(rbtree_t* table, struct regional* region, ldns_buffer* buf,
+	struct ub_packed_rrset_key* nsec3, int rr, uint8_t* dname, 
+	size_t dname_len, struct nsec3_cached_hash** hash);
+
+/**
+ * Get next owner name, converted to base32 encoding and with the
+ * zone name (taken from the nsec3 owner name) appended.
+ * @param rrset: the NSEC3 rrset.
+ * @param r: the rr num of the nsec3 in the rrset.
+ * @param buf: buffer to store name in
+ * @param max: size of buffer.
+ * @return length of name on success. 0 on failure (buffer too short or
+ *	bad format nsec3 record).
+ */
+size_t nsec3_get_nextowner_b32(struct ub_packed_rrset_key* rrset, int r,
+	uint8_t* buf, size_t max);
+
+/**
+ * Convert hash into base32 encoding and with the
+ * zone name appended.
+ * @param hash: hashed buffer
+ * @param hashlen: length of hash
+ * @param zone: name of zone
+ * @param zonelen: length of zonename.
+ * @param buf: buffer to store name in
+ * @param max: size of buffer.
+ * @return length of name on success. 0 on failure (buffer too short or
+ *	bad format nsec3 record).
+ */
+size_t nsec3_hash_to_b32(uint8_t* hash, size_t hashlen, uint8_t* zone,
+	size_t zonelen, uint8_t* buf, size_t max);
+
+/** 
+ * Get NSEC3 parameters out of rr.
+ * @param rrset: the NSEC3 rrset.
+ * @param r: the rr num of the nsec3 in the rrset.
+ * @param algo: nsec3 hash algo.
+ * @param iter: iteration count.
+ * @param salt: ptr to salt inside rdata.
+ * @param saltlen: length of salt.
+ * @return 0 if bad formatted, unknown nsec3 hash algo, or unknown flags set.
+ */
+int nsec3_get_params(struct ub_packed_rrset_key* rrset, int r,
+	int* algo, size_t* iter, uint8_t** salt, size_t* saltlen);
+
+/**
+ * Get NSEC3 hashed in a buffer
+ * @param buf: buffer for temp use.
+ * @param nm: name to hash
+ * @param nmlen: length of nm.
+ * @param algo: algo to use, must be known.
+ * @param iter: iterations
+ * @param salt: salt for nsec3
+ * @param saltlen: length of salt.
+ * @param res: result of hash stored here.
+ * @param max: maximum space for result.
+ * @return 0 on failure, otherwise bytelength stored.
+ */
+size_t nsec3_get_hashed(ldns_buffer* buf, uint8_t* nm, size_t nmlen, int algo, 
+	size_t iter, uint8_t* salt, size_t saltlen, uint8_t* res, size_t max);
+
+/** 
+ * see if NSEC3 RR contains given type
+ * @param rrset: NSEC3 rrset
+ * @param r: RR in rrset
+ * @param type: in host order to check bit for.
+ * @return true if bit set, false if not or error.
+ */
+int nsec3_has_type(struct ub_packed_rrset_key* rrset, int r, uint16_t type);
+
+/** 
+ * return if nsec3 RR has the optout flag 
+ * @param rrset: NSEC3 rrset
+ * @param r: RR in rrset
+ * @return true if optout, false on error or not optout
+ */
+int nsec3_has_optout(struct ub_packed_rrset_key* rrset, int r);
+
+/** 
+ * Return nsec3 RR next hashed owner name 
+ * @param rrset: NSEC3 rrset
+ * @param r: RR in rrset
+ * @param next: ptr into rdata to next owner hash
+ * @param nextlen: length of hash.
+ * @return false on malformed
+ */
+int nsec3_get_nextowner(struct ub_packed_rrset_key* rrset, int r,
+	uint8_t** next, size_t* nextlen);
+
+/**
+ * nsec3Covers
+ * Given a hash and a candidate NSEC3Record, determine if that NSEC3Record
+ * covers the hash. Covers specifically means that the hash is in between
+ * the owner and next hashes and does not equal either.
+ *
+ * @param zone: the zone name.
+ * @param hash: the hash of the name
+ * @param rrset: the rrset of the NSEC3.
+ * @param rr: which rr in the rrset.
+ * @param buf: temporary buffer.
+ * @return true if covers, false if not.
+ */
+int nsec3_covers(uint8_t* zone, struct nsec3_cached_hash* hash,
+	struct ub_packed_rrset_key* rrset, int rr, ldns_buffer* buf);
+
+#endif /* VALIDATOR_VAL_NSEC3_H */
diff --git a/3rdParty/Unbound/src/src/validator/val_sigcrypt.c b/3rdParty/Unbound/src/src/validator/val_sigcrypt.c
new file mode 100644
index 0000000..436b5e8
--- /dev/null
+++ b/3rdParty/Unbound/src/src/validator/val_sigcrypt.c
@@ -0,0 +1,1699 @@
+/*
+ * validator/val_sigcrypt.c - validator signature crypto functions.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains helper functions for the validator module.
+ * The functions help with signature verification and checking, the
+ * bridging between RR wireformat data and crypto calls.
+ */
+#include "config.h"
+#include <ldns/ldns.h>
+#include "validator/val_sigcrypt.h"
+#include "validator/validator.h"
+#include "util/data/msgreply.h"
+#include "util/data/msgparse.h"
+#include "util/data/dname.h"
+#include "util/rbtree.h"
+#include "util/module.h"
+#include "util/net_help.h"
+#include "util/regional.h"
+
+#ifndef HAVE_SSL
+#error "Need SSL library to do digital signature cryptography"
+#endif
+
+#ifdef HAVE_OPENSSL_ERR_H
+#include <openssl/err.h>
+#endif
+
+#ifdef HAVE_OPENSSL_RAND_H
+#include <openssl/rand.h>
+#endif
+
+#ifdef HAVE_OPENSSL_CONF_H
+#include <openssl/conf.h>
+#endif
+
+#ifdef HAVE_OPENSSL_ENGINE_H
+#include <openssl/engine.h>
+#endif
+
+/** return number of rrs in an rrset */
+static size_t
+rrset_get_count(struct ub_packed_rrset_key* rrset)
+{
+	struct packed_rrset_data* d = (struct packed_rrset_data*)
+	rrset->entry.data;
+	if(!d) return 0;
+	return d->count;
+}
+
+/**
+ * Get RR signature count
+ */
+static size_t
+rrset_get_sigcount(struct ub_packed_rrset_key* k)
+{
+	struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data;
+	return d->rrsig_count;
+}
+
+/**
+ * Get signature keytag value
+ * @param k: rrset (with signatures)
+ * @param sig_idx: signature index.
+ * @return keytag or 0 if malformed rrsig.
+ */
+static uint16_t 
+rrset_get_sig_keytag(struct ub_packed_rrset_key* k, size_t sig_idx)
+{
+	uint16_t t;
+	struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data;
+	log_assert(sig_idx < d->rrsig_count);
+	if(d->rr_len[d->count + sig_idx] < 2+18)
+		return 0;
+	memmove(&t, d->rr_data[d->count + sig_idx]+2+16, 2);
+	return ntohs(t);
+}
+
+/**
+ * Get signature signing algorithm value
+ * @param k: rrset (with signatures)
+ * @param sig_idx: signature index.
+ * @return algo or 0 if malformed rrsig.
+ */
+static int 
+rrset_get_sig_algo(struct ub_packed_rrset_key* k, size_t sig_idx)
+{
+	struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data;
+	log_assert(sig_idx < d->rrsig_count);
+	if(d->rr_len[d->count + sig_idx] < 2+3)
+		return 0;
+	return (int)d->rr_data[d->count + sig_idx][2+2];
+}
+
+/** get rdata pointer and size */
+static void
+rrset_get_rdata(struct ub_packed_rrset_key* k, size_t idx, uint8_t** rdata,
+	size_t* len)
+{
+	struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data;
+	log_assert(d && idx < (d->count + d->rrsig_count));
+	*rdata = d->rr_data[idx];
+	*len = d->rr_len[idx];
+}
+
+uint16_t
+dnskey_get_flags(struct ub_packed_rrset_key* k, size_t idx)
+{
+	uint8_t* rdata;
+	size_t len;
+	uint16_t f;
+	rrset_get_rdata(k, idx, &rdata, &len);
+	if(len < 2+2)
+		return 0;
+	memmove(&f, rdata+2, 2);
+	f = ntohs(f);
+	return f;
+}
+
+/**
+ * Get DNSKEY protocol value from rdata
+ * @param k: DNSKEY rrset.
+ * @param idx: which key.
+ * @return protocol octet value
+ */
+static int
+dnskey_get_protocol(struct ub_packed_rrset_key* k, size_t idx)
+{
+	uint8_t* rdata;
+	size_t len;
+	rrset_get_rdata(k, idx, &rdata, &len);
+	if(len < 2+4)
+		return 0;
+	return (int)rdata[2+2];
+}
+
+int
+dnskey_get_algo(struct ub_packed_rrset_key* k, size_t idx)
+{
+	uint8_t* rdata;
+	size_t len;
+	rrset_get_rdata(k, idx, &rdata, &len);
+	if(len < 2+4)
+		return 0;
+	return (int)rdata[2+3];
+}
+
+/** get public key rdata field from a dnskey RR and do some checks */
+static void
+dnskey_get_pubkey(struct ub_packed_rrset_key* k, size_t idx,
+	unsigned char** pk, unsigned int* pklen)
+{
+	uint8_t* rdata;
+	size_t len;
+	rrset_get_rdata(k, idx, &rdata, &len);
+	if(len < 2+5) {
+		*pk = NULL;
+		*pklen = 0;
+		return;
+	}
+	*pk = (unsigned char*)rdata+2+4;
+	*pklen = (unsigned)len-2-4;
+}
+
+int
+ds_get_key_algo(struct ub_packed_rrset_key* k, size_t idx)
+{
+	uint8_t* rdata;
+	size_t len;
+	rrset_get_rdata(k, idx, &rdata, &len);
+	if(len < 2+3)
+		return 0;
+	return (int)rdata[2+2];
+}
+
+int
+ds_get_digest_algo(struct ub_packed_rrset_key* k, size_t idx)
+{
+	uint8_t* rdata;
+	size_t len;
+	rrset_get_rdata(k, idx, &rdata, &len);
+	if(len < 2+4)
+		return 0;
+	return (int)rdata[2+3];
+}
+
+uint16_t 
+ds_get_keytag(struct ub_packed_rrset_key* ds_rrset, size_t ds_idx)
+{
+	uint16_t t;
+	uint8_t* rdata;
+	size_t len;
+	rrset_get_rdata(ds_rrset, ds_idx, &rdata, &len);
+	if(len < 2+2)
+		return 0;
+	memmove(&t, rdata+2, 2);
+	return ntohs(t);
+}
+
+/**
+ * Return pointer to the digest in a DS RR.
+ * @param k: DS rrset.
+ * @param idx: which DS.
+ * @param digest: digest data is returned.
+ *	on error, this is NULL.
+ * @param len: length of digest is returned.
+ *	on error, the length is 0.
+ */
+static void
+ds_get_sigdata(struct ub_packed_rrset_key* k, size_t idx, uint8_t** digest,
+        size_t* len)
+{
+	uint8_t* rdata;
+	size_t rdlen;
+	rrset_get_rdata(k, idx, &rdata, &rdlen);
+	if(rdlen < 2+5) {
+		*digest = NULL;
+		*len = 0;
+		return;
+	}
+	*digest = rdata + 2 + 4;
+	*len = rdlen - 2 - 4;
+}
+
+/**
+ * Return size of DS digest according to its hash algorithm.
+ * @param k: DS rrset.
+ * @param idx: which DS.
+ * @return size in bytes of digest, or 0 if not supported. 
+ */
+static size_t
+ds_digest_size_algo(struct ub_packed_rrset_key* k, size_t idx)
+{
+	switch(ds_get_digest_algo(k, idx)) {
+#ifdef HAVE_EVP_SHA1
+		case LDNS_SHA1:
+			return SHA_DIGEST_LENGTH;
+#endif
+#ifdef HAVE_EVP_SHA256
+		case LDNS_SHA256:
+			return SHA256_DIGEST_LENGTH;
+#endif
+#ifdef USE_GOST
+		case LDNS_HASH_GOST:
+			if(EVP_get_digestbyname("md_gost94"))
+				return 32;
+			else	return 0;
+#endif
+		default: break;
+	}
+	return 0;
+}
+
+#ifdef USE_GOST
+/** Perform GOST hash */
+static int
+do_gost94(unsigned char* data, size_t len, unsigned char* dest)
+{
+	const EVP_MD* md = EVP_get_digestbyname("md_gost94");
+	if(!md) 
+		return 0;
+	return ldns_digest_evp(data, (unsigned int)len, dest, md);
+}
+#endif
+
+/**
+ * Create a DS digest for a DNSKEY entry.
+ *
+ * @param env: module environment. Uses scratch space.
+ * @param dnskey_rrset: DNSKEY rrset.
+ * @param dnskey_idx: index of RR in rrset.
+ * @param ds_rrset: DS rrset
+ * @param ds_idx: index of RR in DS rrset.
+ * @param digest: digest is returned in here (must be correctly sized).
+ * @return false on error.
+ */
+static int
+ds_create_dnskey_digest(struct module_env* env, 
+	struct ub_packed_rrset_key* dnskey_rrset, size_t dnskey_idx,
+	struct ub_packed_rrset_key* ds_rrset, size_t ds_idx,
+	uint8_t* digest)
+{
+	ldns_buffer* b = env->scratch_buffer;
+	uint8_t* dnskey_rdata;
+	size_t dnskey_len;
+	rrset_get_rdata(dnskey_rrset, dnskey_idx, &dnskey_rdata, &dnskey_len);
+
+	/* create digest source material in buffer 
+	 * digest = digest_algorithm( DNSKEY owner name | DNSKEY RDATA);
+	 *	DNSKEY RDATA = Flags | Protocol | Algorithm | Public Key. */
+	ldns_buffer_clear(b);
+	ldns_buffer_write(b, dnskey_rrset->rk.dname, 
+		dnskey_rrset->rk.dname_len);
+	query_dname_tolower(ldns_buffer_begin(b));
+	ldns_buffer_write(b, dnskey_rdata+2, dnskey_len-2); /* skip rdatalen*/
+	ldns_buffer_flip(b);
+	
+	switch(ds_get_digest_algo(ds_rrset, ds_idx)) {
+#ifdef HAVE_EVP_SHA1
+		case LDNS_SHA1:
+			(void)SHA1((unsigned char*)ldns_buffer_begin(b),
+				ldns_buffer_limit(b), (unsigned char*)digest);
+			return 1;
+#endif
+#ifdef HAVE_EVP_SHA256
+		case LDNS_SHA256:
+			(void)SHA256((unsigned char*)ldns_buffer_begin(b),
+				ldns_buffer_limit(b), (unsigned char*)digest);
+			return 1;
+#endif
+#ifdef USE_GOST
+		case LDNS_HASH_GOST:
+			if(do_gost94((unsigned char*)ldns_buffer_begin(b), 
+				ldns_buffer_limit(b), (unsigned char*)digest))
+				return 1;
+#endif
+		default: 
+			verbose(VERB_QUERY, "unknown DS digest algorithm %d", 
+				(int) ds_get_digest_algo(ds_rrset, ds_idx));
+			break;
+	}
+	return 0;
+}
+
+int ds_digest_match_dnskey(struct module_env* env,
+	struct ub_packed_rrset_key* dnskey_rrset, size_t dnskey_idx,
+	struct ub_packed_rrset_key* ds_rrset, size_t ds_idx)
+{
+	uint8_t* ds;	/* DS digest */
+	size_t dslen;
+	uint8_t* digest; /* generated digest */
+	size_t digestlen = ds_digest_size_algo(ds_rrset, ds_idx);
+	
+	if(digestlen == 0) {
+		verbose(VERB_QUERY, "DS fail: not supported, or DS RR "
+			"format error");
+		return 0; /* not supported, or DS RR format error */
+	}
+	/* check digest length in DS with length from hash function */
+	ds_get_sigdata(ds_rrset, ds_idx, &ds, &dslen);
+	if(!ds || dslen != digestlen) {
+		verbose(VERB_QUERY, "DS fail: DS RR algo and digest do not "
+			"match each other");
+		return 0; /* DS algorithm and digest do not match */
+	}
+
+	digest = regional_alloc(env->scratch, digestlen);
+	if(!digest) {
+		verbose(VERB_QUERY, "DS fail: out of memory");
+		return 0; /* mem error */
+	}
+	if(!ds_create_dnskey_digest(env, dnskey_rrset, dnskey_idx, ds_rrset, 
+		ds_idx, digest)) {
+		verbose(VERB_QUERY, "DS fail: could not calc key digest");
+		return 0; /* digest algo failed */
+	}
+	if(memcmp(digest, ds, dslen) != 0) {
+		verbose(VERB_QUERY, "DS fail: digest is different");
+		return 0; /* digest different */
+	}
+	return 1;
+}
+
+int 
+ds_digest_algo_is_supported(struct ub_packed_rrset_key* ds_rrset, 
+	size_t ds_idx)
+{
+	return (ds_digest_size_algo(ds_rrset, ds_idx) != 0);
+}
+
+/** return true if DNSKEY algorithm id is supported */
+static int
+dnskey_algo_id_is_supported(int id)
+{
+	switch(id) {
+	case LDNS_DSA:
+	case LDNS_DSA_NSEC3:
+	case LDNS_RSASHA1:
+	case LDNS_RSASHA1_NSEC3:
+	case LDNS_RSAMD5:
+#if defined(HAVE_EVP_SHA256) && defined(USE_SHA2)
+	case LDNS_RSASHA256:
+#endif
+#if defined(HAVE_EVP_SHA512) && defined(USE_SHA2)
+	case LDNS_RSASHA512:
+#endif
+		return 1;
+#ifdef USE_GOST
+	case LDNS_ECC_GOST:
+		/* we support GOST if it can be loaded */
+		return ldns_key_EVP_load_gost_id();
+#endif
+	default:
+		return 0;
+	}
+}
+
+int 
+ds_key_algo_is_supported(struct ub_packed_rrset_key* ds_rrset, 
+	size_t ds_idx)
+{
+	return dnskey_algo_id_is_supported(ds_get_key_algo(ds_rrset, ds_idx));
+}
+
+uint16_t 
+dnskey_calc_keytag(struct ub_packed_rrset_key* dnskey_rrset, size_t dnskey_idx)
+{
+	uint8_t* data;
+	size_t len;
+	rrset_get_rdata(dnskey_rrset, dnskey_idx, &data, &len);
+	/* do not pass rdatalen to ldns */
+	return ldns_calc_keytag_raw(data+2, len-2);
+}
+
+int dnskey_algo_is_supported(struct ub_packed_rrset_key* dnskey_rrset,
+        size_t dnskey_idx)
+{
+	return dnskey_algo_id_is_supported(dnskey_get_algo(dnskey_rrset, 
+		dnskey_idx));
+}
+
+void algo_needs_init_dnskey_add(struct algo_needs* n,
+        struct ub_packed_rrset_key* dnskey, uint8_t* sigalg)
+{
+	uint8_t algo;
+	size_t i, total = n->num;
+	size_t num = rrset_get_count(dnskey);
+
+	for(i=0; i<num; i++) {
+		algo = (uint8_t)dnskey_get_algo(dnskey, i);
+		if(!dnskey_algo_id_is_supported((int)algo))
+			continue;
+		if(n->needs[algo] == 0) {
+			n->needs[algo] = 1;
+			sigalg[total] = algo;
+			total++;
+		}
+	}
+	sigalg[total] = 0;
+	n->num = total;
+}
+
+void algo_needs_init_list(struct algo_needs* n, uint8_t* sigalg)
+{
+	uint8_t algo;
+	size_t total = 0;
+
+	memset(n->needs, 0, sizeof(uint8_t)*ALGO_NEEDS_MAX);
+	while( (algo=*sigalg++) != 0) {
+		log_assert(dnskey_algo_id_is_supported((int)algo));
+		log_assert(n->needs[algo] == 0);
+		n->needs[algo] = 1;
+		total++;
+	}
+	n->num = total;
+}
+
+void algo_needs_init_ds(struct algo_needs* n, struct ub_packed_rrset_key* ds,
+	int fav_ds_algo, uint8_t* sigalg)
+{
+	uint8_t algo;
+	size_t i, total = 0;
+	size_t num = rrset_get_count(ds);
+
+	memset(n->needs, 0, sizeof(uint8_t)*ALGO_NEEDS_MAX);
+	for(i=0; i<num; i++) {
+		if(ds_get_digest_algo(ds, i) != fav_ds_algo)
+			continue;
+		algo = (uint8_t)ds_get_key_algo(ds, i);
+		if(!dnskey_algo_id_is_supported((int)algo))
+			continue;
+		log_assert(algo != 0); /* we do not support 0 and is EOS */
+		if(n->needs[algo] == 0) {
+			n->needs[algo] = 1;
+			sigalg[total] = algo;		
+			total++;
+		}
+	}
+	sigalg[total] = 0;
+	n->num = total;
+}
+
+int algo_needs_set_secure(struct algo_needs* n, uint8_t algo)
+{
+	if(n->needs[algo]) {
+		n->needs[algo] = 0;
+		n->num --;
+		if(n->num == 0) /* done! */
+			return 1;
+	}
+	return 0;
+}
+
+void algo_needs_set_bogus(struct algo_needs* n, uint8_t algo)
+{
+	if(n->needs[algo]) n->needs[algo] = 2; /* need it, but bogus */
+}
+
+size_t algo_needs_num_missing(struct algo_needs* n)
+{
+	return n->num;
+}
+
+int algo_needs_missing(struct algo_needs* n)
+{
+	int i;
+	/* first check if a needed algo was bogus - report that */
+	for(i=0; i<ALGO_NEEDS_MAX; i++)
+		if(n->needs[i] == 2)
+			return 0;
+	/* now check which algo is missing */
+	for(i=0; i<ALGO_NEEDS_MAX; i++)
+		if(n->needs[i] == 1)
+			return i;
+	return 0;
+}
+
+enum sec_status 
+dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
+	struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
+	uint8_t* sigalg, char** reason)
+{
+	enum sec_status sec;
+	size_t i, num;
+	rbtree_t* sortree = NULL;
+	/* make sure that for all DNSKEY algorithms there are valid sigs */
+	struct algo_needs needs;
+	int alg;
+
+	num = rrset_get_sigcount(rrset);
+	if(num == 0) {
+		verbose(VERB_QUERY, "rrset failed to verify due to a lack of "
+			"signatures");
+		*reason = "no signatures";
+		return sec_status_bogus;
+	}
+
+	if(sigalg) {
+		algo_needs_init_list(&needs, sigalg);
+		if(algo_needs_num_missing(&needs) == 0) {
+			verbose(VERB_QUERY, "zone has no known algorithms");
+			*reason = "zone has no known algorithms";
+			return sec_status_insecure;
+		}
+	}
+	for(i=0; i<num; i++) {
+		sec = dnskeyset_verify_rrset_sig(env, ve, *env->now, rrset, 
+			dnskey, i, &sortree, reason);
+		/* see which algorithm has been fixed up */
+		if(sec == sec_status_secure) {
+			if(!sigalg)
+				return sec; /* done! */
+			else if(algo_needs_set_secure(&needs,
+				(uint8_t)rrset_get_sig_algo(rrset, i)))
+				return sec; /* done! */
+		} else if(sigalg && sec == sec_status_bogus) {
+			algo_needs_set_bogus(&needs,
+				(uint8_t)rrset_get_sig_algo(rrset, i));
+		}
+	}
+	verbose(VERB_ALGO, "rrset failed to verify: no valid signatures for "
+		"%d algorithms", (int)algo_needs_num_missing(&needs));
+	if(sigalg && (alg=algo_needs_missing(&needs)) != 0) {
+		algo_needs_reason(env, alg, reason, "no signatures");
+	}
+	return sec_status_bogus;
+}
+
+void algo_needs_reason(struct module_env* env, int alg, char** reason, char* s)
+{
+	char buf[256];
+	ldns_lookup_table *t = ldns_lookup_by_id(ldns_algorithms, alg);
+	if(t&&t->name)
+		snprintf(buf, sizeof(buf), "%s with algorithm %s", s, t->name);
+	else	snprintf(buf, sizeof(buf), "%s with algorithm ALG%u", s,
+			(unsigned)alg);
+	*reason = regional_strdup(env->scratch, buf);
+	if(!*reason)
+		*reason = s;
+}
+
+enum sec_status 
+dnskey_verify_rrset(struct module_env* env, struct val_env* ve,
+        struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
+	size_t dnskey_idx, char** reason)
+{
+	enum sec_status sec;
+	size_t i, num, numchecked = 0;
+	rbtree_t* sortree = NULL;
+	int buf_canon = 0;
+	uint16_t tag = dnskey_calc_keytag(dnskey, dnskey_idx);
+	int algo = dnskey_get_algo(dnskey, dnskey_idx);
+
+	num = rrset_get_sigcount(rrset);
+	if(num == 0) {
+		verbose(VERB_QUERY, "rrset failed to verify due to a lack of "
+			"signatures");
+		*reason = "no signatures";
+		return sec_status_bogus;
+	}
+	for(i=0; i<num; i++) {
+		/* see if sig matches keytag and algo */
+		if(algo != rrset_get_sig_algo(rrset, i) ||
+			tag != rrset_get_sig_keytag(rrset, i))
+			continue;
+		buf_canon = 0;
+		sec = dnskey_verify_rrset_sig(env->scratch, 
+			env->scratch_buffer, ve, *env->now, rrset, 
+			dnskey, dnskey_idx, i, &sortree, &buf_canon, reason);
+		if(sec == sec_status_secure)
+			return sec;
+		numchecked ++;
+	}
+	verbose(VERB_ALGO, "rrset failed to verify: all signatures are bogus");
+	if(!numchecked) *reason = "signature missing";
+	return sec_status_bogus;
+}
+
+enum sec_status 
+dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, 
+	uint32_t now, struct ub_packed_rrset_key* rrset, 
+	struct ub_packed_rrset_key* dnskey, size_t sig_idx, 
+	struct rbtree_t** sortree, char** reason)
+{
+	/* find matching keys and check them */
+	enum sec_status sec = sec_status_bogus;
+	uint16_t tag = rrset_get_sig_keytag(rrset, sig_idx);
+	int algo = rrset_get_sig_algo(rrset, sig_idx);
+	size_t i, num = rrset_get_count(dnskey);
+	size_t numchecked = 0;
+	int buf_canon = 0;
+	verbose(VERB_ALGO, "verify sig %d %d", (int)tag, algo);
+	if(!dnskey_algo_id_is_supported(algo)) {
+		verbose(VERB_QUERY, "verify sig: unknown algorithm");
+		return sec_status_insecure;
+	}
+	
+	for(i=0; i<num; i++) {
+		/* see if key matches keytag and algo */
+		if(algo != dnskey_get_algo(dnskey, i) ||
+			tag != dnskey_calc_keytag(dnskey, i))
+			continue;
+		numchecked ++;
+
+		/* see if key verifies */
+		sec = dnskey_verify_rrset_sig(env->scratch, 
+			env->scratch_buffer, ve, now, rrset, dnskey, i, 
+			sig_idx, sortree, &buf_canon, reason);
+		if(sec == sec_status_secure)
+			return sec;
+	}
+	if(numchecked == 0) {
+		*reason = "signatures from unknown keys";
+		verbose(VERB_QUERY, "verify: could not find appropriate key");
+		return sec_status_bogus;
+	}
+	return sec_status_bogus;
+}
+
+/**
+ * RR entries in a canonical sorted tree of RRs
+ */
+struct canon_rr {
+	/** rbtree node, key is this structure */
+	rbnode_t node;
+	/** rrset the RR is in */
+	struct ub_packed_rrset_key* rrset;
+	/** which RR in the rrset */
+	size_t rr_idx;
+};
+
+/**
+ * Compare two RR for canonical order, in a field-style sweep.
+ * @param d: rrset data
+ * @param desc: ldns wireformat descriptor.
+ * @param i: first RR to compare
+ * @param j: first RR to compare
+ * @return comparison code.
+ */
+static int
+canonical_compare_byfield(struct packed_rrset_data* d, 
+	const ldns_rr_descriptor* desc, size_t i, size_t j)
+{
+	/* sweep across rdata, keep track of some state:
+	 * 	which rr field, and bytes left in field.
+	 * 	current position in rdata, length left.
+	 * 	are we in a dname, length left in a label.
+	 */
+	int wfi = -1;	/* current wireformat rdata field (rdf) */
+	int wfj = -1;
+	uint8_t* di = d->rr_data[i]+2; /* ptr to current rdata byte */
+	uint8_t* dj = d->rr_data[j]+2;
+	size_t ilen = d->rr_len[i]-2; /* length left in rdata */
+	size_t jlen = d->rr_len[j]-2;
+	int dname_i = 0;  /* true if these bytes are part of a name */
+	int dname_j = 0;
+	size_t lablen_i = 0; /* 0 for label length byte,for first byte of rdf*/
+	size_t lablen_j = 0; /* otherwise remaining length of rdf or label */
+	int dname_num_i = (int)desc->_dname_count; /* decreased at root label */
+	int dname_num_j = (int)desc->_dname_count;
+
+	/* loop while there are rdata bytes available for both rrs,
+	 * and still some lowercasing needs to be done; either the dnames
+	 * have not been reached yet, or they are currently being processed */
+	while(ilen > 0 && jlen > 0 && (dname_num_i > 0 || dname_num_j > 0)) {
+		/* compare these two bytes */
+		/* lowercase if in a dname and not a label length byte */
+		if( ((dname_i && lablen_i)?(uint8_t)tolower((int)*di):*di)
+		 != ((dname_j && lablen_j)?(uint8_t)tolower((int)*dj):*dj)
+		 ) {
+		  if(((dname_i && lablen_i)?(uint8_t)tolower((int)*di):*di)
+		  < ((dname_j && lablen_j)?(uint8_t)tolower((int)*dj):*dj))
+		 	return -1;
+		    return 1;
+		}
+		ilen--;
+		jlen--;
+		/* bytes are equal */
+
+		/* advance field i */
+		/* lablen 0 means that this byte is the first byte of the
+		 * next rdata field; inspect this rdata field and setup
+		 * to process the rest of this rdata field.
+		 * The reason to first read the byte, then setup the rdf,
+		 * is that we are then sure the byte is available and short
+		 * rdata is handled gracefully (even if it is a formerr). */
+		if(lablen_i == 0) { 
+			if(dname_i) {
+				/* scan this dname label */
+				/* capture length to lowercase */
+				lablen_i = (size_t)*di;
+				if(lablen_i == 0) {
+					/* end root label */
+					dname_i = 0;
+					dname_num_i--;
+					/* if dname num is 0, then the
+					 * remainder is binary only */
+					if(dname_num_i == 0)
+						lablen_i = ilen;
+				}
+			} else {
+				/* scan this rdata field */
+				wfi++;
+				if(desc->_wireformat[wfi] 
+					== LDNS_RDF_TYPE_DNAME) {
+					dname_i = 1; 
+					lablen_i = (size_t)*di;
+					if(lablen_i == 0) {
+						dname_i = 0;
+						dname_num_i--;
+						if(dname_num_i == 0)
+							lablen_i = ilen;
+					}
+				} else if(desc->_wireformat[wfi] 
+					== LDNS_RDF_TYPE_STR)
+					lablen_i = (size_t)*di;
+				else	lablen_i = get_rdf_size(
+					desc->_wireformat[wfi]) - 1;
+			}
+		} else	lablen_i--;
+
+		/* advance field j; same as for i */
+		if(lablen_j == 0) { 
+			if(dname_j) {
+				lablen_j = (size_t)*dj;
+				if(lablen_j == 0) {
+					dname_j = 0;
+					dname_num_j--;
+					if(dname_num_j == 0)
+						lablen_j = jlen;
+				}
+			} else {
+				wfj++;
+				if(desc->_wireformat[wfj] 
+					== LDNS_RDF_TYPE_DNAME) {
+					dname_j = 1; 
+					lablen_j = (size_t)*dj;
+					if(lablen_j == 0) {
+						dname_j = 0;
+						dname_num_j--;
+						if(dname_num_j == 0)
+							lablen_j = jlen;
+					}
+				} else if(desc->_wireformat[wfj] 
+					== LDNS_RDF_TYPE_STR)
+					lablen_j = (size_t)*dj;
+				else	lablen_j = get_rdf_size(
+					desc->_wireformat[wfj]) - 1;
+			}
+		} else	lablen_j--;
+		di++;
+		dj++;
+	}
+	/* end of the loop; because we advanced byte by byte; now we have
+	 * that the rdata has ended, or that there is a binary remainder */
+	/* shortest first */
+	if(ilen == 0 && jlen == 0)
+		return 0;
+	if(ilen == 0)
+		return -1;
+	if(jlen == 0)
+		return 1;
+	/* binary remainder, capture comparison in wfi variable */
+	if((wfi = memcmp(di, dj, (ilen<jlen)?ilen:jlen)) != 0)
+		return wfi;
+	if(ilen < jlen)
+		return -1;
+	if(jlen < ilen)
+		return 1;
+	return 0;
+}
+
+/**
+ * Compare two RRs in the same RRset and determine their relative
+ * canonical order.
+ * @param rrset: the rrset in which to perform compares.
+ * @param i: first RR to compare
+ * @param j: first RR to compare
+ * @return 0 if RR i== RR j, -1 if <, +1 if >.
+ */
+static int
+canonical_compare(struct ub_packed_rrset_key* rrset, size_t i, size_t j)
+{
+	struct packed_rrset_data* d = (struct packed_rrset_data*)
+		rrset->entry.data;
+	const ldns_rr_descriptor* desc;
+	uint16_t type = ntohs(rrset->rk.type);
+	size_t minlen;
+	int c;
+
+	if(i==j)
+		return 0;
+	/* in case rdata-len is to be compared for canonical order
+	c = memcmp(d->rr_data[i], d->rr_data[j], 2);
+	if(c != 0)
+		return c; */
+
+	switch(type) {
+		/* These RR types have only a name as RDATA. 
+		 * This name has to be canonicalized.*/
+		case LDNS_RR_TYPE_NS:
+		case LDNS_RR_TYPE_MD:
+		case LDNS_RR_TYPE_MF:
+		case LDNS_RR_TYPE_CNAME:
+		case LDNS_RR_TYPE_MB:
+		case LDNS_RR_TYPE_MG:
+		case LDNS_RR_TYPE_MR:
+		case LDNS_RR_TYPE_PTR:
+		case LDNS_RR_TYPE_DNAME:
+			return query_dname_compare(d->rr_data[i]+2, 
+				d->rr_data[j]+2);
+
+		/* These RR types have STR and fixed size rdata fields
+		 * before one or more name fields that need canonicalizing,
+		 * and after that a byte-for byte remainder can be compared.
+		 */
+		/* type starts with the name; remainder is binary compared */
+		case LDNS_RR_TYPE_NXT: 
+		/* use rdata field formats */
+		case LDNS_RR_TYPE_MINFO:
+		case LDNS_RR_TYPE_RP:
+		case LDNS_RR_TYPE_SOA:
+		case LDNS_RR_TYPE_RT:
+		case LDNS_RR_TYPE_AFSDB:
+		case LDNS_RR_TYPE_KX:
+		case LDNS_RR_TYPE_MX:
+		case LDNS_RR_TYPE_SIG:
+		/* RRSIG signer name has to be downcased */
+		case LDNS_RR_TYPE_RRSIG:
+		case LDNS_RR_TYPE_PX:
+		case LDNS_RR_TYPE_NAPTR:
+		case LDNS_RR_TYPE_SRV:
+			desc = ldns_rr_descript(type);
+			log_assert(desc);
+			/* this holds for the types that need canonicalizing */
+			log_assert(desc->_minimum == desc->_maximum);
+			return canonical_compare_byfield(d, desc, i, j);
+
+		case LDNS_RR_TYPE_HINFO: /* no longer downcased */
+		case LDNS_RR_TYPE_NSEC: 
+	default:
+		/* For unknown RR types, or types not listed above,
+		 * no canonicalization is needed, do binary compare */
+		/* byte for byte compare, equal means shortest first*/
+		minlen = d->rr_len[i]-2;
+		if(minlen > d->rr_len[j]-2)
+			minlen = d->rr_len[j]-2;
+		c = memcmp(d->rr_data[i]+2, d->rr_data[j]+2, minlen);
+		if(c!=0)
+			return c;
+		/* rdata equal, shortest is first */
+		if(d->rr_len[i] < d->rr_len[j])
+			return -1;
+		if(d->rr_len[i] > d->rr_len[j])
+			return 1;
+		/* rdata equal, length equal */
+		break;
+	}
+	return 0;
+}
+
+int
+canonical_tree_compare(const void* k1, const void* k2)
+{
+	struct canon_rr* r1 = (struct canon_rr*)k1;
+	struct canon_rr* r2 = (struct canon_rr*)k2;
+	log_assert(r1->rrset == r2->rrset);
+	return canonical_compare(r1->rrset, r1->rr_idx, r2->rr_idx);
+}
+
+/**
+ * Sort RRs for rrset in canonical order.
+ * Does not actually canonicalize the RR rdatas.
+ * Does not touch rrsigs.
+ * @param rrset: to sort.
+ * @param d: rrset data.
+ * @param sortree: tree to sort into.
+ * @param rrs: rr storage.
+ */
+static void
+canonical_sort(struct ub_packed_rrset_key* rrset, struct packed_rrset_data* d,
+	rbtree_t* sortree, struct canon_rr* rrs)
+{
+	size_t i;
+	/* insert into rbtree to sort and detect duplicates */
+	for(i=0; i<d->count; i++) {
+		rrs[i].node.key = &rrs[i];
+		rrs[i].rrset = rrset;
+		rrs[i].rr_idx = i;
+		if(!rbtree_insert(sortree, &rrs[i].node)) {
+			/* this was a duplicate */
+		}
+	}
+}
+
+/**
+ * Inser canonical owner name into buffer.
+ * @param buf: buffer to insert into at current position.
+ * @param k: rrset with its owner name.
+ * @param sig: signature with signer name and label count.
+ * 	must be length checked, at least 18 bytes long.
+ * @param can_owner: position in buffer returned for future use.
+ * @param can_owner_len: length of canonical owner name.
+ */
+static void
+insert_can_owner(ldns_buffer* buf, struct ub_packed_rrset_key* k,
+	uint8_t* sig, uint8_t** can_owner, size_t* can_owner_len)
+{
+	int rrsig_labels = (int)sig[3];
+	int fqdn_labels = dname_signame_label_count(k->rk.dname);
+	*can_owner = ldns_buffer_current(buf);
+	if(rrsig_labels == fqdn_labels) {
+		/* no change */
+		ldns_buffer_write(buf, k->rk.dname, k->rk.dname_len);
+		query_dname_tolower(*can_owner);
+		*can_owner_len = k->rk.dname_len;
+		return;
+	}
+	log_assert(rrsig_labels < fqdn_labels);
+	/* *. | fqdn(rightmost rrsig_labels) */
+	if(rrsig_labels < fqdn_labels) {
+		int i;
+		uint8_t* nm = k->rk.dname;
+		size_t len = k->rk.dname_len;
+		/* so skip fqdn_labels-rrsig_labels */
+		for(i=0; i<fqdn_labels-rrsig_labels; i++) {
+			dname_remove_label(&nm, &len);	
+		}
+		*can_owner_len = len+2;
+		ldns_buffer_write(buf, (uint8_t*)"\001*", 2);
+		ldns_buffer_write(buf, nm, len);
+		query_dname_tolower(*can_owner);
+	}
+}
+
+/**
+ * Canonicalize Rdata in buffer.
+ * @param buf: buffer at position just after the rdata.
+ * @param rrset: rrset with type.
+ * @param len: length of the rdata (including rdatalen uint16).
+ */
+static void
+canonicalize_rdata(ldns_buffer* buf, struct ub_packed_rrset_key* rrset,
+	size_t len)
+{
+	uint8_t* datstart = ldns_buffer_current(buf)-len+2;
+	switch(ntohs(rrset->rk.type)) {
+		case LDNS_RR_TYPE_NXT: 
+		case LDNS_RR_TYPE_NS:
+		case LDNS_RR_TYPE_MD:
+		case LDNS_RR_TYPE_MF:
+		case LDNS_RR_TYPE_CNAME:
+		case LDNS_RR_TYPE_MB:
+		case LDNS_RR_TYPE_MG:
+		case LDNS_RR_TYPE_MR:
+		case LDNS_RR_TYPE_PTR:
+		case LDNS_RR_TYPE_DNAME:
+			/* type only has a single argument, the name */
+			query_dname_tolower(datstart);
+			return;
+		case LDNS_RR_TYPE_MINFO:
+		case LDNS_RR_TYPE_RP:
+		case LDNS_RR_TYPE_SOA:
+			/* two names after another */
+			query_dname_tolower(datstart);
+			query_dname_tolower(datstart + 
+				dname_valid(datstart, len-2));
+			return;
+		case LDNS_RR_TYPE_RT:
+		case LDNS_RR_TYPE_AFSDB:
+		case LDNS_RR_TYPE_KX:
+		case LDNS_RR_TYPE_MX:
+			/* skip fixed part */
+			if(len < 2+2+1) /* rdlen, skiplen, 1byteroot */
+				return;
+			datstart += 2;
+			query_dname_tolower(datstart);
+			return;
+		case LDNS_RR_TYPE_SIG:
+		/* downcase the RRSIG, compat with BIND (kept it from SIG) */
+		case LDNS_RR_TYPE_RRSIG:
+			/* skip fixed part */
+			if(len < 2+18+1)
+				return;
+			datstart += 18;
+			query_dname_tolower(datstart);
+			return;
+		case LDNS_RR_TYPE_PX:
+			/* skip, then two names after another */
+			if(len < 2+2+1) 
+				return;
+			datstart += 2;
+			query_dname_tolower(datstart);
+			query_dname_tolower(datstart + 
+				dname_valid(datstart, len-2-2));
+			return;
+		case LDNS_RR_TYPE_NAPTR:
+			if(len < 2+4)
+				return;
+			len -= 2+4;
+			datstart += 4;
+			if(len < (size_t)datstart[0]+1) /* skip text field */
+				return;
+			len -= (size_t)datstart[0]+1;
+			datstart += (size_t)datstart[0]+1;
+			if(len < (size_t)datstart[0]+1) /* skip text field */
+				return;
+			len -= (size_t)datstart[0]+1;
+			datstart += (size_t)datstart[0]+1;
+			if(len < (size_t)datstart[0]+1) /* skip text field */
+				return;
+			len -= (size_t)datstart[0]+1;
+			datstart += (size_t)datstart[0]+1;
+			if(len < 1)	/* check name is at least 1 byte*/
+				return;
+			query_dname_tolower(datstart);
+			return;
+		case LDNS_RR_TYPE_SRV:
+			/* skip fixed part */
+			if(len < 2+6+1)
+				return;
+			datstart += 6;
+			query_dname_tolower(datstart);
+			return;
+
+		/* do not canonicalize NSEC rdata name, compat with 
+		 * from bind 9.4 signer, where it does not do so */
+		case LDNS_RR_TYPE_NSEC: /* type starts with the name */
+		case LDNS_RR_TYPE_HINFO: /* not downcased */
+		/* A6 not supported */
+		default:	
+			/* nothing to do for unknown types */
+			return;
+	}
+}
+
+/**
+ * Create canonical form of rrset in the scratch buffer.
+ * @param region: temporary region.
+ * @param buf: the buffer to use.
+ * @param k: the rrset to insert.
+ * @param sig: RRSIG rdata to include.
+ * @param siglen: RRSIG rdata len excluding signature field, but inclusive
+ * 	signer name length.
+ * @param sortree: if NULL is passed a new sorted rrset tree is built.
+ * 	Otherwise it is reused.
+ * @return false on alloc error.
+ */
+static int
+rrset_canonical(struct regional* region, ldns_buffer* buf, 
+	struct ub_packed_rrset_key* k, uint8_t* sig, size_t siglen,
+	struct rbtree_t** sortree)
+{
+	struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data;
+	uint8_t* can_owner = NULL;
+	size_t can_owner_len = 0;
+	struct canon_rr* walk;
+	struct canon_rr* rrs;
+
+	if(!*sortree) {
+		*sortree = (struct rbtree_t*)regional_alloc(region, 
+			sizeof(rbtree_t));
+		if(!*sortree)
+			return 0;
+		rrs = regional_alloc(region, sizeof(struct canon_rr)*d->count);
+		if(!rrs) {
+			*sortree = NULL;
+			return 0;
+		}
+		rbtree_init(*sortree, &canonical_tree_compare);
+		canonical_sort(k, d, *sortree, rrs);
+	}
+
+	ldns_buffer_clear(buf);
+	ldns_buffer_write(buf, sig, siglen);
+	/* canonicalize signer name */
+	query_dname_tolower(ldns_buffer_begin(buf)+18); 
+	RBTREE_FOR(walk, struct canon_rr*, (*sortree)) {
+		/* see if there is enough space left in the buffer */
+		if(ldns_buffer_remaining(buf) < can_owner_len + 2 + 2 + 4
+			+ d->rr_len[walk->rr_idx]) {
+			log_err("verify: failed to canonicalize, "
+				"rrset too big");
+			return 0;
+		}
+		/* determine canonical owner name */
+		if(can_owner)
+			ldns_buffer_write(buf, can_owner, can_owner_len);
+		else	insert_can_owner(buf, k, sig, &can_owner, 
+				&can_owner_len);
+		ldns_buffer_write(buf, &k->rk.type, 2);
+		ldns_buffer_write(buf, &k->rk.rrset_class, 2);
+		ldns_buffer_write(buf, sig+4, 4);
+		ldns_buffer_write(buf, d->rr_data[walk->rr_idx], 
+			d->rr_len[walk->rr_idx]);
+		canonicalize_rdata(buf, k, d->rr_len[walk->rr_idx]);
+	}
+	ldns_buffer_flip(buf);
+	return 1;
+}
+
+/** pretty print rrsig error with dates */
+static void
+sigdate_error(const char* str, int32_t expi, int32_t incep, int32_t now)
+{
+	struct tm tm;
+	char expi_buf[16];
+	char incep_buf[16];
+	char now_buf[16];
+	time_t te, ti, tn;
+
+	if(verbosity < VERB_QUERY)
+		return;
+	te = (time_t)expi;
+	ti = (time_t)incep;
+	tn = (time_t)now;
+	memset(&tm, 0, sizeof(tm));
+	if(gmtime_r(&te, &tm) && strftime(expi_buf, 15, "%Y%m%d%H%M%S", &tm)
+	 &&gmtime_r(&ti, &tm) && strftime(incep_buf, 15, "%Y%m%d%H%M%S", &tm)
+	 &&gmtime_r(&tn, &tm) && strftime(now_buf, 15, "%Y%m%d%H%M%S", &tm)) {
+		log_info("%s expi=%s incep=%s now=%s", str, expi_buf, 
+			incep_buf, now_buf);
+	} else
+		log_info("%s expi=%u incep=%u now=%u", str, (unsigned)expi, 
+			(unsigned)incep, (unsigned)now);
+}
+
+/** check rrsig dates */
+static int
+check_dates(struct val_env* ve, uint32_t unow,
+	uint8_t* expi_p, uint8_t* incep_p, char** reason)
+{
+	/* read out the dates */
+	int32_t expi, incep, now;
+	memmove(&expi, expi_p, sizeof(expi));
+	memmove(&incep, incep_p, sizeof(incep));
+	expi = ntohl(expi);
+	incep = ntohl(incep);
+
+	/* get current date */
+	if(ve->date_override) {
+		if(ve->date_override == -1) {
+			verbose(VERB_ALGO, "date override: ignore date"); 
+			return 1;
+		}
+		now = ve->date_override;
+		verbose(VERB_ALGO, "date override option %d", (int)now); 
+	} else	now = (int32_t)unow;
+
+	/* check them */
+	if(incep - expi > 0) {
+		sigdate_error("verify: inception after expiration, "
+			"signature bad", expi, incep, now);
+		*reason = "signature inception after expiration";
+		return 0;
+	}
+	if(incep - now > 0) {
+		/* within skew ? (calc here to avoid calculation normally) */
+		int32_t skew = (expi-incep)/10;
+		if(skew < ve->skew_min) skew = ve->skew_min;
+		if(skew > ve->skew_max) skew = ve->skew_max;
+		if(incep - now > skew) {
+			sigdate_error("verify: signature bad, current time is"
+				" before inception date", expi, incep, now);
+			*reason = "signature before inception date";
+			return 0;
+		}
+		sigdate_error("verify warning suspicious signature inception "
+			" or bad local clock", expi, incep, now);
+	}
+	if(now - expi > 0) {
+		int32_t skew = (expi-incep)/10;
+		if(skew < ve->skew_min) skew = ve->skew_min;
+		if(skew > ve->skew_max) skew = ve->skew_max;
+		if(now - expi > skew) {
+			sigdate_error("verify: signature expired", expi, 
+				incep, now);
+			*reason = "signature expired";
+			return 0;
+		}
+		sigdate_error("verify warning suspicious signature expiration "
+			" or bad local clock", expi, incep, now);
+	}
+	return 1;
+}
+
+/** adjust rrset TTL for verified rrset, compare to original TTL and expi */
+static void
+adjust_ttl(struct val_env* ve, uint32_t unow, 
+	struct ub_packed_rrset_key* rrset, uint8_t* orig_p, 
+	uint8_t* expi_p, uint8_t* incep_p)
+{
+	struct packed_rrset_data* d = 
+		(struct packed_rrset_data*)rrset->entry.data;
+	/* read out the dates */
+	int32_t origttl, expittl, expi, incep, now;
+	memmove(&origttl, orig_p, sizeof(origttl));
+	memmove(&expi, expi_p, sizeof(expi));
+	memmove(&incep, incep_p, sizeof(incep));
+	expi = ntohl(expi);
+	incep = ntohl(incep);
+	origttl = ntohl(origttl);
+
+	/* get current date */
+	if(ve->date_override) {
+		now = ve->date_override;
+	} else	now = (int32_t)unow;
+	expittl = expi - now;
+
+	/* so now:
+	 * d->ttl: rrset ttl read from message or cache. May be reduced
+	 * origttl: original TTL from signature, authoritative TTL max.
+	 * expittl: TTL until the signature expires.
+	 *
+	 * Use the smallest of these.
+	 */
+	if(d->ttl > (uint32_t)origttl) {
+		verbose(VERB_QUERY, "rrset TTL larger than original TTL,"
+			" adjusting TTL downwards");
+		d->ttl = origttl;
+	}
+	if(expittl > 0 && d->ttl > (uint32_t)expittl) {
+		verbose(VERB_ALGO, "rrset TTL larger than sig expiration ttl,"
+			" adjusting TTL downwards");
+		d->ttl = expittl;
+	}
+}
+
+
+/**
+ * Output a libcrypto openssl error to the logfile.
+ * @param str: string to add to it.
+ * @param e: the error to output, error number from ERR_get_error().
+ */
+static void
+log_crypto_error(const char* str, unsigned long e)
+{
+	char buf[128];
+	/* or use ERR_error_string if ERR_error_string_n is not avail TODO */
+	ERR_error_string_n(e, buf, sizeof(buf));
+	/* buf now contains */
+	/* error:[error code]:[library name]:[function name]:[reason string] */
+	log_err("%s crypto %s", str, buf);
+}
+
+/**
+ * Setup DSA key digest in DER encoding ... 
+ * @param sig: input is signature output alloced ptr (unless failure).
+ * 	caller must free alloced ptr if this routine returns true.
+ * @param len: intput is initial siglen, output is output len.
+ * @return false on failure.
+ */
+static int
+setup_dsa_sig(unsigned char** sig, unsigned int* len)
+{
+	unsigned char* orig = *sig;
+	unsigned int origlen = *len;
+	int newlen;
+	BIGNUM *R, *S;
+	DSA_SIG *dsasig;
+
+	/* extract the R and S field from the sig buffer */
+	if(origlen < 1 + 2*SHA_DIGEST_LENGTH)
+		return 0;
+	R = BN_new();
+	if(!R) return 0;
+	(void) BN_bin2bn(orig + 1, SHA_DIGEST_LENGTH, R);
+	S = BN_new();
+	if(!S) return 0;
+	(void) BN_bin2bn(orig + 21, SHA_DIGEST_LENGTH, S);
+	dsasig = DSA_SIG_new();
+	if(!dsasig) return 0;
+
+	dsasig->r = R;
+	dsasig->s = S;
+	*sig = NULL;
+	newlen = i2d_DSA_SIG(dsasig, sig);
+	if(newlen < 0) {
+		free(*sig);
+		return 0;
+	}
+	*len = (unsigned int)newlen;
+	DSA_SIG_free(dsasig);
+	return 1;
+}
+
+/**
+ * Setup key and digest for verification. Adjust sig if necessary.
+ *
+ * @param algo: key algorithm
+ * @param evp_key: EVP PKEY public key to create.
+ * @param digest_type: digest type to use
+ * @param key: key to setup for.
+ * @param keylen: length of key.
+ * @return false on failure.
+ */
+static int
+setup_key_digest(int algo, EVP_PKEY** evp_key, const EVP_MD** digest_type, 
+	unsigned char* key, size_t keylen)
+{
+	DSA* dsa;
+	RSA* rsa;
+
+	switch(algo) {
+		case LDNS_DSA:
+		case LDNS_DSA_NSEC3:
+			*evp_key = EVP_PKEY_new();
+			if(!*evp_key) {
+				log_err("verify: malloc failure in crypto");
+				return sec_status_unchecked;
+			}
+			dsa = ldns_key_buf2dsa_raw(key, keylen);
+			if(!dsa) {
+				verbose(VERB_QUERY, "verify: "
+					"ldns_key_buf2dsa_raw failed");
+				return 0;
+			}
+			if(EVP_PKEY_assign_DSA(*evp_key, dsa) == 0) {
+				verbose(VERB_QUERY, "verify: "
+					"EVP_PKEY_assign_DSA failed");
+				return 0;
+			}
+			*digest_type = EVP_dss1();
+
+			break;
+		case LDNS_RSASHA1:
+		case LDNS_RSASHA1_NSEC3:
+#if defined(HAVE_EVP_SHA256) && defined(USE_SHA2)
+		case LDNS_RSASHA256:
+#endif
+#if defined(HAVE_EVP_SHA512) && defined(USE_SHA2)
+		case LDNS_RSASHA512:
+#endif
+			*evp_key = EVP_PKEY_new();
+			if(!*evp_key) {
+				log_err("verify: malloc failure in crypto");
+				return sec_status_unchecked;
+			}
+			rsa = ldns_key_buf2rsa_raw(key, keylen);
+			if(!rsa) {
+				verbose(VERB_QUERY, "verify: "
+					"ldns_key_buf2rsa_raw SHA failed");
+				return 0;
+			}
+			if(EVP_PKEY_assign_RSA(*evp_key, rsa) == 0) {
+				verbose(VERB_QUERY, "verify: "
+					"EVP_PKEY_assign_RSA SHA failed");
+				return 0;
+			}
+
+			/* select SHA version */
+#if defined(HAVE_EVP_SHA256) && defined(USE_SHA2)
+			if(algo == LDNS_RSASHA256)
+				*digest_type = EVP_sha256();
+			else
+#endif
+#if defined(HAVE_EVP_SHA512) && defined(USE_SHA2)
+				if(algo == LDNS_RSASHA512)
+				*digest_type = EVP_sha512();
+			else
+#endif
+				*digest_type = EVP_sha1();
+
+			break;
+		case LDNS_RSAMD5:
+			*evp_key = EVP_PKEY_new();
+			if(!*evp_key) {
+				log_err("verify: malloc failure in crypto");
+				return sec_status_unchecked;
+			}
+			rsa = ldns_key_buf2rsa_raw(key, keylen);
+			if(!rsa) {
+				verbose(VERB_QUERY, "verify: "
+					"ldns_key_buf2rsa_raw MD5 failed");
+				return 0;
+			}
+			if(EVP_PKEY_assign_RSA(*evp_key, rsa) == 0) {
+				verbose(VERB_QUERY, "verify: "
+					"EVP_PKEY_assign_RSA MD5 failed");
+				return 0;
+			}
+			*digest_type = EVP_md5();
+
+			break;
+#ifdef USE_GOST
+		case LDNS_ECC_GOST:
+			*evp_key = ldns_gost2pkey_raw(key, keylen);
+			if(!*evp_key) {
+				verbose(VERB_QUERY, "verify: "
+					"ldns_gost2pkey_raw failed");
+				return 0;
+			}
+			*digest_type = EVP_get_digestbyname("md_gost94");
+			if(!*digest_type) {
+				verbose(VERB_QUERY, "verify: "
+					"EVP_getdigest md_gost94 failed");
+				return 0;
+			}
+			break;
+#endif
+		default:
+			verbose(VERB_QUERY, "verify: unknown algorithm %d", 
+				algo);
+			return 0;
+	}
+	return 1;
+}
+
+/**
+ * Check a canonical sig+rrset and signature against a dnskey
+ * @param buf: buffer with data to verify, the first rrsig part and the
+ *	canonicalized rrset.
+ * @param algo: DNSKEY algorithm.
+ * @param sigblock: signature rdata field from RRSIG
+ * @param sigblock_len: length of sigblock data.
+ * @param key: public key data from DNSKEY RR.
+ * @param keylen: length of keydata.
+ * @param reason: bogus reason in more detail.
+ * @return secure if verification succeeded, bogus on crypto failure,
+ *	unchecked on format errors and alloc failures.
+ */
+static enum sec_status
+verify_canonrrset(ldns_buffer* buf, int algo, unsigned char* sigblock, 
+	unsigned int sigblock_len, unsigned char* key, unsigned int keylen,
+	char** reason)
+{
+	const EVP_MD *digest_type;
+	EVP_MD_CTX ctx;
+	int res, dofree = 0;
+	EVP_PKEY *evp_key = NULL;
+	
+	if(!setup_key_digest(algo, &evp_key, &digest_type, key, keylen)) {
+		verbose(VERB_QUERY, "verify: failed to setup key");
+		*reason = "use of key for crypto failed";
+		EVP_PKEY_free(evp_key);
+		return sec_status_bogus;
+	}
+	/* if it is a DSA signature in bind format, convert to DER format */
+	if((algo == LDNS_DSA || algo == LDNS_DSA_NSEC3) && 
+		sigblock_len == 1+2*SHA_DIGEST_LENGTH) {
+		if(!setup_dsa_sig(&sigblock, &sigblock_len)) {
+			verbose(VERB_QUERY, "verify: failed to setup DSA sig");
+			*reason = "use of key for DSA crypto failed";
+			EVP_PKEY_free(evp_key);
+			return sec_status_bogus;
+		}
+		dofree = 1;
+	} 
+
+	/* do the signature cryptography work */
+	EVP_MD_CTX_init(&ctx);
+	if(EVP_VerifyInit(&ctx, digest_type) == 0) {
+		verbose(VERB_QUERY, "verify: EVP_VerifyInit failed");
+		EVP_PKEY_free(evp_key);
+		if(dofree) free(sigblock);
+		return sec_status_unchecked;
+	}
+	if(EVP_VerifyUpdate(&ctx, (unsigned char*)ldns_buffer_begin(buf), 
+		(unsigned int)ldns_buffer_limit(buf)) == 0) {
+		verbose(VERB_QUERY, "verify: EVP_VerifyUpdate failed");
+		EVP_PKEY_free(evp_key);
+		if(dofree) free(sigblock);
+		return sec_status_unchecked;
+	}
+		
+	res = EVP_VerifyFinal(&ctx, sigblock, sigblock_len, evp_key);
+	if(EVP_MD_CTX_cleanup(&ctx) == 0) {
+		verbose(VERB_QUERY, "verify: EVP_MD_CTX_cleanup failed");
+		EVP_PKEY_free(evp_key);
+		if(dofree) free(sigblock);
+		return sec_status_unchecked;
+	}
+	EVP_PKEY_free(evp_key);
+
+	if(dofree)
+		free(sigblock);
+
+	if(res == 1) {
+		return sec_status_secure;
+	} else if(res == 0) {
+		verbose(VERB_QUERY, "verify: signature mismatch");
+		*reason = "signature crypto failed";
+		return sec_status_bogus;
+	}
+
+	log_crypto_error("verify:", ERR_get_error());
+	return sec_status_unchecked;
+}
+
+enum sec_status 
+dnskey_verify_rrset_sig(struct regional* region, ldns_buffer* buf, 
+	struct val_env* ve, uint32_t now,
+        struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
+        size_t dnskey_idx, size_t sig_idx,
+	struct rbtree_t** sortree, int* buf_canon, char** reason)
+{
+	enum sec_status sec;
+	uint8_t* sig;		/* RRSIG rdata */
+	size_t siglen;
+	size_t rrnum = rrset_get_count(rrset);
+	uint8_t* signer;	/* rrsig signer name */
+	size_t signer_len;
+	unsigned char* sigblock; /* signature rdata field */
+	unsigned int sigblock_len;
+	uint16_t ktag;		/* DNSKEY key tag */
+	unsigned char* key;	/* public key rdata field */
+	unsigned int keylen;
+	rrset_get_rdata(rrset, rrnum + sig_idx, &sig, &siglen);
+	/* min length of rdatalen, fixed rrsig, root signer, 1 byte sig */
+	if(siglen < 2+20) {
+		verbose(VERB_QUERY, "verify: signature too short");
+		*reason = "signature too short";
+		return sec_status_bogus;
+	}
+
+	if(!(dnskey_get_flags(dnskey, dnskey_idx) & DNSKEY_BIT_ZSK)) {
+		verbose(VERB_QUERY, "verify: dnskey without ZSK flag");
+		*reason = "dnskey without ZSK flag";
+		return sec_status_bogus; 
+	}
+
+	if(dnskey_get_protocol(dnskey, dnskey_idx) != LDNS_DNSSEC_KEYPROTO) { 
+		/* RFC 4034 says DNSKEY PROTOCOL MUST be 3 */
+		verbose(VERB_QUERY, "verify: dnskey has wrong key protocol");
+		*reason = "dnskey has wrong protocolnumber";
+		return sec_status_bogus;
+	}
+
+	/* verify as many fields in rrsig as possible */
+	signer = sig+2+18;
+	signer_len = dname_valid(signer, siglen-2-18);
+	if(!signer_len) {
+		verbose(VERB_QUERY, "verify: malformed signer name");
+		*reason = "signer name malformed";
+		return sec_status_bogus; /* signer name invalid */
+	}
+	if(!dname_subdomain_c(rrset->rk.dname, signer)) {
+		verbose(VERB_QUERY, "verify: signer name is off-tree");
+		*reason = "signer name off-tree";
+		return sec_status_bogus; /* signer name offtree */
+	}
+	sigblock = (unsigned char*)signer+signer_len;
+	if(siglen < 2+18+signer_len+1) {
+		verbose(VERB_QUERY, "verify: too short, no signature data");
+		*reason = "signature too short, no signature data";
+		return sec_status_bogus; /* sig rdf is < 1 byte */
+	}
+	sigblock_len = (unsigned int)(siglen - 2 - 18 - signer_len);
+
+	/* verify key dname == sig signer name */
+	if(query_dname_compare(signer, dnskey->rk.dname) != 0) {
+		verbose(VERB_QUERY, "verify: wrong key for rrsig");
+		log_nametypeclass(VERB_QUERY, "RRSIG signername is", 
+			signer, 0, 0);
+		log_nametypeclass(VERB_QUERY, "the key name is", 
+			dnskey->rk.dname, 0, 0);
+		*reason = "signer name mismatches key name";
+		return sec_status_bogus;
+	}
+
+	/* verify covered type */
+	/* memcmp works because type is in network format for rrset */
+	if(memcmp(sig+2, &rrset->rk.type, 2) != 0) {
+		verbose(VERB_QUERY, "verify: wrong type covered");
+		*reason = "signature covers wrong type";
+		return sec_status_bogus;
+	}
+	/* verify keytag and sig algo (possibly again) */
+	if((int)sig[2+2] != dnskey_get_algo(dnskey, dnskey_idx)) {
+		verbose(VERB_QUERY, "verify: wrong algorithm");
+		*reason = "signature has wrong algorithm";
+		return sec_status_bogus;
+	}
+	ktag = htons(dnskey_calc_keytag(dnskey, dnskey_idx));
+	if(memcmp(sig+2+16, &ktag, 2) != 0) {
+		verbose(VERB_QUERY, "verify: wrong keytag");
+		*reason = "signature has wrong keytag";
+		return sec_status_bogus;
+	}
+
+	/* verify labels is in a valid range */
+	if((int)sig[2+3] > dname_signame_label_count(rrset->rk.dname)) {
+		verbose(VERB_QUERY, "verify: labelcount out of range");
+		*reason = "signature labelcount out of range";
+		return sec_status_bogus;
+	}
+
+	/* original ttl, always ok */
+
+	if(!*buf_canon) {
+		/* create rrset canonical format in buffer, ready for 
+		 * signature */
+		if(!rrset_canonical(region, buf, rrset, sig+2, 
+			18 + signer_len, sortree)) {
+			log_err("verify: failed due to alloc error");
+			return sec_status_unchecked;
+		}
+		*buf_canon = 1;
+	}
+
+	/* check that dnskey is available */
+	dnskey_get_pubkey(dnskey, dnskey_idx, &key, &keylen);
+	if(!key) {
+		verbose(VERB_QUERY, "verify: short DNSKEY RR");
+		return sec_status_unchecked;
+	}
+
+	/* verify */
+	sec = verify_canonrrset(buf, (int)sig[2+2],
+		sigblock, sigblock_len, key, keylen, reason);
+	
+	if(sec == sec_status_secure) {
+		/* check if TTL is too high - reduce if so */
+		adjust_ttl(ve, now, rrset, sig+2+4, sig+2+8, sig+2+12);
+
+		/* verify inception, expiration dates 
+		 * Do this last so that if you ignore expired-sigs the
+		 * rest is sure to be OK. */
+		if(!check_dates(ve, now, sig+2+8, sig+2+12, reason)) {
+			return sec_status_bogus;
+		}
+	}
+
+	return sec;
+}
diff --git a/3rdParty/Unbound/src/src/validator/val_sigcrypt.h b/3rdParty/Unbound/src/src/validator/val_sigcrypt.h
new file mode 100644
index 0000000..c220b00
--- /dev/null
+++ b/3rdParty/Unbound/src/src/validator/val_sigcrypt.h
@@ -0,0 +1,311 @@
+/*
+ * validator/val_sigcrypt.h - validator signature crypto functions.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains helper functions for the validator module.
+ * The functions help with signature verification and checking, the
+ * bridging between RR wireformat data and crypto calls.
+ */
+
+#ifndef VALIDATOR_VAL_SIGCRYPT_H
+#define VALIDATOR_VAL_SIGCRYPT_H
+#include "util/data/packed_rrset.h"
+struct val_env;
+struct module_env;
+struct ub_packed_rrset_key;
+struct rbtree_t;
+struct regional;
+
+/** number of entries in algorithm needs array */
+#define ALGO_NEEDS_MAX 256
+
+/**
+ * Storage for algorithm needs.  DNSKEY algorithms.
+ */
+struct algo_needs {
+	/** the algorithms (8-bit) with each a number.
+	 * 0: not marked.
+	 * 1: marked 'necessary but not yet fulfilled'
+	 * 2: marked bogus.
+	 * Indexed by algorithm number.
+	 */
+	uint8_t needs[ALGO_NEEDS_MAX];
+	/** the number of entries in the array that are unfulfilled */
+	size_t num;
+};
+
+/**
+ * Initialize algo needs structure, set algos from rrset as needed.
+ * Results are added to an existing need structure.
+ * @param n: struct with storage.
+ * @param dnskey: algos from this struct set as necessary. DNSKEY set.
+ * @param sigalg: adds to signalled algorithm list too.
+ */
+void algo_needs_init_dnskey_add(struct algo_needs* n,
+	struct ub_packed_rrset_key* dnskey, uint8_t* sigalg);
+
+/**
+ * Initialize algo needs structure from a signalled algo list.
+ * @param n: struct with storage.
+ * @param sigalg: signalled algorithm list, numbers ends with 0.
+ */
+void algo_needs_init_list(struct algo_needs* n, uint8_t* sigalg);
+
+/**
+ * Initialize algo needs structure, set algos from rrset as needed.
+ * @param n: struct with storage.
+ * @param ds: algos from this struct set as necessary. DS set.
+ * @param fav_ds_algo: filter to use only this DS algo.
+ * @param sigalg: list of signalled algos, constructed as output,
+ *	provide size ALGO_NEEDS_MAX+1. list of algonumbers, ends with a zero.
+ */
+void algo_needs_init_ds(struct algo_needs* n, struct ub_packed_rrset_key* ds,
+	int fav_ds_algo, uint8_t* sigalg);
+
+/**
+ * Mark this algorithm as a success, sec_secure, and see if we are done.
+ * @param n: storage structure processed.
+ * @param algo: the algorithm processed to be secure.
+ * @return if true, processing has finished successfully, we are satisfied.
+ */
+int algo_needs_set_secure(struct algo_needs* n, uint8_t algo);
+
+/**
+ * Mark this algorithm a failure, sec_bogus.  It can later be overridden
+ * by a success for this algorithm (with a different signature).
+ * @param n: storage structure processed.
+ * @param algo: the algorithm processed to be bogus.
+ */
+void algo_needs_set_bogus(struct algo_needs* n, uint8_t algo);
+
+/**
+ * See how many algorithms are missing (not bogus or secure, but not processed)
+ * @param n: storage structure processed.
+ * @return number of algorithms missing after processing.
+ */
+size_t algo_needs_num_missing(struct algo_needs* n);
+
+/**
+ * See which algo is missing.
+ * @param n: struct after processing.
+ * @return if 0 an algorithm was bogus, if a number, this algorithm was
+ *   missing.  So on 0, report why that was bogus, on number report a missing
+ *   algorithm.  There could be multiple missing, this reports the first one.
+ */
+int algo_needs_missing(struct algo_needs* n);
+
+/**
+ * Format error reason for algorithm missing.
+ * @param env: module env with scratch for temp storage of string.
+ * @param alg: DNSKEY-algorithm missing.
+ * @param reason: destination.
+ * @param s: string, appended with 'with algorithm ..'.
+ */
+void algo_needs_reason(struct module_env* env, int alg, char** reason, char* s);
+
+/** 
+ * Check if dnskey matches a DS digest 
+ * Does not check dnskey-keyid footprint, just the digest.
+ * @param env: module environment. Uses scratch space.
+ * @param dnskey_rrset: DNSKEY rrset.
+ * @param dnskey_idx: index of RR in rrset.
+ * @param ds_rrset: DS rrset
+ * @param ds_idx: index of RR in DS rrset.
+ * @return true if it matches, false on error, not supported or no match.
+ */
+int ds_digest_match_dnskey(struct module_env* env,
+	struct ub_packed_rrset_key* dnskey_rrset, size_t dnskey_idx,
+	struct ub_packed_rrset_key* ds_rrset, size_t ds_idx);
+
+/** 
+ * Get dnskey keytag, footprint value
+ * @param dnskey_rrset: DNSKEY rrset.
+ * @param dnskey_idx: index of RR in rrset.
+ * @return the keytag or 0 for badly formatted DNSKEYs.
+ */
+uint16_t dnskey_calc_keytag(struct ub_packed_rrset_key* dnskey_rrset, 
+	size_t dnskey_idx);
+
+/**
+ * Get DS keytag, footprint value that matches the DNSKEY keytag it signs.
+ * @param ds_rrset: DS rrset
+ * @param ds_idx: index of RR in DS rrset.
+ * @return the keytag or 0 for badly formatted DSs.
+ */ 
+uint16_t ds_get_keytag(struct ub_packed_rrset_key* ds_rrset, size_t ds_idx);
+
+/** 
+ * See if DNSKEY algorithm is supported 
+ * @param dnskey_rrset: DNSKEY rrset.
+ * @param dnskey_idx: index of RR in rrset.
+ * @return true if supported.
+ */
+int dnskey_algo_is_supported(struct ub_packed_rrset_key* dnskey_rrset, 
+	size_t dnskey_idx);
+
+/** 
+ * See if DS digest algorithm is supported 
+ * @param ds_rrset: DS rrset
+ * @param ds_idx: index of RR in DS rrset.
+ * @return true if supported.
+ */
+int ds_digest_algo_is_supported(struct ub_packed_rrset_key* ds_rrset, 
+	size_t ds_idx);
+
+/**
+ * Get DS RR digest algorithm
+ * @param ds_rrset: DS rrset.
+ * @param ds_idx: which DS.
+ * @return algorithm or 0 if DS too short.
+ */
+int ds_get_digest_algo(struct ub_packed_rrset_key* ds_rrset, size_t ds_idx);
+
+/** 
+ * See if DS key algorithm is supported 
+ * @param ds_rrset: DS rrset
+ * @param ds_idx: index of RR in DS rrset.
+ * @return true if supported.
+ */
+int ds_key_algo_is_supported(struct ub_packed_rrset_key* ds_rrset, 
+	size_t ds_idx);
+
+/**
+ * Get DS RR key algorithm. This value should match with the DNSKEY algo.
+ * @param k: DS rrset.
+ * @param idx: which DS.
+ * @return algorithm or 0 if DS too short.
+ */
+int ds_get_key_algo(struct ub_packed_rrset_key* k, size_t idx);
+
+/**
+ * Get DNSKEY RR signature algorithm
+ * @param k: DNSKEY rrset.
+ * @param idx: which DNSKEY RR.
+ * @return algorithm or 0 if DNSKEY too short.
+ */
+int dnskey_get_algo(struct ub_packed_rrset_key* k, size_t idx);
+
+/**
+ * Get DNSKEY RR flags 
+ * @param k: DNSKEY rrset.
+ * @param idx: which DNSKEY RR.
+ * @return flags or 0 if DNSKEY too short.
+ */
+uint16_t dnskey_get_flags(struct ub_packed_rrset_key* k, size_t idx);
+
+/** 
+ * Verify rrset against dnskey rrset. 
+ * @param env: module environment, scratch space is used.
+ * @param ve: validator environment, date settings.
+ * @param rrset: to be validated.
+ * @param dnskey: DNSKEY rrset, keyset to try.
+ * @param sigalg: if nonNULL provide downgrade protection otherwise one
+ *   algorithm is enough.
+ * @param reason: if bogus, a string returned, fixed or alloced in scratch.
+ * @return SECURE if one key in the set verifies one rrsig.
+ *	UNCHECKED on allocation errors, unsupported algorithms, malformed data,
+ *	and BOGUS on verification failures (no keys match any signatures).
+ */
+enum sec_status dnskeyset_verify_rrset(struct module_env* env, 
+	struct val_env* ve, struct ub_packed_rrset_key* rrset, 
+	struct ub_packed_rrset_key* dnskey, uint8_t* sigalg, char** reason);
+
+/** 
+ * verify rrset against one specific dnskey (from rrset) 
+ * @param env: module environment, scratch space is used.
+ * @param ve: validator environment, date settings.
+ * @param rrset: to be validated.
+ * @param dnskey: DNSKEY rrset, keyset.
+ * @param dnskey_idx: which key from the rrset to try.
+ * @param reason: if bogus, a string returned, fixed or alloced in scratch.
+ * @return secure if *this* key signs any of the signatures on rrset.
+ *	unchecked on error or and bogus on bad signature.
+ */
+enum sec_status dnskey_verify_rrset(struct module_env* env, 
+	struct val_env* ve, struct ub_packed_rrset_key* rrset, 
+	struct ub_packed_rrset_key* dnskey, size_t dnskey_idx, char** reason);
+
+/** 
+ * verify rrset, with dnskey rrset, for a specific rrsig in rrset
+ * @param env: module environment, scratch space is used.
+ * @param ve: validator environment, date settings.
+ * @param now: current time for validation (can be overridden).
+ * @param rrset: to be validated.
+ * @param dnskey: DNSKEY rrset, keyset to try.
+ * @param sig_idx: which signature to try to validate.
+ * @param sortree: reused sorted order. Stored in region. Pass NULL at start,
+ * 	and for a new rrset.
+ * @param reason: if bogus, a string returned, fixed or alloced in scratch.
+ * @return secure if any key signs *this* signature. bogus if no key signs it,
+ *	or unchecked on error.
+ */
+enum sec_status dnskeyset_verify_rrset_sig(struct module_env* env, 
+	struct val_env* ve, uint32_t now, struct ub_packed_rrset_key* rrset, 
+	struct ub_packed_rrset_key* dnskey, size_t sig_idx, 
+	struct rbtree_t** sortree, char** reason);
+
+/** 
+ * verify rrset, with specific dnskey(from set), for a specific rrsig 
+ * @param region: scratch region used for temporary allocation.
+ * @param buf: scratch buffer used for canonicalized rrset data.
+ * @param ve: validator environment, date settings.
+ * @param now: current time for validation (can be overridden).
+ * @param rrset: to be validated.
+ * @param dnskey: DNSKEY rrset, keyset.
+ * @param dnskey_idx: which key from the rrset to try.
+ * @param sig_idx: which signature to try to validate.
+ * @param sortree: pass NULL at start, the sorted rrset order is returned.
+ * 	pass it again for the same rrset.
+ * @param buf_canon: if true, the buffer is already canonical.
+ * 	pass false at start. pass old value only for same rrset and same
+ * 	signature (but perhaps different key) for reuse.
+ * @param reason: if bogus, a string returned, fixed or alloced in scratch.
+ * @return secure if this key signs this signature. unchecked on error or 
+ *	bogus if it did not validate.
+ */
+enum sec_status dnskey_verify_rrset_sig(struct regional* region, 
+	ldns_buffer* buf, struct val_env* ve, uint32_t now,
+	struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, 
+	size_t dnskey_idx, size_t sig_idx,
+	struct rbtree_t** sortree, int* buf_canon, char** reason);
+
+/**
+ * canonical compare for two tree entries
+ */
+int canonical_tree_compare(const void* k1, const void* k2);
+
+#endif /* VALIDATOR_VAL_SIGCRYPT_H */
diff --git a/3rdParty/Unbound/src/src/validator/val_utils.c b/3rdParty/Unbound/src/src/validator/val_utils.c
new file mode 100644
index 0000000..b0475d8
--- /dev/null
+++ b/3rdParty/Unbound/src/src/validator/val_utils.c
@@ -0,0 +1,1082 @@
+/*
+ * validator/val_utils.c - validator utility functions.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains helper functions for the validator module.
+ */
+#include "config.h"
+#include "validator/val_utils.h"
+#include "validator/validator.h"
+#include "validator/val_kentry.h"
+#include "validator/val_sigcrypt.h"
+#include "validator/val_anchor.h"
+#include "validator/val_nsec.h"
+#include "validator/val_neg.h"
+#include "services/cache/rrset.h"
+#include "services/cache/dns.h"
+#include "util/data/msgreply.h"
+#include "util/data/packed_rrset.h"
+#include "util/data/dname.h"
+#include "util/net_help.h"
+#include "util/module.h"
+#include "util/regional.h"
+#include "util/config_file.h"
+
+enum val_classification 
+val_classify_response(uint16_t query_flags, struct query_info* origqinf,
+	struct query_info* qinf, struct reply_info* rep, size_t skip)
+{
+	int rcode = (int)FLAGS_GET_RCODE(rep->flags);
+	size_t i;
+
+	/* Normal Name Error's are easy to detect -- but don't mistake a CNAME
+	 * chain ending in NXDOMAIN. */
+	if(rcode == LDNS_RCODE_NXDOMAIN && rep->an_numrrsets == 0)
+		return VAL_CLASS_NAMEERROR;
+
+	/* check for referral: nonRD query and it looks like a nodata */
+	if(!(query_flags&BIT_RD) && rep->an_numrrsets == 0 &&
+		rcode == LDNS_RCODE_NOERROR) {
+		/* SOA record in auth indicates it is NODATA instead.
+		 * All validation requiring NODATA messages have SOA in 
+		 * authority section. */
+		/* uses fact that answer section is empty */
+		int saw_ns = 0;
+		for(i=0; i<rep->ns_numrrsets; i++) {
+			if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_SOA)
+				return VAL_CLASS_NODATA;
+			if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_DS)
+				return VAL_CLASS_REFERRAL;
+			if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS)
+				saw_ns = 1;
+		}
+		return saw_ns?VAL_CLASS_REFERRAL:VAL_CLASS_NODATA;
+	}
+	/* root referral where NS set is in the answer section */
+	if(!(query_flags&BIT_RD) && rep->ns_numrrsets == 0 &&
+		rep->an_numrrsets == 1 && rcode == LDNS_RCODE_NOERROR &&
+		ntohs(rep->rrsets[0]->rk.type) == LDNS_RR_TYPE_NS &&
+		query_dname_compare(rep->rrsets[0]->rk.dname, 
+			origqinf->qname) != 0)
+		return VAL_CLASS_REFERRAL;
+
+	/* dump bad messages */
+	if(rcode != LDNS_RCODE_NOERROR && rcode != LDNS_RCODE_NXDOMAIN)
+		return VAL_CLASS_UNKNOWN;
+	/* next check if the skip into the answer section shows no answer */
+	if(skip>0 && rep->an_numrrsets <= skip)
+		return VAL_CLASS_CNAMENOANSWER;
+
+	/* Next is NODATA */
+	if(rcode == LDNS_RCODE_NOERROR && rep->an_numrrsets == 0)
+		return VAL_CLASS_NODATA;
+	
+	/* We distinguish between CNAME response and other positive/negative
+	 * responses because CNAME answers require extra processing. */
+
+	/* We distinguish between ANY and CNAME or POSITIVE because 
+	 * ANY responses are validated differently. */
+	if(rcode == LDNS_RCODE_NOERROR && qinf->qtype == LDNS_RR_TYPE_ANY)
+		return VAL_CLASS_ANY;
+	
+	/* Note that DNAMEs will be ignored here, unless qtype=DNAME. Unless
+	 * qtype=CNAME, this will yield a CNAME response. */
+	for(i=skip; i<rep->an_numrrsets; i++) {
+		if(rcode == LDNS_RCODE_NOERROR &&
+			ntohs(rep->rrsets[i]->rk.type) == qinf->qtype)
+			return VAL_CLASS_POSITIVE;
+		if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_CNAME)
+			return VAL_CLASS_CNAME;
+	}
+	log_dns_msg("validator: error. failed to classify response message: ",
+		qinf, rep);
+	return VAL_CLASS_UNKNOWN;
+}
+
+/** Get signer name from RRSIG */
+static void
+rrsig_get_signer(uint8_t* data, size_t len, uint8_t** sname, size_t* slen)
+{
+	/* RRSIG rdata is not allowed to be compressed, it is stored
+	 * uncompressed in memory as well, so return a ptr to the name */
+	if(len < 21) {
+		/* too short RRSig:
+		 * short, byte, byte, long, long, long, short, "." is
+		 * 2	1	1	4	4  4	2	1 = 19
+		 * 			and a skip of 18 bytes to the name.
+		 * +2 for the rdatalen is 21 bytes len for root label */
+		*sname = NULL;
+		*slen = 0;
+		return;
+	}
+	data += 20; /* skip the fixed size bits */
+	len -= 20;
+	*slen = dname_valid(data, len);
+	if(!*slen) {
+		/* bad dname in this rrsig. */
+		*sname = NULL;
+		return;
+	}
+	*sname = data;
+}
+
+void 
+val_find_rrset_signer(struct ub_packed_rrset_key* rrset, uint8_t** sname,
+	size_t* slen)
+{
+	struct packed_rrset_data* d = (struct packed_rrset_data*)
+		rrset->entry.data;
+	/* return signer for first signature, or NULL */
+	if(d->rrsig_count == 0) {
+		*sname = NULL;
+		*slen = 0;
+		return;
+	}
+	/* get rrsig signer name out of the signature */
+	rrsig_get_signer(d->rr_data[d->count], d->rr_len[d->count], 
+		sname, slen);
+}
+
+/**
+ * Find best signer name in this set of rrsigs.
+ * @param rrset: which rrsigs to look through.
+ * @param qinf: the query name that needs validation.
+ * @param signer_name: the best signer_name. Updated if a better one is found.
+ * @param signer_len: length of signer name.
+ * @param matchcount: count of current best name (starts at 0 for no match).
+ * 	Updated if match is improved.
+ */
+static void
+val_find_best_signer(struct ub_packed_rrset_key* rrset, 
+	struct query_info* qinf, uint8_t** signer_name, size_t* signer_len, 
+	int* matchcount)
+{
+	struct packed_rrset_data* d = (struct packed_rrset_data*)
+		rrset->entry.data;
+	uint8_t* sign;
+	size_t i;
+	int m;
+	for(i=d->count; i<d->count+d->rrsig_count; i++) {
+		sign = d->rr_data[i]+2+18;
+		/* look at signatures that are valid (long enough),
+		 * and have a signer name that is a superdomain of qname,
+		 * and then check the number of labels in the shared topdomain
+		 * improve the match if possible */
+		if(d->rr_len[i] > 2+19 && /* rdata, sig + root label*/
+			dname_subdomain_c(qinf->qname, sign)) {
+			(void)dname_lab_cmp(qinf->qname, 
+				dname_count_labels(qinf->qname), 
+				sign, dname_count_labels(sign), &m);
+			if(m > *matchcount) {
+				*matchcount = m;
+				*signer_name = sign;
+				(void)dname_count_size_labels(*signer_name,
+					signer_len);
+			}
+		}
+	}
+}
+
+void 
+val_find_signer(enum val_classification subtype, struct query_info* qinf, 
+	struct reply_info* rep, size_t skip, uint8_t** signer_name, 
+	size_t* signer_len)
+{
+	size_t i;
+	
+	if(subtype == VAL_CLASS_POSITIVE || subtype == VAL_CLASS_ANY) {
+		/* check for the answer rrset */
+		for(i=skip; i<rep->an_numrrsets; i++) {
+			if(query_dname_compare(qinf->qname, 
+				rep->rrsets[i]->rk.dname) == 0) {
+				val_find_rrset_signer(rep->rrsets[i], 
+					signer_name, signer_len);
+				return;
+			}
+		}
+		*signer_name = NULL;
+		*signer_len = 0;
+	} else if(subtype == VAL_CLASS_CNAME) {
+		/* check for the first signed cname/dname rrset */
+		for(i=skip; i<rep->an_numrrsets; i++) {
+			val_find_rrset_signer(rep->rrsets[i], 
+				signer_name, signer_len);
+			if(*signer_name)
+				return;
+			if(ntohs(rep->rrsets[i]->rk.type) != LDNS_RR_TYPE_DNAME)
+				break; /* only check CNAME after a DNAME */
+		}
+		*signer_name = NULL;
+		*signer_len = 0;
+	} else if(subtype == VAL_CLASS_NAMEERROR 
+		|| subtype == VAL_CLASS_NODATA) {
+		/*Check to see if the AUTH section NSEC record(s) have rrsigs*/
+		for(i=rep->an_numrrsets; i<
+			rep->an_numrrsets+rep->ns_numrrsets; i++) {
+			if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC
+				|| ntohs(rep->rrsets[i]->rk.type) ==
+				LDNS_RR_TYPE_NSEC3) {
+				val_find_rrset_signer(rep->rrsets[i], 
+					signer_name, signer_len);
+				return;
+			}
+		}
+	} else if(subtype == VAL_CLASS_CNAMENOANSWER) {
+		/* find closest superdomain signer name in authority section
+		 * NSEC and NSEC3s */
+		int matchcount = 0;
+		*signer_name = NULL;
+		*signer_len = 0;
+		for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep->
+			ns_numrrsets; i++) { 
+			if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC
+				|| ntohs(rep->rrsets[i]->rk.type) == 
+				LDNS_RR_TYPE_NSEC3) {
+				val_find_best_signer(rep->rrsets[i], qinf,
+					signer_name, signer_len, &matchcount);
+			}
+		}
+	} else if(subtype == VAL_CLASS_REFERRAL) {
+		/* find keys for the item at skip */
+		if(skip < rep->rrset_count) {
+			val_find_rrset_signer(rep->rrsets[skip], 
+				signer_name, signer_len);
+			return;
+		}
+		*signer_name = NULL;
+		*signer_len = 0;
+	} else {
+		verbose(VERB_QUERY, "find_signer: could not find signer name"
+			" for unknown type response");
+		*signer_name = NULL;
+		*signer_len = 0;
+	}
+}
+
+/** return number of rrs in an rrset */
+static size_t
+rrset_get_count(struct ub_packed_rrset_key* rrset)
+{
+	struct packed_rrset_data* d = (struct packed_rrset_data*)
+		rrset->entry.data;
+	if(!d) return 0;
+	return d->count;
+}
+
+/** return TTL of rrset */
+static uint32_t
+rrset_get_ttl(struct ub_packed_rrset_key* rrset)
+{
+	struct packed_rrset_data* d = (struct packed_rrset_data*)
+		rrset->entry.data;
+	if(!d) return 0;
+	return d->ttl;
+}
+
+enum sec_status 
+val_verify_rrset(struct module_env* env, struct val_env* ve,
+        struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys,
+	uint8_t* sigalg, char** reason)
+{
+	enum sec_status sec;
+	struct packed_rrset_data* d = (struct packed_rrset_data*)rrset->
+		entry.data;
+	if(d->security == sec_status_secure) {
+		/* re-verify all other statuses, because keyset may change*/
+		log_nametypeclass(VERB_ALGO, "verify rrset cached", 
+			rrset->rk.dname, ntohs(rrset->rk.type), 
+			ntohs(rrset->rk.rrset_class));
+		return d->security;
+	}
+	/* check in the cache if verification has already been done */
+	rrset_check_sec_status(env->rrset_cache, rrset, *env->now);
+	if(d->security == sec_status_secure) {
+		log_nametypeclass(VERB_ALGO, "verify rrset from cache", 
+			rrset->rk.dname, ntohs(rrset->rk.type), 
+			ntohs(rrset->rk.rrset_class));
+		return d->security;
+	}
+	log_nametypeclass(VERB_ALGO, "verify rrset", rrset->rk.dname,
+		ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class));
+	sec = dnskeyset_verify_rrset(env, ve, rrset, keys, sigalg, reason);
+	verbose(VERB_ALGO, "verify result: %s", sec_status_to_string(sec));
+	regional_free_all(env->scratch);
+
+	/* update rrset security status 
+	 * only improves security status 
+	 * and bogus is set only once, even if we rechecked the status */
+	if(sec > d->security) {
+		d->security = sec;
+		if(sec == sec_status_secure)
+			d->trust = rrset_trust_validated;
+		else if(sec == sec_status_bogus) {
+			size_t i;
+			/* update ttl for rrset to fixed value. */
+			d->ttl = ve->bogus_ttl;
+			for(i=0; i<d->count+d->rrsig_count; i++)
+				d->rr_ttl[i] = ve->bogus_ttl;
+			/* leave RR specific TTL: not used for determine
+			 * if RRset timed out and clients see proper value. */
+			lock_basic_lock(&ve->bogus_lock);
+			ve->num_rrset_bogus++;
+			lock_basic_unlock(&ve->bogus_lock);
+		}
+		/* if status updated - store in cache for reuse */
+		rrset_update_sec_status(env->rrset_cache, rrset, *env->now);
+	}
+
+	return sec;
+}
+
+enum sec_status 
+val_verify_rrset_entry(struct module_env* env, struct val_env* ve,
+        struct ub_packed_rrset_key* rrset, struct key_entry_key* kkey,
+	char** reason)
+{
+	/* temporary dnskey rrset-key */
+	struct ub_packed_rrset_key dnskey;
+	struct key_entry_data* kd = (struct key_entry_data*)kkey->entry.data;
+	enum sec_status sec;
+	dnskey.rk.type = htons(kd->rrset_type);
+	dnskey.rk.rrset_class = htons(kkey->key_class);
+	dnskey.rk.flags = 0;
+	dnskey.rk.dname = kkey->name;
+	dnskey.rk.dname_len = kkey->namelen;
+	dnskey.entry.key = &dnskey;
+	dnskey.entry.data = kd->rrset_data;
+	sec = val_verify_rrset(env, ve, rrset, &dnskey, kd->algo, reason);
+	return sec;
+}
+
+/** verify that a DS RR hashes to a key and that key signs the set */
+static enum sec_status
+verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve, 
+	struct ub_packed_rrset_key* dnskey_rrset, 
+        struct ub_packed_rrset_key* ds_rrset, size_t ds_idx, char** reason)
+{
+	enum sec_status sec = sec_status_bogus;
+	size_t i, num, numchecked = 0, numhashok = 0;
+	num = rrset_get_count(dnskey_rrset);
+	for(i=0; i<num; i++) {
+		/* Skip DNSKEYs that don't match the basic criteria. */
+		if(ds_get_key_algo(ds_rrset, ds_idx) 
+		   != dnskey_get_algo(dnskey_rrset, i)
+		   || dnskey_calc_keytag(dnskey_rrset, i)
+		   != ds_get_keytag(ds_rrset, ds_idx)) {
+			continue;
+		}
+		numchecked++;
+		verbose(VERB_ALGO, "attempt DS match algo %d keytag %d",
+			ds_get_key_algo(ds_rrset, ds_idx),
+			ds_get_keytag(ds_rrset, ds_idx));
+
+		/* Convert the candidate DNSKEY into a hash using the 
+		 * same DS hash algorithm. */
+		if(!ds_digest_match_dnskey(env, dnskey_rrset, i, ds_rrset, 
+			ds_idx)) {
+			verbose(VERB_ALGO, "DS match attempt failed");
+			continue;
+		}
+		numhashok++;
+		verbose(VERB_ALGO, "DS match digest ok, trying signature");
+
+		/* Otherwise, we have a match! Make sure that the DNSKEY 
+		 * verifies *with this key*  */
+		sec = dnskey_verify_rrset(env, ve, dnskey_rrset, 
+			dnskey_rrset, i, reason);
+		if(sec == sec_status_secure) {
+			return sec;
+		}
+		/* If it didn't validate with the DNSKEY, try the next one! */
+	}
+	if(numchecked == 0)
+		algo_needs_reason(env, ds_get_key_algo(ds_rrset, ds_idx),
+			reason, "no keys have a DS");
+	else if(numhashok == 0)
+		*reason = "DS hash mismatches key";
+	else if(!*reason)
+		*reason = "keyset not secured by DNSKEY that matches DS";
+	return sec_status_bogus;
+}
+
+int val_favorite_ds_algo(struct ub_packed_rrset_key* ds_rrset)
+{
+	size_t i, num = rrset_get_count(ds_rrset);
+	int d, digest_algo = 0; /* DS digest algo 0 is not used. */
+	/* find favorite algo, for now, highest number supported */
+	for(i=0; i<num; i++) {
+		if(!ds_digest_algo_is_supported(ds_rrset, i) ||
+			!ds_key_algo_is_supported(ds_rrset, i)) {
+			continue;
+		}
+		d = ds_get_digest_algo(ds_rrset, i);
+		if(d > digest_algo)
+			digest_algo = d;
+	}
+	return digest_algo;
+}
+
+enum sec_status 
+val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve,
+	struct ub_packed_rrset_key* dnskey_rrset,
+	struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason)
+{
+	/* as long as this is false, we can consider this DS rrset to be
+	 * equivalent to no DS rrset. */
+	int has_useful_ds = 0, digest_algo, alg;
+	struct algo_needs needs;
+	size_t i, num;
+	enum sec_status sec;
+
+	if(dnskey_rrset->rk.dname_len != ds_rrset->rk.dname_len ||
+		query_dname_compare(dnskey_rrset->rk.dname, ds_rrset->rk.dname)
+		!= 0) {
+		verbose(VERB_QUERY, "DNSKEY RRset did not match DS RRset "
+			"by name");
+		*reason = "DNSKEY RRset did not match DS RRset by name";
+		return sec_status_bogus;
+	}
+
+	digest_algo = val_favorite_ds_algo(ds_rrset);
+	if(sigalg)
+		algo_needs_init_ds(&needs, ds_rrset, digest_algo, sigalg);
+	num = rrset_get_count(ds_rrset);
+	for(i=0; i<num; i++) {
+		/* Check to see if we can understand this DS. 
+		 * And check it is the strongest digest */
+		if(!ds_digest_algo_is_supported(ds_rrset, i) ||
+			!ds_key_algo_is_supported(ds_rrset, i) ||
+			ds_get_digest_algo(ds_rrset, i) != digest_algo) {
+			continue;
+		}
+
+		/* Once we see a single DS with a known digestID and 
+		 * algorithm, we cannot return INSECURE (with a 
+		 * "null" KeyEntry). */
+		has_useful_ds = true;
+
+		sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset, 
+			ds_rrset, i, reason);
+		if(sec == sec_status_secure) {
+			if(!sigalg || algo_needs_set_secure(&needs,
+				(uint8_t)ds_get_key_algo(ds_rrset, i))) {
+				verbose(VERB_ALGO, "DS matched DNSKEY.");
+				return sec_status_secure;
+			}
+		} else if(sigalg && sec == sec_status_bogus) {
+			algo_needs_set_bogus(&needs,
+				(uint8_t)ds_get_key_algo(ds_rrset, i));
+		}
+	}
+
+	/* None of the DS's worked out. */
+
+	/* If no DSs were understandable, then this is OK. */
+	if(!has_useful_ds) {
+		verbose(VERB_ALGO, "No usable DS records were found -- "
+			"treating as insecure.");
+		return sec_status_insecure;
+	}
+	/* If any were understandable, then it is bad. */
+	verbose(VERB_QUERY, "Failed to match any usable DS to a DNSKEY.");
+	if(sigalg && (alg=algo_needs_missing(&needs)) != 0) {
+		algo_needs_reason(env, alg, reason, "missing verification of "
+			"DNSKEY signature");
+	}
+	return sec_status_bogus;
+}
+
+struct key_entry_key* 
+val_verify_new_DNSKEYs(struct regional* region, struct module_env* env, 
+	struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, 
+	struct ub_packed_rrset_key* ds_rrset, int downprot, char** reason)
+{
+	uint8_t sigalg[ALGO_NEEDS_MAX+1];
+	enum sec_status sec = val_verify_DNSKEY_with_DS(env, ve, 
+		dnskey_rrset, ds_rrset, downprot?sigalg:NULL, reason);
+
+	if(sec == sec_status_secure) {
+		return key_entry_create_rrset(region, 
+			ds_rrset->rk.dname, ds_rrset->rk.dname_len,
+			ntohs(ds_rrset->rk.rrset_class), dnskey_rrset,
+			downprot?sigalg:NULL, *env->now);
+	} else if(sec == sec_status_insecure) {
+		return key_entry_create_null(region, ds_rrset->rk.dname,
+			ds_rrset->rk.dname_len, 
+			ntohs(ds_rrset->rk.rrset_class),
+			rrset_get_ttl(ds_rrset), *env->now);
+	}
+	return key_entry_create_bad(region, ds_rrset->rk.dname,
+		ds_rrset->rk.dname_len, ntohs(ds_rrset->rk.rrset_class),
+		BOGUS_KEY_TTL, *env->now);
+}
+
+enum sec_status 
+val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve,
+	struct ub_packed_rrset_key* dnskey_rrset,
+	struct ub_packed_rrset_key* ta_ds,
+	struct ub_packed_rrset_key* ta_dnskey, uint8_t* sigalg, char** reason)
+{
+	/* as long as this is false, we can consider this anchor to be
+	 * equivalent to no anchor. */
+	int has_useful_ta = 0, digest_algo = 0, alg;
+	struct algo_needs needs;
+	size_t i, num;
+	enum sec_status sec;
+
+	if(ta_ds && (dnskey_rrset->rk.dname_len != ta_ds->rk.dname_len ||
+		query_dname_compare(dnskey_rrset->rk.dname, ta_ds->rk.dname)
+		!= 0)) {
+		verbose(VERB_QUERY, "DNSKEY RRset did not match DS RRset "
+			"by name");
+		*reason = "DNSKEY RRset did not match DS RRset by name";
+		return sec_status_bogus;
+	}
+	if(ta_dnskey && (dnskey_rrset->rk.dname_len != ta_dnskey->rk.dname_len
+	     || query_dname_compare(dnskey_rrset->rk.dname, ta_dnskey->rk.dname)
+		!= 0)) {
+		verbose(VERB_QUERY, "DNSKEY RRset did not match anchor RRset "
+			"by name");
+		*reason = "DNSKEY RRset did not match anchor RRset by name";
+		return sec_status_bogus;
+	}
+
+	if(ta_ds)
+		digest_algo = val_favorite_ds_algo(ta_ds);
+	if(sigalg) {
+		if(ta_ds)
+			algo_needs_init_ds(&needs, ta_ds, digest_algo, sigalg);
+		else	memset(&needs, 0, sizeof(needs));
+		if(ta_dnskey)
+			algo_needs_init_dnskey_add(&needs, ta_dnskey, sigalg);
+	}
+	if(ta_ds) {
+	    num = rrset_get_count(ta_ds);
+	    for(i=0; i<num; i++) {
+		/* Check to see if we can understand this DS. 
+		 * And check it is the strongest digest */
+		if(!ds_digest_algo_is_supported(ta_ds, i) ||
+			!ds_key_algo_is_supported(ta_ds, i) ||
+			ds_get_digest_algo(ta_ds, i) != digest_algo)
+			continue;
+
+		/* Once we see a single DS with a known digestID and 
+		 * algorithm, we cannot return INSECURE (with a 
+		 * "null" KeyEntry). */
+		has_useful_ta = true;
+
+		sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset, 
+			ta_ds, i, reason);
+		if(sec == sec_status_secure) {
+			if(!sigalg || algo_needs_set_secure(&needs,
+				(uint8_t)ds_get_key_algo(ta_ds, i))) {
+				verbose(VERB_ALGO, "DS matched DNSKEY.");
+				return sec_status_secure;
+			}
+		} else if(sigalg && sec == sec_status_bogus) {
+			algo_needs_set_bogus(&needs,
+				(uint8_t)ds_get_key_algo(ta_ds, i));
+		}
+	    }
+	}
+
+	/* None of the DS's worked out: check the DNSKEYs. */
+	if(ta_dnskey) {
+	    num = rrset_get_count(ta_dnskey);
+	    for(i=0; i<num; i++) {
+		/* Check to see if we can understand this DNSKEY */
+		if(!dnskey_algo_is_supported(ta_dnskey, i))
+			continue;
+
+		/* we saw a useful TA */
+		has_useful_ta = true;
+
+		sec = dnskey_verify_rrset(env, ve, dnskey_rrset,
+			ta_dnskey, i, reason);
+		if(sec == sec_status_secure) {
+			if(!sigalg || algo_needs_set_secure(&needs,
+				(uint8_t)dnskey_get_algo(ta_dnskey, i))) {
+				verbose(VERB_ALGO, "anchor matched DNSKEY.");
+				return sec_status_secure;
+			}
+		} else if(sigalg && sec == sec_status_bogus) {
+			algo_needs_set_bogus(&needs,
+				(uint8_t)dnskey_get_algo(ta_dnskey, i));
+		}
+	    }
+	}
+
+	/* If no DSs were understandable, then this is OK. */
+	if(!has_useful_ta) {
+		verbose(VERB_ALGO, "No usable trust anchors were found -- "
+			"treating as insecure.");
+		return sec_status_insecure;
+	}
+	/* If any were understandable, then it is bad. */
+	verbose(VERB_QUERY, "Failed to match any usable anchor to a DNSKEY.");
+	if(sigalg && (alg=algo_needs_missing(&needs)) != 0) {
+		algo_needs_reason(env, alg, reason, "missing verification of "
+			"DNSKEY signature");
+	}
+	return sec_status_bogus;
+}
+
+struct key_entry_key* 
+val_verify_new_DNSKEYs_with_ta(struct regional* region, struct module_env* env,
+	struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, 
+	struct ub_packed_rrset_key* ta_ds_rrset,
+	struct ub_packed_rrset_key* ta_dnskey_rrset, int downprot,
+	char** reason)
+{
+	uint8_t sigalg[ALGO_NEEDS_MAX+1];
+	enum sec_status sec = val_verify_DNSKEY_with_TA(env, ve, 
+		dnskey_rrset, ta_ds_rrset, ta_dnskey_rrset,
+		downprot?sigalg:NULL, reason);
+
+	if(sec == sec_status_secure) {
+		return key_entry_create_rrset(region, 
+			dnskey_rrset->rk.dname, dnskey_rrset->rk.dname_len,
+			ntohs(dnskey_rrset->rk.rrset_class), dnskey_rrset,
+			downprot?sigalg:NULL, *env->now);
+	} else if(sec == sec_status_insecure) {
+		return key_entry_create_null(region, dnskey_rrset->rk.dname,
+			dnskey_rrset->rk.dname_len, 
+			ntohs(dnskey_rrset->rk.rrset_class),
+			rrset_get_ttl(dnskey_rrset), *env->now);
+	}
+	return key_entry_create_bad(region, dnskey_rrset->rk.dname,
+		dnskey_rrset->rk.dname_len, ntohs(dnskey_rrset->rk.rrset_class),
+		BOGUS_KEY_TTL, *env->now);
+}
+
+int 
+val_dsset_isusable(struct ub_packed_rrset_key* ds_rrset)
+{
+	size_t i;
+	for(i=0; i<rrset_get_count(ds_rrset); i++) {
+		if(ds_digest_algo_is_supported(ds_rrset, i) &&
+			ds_key_algo_is_supported(ds_rrset, i))
+			return 1;
+	}
+	return 0;
+}
+
+/** get label count for a signature */
+static uint8_t
+rrsig_get_labcount(struct packed_rrset_data* d, size_t sig)
+{
+	if(d->rr_len[sig] < 2+4)
+		return 0; /* bad sig length */
+	return d->rr_data[sig][2+3];
+}
+
+int 
+val_rrset_wildcard(struct ub_packed_rrset_key* rrset, uint8_t** wc)
+{
+	struct packed_rrset_data* d = (struct packed_rrset_data*)rrset->
+		entry.data;
+	uint8_t labcount;
+	int labdiff;
+	uint8_t* wn;
+	size_t i, wl;
+	if(d->rrsig_count == 0) {
+		return 1;
+	}
+	labcount = rrsig_get_labcount(d, d->count + 0);
+	/* check rest of signatures identical */
+	for(i=1; i<d->rrsig_count; i++) {
+		if(labcount != rrsig_get_labcount(d, d->count + i)) {
+			return 0;
+		}
+	}
+	/* OK the rrsigs check out */
+	/* if the RRSIG label count is shorter than the number of actual 
+	 * labels, then this rrset was synthesized from a wildcard.
+	 * Note that the RRSIG label count doesn't count the root label. */
+	wn = rrset->rk.dname;
+	wl = rrset->rk.dname_len;
+	/* skip a leading wildcard label in the dname (RFC4035 2.2) */
+	if(dname_is_wild(wn)) {
+		wn += 2;
+		wl -= 2;
+	}
+	labdiff = (dname_count_labels(wn) - 1) - (int)labcount;
+	if(labdiff > 0) {
+		*wc = wn;
+		dname_remove_labels(wc, &wl, labdiff);
+		return 1;
+	}
+	return 1;
+}
+
+int
+val_chase_cname(struct query_info* qchase, struct reply_info* rep,
+	size_t* cname_skip) {
+	size_t i;
+	/* skip any DNAMEs, go to the CNAME for next part */
+	for(i = *cname_skip; i < rep->an_numrrsets; i++) {
+		if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_CNAME &&
+			query_dname_compare(qchase->qname, rep->rrsets[i]->
+				rk.dname) == 0) {
+			qchase->qname = NULL;
+			get_cname_target(rep->rrsets[i], &qchase->qname,
+				&qchase->qname_len);
+			if(!qchase->qname)
+				return 0; /* bad CNAME rdata */
+			(*cname_skip) = i+1;
+			return 1;
+		}
+	}
+	return 0; /* CNAME classified but no matching CNAME ?! */
+}
+
+/** see if rrset has signer name as one of the rrsig signers */
+static int
+rrset_has_signer(struct ub_packed_rrset_key* rrset, uint8_t* name, size_t len)
+{
+	struct packed_rrset_data* d = (struct packed_rrset_data*)rrset->
+		entry.data;
+	size_t i;
+	for(i = d->count; i< d->count+d->rrsig_count; i++) {
+		if(d->rr_len[i] > 2+18+len) {
+			/* at least rdatalen + signature + signame (+1 sig)*/
+			if(query_dname_compare(name, d->rr_data[i]+2+18) == 0)
+			{
+				return 1;
+			}
+		}
+	}
+	return 0;
+}
+
+void 
+val_fill_reply(struct reply_info* chase, struct reply_info* orig, 
+	size_t skip, uint8_t* name, size_t len, uint8_t* signer)
+{
+	size_t i;
+	int seen_dname = 0;
+	chase->rrset_count = 0;
+	chase->an_numrrsets = 0;
+	chase->ns_numrrsets = 0;
+	chase->ar_numrrsets = 0;
+	/* ANSWER section */
+	for(i=skip; i<orig->an_numrrsets; i++) {
+		if(!signer) {
+			if(query_dname_compare(name, 
+				orig->rrsets[i]->rk.dname) == 0)
+				chase->rrsets[chase->an_numrrsets++] = 
+					orig->rrsets[i];
+		} else if(seen_dname && ntohs(orig->rrsets[i]->rk.type) == 
+			LDNS_RR_TYPE_CNAME) {
+			chase->rrsets[chase->an_numrrsets++] = orig->rrsets[i];
+			seen_dname = 0;
+		} else if(rrset_has_signer(orig->rrsets[i], name, len)) {
+			chase->rrsets[chase->an_numrrsets++] = orig->rrsets[i];
+			if(ntohs(orig->rrsets[i]->rk.type) == 
+				LDNS_RR_TYPE_DNAME) {
+					seen_dname = 1;
+			}
+		}
+	}	
+	/* AUTHORITY section */
+	for(i = (skip > orig->an_numrrsets)?skip:orig->an_numrrsets;
+		i<orig->an_numrrsets+orig->ns_numrrsets; 
+		i++) {
+		if(!signer) {
+			if(query_dname_compare(name, 
+				orig->rrsets[i]->rk.dname) == 0)
+				chase->rrsets[chase->an_numrrsets+
+				    chase->ns_numrrsets++] = orig->rrsets[i];
+		} else if(rrset_has_signer(orig->rrsets[i], name, len)) {
+			chase->rrsets[chase->an_numrrsets+
+				chase->ns_numrrsets++] = orig->rrsets[i];
+		}
+	}
+	/* ADDITIONAL section */
+	for(i= (skip>orig->an_numrrsets+orig->ns_numrrsets)?
+		skip:orig->an_numrrsets+orig->ns_numrrsets; 
+		i<orig->rrset_count; i++) {
+		if(!signer) {
+			if(query_dname_compare(name, 
+				orig->rrsets[i]->rk.dname) == 0)
+			    chase->rrsets[chase->an_numrrsets
+				+orig->ns_numrrsets+chase->ar_numrrsets++] 
+				= orig->rrsets[i];
+		} else if(rrset_has_signer(orig->rrsets[i], name, len)) {
+			chase->rrsets[chase->an_numrrsets+orig->ns_numrrsets+
+				chase->ar_numrrsets++] = orig->rrsets[i];
+		}
+	}
+	chase->rrset_count = chase->an_numrrsets + chase->ns_numrrsets + 
+		chase->ar_numrrsets;
+}
+
+void
+val_check_nonsecure(struct val_env* ve, struct reply_info* rep) 
+{
+	size_t i;
+	/* authority */
+	for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep->ns_numrrsets; i++) {
+		if(((struct packed_rrset_data*)rep->rrsets[i]->entry.data)
+			->security != sec_status_secure) {
+			/* because we want to return the authentic original
+			 * message when presented with CD-flagged queries,
+			 * we need to preserve AUTHORITY section data.
+			 * However, this rrset is not signed or signed
+			 * with the wrong keys. Validation has tried to
+			 * verify this rrset with the keysets of import.
+			 * But this rrset did not verify.
+			 * Therefore the message is bogus.
+			 */
+
+			/* check if authority consists of only an NS record
+			 * which is bad, and there is an answer section with
+			 * data.  In that case, delete NS and additional to 
+			 * be lenient and make a minimal response */
+			if(rep->an_numrrsets != 0 && rep->ns_numrrsets == 1 &&
+				ntohs(rep->rrsets[i]->rk.type) 
+				== LDNS_RR_TYPE_NS) {
+				verbose(VERB_ALGO, "truncate to minimal");
+				rep->ns_numrrsets = 0;
+				rep->ar_numrrsets = 0;
+				rep->rrset_count = rep->an_numrrsets;
+				return;
+			}
+
+			log_nametypeclass(VERB_QUERY, "message is bogus, "
+				"non secure rrset",
+				rep->rrsets[i]->rk.dname, 
+				ntohs(rep->rrsets[i]->rk.type),
+				ntohs(rep->rrsets[i]->rk.rrset_class));
+			rep->security = sec_status_bogus;
+			return;
+		}
+	}
+	/* additional */
+	if(!ve->clean_additional)
+		return;
+	for(i=rep->an_numrrsets+rep->ns_numrrsets; i<rep->rrset_count; i++) {
+		if(((struct packed_rrset_data*)rep->rrsets[i]->entry.data)
+			->security != sec_status_secure) {
+			/* This does not cause message invalidation. It was
+			 * simply unsigned data in the additional. The
+			 * RRSIG must have been truncated off the message.
+			 *
+			 * However, we do not want to return possible bogus
+			 * data to clients that rely on this service for
+			 * their authentication.
+			 */
+			/* remove this unneeded additional rrset */
+			memmove(rep->rrsets+i, rep->rrsets+i+1, 
+				sizeof(struct ub_packed_rrset_key*)*
+				(rep->rrset_count - i - 1));
+			rep->ar_numrrsets--;
+			rep->rrset_count--;
+			i--;
+		}
+	}
+}
+
+/** check no anchor and unlock */
+static int
+check_no_anchor(struct val_anchors* anchors, uint8_t* nm, size_t l, uint16_t c)
+{
+	struct trust_anchor* ta;
+	if((ta=anchors_lookup(anchors, nm, l, c))) {
+		lock_basic_unlock(&ta->lock);
+	}
+	return !ta;
+}
+
+void 
+val_mark_indeterminate(struct reply_info* rep, struct val_anchors* anchors, 
+	struct rrset_cache* r, struct module_env* env)
+{
+	size_t i;
+	struct packed_rrset_data* d;
+	for(i=0; i<rep->rrset_count; i++) {
+		d = (struct packed_rrset_data*)rep->rrsets[i]->entry.data;
+		if(d->security == sec_status_unchecked &&
+		   check_no_anchor(anchors, rep->rrsets[i]->rk.dname,
+			rep->rrsets[i]->rk.dname_len, 
+			ntohs(rep->rrsets[i]->rk.rrset_class))) 
+		{ 	
+			/* mark as indeterminate */
+			d->security = sec_status_indeterminate;
+			rrset_update_sec_status(r, rep->rrsets[i], *env->now);
+		}
+	}
+}
+
+void 
+val_mark_insecure(struct reply_info* rep, uint8_t* kname,
+	struct rrset_cache* r, struct module_env* env)
+{
+	size_t i;
+	struct packed_rrset_data* d;
+	for(i=0; i<rep->rrset_count; i++) {
+		d = (struct packed_rrset_data*)rep->rrsets[i]->entry.data;
+		if(d->security == sec_status_unchecked &&
+		   dname_subdomain_c(rep->rrsets[i]->rk.dname, kname)) {
+			/* mark as insecure */
+			d->security = sec_status_insecure;
+			rrset_update_sec_status(r, rep->rrsets[i], *env->now);
+		}
+	}
+}
+
+size_t 
+val_next_unchecked(struct reply_info* rep, size_t skip)
+{
+	size_t i;
+	struct packed_rrset_data* d;
+	for(i=skip+1; i<rep->rrset_count; i++) {
+		d = (struct packed_rrset_data*)rep->rrsets[i]->entry.data;
+		if(d->security == sec_status_unchecked) {
+			return i;
+		}
+	}
+	return rep->rrset_count;
+}
+
+const char*
+val_classification_to_string(enum val_classification subtype)
+{
+	switch(subtype) {
+		case VAL_CLASS_UNTYPED: 	return "untyped";
+		case VAL_CLASS_UNKNOWN: 	return "unknown";
+		case VAL_CLASS_POSITIVE: 	return "positive";
+		case VAL_CLASS_CNAME: 		return "cname";
+		case VAL_CLASS_NODATA: 		return "nodata";
+		case VAL_CLASS_NAMEERROR: 	return "nameerror";
+		case VAL_CLASS_CNAMENOANSWER: 	return "cnamenoanswer";
+		case VAL_CLASS_REFERRAL: 	return "referral";
+		case VAL_CLASS_ANY: 		return "qtype_any";
+		default:
+			return "bad_val_classification";
+	}
+}
+
+/** log a sock_list entry */
+static void
+sock_list_logentry(enum verbosity_value v, const char* s, struct sock_list* p)
+{
+	if(p->len)
+		log_addr(v, s, &p->addr, p->len);
+	else	verbose(v, "%s cache", s);
+}
+
+void val_blacklist(struct sock_list** blacklist, struct regional* region,
+	struct sock_list* origin, int cross)
+{
+	/* debug printout */
+	if(verbosity >= VERB_ALGO) {
+		struct sock_list* p;
+		for(p=*blacklist; p; p=p->next)
+			sock_list_logentry(VERB_ALGO, "blacklist", p);
+		if(!origin)
+			verbose(VERB_ALGO, "blacklist add: cache");
+		for(p=origin; p; p=p->next)
+			sock_list_logentry(VERB_ALGO, "blacklist add", p);
+	}
+	/* blacklist the IPs or the cache */
+	if(!origin) {
+		/* only add if nothing there. anything else also stops cache*/
+		if(!*blacklist)
+			sock_list_insert(blacklist, NULL, 0, region);
+	} else if(!cross)
+		sock_list_prepend(blacklist, origin);
+	else	sock_list_merge(blacklist, region, origin);
+}
+
+int val_has_signed_nsecs(struct reply_info* rep, char** reason)
+{
+	size_t i, num_nsec = 0, num_nsec3 = 0;
+	struct packed_rrset_data* d;
+	for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep->ns_numrrsets; i++) {
+		if(rep->rrsets[i]->rk.type == htons(LDNS_RR_TYPE_NSEC))
+			num_nsec++;
+		else if(rep->rrsets[i]->rk.type == htons(LDNS_RR_TYPE_NSEC3))
+			num_nsec3++;
+		else continue;
+		d = (struct packed_rrset_data*)rep->rrsets[i]->entry.data;
+		if(d && d->rrsig_count != 0) {
+			return 1;
+		}
+	}
+	if(num_nsec == 0 && num_nsec3 == 0)
+		*reason = "no DNSSEC records";
+	else if(num_nsec != 0)
+		*reason = "no signatures over NSECs";
+	else	*reason = "no signatures over NSEC3s";
+	return 0;
+}
+
+struct dns_msg* 
+val_find_DS(struct module_env* env, uint8_t* nm, size_t nmlen, uint16_t c, 
+	struct regional* region, uint8_t* topname)
+{
+	struct dns_msg* msg;
+	struct query_info qinfo;
+	struct ub_packed_rrset_key *rrset = rrset_cache_lookup(
+		env->rrset_cache, nm, nmlen, LDNS_RR_TYPE_DS, c, 0, 
+		*env->now, 0);
+	if(rrset) {
+		/* DS rrset exists. Return it to the validator immediately*/
+		struct ub_packed_rrset_key* copy = packed_rrset_copy_region(
+			rrset, region, *env->now);
+		lock_rw_unlock(&rrset->entry.lock);
+		if(!copy)
+			return NULL;
+		msg = dns_msg_create(nm, nmlen, LDNS_RR_TYPE_DS, c, region, 1);
+		if(!msg)
+			return NULL;
+		msg->rep->rrsets[0] = copy;
+		msg->rep->rrset_count++;
+		msg->rep->an_numrrsets++;
+		return msg;
+	}
+	/* lookup in rrset and negative cache for NSEC/NSEC3 */
+	qinfo.qname = nm;
+	qinfo.qname_len = nmlen;
+	qinfo.qtype = LDNS_RR_TYPE_DS;
+	qinfo.qclass = c;
+	/* do not add SOA to reply message, it is going to be used internal */
+	msg = val_neg_getmsg(env->neg_cache, &qinfo, region, env->rrset_cache,
+		env->scratch_buffer, *env->now, 0, topname);
+	return msg;
+}
diff --git a/3rdParty/Unbound/src/src/validator/val_utils.h b/3rdParty/Unbound/src/src/validator/val_utils.h
new file mode 100644
index 0000000..f0afc37
--- /dev/null
+++ b/3rdParty/Unbound/src/src/validator/val_utils.h
@@ -0,0 +1,403 @@
+/*
+ * validator/val_utils.h - validator utility functions.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains helper functions for the validator module.
+ */
+
+#ifndef VALIDATOR_VAL_UTILS_H
+#define VALIDATOR_VAL_UTILS_H
+#include "util/data/packed_rrset.h"
+struct query_info;
+struct reply_info;
+struct val_env;
+struct module_env;
+struct ub_packed_rrset_key;
+struct key_entry_key;
+struct regional;
+struct val_anchors;
+struct rrset_cache;
+struct sock_list;
+
+/**
+ * Response classifications for the validator. The different types of proofs.
+ */
+enum val_classification {
+	/** Not subtyped yet. */
+	VAL_CLASS_UNTYPED = 0,
+	/** Not a recognized subtype. */
+	VAL_CLASS_UNKNOWN,
+	/** A positive, direct, response */
+	VAL_CLASS_POSITIVE,
+	/** A positive response, with a CNAME/DNAME chain. */
+	VAL_CLASS_CNAME,
+	/** A NOERROR/NODATA response. */
+	VAL_CLASS_NODATA,
+	/** A NXDOMAIN response. */
+	VAL_CLASS_NAMEERROR,
+	/** A CNAME/DNAME chain, and the offset is at the end of it,
+	 * but there is no answer here, it can be NAMERROR or NODATA. */
+	VAL_CLASS_CNAMENOANSWER,
+	/** A referral, from cache with a nonRD query. */
+	VAL_CLASS_REFERRAL,
+	/** A response to a qtype=ANY query. */
+	VAL_CLASS_ANY
+};
+
+/**
+ * Given a response, classify ANSWER responses into a subtype.
+ * @param query_flags: query flags for the original query.
+ * @param origqinf: query info. The original query name.
+ * @param qinf: query info. The chased query name.
+ * @param rep: response. The original response.
+ * @param skip: offset into the original response answer section.
+ * @return A subtype, all values possible except UNTYPED .
+ * 	Once CNAME type is returned you can increase skip.
+ * 	Then, another CNAME type, CNAME_NOANSWER or POSITIVE are possible.
+ */
+enum val_classification val_classify_response(uint16_t query_flags,
+	struct query_info* origqinf, struct query_info* qinf, 
+	struct reply_info* rep, size_t skip);
+
+/**
+ * Given a response, determine the name of the "signer". This is primarily
+ * to determine if the response is, in fact, signed at all, and, if so, what
+ * is the name of the most pertinent keyset.
+ *
+ * @param subtype: the type from classify.
+ * @param qinf: query, the chased query name.
+ * @param rep: response to that, original response.
+ * @param cname_skip: how many answer rrsets have been skipped due to CNAME
+ * 	chains being chased around.
+ * @param signer_name:  signer name, if the response is signed 
+ * 	(even partially), or null if the response isn't signed.
+ * @param signer_len: length of signer_name of 0 if signer_name is NULL.
+ */
+void val_find_signer(enum val_classification subtype, 
+	struct query_info* qinf, struct reply_info* rep,
+	size_t cname_skip, uint8_t** signer_name, size_t* signer_len);
+
+/**
+ * Verify RRset with keys
+ * @param env: module environment (scratch buffer)
+ * @param ve: validator environment (verification settings)
+ * @param rrset: what to verify
+ * @param keys: dnskey rrset to verify with.
+ * @param sigalg: if nonNULL provide downgrade protection otherwise one
+ *   algorithm is enough.  Algo list is constructed in here.
+ * @param reason: reason of failure. Fixed string or alloced in scratch.
+ * @return security status of verification.
+ */
+enum sec_status val_verify_rrset(struct module_env* env, struct val_env* ve,
+	struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys,
+	uint8_t* sigalg, char** reason);
+
+/**
+ * Verify RRset with keys from a keyset.
+ * @param env: module environment (scratch buffer)
+ * @param ve: validator environment (verification settings)
+ * @param rrset: what to verify
+ * @param kkey: key_entry to verify with.
+ * @param reason: reason of failure. Fixed string or alloced in scratch.
+ * @return security status of verification.
+ */
+enum sec_status val_verify_rrset_entry(struct module_env* env, 
+	struct val_env* ve, struct ub_packed_rrset_key* rrset, 
+	struct key_entry_key* kkey, char** reason);
+
+/**
+ * Verify DNSKEYs with DS rrset. Like val_verify_new_DNSKEYs but
+ * returns a sec_status instead of a key_entry.
+ * @param env: module environment (scratch buffer)
+ * @param ve: validator environment (verification settings)
+ * @param dnskey_rrset: DNSKEY rrset to verify
+ * @param ds_rrset: DS rrset to verify with.
+ * @param sigalg: if nonNULL provide downgrade protection otherwise one
+ *   algorithm is enough.  The list of signalled algorithms is returned,
+ *   must have enough space for ALGO_NEEDS_MAX+1.
+ * @param reason: reason of failure. Fixed string or alloced in scratch.
+ * @return: sec_status_secure if a DS matches.
+ *     sec_status_insecure if end of trust (i.e., unknown algorithms).
+ *     sec_status_bogus if it fails.
+ */
+enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env, 
+	struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, 
+	struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason);
+
+/**
+ * Verify DNSKEYs with DS and DNSKEY rrset.  Like val_verify_DNSKEY_with_DS
+ * but for a trust anchor.
+ * @param env: module environment (scratch buffer)
+ * @param ve: validator environment (verification settings)
+ * @param dnskey_rrset: DNSKEY rrset to verify
+ * @param ta_ds: DS rrset to verify with.
+ * @param ta_dnskey: DNSKEY rrset to verify with.
+ * @param sigalg: if nonNULL provide downgrade protection otherwise one
+ *   algorithm is enough.  The list of signalled algorithms is returned,
+ *   must have enough space for ALGO_NEEDS_MAX+1.
+ * @param reason: reason of failure. Fixed string or alloced in scratch.
+ * @return: sec_status_secure if a DS matches.
+ *     sec_status_insecure if end of trust (i.e., unknown algorithms).
+ *     sec_status_bogus if it fails.
+ */
+enum sec_status val_verify_DNSKEY_with_TA(struct module_env* env, 
+	struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, 
+	struct ub_packed_rrset_key* ta_ds,
+	struct ub_packed_rrset_key* ta_dnskey, uint8_t* sigalg, char** reason);
+
+/**
+ * Verify new DNSKEYs with DS rrset. The DS contains hash values that should
+ * match the DNSKEY keys.
+ * match the DS to a DNSKEY and verify the DNSKEY rrset with that key.
+ *
+ * @param region: where to allocate key entry result.
+ * @param env: module environment (scratch buffer)
+ * @param ve: validator environment (verification settings)
+ * @param dnskey_rrset: DNSKEY rrset to verify
+ * @param ds_rrset: DS rrset to verify with.
+ * @param downprot: if true provide downgrade protection otherwise one
+ *   algorithm is enough.
+ * @param reason: reason of failure. Fixed string or alloced in scratch.
+ * @return a KeyEntry. This will either contain the now trusted
+ *         dnskey_rrset, a "null" key entry indicating that this DS
+ *         rrset/DNSKEY pair indicate an secure end to the island of trust
+ *         (i.e., unknown algorithms), or a "bad" KeyEntry if the dnskey
+ *         rrset fails to verify. Note that the "null" response should
+ *         generally only occur in a private algorithm scenario: normally
+ *         this sort of thing is checked before fetching the matching DNSKEY
+ *         rrset.
+ *         if downprot is set, a key entry with an algo list is made.
+ */
+struct key_entry_key* val_verify_new_DNSKEYs(struct regional* region, 
+	struct module_env* env, struct val_env* ve, 
+	struct ub_packed_rrset_key* dnskey_rrset, 
+	struct ub_packed_rrset_key* ds_rrset, int downprot, char** reason);
+
+
+/**
+ * Verify rrset with trust anchor: DS and DNSKEY rrset.
+ *
+ * @param region: where to allocate key entry result.
+ * @param env: module environment (scratch buffer)
+ * @param ve: validator environment (verification settings)
+ * @param dnskey_rrset: DNSKEY rrset to verify
+ * @param ta_ds_rrset: DS rrset to verify with.
+ * @param ta_dnskey_rrset: the DNSKEY rrset to verify with.
+ * @param downprot: if true provide downgrade protection otherwise one
+ *   algorithm is enough.
+ * @param reason: reason of failure. Fixed string or alloced in scratch.
+ * @return a KeyEntry. This will either contain the now trusted
+ *         dnskey_rrset, a "null" key entry indicating that this DS
+ *         rrset/DNSKEY pair indicate an secure end to the island of trust
+ *         (i.e., unknown algorithms), or a "bad" KeyEntry if the dnskey
+ *         rrset fails to verify. Note that the "null" response should
+ *         generally only occur in a private algorithm scenario: normally
+ *         this sort of thing is checked before fetching the matching DNSKEY
+ *         rrset.
+ *         if downprot is set, a key entry with an algo list is made.
+ */
+struct key_entry_key* val_verify_new_DNSKEYs_with_ta(struct regional* region, 
+	struct module_env* env, struct val_env* ve, 
+	struct ub_packed_rrset_key* dnskey_rrset, 
+	struct ub_packed_rrset_key* ta_ds_rrset, 
+	struct ub_packed_rrset_key* ta_dnskey_rrset,
+	int downprot, char** reason);
+
+/**
+ * Determine if DS rrset is usable for validator or not.
+ * Returns true if the algorithms for key and DShash are supported,
+ * for at least one RR.
+ *
+ * @param ds_rrset: the newly received DS rrset.
+ * @return true or false if not usable.
+ */
+int val_dsset_isusable(struct ub_packed_rrset_key* ds_rrset);
+
+/**
+ * Determine by looking at a signed RRset whether or not the RRset name was
+ * the result of a wildcard expansion. If so, return the name of the
+ * generating wildcard.
+ * 
+ * @param rrset The rrset to chedck.
+ * @param wc: the wildcard name, if the rrset was synthesized from a wildcard.
+ *         unchanged if not.  The wildcard name, without "*." in front, is 
+ *         returned. This is a pointer into the rrset owner name.
+ * @return false if the signatures are inconsistent in indicating the 
+ * 	wildcard status; possible spoofing of wildcard response for other
+ * 	responses is being tried. We lost the status which rrsig was verified
+ * 	after the verification routine finished, so we simply check if
+ * 	the signatures are consistent; inserting a fake signature is a denial
+ * 	of service; but in that you could also have removed the real 
+ * 	signature anyway.
+ */
+int val_rrset_wildcard(struct ub_packed_rrset_key* rrset, uint8_t** wc);
+
+/**
+ * Chase the cname to the next query name.
+ * @param qchase: the current query name, updated to next target.
+ * @param rep: original message reply to look at CNAMEs.
+ * @param cname_skip: the skip into the answer section. Updated to skip
+ * 	DNAME and CNAME to the next part of the answer.
+ * @return false on error (bad rdata).
+ */
+int val_chase_cname(struct query_info* qchase, struct reply_info* rep,
+	size_t* cname_skip);
+
+/**
+ * Fill up the chased reply with the content from the original reply;
+ * as pointers to those rrsets. Select the part after the cname_skip into
+ * the answer section, NS and AR sections that are signed with same signer.
+ *
+ * @param chase: chased reply, filled up.
+ * @param orig: original reply.
+ * @param cname_skip: which part of the answer section to skip.
+ * 	The skipped part contains CNAME(and DNAME)s that have been chased.
+ * @param name: the signer name to look for.
+ * @param len: length of name.
+ * @param signer: signer name or NULL if an unsigned RRset is considered.
+ *	If NULL, rrsets with the lookup name are copied over.
+ */
+void val_fill_reply(struct reply_info* chase, struct reply_info* orig, 
+	size_t cname_skip, uint8_t* name, size_t len, uint8_t* signer);
+
+/**
+ * Remove all unsigned or non-secure status rrsets from NS and AR sections.
+ * So that unsigned data does not get let through to clients, when we have
+ * found the data to be secure.
+ *
+ * @param ve: validator environment with cleaning options.
+ * @param rep: reply to dump all nonsecure stuff out of.
+ */
+void val_check_nonsecure(struct val_env* ve, struct reply_info* rep);
+
+/**
+ * Mark all unchecked rrset entries not below a trust anchor as indeterminate.
+ * Only security==unchecked rrsets are updated.
+ * @param rep: the reply with rrsets.
+ * @param anchors: the trust anchors.
+ * @param r: rrset cache to store updated security status into.
+ * @param env: module environment
+ */
+void val_mark_indeterminate(struct reply_info* rep, 
+	struct val_anchors* anchors, struct rrset_cache* r, 
+	struct module_env* env);
+
+/**
+ * Mark all unchecked rrset entries below a NULL key entry as insecure.
+ * Only security==unchecked rrsets are updated.
+ * @param rep: the reply with rrsets.
+ * @param kname: end of secure space name.
+ * @param r: rrset cache to store updated security status into.
+ * @param env: module environment
+ */
+void val_mark_insecure(struct reply_info* rep, uint8_t* kname,
+	struct rrset_cache* r, struct module_env* env);
+
+/**
+ * Find next unchecked rrset position, return it for skip.
+ * @param rep: the original reply to look into.
+ * @param skip: the skip now.
+ * @return new skip, which may be at the rep->rrset_count position to signal
+ * 	there are no unchecked items.
+ */
+size_t val_next_unchecked(struct reply_info* rep, size_t skip);
+
+/**
+ * Find the signer name for an RRset.
+ * @param rrset: the rrset.
+ * @param sname: signer name is returned or NULL if not signed.
+ * @param slen: length of sname (or 0).
+ */
+void val_find_rrset_signer(struct ub_packed_rrset_key* rrset, uint8_t** sname,
+	size_t* slen);
+
+/**
+ * Get string to denote the classification result.
+ * @param subtype: from classification function.
+ * @return static string to describe the classification.
+ */
+const char* val_classification_to_string(enum val_classification subtype);
+
+/**
+ * Add existing list to blacklist.
+ * @param blacklist: the blacklist with result
+ * @param region: the region where blacklist is allocated.
+ *	Allocation failures are logged.
+ * @param origin: origin list to add, if NULL, a cache-entry is added to
+ *   the blacklist to stop cache from being used.
+ * @param cross: if true this is a cross-qstate copy, and the 'origin'
+ *   list is not allocated in the same region as the blacklist.
+ */
+void val_blacklist(struct sock_list** blacklist, struct regional* region,
+	struct sock_list* origin, int cross);
+
+/**
+ * check if has dnssec info, and if it has signed nsecs. gives error reason.
+ * @param rep: reply to check.
+ * @param reason: returned on fail.
+ * @return false if message has no signed nsecs.  Can not prove negatives.
+ */
+int val_has_signed_nsecs(struct reply_info* rep, char** reason);
+
+/**
+ * Return algo number for favorite (best) algorithm that we support in DS.
+ * @param ds_rrset: the DSes in this rrset are inspected and best algo chosen.
+ * @return algo number or 0 if none supported. 0 is unused as algo number.
+ */
+int val_favorite_ds_algo(struct ub_packed_rrset_key* ds_rrset);
+
+/**
+ * Find DS denial message in cache.  Saves new qstate allocation and allows
+ * the validator to use partial content which is not enough to construct a
+ * message for network (or user) consumption.  Without SOA for example,
+ * which is a common occurence in the unbound code since the referrals contain
+ * NSEC/NSEC3 rrs without the SOA element, thus do not allow synthesis of a
+ * full negative reply, but do allow synthesis of sufficient proof.
+ * @param env: query env with caches and time.
+ * @param nm: name of DS record sought.
+ * @param nmlen: length of name.
+ * @param c: class of DS RR.
+ * @param region: where to allocate result.
+ * @param topname: name of the key that is currently in use, that will get
+ *	used to validate the result, and thus no higher entries from the
+ *	negative cache need to be examined.
+ * @return a dns_msg on success. NULL on failure.
+ */
+struct dns_msg* val_find_DS(struct module_env* env, uint8_t* nm, size_t nmlen,
+	uint16_t c, struct regional* region, uint8_t* topname);
+
+#endif /* VALIDATOR_VAL_UTILS_H */
diff --git a/3rdParty/Unbound/src/src/validator/validator.c b/3rdParty/Unbound/src/src/validator/validator.c
new file mode 100644
index 0000000..c05a8cf
--- /dev/null
+++ b/3rdParty/Unbound/src/src/validator/validator.c
@@ -0,0 +1,2957 @@
+/*
+ * validator/validator.c - secure validator DNS query response module
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains a module that performs validation of DNS queries.
+ * According to RFC 4034.
+ */
+#include "config.h"
+#include <ldns/ldns.h>
+#include "validator/validator.h"
+#include "validator/val_anchor.h"
+#include "validator/val_kcache.h"
+#include "validator/val_kentry.h"
+#include "validator/val_utils.h"
+#include "validator/val_nsec.h"
+#include "validator/val_nsec3.h"
+#include "validator/val_neg.h"
+#include "validator/val_sigcrypt.h"
+#include "validator/autotrust.h"
+#include "services/cache/dns.h"
+#include "util/data/dname.h"
+#include "util/module.h"
+#include "util/log.h"
+#include "util/net_help.h"
+#include "util/regional.h"
+#include "util/config_file.h"
+#include "util/fptr_wlist.h"
+
+/* forward decl for cache response and normal super inform calls of a DS */
+static void process_ds_response(struct module_qstate* qstate, 
+	struct val_qstate* vq, int id, int rcode, struct dns_msg* msg, 
+	struct query_info* qinfo, struct sock_list* origin);
+
+/** fill up nsec3 key iterations config entry */
+static int
+fill_nsec3_iter(struct val_env* ve, char* s, int c)
+{
+	char* e;
+	int i;
+	free(ve->nsec3_keysize);
+	free(ve->nsec3_maxiter);
+	ve->nsec3_keysize = (size_t*)calloc(sizeof(size_t), (size_t)c);
+	ve->nsec3_maxiter = (size_t*)calloc(sizeof(size_t), (size_t)c);
+	if(!ve->nsec3_keysize || !ve->nsec3_maxiter) {
+		log_err("out of memory");
+		return 0;
+	}
+	for(i=0; i<c; i++) {
+		ve->nsec3_keysize[i] = (size_t)strtol(s, &e, 10);
+		if(s == e) {
+			log_err("cannot parse: %s", s);
+			return 0;
+		}
+		s = e;
+		ve->nsec3_maxiter[i] = (size_t)strtol(s, &e, 10);
+		if(s == e) {
+			log_err("cannot parse: %s", s);
+			return 0;
+		}
+		s = e;
+		if(i>0 && ve->nsec3_keysize[i-1] >= ve->nsec3_keysize[i]) {
+			log_err("nsec3 key iterations not ascending: %d %d",
+				(int)ve->nsec3_keysize[i-1], 
+				(int)ve->nsec3_keysize[i]);
+			return 0;
+		}
+		verbose(VERB_ALGO, "validator nsec3cfg keysz %d mxiter %d",
+			(int)ve->nsec3_keysize[i], (int)ve->nsec3_maxiter[i]);
+	}
+	return 1;
+}
+
+/** apply config settings to validator */
+static int
+val_apply_cfg(struct module_env* env, struct val_env* val_env, 
+	struct config_file* cfg)
+{
+	int c;
+	val_env->bogus_ttl = (uint32_t)cfg->bogus_ttl;
+	val_env->clean_additional = cfg->val_clean_additional;
+	val_env->permissive_mode = cfg->val_permissive_mode;
+	if(!env->anchors)
+		env->anchors = anchors_create();
+	if(!env->anchors) {
+		log_err("out of memory");
+		return 0;
+	}
+	if(!val_env->kcache)
+		val_env->kcache = key_cache_create(cfg);
+	if(!val_env->kcache) {
+		log_err("out of memory");
+		return 0;
+	}
+	env->key_cache = val_env->kcache;
+	if(!anchors_apply_cfg(env->anchors, cfg)) {
+		log_err("validator: error in trustanchors config");
+		return 0;
+	}
+	val_env->date_override = cfg->val_date_override;
+	val_env->skew_min = cfg->val_sig_skew_min;
+	val_env->skew_max = cfg->val_sig_skew_max;
+	c = cfg_count_numbers(cfg->val_nsec3_key_iterations);
+	if(c < 1 || (c&1)) {
+		log_err("validator: unparseable or odd nsec3 key "
+			"iterations: %s", cfg->val_nsec3_key_iterations);
+		return 0;
+	}
+	val_env->nsec3_keyiter_count = c/2;
+	if(!fill_nsec3_iter(val_env, cfg->val_nsec3_key_iterations, c/2)) {
+		log_err("validator: cannot apply nsec3 key iterations");
+		return 0;
+	}
+	if(!val_env->neg_cache)
+		val_env->neg_cache = val_neg_create(cfg,
+			val_env->nsec3_maxiter[val_env->nsec3_keyiter_count-1]);
+	if(!val_env->neg_cache) {
+		log_err("out of memory");
+		return 0;
+	}
+	env->neg_cache = val_env->neg_cache;
+	return 1;
+}
+
+int
+val_init(struct module_env* env, int id)
+{
+	struct val_env* val_env = (struct val_env*)calloc(1,
+		sizeof(struct val_env));
+	if(!val_env) {
+		log_err("malloc failure");
+		return 0;
+	}
+	env->modinfo[id] = (void*)val_env;
+	env->need_to_validate = 1;
+	val_env->permissive_mode = 0;
+	lock_basic_init(&val_env->bogus_lock);
+	lock_protect(&val_env->bogus_lock, &val_env->num_rrset_bogus,
+		sizeof(val_env->num_rrset_bogus));
+	if(!val_apply_cfg(env, val_env, env->cfg)) {
+		log_err("validator: could not apply configuration settings.");
+		return 0;
+	}
+	return 1;
+}
+
+void
+val_deinit(struct module_env* env, int id)
+{
+	struct val_env* val_env;
+	if(!env || !env->modinfo[id])
+		return;
+	val_env = (struct val_env*)env->modinfo[id];
+	lock_basic_destroy(&val_env->bogus_lock);
+	anchors_delete(env->anchors);
+	env->anchors = NULL;
+	key_cache_delete(val_env->kcache);
+	neg_cache_delete(val_env->neg_cache);
+	free(val_env->nsec3_keysize);
+	free(val_env->nsec3_maxiter);
+	free(val_env);
+	env->modinfo[id] = NULL;
+}
+
+/** fill in message structure */
+static struct val_qstate*
+val_new_getmsg(struct module_qstate* qstate, struct val_qstate* vq)
+{
+	if(!qstate->return_msg || qstate->return_rcode != LDNS_RCODE_NOERROR) {
+		/* create a message to verify */
+		verbose(VERB_ALGO, "constructing reply for validation");
+		vq->orig_msg = (struct dns_msg*)regional_alloc(qstate->region,
+			sizeof(struct dns_msg));
+		if(!vq->orig_msg)
+			return NULL;
+		vq->orig_msg->qinfo = qstate->qinfo;
+		vq->orig_msg->rep = (struct reply_info*)regional_alloc(
+			qstate->region, sizeof(struct reply_info));
+		if(!vq->orig_msg->rep)
+			return NULL;
+		memset(vq->orig_msg->rep, 0, sizeof(struct reply_info));
+		vq->orig_msg->rep->flags = (uint16_t)(qstate->return_rcode&0xf)
+			|BIT_QR|BIT_RA|(qstate->query_flags|(BIT_CD|BIT_RD));
+		vq->orig_msg->rep->qdcount = 1;
+	} else {
+		vq->orig_msg = qstate->return_msg;
+	}
+	vq->qchase = qstate->qinfo;
+	/* chase reply will be an edited (sub)set of the orig msg rrset ptrs */
+	vq->chase_reply = regional_alloc_init(qstate->region, 
+		vq->orig_msg->rep, 
+		sizeof(struct reply_info) - sizeof(struct rrset_ref));
+	if(!vq->chase_reply)
+		return NULL;
+	vq->chase_reply->rrsets = regional_alloc_init(qstate->region,
+		vq->orig_msg->rep->rrsets, sizeof(struct ub_packed_rrset_key*)
+			* vq->orig_msg->rep->rrset_count);
+	if(!vq->chase_reply->rrsets)
+		return NULL;
+	vq->rrset_skip = 0;
+	return vq;
+}
+
+/** allocate new validator query state */
+static struct val_qstate*
+val_new(struct module_qstate* qstate, int id)
+{
+	struct val_qstate* vq = (struct val_qstate*)regional_alloc(
+		qstate->region, sizeof(*vq));
+	log_assert(!qstate->minfo[id]);
+	if(!vq)
+		return NULL;
+	memset(vq, 0, sizeof(*vq));
+	qstate->minfo[id] = vq;
+	vq->state = VAL_INIT_STATE;
+	return val_new_getmsg(qstate, vq);
+}
+
+/**
+ * Exit validation with an error status
+ * 
+ * @param qstate: query state
+ * @param id: validator id.
+ * @return false, for use by caller to return to stop processing.
+ */
+static int
+val_error(struct module_qstate* qstate, int id)
+{
+	qstate->ext_state[id] = module_error;
+	qstate->return_rcode = LDNS_RCODE_SERVFAIL;
+	return 0;
+}
+
+/** 
+ * Check to see if a given response needs to go through the validation
+ * process. Typical reasons for this routine to return false are: CD bit was
+ * on in the original request, or the response is a kind of message that 
+ * is unvalidatable (i.e., SERVFAIL, REFUSED, etc.)
+ *
+ * @param qstate: query state.
+ * @param ret_rc: rcode for this message (if noerror - examine ret_msg).
+ * @param ret_msg: return msg, can be NULL; look at rcode instead.
+ * @return true if the response could use validation (although this does not
+ *         mean we can actually validate this response).
+ */
+static int
+needs_validation(struct module_qstate* qstate, int ret_rc, 
+	struct dns_msg* ret_msg)
+{
+	int rcode;
+
+	/* If the CD bit is on in the original request, then we don't bother to
+	 * validate anything.*/
+	if(qstate->query_flags & BIT_CD) {
+		verbose(VERB_ALGO, "not validating response due to CD bit");
+		return 0;
+	}
+
+	if(ret_rc != LDNS_RCODE_NOERROR || !ret_msg)
+		rcode = ret_rc;
+	else 	rcode = (int)FLAGS_GET_RCODE(ret_msg->rep->flags);
+
+	if(rcode != LDNS_RCODE_NOERROR && rcode != LDNS_RCODE_NXDOMAIN) {
+		verbose(VERB_ALGO, "cannot validate non-answer, rcode %s",
+			ldns_lookup_by_id(ldns_rcodes, rcode)?
+			ldns_lookup_by_id(ldns_rcodes, rcode)->name:"??");
+		return 0;
+	}
+
+	/* cannot validate positive RRSIG response. (negatives can) */
+	if(qstate->qinfo.qtype == LDNS_RR_TYPE_RRSIG &&
+		rcode == LDNS_RCODE_NOERROR && ret_msg &&
+		ret_msg->rep->an_numrrsets > 0) {
+		verbose(VERB_ALGO, "cannot validate RRSIG, no sigs on sigs.");
+		return 0;
+	}
+	return 1;
+}
+
+/**
+ * Check to see if the response has already been validated.
+ * @param ret_msg: return msg, can be NULL
+ * @return true if the response has already been validated
+ */
+static int
+already_validated(struct dns_msg* ret_msg)
+{
+	/* validate unchecked, and re-validate bogus messages */
+	if (ret_msg && ret_msg->rep->security > sec_status_bogus)
+	{
+		verbose(VERB_ALGO, "response has already been validated: %s",
+			sec_status_to_string(ret_msg->rep->security));
+		return 1;
+	}
+	return 0;
+}
+
+/**
+ * Generate a request for DNS data.
+ *
+ * @param qstate: query state that is the parent.
+ * @param id: module id.
+ * @param name: what name to query for.
+ * @param namelen: length of name.
+ * @param qtype: query type.
+ * @param qclass: query class.
+ * @param flags: additional flags, such as the CD bit (BIT_CD), or 0.
+ * @return false on alloc failure.
+ */
+static int
+generate_request(struct module_qstate* qstate, int id, uint8_t* name, 
+	size_t namelen, uint16_t qtype, uint16_t qclass, uint16_t flags)
+{
+	struct val_qstate* vq = (struct val_qstate*)qstate->minfo[id];
+	struct module_qstate* newq;
+	struct query_info ask;
+	ask.qname = name;
+	ask.qname_len = namelen;
+	ask.qtype = qtype;
+	ask.qclass = qclass;
+	log_query_info(VERB_ALGO, "generate request", &ask);
+	fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
+	if(!(*qstate->env->attach_sub)(qstate, &ask, 
+		(uint16_t)(BIT_RD|flags), 0, &newq)){
+		log_err("Could not generate request: out of memory");
+		return 0;
+	}
+	/* newq; validator does not need state created for that
+	 * query, and its a 'normal' for iterator as well */
+	if(newq) {
+		/* add our blacklist to the query blacklist */
+		sock_list_merge(&newq->blacklist, newq->region,
+			vq->chain_blacklist);
+	}
+	qstate->ext_state[id] = module_wait_subquery;
+	return 1;
+}
+
+/**
+ * Prime trust anchor for use.
+ * Generate and dispatch a priming query for the given trust anchor.
+ * The trust anchor can be DNSKEY or DS and does not have to be signed.
+ *
+ * @param qstate: query state.
+ * @param vq: validator query state.
+ * @param id: module id.
+ * @param toprime: what to prime.
+ * @return false on a processing error.
+ */
+static int
+prime_trust_anchor(struct module_qstate* qstate, struct val_qstate* vq,
+	int id, struct trust_anchor* toprime)
+{
+	int ret = generate_request(qstate, id, toprime->name, toprime->namelen,
+		LDNS_RR_TYPE_DNSKEY, toprime->dclass, BIT_CD);
+	if(!ret) {
+		log_err("Could not prime trust anchor: out of memory");
+		return 0;
+	}
+	/* ignore newq; validator does not need state created for that
+	 * query, and its a 'normal' for iterator as well */
+	vq->wait_prime_ta = 1; /* to elicit PRIME_RESP_STATE processing 
+		from the validator inform_super() routine */
+	/* store trust anchor name for later lookup when prime returns */
+	vq->trust_anchor_name = regional_alloc_init(qstate->region,
+		toprime->name, toprime->namelen);
+	vq->trust_anchor_len = toprime->namelen;
+	vq->trust_anchor_labs = toprime->namelabs;
+	if(!vq->trust_anchor_name) {
+		log_err("Could not prime trust anchor: out of memory");
+		return 0;
+	}
+	return 1;
+}
+
+/**
+ * Validate if the ANSWER and AUTHORITY sections contain valid rrsets.
+ * They must be validly signed with the given key.
+ * Tries to validate ADDITIONAL rrsets as well, but only to check them.
+ * Allows unsigned CNAME after a DNAME that expands the DNAME.
+ * 
+ * Note that by the time this method is called, the process of finding the
+ * trusted DNSKEY rrset that signs this response must already have been
+ * completed.
+ * 
+ * @param qstate: query state.
+ * @param env: module env for verify.
+ * @param ve: validator env for verify.
+ * @param qchase: query that was made.
+ * @param chase_reply: answer to validate.
+ * @param key_entry: the key entry, which is trusted, and which matches
+ * 	the signer of the answer. The key entry isgood().
+ * @return false if any of the rrsets in the an or ns sections of the message 
+ * 	fail to verify. The message is then set to bogus.
+ */
+static int
+validate_msg_signatures(struct module_qstate* qstate, struct module_env* env,
+	struct val_env* ve, struct query_info* qchase,
+	struct reply_info* chase_reply, struct key_entry_key* key_entry)
+{
+	uint8_t* sname;
+	size_t i, slen;
+	struct ub_packed_rrset_key* s;
+	enum sec_status sec;
+	int dname_seen = 0;
+	char* reason = NULL;
+
+	/* validate the ANSWER section */
+	for(i=0; i<chase_reply->an_numrrsets; i++) {
+		s = chase_reply->rrsets[i];
+		/* Skip the CNAME following a (validated) DNAME.
+		 * Because of the normalization routines in the iterator, 
+		 * there will always be an unsigned CNAME following a DNAME 
+		 * (unless qtype=DNAME). */
+		if(dname_seen && ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME) {
+			dname_seen = 0;
+			/* CNAME was synthesized by our own iterator */
+			/* since the DNAME verified, mark the CNAME as secure */
+			((struct packed_rrset_data*)s->entry.data)->security =
+				sec_status_secure;
+			((struct packed_rrset_data*)s->entry.data)->trust =
+				rrset_trust_validated;
+			continue;
+		}
+
+		/* Verify the answer rrset */
+		sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason);
+		/* If the (answer) rrset failed to validate, then this 
+		 * message is BAD. */
+		if(sec != sec_status_secure) {
+			log_nametypeclass(VERB_QUERY, "validator: response "
+				"has failed ANSWER rrset:", s->rk.dname,
+				ntohs(s->rk.type), ntohs(s->rk.rrset_class));
+			errinf(qstate, reason);
+			if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME)
+				errinf(qstate, "for CNAME");
+			else if(ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME)
+				errinf(qstate, "for DNAME");
+			errinf_origin(qstate, qstate->reply_origin);
+			chase_reply->security = sec_status_bogus;
+			return 0;
+		}
+
+		/* Notice a DNAME that should be followed by an unsigned 
+		 * CNAME. */
+		if(qchase->qtype != LDNS_RR_TYPE_DNAME && 
+			ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME) {
+			dname_seen = 1;
+		}
+	}
+
+	/* validate the AUTHORITY section */
+	for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
+		chase_reply->ns_numrrsets; i++) {
+		s = chase_reply->rrsets[i];
+		sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason);
+		/* If anything in the authority section fails to be secure, 
+		 * we have a bad message. */
+		if(sec != sec_status_secure) {
+			log_nametypeclass(VERB_QUERY, "validator: response "
+				"has failed AUTHORITY rrset:", s->rk.dname,
+				ntohs(s->rk.type), ntohs(s->rk.rrset_class));
+			errinf(qstate, reason);
+			errinf_rrset(qstate, s);
+			errinf_origin(qstate, qstate->reply_origin);
+			chase_reply->security = sec_status_bogus;
+			return 0;
+		}
+	}
+
+	/* attempt to validate the ADDITIONAL section rrsets */
+	if(!ve->clean_additional)
+		return 1;
+	for(i=chase_reply->an_numrrsets+chase_reply->ns_numrrsets; 
+		i<chase_reply->rrset_count; i++) {
+		s = chase_reply->rrsets[i];
+		/* only validate rrs that have signatures with the key */
+		/* leave others unchecked, those get removed later on too */
+		val_find_rrset_signer(s, &sname, &slen);
+		if(sname && query_dname_compare(sname, key_entry->name)==0)
+			(void)val_verify_rrset_entry(env, ve, s, key_entry,
+				&reason);
+		/* the additional section can fail to be secure, 
+		 * it is optional, check signature in case we need
+		 * to clean the additional section later. */
+	}
+
+	return 1;
+}
+
+/**
+ * Detect wrong truncated response (say from BIND 9.6.1 that is forwarding
+ * and saw the NS record without signatures from a referral).
+ * The positive response has a mangled authority section.
+ * Remove that authority section and the additional section.
+ * @param rep: reply
+ * @return true if a wrongly truncated response.
+ */
+static int
+detect_wrongly_truncated(struct reply_info* rep)
+{
+	size_t i;
+	/* only NS in authority, and it is bogus */
+	if(rep->ns_numrrsets != 1 || rep->an_numrrsets == 0)
+		return 0;
+	if(ntohs(rep->rrsets[ rep->an_numrrsets ]->rk.type) != LDNS_RR_TYPE_NS)
+		return 0;
+	if(((struct packed_rrset_data*)rep->rrsets[ rep->an_numrrsets ]
+		->entry.data)->security == sec_status_secure)
+		return 0;
+	/* answer section is present and secure */
+	for(i=0; i<rep->an_numrrsets; i++) {
+		if(((struct packed_rrset_data*)rep->rrsets[ i ]
+			->entry.data)->security != sec_status_secure)
+			return 0;
+	}
+	verbose(VERB_ALGO, "truncating to minimal response");
+	return 1;
+}
+
+
+/**
+ * Given a "positive" response -- a response that contains an answer to the
+ * question, and no CNAME chain, validate this response. 
+ *
+ * The answer and authority RRsets must already be verified as secure.
+ * 
+ * @param env: module env for verify.
+ * @param ve: validator env for verify.
+ * @param qchase: query that was made.
+ * @param chase_reply: answer to that query to validate.
+ * @param kkey: the key entry, which is trusted, and which matches
+ * 	the signer of the answer. The key entry isgood().
+ */
+static void
+validate_positive_response(struct module_env* env, struct val_env* ve,
+	struct query_info* qchase, struct reply_info* chase_reply,
+	struct key_entry_key* kkey)
+{
+	uint8_t* wc = NULL;
+	int wc_NSEC_ok = 0;
+	int nsec3s_seen = 0;
+	size_t i;
+	struct ub_packed_rrset_key* s;
+
+	/* validate the ANSWER section - this will be the answer itself */
+	for(i=0; i<chase_reply->an_numrrsets; i++) {
+		s = chase_reply->rrsets[i];
+
+		/* Check to see if the rrset is the result of a wildcard 
+		 * expansion. If so, an additional check will need to be 
+		 * made in the authority section. */
+		if(!val_rrset_wildcard(s, &wc)) {
+			log_nametypeclass(VERB_QUERY, "Positive response has "
+				"inconsistent wildcard sigs:", s->rk.dname,
+				ntohs(s->rk.type), ntohs(s->rk.rrset_class));
+			chase_reply->security = sec_status_bogus;
+			return;
+		}
+	}
+
+	/* validate the AUTHORITY section as well - this will generally be 
+	 * the NS rrset (which could be missing, no problem) */
+	for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
+		chase_reply->ns_numrrsets; i++) {
+		s = chase_reply->rrsets[i];
+
+		/* If this is a positive wildcard response, and we have a 
+		 * (just verified) NSEC record, try to use it to 1) prove 
+		 * that qname doesn't exist and 2) that the correct wildcard 
+		 * was used. */
+		if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) {
+			if(val_nsec_proves_positive_wildcard(s, qchase, wc)) {
+				wc_NSEC_ok = 1;
+			}
+			/* if not, continue looking for proof */
+		}
+
+		/* Otherwise, if this is a positive wildcard response and 
+		 * we have NSEC3 records */
+		if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
+			nsec3s_seen = 1;
+		}
+	}
+
+	/* If this was a positive wildcard response that we haven't already
+	 * proven, and we have NSEC3 records, try to prove it using the NSEC3
+	 * records. */
+	if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) {
+		enum sec_status sec = nsec3_prove_wildcard(env, ve, 
+			chase_reply->rrsets+chase_reply->an_numrrsets,
+			chase_reply->ns_numrrsets, qchase, kkey, wc);
+		if(sec == sec_status_insecure) {
+			verbose(VERB_ALGO, "Positive wildcard response is "
+				"insecure");
+			chase_reply->security = sec_status_insecure;
+			return;
+		} else if(sec == sec_status_secure)
+			wc_NSEC_ok = 1;
+	}
+
+	/* If after all this, we still haven't proven the positive wildcard
+	 * response, fail. */
+	if(wc != NULL && !wc_NSEC_ok) {
+		verbose(VERB_QUERY, "positive response was wildcard "
+			"expansion and did not prove original data "
+			"did not exist");
+		chase_reply->security = sec_status_bogus;
+		return;
+	}
+
+	verbose(VERB_ALGO, "Successfully validated positive response");
+	chase_reply->security = sec_status_secure;
+}
+
+/** 
+ * Validate a NOERROR/NODATA signed response -- a response that has a
+ * NOERROR Rcode but no ANSWER section RRsets. This consists of making 
+ * certain that the authority section NSEC/NSEC3s proves that the qname 
+ * does exist and the qtype doesn't.
+ *
+ * The answer and authority RRsets must already be verified as secure.
+ *
+ * @param env: module env for verify.
+ * @param ve: validator env for verify.
+ * @param qchase: query that was made.
+ * @param chase_reply: answer to that query to validate.
+ * @param kkey: the key entry, which is trusted, and which matches
+ * 	the signer of the answer. The key entry isgood().
+ */
+static void
+validate_nodata_response(struct module_env* env, struct val_env* ve,
+	struct query_info* qchase, struct reply_info* chase_reply,
+	struct key_entry_key* kkey)
+{
+	/* Since we are here, there must be nothing in the ANSWER section to
+	 * validate. */
+	/* (Note: CNAME/DNAME responses will not directly get here --
+	 * instead, they are chased down into indiviual CNAME validations,
+	 * and at the end of the cname chain a POSITIVE, or CNAME_NOANSWER 
+	 * validation.) */
+	
+	/* validate the AUTHORITY section */
+	int has_valid_nsec = 0; /* If true, then the NODATA has been proven.*/
+	uint8_t* ce = NULL; /* for wildcard nodata responses. This is the 
+				proven closest encloser. */
+	uint8_t* wc = NULL; /* for wildcard nodata responses. wildcard nsec */
+	int nsec3s_seen = 0; /* nsec3s seen */
+	struct ub_packed_rrset_key* s; 
+	size_t i;
+
+	for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
+		chase_reply->ns_numrrsets; i++) {
+		s = chase_reply->rrsets[i];
+		/* If we encounter an NSEC record, try to use it to prove 
+		 * NODATA.
+		 * This needs to handle the ENT NODATA case. */
+		if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) {
+			if(nsec_proves_nodata(s, qchase, &wc)) {
+				has_valid_nsec = 1;
+				/* sets wc-encloser if wildcard applicable */
+			} 
+			if(val_nsec_proves_name_error(s, qchase->qname)) {
+				ce = nsec_closest_encloser(qchase->qname, s);
+			}
+			if(val_nsec_proves_insecuredelegation(s, qchase)) {
+				verbose(VERB_ALGO, "delegation is insecure");
+				chase_reply->security = sec_status_insecure;
+				return;
+			}
+		} else if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
+			nsec3s_seen = 1;
+		}
+	}
+
+	/* check to see if we have a wildcard NODATA proof. */
+
+	/* The wildcard NODATA is 1 NSEC proving that qname does not exist 
+	 * (and also proving what the closest encloser is), and 1 NSEC 
+	 * showing the matching wildcard, which must be *.closest_encloser. */
+	if(wc && !ce)
+		has_valid_nsec = 0;
+	else if(wc && ce) {
+		if(query_dname_compare(wc, ce) != 0) {
+			has_valid_nsec = 0;
+		}
+	}
+	
+	if(!has_valid_nsec && nsec3s_seen) {
+		enum sec_status sec = nsec3_prove_nodata(env, ve, 
+			chase_reply->rrsets+chase_reply->an_numrrsets,
+			chase_reply->ns_numrrsets, qchase, kkey);
+		if(sec == sec_status_insecure) {
+			verbose(VERB_ALGO, "NODATA response is insecure");
+			chase_reply->security = sec_status_insecure;
+			return;
+		} else if(sec == sec_status_secure)
+			has_valid_nsec = 1;
+	}
+
+	if(!has_valid_nsec) {
+		verbose(VERB_QUERY, "NODATA response failed to prove NODATA "
+			"status with NSEC/NSEC3");
+		if(verbosity >= VERB_ALGO)
+			log_dns_msg("Failed NODATA", qchase, chase_reply);
+		chase_reply->security = sec_status_bogus;
+		return;
+	}
+
+	verbose(VERB_ALGO, "successfully validated NODATA response.");
+	chase_reply->security = sec_status_secure;
+}
+
+/** 
+ * Validate a NAMEERROR signed response -- a response that has a NXDOMAIN
+ * Rcode. 
+ * This consists of making certain that the authority section NSEC proves 
+ * that the qname doesn't exist and the covering wildcard also doesn't exist..
+ * 
+ * The answer and authority RRsets must have already been verified as secure.
+ *
+ * @param env: module env for verify.
+ * @param ve: validator env for verify.
+ * @param qchase: query that was made.
+ * @param chase_reply: answer to that query to validate.
+ * @param kkey: the key entry, which is trusted, and which matches
+ * 	the signer of the answer. The key entry isgood().
+ */
+static void
+validate_nameerror_response(struct module_env* env, struct val_env* ve,
+	struct query_info* qchase, struct reply_info* chase_reply,
+	struct key_entry_key* kkey)
+{
+	int has_valid_nsec = 0;
+	int has_valid_wnsec = 0;
+	int nsec3s_seen = 0;
+	struct ub_packed_rrset_key* s; 
+	size_t i;
+
+	for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
+		chase_reply->ns_numrrsets; i++) {
+		s = chase_reply->rrsets[i];
+		if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) {
+			if(val_nsec_proves_name_error(s, qchase->qname))
+				has_valid_nsec = 1;
+			if(val_nsec_proves_no_wc(s, qchase->qname, 
+				qchase->qname_len))
+				has_valid_wnsec = 1;
+			if(val_nsec_proves_insecuredelegation(s, qchase)) {
+				verbose(VERB_ALGO, "delegation is insecure");
+				chase_reply->security = sec_status_insecure;
+				return;
+			}
+		} else if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3)
+			nsec3s_seen = 1;
+	}
+
+	if((!has_valid_nsec || !has_valid_wnsec) && nsec3s_seen) {
+		/* use NSEC3 proof, both answer and auth rrsets, in case
+		 * NSEC3s end up in the answer (due to qtype=NSEC3 or so) */
+		chase_reply->security = nsec3_prove_nameerror(env, ve,
+			chase_reply->rrsets, chase_reply->an_numrrsets+
+			chase_reply->ns_numrrsets, qchase, kkey);
+		if(chase_reply->security != sec_status_secure) {
+			verbose(VERB_QUERY, "NameError response failed nsec, "
+				"nsec3 proof was %s", sec_status_to_string(
+				chase_reply->security));
+			return;
+		}
+		has_valid_nsec = 1;
+		has_valid_wnsec = 1;
+	}
+
+	/* If the message fails to prove either condition, it is bogus. */
+	if(!has_valid_nsec) {
+		verbose(VERB_QUERY, "NameError response has failed to prove: "
+		          "qname does not exist");
+		chase_reply->security = sec_status_bogus;
+		return;
+	}
+
+	if(!has_valid_wnsec) {
+		verbose(VERB_QUERY, "NameError response has failed to prove: "
+		          "covering wildcard does not exist");
+		chase_reply->security = sec_status_bogus;
+		return;
+	}
+
+	/* Otherwise, we consider the message secure. */
+	verbose(VERB_ALGO, "successfully validated NAME ERROR response.");
+	chase_reply->security = sec_status_secure;
+}
+
+/** 
+ * Given a referral response, validate rrsets and take least trusted rrset
+ * as the current validation status.
+ * 
+ * Note that by the time this method is called, the process of finding the
+ * trusted DNSKEY rrset that signs this response must already have been
+ * completed.
+ * 
+ * @param chase_reply: answer to validate.
+ */
+static void
+validate_referral_response(struct reply_info* chase_reply)
+{
+	size_t i;
+	enum sec_status s;
+	/* message security equals lowest rrset security */
+	chase_reply->security = sec_status_secure;
+	for(i=0; i<chase_reply->rrset_count; i++) {
+		s = ((struct packed_rrset_data*)chase_reply->rrsets[i]
+			->entry.data)->security;
+		if(s < chase_reply->security)
+			chase_reply->security = s;
+	}
+	verbose(VERB_ALGO, "validated part of referral response as %s",
+		sec_status_to_string(chase_reply->security));
+}
+
+/** 
+ * Given an "ANY" response -- a response that contains an answer to a
+ * qtype==ANY question, with answers. This does no checking that all 
+ * types are present.
+ * 
+ * NOTE: it may be possible to get parent-side delegation point records
+ * here, which won't all be signed. Right now, this routine relies on the
+ * upstream iterative resolver to not return these responses -- instead
+ * treating them as referrals.
+ * 
+ * NOTE: RFC 4035 is silent on this issue, so this may change upon
+ * clarification. Clarification draft -05 says to not check all types are
+ * present.
+ * 
+ * Note that by the time this method is called, the process of finding the
+ * trusted DNSKEY rrset that signs this response must already have been
+ * completed.
+ * 
+ * @param env: module env for verify.
+ * @param ve: validator env for verify.
+ * @param qchase: query that was made.
+ * @param chase_reply: answer to that query to validate.
+ * @param kkey: the key entry, which is trusted, and which matches
+ * 	the signer of the answer. The key entry isgood().
+ */
+static void
+validate_any_response(struct module_env* env, struct val_env* ve,
+	struct query_info* qchase, struct reply_info* chase_reply,
+	struct key_entry_key* kkey)
+{
+	/* all answer and auth rrsets already verified */
+	/* but check if a wildcard response is given, then check NSEC/NSEC3
+	 * for qname denial to see if wildcard is applicable */
+	uint8_t* wc = NULL;
+	int wc_NSEC_ok = 0;
+	int nsec3s_seen = 0;
+	size_t i;
+	struct ub_packed_rrset_key* s;
+
+	if(qchase->qtype != LDNS_RR_TYPE_ANY) {
+		log_err("internal error: ANY validation called for non-ANY");
+		chase_reply->security = sec_status_bogus;
+		return;
+	}
+
+	/* validate the ANSWER section - this will be the answer itself */
+	for(i=0; i<chase_reply->an_numrrsets; i++) {
+		s = chase_reply->rrsets[i];
+
+		/* Check to see if the rrset is the result of a wildcard 
+		 * expansion. If so, an additional check will need to be 
+		 * made in the authority section. */
+		if(!val_rrset_wildcard(s, &wc)) {
+			log_nametypeclass(VERB_QUERY, "Positive ANY response"
+				" has inconsistent wildcard sigs:", 
+				s->rk.dname, ntohs(s->rk.type), 
+				ntohs(s->rk.rrset_class));
+			chase_reply->security = sec_status_bogus;
+			return;
+		}
+	}
+
+	/* if it was a wildcard, check for NSEC/NSEC3s in both answer
+	 * and authority sections (NSEC may be moved to the ANSWER section) */
+	if(wc != NULL)
+	  for(i=0; i<chase_reply->an_numrrsets+chase_reply->ns_numrrsets; 
+	  	i++) {
+		s = chase_reply->rrsets[i];
+
+		/* If this is a positive wildcard response, and we have a 
+		 * (just verified) NSEC record, try to use it to 1) prove 
+		 * that qname doesn't exist and 2) that the correct wildcard 
+		 * was used. */
+		if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) {
+			if(val_nsec_proves_positive_wildcard(s, qchase, wc)) {
+				wc_NSEC_ok = 1;
+			}
+			/* if not, continue looking for proof */
+		}
+
+		/* Otherwise, if this is a positive wildcard response and 
+		 * we have NSEC3 records */
+		if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
+			nsec3s_seen = 1;
+		}
+	}
+
+	/* If this was a positive wildcard response that we haven't already
+	 * proven, and we have NSEC3 records, try to prove it using the NSEC3
+	 * records. */
+	if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) {
+		/* look both in answer and auth section for NSEC3s */
+		enum sec_status sec = nsec3_prove_wildcard(env, ve, 
+			chase_reply->rrsets,
+			chase_reply->an_numrrsets+chase_reply->ns_numrrsets, 
+			qchase, kkey, wc);
+		if(sec == sec_status_insecure) {
+			verbose(VERB_ALGO, "Positive ANY wildcard response is "
+				"insecure");
+			chase_reply->security = sec_status_insecure;
+			return;
+		} else if(sec == sec_status_secure)
+			wc_NSEC_ok = 1;
+	}
+
+	/* If after all this, we still haven't proven the positive wildcard
+	 * response, fail. */
+	if(wc != NULL && !wc_NSEC_ok) {
+		verbose(VERB_QUERY, "positive ANY response was wildcard "
+			"expansion and did not prove original data "
+			"did not exist");
+		chase_reply->security = sec_status_bogus;
+		return;
+	}
+
+	verbose(VERB_ALGO, "Successfully validated positive ANY response");
+	chase_reply->security = sec_status_secure;
+}
+
+/**
+ * Validate CNAME response, or DNAME+CNAME.
+ * This is just like a positive proof, except that this is about a 
+ * DNAME+CNAME. Possible wildcard proof.
+ * Difference with positive proof is that this routine refuses 
+ * wildcarded DNAMEs.
+ * 
+ * The answer and authority rrsets must already be verified as secure.
+ * 
+ * @param env: module env for verify.
+ * @param ve: validator env for verify.
+ * @param qchase: query that was made.
+ * @param chase_reply: answer to that query to validate.
+ * @param kkey: the key entry, which is trusted, and which matches
+ * 	the signer of the answer. The key entry isgood().
+ */
+static void
+validate_cname_response(struct module_env* env, struct val_env* ve,
+	struct query_info* qchase, struct reply_info* chase_reply,
+	struct key_entry_key* kkey)
+{
+	uint8_t* wc = NULL;
+	int wc_NSEC_ok = 0;
+	int nsec3s_seen = 0;
+	size_t i;
+	struct ub_packed_rrset_key* s;
+
+	/* validate the ANSWER section - this will be the CNAME (+DNAME) */
+	for(i=0; i<chase_reply->an_numrrsets; i++) {
+		s = chase_reply->rrsets[i];
+
+		/* Check to see if the rrset is the result of a wildcard 
+		 * expansion. If so, an additional check will need to be 
+		 * made in the authority section. */
+		if(!val_rrset_wildcard(s, &wc)) {
+			log_nametypeclass(VERB_QUERY, "Cname response has "
+				"inconsistent wildcard sigs:", s->rk.dname,
+				ntohs(s->rk.type), ntohs(s->rk.rrset_class));
+			chase_reply->security = sec_status_bogus;
+			return;
+		}
+		
+		/* Refuse wildcarded DNAMEs rfc 4597. 
+		 * Do not follow a wildcarded DNAME because 
+		 * its synthesized CNAME expansion is underdefined */
+		if(qchase->qtype != LDNS_RR_TYPE_DNAME && 
+			ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME && wc) {
+			log_nametypeclass(VERB_QUERY, "cannot validate a "
+				"wildcarded DNAME:", s->rk.dname, 
+				ntohs(s->rk.type), ntohs(s->rk.rrset_class));
+			chase_reply->security = sec_status_bogus;
+			return;
+		}
+	}
+
+	/* AUTHORITY section */
+	for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
+		chase_reply->ns_numrrsets; i++) {
+		s = chase_reply->rrsets[i];
+
+		/* If this is a positive wildcard response, and we have a 
+		 * (just verified) NSEC record, try to use it to 1) prove 
+		 * that qname doesn't exist and 2) that the correct wildcard 
+		 * was used. */
+		if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) {
+			if(val_nsec_proves_positive_wildcard(s, qchase, wc)) {
+				wc_NSEC_ok = 1;
+			}
+			/* if not, continue looking for proof */
+		}
+
+		/* Otherwise, if this is a positive wildcard response and 
+		 * we have NSEC3 records */
+		if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
+			nsec3s_seen = 1;
+		}
+	}
+
+	/* If this was a positive wildcard response that we haven't already
+	 * proven, and we have NSEC3 records, try to prove it using the NSEC3
+	 * records. */
+	if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) {
+		enum sec_status sec = nsec3_prove_wildcard(env, ve, 
+			chase_reply->rrsets+chase_reply->an_numrrsets,
+			chase_reply->ns_numrrsets, qchase, kkey, wc);
+		if(sec == sec_status_insecure) {
+			verbose(VERB_ALGO, "wildcard CNAME response is "
+				"insecure");
+			chase_reply->security = sec_status_insecure;
+			return;
+		} else if(sec == sec_status_secure)
+			wc_NSEC_ok = 1;
+	}
+
+	/* If after all this, we still haven't proven the positive wildcard
+	 * response, fail. */
+	if(wc != NULL && !wc_NSEC_ok) {
+		verbose(VERB_QUERY, "CNAME response was wildcard "
+			"expansion and did not prove original data "
+			"did not exist");
+		chase_reply->security = sec_status_bogus;
+		return;
+	}
+
+	verbose(VERB_ALGO, "Successfully validated CNAME response");
+	chase_reply->security = sec_status_secure;
+}
+
+/**
+ * Validate CNAME NOANSWER response, no more data after a CNAME chain.
+ * This can be a NODATA or a NAME ERROR case, but not both at the same time.
+ * We don't know because the rcode has been set to NOERROR by the CNAME.
+ * 
+ * The answer and authority rrsets must already be verified as secure.
+ * 
+ * @param env: module env for verify.
+ * @param ve: validator env for verify.
+ * @param qchase: query that was made.
+ * @param chase_reply: answer to that query to validate.
+ * @param kkey: the key entry, which is trusted, and which matches
+ * 	the signer of the answer. The key entry isgood().
+ */
+static void
+validate_cname_noanswer_response(struct module_env* env, struct val_env* ve,
+	struct query_info* qchase, struct reply_info* chase_reply,
+	struct key_entry_key* kkey)
+{
+	int nodata_valid_nsec = 0; /* If true, then NODATA has been proven.*/
+	uint8_t* ce = NULL; /* for wildcard nodata responses. This is the 
+				proven closest encloser. */
+	uint8_t* wc = NULL; /* for wildcard nodata responses. wildcard nsec */
+	int nxdomain_valid_nsec = 0; /* if true, namerror has been proven */
+	int nxdomain_valid_wnsec = 0;
+	int nsec3s_seen = 0; /* nsec3s seen */
+	struct ub_packed_rrset_key* s; 
+	size_t i;
+
+	/* the AUTHORITY section */
+	for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
+		chase_reply->ns_numrrsets; i++) {
+		s = chase_reply->rrsets[i];
+
+		/* If we encounter an NSEC record, try to use it to prove 
+		 * NODATA. This needs to handle the ENT NODATA case. 
+		 * Also try to prove NAMEERROR, and absence of a wildcard */
+		if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) {
+			if(nsec_proves_nodata(s, qchase, &wc)) {
+				nodata_valid_nsec = 1;
+				/* set wc encloser if wildcard applicable */
+			} 
+			if(val_nsec_proves_name_error(s, qchase->qname)) {
+				ce = nsec_closest_encloser(qchase->qname, s);
+				nxdomain_valid_nsec = 1;
+			}
+			if(val_nsec_proves_no_wc(s, qchase->qname, 
+				qchase->qname_len))
+				nxdomain_valid_wnsec = 1;
+			if(val_nsec_proves_insecuredelegation(s, qchase)) {
+				verbose(VERB_ALGO, "delegation is insecure");
+				chase_reply->security = sec_status_insecure;
+				return;
+			}
+		} else if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
+			nsec3s_seen = 1;
+		}
+	}
+
+	/* check to see if we have a wildcard NODATA proof. */
+
+	/* The wildcard NODATA is 1 NSEC proving that qname does not exists 
+	 * (and also proving what the closest encloser is), and 1 NSEC 
+	 * showing the matching wildcard, which must be *.closest_encloser. */
+	if(wc && !ce)
+		nodata_valid_nsec = 0;
+	else if(wc && ce) {
+		if(query_dname_compare(wc, ce) != 0) {
+			nodata_valid_nsec = 0;
+		}
+	}
+	if(nxdomain_valid_nsec && !nxdomain_valid_wnsec) {
+		/* name error is missing wildcard denial proof */
+		nxdomain_valid_nsec = 0;
+	}
+	
+	if(nodata_valid_nsec && nxdomain_valid_nsec) {
+		verbose(VERB_QUERY, "CNAMEchain to noanswer proves that name "
+			"exists and not exists, bogus");
+		chase_reply->security = sec_status_bogus;
+		return;
+	}
+	if(!nodata_valid_nsec && !nxdomain_valid_nsec && nsec3s_seen) {
+		int nodata;
+		enum sec_status sec = nsec3_prove_nxornodata(env, ve, 
+			chase_reply->rrsets+chase_reply->an_numrrsets,
+			chase_reply->ns_numrrsets, qchase, kkey, &nodata);
+		if(sec == sec_status_insecure) {
+			verbose(VERB_ALGO, "CNAMEchain to noanswer response "
+				"is insecure");
+			chase_reply->security = sec_status_insecure;
+			return;
+		} else if(sec == sec_status_secure) {
+			if(nodata)
+				nodata_valid_nsec = 1;
+			else	nxdomain_valid_nsec = 1;
+		}
+	}
+
+	if(!nodata_valid_nsec && !nxdomain_valid_nsec) {
+		verbose(VERB_QUERY, "CNAMEchain to noanswer response failed "
+			"to prove status with NSEC/NSEC3");
+		if(verbosity >= VERB_ALGO)
+			log_dns_msg("Failed CNAMEnoanswer", qchase, chase_reply);
+		chase_reply->security = sec_status_bogus;
+		return;
+	}
+
+	if(nodata_valid_nsec)
+		verbose(VERB_ALGO, "successfully validated CNAME chain to a "
+			"NODATA response.");
+	else	verbose(VERB_ALGO, "successfully validated CNAME chain to a "
+			"NAMEERROR response.");
+	chase_reply->security = sec_status_secure;
+}
+
+/** 
+ * Process init state for validator.
+ * Process the INIT state. First tier responses start in the INIT state.
+ * This is where they are vetted for validation suitability, and the initial
+ * key search is done.
+ * 
+ * Currently, events the come through this routine will be either promoted
+ * to FINISHED/CNAME_RESP (no validation needed), FINDKEY (next step to
+ * validation), or will be (temporarily) retired and a new priming request
+ * event will be generated.
+ *
+ * @param qstate: query state.
+ * @param vq: validator query state.
+ * @param ve: validator shared global environment.
+ * @param id: module id.
+ * @return true if the event should be processed further on return, false if
+ *         not.
+ */
+static int
+processInit(struct module_qstate* qstate, struct val_qstate* vq, 
+	struct val_env* ve, int id)
+{
+	uint8_t* lookup_name;
+	size_t lookup_len;
+	struct trust_anchor* anchor;
+	enum val_classification subtype = val_classify_response(
+		qstate->query_flags, &qstate->qinfo, &vq->qchase, 
+		vq->orig_msg->rep, vq->rrset_skip);
+	if(vq->restart_count > VAL_MAX_RESTART_COUNT) {
+		verbose(VERB_ALGO, "restart count exceeded");
+		return val_error(qstate, id);
+	}
+	verbose(VERB_ALGO, "validator classification %s", 
+		val_classification_to_string(subtype));
+	if(subtype == VAL_CLASS_REFERRAL && 
+		vq->rrset_skip < vq->orig_msg->rep->rrset_count) {
+		/* referral uses the rrset name as qchase, to find keys for
+		 * that rrset */
+		vq->qchase.qname = vq->orig_msg->rep->
+			rrsets[vq->rrset_skip]->rk.dname;
+		vq->qchase.qname_len = vq->orig_msg->rep->
+			rrsets[vq->rrset_skip]->rk.dname_len;
+		vq->qchase.qtype = ntohs(vq->orig_msg->rep->
+			rrsets[vq->rrset_skip]->rk.type);
+		vq->qchase.qclass = ntohs(vq->orig_msg->rep->
+			rrsets[vq->rrset_skip]->rk.rrset_class);
+	}
+	lookup_name = vq->qchase.qname;
+	lookup_len = vq->qchase.qname_len;
+	/* for type DS look at the parent side for keys/trustanchor */
+	/* also for NSEC not at apex */
+	if(vq->qchase.qtype == LDNS_RR_TYPE_DS ||
+		(vq->qchase.qtype == LDNS_RR_TYPE_NSEC && 
+		 vq->orig_msg->rep->rrset_count > vq->rrset_skip &&
+		 ntohs(vq->orig_msg->rep->rrsets[vq->rrset_skip]->rk.type) ==
+		 LDNS_RR_TYPE_NSEC &&
+		 !(vq->orig_msg->rep->rrsets[vq->rrset_skip]->
+		 rk.flags&PACKED_RRSET_NSEC_AT_APEX))) {
+		dname_remove_label(&lookup_name, &lookup_len);
+	}
+
+	val_mark_indeterminate(vq->chase_reply, qstate->env->anchors, 
+		qstate->env->rrset_cache, qstate->env);
+	vq->key_entry = NULL;
+	vq->empty_DS_name = NULL;
+	vq->ds_rrset = 0;
+	anchor = anchors_lookup(qstate->env->anchors, 
+		lookup_name, lookup_len, vq->qchase.qclass);
+
+	/* Determine the signer/lookup name */
+	val_find_signer(subtype, &vq->qchase, vq->orig_msg->rep, 
+		vq->rrset_skip, &vq->signer_name, &vq->signer_len);
+	if(vq->signer_name != NULL &&
+		!dname_subdomain_c(lookup_name, vq->signer_name)) {
+		log_nametypeclass(VERB_ALGO, "this signer name is not a parent "
+			"of lookupname, omitted", vq->signer_name, 0, 0);
+		vq->signer_name = NULL;
+	}
+	if(vq->signer_name == NULL) {
+		log_nametypeclass(VERB_ALGO, "no signer, using", lookup_name,
+			0, 0);
+	} else {
+		lookup_name = vq->signer_name;
+		lookup_len = vq->signer_len;
+		log_nametypeclass(VERB_ALGO, "signer is", lookup_name, 0, 0);
+	}
+
+	/* for NXDOMAIN it could be signed by a parent of the trust anchor */
+	if(subtype == VAL_CLASS_NAMEERROR && vq->signer_name &&
+		anchor && dname_strict_subdomain_c(anchor->name, lookup_name)){
+		lock_basic_unlock(&anchor->lock);
+		anchor = anchors_lookup(qstate->env->anchors, 
+			lookup_name, lookup_len, vq->qchase.qclass);
+		if(!anchor) { /* unsigned parent denies anchor*/
+			verbose(VERB_QUERY, "unsigned parent zone denies"
+				" trust anchor, indeterminate");
+			vq->chase_reply->security = sec_status_indeterminate;
+			vq->state = VAL_FINISHED_STATE;
+			return 1;
+		}
+		verbose(VERB_ALGO, "trust anchor NXDOMAIN by signed parent");
+	} else if(subtype == VAL_CLASS_POSITIVE &&
+		qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY &&
+		query_dname_compare(lookup_name, qstate->qinfo.qname) == 0) {
+		/* is a DNSKEY so lookup a bit higher since we want to
+		 * get it from a parent or from trustanchor */
+		dname_remove_label(&lookup_name, &lookup_len);
+	}
+
+	if(vq->rrset_skip > 0 || subtype == VAL_CLASS_CNAME ||
+		subtype == VAL_CLASS_REFERRAL) {
+		/* extract this part of orig_msg into chase_reply for
+		 * the eventual VALIDATE stage */
+		val_fill_reply(vq->chase_reply, vq->orig_msg->rep, 
+			vq->rrset_skip, lookup_name, lookup_len, 
+			vq->signer_name);
+		if(verbosity >= VERB_ALGO)
+			log_dns_msg("chased extract", &vq->qchase, 
+				vq->chase_reply);
+	}
+
+	vq->key_entry = key_cache_obtain(ve->kcache, lookup_name, lookup_len,
+		vq->qchase.qclass, qstate->region, *qstate->env->now);
+
+	/* there is no key(from DLV) and no trust anchor */
+	if(vq->key_entry == NULL && anchor == NULL) {
+		/*response isn't under a trust anchor, so we cannot validate.*/
+		vq->chase_reply->security = sec_status_indeterminate;
+		/* go to finished state to cache this result */
+		vq->state = VAL_FINISHED_STATE;
+		return 1;
+	}
+	/* if not key, or if keyentry is *above* the trustanchor, i.e.
+	 * the keyentry is based on another (higher) trustanchor */
+	else if(vq->key_entry == NULL || (anchor &&
+		dname_strict_subdomain_c(anchor->name, vq->key_entry->name))) {
+		/* trust anchor is an 'unsigned' trust anchor */
+		if(anchor && anchor->numDS == 0 && anchor->numDNSKEY == 0) {
+			vq->chase_reply->security = sec_status_insecure;
+			val_mark_insecure(vq->chase_reply, anchor->name, 
+				qstate->env->rrset_cache, qstate->env);
+			lock_basic_unlock(&anchor->lock);
+			vq->dlv_checked=1; /* skip DLV check */
+			/* go to finished state to cache this result */
+			vq->state = VAL_FINISHED_STATE;
+			return 1;
+		}
+		/* fire off a trust anchor priming query. */
+		verbose(VERB_DETAIL, "prime trust anchor");
+		if(!prime_trust_anchor(qstate, vq, id, anchor)) {
+			lock_basic_unlock(&anchor->lock);
+			return val_error(qstate, id);
+		}
+		lock_basic_unlock(&anchor->lock);
+		/* and otherwise, don't continue processing this event.
+		 * (it will be reactivated when the priming query returns). */
+		vq->state = VAL_FINDKEY_STATE;
+		return 0;
+	}
+	if(anchor) {
+		lock_basic_unlock(&anchor->lock);
+	}
+
+	if(key_entry_isnull(vq->key_entry)) {
+		/* response is under a null key, so we cannot validate
+		 * However, we do set the status to INSECURE, since it is 
+		 * essentially proven insecure. */
+		vq->chase_reply->security = sec_status_insecure;
+		val_mark_insecure(vq->chase_reply, vq->key_entry->name, 
+			qstate->env->rrset_cache, qstate->env);
+		/* go to finished state to cache this result */
+		vq->state = VAL_FINISHED_STATE;
+		return 1;
+	} else if(key_entry_isbad(vq->key_entry)) {
+		/* key is bad, chain is bad, reply is bogus */
+		errinf_dname(qstate, "key for validation", vq->key_entry->name);
+		errinf(qstate, "is marked as invalid");
+		if(key_entry_get_reason(vq->key_entry)) {
+			errinf(qstate, "because of a previous");
+			errinf(qstate, key_entry_get_reason(vq->key_entry));
+		}
+		/* no retries, stop bothering the authority until timeout */
+		vq->restart_count = VAL_MAX_RESTART_COUNT;
+		vq->chase_reply->security = sec_status_bogus;
+		vq->state = VAL_FINISHED_STATE;
+		return 1;
+	}
+
+	/* otherwise, we have our "closest" cached key -- continue 
+	 * processing in the next state. */
+	vq->state = VAL_FINDKEY_STATE;
+	return 1;
+}
+
+/**
+ * Process the FINDKEY state. Generally this just calculates the next name
+ * to query and either issues a DS or a DNSKEY query. It will check to see
+ * if the correct key has already been reached, in which case it will
+ * advance the event to the next state.
+ *
+ * @param qstate: query state.
+ * @param vq: validator query state.
+ * @param id: module id.
+ * @return true if the event should be processed further on return, false if
+ *         not.
+ */
+static int
+processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id)
+{
+	uint8_t* target_key_name, *current_key_name;
+	size_t target_key_len;
+	int strip_lab;
+
+	log_query_info(VERB_ALGO, "validator: FindKey", &vq->qchase);
+	/* We know that state.key_entry is not 0 or bad key -- if it were,
+	 * then previous processing should have directed this event to 
+	 * a different state. 
+	 * It could be an isnull key, which signals that a DLV was just
+	 * done and the DNSKEY after the DLV failed with dnssec-retry state
+	 * and the DNSKEY has to be performed again. */
+	log_assert(vq->key_entry && !key_entry_isbad(vq->key_entry));
+	if(key_entry_isnull(vq->key_entry)) {
+		if(!generate_request(qstate, id, vq->ds_rrset->rk.dname, 
+			vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY, 
+			vq->qchase.qclass, BIT_CD)) {
+			log_err("mem error generating DNSKEY request");
+			return val_error(qstate, id);
+		}
+		return 0;
+	}
+
+	target_key_name = vq->signer_name;
+	target_key_len = vq->signer_len;
+	if(!target_key_name) {
+		target_key_name = vq->qchase.qname;
+		target_key_len = vq->qchase.qname_len;
+	}
+
+	current_key_name = vq->key_entry->name;
+
+	/* If our current key entry matches our target, then we are done. */
+	if(query_dname_compare(target_key_name, current_key_name) == 0) {
+		vq->state = VAL_VALIDATE_STATE;
+		return 1;
+	}
+
+	if(vq->empty_DS_name) {
+		/* if the last empty nonterminal/emptyDS name we detected is
+		 * below the current key, use that name to make progress
+		 * along the chain of trust */
+		if(query_dname_compare(target_key_name, 
+			vq->empty_DS_name) == 0) {
+			/* do not query for empty_DS_name again */
+			verbose(VERB_ALGO, "Cannot retrieve DS for signature");
+			errinf(qstate, "no signatures");
+			errinf_origin(qstate, qstate->reply_origin);
+			vq->chase_reply->security = sec_status_bogus;
+			vq->state = VAL_FINISHED_STATE;
+			return 1;
+		}
+		current_key_name = vq->empty_DS_name;
+	}
+
+	log_nametypeclass(VERB_ALGO, "current keyname", current_key_name,
+		LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN);
+	log_nametypeclass(VERB_ALGO, "target keyname", target_key_name,
+		LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN);
+	/* assert we are walking down the DNS tree */
+	if(!dname_subdomain_c(target_key_name, current_key_name)) {
+		verbose(VERB_ALGO, "bad signer name");
+		vq->chase_reply->security = sec_status_bogus;
+		vq->state = VAL_FINISHED_STATE;
+		return 1;
+	}
+	/* so this value is >= -1 */
+	strip_lab = dname_count_labels(target_key_name) - 
+		dname_count_labels(current_key_name) - 1;
+	log_assert(strip_lab >= -1);
+	verbose(VERB_ALGO, "striplab %d", strip_lab);
+	if(strip_lab > 0) {
+		dname_remove_labels(&target_key_name, &target_key_len, 
+			strip_lab);
+	}
+	log_nametypeclass(VERB_ALGO, "next keyname", target_key_name,
+		LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN);
+
+	/* The next step is either to query for the next DS, or to query 
+	 * for the next DNSKEY. */
+	if(vq->ds_rrset)
+		log_nametypeclass(VERB_ALGO, "DS RRset", vq->ds_rrset->rk.dname, LDNS_RR_TYPE_DS, LDNS_RR_CLASS_IN);
+	else verbose(VERB_ALGO, "No DS RRset");
+
+	if(vq->ds_rrset && query_dname_compare(vq->ds_rrset->rk.dname,
+		vq->key_entry->name) != 0) {
+		if(!generate_request(qstate, id, vq->ds_rrset->rk.dname, 
+			vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY, 
+			vq->qchase.qclass, BIT_CD)) {
+			log_err("mem error generating DNSKEY request");
+			return val_error(qstate, id);
+		}
+		return 0;
+	}
+
+	if(!vq->ds_rrset || query_dname_compare(vq->ds_rrset->rk.dname,
+		target_key_name) != 0) {
+		/* check if there is a cache entry : pick up an NSEC if
+		 * there is no DS, check if that NSEC has DS-bit unset, and
+		 * thus can disprove the secure delagation we seek.
+		 * We can then use that NSEC even in the absence of a SOA
+		 * record that would be required by the iterator to supply
+		 * a completely protocol-correct response. 
+		 * Uses negative cache for NSEC3 lookup of DS responses. */
+		/* only if cache not blacklisted, of course */
+		struct dns_msg* msg;
+		if(!qstate->blacklist && !vq->chain_blacklist &&
+			(msg=val_find_DS(qstate->env, target_key_name, 
+			target_key_len, vq->qchase.qclass, qstate->region,
+			vq->key_entry->name)) ) {
+			verbose(VERB_ALGO, "Process cached DS response");
+			process_ds_response(qstate, vq, id, LDNS_RCODE_NOERROR,
+				msg, &msg->qinfo, NULL);
+			return 1; /* continue processing ds-response results */
+		}
+		if(!generate_request(qstate, id, target_key_name, 
+			target_key_len, LDNS_RR_TYPE_DS, vq->qchase.qclass,
+			BIT_CD)) {
+			log_err("mem error generating DS request");
+			return val_error(qstate, id);
+		}
+		return 0;
+	}
+
+	/* Otherwise, it is time to query for the DNSKEY */
+	if(!generate_request(qstate, id, vq->ds_rrset->rk.dname, 
+		vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY, 
+		vq->qchase.qclass, BIT_CD)) {
+		log_err("mem error generating DNSKEY request");
+		return val_error(qstate, id);
+	}
+
+	return 0;
+}
+
+/**
+ * Process the VALIDATE stage, the init and findkey stages are finished,
+ * and the right keys are available to validate the response.
+ * Or, there are no keys available, in order to invalidate the response.
+ *
+ * After validation, the status is recorded in the message and rrsets,
+ * and finished state is started.
+ *
+ * @param qstate: query state.
+ * @param vq: validator query state.
+ * @param ve: validator shared global environment.
+ * @param id: module id.
+ * @return true if the event should be processed further on return, false if
+ *         not.
+ */
+static int
+processValidate(struct module_qstate* qstate, struct val_qstate* vq, 
+	struct val_env* ve, int id)
+{
+	enum val_classification subtype;
+
+	if(!vq->key_entry) {
+		verbose(VERB_ALGO, "validate: no key entry, failed");
+		return val_error(qstate, id);
+	}
+
+	/* This is the default next state. */
+	vq->state = VAL_FINISHED_STATE;
+
+	/* Unsigned responses must be underneath a "null" key entry.*/
+	if(key_entry_isnull(vq->key_entry)) {
+		verbose(VERB_DETAIL, "Verified that %sresponse is INSECURE",
+			vq->signer_name?"":"unsigned ");
+		vq->chase_reply->security = sec_status_insecure;
+		val_mark_insecure(vq->chase_reply, vq->key_entry->name, 
+			qstate->env->rrset_cache, qstate->env);
+		key_cache_insert(ve->kcache, vq->key_entry, qstate);
+		return 1;
+	}
+
+	if(key_entry_isbad(vq->key_entry)) {
+		log_nametypeclass(VERB_DETAIL, "Could not establish a chain "
+			"of trust to keys for", vq->key_entry->name,
+			LDNS_RR_TYPE_DNSKEY, vq->key_entry->key_class);
+		vq->chase_reply->security = sec_status_bogus;
+		errinf(qstate, "while building chain of trust");
+		if(vq->restart_count >= VAL_MAX_RESTART_COUNT)
+			key_cache_insert(ve->kcache, vq->key_entry, qstate);
+		return 1;
+	}
+
+	/* signerName being null is the indicator that this response was 
+	 * unsigned */
+	if(vq->signer_name == NULL) {
+		log_query_info(VERB_ALGO, "processValidate: state has no "
+			"signer name", &vq->qchase);
+		verbose(VERB_DETAIL, "Could not establish validation of "
+		          "INSECURE status of unsigned response.");
+		errinf(qstate, "no signatures");
+		errinf_origin(qstate, qstate->reply_origin);
+		vq->chase_reply->security = sec_status_bogus;
+		return 1;
+	}
+	subtype = val_classify_response(qstate->query_flags, &qstate->qinfo,
+		&vq->qchase, vq->orig_msg->rep, vq->rrset_skip);
+
+	/* check signatures in the message; 
+	 * answer and authority must be valid, additional is only checked. */
+	if(!validate_msg_signatures(qstate, qstate->env, ve, &vq->qchase, 
+		vq->chase_reply, vq->key_entry)) {
+		/* workaround bad recursor out there that truncates (even
+		 * with EDNS4k) to 512 by removing RRSIG from auth section
+		 * for positive replies*/
+		if((subtype == VAL_CLASS_POSITIVE || subtype == VAL_CLASS_ANY
+			|| subtype == VAL_CLASS_CNAME) &&
+			detect_wrongly_truncated(vq->orig_msg->rep)) {
+			/* truncate the message some more */
+			vq->orig_msg->rep->ns_numrrsets = 0;
+			vq->orig_msg->rep->ar_numrrsets = 0;
+			vq->orig_msg->rep->rrset_count = 
+				vq->orig_msg->rep->an_numrrsets;
+			vq->chase_reply->ns_numrrsets = 0;
+			vq->chase_reply->ar_numrrsets = 0;
+			vq->chase_reply->rrset_count = 
+				vq->chase_reply->an_numrrsets;
+			qstate->errinf = NULL;
+		}
+		else {
+			verbose(VERB_DETAIL, "Validate: message contains "
+				"bad rrsets");
+			return 1;
+		}
+	}
+
+	switch(subtype) {
+		case VAL_CLASS_POSITIVE:
+			verbose(VERB_ALGO, "Validating a positive response");
+			validate_positive_response(qstate->env, ve,
+				&vq->qchase, vq->chase_reply, vq->key_entry);
+			verbose(VERB_DETAIL, "validate(positive): %s",
+			  	sec_status_to_string(
+				vq->chase_reply->security));
+			break;
+			
+		case VAL_CLASS_NODATA:
+			verbose(VERB_ALGO, "Validating a nodata response");
+			validate_nodata_response(qstate->env, ve,
+				&vq->qchase, vq->chase_reply, vq->key_entry);
+			verbose(VERB_DETAIL, "validate(nodata): %s",
+			  	sec_status_to_string(
+				vq->chase_reply->security));
+			break;
+
+		case VAL_CLASS_NAMEERROR:
+			verbose(VERB_ALGO, "Validating a nxdomain response");
+			validate_nameerror_response(qstate->env, ve, 
+				&vq->qchase, vq->chase_reply, vq->key_entry);
+			verbose(VERB_DETAIL, "validate(nxdomain): %s",
+			  	sec_status_to_string(
+				vq->chase_reply->security));
+			break;
+
+		case VAL_CLASS_CNAME:
+			verbose(VERB_ALGO, "Validating a cname response");
+			validate_cname_response(qstate->env, ve,
+				&vq->qchase, vq->chase_reply, vq->key_entry);
+			verbose(VERB_DETAIL, "validate(cname): %s",
+			  	sec_status_to_string(
+				vq->chase_reply->security));
+			break;
+
+		case VAL_CLASS_CNAMENOANSWER:
+			verbose(VERB_ALGO, "Validating a cname noanswer "
+				"response");
+			validate_cname_noanswer_response(qstate->env, ve,
+				&vq->qchase, vq->chase_reply, vq->key_entry);
+			verbose(VERB_DETAIL, "validate(cname_noanswer): %s",
+			  	sec_status_to_string(
+				vq->chase_reply->security));
+			break;
+
+		case VAL_CLASS_REFERRAL:
+			verbose(VERB_ALGO, "Validating a referral response");
+			validate_referral_response(vq->chase_reply);
+			verbose(VERB_DETAIL, "validate(referral): %s",
+			  	sec_status_to_string(
+				vq->chase_reply->security));
+			break;
+
+		case VAL_CLASS_ANY:
+			verbose(VERB_ALGO, "Validating a positive ANY "
+				"response");
+			validate_any_response(qstate->env, ve, &vq->qchase, 
+				vq->chase_reply, vq->key_entry);
+			verbose(VERB_DETAIL, "validate(positive_any): %s",
+			  	sec_status_to_string(
+				vq->chase_reply->security));
+			break;
+
+		default:
+			log_err("validate: unhandled response subtype: %d",
+				subtype);
+	}
+	if(vq->chase_reply->security == sec_status_bogus) {
+		if(subtype == VAL_CLASS_POSITIVE)
+			errinf(qstate, "wildcard");
+		else errinf(qstate, val_classification_to_string(subtype));
+		errinf(qstate, "proof failed");
+		errinf_origin(qstate, qstate->reply_origin);
+	}
+
+	return 1;
+}
+
+/**
+ * Init DLV check.
+ * Called when a query is determined by other trust anchors to be insecure
+ * (or indeterminate).  Then we look if there is a key in the DLV.
+ * Performs aggressive negative cache check to see if there is no key.
+ * Otherwise, spawns a DLV query, and changes to the DLV wait state.
+ *
+ * @param qstate: query state.
+ * @param vq: validator query state.
+ * @param ve: validator shared global environment.
+ * @param id: module id.
+ * @return  true if there is no DLV.
+ * 	false: processing is finished for the validator operate().
+ * 	This function may exit in three ways:
+ *         o	no DLV (agressive cache), so insecure. (true)
+ *         o	error - stop processing (false)
+ *         o	DLV lookup was started, stop processing (false)
+ */
+static int
+val_dlv_init(struct module_qstate* qstate, struct val_qstate* vq, 
+	struct val_env* ve, int id)
+{
+	uint8_t* nm;
+	size_t nm_len;
+	/* there must be a DLV configured */
+	log_assert(qstate->env->anchors->dlv_anchor);
+	/* this bool is true to avoid looping in the DLV checks */
+	log_assert(vq->dlv_checked);
+
+	/* init the DLV lookup variables */
+	vq->dlv_lookup_name = NULL;
+	vq->dlv_lookup_name_len = 0;
+	vq->dlv_insecure_at = NULL;
+	vq->dlv_insecure_at_len = 0;
+
+	/* Determine the name for which we want to lookup DLV.
+	 * This name is for the current message, or 
+	 * for the current RRset for CNAME, referral subtypes.
+	 * If there is a signer, use that, otherwise the domain name */
+	if(vq->signer_name) {
+		nm = vq->signer_name;
+		nm_len = vq->signer_len;
+	} else {
+		/* use qchase */
+		nm = vq->qchase.qname;
+		nm_len = vq->qchase.qname_len;
+		if(vq->qchase.qtype == LDNS_RR_TYPE_DS)
+			dname_remove_label(&nm, &nm_len);
+	}
+	log_nametypeclass(VERB_ALGO, "DLV init look", nm, LDNS_RR_TYPE_DS,
+		vq->qchase.qclass);
+	log_assert(nm && nm_len);
+	/* sanity check: no DLV lookups below the DLV anchor itself.
+	 * Like, an securely insecure delegation there makes no sense. */
+	if(dname_subdomain_c(nm, qstate->env->anchors->dlv_anchor->name)) {
+		verbose(VERB_ALGO, "DLV lookup within DLV repository denied");
+		return 1;
+	}
+	/* concat name (minus root label) + dlv name */
+	vq->dlv_lookup_name_len = nm_len - 1 + 
+		qstate->env->anchors->dlv_anchor->namelen;
+	vq->dlv_lookup_name = regional_alloc(qstate->region, 
+		vq->dlv_lookup_name_len);
+	if(!vq->dlv_lookup_name) {
+		log_err("Out of memory preparing DLV lookup");
+		return val_error(qstate, id);
+	}
+	memmove(vq->dlv_lookup_name, nm, nm_len-1);
+	memmove(vq->dlv_lookup_name+nm_len-1, 
+		qstate->env->anchors->dlv_anchor->name, 
+		qstate->env->anchors->dlv_anchor->namelen);
+	log_nametypeclass(VERB_ALGO, "DLV name", vq->dlv_lookup_name, 
+		LDNS_RR_TYPE_DLV, vq->qchase.qclass);
+
+	/* determine where the insecure point was determined, the DLV must 
+	 * be equal or below that to continue building the trust chain 
+	 * down. May be NULL if no trust chain was built yet */
+	nm = NULL;
+	if(vq->key_entry && key_entry_isnull(vq->key_entry)) {
+		nm = vq->key_entry->name;
+		nm_len = vq->key_entry->namelen;
+	}
+	if(nm) {
+		vq->dlv_insecure_at_len = nm_len - 1 +
+			qstate->env->anchors->dlv_anchor->namelen;
+		vq->dlv_insecure_at = regional_alloc(qstate->region,
+			vq->dlv_insecure_at_len);
+		if(!vq->dlv_insecure_at) {
+			log_err("Out of memory preparing DLV lookup");
+			return val_error(qstate, id);
+		}
+		memmove(vq->dlv_insecure_at, nm, nm_len-1);
+		memmove(vq->dlv_insecure_at+nm_len-1, 
+			qstate->env->anchors->dlv_anchor->name, 
+			qstate->env->anchors->dlv_anchor->namelen);
+		log_nametypeclass(VERB_ALGO, "insecure_at", 
+			vq->dlv_insecure_at, 0, vq->qchase.qclass);
+	}
+
+	/* If we can find the name in the aggressive negative cache,
+	 * give up; insecure is the answer */
+	while(val_neg_dlvlookup(ve->neg_cache, vq->dlv_lookup_name,
+		vq->dlv_lookup_name_len, vq->qchase.qclass,
+		qstate->env->rrset_cache, *qstate->env->now)) {
+		/* go up */
+		dname_remove_label(&vq->dlv_lookup_name, 
+			&vq->dlv_lookup_name_len);
+		/* too high? */
+		if(!dname_subdomain_c(vq->dlv_lookup_name,
+			qstate->env->anchors->dlv_anchor->name)) {
+			verbose(VERB_ALGO, "ask above dlv repo");
+			return 1; /* Above the repo is insecure */
+		}
+		/* above chain of trust? */
+		if(vq->dlv_insecure_at && !dname_subdomain_c(
+			vq->dlv_lookup_name, vq->dlv_insecure_at)) {
+			verbose(VERB_ALGO, "ask above insecure endpoint");
+			return 1;
+		}
+	}
+
+	/* perform a lookup for the DLV; with validation */
+	vq->state = VAL_DLVLOOKUP_STATE;
+	if(!generate_request(qstate, id, vq->dlv_lookup_name, 
+		vq->dlv_lookup_name_len, LDNS_RR_TYPE_DLV,
+		vq->qchase.qclass, 0)) {
+		return val_error(qstate, id);
+	}
+
+	/* Find the closest encloser DLV from the repository.
+	 * then that is used to build another chain of trust 
+	 * This may first require a query 'too low' that has NSECs in
+	 * the answer, from which we determine the closest encloser DLV. 
+	 * When determine the closest encloser, skip empty nonterminals,
+	 * since we want a nonempty node in the DLV repository. */
+
+	return 0;
+}
+
+/**
+ * The Finished state. The validation status (good or bad) has been determined.
+ *
+ * @param qstate: query state.
+ * @param vq: validator query state.
+ * @param ve: validator shared global environment.
+ * @param id: module id.
+ * @return true if the event should be processed further on return, false if
+ *         not.
+ */
+static int
+processFinished(struct module_qstate* qstate, struct val_qstate* vq, 
+	struct val_env* ve, int id)
+{
+	enum val_classification subtype = val_classify_response(
+		qstate->query_flags, &qstate->qinfo, &vq->qchase, 
+		vq->orig_msg->rep, vq->rrset_skip);
+
+	/* if the result is insecure or indeterminate and we have not 
+	 * checked the DLV yet, check the DLV */
+	if((vq->chase_reply->security == sec_status_insecure ||
+		vq->chase_reply->security == sec_status_indeterminate) &&
+		qstate->env->anchors->dlv_anchor && !vq->dlv_checked) {
+		vq->dlv_checked = 1;
+		if(!val_dlv_init(qstate, vq, ve, id))
+			return 0;
+	}
+
+	/* store overall validation result in orig_msg */
+	if(vq->rrset_skip == 0)
+		vq->orig_msg->rep->security = vq->chase_reply->security;
+	else if(vq->rrset_skip < vq->orig_msg->rep->an_numrrsets + 
+		vq->orig_msg->rep->ns_numrrsets) {
+		/* ignore sec status of additional section if a referral 
+		 * type message skips there and
+		 * use the lowest security status as end result. */
+		if(vq->chase_reply->security < vq->orig_msg->rep->security)
+			vq->orig_msg->rep->security = 
+				vq->chase_reply->security;
+	}
+
+	if(subtype == VAL_CLASS_REFERRAL) {
+		/* for a referral, move to next unchecked rrset and check it*/
+		vq->rrset_skip = val_next_unchecked(vq->orig_msg->rep, 
+			vq->rrset_skip);
+		if(vq->rrset_skip < vq->orig_msg->rep->rrset_count) {
+			/* and restart for this rrset */
+			verbose(VERB_ALGO, "validator: go to next rrset");
+			vq->chase_reply->security = sec_status_unchecked;
+			vq->dlv_checked = 0; /* can do DLV for this RR */
+			vq->state = VAL_INIT_STATE;
+			return 1;
+		}
+		/* referral chase is done */
+	}
+	if(vq->chase_reply->security != sec_status_bogus &&
+		subtype == VAL_CLASS_CNAME) {
+		/* chase the CNAME; process next part of the message */
+		if(!val_chase_cname(&vq->qchase, vq->orig_msg->rep, 
+			&vq->rrset_skip)) {
+			verbose(VERB_ALGO, "validator: failed to chase CNAME");
+			vq->orig_msg->rep->security = sec_status_bogus;
+		} else {
+			/* restart process for new qchase at rrset_skip */
+			log_query_info(VERB_ALGO, "validator: chased to",
+				&vq->qchase);
+			vq->chase_reply->security = sec_status_unchecked;
+			vq->dlv_checked = 0; /* can do DLV for this RR */
+			vq->state = VAL_INIT_STATE;
+			return 1;
+		}
+	}
+
+	if(vq->orig_msg->rep->security == sec_status_secure) {
+		/* If the message is secure, check that all rrsets are
+		 * secure (i.e. some inserted RRset for CNAME chain with
+		 * a different signer name). And drop additional rrsets
+		 * that are not secure (if clean-additional option is set) */
+		/* this may cause the msg to be marked bogus */
+		val_check_nonsecure(ve, vq->orig_msg->rep);
+		if(vq->orig_msg->rep->security == sec_status_secure) {
+			log_query_info(VERB_DETAIL, "validation success", 
+				&qstate->qinfo);
+		}
+	}
+
+	/* if the result is bogus - set message ttl to bogus ttl to avoid
+	 * endless bogus revalidation */
+	if(vq->orig_msg->rep->security == sec_status_bogus) {
+		/* see if we can try again to fetch data */
+		if(vq->restart_count < VAL_MAX_RESTART_COUNT) {
+			int restart_count = vq->restart_count+1;
+			verbose(VERB_ALGO, "validation failed, "
+				"blacklist and retry to fetch data");
+			val_blacklist(&qstate->blacklist, qstate->region, 
+				qstate->reply_origin, 0);
+			qstate->reply_origin = NULL;
+			qstate->errinf = NULL;
+			memset(vq, 0, sizeof(*vq));
+			vq->restart_count = restart_count;
+			vq->state = VAL_INIT_STATE;
+			verbose(VERB_ALGO, "pass back to next module");
+			qstate->ext_state[id] = module_restart_next;
+			return 0;
+		}
+
+		vq->orig_msg->rep->ttl = ve->bogus_ttl;
+		vq->orig_msg->rep->prefetch_ttl = 
+			PREFETCH_TTL_CALC(vq->orig_msg->rep->ttl);
+		if(qstate->env->cfg->val_log_level >= 1 &&
+			!qstate->env->cfg->val_log_squelch) {
+			if(qstate->env->cfg->val_log_level < 2)
+				log_query_info(0, "validation failure",
+					&qstate->qinfo);
+			else {
+				char* err = errinf_to_str(qstate);
+				if(err) log_info("%s", err);
+				free(err);
+			}
+		}
+		/* If we are in permissive mode, bogus gets indeterminate */
+		if(ve->permissive_mode)
+			vq->orig_msg->rep->security = sec_status_indeterminate;
+	}
+
+	/* store results in cache */
+	if(qstate->query_flags&BIT_RD) {
+		if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo, 
+			vq->orig_msg->rep, 0, qstate->prefetch_leeway, NULL)) {
+			log_err("out of memory caching validator results");
+		}
+	} else {
+		/* for a referral, store the verified RRsets */
+		/* and this does not get prefetched, so no leeway */
+		if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo, 
+			vq->orig_msg->rep, 1, 0, NULL)) {
+			log_err("out of memory caching validator results");
+		}
+	}
+	qstate->return_rcode = LDNS_RCODE_NOERROR;
+	qstate->return_msg = vq->orig_msg;
+	qstate->ext_state[id] = module_finished;
+	return 0;
+}
+
+/**
+ * The DLVLookup state. Process DLV lookups.
+ *
+ * @param qstate: query state.
+ * @param vq: validator query state.
+ * @param ve: validator shared global environment.
+ * @param id: module id.
+ * @return true if the event should be processed further on return, false if
+ *         not.
+ */
+static int
+processDLVLookup(struct module_qstate* qstate, struct val_qstate* vq, 
+	struct val_env* ve, int id)
+{
+	/* see if this we are ready to continue normal resolution */
+	/* we may need more DLV lookups */
+	if(vq->dlv_status==dlv_error)
+		verbose(VERB_ALGO, "DLV woke up with status dlv_error");
+	else if(vq->dlv_status==dlv_success)
+		verbose(VERB_ALGO, "DLV woke up with status dlv_success");
+	else if(vq->dlv_status==dlv_ask_higher)
+		verbose(VERB_ALGO, "DLV woke up with status dlv_ask_higher");
+	else if(vq->dlv_status==dlv_there_is_no_dlv)
+		verbose(VERB_ALGO, "DLV woke up with status dlv_there_is_no_dlv");
+	else 	verbose(VERB_ALGO, "DLV woke up with status unknown");
+
+	if(vq->dlv_status == dlv_error) {
+		verbose(VERB_QUERY, "failed DLV lookup");
+		return val_error(qstate, id);
+	} else if(vq->dlv_status == dlv_success) {
+		uint8_t* nm;
+		size_t nmlen;
+		/* chain continues with DNSKEY, continue in FINDKEY */
+		vq->state = VAL_FINDKEY_STATE;
+
+		/* strip off the DLV suffix from the name; could result in . */
+		log_assert(dname_subdomain_c(vq->ds_rrset->rk.dname,
+			qstate->env->anchors->dlv_anchor->name));
+		nmlen = vq->ds_rrset->rk.dname_len -
+			qstate->env->anchors->dlv_anchor->namelen + 1;
+		nm = regional_alloc_init(qstate->region, 
+			vq->ds_rrset->rk.dname, nmlen);
+		if(!nm) {
+			log_err("Out of memory in DLVLook");
+			return val_error(qstate, id);
+		}
+		nm[nmlen-1] = 0;
+
+		vq->ds_rrset->rk.dname = nm;
+		vq->ds_rrset->rk.dname_len = nmlen;
+
+		/* create a nullentry for the key so the dnskey lookup
+		 * can be retried after a validation failure for it */
+		vq->key_entry = key_entry_create_null(qstate->region,
+			nm, nmlen, vq->qchase.qclass, 0, 0);
+		if(!vq->key_entry) {
+			log_err("Out of memory in DLVLook");
+			return val_error(qstate, id);
+		}
+
+		if(!generate_request(qstate, id, vq->ds_rrset->rk.dname, 
+			vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY, 
+			vq->qchase.qclass, BIT_CD)) {
+			log_err("mem error generating DNSKEY request");
+			return val_error(qstate, id);
+		}
+		return 0;
+	} else if(vq->dlv_status == dlv_there_is_no_dlv) {
+		/* continue with the insecure result we got */
+		vq->state = VAL_FINISHED_STATE;
+		return 1;
+	} 
+	log_assert(vq->dlv_status == dlv_ask_higher);
+
+	/* ask higher, make sure we stay in DLV repo, below dlv_at */
+	if(!dname_subdomain_c(vq->dlv_lookup_name,
+		qstate->env->anchors->dlv_anchor->name)) {
+		/* just like, there is no DLV */
+		verbose(VERB_ALGO, "ask above dlv repo");
+		vq->state = VAL_FINISHED_STATE;
+		return 1;
+	}
+	if(vq->dlv_insecure_at && !dname_subdomain_c(vq->dlv_lookup_name,
+		vq->dlv_insecure_at)) {
+		/* already checked a chain lower than dlv_lookup_name */
+		verbose(VERB_ALGO, "ask above insecure endpoint");
+		log_nametypeclass(VERB_ALGO, "enpt", vq->dlv_insecure_at, 0, 0);
+		vq->state = VAL_FINISHED_STATE;
+		return 1;
+	}
+
+	/* check negative cache before making new request */
+	if(val_neg_dlvlookup(ve->neg_cache, vq->dlv_lookup_name,
+		vq->dlv_lookup_name_len, vq->qchase.qclass,
+		qstate->env->rrset_cache, *qstate->env->now)) {
+		/* does not exist, go up one (go higher). */
+		dname_remove_label(&vq->dlv_lookup_name, 
+			&vq->dlv_lookup_name_len);
+		/* limit number of labels, limited number of recursion */
+		return processDLVLookup(qstate, vq, ve, id);
+	}
+
+	if(!generate_request(qstate, id, vq->dlv_lookup_name,
+		vq->dlv_lookup_name_len, LDNS_RR_TYPE_DLV, 
+		vq->qchase.qclass, 0)) {
+		return val_error(qstate, id);
+	}
+
+	return 0;
+}
+
+/** 
+ * Handle validator state.
+ * If a method returns true, the next state is started. If false, then
+ * processing will stop.
+ * @param qstate: query state.
+ * @param vq: validator query state.
+ * @param ve: validator shared global environment.
+ * @param id: module id.
+ */
+static void
+val_handle(struct module_qstate* qstate, struct val_qstate* vq, 
+	struct val_env* ve, int id)
+{
+	int cont = 1;
+	while(cont) {
+		verbose(VERB_ALGO, "val handle processing q with state %s",
+			val_state_to_string(vq->state));
+		switch(vq->state) {
+			case VAL_INIT_STATE:
+				cont = processInit(qstate, vq, ve, id);
+				break;
+			case VAL_FINDKEY_STATE: 
+				cont = processFindKey(qstate, vq, id);
+				break;
+			case VAL_VALIDATE_STATE: 
+				cont = processValidate(qstate, vq, ve, id);
+				break;
+			case VAL_FINISHED_STATE: 
+				cont = processFinished(qstate, vq, ve, id);
+				break;
+			case VAL_DLVLOOKUP_STATE: 
+				cont = processDLVLookup(qstate, vq, ve, id);
+				break;
+			default:
+				log_warn("validator: invalid state %d",
+					vq->state);
+				cont = 0;
+				break;
+		}
+	}
+}
+
+void
+val_operate(struct module_qstate* qstate, enum module_ev event, int id,
+        struct outbound_entry* outbound)
+{
+	struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
+	struct val_qstate* vq = (struct val_qstate*)qstate->minfo[id];
+	verbose(VERB_QUERY, "validator[module %d] operate: extstate:%s "
+		"event:%s", id, strextstate(qstate->ext_state[id]), 
+		strmodulevent(event));
+	log_query_info(VERB_QUERY, "validator operate: query",
+		&qstate->qinfo);
+	if(vq && qstate->qinfo.qname != vq->qchase.qname) 
+		log_query_info(VERB_QUERY, "validator operate: chased to",
+		&vq->qchase);
+	(void)outbound;
+	if(event == module_event_new || 
+		(event == module_event_pass && vq == NULL)) {
+		/* pass request to next module, to get it */
+		verbose(VERB_ALGO, "validator: pass to next module");
+		qstate->ext_state[id] = module_wait_module;
+		return;
+	}
+	if(event == module_event_moddone) {
+		/* check if validation is needed */
+		verbose(VERB_ALGO, "validator: nextmodule returned");
+		if(!needs_validation(qstate, qstate->return_rcode, 
+			qstate->return_msg)) {
+			/* no need to validate this */
+			if(qstate->return_msg)
+				qstate->return_msg->rep->security =
+					sec_status_indeterminate;
+			qstate->ext_state[id] = module_finished;
+			return;
+		}
+		if(already_validated(qstate->return_msg)) {
+			qstate->ext_state[id] = module_finished;
+			return;
+		}
+		/* qclass ANY should have validation result from spawned 
+		 * queries. If we get here, it is bogus or an internal error */
+		if(qstate->qinfo.qclass == LDNS_RR_CLASS_ANY) {
+			verbose(VERB_ALGO, "cannot validate classANY: bogus");
+			if(qstate->return_msg)
+				qstate->return_msg->rep->security =
+					sec_status_bogus;
+			qstate->ext_state[id] = module_finished;
+			return;
+		}
+		/* create state to start validation */
+		qstate->ext_state[id] = module_error; /* override this */
+		if(!vq) {
+			vq = val_new(qstate, id);
+			if(!vq) {
+				log_err("validator: malloc failure");
+				qstate->ext_state[id] = module_error;
+				return;
+			}
+		} else if(!vq->orig_msg) {
+			if(!val_new_getmsg(qstate, vq)) {
+				log_err("validator: malloc failure");
+				qstate->ext_state[id] = module_error;
+				return;
+			}
+		}
+		val_handle(qstate, vq, ve, id);
+		return;
+	}
+	if(event == module_event_pass) {
+		qstate->ext_state[id] = module_error; /* override this */
+		/* continue processing, since val_env exists */
+		val_handle(qstate, vq, ve, id);
+		return;
+	}
+	log_err("validator: bad event %s", strmodulevent(event));
+	qstate->ext_state[id] = module_error;
+	return;
+}
+
+/**
+ * Evaluate the response to a priming request.
+ *
+ * @param dnskey_rrset: DNSKEY rrset (can be NULL if none) in prime reply.
+ * 	(this rrset is allocated in the wrong region, not the qstate).
+ * @param ta: trust anchor.
+ * @param qstate: qstate that needs key.
+ * @param id: module id.
+ * @return new key entry or NULL on allocation failure.
+ *	The key entry will either contain a validated DNSKEY rrset, or
+ *	represent a Null key (query failed, but validation did not), or a
+ *	Bad key (validation failed).
+ */
+static struct key_entry_key*
+primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset, 
+	struct trust_anchor* ta, struct module_qstate* qstate, int id)
+{
+	struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
+	struct key_entry_key* kkey = NULL;
+	enum sec_status sec = sec_status_unchecked;
+	char* reason = NULL;
+	int downprot = 1;
+
+	if(!dnskey_rrset) {
+		log_nametypeclass(VERB_OPS, "failed to prime trust anchor -- "
+			"could not fetch DNSKEY rrset", 
+			ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass);
+		if(qstate->env->cfg->harden_dnssec_stripped) {
+			errinf(qstate, "no DNSKEY rrset");
+			kkey = key_entry_create_bad(qstate->region, ta->name,
+				ta->namelen, ta->dclass, BOGUS_KEY_TTL,
+				*qstate->env->now);
+		} else 	kkey = key_entry_create_null(qstate->region, ta->name,
+				ta->namelen, ta->dclass, NULL_KEY_TTL,
+				*qstate->env->now);
+		if(!kkey) {
+			log_err("out of memory: allocate fail prime key");
+			return NULL;
+		}
+		return kkey;
+	}
+	/* attempt to verify with trust anchor DS and DNSKEY */
+	kkey = val_verify_new_DNSKEYs_with_ta(qstate->region, qstate->env, ve, 
+		dnskey_rrset, ta->ds_rrset, ta->dnskey_rrset, downprot,
+		&reason);
+	if(!kkey) {
+		log_err("out of memory: verifying prime TA");
+		return NULL;
+	}
+	if(key_entry_isgood(kkey))
+		sec = sec_status_secure;
+	else
+		sec = sec_status_bogus;
+	verbose(VERB_DETAIL, "validate keys with anchor(DS): %s", 
+		sec_status_to_string(sec));
+
+	if(sec != sec_status_secure) {
+		log_nametypeclass(VERB_OPS, "failed to prime trust anchor -- "
+			"DNSKEY rrset is not secure", 
+			ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass);
+		/* NOTE: in this case, we should probably reject the trust 
+		 * anchor for longer, perhaps forever. */
+		if(qstate->env->cfg->harden_dnssec_stripped) {
+			errinf(qstate, reason);
+			kkey = key_entry_create_bad(qstate->region, ta->name,
+				ta->namelen, ta->dclass, BOGUS_KEY_TTL,
+				*qstate->env->now);
+		} else 	kkey = key_entry_create_null(qstate->region, ta->name,
+				ta->namelen, ta->dclass, NULL_KEY_TTL,
+				*qstate->env->now);
+		if(!kkey) {
+			log_err("out of memory: allocate null prime key");
+			return NULL;
+		}
+		return kkey;
+	}
+
+	log_nametypeclass(VERB_DETAIL, "Successfully primed trust anchor", 
+		ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass);
+	return kkey;
+}
+
+/**
+ * In inform supers, with the resulting message and rcode and the current
+ * keyset in the super state, validate the DS response, returning a KeyEntry.
+ *
+ * @param qstate: query state that is validating and asked for a DS.
+ * @param vq: validator query state
+ * @param id: module id.
+ * @param rcode: rcode result value.
+ * @param msg: result message (if rcode is OK).
+ * @param qinfo: from the sub query state, query info.
+ * @param ke: the key entry to return. It returns
+ *	is_bad if the DS response fails to validate, is_null if the
+ *	DS response indicated an end to secure space, is_good if the DS
+ *	validated. It returns ke=NULL if the DS response indicated that the
+ *	request wasn't a delegation point.
+ * @return 0 on servfail error (malloc failure).
+ */
+static int
+ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
+        int id, int rcode, struct dns_msg* msg, struct query_info* qinfo,
+	struct key_entry_key** ke)
+{
+	struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
+	char* reason = NULL;
+	enum val_classification subtype;
+	if(rcode != LDNS_RCODE_NOERROR) {
+		char* rc = ldns_pkt_rcode2str(rcode);
+		/* errors here pretty much break validation */
+		verbose(VERB_DETAIL, "DS response was error, thus bogus");
+		errinf(qstate, rc);
+		errinf(qstate, "no DS");
+		free(rc);
+		goto return_bogus;
+	}
+
+	subtype = val_classify_response(BIT_RD, qinfo, qinfo, msg->rep, 0);
+	if(subtype == VAL_CLASS_POSITIVE) {
+		struct ub_packed_rrset_key* ds;
+		enum sec_status sec;
+		ds = reply_find_answer_rrset(qinfo, msg->rep);
+		/* If there was no DS rrset, then we have mis-classified 
+		 * this message. */
+		if(!ds) {
+			log_warn("internal error: POSITIVE DS response was "
+				"missing DS.");
+			errinf(qstate, "no DS record");
+			goto return_bogus;
+		}
+		/* Verify only returns BOGUS or SECURE. If the rrset is 
+		 * bogus, then we are done. */
+		sec = val_verify_rrset_entry(qstate->env, ve, ds, 
+			vq->key_entry, &reason);
+		if(sec != sec_status_secure) {
+			verbose(VERB_DETAIL, "DS rrset in DS response did "
+				"not verify");
+			errinf(qstate, reason);
+			goto return_bogus;
+		}
+
+		/* If the DS rrset validates, we still have to make sure 
+		 * that they are usable. */
+		if(!val_dsset_isusable(ds)) {
+			/* If they aren't usable, then we treat it like 
+			 * there was no DS. */
+			*ke = key_entry_create_null(qstate->region, 
+				qinfo->qname, qinfo->qname_len, qinfo->qclass, 
+				ub_packed_rrset_ttl(ds), *qstate->env->now);
+			return (*ke) != NULL;
+		}
+
+		/* Otherwise, we return the positive response. */
+		log_query_info(VERB_DETAIL, "validated DS", qinfo);
+		*ke = key_entry_create_rrset(qstate->region,
+			qinfo->qname, qinfo->qname_len, qinfo->qclass, ds,
+			NULL, *qstate->env->now);
+		return (*ke) != NULL;
+	} else if(subtype == VAL_CLASS_NODATA || 
+		subtype == VAL_CLASS_NAMEERROR) {
+		/* NODATA means that the qname exists, but that there was 
+		 * no DS.  This is a pretty normal case. */
+		uint32_t proof_ttl = 0;
+		enum sec_status sec;
+
+		/* make sure there are NSECs or NSEC3s with signatures */
+		if(!val_has_signed_nsecs(msg->rep, &reason)) {
+			verbose(VERB_ALGO, "no NSECs: %s", reason);
+			errinf(qstate, reason);
+			goto return_bogus;
+		}
+
+		/* For subtype Name Error.
+		 * attempt ANS 2.8.1.0 compatibility where it sets rcode
+		 * to nxdomain, but really this is an Nodata/Noerror response.
+		 * Find and prove the empty nonterminal in that case */
+
+		/* Try to prove absence of the DS with NSEC */
+		sec = val_nsec_prove_nodata_dsreply(
+			qstate->env, ve, qinfo, msg->rep, vq->key_entry, 
+			&proof_ttl, &reason);
+		switch(sec) {
+			case sec_status_secure:
+				verbose(VERB_DETAIL, "NSEC RRset for the "
+					"referral proved no DS.");
+				*ke = key_entry_create_null(qstate->region, 
+					qinfo->qname, qinfo->qname_len, 
+					qinfo->qclass, proof_ttl,
+					*qstate->env->now);
+				return (*ke) != NULL;
+			case sec_status_insecure:
+				verbose(VERB_DETAIL, "NSEC RRset for the "
+				  "referral proved not a delegation point");
+				*ke = NULL;
+				return 1;
+			case sec_status_bogus:
+				verbose(VERB_DETAIL, "NSEC RRset for the "
+					"referral did not prove no DS.");
+				errinf(qstate, reason);
+				goto return_bogus;
+			case sec_status_unchecked:
+			default:
+				/* NSEC proof did not work, try next */
+				break;
+		}
+
+		sec = nsec3_prove_nods(qstate->env, ve, 
+			msg->rep->rrsets + msg->rep->an_numrrsets,
+			msg->rep->ns_numrrsets, qinfo, vq->key_entry, &reason);
+		switch(sec) {
+			case sec_status_insecure:
+				/* case insecure also continues to unsigned
+				 * space.  If nsec3-iter-count too high or
+				 * optout, then treat below as unsigned */
+			case sec_status_secure:
+				verbose(VERB_DETAIL, "NSEC3s for the "
+					"referral proved no DS.");
+				*ke = key_entry_create_null(qstate->region, 
+					qinfo->qname, qinfo->qname_len, 
+					qinfo->qclass, proof_ttl,
+					*qstate->env->now);
+				return (*ke) != NULL;
+			case sec_status_indeterminate:
+				verbose(VERB_DETAIL, "NSEC3s for the "
+				  "referral proved no delegation");
+				*ke = NULL;
+				return 1;
+			case sec_status_bogus:
+				verbose(VERB_DETAIL, "NSEC3s for the "
+					"referral did not prove no DS.");
+				errinf(qstate, reason);
+				goto return_bogus;
+			case sec_status_unchecked:
+			default:
+				/* NSEC3 proof did not work */
+				break;
+		}
+
+		/* Apparently, no available NSEC/NSEC3 proved NODATA, so 
+		 * this is BOGUS. */
+		verbose(VERB_DETAIL, "DS %s ran out of options, so return "
+			"bogus", val_classification_to_string(subtype));
+		errinf(qstate, "no DS but also no proof of that");
+		goto return_bogus;
+	} else if(subtype == VAL_CLASS_CNAME || 
+		subtype == VAL_CLASS_CNAMENOANSWER) {
+		/* if the CNAME matches the exact name we want and is signed
+		 * properly, then also, we are sure that no DS exists there,
+		 * much like a NODATA proof */
+		enum sec_status sec;
+		struct ub_packed_rrset_key* cname;
+		cname = reply_find_rrset_section_an(msg->rep, qinfo->qname,
+			qinfo->qname_len, LDNS_RR_TYPE_CNAME, qinfo->qclass);
+		if(!cname) {
+			errinf(qstate, "validator classified CNAME but no "
+				"CNAME of the queried name for DS");
+			goto return_bogus;
+		}
+		if(((struct packed_rrset_data*)cname->entry.data)->rrsig_count
+			== 0) {
+		        if(msg->rep->an_numrrsets != 0 && ntohs(msg->rep->
+				rrsets[0]->rk.type)==LDNS_RR_TYPE_DNAME) {
+				errinf(qstate, "DS got DNAME answer");
+			} else {
+				errinf(qstate, "DS got unsigned CNAME answer");
+			}
+			goto return_bogus;
+		}
+		sec = val_verify_rrset_entry(qstate->env, ve, cname, 
+			vq->key_entry, &reason);
+		if(sec == sec_status_secure) {
+			verbose(VERB_ALGO, "CNAME validated, "
+				"proof that DS does not exist");
+			/* and that it is not a referral point */
+			*ke = NULL;
+			return 1;
+		}
+		errinf(qstate, "CNAME in DS response was not secure.");
+		errinf(qstate, reason);
+		goto return_bogus;
+	} else {
+		verbose(VERB_QUERY, "Encountered an unhandled type of "
+			"DS response, thus bogus.");
+		errinf(qstate, "no DS and");
+		if(FLAGS_GET_RCODE(msg->rep->flags) != LDNS_RCODE_NOERROR) {
+			char* rc = ldns_pkt_rcode2str(
+				FLAGS_GET_RCODE(msg->rep->flags));
+			errinf(qstate, rc);
+			free(rc);
+		} else	errinf(qstate, val_classification_to_string(subtype));
+		errinf(qstate, "message fails to prove that");
+		goto return_bogus;
+	}
+return_bogus:
+	*ke = key_entry_create_bad(qstate->region, qinfo->qname,
+		qinfo->qname_len, qinfo->qclass, 
+		BOGUS_KEY_TTL, *qstate->env->now);
+	return (*ke) != NULL;
+}
+
+/**
+ * Process DS response. Called from inform_supers.
+ * Because it is in inform_supers, the mesh itself is busy doing callbacks
+ * for a state that is to be deleted soon; don't touch the mesh; instead
+ * set a state in the super, as the super will be reactivated soon.
+ * Perform processing to determine what state to set in the super.
+ *
+ * @param qstate: query state that is validating and asked for a DS.
+ * @param vq: validator query state
+ * @param id: module id.
+ * @param rcode: rcode result value.
+ * @param msg: result message (if rcode is OK).
+ * @param qinfo: from the sub query state, query info.
+ * @param origin: the origin of msg.
+ */
+static void
+process_ds_response(struct module_qstate* qstate, struct val_qstate* vq,
+	int id, int rcode, struct dns_msg* msg, struct query_info* qinfo,
+	struct sock_list* origin)
+{
+	struct key_entry_key* dske = NULL;
+	uint8_t* olds = vq->empty_DS_name;
+	vq->empty_DS_name = NULL;
+	if(!ds_response_to_ke(qstate, vq, id, rcode, msg, qinfo, &dske)) {
+			log_err("malloc failure in process_ds_response");
+			vq->key_entry = NULL; /* make it error */
+			vq->state = VAL_VALIDATE_STATE;
+			return;
+	}
+	if(dske == NULL) {
+		vq->empty_DS_name = regional_alloc_init(qstate->region,
+			qinfo->qname, qinfo->qname_len);
+		if(!vq->empty_DS_name) {
+			log_err("malloc failure in empty_DS_name");
+			vq->key_entry = NULL; /* make it error */
+			vq->state = VAL_VALIDATE_STATE;
+			return;
+		}
+		vq->empty_DS_len = qinfo->qname_len;
+		vq->chain_blacklist = NULL;
+		/* ds response indicated that we aren't on a delegation point.
+		 * Keep the forState.state on FINDKEY. */
+	} else if(key_entry_isgood(dske)) {
+		vq->ds_rrset = key_entry_get_rrset(dske, qstate->region);
+		if(!vq->ds_rrset) {
+			log_err("malloc failure in process DS");
+			vq->key_entry = NULL; /* make it error */
+			vq->state = VAL_VALIDATE_STATE;
+			return;
+		}
+		vq->chain_blacklist = NULL; /* fresh blacklist for next part*/
+		/* Keep the forState.state on FINDKEY. */
+	} else if(key_entry_isbad(dske) 
+		&& vq->restart_count < VAL_MAX_RESTART_COUNT) {
+		vq->empty_DS_name = olds;
+		val_blacklist(&vq->chain_blacklist, qstate->region, origin, 1);
+		qstate->errinf = NULL;
+		vq->restart_count++;
+	} else {
+		if(key_entry_isbad(dske)) {
+			errinf_origin(qstate, origin);
+			errinf_dname(qstate, "for DS", qinfo->qname);
+		}
+		/* NOTE: the reason for the DS to be not good (that is, 
+		 * either bad or null) should have been logged by 
+		 * dsResponseToKE. */
+		vq->key_entry = dske;
+		/* The FINDKEY phase has ended, so move on. */
+		vq->state = VAL_VALIDATE_STATE;
+	}
+}
+
+/**
+ * Process DNSKEY response. Called from inform_supers.
+ * Sets the key entry in the state.
+ * Because it is in inform_supers, the mesh itself is busy doing callbacks
+ * for a state that is to be deleted soon; don't touch the mesh; instead
+ * set a state in the super, as the super will be reactivated soon.
+ * Perform processing to determine what state to set in the super.
+ *
+ * @param qstate: query state that is validating and asked for a DNSKEY.
+ * @param vq: validator query state
+ * @param id: module id.
+ * @param rcode: rcode result value.
+ * @param msg: result message (if rcode is OK).
+ * @param qinfo: from the sub query state, query info.
+ * @param origin: the origin of msg.
+ */
+static void
+process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq,
+	int id, int rcode, struct dns_msg* msg, struct query_info* qinfo,
+	struct sock_list* origin)
+{
+	struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
+	struct key_entry_key* old = vq->key_entry;
+	struct ub_packed_rrset_key* dnskey = NULL;
+	int downprot;
+	char* reason = NULL;
+
+	if(rcode == LDNS_RCODE_NOERROR)
+		dnskey = reply_find_answer_rrset(qinfo, msg->rep);
+
+	if(dnskey == NULL) {
+		/* bad response */
+		verbose(VERB_DETAIL, "Missing DNSKEY RRset in response to "
+			"DNSKEY query.");
+		if(vq->restart_count < VAL_MAX_RESTART_COUNT) {
+			val_blacklist(&vq->chain_blacklist, qstate->region,
+				origin, 1);
+			qstate->errinf = NULL;
+			vq->restart_count++;
+			return;
+		}
+		vq->key_entry = key_entry_create_bad(qstate->region, 
+			qinfo->qname, qinfo->qname_len, qinfo->qclass,
+			BOGUS_KEY_TTL, *qstate->env->now);
+		if(!vq->key_entry) {
+			log_err("alloc failure in missing dnskey response");
+			/* key_entry is NULL for failure in Validate */
+		}
+		errinf(qstate, "No DNSKEY record");
+		errinf_origin(qstate, origin);
+		errinf_dname(qstate, "for key", qinfo->qname);
+		vq->state = VAL_VALIDATE_STATE;
+		return;
+	}
+	if(!vq->ds_rrset) {
+		log_err("internal error: no DS rrset for new DNSKEY response");
+		vq->key_entry = NULL;
+		vq->state = VAL_VALIDATE_STATE;
+		return;
+	}
+	downprot = 1;
+	vq->key_entry = val_verify_new_DNSKEYs(qstate->region, qstate->env,
+		ve, dnskey, vq->ds_rrset, downprot, &reason);
+
+	if(!vq->key_entry) {
+		log_err("out of memory in verify new DNSKEYs");
+		vq->state = VAL_VALIDATE_STATE;
+		return;
+	}
+	/* If the key entry isBad or isNull, then we can move on to the next
+	 * state. */
+	if(!key_entry_isgood(vq->key_entry)) {
+		if(key_entry_isbad(vq->key_entry)) {
+			if(vq->restart_count < VAL_MAX_RESTART_COUNT) {
+				val_blacklist(&vq->chain_blacklist, 
+					qstate->region, origin, 1);
+				qstate->errinf = NULL;
+				vq->restart_count++;
+				vq->key_entry = old;
+				return;
+			}
+			verbose(VERB_DETAIL, "Did not match a DS to a DNSKEY, "
+				"thus bogus.");
+			errinf(qstate, reason);
+			errinf_origin(qstate, origin);
+			errinf_dname(qstate, "for key", qinfo->qname);
+		}
+		vq->chain_blacklist = NULL;
+		vq->state = VAL_VALIDATE_STATE;
+		return;
+	}
+	vq->chain_blacklist = NULL;
+	qstate->errinf = NULL;
+
+	/* The DNSKEY validated, so cache it as a trusted key rrset. */
+	key_cache_insert(ve->kcache, vq->key_entry, qstate);
+
+	/* If good, we stay in the FINDKEY state. */
+	log_query_info(VERB_DETAIL, "validated DNSKEY", qinfo);
+}
+
+/**
+ * Process prime response
+ * Sets the key entry in the state.
+ *
+ * @param qstate: query state that is validating and primed a trust anchor.
+ * @param vq: validator query state
+ * @param id: module id.
+ * @param rcode: rcode result value.
+ * @param msg: result message (if rcode is OK).
+ * @param origin: the origin of msg.
+ */
+static void
+process_prime_response(struct module_qstate* qstate, struct val_qstate* vq,
+	int id, int rcode, struct dns_msg* msg, struct sock_list* origin)
+{
+	struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
+	struct ub_packed_rrset_key* dnskey_rrset = NULL;
+	struct trust_anchor* ta = anchor_find(qstate->env->anchors, 
+		vq->trust_anchor_name, vq->trust_anchor_labs,
+		vq->trust_anchor_len, vq->qchase.qclass);
+	if(!ta) {
+		/* trust anchor revoked, restart with less anchors */
+		vq->state = VAL_INIT_STATE;
+		if(!vq->trust_anchor_name)
+			vq->state = VAL_VALIDATE_STATE; /* break a loop */
+		vq->trust_anchor_name = NULL;
+		return;
+	}
+	/* Fetch and validate the keyEntry that corresponds to the 
+	 * current trust anchor. */
+	if(rcode == LDNS_RCODE_NOERROR) {
+		dnskey_rrset = reply_find_rrset_section_an(msg->rep,
+			ta->name, ta->namelen, LDNS_RR_TYPE_DNSKEY,
+			ta->dclass);
+	}
+	if(ta->autr) {
+		if(!autr_process_prime(qstate->env, ve, ta, dnskey_rrset)) {
+			/* trust anchor revoked, restart with less anchors */
+			vq->state = VAL_INIT_STATE;
+			vq->trust_anchor_name = NULL;
+			return;
+		}
+	}
+	vq->key_entry = primeResponseToKE(dnskey_rrset, ta, qstate, id);
+	lock_basic_unlock(&ta->lock);
+	if(vq->key_entry) {
+		if(key_entry_isbad(vq->key_entry) 
+			&& vq->restart_count < VAL_MAX_RESTART_COUNT) {
+			val_blacklist(&vq->chain_blacklist, qstate->region, 
+				origin, 1);
+			qstate->errinf = NULL;
+			vq->restart_count++;
+			vq->key_entry = NULL;
+			vq->state = VAL_INIT_STATE;
+			return;
+		} 
+		vq->chain_blacklist = NULL;
+		errinf_origin(qstate, origin);
+		errinf_dname(qstate, "for trust anchor", ta->name);
+		/* store the freshly primed entry in the cache */
+		key_cache_insert(ve->kcache, vq->key_entry, qstate);
+	}
+
+	/* If the result of the prime is a null key, skip the FINDKEY state.*/
+	if(!vq->key_entry || key_entry_isnull(vq->key_entry) ||
+		key_entry_isbad(vq->key_entry)) {
+		vq->state = VAL_VALIDATE_STATE;
+	}
+	/* the qstate will be reactivated after inform_super is done */
+}
+
+/**
+ * Process DLV response. Called from inform_supers.
+ * Because it is in inform_supers, the mesh itself is busy doing callbacks
+ * for a state that is to be deleted soon; don't touch the mesh; instead
+ * set a state in the super, as the super will be reactivated soon.
+ * Perform processing to determine what state to set in the super.
+ *
+ * @param qstate: query state that is validating and asked for a DLV.
+ * @param vq: validator query state
+ * @param id: module id.
+ * @param rcode: rcode result value.
+ * @param msg: result message (if rcode is OK).
+ * @param qinfo: from the sub query state, query info.
+ */
+static void
+process_dlv_response(struct module_qstate* qstate, struct val_qstate* vq,
+	int id, int rcode, struct dns_msg* msg, struct query_info* qinfo)
+{
+	struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
+
+	verbose(VERB_ALGO, "process dlv response to super");
+	if(rcode != LDNS_RCODE_NOERROR) {
+		/* lookup failed, set in vq to give up */
+		vq->dlv_status = dlv_error;
+		verbose(VERB_ALGO, "response is error");
+		return;
+	}
+	if(msg->rep->security != sec_status_secure) {
+		vq->dlv_status = dlv_error;
+		verbose(VERB_ALGO, "response is not secure, %s",
+			sec_status_to_string(msg->rep->security));
+		return;
+	}
+	/* was the lookup a success? validated DLV? */
+	if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_NOERROR &&
+		msg->rep->an_numrrsets == 1 &&
+		msg->rep->security == sec_status_secure &&
+		ntohs(msg->rep->rrsets[0]->rk.type) == LDNS_RR_TYPE_DLV &&
+		ntohs(msg->rep->rrsets[0]->rk.rrset_class) == qinfo->qclass &&
+		query_dname_compare(msg->rep->rrsets[0]->rk.dname, 
+			vq->dlv_lookup_name) == 0) {
+		/* yay! it is just like a DS */
+		vq->ds_rrset = (struct ub_packed_rrset_key*)
+			regional_alloc_init(qstate->region,
+			msg->rep->rrsets[0], sizeof(*vq->ds_rrset));
+		if(!vq->ds_rrset) {
+			log_err("out of memory in process_dlv");
+			return;
+		}
+		vq->ds_rrset->entry.key = vq->ds_rrset;
+		vq->ds_rrset->rk.dname = (uint8_t*)regional_alloc_init(
+			qstate->region, vq->ds_rrset->rk.dname, 
+			vq->ds_rrset->rk.dname_len);
+		if(!vq->ds_rrset->rk.dname) {
+			log_err("out of memory in process_dlv");
+			vq->dlv_status = dlv_error;
+			return;
+		}
+		vq->ds_rrset->entry.data = regional_alloc_init(qstate->region,
+			vq->ds_rrset->entry.data, 
+			packed_rrset_sizeof(vq->ds_rrset->entry.data));
+		if(!vq->ds_rrset->entry.data) {
+			log_err("out of memory in process_dlv");
+			vq->dlv_status = dlv_error;
+			return;
+		}
+		packed_rrset_ptr_fixup(vq->ds_rrset->entry.data);
+		/* make vq do a DNSKEY query next up */
+		vq->dlv_status = dlv_success;
+		return;
+	}
+	/* store NSECs into negative cache */
+	val_neg_addreply(ve->neg_cache, msg->rep);
+
+	/* was the lookup a failure? 
+	 *   if we have to go up into the DLV for a higher DLV anchor
+	 *   then set this in the vq, so it can make queries when activated.
+	 * See if the NSECs indicate that we should look for higher DLV
+	 * or, that there is no DLV securely */
+	if(!val_nsec_check_dlv(qinfo, msg->rep, &vq->dlv_lookup_name, 
+		&vq->dlv_lookup_name_len)) {
+		vq->dlv_status = dlv_error;
+		verbose(VERB_ALGO, "nsec error");
+		return;
+	}
+	if(!dname_subdomain_c(vq->dlv_lookup_name, 
+		qstate->env->anchors->dlv_anchor->name)) {
+		vq->dlv_status = dlv_there_is_no_dlv;
+		return;
+	}
+	vq->dlv_status = dlv_ask_higher;
+}
+
+/* 
+ * inform validator super.
+ * 
+ * @param qstate: query state that finished.
+ * @param id: module id.
+ * @param super: the qstate to inform.
+ */
+void
+val_inform_super(struct module_qstate* qstate, int id,
+	struct module_qstate* super)
+{
+	struct val_qstate* vq = (struct val_qstate*)super->minfo[id];
+	log_query_info(VERB_ALGO, "validator: inform_super, sub is",
+		&qstate->qinfo);
+	log_query_info(VERB_ALGO, "super is", &super->qinfo);
+	if(!vq) {
+		verbose(VERB_ALGO, "super: has no validator state");
+		return;
+	}
+	if(vq->wait_prime_ta) {
+		vq->wait_prime_ta = 0;
+		process_prime_response(super, vq, id, qstate->return_rcode,
+			qstate->return_msg, qstate->reply_origin);
+		return;
+	}
+	if(qstate->qinfo.qtype == LDNS_RR_TYPE_DS) {
+		process_ds_response(super, vq, id, qstate->return_rcode,
+			qstate->return_msg, &qstate->qinfo, 
+			qstate->reply_origin);
+		return;
+	} else if(qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY) {
+		process_dnskey_response(super, vq, id, qstate->return_rcode,
+			qstate->return_msg, &qstate->qinfo,
+			qstate->reply_origin);
+		return;
+	} else if(qstate->qinfo.qtype == LDNS_RR_TYPE_DLV) {
+		process_dlv_response(super, vq, id, qstate->return_rcode,
+			qstate->return_msg, &qstate->qinfo);
+		return;
+	}
+	log_err("internal error in validator: no inform_supers possible");
+}
+
+void
+val_clear(struct module_qstate* qstate, int id)
+{
+	if(!qstate)
+		return;
+	/* everything is allocated in the region, so assign NULL */
+	qstate->minfo[id] = NULL;
+}
+
+size_t 
+val_get_mem(struct module_env* env, int id)
+{
+	struct val_env* ve = (struct val_env*)env->modinfo[id];
+	if(!ve)
+		return 0;
+	return sizeof(*ve) + key_cache_get_mem(ve->kcache) + 
+		val_neg_get_mem(ve->neg_cache) +
+		anchors_get_mem(env->anchors) + 
+		sizeof(size_t)*2*ve->nsec3_keyiter_count;
+}
+
+/**
+ * The validator function block 
+ */
+static struct module_func_block val_block = {
+	"validator",
+	&val_init, &val_deinit, &val_operate, &val_inform_super, &val_clear,
+	&val_get_mem
+};
+
+struct module_func_block* 
+val_get_funcblock(void)
+{
+	return &val_block;
+}
+
+const char* 
+val_state_to_string(enum val_state state)
+{
+	switch(state) {
+		case VAL_INIT_STATE: return "VAL_INIT_STATE";
+		case VAL_FINDKEY_STATE: return "VAL_FINDKEY_STATE";
+		case VAL_VALIDATE_STATE: return "VAL_VALIDATE_STATE";
+		case VAL_FINISHED_STATE: return "VAL_FINISHED_STATE";
+		case VAL_DLVLOOKUP_STATE: return "VAL_DLVLOOKUP_STATE";
+	}
+	return "UNKNOWN VALIDATOR STATE";
+}
+
diff --git a/3rdParty/Unbound/src/src/validator/validator.h b/3rdParty/Unbound/src/src/validator/validator.h
new file mode 100644
index 0000000..18e905e
--- /dev/null
+++ b/3rdParty/Unbound/src/src/validator/validator.h
@@ -0,0 +1,294 @@
+/*
+ * validator/validator.h - secure validator DNS query response module
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ * 
+ * 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.
+ * 
+ * Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 REGENTS 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.
+ */
+
+/**
+ * \file
+ *
+ * This file contains a module that performs validation of DNS queries.
+ * According to RFC 4034.
+ */
+
+#ifndef VALIDATOR_VALIDATOR_H
+#define VALIDATOR_VALIDATOR_H
+#include "util/module.h"
+#include "util/data/msgreply.h"
+#include "validator/val_utils.h"
+struct val_anchors;
+struct key_cache;
+struct key_entry_key;
+struct val_neg_cache;
+struct config_strlist;
+
+/**
+ * This is the TTL to use when a trust anchor fails to prime. A trust anchor
+ * will be primed no more often than this interval.  Used when harden-
+ * dnssec-stripped is off and the trust anchor fails.
+ */
+#define NULL_KEY_TTL	900 /* seconds */
+
+/**
+ * TTL for bogus key entries.  When a DS or DNSKEY fails in the chain of
+ * trust the entire zone for that name is blacked out for this TTL.
+ */
+#define BOGUS_KEY_TTL	900 /* seconds */
+
+/** max number of query restarts, number of IPs to probe */
+#define VAL_MAX_RESTART_COUNT 5
+
+/**
+ * Global state for the validator. 
+ */
+struct val_env {
+	/** key cache; these are validated keys. trusted keys only
+	 * end up here after being primed. */
+	struct key_cache* kcache;
+
+	/** aggressive negative cache. index into NSECs in rrset cache. */
+	struct val_neg_cache* neg_cache;
+
+	/** for debug testing a fixed validation date can be entered.
+	 * if 0, current time is used for rrsig validation */
+	int32_t date_override;
+
+	/** clock skew min for signatures */
+	int32_t skew_min;
+
+	/** clock skew max for signatures */
+	int32_t skew_max;
+
+	/** TTL for bogus data; used instead of untrusted TTL from data.
+	 * Bogus data will not be verified more often than this interval. 
+	 * seconds. */
+	uint32_t bogus_ttl;
+
+	/** If set, the validator should clean the additional section of
+	 * secure messages.
+	 */
+	int clean_additional;
+
+	/**
+	 * If set, the validator will not make messages bogus, instead
+	 * indeterminate is issued, so that no clients receive SERVFAIL.
+	 * This allows an operator to run validation 'shadow' without
+	 * hurting responses to clients.
+	 */
+	int permissive_mode;
+
+	/**
+	 * Number of entries in the NSEC3 maximum iteration count table.
+	 * Keep this table short, and sorted by size
+	 */
+	int nsec3_keyiter_count;
+
+	/**
+	 * NSEC3 maximum iteration count per signing key size.
+	 * This array contains key size values (in increasing order)
+	 */
+	size_t* nsec3_keysize;
+
+	/**
+	 * NSEC3 maximum iteration count per signing key size.
+	 * This array contains the maximum iteration count for the keysize
+	 * in the keysize array.
+	 */
+	size_t* nsec3_maxiter;
+
+	/** lock on bogus counter */
+	lock_basic_t bogus_lock;
+	/** number of times rrsets marked bogus */
+	size_t num_rrset_bogus;
+};
+
+/**
+ * State of the validator for a query.
+ */
+enum val_state {
+	/** initial state for validation */
+	VAL_INIT_STATE = 0,
+	/** find the proper keys for validation, follow trust chain */
+	VAL_FINDKEY_STATE,
+	/** validate the answer, using found key entry */
+	VAL_VALIDATE_STATE,
+	/** finish up */
+	VAL_FINISHED_STATE,
+	/** DLV lookup state, processing DLV queries */
+	VAL_DLVLOOKUP_STATE
+};
+
+/**
+ * Per query state for the validator module.
+ */
+struct val_qstate {
+	/** 
+	 * State of the validator module.
+	 */
+	enum val_state state;
+
+	/**
+	 * The original message we have been given to validate.
+	 */
+	struct dns_msg* orig_msg;
+
+	/**
+	 * The query restart count
+	 */
+	int restart_count;
+	/** The blacklist saved for chainoftrust elements */
+	struct sock_list* chain_blacklist;
+
+	/**
+	 * The query name we have chased to; qname after following CNAMEs
+	 */
+	struct query_info qchase;
+
+	/**
+	 * The chased reply, extract from original message. Can be:
+	 * 	o CNAME
+	 * 	o DNAME + CNAME
+	 * 	o answer 
+	 * 	plus authority, additional (nsecs) that have same signature.
+	 */
+	struct reply_info* chase_reply;
+
+	/**
+	 * The cname skip value; the number of rrsets that have been skipped
+	 * due to chasing cnames. This is the offset into the 
+	 * orig_msg->rep->rrsets array, into the answer section.
+	 * starts at 0 - for the full original message.
+	 * if it is >0 - qchase followed the cname, chase_reply setup to be
+	 * that message and relevant authority rrsets.
+	 *
+	 * The skip is also used for referral messages, where it will
+	 * range from 0, over the answer, authority and additional sections.
+	 */
+	size_t rrset_skip;
+
+	/** trust anchor name */
+	uint8_t* trust_anchor_name;
+	/** trust anchor labels */
+	int trust_anchor_labs;
+	/** trust anchor length */
+	size_t trust_anchor_len;
+
+	/** the DS rrset */
+	struct ub_packed_rrset_key* ds_rrset;
+
+	/** domain name for empty nonterminal detection */
+	uint8_t* empty_DS_name;
+	/** length of empty_DS_name */
+	size_t empty_DS_len;
+
+	/** the current key entry */
+	struct key_entry_key* key_entry;
+
+	/** subtype */
+	enum val_classification subtype;
+
+	/** signer name */
+	uint8_t* signer_name;
+	/** length of signer_name */
+	size_t signer_len;
+
+	/** true if this state is waiting to prime a trust anchor */
+	int wait_prime_ta;
+
+	/** have we already checked the DLV? */
+	int dlv_checked;
+	/** The name for which the DLV is looked up. For the current message
+	 * or for the current RRset (for CNAME, REFERRAL types).
+	 * If there is signer name, that may be it, else a domain name */
+	uint8_t* dlv_lookup_name;
+	/** length of dlv lookup name */
+	size_t dlv_lookup_name_len;
+	/** Name at which chain of trust stopped with insecure, starting DLV
+	 * DLV must result in chain going further down */
+	uint8_t* dlv_insecure_at;
+	/** length of dlv insecure point name */
+	size_t dlv_insecure_at_len;
+	/** status of DLV lookup. Indication to VAL_DLV_STATE what to do */
+	enum dlv_status {
+		dlv_error, /* server failure */
+		dlv_success, /* got a DLV */
+		dlv_ask_higher, /* ask again */
+		dlv_there_is_no_dlv /* got no DLV, sure of it */
+	} dlv_status;
+};
+
+/**
+ * Get the validator function block.
+ * @return: function block with function pointers to validator methods.
+ */
+struct module_func_block* val_get_funcblock(void);
+
+/**
+ * Get validator state as a string
+ * @param state: to convert
+ * @return constant string that is printable.
+ */
+const char* val_state_to_string(enum val_state state);
+
+/** validator init */
+int val_init(struct module_env* env, int id);
+
+/** validator deinit */
+void val_deinit(struct module_env* env, int id);
+
+/** validator operate on a query */
+void val_operate(struct module_qstate* qstate, enum module_ev event, int id,
+        struct outbound_entry* outbound);
+
+/** 
+ * inform validator super.
+ * 
+ * @param qstate: query state that finished.
+ * @param id: module id.
+ * @param super: the qstate to inform.
+ */
+void val_inform_super(struct module_qstate* qstate, int id,
+	struct module_qstate* super);
+
+/** validator cleanup query state */
+void val_clear(struct module_qstate* qstate, int id);
+
+/**
+ * Debug helper routine that assists worker in determining memory in 
+ * use.
+ * @param env: module environment 
+ * @param id: module id.
+ * @return memory in use in bytes.
+ */
+size_t val_get_mem(struct module_env* env, int id);
+
+#endif /* VALIDATOR_VALIDATOR_H */
diff --git a/BuildTools/SCons/SConscript.boot b/BuildTools/SCons/SConscript.boot
index 188184c..983435e 100644
--- a/BuildTools/SCons/SConscript.boot
+++ b/BuildTools/SCons/SConscript.boot
@@ -55,6 +55,7 @@ vars.Add(PathVariable("docbook_xsl", "DocBook XSL", None, PathVariable.PathAccep
 vars.Add(BoolVariable("build_examples", "Build example programs", "yes"))
 vars.Add(BoolVariable("enable_variants", "Build in a separate dir under build/, depending on compile flags", "no"))
 vars.Add(BoolVariable("experimental", "Build experimental features", "no"))
+vars.Add(BoolVariable("unbound", "Build bundled ldns and unbound. Use them for DNS lookup.", "no"))
 
 ################################################################################
 # Set up default build & configure environment
diff --git a/BuildTools/SCons/SConstruct b/BuildTools/SCons/SConstruct
index b3d3c8f..1bc3503 100644
--- a/BuildTools/SCons/SConstruct
+++ b/BuildTools/SCons/SConstruct
@@ -311,6 +311,14 @@ else :
 	env["LIBIDN_BUNDLED"] = 1
 conf.Finish()
 
+# Unbound
+if env["unbound"] :
+    env["LDNS_BUNDLED"] = 1
+    env["UNBOUND_BUNDLED"] = 1
+else :
+    env["LDNS_FLAGS"] = {}
+    env["UNBOUND_FLAGS"] = {}
+
 # LibMiniUPnPc
 if env["experimental"] :
 	#libminiupnpc_conf_env = conf_env.Clone()
diff --git a/Swiften/Network/BoostNetworkFactories.cpp b/Swiften/Network/BoostNetworkFactories.cpp
index 488e519..091a70a 100644
--- a/Swiften/Network/BoostNetworkFactories.cpp
+++ b/Swiften/Network/BoostNetworkFactories.cpp
@@ -15,12 +15,15 @@
 #include <Swiften/TLS/PlatformTLSFactories.h>
 #include <Swiften/Network/PlatformProxyProvider.h>
 
+#include <Swiften/Network/UnboundDomainNameResolver.h>
+
 namespace Swift {
 
 BoostNetworkFactories::BoostNetworkFactories(EventLoop* eventLoop) : eventLoop(eventLoop){
 	timerFactory = new BoostTimerFactory(ioServiceThread.getIOService(), eventLoop);
 	connectionFactory = new BoostConnectionFactory(ioServiceThread.getIOService(), eventLoop);
-	domainNameResolver = new PlatformDomainNameResolver(eventLoop);
+	domainNameResolver = new UnboundDomainNameResolver(eventLoop, timerFactory);
+	//domainNameResolver = new PlatformDomainNameResolver(eventLoop);
 	connectionServerFactory = new BoostConnectionServerFactory(ioServiceThread.getIOService(), eventLoop);
 #ifdef SWIFT_EXPERIMENTAL_FT
 	natTraverser = new PlatformNATTraversalWorker(eventLoop);
diff --git a/Swiften/Network/SConscript b/Swiften/Network/SConscript
index 0d0abbd..429ca51 100644
--- a/Swiften/Network/SConscript
+++ b/Swiften/Network/SConscript
@@ -5,6 +5,10 @@ myenv.MergeFlags(myenv["LIBIDN_FLAGS"])
 if myenv.get("HAVE_CARES", False) :
    myenv.MergeFlags(myenv.get("CARES_FLAGS", {}))
 
+if myenv.get("unbound", False) :
+   myenv.MergeFlags(myenv.get("UNBOUND_FLAGS", {}))
+   myenv.MergeFlags(myenv.get("LDNS_FLAGS", {}))
+
 sourceList = [
 			"HTTPConnectProxiedConnection.cpp",
 			"HTTPConnectProxiedConnectionFactory.cpp",
@@ -17,7 +21,7 @@ sourceList = [
 			"BoostIOServiceThread.cpp",
 			"BOSHConnection.cpp",
 			"BOSHConnectionPool.cpp",
-                        "CachingNameOnlyDomainNameResolver.cpp",
+            "CachingNameOnlyDomainNameResolver.cpp",
 			"ConnectionFactory.cpp",
 			"ConnectionServer.cpp",
 			"ConnectionServerFactory.cpp",
@@ -57,6 +61,9 @@ sourceList = [
 
 if myenv.get("HAVE_CARES", False) :
    sourceList.append("CAresDomainNameResolver.cpp")
+
+if myenv.get("unbound", False) :
+	sourceList.append("UnboundDomainNameResolver.cpp")
 		
 if myenv["PLATFORM"] == "darwin" :
 	myenv.Append(FRAMEWORKS = ["CoreServices", "SystemConfiguration"])
diff --git a/Swiften/Network/UnboundDomainNameResolver.cpp b/Swiften/Network/UnboundDomainNameResolver.cpp
new file mode 100644
index 0000000..1b36012
--- /dev/null
+++ b/Swiften/Network/UnboundDomainNameResolver.cpp
@@ -0,0 +1,221 @@
+#include "UnboundDomainNameResolver.h"
+
+#include <vector>
+
+#include <boost/bind.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/Log.h>
+#include <Swiften/Network/DomainNameAddressQuery.h>
+#include <Swiften/Network/DomainNameResolveError.h>
+#include <Swiften/Network/DomainNameServiceQuery.h>
+#include <Swiften/Network/HostAddress.h>
+#include <Swiften/Network/Timer.h>
+#include <Swiften/Network/TimerFactory.h>
+
+
+#include <arpa/inet.h>
+#include <ldns/ldns.h>
+#include <unbound.h>
+#include <unistd.h>
+
+namespace Swift {
+
+class UnboundDomainNameServiceQuery : public DomainNameServiceQuery {
+	public:
+		UnboundDomainNameServiceQuery(TimerFactory* timerFactory, const std::string& name) : timerFactory(timerFactory), name(name), ubContext(0) {
+
+		}
+
+		virtual ~UnboundDomainNameServiceQuery() {
+			if (ubContext) {
+				ub_ctx_delete(ubContext);
+			}
+		}
+
+		static void unbound_service_callback(void* data, int err, struct ub_result* result) {
+			UnboundDomainNameServiceQuery* query = static_cast<UnboundDomainNameServiceQuery*>(data);
+			query->handleResult(err, result);
+		}
+
+		virtual void run() {
+			int retval;
+
+			/* create context */
+			ubContext = ub_ctx_create();
+			if(!ubContext) {
+				SWIFT_LOG(debug) << "could not create unbound context" << std::endl;
+				return;
+			}
+			ub_ctx_async(ubContext, true);
+			retval = ub_resolve_async(ubContext, const_cast<char*>(name.c_str()), LDNS_RR_TYPE_SRV,
+						  1 /* CLASS IN (internet) */,
+						  this, unbound_service_callback, NULL);
+			if(retval != 0) {
+				SWIFT_LOG(debug) << "resolve error: " << ub_strerror(retval) << std::endl;
+			}
+
+			timer = timerFactory->createTimer(500);
+			timer->onTick.connect(boost::bind(&UnboundDomainNameServiceQuery::processData, this));
+			timer->start();
+		}
+
+		void processData() {
+			if (ub_poll(ubContext)) {
+				int ret = ub_process(ubContext);
+				if(ret != 0) {
+					SWIFT_LOG(debug) << "resolve error: " << ub_strerror(ret) << std::endl;
+				}
+			} else {
+				timer->start();
+			}
+		}
+
+		void handleResult(int err, struct ub_result* result) {
+			std::vector<DomainNameServiceQuery::Result> serviceRecords;
+
+			if(err != 0) {
+				SWIFT_LOG(debug) << "resolve error: " << ub_strerror(err) << std::endl;
+			} else {
+				if(result->havedata) {
+					ldns_pkt* replyPacket = 0;
+					ldns_buffer* buffer = ldns_buffer_new(1024);
+					if (buffer && ldns_wire2pkt(&replyPacket, static_cast<const uint8_t*>(result->answer_packet), result->answer_len) == LDNS_STATUS_OK) {
+						ldns_rr_list* rrList = ldns_pkt_answer(replyPacket);
+						for (size_t n = 0; n < ldns_rr_list_rr_count(rrList); n++) {
+							ldns_rr* rr = ldns_rr_list_rr(rrList, n);
+							if ((ldns_rr_get_type(rr) != LDNS_RR_TYPE_SRV) ||
+								(ldns_rr_get_class(rr) != LDNS_RR_CLASS_IN) ||
+							    (ldns_rr_rd_count(rr) != 4)) {
+								continue;
+							}
+
+							DomainNameServiceQuery::Result serviceRecord;
+							serviceRecord.priority = ldns_rdf2native_int16(ldns_rr_rdf(rr, 0));
+							serviceRecord.weight = ldns_rdf2native_int16(ldns_rr_rdf(rr, 1));
+							serviceRecord.port = ldns_rdf2native_int16(ldns_rr_rdf(rr, 2));
+
+							ldns_buffer_rewind(buffer);
+							if ((ldns_rdf2buffer_str_dname(buffer, ldns_rr_rdf(rr, 3)) != LDNS_STATUS_OK) ||
+							    (ldns_buffer_position(buffer) < 2) ||
+							    !ldns_buffer_reserve(buffer, 1)) {
+								// either name invalid, empty or buffer to small
+								continue;
+							}
+							char terminator = 0;
+							ldns_buffer_write(buffer, &terminator, sizeof(terminator));
+
+							serviceRecord.hostname = std::string(reinterpret_cast<char*>(ldns_buffer_at(buffer, 0)));
+							serviceRecords.push_back(serviceRecord);
+							SWIFT_LOG(debug) << "hostname " << serviceRecord.hostname << " added" << std::endl;
+						}
+					}
+					if (replyPacket) ldns_pkt_free(replyPacket);
+					if (buffer) ldns_buffer_free(buffer);
+				}
+			}
+
+			ub_resolve_free(result);
+			onResult(serviceRecords);
+		}
+
+	private:
+		TimerFactory* timerFactory;
+		Timer::ref timer;
+		std::string name;
+		ub_ctx* ubContext;
+};
+
+class UnboundDomainNameAddressQuery : public DomainNameAddressQuery {
+	public:
+		UnboundDomainNameAddressQuery(TimerFactory* timerFactory, const std::string& name) : timerFactory(timerFactory), name(name), ubContext(0) {
+
+		}
+
+		virtual ~UnboundDomainNameAddressQuery() {
+			if (ubContext) {
+				ub_ctx_delete(ubContext);
+			}
+		}
+
+		static void unbound_address_callback(void* data, int err, struct ub_result* result) {
+			UnboundDomainNameAddressQuery* query = static_cast<UnboundDomainNameAddressQuery*>(data);
+			query->handleResult(err, result);
+		}
+
+		virtual void run() {
+			int retval;
+
+			/* create context */
+			ubContext = ub_ctx_create();
+			if(!ubContext) {
+				SWIFT_LOG(debug) << "could not create unbound context" << std::endl;
+				return;
+			}
+
+			ub_ctx_async(ubContext, true);
+			retval = ub_resolve_async(ubContext, const_cast<char*>(name.c_str()), LDNS_RR_TYPE_A,
+						  1 /* CLASS IN (internet) */,
+						  this, unbound_address_callback, NULL);
+			if(retval != 0) {
+				SWIFT_LOG(debug) << "resolve error: " << ub_strerror(retval) << std::endl;
+			}
+
+			timer = timerFactory->createTimer(500);
+			timer->onTick.connect(boost::bind(&UnboundDomainNameAddressQuery::processData, this));
+			timer->start();
+		}
+
+		void processData() {
+			if (ub_poll(ubContext)) {
+				int ret = ub_process(ubContext);
+				if(ret != 0) {
+					SWIFT_LOG(debug) << "resolve error: " << ub_strerror(ret) << std::endl;
+				}
+			} else {
+				timer->start();
+			}
+		}
+
+		void handleResult(int err, struct ub_result* result) {
+			std::vector<HostAddress> addresses;
+			boost::optional<DomainNameResolveError> error;
+
+			if(err != 0) {
+				SWIFT_LOG(debug) << "resolve error: " << ub_strerror(err) << std::endl;
+				error = DomainNameResolveError();
+			} else {
+				if(result->havedata) {
+					for(int i=0; result->data[i]; i++) {
+						addresses.push_back(HostAddress(std::string(inet_ntoa(* (struct in_addr*)result->data[i]))));
+					}
+				}
+			}
+
+			ub_resolve_free(result);
+			onResult(addresses, error);
+		}
+
+	private:
+		TimerFactory* timerFactory;
+		Timer::ref timer;
+		std::string name;
+		ub_ctx* ubContext;
+
+};
+
+UnboundDomainNameResolver::UnboundDomainNameResolver(EventLoop* eventLoop, TimerFactory* timerFactory) : eventLoop(eventLoop), timerFactory(timerFactory) {
+}
+
+UnboundDomainNameResolver::~UnboundDomainNameResolver() {
+}
+
+boost::shared_ptr<DomainNameServiceQuery> UnboundDomainNameResolver::createServiceQuery(const std::string& name) {
+	return boost::make_shared<UnboundDomainNameServiceQuery>(timerFactory, name);
+}
+
+boost::shared_ptr<DomainNameAddressQuery> UnboundDomainNameResolver::createAddressQuery(const std::string& name) {
+	return boost::make_shared<UnboundDomainNameAddressQuery>(timerFactory, name);
+}
+
+}
diff --git a/Swiften/Network/UnboundDomainNameResolver.h b/Swiften/Network/UnboundDomainNameResolver.h
new file mode 100644
index 0000000..37cf2f6
--- /dev/null
+++ b/Swiften/Network/UnboundDomainNameResolver.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <Swiften/Network/DomainNameResolver.h>
+
+namespace Swift {
+	class EventLoop;
+	class TimerFactory;
+
+	class UnboundDomainNameResolver : public DomainNameResolver {
+		public:
+			UnboundDomainNameResolver(EventLoop* eventLoop, TimerFactory* timerFactory);
+			virtual ~UnboundDomainNameResolver();
+
+			virtual boost::shared_ptr<DomainNameServiceQuery> createServiceQuery(const std::string& name);
+			virtual boost::shared_ptr<DomainNameAddressQuery> createAddressQuery(const std::string& name);
+
+		private:
+			EventLoop* eventLoop;
+			TimerFactory* timerFactory;
+	};
+
+}
diff --git a/Swiften/SConscript b/Swiften/SConscript
index c12d3b2..42c1588 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -8,6 +8,9 @@ Import("env")
 
 swiften_dep_modules = ["BOOST", "GCONF", "LIBIDN", "ZLIB", "OPENSSL", "LIBXML", "EXPAT", "AVAHI", "LIBMINIUPNPC", "LIBNATPMP"]
 
+if env["unbound"] :
+    swiften_dep_modules.extend(["LDNS", "UNBOUND"])
+
 if env["SCONS_STAGE"] == "flags" :
 	env["SWIFTEN_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "swift")
 	version_match = re.match("(\d+)\.(\d+).*", env["SWIFTEN_VERSION"])
-- 
cgit v0.10.2-6-g49f6