dnssec.patch
author David Keeler <dkeeler@mozilla.com>
Thu, 01 Sep 2011 11:03:07 -0700
changeset 48 3e596f38a49a3f0fec18e6ed3eeaf1667007ab44
parent 46 6aeecf37ea57dccbcaecbb43ff3d1fe35668768f
permissions -rw-r--r--
fixed key disconnect test case

# HG changeset patch
# Parent 1ac7dbb1fb2671ae71319bd680faf5e4a44970e5
# User David Keeler <dkeeler@mozilla.com>

diff --git a/security/dnssec/LICENSE b/security/dnssec/LICENSE
new file mode 100644
--- /dev/null
+++ b/security/dnssec/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/security/dnssec/Makefile.in b/security/dnssec/Makefile.in
new file mode 100644
--- /dev/null
+++ b/security/dnssec/Makefile.in
@@ -0,0 +1,104 @@
+#! gmake
+# 
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 1994-2000
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Javier Delgadillo <javi@netscape.com>
+#   Terry Hayes     <thayes@netscape.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE		= dnssec
+LIBRARY_NAME	= dnssec
+MODULE_NAME	= NSS
+EXPORT_LIBRARY	= 1
+GRE_MODULE	= 1
+
+CSRCS =          \
+	buffer.c \
+	dname.c \
+	dnssec.c \
+	dnssec_sign.c \
+	dnssec_verify.c \
+	dnssec_zone.c \
+	error.c \
+	higher.c \
+	host2str.c \
+	host2wire.c \
+	keys.c \
+	net.c \
+	packet.c \
+	parse.c \
+	rbtree.c \
+	rdata.c \
+	resolver.c \
+	rr.c \
+	rr_functions.c \
+	sha1.c \
+	sha2.c \
+	str2host.c \
+	tsig.c \
+	update.c \
+	util.c \
+	verify.c \
+	wire2host.c \
+	zone.c \
+	b32_ntop.c \
+	b32_pton.c \
+	b64_ntop.c \
+	b64_pton.c \
+	strlcpy.c \
+  $(NULL)
+
+EXPORTS = \
+  verify.h \
+	$(NULL)
+
+ifdef ENABLE_TESTS
+TOOL_DIRS += test
+endif
+
+EXTRA_DEPS = $(NSS_DEP_LIBS)
+
+# Use local includes because they are inserted before INCLUDES
+# so that Mozilla's nss.h is used, not glibc's
+LOCAL_INCLUDES += $(NSS_CFLAGS) -Ildns
+
+include $(topsrcdir)/config/rules.mk
diff --git a/security/dnssec/b32_ntop.c b/security/dnssec/b32_ntop.c
new file mode 100644
--- /dev/null
+++ b/security/dnssec/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/security/dnssec/b32_pton.c b/security/dnssec/b32_pton.c
new file mode 100644
--- /dev/null
+++ b/security/dnssec/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/security/dnssec/b64_ntop.c b/security/dnssec/b64_ntop.c
new file mode 100644
--- /dev/null
+++ b/security/dnssec/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/security/dnssec/b64_pton.c b/security/dnssec/b64_pton.c
new file mode 100644
--- /dev/null
+++ b/security/dnssec/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/security/dnssec/buffer.c b/security/dnssec/buffer.c
new file mode 100644
--- /dev/null
+++ b/security/dnssec/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/security/dnssec/dname.c b/security/dnssec/dname.c
new file mode 100644
--- /dev/null
+++ b/security/dnssec/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/security/dnssec/dnssec.c b/security/dnssec/dnssec.c
new file mode 100644
--- /dev/null
+++ b/security/dnssec/dnssec.c
@@ -0,0 +1,1757 @@
+/*
+ * 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;
+	ldns_rr *ce;
+	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);
+
+	ce = NULL;
+	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
+	case LDNS_SHA384:
+		digest = LDNS_XMALLOC(uint8_t, SHA384_DIGEST_LENGTH);
+		if (!digest) {
+			ldns_rr_free(ds);
+			return NULL;
+		}
+                break;
+#else
+		/* not implemented */
+		ldns_rr_free(ds);
+		return NULL;
+#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 &&
+					 nsec_type != LDNS_RR_TYPE_NSEC3)) {
+		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 = 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 || HAVE_NSS
+/* sig may be null - if so look in the packet */
+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)
+{
+	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(rrset, sigs, k, good_keys);
+}
+#endif /* HAVE_SSL || HAVE_NSS */
+
+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/security/dnssec/dnssec_sign.c b/security/dnssec/dnssec_sign.c
new file mode 100644
--- /dev/null
+++ b/security/dnssec/dnssec_sign.c
@@ -0,0 +1,1360 @@
+#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;
+
+	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 */
+	(void)ldns_rr_rrsig_set_signame(
+			current_sig,
+			ldns_rdf_clone(ldns_key_pubkey_owner(current_key)));
+	/* 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
+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)
+{
+	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;
+
+	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;
+	}
+
+	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);
+		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;
+}
+#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(zone, new_rrs, key_list,
+		func, arg, algorithm, flags, iterations, salt_length, salt, 0);
+}
+
+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)
+{
+	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(zone,
+											new_rrs,
+											algorithm,
+											flags,
+											iterations,
+											salt_length,
+											salt);
+			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_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/security/dnssec/dnssec_verify.c b/security/dnssec/dnssec_verify.c
new file mode 100644
--- /dev/null
+++ b/security/dnssec/dnssec_verify.c
@@ -0,0 +1,2465 @@
+#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>
+#endif
+
+#ifdef HAVE_NSS
+#include <nss.h>
+#include <seccomon.h>
+#include <secitem.h>
+#include <secoidt.h>
+#include <cryptohi.h>
+#include <keyhi.h>
+#include <prmem.h>
+#include "verify.h"
+#endif
+
+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(FILE *out, const ldns_dnssec_data_chain *chain)
+{
+	ldns_lookup_table *rcode;
+	const ldns_rr_descriptor *rr_descriptor;
+	if (chain) {
+		ldns_dnssec_data_chain_print(out, 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(out, chain->rrset);
+			fprintf(out, "sigs:\n");
+			ldns_rr_list_print(out, chain->signatures);
+			fprintf(out, "---\n");
+		} else {
+			fprintf(out, "<no data>\n");
+		}
+	}
+}
+
+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(FILE *out,
+						  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) {
+#ifdef LDNS_CONFIG_HAVE_SSL
+							printf("; SSL Error: ");
+							ERR_load_crypto_strings();
+							ERR_print_errors_fp(stdout);
+							printf("\n");
+#endif
+						}
+						ldns_rr_print(out, tree->parent_signature[i]);
+						printf("For RRset:\n");
+						ldns_rr_list_print(out, tree->rrset);
+						printf("With key:\n");
+						ldns_rr_print(out, tree->parents[i]->rr);
+					}
+				ldns_dnssec_trust_tree_print_sm(out,
+										  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(FILE *out,
+					    ldns_dnssec_trust_tree *tree,
+					    size_t tabs,
+					    bool extended)
+{
+	ldns_dnssec_trust_tree_print_sm(out, tree, tabs, extended, NULL, 0);
+}
+
+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(ldns_dnssec_data_chain *data_chain, ldns_rr *rr)
+{
+	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(
+						    new_tree,
+						    data_chain,
+						    cur_sig_rr);
+					}
+
+					/* option 2 */
+					ldns_dnssec_derive_trust_tree_dnskey_rrset(
+					    new_tree,
+					    data_chain,
+					    cur_rr,
+					    cur_sig_rr);
+				}
+					
+				ldns_dnssec_derive_trust_tree_ds_rrset(new_tree,
+											    data_chain,
+											    cur_rr);
+			} else {
+				/* no signatures? maybe it's nsec data */
+					
+				/* just add every rr from parent as new parent */
+				ldns_dnssec_derive_trust_tree_no_sig(new_tree, data_chain);
+			}
+		}
+	}
+
+	return new_tree;
+}
+
+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)
+{
+	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(tmp_rrset,
+											 cur_sig_rr,
+											 cur_parent_rr);
+					/* 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(data_chain->parent,
+						                              cur_parent_rr);
+					(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_dnskey_rrset(ldns_dnssec_trust_tree *new_tree,
+                                           ldns_dnssec_data_chain *data_chain,
+                                           ldns_rr *cur_rr,
+                                           ldns_rr *cur_sig_rr)
+{
+	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(cur_rrset,
+				                               cur_sig_rr,
+				                               cur_parent_rr);
+				(void) ldns_dnssec_trust_tree_add_parent(new_tree,
+				            cur_parent_tree, cur_sig_rr, cur_status);
+			}
+		}
+	}
+}
+
+void
+ldns_dnssec_derive_trust_tree_ds_rrset(ldns_dnssec_trust_tree *new_tree,
+                                       ldns_dnssec_data_chain *data_chain,
+                                       ldns_rr *cur_rr)
+{
+	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(
+							    data_chain->parent, cur_parent_rr);
+						(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_no_sig(ldns_dnssec_trust_tree *new_tree,
+                                     ldns_dnssec_data_chain *data_chain)
+{
+	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_NSEC3) {
+				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(data_chain->parent,
+										cur_parent_rr);
+			(void) ldns_dnssec_trust_tree_add_parent(new_tree,
+			            cur_parent_tree, NULL, result);
+		}
+	}
+}
+
+/*
+ * 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(ldns_rr_list *rrset, ldns_rr_list *rrsig, const ldns_rr_list *keys, 
+		  ldns_rr_list *good_keys, time_t time)
+{
+	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(rrset, 
+				ldns_rr_list_rr(rrsig, i), keys, good_keys, time);
+			/* 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_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(const ldns_resolver *res,
+                             const ldns_rdf *domain,
+                             const ldns_rr_list *keys,
+                             ldns_status *status)
+{
+	ldns_rr_list * trusted_keys = NULL;
+	ldns_rr_list * ds_keys = NULL;
+
+	if (res && domain && keys) {
+
+		if ((trusted_keys = ldns_validate_domain_dnskey(res,
+                                         domain,
+                                         keys))) {
+			*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;
+      
+			if (ldns_rdf_size(domain) > 1) {
+				/* Fail if we are at the root */
+				ldns_rr_list * parent_keys;
+				ldns_rdf * parent_domain = ldns_dname_left_chop(domain);
+	
+				if ((parent_keys = 
+					ldns_fetch_valid_domain_keys(res,
+					     parent_domain,
+					     keys,
+					     status))) {
+					/* Check DS records */
+					if ((ds_keys =
+						ldns_validate_domain_ds(res,
+						     domain,
+						     parent_keys))) {
+						trusted_keys =
+							ldns_fetch_valid_domain_keys(res,
+							     domain,
+							     ds_keys,
+							     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);
+				}
+				ldns_rdf_deep_free(parent_domain);
+			}
+		}
+	}
+	return trusted_keys;
+}
+
+ldns_rr_list *
+ldns_validate_domain_dnskey(const ldns_resolver * res,
+					   const ldns_rdf * domain,
+					   const ldns_rr_list * keys)
+{
+	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(domain_keys,
+											   cur_sig,
+											   cur_key)
+							    == 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_ds(const ldns_resolver *res,
+				    const ldns_rdf * domain,
+				    const ldns_rr_list * keys)
+{
+	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(rrset, sigs, keys, NULL, 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_status
+ldns_verify_trusted(ldns_resolver *res,
+				ldns_rr_list *rrset,
+				ldns_rr_list * rrsigs,
+				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(
+						    res,
+						    ldns_rr_rrsig_signame(cur_sig),
+						    ldns_resolver_dnssec_anchors(res),
+						    &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(rrset,
+										  cur_sig,
+										  cur_key))
+				    == 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;
+				} else {
+					ldns_rr_list_print(stdout, rrset);
+					ldns_rr_print(stdout, cur_sig);
+					ldns_rr_print(stdout, cur_key);
+        	
+				}
+			}
+		}
+	}
+
+	ldns_rr_list_deep_free(trusted_keys);
+	return result;
+}
+
+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;
+}
+
+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;
+	} 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
+	       );
+}
+
+#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)
+{
+#if LDNS_BUILD_CONFIG_HAVE_SSL
+	/* 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;
+	}
+#elif LDNS_BUILD_CONFIG_HAVE_NSS
+  SECOidTag algid;
+  /* check for right key */
+  switch(algo) {
+  case LDNS_RSASHA1:
+  case LDNS_RSASHA1_NSEC3:
+    algid = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
+    break;
+  case LDNS_RSASHA256:
+    algid = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION;
+    break;
+  case LDNS_RSASHA512:
+    algid = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION;
+    break;
+  case LDNS_RSAMD5:
+    algid = SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION;
+    break;
+  case LDNS_DSA:
+  case LDNS_DSA_NSEC3:
+  default:
+    /* do you know this alg?! */
+    return LDNS_STATUS_CRYPTO_UNKNOWN_ALGO;
+  }
+
+  SECItem signature;
+  signature.type = siBuffer;
+  signature.data = sig;
+  signature.len = siglen;
+
+  unsigned char *exp = key + 1;
+  size_t explen = (size_t)key[0];
+  if (explen + 1 >= keylen) {
+    return LDNS_STATUS_ERR;
+  }
+  unsigned char *mod = key + 1 + explen;
+  size_t modlen = keylen - 1 - explen;
+  SECItem modulus, exponent;
+  modulus.data = mod;
+  modulus.len = modlen;
+  exponent.data = exp;
+  exponent.len = explen;
+  SECKEYPublicKey *pubkey = SECKEY_ImportRSAPublicKey(&modulus, &exponent);
+  if (pubkey == NULL) {
+    return LDNS_STATUS_MEM_ERR;
+  }
+
+  size_t datalen = ldns_buffer_position(verify_buf);
+  unsigned char *data = ldns_buffer_begin(verify_buf);
+  SECStatus status = VFY_VerifyData(data, datalen, pubkey, &signature, algid,
+                                    NULL);
+  SECKEY_DestroyPublicKey(pubkey);
+
+  if (status == SECSuccess) {
+    return LDNS_STATUS_OK;
+  } else {
+    return LDNS_STATUS_CRYPTO_BOGUS;
+  }
+#endif /* LDNS_BUILD_CONFIG_HAVE_NSS */
+}
+
+
+/**
+ * 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;
+#ifdef LDNS_CONFIG_HAVE_SSL
+	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) {
+			return LDNS_STATUS_MEM_ERR;
+		}
+		break;
+#endif
+#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, int32_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(ldns_rr_list *rrset,
+					 ldns_rr *rrsig,
+					 const ldns_rr_list *keys, 
+					 ldns_rr_list *good_keys,
+           time_t time)
+{
+	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, (int32_t)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;
+}
+
+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(ldns_rr_list *rrset, ldns_rr *rrsig, ldns_rr *key)
+{
+	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, 
+			(int32_t)time(NULL));
+
+	return result;
+}
+
+#if LDNS_BUILD_CONFIG_HAVE_SSL
+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 /* LDNS_BUILD_CONFIG_HAVE_SSL */
diff --git a/security/dnssec/dnssec_zone.c b/security/dnssec/dnssec_zone.c
new file mode 100644
--- /dev/null
+++ b/security/dnssec/dnssec_zone.c
@@ -0,0 +1,848 @@
+/*
+ * 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(FILE *out, ldns_dnssec_rrs *rrs)
+{
+	if (!rrs) {
+		fprintf(out, "<void>");
+	} else {
+		if (rrs->rr) {
+			ldns_rr_print(out, rrs->rr);
+		}
+		if (rrs->next) {
+			ldns_dnssec_rrs_print(out, rrs->next);
+		}
+	}
+}
+
+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(FILE *out,
+					    ldns_dnssec_rrsets *rrsets,
+					    bool follow,
+					    bool show_soa)
+{
+	if (!rrsets) {
+		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(out, rrsets->rrs);
+			if (rrsets->signatures) {
+				ldns_dnssec_rrs_print(out, rrsets->signatures);
+			}
+		}
+		if (follow && rrsets->next) {
+			ldns_dnssec_rrsets_print_soa(out, rrsets->next, follow, show_soa);
+		}
+	}
+}
+
+void
+ldns_dnssec_rrsets_print(FILE *out, ldns_dnssec_rrsets *rrsets, bool follow)
+{
+	ldns_dnssec_rrsets_print_soa(out, rrsets, follow, true);
+}
+
+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(FILE *out, ldns_dnssec_name *name, bool show_soa)
+{
+	if (name) {
+		if(name->rrsets) {
+			ldns_dnssec_rrsets_print_soa(out, name->rrsets, true, show_soa);
+		} else {
+			fprintf(out, ";; Empty nonterminal: ");
+			ldns_rdf_print(out, name->name);
+			fprintf(out, "\n");
+		}
+		if(name->nsec) {
+			ldns_rr_print(out, name->nsec);
+		}
+		if (name->nsec_signatures) {
+			ldns_dnssec_rrs_print(out, name->nsec_signatures);
+		}
+	} else {
+		fprintf(out, "<void>\n");
+	}
+}
+
+void
+ldns_dnssec_name_print(FILE *out, ldns_dnssec_name *name)
+{
+	ldns_dnssec_name_print_soa(out, name, true);
+}
+
+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(FILE *out, 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(out, name, print_soa);
+		fprintf(out, ";\n");
+		node = ldns_rbtree_next(node);
+	}
+}
+
+void
+ldns_dnssec_zone_print(FILE *out, ldns_dnssec_zone *zone)
+{
+	if (zone) {
+		if (zone->soa) {
+			fprintf(out, ";; Zone: ");
+			ldns_rdf_print(out, ldns_dnssec_name_name(zone->soa));
+			fprintf(out, "\n;\n");
+			ldns_dnssec_rrsets_print(
+			    out,
+			    ldns_dnssec_name_find_rrset(zone->soa,
+									  LDNS_RR_TYPE_SOA),
+			    false);
+			fprintf(out, ";\n");
+		}
+
+		if (zone->names) {
+			ldns_dnssec_zone_names_print(out, zone->names, false);
+		}
+	}
+}
+
+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/security/dnssec/error.c b/security/dnssec/error.c
new file mode 100644
--- /dev/null
+++ b/security/dnssec/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/security/dnssec/higher.c b/security/dnssec/higher.c
new file mode 100644
--- /dev/null
+++ b/security/dnssec/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/security/dnssec/host2str.c b/security/dnssec/host2str.c
new file mode 100644
--- /dev/null
+++ b/security/dnssec/host2str.c
@@ -0,0 +1,2087 @@
+/*
+ * 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 }
+};
+
+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_status
+ldns_rr2buffer_str(ldns_buffer *output, const ldns_rr *rr)
+{
+	uint16_t i, flags;
+	ldns_status status = LDNS_STATUS_OK;
+
+	if (!rr) {
+		ldns_buffer_printf(output, "(null)\n");
+	} else {
+		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! */
+					flags = ldns_rdf2native_int16(ldns_rr_rdf(rr, 0));
+					if (flags == 256 || flags == 384) {
+						ldns_buffer_printf(output,
+								" ;{id = %u (zsk), size = %db}",
+								(unsigned int) ldns_calc_keytag(rr),
+								ldns_rr_dnskey_key_size(rr));
+						break;
+					}
+					if (flags == 257 || flags == 385) {
+						ldns_buffer_printf(output,
+								" ;{id = %u (ksk), size = %db}",
+								(unsigned int) ldns_calc_keytag(rr),
+								ldns_rr_dnskey_key_size(rr));
+						break;
+					}
+					ldns_buffer_printf(output, " ;{id = %u, size = %db}",
+							(unsigned int) ldns_calc_keytag(rr),
+							ldns_rr_dnskey_key_size(rr));
+					break;
+				case LDNS_RR_TYPE_RRSIG:
+					if (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 (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 (ldns_nsec3_optout(rr)) {
+						ldns_buffer_printf(output, " ; flags: optout");
+					}
+					break;
+				default:
+					break;
+
+			}
+		}
+		/* last */
+		ldns_buffer_printf(output, "\n");
+	}
+	return ldns_buffer_status(output);
+}
+
+ldns_status
+ldns_rr_list2buffer_str(ldns_buffer *output, const ldns_rr_list *list)
+{
+	uint16_t i;
+
+	for(i = 0; i < ldns_rr_list_rr_count(list); i++) {
+		(void) ldns_rr2buffer_str(output, ldns_rr_list_rr(list, i));
+	}
+	return ldns_buffer_status(output);
+}
+
+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(ldns_buffer *output, 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(output,
+				       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(output,
+				       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(output,
+				       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(output,
+				       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(output, 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;
+}
+
+#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, b6