Blame SOURCES/bind-9.9-allow_external_dnskey.patch

d56ed2
From 0c91911b4d1e872b87eaf6431ed47fe24d18dd43 Mon Sep 17 00:00:00 2001
d56ed2
From: Mark Andrews <marka@isc.org>
d56ed2
Date: Wed, 4 Sep 2013 13:53:02 +1000
d56ed2
Subject: [PATCH] 3642.   [func]          Allow externally generated DNSKEY to
d56ed2
 be imported                         into the DNSKEY management framework.  A
d56ed2
 new tool                         dnssec-importkey is used to this. [RT
d56ed2
 #34698]
d56ed2
d56ed2
---
d56ed2
 CHANGES                                |   4 +
d56ed2
 bin/dnssec/.gitignore                  |   1 +
d56ed2
 bin/dnssec/Makefile.in                 |   8 +-
d56ed2
 bin/dnssec/dnssec-importkey.8          | 112 +++++++++
d56ed2
 bin/dnssec/dnssec-importkey.c          | 399 +++++++++++++++++++++++++++++++++
d56ed2
 bin/dnssec/dnssec-importkey.docbook    | 185 +++++++++++++++
d56ed2
 bin/dnssec/dnssec-importkey.html       | 112 +++++++++
d56ed2
 bin/dnssec/dnssec-settime.c            |   4 +-
d56ed2
 bin/tests/system/conf.sh.in            |   1 +
d56ed2
 bin/tests/system/inline/clean.sh       |   3 +
d56ed2
 bin/tests/system/inline/ns1/root.db.in |   3 +
d56ed2
 bin/tests/system/inline/ns3/named.conf |   8 +
d56ed2
 bin/tests/system/inline/ns3/sign.sh    |  29 +++
d56ed2
 bin/tests/system/inline/setup.sh       |   1 +
d56ed2
 bin/tests/system/inline/tests.sh       |  17 +-
d56ed2
 lib/dns/dnssec.c                       |  96 ++++----
d56ed2
 lib/dns/dst_api.c                      |  10 +
d56ed2
 lib/dns/dst_internal.h                 |   1 +
d56ed2
 lib/dns/dst_parse.c                    |  63 ++++--
d56ed2
 lib/dns/dst_result.c                   |   2 +-
d56ed2
 lib/dns/include/dns/master.h           |   1 +
d56ed2
 lib/dns/include/dst/dst.h              |   6 +
d56ed2
 lib/dns/master.c                       |   4 +-
d56ed2
 lib/dns/openssldsa_link.c              |  19 ++
d56ed2
 lib/dns/opensslecdsa_link.c            |  53 +++--
d56ed2
 lib/dns/opensslgost_link.c             |  27 ++-
d56ed2
 lib/dns/opensslrsa_link.c              |  18 +-
d56ed2
 lib/dns/zone.c                         |   4 +
d56ed2
 28 files changed, 1108 insertions(+), 83 deletions(-)
d56ed2
 create mode 100644 bin/dnssec/dnssec-importkey.8
d56ed2
 create mode 100644 bin/dnssec/dnssec-importkey.c
d56ed2
 create mode 100644 bin/dnssec/dnssec-importkey.docbook
d56ed2
 create mode 100644 bin/dnssec/dnssec-importkey.html
d56ed2
d56ed2
diff --git a/bin/dnssec/Makefile.in b/bin/dnssec/Makefile.in
d56ed2
index 4f8bceb..ecb0fae 100644
d56ed2
--- a/bin/dnssec/Makefile.in
d56ed2
+++ b/bin/dnssec/Makefile.in
d56ed2
@@ -45,13 +45,13 @@ NOSYMLIBS =	${DNSLIBS} ${ISCNOSYMLIBS} @LIBS@
d56ed2
 TARGETS =	dnssec-keygen@EXEEXT@ dnssec-signzone@EXEEXT@ \
d56ed2
 		dnssec-keyfromlabel@EXEEXT@ dnssec-dsfromkey@EXEEXT@ \
d56ed2
 		dnssec-revoke@EXEEXT@ dnssec-settime@EXEEXT@ \
d56ed2
-		dnssec-verify@EXEEXT@
d56ed2
+		dnssec-verify@EXEEXT@ dnssec-importkey@EXEEXT@
d56ed2
 
d56ed2
 OBJS =		dnssectool.@O@
d56ed2
 
d56ed2
 SRCS =		dnssec-dsfromkey.c dnssec-keyfromlabel.c dnssec-keygen.c \
d56ed2
 		dnssec-revoke.c dnssec-settime.c dnssec-signzone.c \
d56ed2
-		dnssec-verify.c dnssectool.c
d56ed2
+		dnssec-verify.c dnssec-importkey.c dnssectool.c 
d56ed2
 
d56ed2
 MANPAGES =	dnssec-dsfromkey.8 dnssec-keyfromlabel.8 dnssec-keygen.8 \
d56ed2
 		dnssec-revoke.8 dnssec-settime.8 dnssec-signzone.8 \
d56ed2
@@ -102,6 +102,10 @@ dnssec-settime@EXEEXT@: dnssec-settime.@O@ ${OBJS} ${DEPLIBS}
d56ed2
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
d56ed2
 	dnssec-settime.@O@ ${OBJS} ${LIBS}
d56ed2
 
d56ed2
+dnssec-importkey@EXEEXT@: dnssec-importkey.@O@ ${OBJS} ${DEPLIBS}
d56ed2
+	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
d56ed2
+	dnssec-importkey.@O@ ${OBJS} ${LIBS}
d56ed2
+
d56ed2
 doc man:: ${MANOBJS}
d56ed2
 
d56ed2
 docclean manclean maintainer-clean::
d56ed2
diff --git a/bin/dnssec/dnssec-importkey.8 b/bin/dnssec/dnssec-importkey.8
d56ed2
new file mode 100644
d56ed2
index 0000000..33a3ef4
d56ed2
--- /dev/null
d56ed2
+++ b/bin/dnssec/dnssec-importkey.8
d56ed2
@@ -0,0 +1,112 @@
d56ed2
+.\" Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
d56ed2
+.\" 
d56ed2
+.\" Permission to use, copy, modify, and/or distribute this software for any
d56ed2
+.\" purpose with or without fee is hereby granted, provided that the above
d56ed2
+.\" copyright notice and this permission notice appear in all copies.
d56ed2
+.\" 
d56ed2
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
d56ed2
+.\" REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
d56ed2
+.\" AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
d56ed2
+.\" INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
d56ed2
+.\" LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
d56ed2
+.\" OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
d56ed2
+.\" PERFORMANCE OF THIS SOFTWARE.
d56ed2
+.\"
d56ed2
+.\" $Id$
d56ed2
+.\"
d56ed2
+.hy 0
d56ed2
+.ad l
d56ed2
+'\" t
d56ed2
+.\"     Title: dnssec-importkey
d56ed2
+.\"    Author: [see the "AUTHOR" section]
d56ed2
+.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
d56ed2
+.\"      Date: August 30, 2013
d56ed2
+.\"    Manual: BIND9
d56ed2
+.\"    Source: BIND9
d56ed2
+.\"  Language: English
d56ed2
+.\"
d56ed2
+.TH "DNSSEC\-IMPORTKEY" "8" "August 30, 2013" "BIND9" "BIND9"
d56ed2
+.\" -----------------------------------------------------------------
d56ed2
+.\" * Define some portability stuff
d56ed2
+.\" -----------------------------------------------------------------
d56ed2
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
d56ed2
+.\" http://bugs.debian.org/507673
d56ed2
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
d56ed2
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
d56ed2
+.ie \n(.g .ds Aq \(aq
d56ed2
+.el       .ds Aq '
d56ed2
+.\" -----------------------------------------------------------------
d56ed2
+.\" * set default formatting
d56ed2
+.\" -----------------------------------------------------------------
d56ed2
+.\" disable hyphenation
d56ed2
+.nh
d56ed2
+.\" disable justification (adjust text to left margin only)
d56ed2
+.ad l
d56ed2
+.\" -----------------------------------------------------------------
d56ed2
+.\" * MAIN CONTENT STARTS HERE *
d56ed2
+.\" -----------------------------------------------------------------
d56ed2
+.SH "NAME"
d56ed2
+dnssec-importkey \- Import DNSKEY records from external systems so they can be managed\&.
d56ed2
+.SH "SYNOPSIS"
d56ed2
+.HP 17
d56ed2
+\fBdnssec\-importkey\fR [\fB\-f\ \fR\fB\fIfilename\fR\fR] [\fB\-K\ \fR\fB\fIdirectory\fR\fR] [\fB\-P\ \fR\fB\fIdate/offset\fR\fR] [\fB\-D\ \fR\fB\fIdate/offset\fR\fR] [\fB\-h\fR] [\fB\-v\ \fR\fB\fIlevel\fR\fR] [\fBkeyname\fR]
d56ed2
+.SH "DESCRIPTION"
d56ed2
+.PP
d56ed2
+\fBdnssec\-importkey\fR
d56ed2
+read a DNSKEY record and generated a \&.key/\&.private key pair\&. Publication (\fB\-P\fR) and deletions (\fB\-D\fR) times can be set for the key\&.
d56ed2
+.SH "OPTIONS"
d56ed2
+.PP
d56ed2
+\-f \fIfilename\fR
d56ed2
+.RS 4
d56ed2
+Filename to read the key from\&.
d56ed2
+.RE
d56ed2
+.PP
d56ed2
+\-K \fIdirectory\fR
d56ed2
+.RS 4
d56ed2
+Sets the directory in which the key files are to reside\&.
d56ed2
+.RE
d56ed2
+.PP
d56ed2
+\-L \fIttl\fR
d56ed2
+.RS 4
d56ed2
+Sets the default TTL to use for this key when it is converted into a DNSKEY RR\&. If the key is imported into a zone, this is the TTL that will be used for it, unless there was already a DNSKEY RRset in place, in which case the existing TTL would take precedence\&. importkey the default TTL to
d56ed2
+0
d56ed2
+or
d56ed2
+none
d56ed2
+removes it\&.
d56ed2
+.RE
d56ed2
+.PP
d56ed2
+\-h
d56ed2
+.RS 4
d56ed2
+Emit usage message and exit\&.
d56ed2
+.RE
d56ed2
+.PP
d56ed2
+\-v \fIlevel\fR
d56ed2
+.RS 4
d56ed2
+Sets the debugging level\&.
d56ed2
+.RE
d56ed2
+.SH "TIMING OPTIONS"
d56ed2
+.PP
d56ed2
+Dates can be expressed in the format YYYYMMDD or YYYYMMDDHHMMSS\&. If the argument begins with a \*(Aq+\*(Aq or \*(Aq\-\*(Aq, it is interpreted as an offset from the present time\&. For convenience, if such an offset is followed by one of the suffixes \*(Aqy\*(Aq, \*(Aqmo\*(Aq, \*(Aqw\*(Aq, \*(Aqd\*(Aq, \*(Aqh\*(Aq, or \*(Aqmi\*(Aq, then the offset is computed in years (defined as 365 24\-hour days, ignoring leap years), months (defined as 30 24\-hour days), weeks, days, hours, or minutes, respectively\&. Without a suffix, the offset is computed in seconds\&. To unset a date, use \*(Aqnone\*(Aq\&.
d56ed2
+.PP
d56ed2
+\-P \fIdate/offset\fR
d56ed2
+.RS 4
d56ed2
+Sets the date on which a key is to be published to the zone\&. After that date, the key will be included in the zone but will not be used to sign it\&.
d56ed2
+.RE
d56ed2
+.PP
d56ed2
+\-D \fIdate/offset\fR
d56ed2
+.RS 4
d56ed2
+Sets the date on which the key is to be deleted\&. After that date, the key will no longer be included in the zone\&. (It may remain in the key repository, however\&.)
d56ed2
+.RE
d56ed2
+.SH "SEE ALSO"
d56ed2
+.PP
d56ed2
+\fBdnssec-keygen\fR(8),
d56ed2
+\fBdnssec-signzone\fR(8),
d56ed2
+BIND 9 Administrator Reference Manual,
d56ed2
+RFC 5011\&.
d56ed2
+.SH "AUTHOR"
d56ed2
+.PP
d56ed2
+Internet Systems Consortium
d56ed2
+.SH "COPYRIGHT"
d56ed2
+.br
d56ed2
+Copyright \(co 2013 Internet Systems Consortium, Inc. ("ISC")
d56ed2
+.br
d56ed2
diff --git a/bin/dnssec/dnssec-importkey.c b/bin/dnssec/dnssec-importkey.c
d56ed2
new file mode 100644
d56ed2
index 0000000..3491828
d56ed2
--- /dev/null
d56ed2
+++ b/bin/dnssec/dnssec-importkey.c
d56ed2
@@ -0,0 +1,399 @@
d56ed2
+/*
d56ed2
+ * Copyright (C) 2008-2012  Internet Systems Consortium, Inc. ("ISC")
d56ed2
+ *
d56ed2
+ * Permission to use, copy, modify, and/or distribute this software for any
d56ed2
+ * purpose with or without fee is hereby granted, provided that the above
d56ed2
+ * copyright notice and this permission notice appear in all copies.
d56ed2
+ *
d56ed2
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
d56ed2
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
d56ed2
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
d56ed2
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
d56ed2
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
d56ed2
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
d56ed2
+ * PERFORMANCE OF THIS SOFTWARE.
d56ed2
+ */
d56ed2
+
d56ed2
+/*! \file */
d56ed2
+
d56ed2
+#include <config.h>
d56ed2
+
d56ed2
+#include <stdlib.h>
d56ed2
+
d56ed2
+#include <isc/buffer.h>
d56ed2
+#include <isc/commandline.h>
d56ed2
+#include <isc/entropy.h>
d56ed2
+#include <isc/hash.h>
d56ed2
+#include <isc/mem.h>
d56ed2
+#include <isc/print.h>
d56ed2
+#include <isc/string.h>
d56ed2
+#include <isc/util.h>
d56ed2
+
d56ed2
+#include <dns/callbacks.h>
d56ed2
+#include <dns/db.h>
d56ed2
+#include <dns/dbiterator.h>
d56ed2
+#include <dns/ds.h>
d56ed2
+#include <dns/fixedname.h>
d56ed2
+#include <dns/keyvalues.h>
d56ed2
+#include <dns/log.h>
d56ed2
+#include <dns/master.h>
d56ed2
+#include <dns/name.h>
d56ed2
+#include <dns/rdata.h>
d56ed2
+#include <dns/rdataclass.h>
d56ed2
+#include <dns/rdataset.h>
d56ed2
+#include <dns/rdatasetiter.h>
d56ed2
+#include <dns/rdatatype.h>
d56ed2
+#include <dns/result.h>
d56ed2
+
d56ed2
+#include <dst/dst.h>
d56ed2
+
d56ed2
+#include "dnssectool.h"
d56ed2
+
d56ed2
+#ifndef PATH_MAX
d56ed2
+#define PATH_MAX 1024   /* AIX, WIN32, and others don't define this. */
d56ed2
+#endif
d56ed2
+
d56ed2
+const char *program = "dnssec-importkey";
d56ed2
+int verbose;
d56ed2
+
d56ed2
+static dns_rdataclass_t rdclass;
d56ed2
+static dns_fixedname_t	fixed;
d56ed2
+static dns_name_t	*name = NULL;
d56ed2
+static isc_mem_t	*mctx = NULL;
d56ed2
+static isc_boolean_t	setpub = ISC_FALSE, setdel = ISC_FALSE;
d56ed2
+static isc_stdtime_t	pub = 0, del = 0;
d56ed2
+
d56ed2
+static isc_result_t
d56ed2
+initname(char *setname) {
d56ed2
+	isc_result_t result;
d56ed2
+	isc_buffer_t buf;
d56ed2
+
d56ed2
+	dns_fixedname_init(&fixed);
d56ed2
+	name = dns_fixedname_name(&fixed);
d56ed2
+
d56ed2
+	isc_buffer_init(&buf, setname, strlen(setname));
d56ed2
+	isc_buffer_add(&buf, strlen(setname));
d56ed2
+	result = dns_name_fromtext(name, &buf, dns_rootname, 0, NULL);
d56ed2
+	return (result);
d56ed2
+}
d56ed2
+
d56ed2
+static void
d56ed2
+db_load_from_stream(dns_db_t *db, FILE *fp) {
d56ed2
+	isc_result_t result;
d56ed2
+	dns_rdatacallbacks_t callbacks;
d56ed2
+
d56ed2
+	dns_rdatacallbacks_init(&callbacks);
d56ed2
+	result = dns_db_beginload(db, &callbacks.add, &callbacks.add_private);
d56ed2
+	if (result != ISC_R_SUCCESS)
d56ed2
+		fatal("dns_db_beginload failed: %s", isc_result_totext(result));
d56ed2
+
d56ed2
+	result = dns_master_loadstream(fp, name, name, rdclass, 0,
d56ed2
+				       &callbacks, mctx);
d56ed2
+	if (result != ISC_R_SUCCESS)
d56ed2
+		fatal("can't load from input: %s", isc_result_totext(result));
d56ed2
+
d56ed2
+	result = dns_db_endload(db, &callbacks.add_private);
d56ed2
+	if (result != ISC_R_SUCCESS)
d56ed2
+		fatal("dns_db_endload failed: %s", isc_result_totext(result));
d56ed2
+}
d56ed2
+
d56ed2
+static isc_result_t
d56ed2
+loadset(const char *filename, dns_rdataset_t *rdataset) {
d56ed2
+	isc_result_t	 result;
d56ed2
+	dns_db_t	 *db = NULL;
d56ed2
+	dns_dbnode_t	 *node = NULL;
d56ed2
+	char setname[DNS_NAME_FORMATSIZE];
d56ed2
+
d56ed2
+	dns_name_format(name, setname, sizeof(setname));
d56ed2
+
d56ed2
+	result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone,
d56ed2
+			       rdclass, 0, NULL, &db);
d56ed2
+	if (result != ISC_R_SUCCESS)
d56ed2
+		fatal("can't create database");
d56ed2
+
d56ed2
+	if (strcmp(filename, "-") == 0) {
d56ed2
+		db_load_from_stream(db, stdin);
d56ed2
+		filename = "input";
d56ed2
+	} else {
d56ed2
+		result = dns_db_load3(db, filename, dns_masterformat_text,
d56ed2
+				      DNS_MASTER_NOTTL);
d56ed2
+		if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE)
d56ed2
+			fatal("can't load %s: %s", filename,
d56ed2
+			      isc_result_totext(result));
d56ed2
+	}
d56ed2
+
d56ed2
+	result = dns_db_findnode(db, name, ISC_FALSE, &node);
d56ed2
+	if (result != ISC_R_SUCCESS)
d56ed2
+		fatal("can't find %s node in %s", setname, filename);
d56ed2
+
d56ed2
+	result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_dnskey,
d56ed2
+				     0, 0, rdataset, NULL);
d56ed2
+
d56ed2
+	if (result == ISC_R_NOTFOUND)
d56ed2
+		fatal("no DNSKEY RR for %s in %s", setname, filename);
d56ed2
+	else if (result != ISC_R_SUCCESS)
d56ed2
+		fatal("dns_db_findrdataset");
d56ed2
+
d56ed2
+	if (node != NULL)
d56ed2
+		dns_db_detachnode(db, &node);
d56ed2
+	if (db != NULL)
d56ed2
+		dns_db_detach(&db);
d56ed2
+	return (result);
d56ed2
+}
d56ed2
+
d56ed2
+static void
d56ed2
+loadkey(char *filename, unsigned char *key_buf, unsigned int key_buf_size,
d56ed2
+	dns_rdata_t *rdata)
d56ed2
+{
d56ed2
+	isc_result_t  result;
d56ed2
+	dst_key_t     *key = NULL;
d56ed2
+	isc_buffer_t  keyb;
d56ed2
+	isc_region_t  r;
d56ed2
+
d56ed2
+	dns_rdata_init(rdata);
d56ed2
+
d56ed2
+	isc_buffer_init(&keyb, key_buf, key_buf_size);
d56ed2
+
d56ed2
+	result = dst_key_fromnamedfile(filename, NULL, DST_TYPE_PUBLIC,
d56ed2
+				       mctx, &key);
d56ed2
+	if (result != ISC_R_SUCCESS)
d56ed2
+		fatal("invalid keyfile name %s: %s",
d56ed2
+		      filename, isc_result_totext(result));
d56ed2
+
d56ed2
+	if (verbose > 2) {
d56ed2
+		char keystr[DST_KEY_FORMATSIZE];
d56ed2
+
d56ed2
+		dst_key_format(key, keystr, sizeof(keystr));
d56ed2
+		fprintf(stderr, "%s: %s\n", program, keystr);
d56ed2
+	}
d56ed2
+
d56ed2
+	result = dst_key_todns(key, &keyb);
d56ed2
+	if (result != ISC_R_SUCCESS)
d56ed2
+		fatal("can't decode key");
d56ed2
+
d56ed2
+	isc_buffer_usedregion(&keyb, &r);
d56ed2
+	dns_rdata_fromregion(rdata, dst_key_class(key),
d56ed2
+			     dns_rdatatype_dnskey, &r);
d56ed2
+
d56ed2
+	rdclass = dst_key_class(key);
d56ed2
+
d56ed2
+	dns_fixedname_init(&fixed);
d56ed2
+	name = dns_fixedname_name(&fixed);
d56ed2
+	result = dns_name_copy(dst_key_name(key), name, NULL);
d56ed2
+	if (result != ISC_R_SUCCESS)
d56ed2
+		fatal("can't copy name");
d56ed2
+
d56ed2
+	dst_key_free(&key);
d56ed2
+}
d56ed2
+
d56ed2
+static void
d56ed2
+emit(const char *dir, dns_rdata_t *rdata) {
d56ed2
+	isc_result_t result;
d56ed2
+	char keystr[DST_KEY_FORMATSIZE];
d56ed2
+	char newname[1024];
d56ed2
+	isc_buffer_t buf;
d56ed2
+	dst_key_t *key = NULL;
d56ed2
+
d56ed2
+	isc_buffer_init(&buf, rdata->data, rdata->length);
d56ed2
+	isc_buffer_add(&buf, rdata->length);
d56ed2
+	result = dst_key_fromdns(name, rdclass, &buf, mctx, &key);
d56ed2
+	if (result != ISC_R_SUCCESS) {
d56ed2
+		fatal("dst_key_fromdns: %s", isc_result_totext(result));
d56ed2
+	}
d56ed2
+
d56ed2
+	dst_key_setexternal(key, ISC_TRUE);
d56ed2
+	if (setpub)
d56ed2
+		dst_key_settime(key, DST_TIME_PUBLISH, pub);
d56ed2
+	if (setdel)
d56ed2
+		dst_key_settime(key, DST_TIME_DELETE, del);
d56ed2
+
d56ed2
+	isc_buffer_init(&buf, newname, sizeof(newname));
d56ed2
+	result = dst_key_buildfilename(key, DST_TYPE_PUBLIC, dir, &buf;;
d56ed2
+	if (result != ISC_R_SUCCESS) {
d56ed2
+		fatal("Failed to build public key filename: %s",
d56ed2
+		      isc_result_totext(result));
d56ed2
+	}
d56ed2
+
d56ed2
+	result = dst_key_tofile(key, DST_TYPE_PUBLIC|DST_TYPE_PRIVATE,
d56ed2
+				dir);
d56ed2
+	if (result != ISC_R_SUCCESS) {
d56ed2
+		dst_key_format(key, keystr, sizeof(keystr));
d56ed2
+		fatal("Failed to write key %s: %s", keystr,
d56ed2
+		      isc_result_totext(result));
d56ed2
+	}
d56ed2
+
d56ed2
+	printf("%s\n", newname);
d56ed2
+
d56ed2
+	isc_buffer_clear(&buf;;
d56ed2
+	result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, dir, &buf;;
d56ed2
+	if (result != ISC_R_SUCCESS) {
d56ed2
+		fatal("Failed to build private key filename: %s",
d56ed2
+		      isc_result_totext(result));
d56ed2
+	}
d56ed2
+	printf("%s\n", newname);
d56ed2
+	dst_key_free(&key);
d56ed2
+}
d56ed2
+
d56ed2
+ISC_PLATFORM_NORETURN_PRE static void
d56ed2
+usage(void) ISC_PLATFORM_NORETURN_POST;
d56ed2
+
d56ed2
+static void
d56ed2
+usage(void) {
d56ed2
+	fprintf(stderr, "Usage:\n");
d56ed2
+	fprintf(stderr,	"    %s options [-K dir] file\n\n", program);
d56ed2
+	fprintf(stderr, "Version: %s\n", VERSION);
d56ed2
+	fprintf(stderr, "Options:\n");
d56ed2
+	fprintf(stderr, "    -v <verbose level>\n");
d56ed2
+	fprintf(stderr, "    -K <directory>: directory in which to store "
d56ed2
+			"the keyset files\n");
d56ed2
+	fprintf(stderr, "    -f file: read keyset from zone file\n");
d56ed2
+
d56ed2
+	exit (-1);
d56ed2
+}
d56ed2
+
d56ed2
+int
d56ed2
+main(int argc, char **argv) {
d56ed2
+	char		*classname = NULL;
d56ed2
+	char		*filename = NULL, *dir = NULL, *namestr;
d56ed2
+	char		*endp;
d56ed2
+	int		ch;
d56ed2
+	isc_result_t	result;
d56ed2
+	isc_log_t	*log = NULL;
d56ed2
+	isc_entropy_t	*ectx = NULL;
d56ed2
+	dns_rdataset_t	rdataset;
d56ed2
+	dns_rdata_t	rdata;
d56ed2
+	isc_stdtime_t   now;
d56ed2
+
d56ed2
+	dns_rdata_init(&rdata);
d56ed2
+	isc_stdtime_get(&now;;
d56ed2
+
d56ed2
+	if (argc == 1)
d56ed2
+		usage();
d56ed2
+
d56ed2
+	result = isc_mem_create(0, 0, &mctx);
d56ed2
+	if (result != ISC_R_SUCCESS)
d56ed2
+		fatal("out of memory");
d56ed2
+
d56ed2
+	dns_result_register();
d56ed2
+
d56ed2
+	isc_commandline_errprint = ISC_FALSE;
d56ed2
+
d56ed2
+	while ((ch = isc_commandline_parse(argc, argv, "D:f:hK:P:v:")) != -1) {
d56ed2
+		switch (ch) {
d56ed2
+                case 'D':
d56ed2
+                        if (setdel)
d56ed2
+                                fatal("-D specified more than once");
d56ed2
+
d56ed2
+			setdel = ISC_TRUE;
d56ed2
+			del = strtotime(isc_commandline_argument, now, now);
d56ed2
+			break;
d56ed2
+		case 'K':
d56ed2
+			dir = isc_commandline_argument;
d56ed2
+			if (strlen(dir) == 0U)
d56ed2
+				fatal("directory must be non-empty string");
d56ed2
+			break;
d56ed2
+                case 'P':
d56ed2
+                        if (setpub)
d56ed2
+                                fatal("-P specified more than once");
d56ed2
+
d56ed2
+			setpub = ISC_TRUE;
d56ed2
+			pub = strtotime(isc_commandline_argument, now, now);
d56ed2
+                        break;
d56ed2
+		case 'f':
d56ed2
+			filename = isc_commandline_argument;
d56ed2
+			break;
d56ed2
+		case 'v':
d56ed2
+			verbose = strtol(isc_commandline_argument, &endp, 0);
d56ed2
+			if (*endp != '\0')
d56ed2
+				fatal("-v must be followed by a number");
d56ed2
+			break;
d56ed2
+		case '?':
d56ed2
+			if (isc_commandline_option != '?')
d56ed2
+				fprintf(stderr, "%s: invalid argument -%c\n",
d56ed2
+					program, isc_commandline_option);
d56ed2
+			/* FALLTHROUGH */
d56ed2
+		case 'h':
d56ed2
+			usage();
d56ed2
+
d56ed2
+		default:
d56ed2
+			fprintf(stderr, "%s: unhandled option -%c\n",
d56ed2
+				program, isc_commandline_option);
d56ed2
+			exit(1);
d56ed2
+		}
d56ed2
+	}
d56ed2
+
d56ed2
+	rdclass = strtoclass(classname);
d56ed2
+
d56ed2
+	if (argc < isc_commandline_index + 1 && filename == NULL)
d56ed2
+		fatal("the key file name was not specified");
d56ed2
+	if (argc > isc_commandline_index + 1)
d56ed2
+		fatal("extraneous arguments");
d56ed2
+
d56ed2
+	if (ectx == NULL)
d56ed2
+		setup_entropy(mctx, NULL, &ectx);
d56ed2
+	result = isc_hash_create(mctx, ectx, DNS_NAME_MAXWIRE);
d56ed2
+	if (result != ISC_R_SUCCESS)
d56ed2
+		fatal("could not initialize hash");
d56ed2
+	result = dst_lib_init(mctx, ectx,
d56ed2
+			      ISC_ENTROPY_BLOCKING | ISC_ENTROPY_GOODONLY);
d56ed2
+	if (result != ISC_R_SUCCESS)
d56ed2
+		fatal("could not initialize dst: %s",
d56ed2
+		      isc_result_totext(result));
d56ed2
+	isc_entropy_stopcallbacksources(ectx);
d56ed2
+
d56ed2
+	setup_logging(verbose, mctx, &log;;
d56ed2
+
d56ed2
+	dns_rdataset_init(&rdataset);
d56ed2
+
d56ed2
+	if (filename != NULL) {
d56ed2
+		if (argc < isc_commandline_index + 1 && filename != NULL) {
d56ed2
+			/* using zone name as the zone file name */
d56ed2
+			namestr = filename;
d56ed2
+		} else
d56ed2
+			namestr = argv[isc_commandline_index];
d56ed2
+
d56ed2
+		result = initname(namestr);
d56ed2
+		if (result != ISC_R_SUCCESS)
d56ed2
+			fatal("could not initialize name %s", namestr);
d56ed2
+
d56ed2
+		result = loadset(filename, &rdataset);
d56ed2
+
d56ed2
+		if (result != ISC_R_SUCCESS)
d56ed2
+			fatal("could not load DNSKEY set: %s\n",
d56ed2
+			      isc_result_totext(result));
d56ed2
+
d56ed2
+		for (result = dns_rdataset_first(&rdataset);
d56ed2
+		     result == ISC_R_SUCCESS;
d56ed2
+		     result = dns_rdataset_next(&rdataset)) {
d56ed2
+
d56ed2
+			dns_rdata_init(&rdata);
d56ed2
+			dns_rdataset_current(&rdataset, &rdata);
d56ed2
+			emit(dir, &rdata);
d56ed2
+		}
d56ed2
+	} else {
d56ed2
+		unsigned char key_buf[DST_KEY_MAXSIZE];
d56ed2
+
d56ed2
+		loadkey(argv[isc_commandline_index], key_buf,
d56ed2
+			DST_KEY_MAXSIZE, &rdata);
d56ed2
+
d56ed2
+		emit(dir, &rdata);
d56ed2
+	}
d56ed2
+
d56ed2
+	if (dns_rdataset_isassociated(&rdataset))
d56ed2
+		dns_rdataset_disassociate(&rdataset);
d56ed2
+	cleanup_logging(&log;;
d56ed2
+	dst_lib_destroy();
d56ed2
+	isc_hash_destroy();
d56ed2
+	cleanup_entropy(&ectx);
d56ed2
+	dns_name_destroy();
d56ed2
+	if (verbose > 10)
d56ed2
+		isc_mem_stats(mctx, stdout);
d56ed2
+	isc_mem_destroy(&mctx);
d56ed2
+
d56ed2
+	fflush(stdout);
d56ed2
+	if (ferror(stdout)) {
d56ed2
+		fprintf(stderr, "write error\n");
d56ed2
+		return (1);
d56ed2
+	} else
d56ed2
+		return (0);
d56ed2
+}
d56ed2
diff --git a/bin/dnssec/dnssec-importkey.docbook b/bin/dnssec/dnssec-importkey.docbook
d56ed2
new file mode 100644
d56ed2
index 0000000..b8d160c
d56ed2
--- /dev/null
d56ed2
+++ b/bin/dnssec/dnssec-importkey.docbook
d56ed2
@@ -0,0 +1,185 @@
d56ed2
+
d56ed2
+               "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
d56ed2
+               []>
d56ed2
+
d56ed2
+ - Copyright (C) 2009-2011  Internet Systems Consortium, Inc. ("ISC")
d56ed2
+ -
d56ed2
+ - Permission to use, copy, modify, and/or distribute this software for any
d56ed2
+ - purpose with or without fee is hereby granted, provided that the above
d56ed2
+ - copyright notice and this permission notice appear in all copies.
d56ed2
+ -
d56ed2
+ - THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
d56ed2
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
d56ed2
+ - AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
d56ed2
+ - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
d56ed2
+ - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
d56ed2
+ - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
d56ed2
+ - PERFORMANCE OF THIS SOFTWARE.
d56ed2
+-->
d56ed2
+
d56ed2
+
d56ed2
+<refentry id="man.dnssec-importkey">
d56ed2
+  <refentryinfo>
d56ed2
+    <date>August 30, 2013</date>
d56ed2
+  </refentryinfo>
d56ed2
+
d56ed2
+  <refmeta>
d56ed2
+    <refentrytitle><application>dnssec-importkey</application></refentrytitle>
d56ed2
+    <manvolnum>8</manvolnum>
d56ed2
+    <refmiscinfo>BIND9</refmiscinfo>
d56ed2
+  </refmeta>
d56ed2
+
d56ed2
+  <refnamediv>
d56ed2
+    <refname><application>dnssec-importkey</application></refname>
d56ed2
+    <refpurpose>Import DNSKEY records from external systems so they can be managed.</refpurpose>
d56ed2
+  </refnamediv>
d56ed2
+
d56ed2
+  <docinfo>
d56ed2
+    <copyright>
d56ed2
+      <year>2013</year>
d56ed2
+      <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
d56ed2
+    </copyright>
d56ed2
+  </docinfo>
d56ed2
+
d56ed2
+  <refsynopsisdiv>
d56ed2
+    <cmdsynopsis>
d56ed2
+      <command>dnssec-importkey</command>
d56ed2
+      <arg><option>-f <replaceable class="parameter">filename</replaceable></option></arg>
d56ed2
+      <arg><option>-K <replaceable class="parameter">directory</replaceable></option></arg>
d56ed2
+      <arg><option>-P <replaceable class="parameter">date/offset</replaceable></option></arg>
d56ed2
+      <arg><option>-D <replaceable class="parameter">date/offset</replaceable></option></arg>
d56ed2
+      <arg><option>-h</option></arg>
d56ed2
+      <arg><option>-v <replaceable class="parameter">level</replaceable></option></arg>
d56ed2
+      <arg><option>keyname</option></arg>
d56ed2
+    </cmdsynopsis>
d56ed2
+  </refsynopsisdiv>
d56ed2
+
d56ed2
+  <refsect1>
d56ed2
+    <title>DESCRIPTION</title>
d56ed2
+    <para><command>dnssec-importkey</command>
d56ed2
+      read a DNSKEY record and generated a .key/.private key pair.
d56ed2
+      Publication (<option>-P</option>) and deletions (<option>-D</option>)
d56ed2
+      times can be set for the key.
d56ed2
+    </para>
d56ed2
+  </refsect1>
d56ed2
+
d56ed2
+  <refsect1>
d56ed2
+    <title>OPTIONS</title>
d56ed2
+
d56ed2
+    <variablelist>
d56ed2
+      <varlistentry>
d56ed2
+	<term>-f <replaceable class="parameter">filename</replaceable></term>
d56ed2
+        <listitem>
d56ed2
+	  <para>
d56ed2
+	    Filename to read the key from.
d56ed2
+	  </para>
d56ed2
+        </listitem>
d56ed2
+      </varlistentry>
d56ed2
+  
d56ed2
+      <varlistentry>
d56ed2
+        <term>-K <replaceable class="parameter">directory</replaceable></term>
d56ed2
+        <listitem>
d56ed2
+          <para>
d56ed2
+            Sets the directory in which the key files are to reside.
d56ed2
+          </para>
d56ed2
+        </listitem>
d56ed2
+      </varlistentry>
d56ed2
+
d56ed2
+      <varlistentry>
d56ed2
+        <term>-L <replaceable class="parameter">ttl</replaceable></term>
d56ed2
+        <listitem>
d56ed2
+          <para>
d56ed2
+            Sets the default TTL to use for this key when it is converted
d56ed2
+            into a DNSKEY RR.  If the key is imported into a zone,
d56ed2
+            this is the TTL that will be used for it, unless there was
d56ed2
+            already a DNSKEY RRset in place, in which case the existing TTL
d56ed2
+            would take precedence.  importkey the default TTL to
d56ed2
+            <literal>0</literal> or <literal>none</literal> removes it.
d56ed2
+          </para>
d56ed2
+        </listitem>
d56ed2
+      </varlistentry>
d56ed2
+
d56ed2
+      <varlistentry>
d56ed2
+	<term>-h</term>
d56ed2
+        <listitem>
d56ed2
+	  <para>
d56ed2
+	    Emit usage message and exit.
d56ed2
+	  </para>
d56ed2
+        </listitem>
d56ed2
+      </varlistentry>
d56ed2
+  
d56ed2
+      <varlistentry>
d56ed2
+        <term>-v <replaceable class="parameter">level</replaceable></term>
d56ed2
+        <listitem>
d56ed2
+          <para>
d56ed2
+            Sets the debugging level.
d56ed2
+          </para>
d56ed2
+        </listitem>
d56ed2
+      </varlistentry>
d56ed2
+
d56ed2
+    </variablelist>
d56ed2
+  </refsect1>
d56ed2
+
d56ed2
+  <refsect1>
d56ed2
+    <title>TIMING OPTIONS</title>
d56ed2
+    <para>
d56ed2
+      Dates can be expressed in the format YYYYMMDD or YYYYMMDDHHMMSS.
d56ed2
+      If the argument begins with a '+' or '-', it is interpreted as
d56ed2
+      an offset from the present time.  For convenience, if such an offset
d56ed2
+      is followed by one of the suffixes 'y', 'mo', 'w', 'd', 'h', or 'mi',
d56ed2
+      then the offset is computed in years (defined as 365 24-hour days,
d56ed2
+      ignoring leap years), months (defined as 30 24-hour days), weeks,
d56ed2
+      days, hours, or minutes, respectively.  Without a suffix, the offset
d56ed2
+      is computed in seconds.  To unset a date, use 'none'.
d56ed2
+    </para>
d56ed2
+
d56ed2
+    <variablelist>
d56ed2
+      <varlistentry>
d56ed2
+        <term>-P <replaceable class="parameter">date/offset</replaceable></term>
d56ed2
+        <listitem>
d56ed2
+          <para>
d56ed2
+            Sets the date on which a key is to be published to the zone.
d56ed2
+            After that date, the key will be included in the zone but will
d56ed2
+            not be used to sign it.
d56ed2
+          </para>
d56ed2
+        </listitem>
d56ed2
+      </varlistentry>
d56ed2
+
d56ed2
+      <varlistentry>
d56ed2
+        <term>-D <replaceable class="parameter">date/offset</replaceable></term>
d56ed2
+        <listitem>
d56ed2
+          <para>
d56ed2
+            Sets the date on which the key is to be deleted.  After that
d56ed2
+            date, the key will no longer be included in the zone.  (It
d56ed2
+            may remain in the key repository, however.)
d56ed2
+          </para>
d56ed2
+        </listitem>
d56ed2
+      </varlistentry>
d56ed2
+
d56ed2
+    </variablelist>
d56ed2
+  </refsect1>
d56ed2
+
d56ed2
+  <refsect1>
d56ed2
+    <title>SEE ALSO</title>
d56ed2
+    <para><citerefentry>
d56ed2
+        <refentrytitle>dnssec-keygen</refentrytitle><manvolnum>8</manvolnum>
d56ed2
+      </citerefentry>,
d56ed2
+      <citerefentry>
d56ed2
+        <refentrytitle>dnssec-signzone</refentrytitle><manvolnum>8</manvolnum>
d56ed2
+      </citerefentry>,
d56ed2
+      <citetitle>BIND 9 Administrator Reference Manual</citetitle>,
d56ed2
+      <citetitle>RFC 5011</citetitle>.
d56ed2
+    </para>
d56ed2
+  </refsect1>
d56ed2
+
d56ed2
+  <refsect1>
d56ed2
+    <title>AUTHOR</title>
d56ed2
+    <para><corpauthor>Internet Systems Consortium</corpauthor>
d56ed2
+    </para>
d56ed2
+  </refsect1>
d56ed2
+
d56ed2
+</refentry>
d56ed2
+ - Local variables:
d56ed2
+ - mode: sgml
d56ed2
+ - End:
d56ed2
+-->
d56ed2
diff --git a/bin/dnssec/dnssec-importkey.html b/bin/dnssec/dnssec-importkey.html
d56ed2
new file mode 100644
d56ed2
index 0000000..f74be50
d56ed2
--- /dev/null
d56ed2
+++ b/bin/dnssec/dnssec-importkey.html
d56ed2
@@ -0,0 +1,112 @@
d56ed2
+
d56ed2
+ - Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
d56ed2
+ - 
d56ed2
+ - Permission to use, copy, modify, and/or distribute this software for any
d56ed2
+ - purpose with or without fee is hereby granted, provided that the above
d56ed2
+ - copyright notice and this permission notice appear in all copies.
d56ed2
+ - 
d56ed2
+ - THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
d56ed2
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
d56ed2
+ - AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
d56ed2
+ - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
d56ed2
+ - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
d56ed2
+ - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
d56ed2
+ - PERFORMANCE OF THIS SOFTWARE.
d56ed2
+-->
d56ed2
+
d56ed2
+<html>
d56ed2
+<head>
d56ed2
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
d56ed2
+<title>dnssec-importkey</title>
d56ed2
+<meta name="generator" content="DocBook XSL Stylesheets V1.78.1">
d56ed2
+</head>
d56ed2
+<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
d56ed2
+
d56ed2
+
d56ed2
+

Name

d56ed2
+

dnssec-importkey — Import DNSKEY records from external systems so they can be managed.

d56ed2
+
d56ed2
+
d56ed2
+

Synopsis

d56ed2
+

dnssec-importkey [-f filename] [-K directory] [-P date/offset] [-D date/offset] [-h] [-v level] [keyname]

d56ed2
+
d56ed2
+
d56ed2
+

DESCRIPTION

d56ed2
+

dnssec-importkey

d56ed2
+      read a DNSKEY record and generated a .key/.private key pair.
d56ed2
+      Publication (-P) and deletions (-D)
d56ed2
+      times can be set for the key.
d56ed2
+    

d56ed2
+
d56ed2
+
d56ed2
+

OPTIONS

d56ed2
+
d56ed2
+
-f filename
d56ed2
+

d56ed2
+	    Filename to read the key from.
d56ed2
+	  

d56ed2
+
-K directory
d56ed2
+

d56ed2
+            Sets the directory in which the key files are to reside.
d56ed2
+          

d56ed2
+
-L ttl
d56ed2
+

d56ed2
+            Sets the default TTL to use for this key when it is converted
d56ed2
+            into a DNSKEY RR.  If the key is imported into a zone,
d56ed2
+            this is the TTL that will be used for it, unless there was
d56ed2
+            already a DNSKEY RRset in place, in which case the existing TTL
d56ed2
+            would take precedence.  importkey the default TTL to
d56ed2
+            0 or none removes it.
d56ed2
+          

d56ed2
+
-h
d56ed2
+

d56ed2
+	    Emit usage message and exit.
d56ed2
+	  

d56ed2
+
-v level
d56ed2
+

d56ed2
+            Sets the debugging level.
d56ed2
+          

d56ed2
+
d56ed2
+
d56ed2
+
d56ed2
+

TIMING OPTIONS

d56ed2
+

d56ed2
+      Dates can be expressed in the format YYYYMMDD or YYYYMMDDHHMMSS.
d56ed2
+      If the argument begins with a '+' or '-', it is interpreted as
d56ed2
+      an offset from the present time.  For convenience, if such an offset
d56ed2
+      is followed by one of the suffixes 'y', 'mo', 'w', 'd', 'h', or 'mi',
d56ed2
+      then the offset is computed in years (defined as 365 24-hour days,
d56ed2
+      ignoring leap years), months (defined as 30 24-hour days), weeks,
d56ed2
+      days, hours, or minutes, respectively.  Without a suffix, the offset
d56ed2
+      is computed in seconds.  To unset a date, use 'none'.
d56ed2
+    

d56ed2
+
d56ed2
+
-P date/offset
d56ed2
+

d56ed2
+            Sets the date on which a key is to be published to the zone.
d56ed2
+            After that date, the key will be included in the zone but will
d56ed2
+            not be used to sign it.
d56ed2
+          

d56ed2
+
-D date/offset
d56ed2
+

d56ed2
+            Sets the date on which the key is to be deleted.  After that
d56ed2
+            date, the key will no longer be included in the zone.  (It
d56ed2
+            may remain in the key repository, however.)
d56ed2
+          

d56ed2
+
d56ed2
+
d56ed2
+
d56ed2
+

SEE ALSO

d56ed2
+

dnssec-keygen(8),

d56ed2
+      dnssec-signzone(8),
d56ed2
+      BIND 9 Administrator Reference Manual,
d56ed2
+      RFC 5011.
d56ed2
+    

d56ed2
+
d56ed2
+
d56ed2
+

AUTHOR

d56ed2
+

Internet Systems Consortium

d56ed2
+    

d56ed2
+
d56ed2
+</body>
d56ed2
+</html>
d56ed2
diff --git a/bin/dnssec/dnssec-settime.c b/bin/dnssec/dnssec-settime.c
d56ed2
index 4c88a07..108d803 100644
d56ed2
--- a/bin/dnssec/dnssec-settime.c
d56ed2
+++ b/bin/dnssec/dnssec-settime.c
d56ed2
@@ -370,7 +370,7 @@ main(int argc, char **argv) {
d56ed2
 		if (result != ISC_R_SUCCESS)
d56ed2
 			fatal("Invalid keyfile %s: %s",
d56ed2
 			      filename, isc_result_totext(result));
d56ed2
-		if (!dst_key_isprivate(prevkey))
d56ed2
+		if (!dst_key_isprivate(prevkey) && !dst_key_isexternal(prevkey))
d56ed2
 			fatal("%s is not a private key", filename);
d56ed2
 
d56ed2
 		name = dst_key_name(prevkey);
d56ed2
@@ -462,7 +462,7 @@ main(int argc, char **argv) {
d56ed2
 		fatal("Invalid keyfile %s: %s",
d56ed2
 		      filename, isc_result_totext(result));
d56ed2
 
d56ed2
-	if (!dst_key_isprivate(key))
d56ed2
+	if (!dst_key_isprivate(key) && !dst_key_isexternal(key))
d56ed2
 		fatal("%s is not a private key", filename);
d56ed2
 
d56ed2
 	dst_key_format(key, keystr, sizeof(keystr));
d56ed2
diff --git a/bin/tests/system/conf.sh.in b/bin/tests/system/conf.sh.in
d56ed2
index 60f22f8..b15853d 100644
d56ed2
--- a/bin/tests/system/conf.sh.in
d56ed2
+++ b/bin/tests/system/conf.sh.in
d56ed2
@@ -43,6 +43,7 @@ SIGNER=$TOP/bin/dnssec/dnssec-signzone
d56ed2
 REVOKE=$TOP/bin/dnssec/dnssec-revoke
d56ed2
 SETTIME=$TOP/bin/dnssec/dnssec-settime
d56ed2
 DSFROMKEY=$TOP/bin/dnssec/dnssec-dsfromkey
d56ed2
+IMPORTKEY=$TOP/bin/dnssec/dnssec-importkey
d56ed2
 CHECKDS=$TOP/bin/python/dnssec-checkds
d56ed2
 COVERAGE=$TOP/bin/python/dnssec-coverage
d56ed2
 CHECKZONE=$TOP/bin/check/named-checkzone
d56ed2
diff --git a/bin/tests/system/inline/clean.sh b/bin/tests/system/inline/clean.sh
d56ed2
index 8ef3e2c..412031a 100644
d56ed2
--- a/bin/tests/system/inline/clean.sh
d56ed2
+++ b/bin/tests/system/inline/clean.sh
d56ed2
@@ -60,6 +60,9 @@ rm -f ns3/retransfer.bk
d56ed2
 rm -f ns3/retransfer.bk.jnl
d56ed2
 rm -f ns3/retransfer.bk.signed
d56ed2
 rm -f ns3/retransfer.bk.signed.jnl
d56ed2
+rm -f ns3/externalkey.db
d56ed2
+rm -f ns3/externalkey.db.signed
d56ed2
+rm -f ns3/externalkey.db.signed.jnl
d56ed2
 rm -f ns4/K*
d56ed2
 rm -f ns4/noixfr.db
d56ed2
 rm -f ns4/noixfr.db.jnl
d56ed2
diff --git a/bin/tests/system/inline/ns1/root.db.in b/bin/tests/system/inline/ns1/root.db.in
d56ed2
index 8d3af51..a08db51 100644
d56ed2
--- a/bin/tests/system/inline/ns1/root.db.in
d56ed2
+++ b/bin/tests/system/inline/ns1/root.db.in
d56ed2
@@ -50,3 +50,6 @@ ns3.retransfer.		A	10.53.0.3
d56ed2
 
d56ed2
 nsec3.			NS	ns3.nsec3.
d56ed2
 ns3.nsec3.		A	10.53.0.3
d56ed2
+
d56ed2
+externalkey.		NS	ns3.externalkey.
d56ed2
+ns3.externalkey.	A	10.53.0.3
d56ed2
diff --git a/bin/tests/system/inline/ns3/named.conf b/bin/tests/system/inline/ns3/named.conf
d56ed2
index a17384c..7c23edd 100644
d56ed2
--- a/bin/tests/system/inline/ns3/named.conf
d56ed2
+++ b/bin/tests/system/inline/ns3/named.conf
d56ed2
@@ -103,3 +103,11 @@ zone "nsec3" {
d56ed2
 	allow-update { any; };
d56ed2
 	file "nsec3.db";
d56ed2
 };
d56ed2
+
d56ed2
+zone "externalkey" {
d56ed2
+	type master;
d56ed2
+	inline-signing yes;
d56ed2
+	auto-dnssec maintain;
d56ed2
+	allow-update { any; };
d56ed2
+	file "externalkey.db";
d56ed2
+};
d56ed2
diff --git a/bin/tests/system/inline/ns3/sign.sh b/bin/tests/system/inline/ns3/sign.sh
d56ed2
index bbd11af..f695973 100644
d56ed2
--- a/bin/tests/system/inline/ns3/sign.sh
d56ed2
+++ b/bin/tests/system/inline/ns3/sign.sh
d56ed2
@@ -92,3 +92,32 @@ do
d56ed2
 	keyname=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 768 -n zone $zone`
d56ed2
 	keyname=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 1024 -n zone -f KSK $zone`
d56ed2
 done
d56ed2
+
d56ed2
+zone=externalkey
d56ed2
+rm -f K${zone}.+*+*.key
d56ed2
+rm -f K${zone}.+*+*.private
d56ed2
+
d56ed2
+for alg in ECDSAP256SHA256 NSEC3RSASHA1 DSA ECCGOST
d56ed2
+do
d56ed2
+
d56ed2
+if test $alg = ECCGOST
d56ed2
+then
d56ed2
+	sh ../../gost/prereq.sh 2> /dev/null || continue
d56ed2
+fi
d56ed2
+
d56ed2
+k1=`$KEYGEN -q -r $RANDFILE -a $alg -b 1024 -n zone -f KSK $zone`
d56ed2
+k2=`$KEYGEN -q -r $RANDFILE -a $alg -b 1024 -n zone $zone`
d56ed2
+k3=`$KEYGEN -q -r $RANDFILE -a $alg -b 1024 -n zone $zone`
d56ed2
+k4=`$KEYGEN -q -r $RANDFILE -a $alg -b 1024 -n zone $zone`
d56ed2
+keyname=`$KEYGEN -q -r $RANDFILE -a $alg -b 1024 -n zone $zone`
d56ed2
+keyname=`$KEYGEN -q -r $RANDFILE -a $alg -b 1024 -n zone -f KSK $zone`
d56ed2
+$DSFROMKEY -T 1200 $keyname >> ../ns1/root.db
d56ed2
+rm -f ${k3}.* ${k4}.*
d56ed2
+
d56ed2
+#
d56ed2
+# Convert k1 and k2 in to External Keys.
d56ed2
+rm -f $k1.private 
d56ed2
+$IMPORTKEY -P now -D now+3600 -f $k1.key $zone
d56ed2
+rm -f $k2.private 
d56ed2
+$IMPORTKEY -f $k2.key $zone
d56ed2
+done
d56ed2
diff --git a/bin/tests/system/inline/setup.sh b/bin/tests/system/inline/setup.sh
d56ed2
index 9fa42ab..adee4ff 100644
d56ed2
--- a/bin/tests/system/inline/setup.sh
d56ed2
+++ b/bin/tests/system/inline/setup.sh
d56ed2
@@ -29,6 +29,7 @@ cp ns3/master.db.in ns3/dynamic.db
d56ed2
 cp ns3/master.db.in ns3/updated.db
d56ed2
 cp ns3/master.db.in ns3/expired.db
d56ed2
 cp ns3/master.db.in ns3/nsec3.db
d56ed2
+cp ns3/master.db.in ns3/externalkey.db
d56ed2
 
d56ed2
 touch ns4/trusted.conf
d56ed2
 cp ns4/noixfr.db.in ns4/noixfr.db
d56ed2
diff --git a/bin/tests/system/inline/tests.sh b/bin/tests/system/inline/tests.sh
d56ed2
index 7e2e3d2..8acdee2 100644
d56ed2
--- a/bin/tests/system/inline/tests.sh
d56ed2
+++ b/bin/tests/system/inline/tests.sh
d56ed2
@@ -809,7 +809,22 @@ $RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 addzone test-$zone \
d56ed2
 $RNDC -c ../common/rndc.conf -s 10.53.0.3 -p 9953 delzone test-$zone
d56ed2
 done
d56ed2
 
d56ed2
-if [ $ret != 0 ]; then echo "I:failed"; fi
d56ed2
+n=`expr $n + 1`
d56ed2
+echo "I:testing adding external keys to a inline zone ($n)"
d56ed2
+ret=0
d56ed2
+$DIG $DIGOPTS @10.53.0.3 -p 5300 dnskey externalkey > dig.out.ns3.test$n
d56ed2
+for alg in 3 7 12 13
d56ed2
+do
d56ed2
+if test $alg = 12 
d56ed2
+then
d56ed2
+	sh ../gost/prereq.sh 2>/dev/null || continue;
d56ed2
+fi
d56ed2
+
d56ed2
+dnskeys=`grep "IN.DNSKEY.25[67] [0-9]* $alg " dig.out.ns3.test$n | wc -l`
d56ed2
+rrsigs=`grep "RRSIG.DNSKEY $alg " dig.out.ns3.test$n | wc -l`
d56ed2
+test ${dnskeys:-0} -eq 3 || { echo "I: failed $alg (dnskeys ${dnskeys:-0})"; ret=1; }
d56ed2
+test ${rrsigs:-0} -eq 2 || { echo "I: failed $alg (rrsigs ${rrsigs:-0})"; ret=1; }
d56ed2
+done
d56ed2
 status=`expr $status + $ret`
d56ed2
 
d56ed2
 exit $status
d56ed2
diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c
d56ed2
index a1c5c69..cf97404 100644
d56ed2
--- a/lib/dns/dnssec.c
d56ed2
+++ b/lib/dns/dnssec.c
d56ed2
@@ -684,6 +684,7 @@ dns_dnssec_findzonekeys2(dns_db_t *db, dns_dbversion_t *ver,
d56ed2
 	isc_stdtime_get(&now;;
d56ed2
 
d56ed2
 	*nkeys = 0;
d56ed2
+	memset(keys, 0, sizeof(*keys) * maxkeys);
d56ed2
 	dns_rdataset_init(&rdataset);
d56ed2
 	RETERR(dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey, 0, 0,
d56ed2
 				   &rdataset, NULL));
d56ed2
@@ -1312,9 +1313,9 @@ dns_dnssec_findmatchingkeys(dns_name_t *origin, const char *directory,
d56ed2
 	isc_dir_t dir;
d56ed2
 	dns_dnsseckey_t *key = NULL;
d56ed2
 	dst_key_t *dstkey = NULL;
d56ed2
-	char namebuf[DNS_NAME_FORMATSIZE], *p;
d56ed2
+	char namebuf[DNS_NAME_FORMATSIZE];
d56ed2
 	isc_buffer_t b;
d56ed2
-	unsigned int len;
d56ed2
+	unsigned int len, i;
d56ed2
 	isc_stdtime_t now;
d56ed2
 
d56ed2
 	REQUIRE(keylist != NULL);
d56ed2
@@ -1334,49 +1335,62 @@ dns_dnssec_findmatchingkeys(dns_name_t *origin, const char *directory,
d56ed2
 	isc_stdtime_get(&now;;
d56ed2
 
d56ed2
 	while (isc_dir_read(&dir) == ISC_R_SUCCESS) {
d56ed2
-		if (dir.entry.name[0] == 'K' &&
d56ed2
-		    dir.entry.length > len + 1 &&
d56ed2
-		    dir.entry.name[len + 1] == '+' &&
d56ed2
-		    strncasecmp(dir.entry.name + 1, namebuf, len) == 0) {
d56ed2
-			p = strrchr(dir.entry.name, '.');
d56ed2
-			if (p != NULL && strcmp(p, ".private") != 0)
d56ed2
-				continue;
d56ed2
+		if (dir.entry.name[0] != 'K' ||
d56ed2
+		    dir.entry.length < len + 1 ||
d56ed2
+		    dir.entry.name[len + 1] != '+' ||
d56ed2
+		    strncasecmp(dir.entry.name + 1, namebuf, len) != 0)
d56ed2
+			continue;
d56ed2
+
d56ed2
+		for (i = len + 1 + 1; i < dir.entry.length ; i++)
d56ed2
+			if (dir.entry.name[i] < '0' || dir.entry.name[i] > '9')
d56ed2
+				break;
d56ed2
+
d56ed2
+		if (i == len + 1 + 1 || i >= dir.entry.length ||
d56ed2
+		    dir.entry.name[i] != '+')
d56ed2
+			continue;
d56ed2
+
d56ed2
+		for (i++ ; i < dir.entry.length ; i++)
d56ed2
+			if (dir.entry.name[i] < '0' || dir.entry.name[i] > '9')
d56ed2
+				break;
d56ed2
 
d56ed2
-			dstkey = NULL;
d56ed2
-			result = dst_key_fromnamedfile(dir.entry.name,
d56ed2
-						       directory,
d56ed2
-						       DST_TYPE_PUBLIC |
d56ed2
-						       DST_TYPE_PRIVATE,
d56ed2
-						       mctx, &dstkey);
d56ed2
-
d56ed2
-			if (result != ISC_R_SUCCESS) {
d56ed2
-				isc_log_write(dns_lctx,
d56ed2
-					      DNS_LOGCATEGORY_GENERAL,
d56ed2
-					      DNS_LOGMODULE_DNSSEC,
d56ed2
-					      ISC_LOG_WARNING,
d56ed2
-					      "dns_dnssec_findmatchingkeys: "
d56ed2
-					      "error reading key file %s: %s",
d56ed2
-					      dir.entry.name,
d56ed2
-					      isc_result_totext(result));
d56ed2
+		if (strcmp(dir.entry.name + i, ".private") != 0)
d56ed2
 				continue;
d56ed2
-			}
d56ed2
 
d56ed2
-			RETERR(dns_dnsseckey_create(mctx, &dstkey, &key));
d56ed2
-			key->source = dns_keysource_repository;
d56ed2
-			get_hints(key, now);
d56ed2
+		dstkey = NULL;
d56ed2
+		result = dst_key_fromnamedfile(dir.entry.name,
d56ed2
+					       directory,
d56ed2
+					       DST_TYPE_PUBLIC |
d56ed2
+					       DST_TYPE_PRIVATE,
d56ed2
+					       mctx, &dstkey);
d56ed2
 
d56ed2
-			if (key->legacy) {
d56ed2
-				dns_dnsseckey_destroy(mctx, &key);
d56ed2
-			} else {
d56ed2
-				ISC_LIST_APPEND(list, key, link);
d56ed2
-				key = NULL;
d56ed2
-			}
d56ed2
+		if (result != ISC_R_SUCCESS) {
d56ed2
+			isc_log_write(dns_lctx,
d56ed2
+				      DNS_LOGCATEGORY_GENERAL,
d56ed2
+				      DNS_LOGMODULE_DNSSEC,
d56ed2
+				      ISC_LOG_WARNING,
d56ed2
+				      "dns_dnssec_findmatchingkeys: "
d56ed2
+				      "error reading key file %s: %s",
d56ed2
+				      dir.entry.name,
d56ed2
+				      isc_result_totext(result));
d56ed2
+			continue;
d56ed2
+		}
d56ed2
+
d56ed2
+		RETERR(dns_dnsseckey_create(mctx, &dstkey, &key));
d56ed2
+		key->source = dns_keysource_repository;
d56ed2
+		get_hints(key, now);
d56ed2
+
d56ed2
+		if (key->legacy) {
d56ed2
+			dns_dnsseckey_destroy(mctx, &key);
d56ed2
+		} else {
d56ed2
+			ISC_LIST_APPEND(list, key, link);
d56ed2
+			key = NULL;
d56ed2
 		}
d56ed2
 	}
d56ed2
 
d56ed2
-	if (!ISC_LIST_EMPTY(list))
d56ed2
+	if (!ISC_LIST_EMPTY(list)) {
d56ed2
+		result = ISC_R_SUCCESS;
d56ed2
 		ISC_LIST_APPENDLIST(*keylist, list, link);
d56ed2
-	else
d56ed2
+	} else
d56ed2
 		result = ISC_R_NOTFOUND;
d56ed2
 
d56ed2
  failure:
d56ed2
@@ -1794,7 +1808,13 @@ dns_dnssec_updatekeys(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *newkeys,
d56ed2
 		for (key2 = ISC_LIST_HEAD(*keys);
d56ed2
 		     key2 != NULL;
d56ed2
 		     key2 = ISC_LIST_NEXT(key2, link)) {
d56ed2
-			if (dst_key_pubcompare(key1->key, key2->key,
d56ed2
+			int f1 = dst_key_flags(key1->key);
d56ed2
+			int f2 = dst_key_flags(key2->key);
d56ed2
+			int nr1 = f1 & ~DNS_KEYFLAG_REVOKE;
d56ed2
+			int nr2 = f2 & ~DNS_KEYFLAG_REVOKE;
d56ed2
+			if (nr1 == nr2 &&
d56ed2
+			    dst_key_alg(key1->key) == dst_key_alg(key2->key) &&
d56ed2
+			    dst_key_pubcompare(key1->key, key2->key,
d56ed2
 					       ISC_TRUE)) {
d56ed2
 				int r1, r2;
d56ed2
 				r1 = dst_key_flags(key1->key) &
d56ed2
diff --git a/lib/dns/dst_api.c b/lib/dns/dst_api.c
d56ed2
index c310be5..4c174f3 100644
d56ed2
--- a/lib/dns/dst_api.c
d56ed2
+++ b/lib/dns/dst_api.c
d56ed2
@@ -448,6 +448,16 @@ dst_key_tofile(const dst_key_t *key, int type, const char *directory) {
d56ed2
 		return (ISC_R_SUCCESS);
d56ed2
 }
d56ed2
 
d56ed2
+void
d56ed2
+dst_key_setexternal(dst_key_t *key, isc_boolean_t value) {
d56ed2
+	key->external = value;
d56ed2
+}
d56ed2
+
d56ed2
+isc_boolean_t
d56ed2
+dst_key_isexternal(dst_key_t *key) {
d56ed2
+	return (key->external);
d56ed2
+}
d56ed2
+
d56ed2
 isc_result_t
d56ed2
 dst_key_fromfile(dns_name_t *name, dns_keytag_t id,
d56ed2
 		 unsigned int alg, int type, const char *directory,
d56ed2
diff --git a/lib/dns/dst_internal.h b/lib/dns/dst_internal.h
d56ed2
index 48ce9b8..49ca424 100644
d56ed2
--- a/lib/dns/dst_internal.h
d56ed2
+++ b/lib/dns/dst_internal.h
d56ed2
@@ -128,6 +128,7 @@ struct dst_key {
d56ed2
 	isc_boolean_t	numset[DST_MAX_NUMERIC + 1]; /*%< data set? */
d56ed2
 	isc_boolean_t 	inactive;      /*%< private key not present as it is
d56ed2
 					    inactive */
d56ed2
+	isc_boolean_t 	external;      /*%< external key */
d56ed2
 
d56ed2
 	int		fmt_major;     /*%< private key format, major version */
d56ed2
 	int		fmt_minor;     /*%< private key format, minor version */
d56ed2
diff --git a/lib/dns/dst_parse.c b/lib/dns/dst_parse.c
d56ed2
index ca43cb3..6348cc1 100644
d56ed2
--- a/lib/dns/dst_parse.c
d56ed2
+++ b/lib/dns/dst_parse.c
d56ed2
@@ -178,14 +178,18 @@ find_numericdata(const char *s) {
d56ed2
 }
d56ed2
 
d56ed2
 static int
d56ed2
-check_rsa(const dst_private_t *priv) {
d56ed2
+check_rsa(const dst_private_t *priv, isc_boolean_t external) {
d56ed2
 	int i, j;
d56ed2
 	isc_boolean_t have[RSA_NTAGS];
d56ed2
 	isc_boolean_t ok;
d56ed2
 	unsigned int mask;
d56ed2
 
d56ed2
+	if (external)
d56ed2
+		return ((priv->nelements == 0) ? 0 : -1);
d56ed2
+
d56ed2
 	for (i = 0; i < RSA_NTAGS; i++)
d56ed2
 		have[i] = ISC_FALSE;
d56ed2
+
d56ed2
 	for (j = 0; j < priv->nelements; j++) {
d56ed2
 		for (i = 0; i < RSA_NTAGS; i++)
d56ed2
 			if (priv->elements[j].tag == TAG(DST_ALG_RSAMD5, i))
d56ed2
@@ -231,10 +235,15 @@ check_dh(const dst_private_t *priv) {
d56ed2
 }
d56ed2
 
d56ed2
 static int
d56ed2
-check_dsa(const dst_private_t *priv) {
d56ed2
+check_dsa(const dst_private_t *priv, isc_boolean_t external) {
d56ed2
 	int i, j;
d56ed2
+
d56ed2
+	if (external)
d56ed2
+		return ((priv->nelements == 0)? 0 : -1);
d56ed2
+
d56ed2
 	if (priv->nelements != DSA_NTAGS)
d56ed2
 		return (-1);
d56ed2
+
d56ed2
 	for (i = 0; i < DSA_NTAGS; i++) {
d56ed2
 		for (j = 0; j < priv->nelements; j++)
d56ed2
 			if (priv->elements[j].tag == TAG(DST_ALG_DSA, i))
d56ed2
@@ -246,7 +255,11 @@ check_dsa(const dst_private_t *priv) {
d56ed2
 }
d56ed2
 
d56ed2
 static int
d56ed2
-check_gost(const dst_private_t *priv) {
d56ed2
+check_gost(const dst_private_t *priv, isc_boolean_t external) {
d56ed2
+
d56ed2
+	if (external)
d56ed2
+		return ((priv->nelements == 0)? 0 : -1);
d56ed2
+
d56ed2
 	if (priv->nelements != GOST_NTAGS)
d56ed2
 		return (-1);
d56ed2
 	if (priv->elements[0].tag != TAG(DST_ALG_ECCGOST, 0))
d56ed2
@@ -255,7 +268,11 @@ check_gost(const dst_private_t *priv) {
d56ed2
 }
d56ed2
 
d56ed2
 static int
d56ed2
-check_ecdsa(const dst_private_t *priv) {
d56ed2
+check_ecdsa(const dst_private_t *priv, isc_boolean_t external) {
d56ed2
+
d56ed2
+	if (external)
d56ed2
+		return ((priv->nelements == 0) ? 0 : -1);
d56ed2
+
d56ed2
 	if (priv->nelements != ECDSA_NTAGS)
d56ed2
 		return (-1);
d56ed2
 	if (priv->elements[0].tag != TAG(DST_ALG_ECDSA256, 0))
d56ed2
@@ -309,7 +326,7 @@ check_hmac_sha(const dst_private_t *priv, unsigned int ntags,
d56ed2
 
d56ed2
 static int
d56ed2
 check_data(const dst_private_t *priv, const unsigned int alg,
d56ed2
-	   isc_boolean_t old)
d56ed2
+	   isc_boolean_t old, isc_boolean_t external)
d56ed2
 {
d56ed2
 	/* XXXVIX this switch statement is too sparse to gen a jump table. */
d56ed2
 	switch (alg) {
d56ed2
@@ -318,17 +335,17 @@ check_data(const dst_private_t *priv, const unsigned int alg,
d56ed2
 	case DST_ALG_NSEC3RSASHA1:
d56ed2
 	case DST_ALG_RSASHA256:
d56ed2
 	case DST_ALG_RSASHA512:
d56ed2
-		return (check_rsa(priv));
d56ed2
+		return (check_rsa(priv, external));
d56ed2
 	case DST_ALG_DH:
d56ed2
 		return (check_dh(priv));
d56ed2
 	case DST_ALG_DSA:
d56ed2
 	case DST_ALG_NSEC3DSA:
d56ed2
-		return (check_dsa(priv));
d56ed2
+		return (check_dsa(priv, external));
d56ed2
 	case DST_ALG_ECCGOST:
d56ed2
-		return (check_gost(priv));
d56ed2
+		return (check_gost(priv, external));
d56ed2
 	case DST_ALG_ECDSA256:
d56ed2
 	case DST_ALG_ECDSA384:
d56ed2
-		return (check_ecdsa(priv));
d56ed2
+		return (check_ecdsa(priv, external));
d56ed2
 	case DST_ALG_HMACMD5:
d56ed2
 		return (check_hmac_md5(priv, old));
d56ed2
 	case DST_ALG_HMACSHA1:
d56ed2
@@ -372,6 +389,7 @@ dst__privstruct_parse(dst_key_t *key, unsigned int alg, isc_lex_t *lex,
d56ed2
 	unsigned int opt = ISC_LEXOPT_EOL;
d56ed2
 	isc_stdtime_t when;
d56ed2
 	isc_result_t ret;
d56ed2
+	isc_boolean_t external = ISC_FALSE;
d56ed2
 
d56ed2
 	REQUIRE(priv != NULL);
d56ed2
 
d56ed2
@@ -467,9 +485,15 @@ dst__privstruct_parse(dst_key_t *key, unsigned int alg, isc_lex_t *lex,
d56ed2
 
d56ed2
 		if (token.type != isc_tokentype_string) {
d56ed2
 			ret = DST_R_INVALIDPRIVATEKEY;
d56ed2
+			NEXTTOKEN(lex, opt, &token);
d56ed2
 			goto fail;
d56ed2
 		}
d56ed2
 
d56ed2
+		if (strcmp(DST_AS_STR(token), "External:") == 0) {
d56ed2
+			external = ISC_TRUE;
d56ed2
+			goto next;
d56ed2
+		}
d56ed2
+
d56ed2
 		/* Numeric metadata */
d56ed2
 		tag = find_numericdata(DST_AS_STR(token));
d56ed2
 		if (tag >= 0) {
d56ed2
@@ -534,8 +558,14 @@ dst__privstruct_parse(dst_key_t *key, unsigned int alg, isc_lex_t *lex,
d56ed2
 		READLINE(lex, opt, &token);
d56ed2
 		data = NULL;
d56ed2
 	}
d56ed2
+
d56ed2
  done:
d56ed2
-	check = check_data(priv, alg, ISC_TRUE);
d56ed2
+	if (external && priv->nelements != 0) {
d56ed2
+		ret = DST_R_INVALIDPRIVATEKEY;
d56ed2
+		goto fail;
d56ed2
+	}
d56ed2
+
d56ed2
+	check = check_data(priv, alg, ISC_TRUE, external);
d56ed2
 	if (check < 0) {
d56ed2
 		ret = DST_R_INVALIDPRIVATEKEY;
d56ed2
 		goto fail;
d56ed2
@@ -544,6 +574,8 @@ dst__privstruct_parse(dst_key_t *key, unsigned int alg, isc_lex_t *lex,
d56ed2
 		goto fail;
d56ed2
 	}
d56ed2
 
d56ed2
+	key->external = external;
d56ed2
+
d56ed2
 	return (ISC_R_SUCCESS);
d56ed2
 
d56ed2
 fail:
d56ed2
@@ -573,7 +605,7 @@ dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv,
d56ed2
 
d56ed2
 	REQUIRE(priv != NULL);
d56ed2
 
d56ed2
-	ret = check_data(priv, dst_key_alg(key), ISC_FALSE);
d56ed2
+	ret = check_data(priv, dst_key_alg(key), ISC_FALSE, key->external);
d56ed2
 	if (ret < 0)
d56ed2
 		return (DST_R_INVALIDPRIVATEKEY);
d56ed2
 	else if (ret != ISC_R_SUCCESS)
d56ed2
@@ -691,6 +723,9 @@ dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv,
d56ed2
 	       fprintf(fp, "%s %.*s\n", s, (int)r.length, r.base);
d56ed2
 	}
d56ed2
 
d56ed2
+	if (key->external)
d56ed2
+	       fprintf(fp, "External:\n");
d56ed2
+
d56ed2
 	/* Add the metadata tags */
d56ed2
 	if (major > 1 || (major == 1 && minor >= 3)) {
d56ed2
 		for (i = 0; i < NUMERIC_NTAGS; i++) {
d56ed2
@@ -706,14 +741,14 @@ dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv,
d56ed2
 
d56ed2
 			isc_buffer_init(&b, buffer, sizeof(buffer));
d56ed2
 			result = dns_time32_totext(when, &b);
d56ed2
-		       if (result != ISC_R_SUCCESS) {
d56ed2
+			if (result != ISC_R_SUCCESS) {
d56ed2
 			       fclose(fp);
d56ed2
 			       return (DST_R_INVALIDPRIVATEKEY);
d56ed2
-		       }
d56ed2
+			}
d56ed2
 
d56ed2
 			isc_buffer_usedregion(&b, &r);
d56ed2
 
d56ed2
-		       fprintf(fp, "%s %.*s\n", timetags[i], (int)r.length,
d56ed2
+			fprintf(fp, "%s %.*s\n", timetags[i], (int)r.length,
d56ed2
 				r.base);
d56ed2
 		}
d56ed2
 	}
d56ed2
diff --git a/lib/dns/dst_result.c b/lib/dns/dst_result.c
d56ed2
index 297e809..30aa1fa 100644
d56ed2
--- a/lib/dns/dst_result.c
d56ed2
+++ b/lib/dns/dst_result.c
d56ed2
@@ -35,7 +35,7 @@ static const char *text[DST_R_NRESULTS] = {
d56ed2
 	"illegal operation for a null key",	/*%< 3 */
d56ed2
 	"public key is invalid",		/*%< 4 */
d56ed2
 	"private key is invalid",		/*%< 5 */
d56ed2
-	"UNUSED6",				/*%< 6 */
d56ed2
+	"external key",				/*%< 6 */
d56ed2
 	"error occurred writing key to disk",	/*%< 7 */
d56ed2
 	"invalid algorithm specific parameter",	/*%< 8 */
d56ed2
 	"UNUSED9",				/*%< 9 */
d56ed2
diff --git a/lib/dns/include/dns/master.h b/lib/dns/include/dns/master.h
d56ed2
index 6abcb7e..931e930 100644
d56ed2
--- a/lib/dns/include/dns/master.h
d56ed2
+++ b/lib/dns/include/dns/master.h
d56ed2
@@ -57,6 +57,7 @@
d56ed2
 
d56ed2
 #define DNS_MASTER_RESIGN	0x00002000
d56ed2
 #define DNS_MASTER_KEY	 	0x00004000	/*%< Loading a key zone master file. */
d56ed2
+#define DNS_MASTER_NOTTL	0x00008000	/*%< Don't require ttl. */
d56ed2
 
d56ed2
 ISC_LANG_BEGINDECLS
d56ed2
 
d56ed2
diff --git a/lib/dns/include/dst/dst.h b/lib/dns/include/dst/dst.h
d56ed2
index 6ce3a0c..8676d1a 100644
d56ed2
--- a/lib/dns/include/dst/dst.h
d56ed2
+++ b/lib/dns/include/dst/dst.h
d56ed2
@@ -953,6 +953,12 @@ dst_key_setinactive(dst_key_t *key, isc_boolean_t inactive);
d56ed2
  *	'key' to be valid.
d56ed2
  */
d56ed2
 
d56ed2
+void
d56ed2
+dst_key_setexternal(dst_key_t *key, isc_boolean_t value);
d56ed2
+
d56ed2
+isc_boolean_t
d56ed2
+dst_key_isexternal(dst_key_t *key);
d56ed2
+
d56ed2
 ISC_LANG_ENDDECLS
d56ed2
 
d56ed2
 #endif /* DST_DST_H */
d56ed2
diff --git a/lib/dns/master.c b/lib/dns/master.c
d56ed2
index aa8f1ac..1a2c84a 100644
d56ed2
--- a/lib/dns/master.c
d56ed2
+++ b/lib/dns/master.c
d56ed2
@@ -592,9 +592,9 @@ loadctx_create(dns_masterformat_t format, isc_mem_t *mctx,
d56ed2
 		isc_lex_setcomments(lctx->lex, ISC_LEXCOMMENT_DNSMASTERFILE);
d56ed2
 	}
d56ed2
 
d56ed2
-	lctx->ttl_known = ISC_FALSE;
d56ed2
+	lctx->ttl_known = ISC_TF((options & DNS_MASTER_NOTTL) != 0);
d56ed2
 	lctx->ttl = 0;
d56ed2
-	lctx->default_ttl_known = ISC_FALSE;
d56ed2
+	lctx->default_ttl_known = lctx->ttl_known;
d56ed2
 	lctx->default_ttl = 0;
d56ed2
 	lctx->warn_1035 = ISC_TRUE;	/* XXX Argument? */
d56ed2
 	lctx->warn_tcr = ISC_TRUE;	/* XXX Argument? */
d56ed2
diff --git a/lib/dns/openssldsa_link.c b/lib/dns/openssldsa_link.c
d56ed2
index 8bea1c0..a24baae 100644
d56ed2
--- a/lib/dns/openssldsa_link.c
d56ed2
+++ b/lib/dns/openssldsa_link.c
d56ed2
@@ -522,6 +522,11 @@ openssldsa_tofile(const dst_key_t *key, const char *directory) {
d56ed2
 
d56ed2
 	if (key->keydata.dsa == NULL)
d56ed2
 		return (DST_R_NULLKEY);
d56ed2
+	
d56ed2
+	if (key->external) {
d56ed2
+		priv.nelements = 0;
d56ed2
+		return (dst__privstruct_writefile(key, &priv, directory));
d56ed2
+	}
d56ed2
 
d56ed2
 	dsa = key->keydata.dsa;
d56ed2
 
d56ed2
@@ -569,6 +574,7 @@ openssldsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
d56ed2
 #define DST_RET(a) {ret = a; goto err;}
d56ed2
 
d56ed2
 	UNUSED(pub);
d56ed2
+
d56ed2
 	/* read private key file */
d56ed2
 	ret = dst__privstruct_parse(key, DST_ALG_DSA, lexer, mctx, &priv;;
d56ed2
 	if (ret != ISC_R_SUCCESS)
d56ed2
@@ -607,6 +613,19 @@ openssldsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
d56ed2
 	}
d56ed2
 	dst__privstruct_free(&priv, mctx);
d56ed2
 
d56ed2
+	if (key->external) {
d56ed2
+		if (pub == NULL)
d56ed2
+			DST_RET(DST_R_INVALIDPRIVATEKEY);
d56ed2
+		dsa->q = pub->keydata.dsa->q;
d56ed2
+		pub->keydata.dsa->q = NULL;
d56ed2
+		dsa->p = pub->keydata.dsa->p;
d56ed2
+		pub->keydata.dsa->p = NULL;
d56ed2
+		dsa->g = pub->keydata.dsa->g;
d56ed2
+		pub->keydata.dsa->g =  NULL;
d56ed2
+		dsa->pub_key = pub->keydata.dsa->pub_key;
d56ed2
+		pub->keydata.dsa->pub_key = NULL;
d56ed2
+	}
d56ed2
+
d56ed2
 	key->key_size = BN_num_bits(dsa->p);
d56ed2
 
d56ed2
 	return (ISC_R_SUCCESS);
d56ed2
diff --git a/lib/dns/opensslecdsa_link.c b/lib/dns/opensslecdsa_link.c
d56ed2
index c3f5061..7eff9a0 100644
d56ed2
--- a/lib/dns/opensslecdsa_link.c
d56ed2
+++ b/lib/dns/opensslecdsa_link.c
d56ed2
@@ -453,6 +453,11 @@ opensslecdsa_tofile(const dst_key_t *key, const char *directory) {
d56ed2
 	if (key->keydata.pkey == NULL)
d56ed2
 		return (DST_R_NULLKEY);
d56ed2
 
d56ed2
+	if (key->external) {
d56ed2
+		priv.nelements = 0;
d56ed2
+		return (dst__privstruct_writefile(key, &priv, directory));
d56ed2
+	}
d56ed2
+
d56ed2
 	pkey = key->keydata.pkey;
d56ed2
 	eckey = EVP_PKEY_get1_EC_KEY(pkey);
d56ed2
 	if (eckey == NULL)
d56ed2
@@ -514,8 +519,9 @@ static isc_result_t
d56ed2
 opensslecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
d56ed2
 	dst_private_t priv;
d56ed2
 	isc_result_t ret;
d56ed2
-	EVP_PKEY *pkey;
d56ed2
-	EC_KEY *eckey = NULL;
d56ed2
+	EVP_PKEY *pkey, *pubpkey;
d56ed2
+	EC_KEY *eckey = NULL, *pubeckey = NULL;
d56ed2
+	const EC_POINT *pubkey;
d56ed2
 	BIGNUM *privkey;
d56ed2
 	int group_nid;
d56ed2
 	isc_mem_t *mctx = key->mctx;
d56ed2
@@ -537,17 +543,36 @@ opensslecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
d56ed2
 	if (ret != ISC_R_SUCCESS)
d56ed2
 		goto err;
d56ed2
 
d56ed2
-	privkey = BN_bin2bn(priv.elements[0].data,
d56ed2
-			    priv.elements[0].length, NULL);
d56ed2
-	if (privkey == NULL)
d56ed2
-		DST_RET(ISC_R_NOMEMORY);
d56ed2
-	if (!EC_KEY_set_private_key(eckey, privkey))
d56ed2
-		DST_RET(ISC_R_NOMEMORY);
d56ed2
-	if (ecdsa_check(eckey, pub) != ISC_R_SUCCESS)
d56ed2
-		DST_RET(DST_R_INVALIDPRIVATEKEY);
d56ed2
-	dst__privstruct_free(&priv, mctx);
d56ed2
-	memset(&priv, 0, sizeof(priv));
d56ed2
-
d56ed2
+	if (key->external) {
d56ed2
+		/*
d56ed2
+		 * Copy the public key to this new key.
d56ed2
+		 */
d56ed2
+		if (pub == NULL)
d56ed2
+			DST_RET(DST_R_INVALIDPRIVATEKEY);
d56ed2
+		pubpkey = pub->keydata.pkey;
d56ed2
+		pubeckey = EVP_PKEY_get1_EC_KEY(pubpkey);
d56ed2
+		if (pubeckey == NULL)
d56ed2
+			DST_RET(DST_R_INVALIDPRIVATEKEY);
d56ed2
+		pubkey = EC_KEY_get0_public_key(pubeckey);
d56ed2
+		if (pubkey == NULL)
d56ed2
+			DST_RET(DST_R_INVALIDPRIVATEKEY);
d56ed2
+		if (EC_KEY_set_public_key(eckey, pubkey) != 1)
d56ed2
+			DST_RET(DST_R_INVALIDPRIVATEKEY);
d56ed2
+		if (EC_KEY_check_key(eckey) != 1)
d56ed2
+			DST_RET(DST_R_INVALIDPRIVATEKEY);
d56ed2
+	} else {
d56ed2
+		privkey = BN_bin2bn(priv.elements[0].data,
d56ed2
+				    priv.elements[0].length, NULL);
d56ed2
+		if (privkey == NULL)
d56ed2
+			DST_RET(ISC_R_NOMEMORY);
d56ed2
+		if (!EC_KEY_set_private_key(eckey, privkey))
d56ed2
+			DST_RET(ISC_R_NOMEMORY);
d56ed2
+		if (ecdsa_check(eckey, pub) != ISC_R_SUCCESS)
d56ed2
+			DST_RET(DST_R_INVALIDPRIVATEKEY);
d56ed2
+		dst__privstruct_free(&priv, mctx);
d56ed2
+		memset(&priv, 0, sizeof(priv));
d56ed2
+	}
d56ed2
+ 
d56ed2
 	pkey = EVP_PKEY_new();
d56ed2
 	if (pkey == NULL)
d56ed2
 		DST_RET (ISC_R_NOMEMORY);
d56ed2
@@ -561,6 +586,8 @@ opensslecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
d56ed2
  err:
d56ed2
 	if (eckey != NULL)
d56ed2
 		EC_KEY_free(eckey);
d56ed2
+	if (pubeckey != NULL)
d56ed2
+		EC_KEY_free(pubeckey);
d56ed2
 	dst__privstruct_free(&priv, mctx);
d56ed2
 	memset(&priv, 0, sizeof(priv));
d56ed2
 	return (ret);
d56ed2
diff --git a/lib/dns/opensslgost_link.c b/lib/dns/opensslgost_link.c
d56ed2
index 1ce4405..325a7c0 100644
d56ed2
--- a/lib/dns/opensslgost_link.c
d56ed2
+++ b/lib/dns/opensslgost_link.c
d56ed2
@@ -296,6 +296,11 @@ opensslgost_tofile(const dst_key_t *key, const char *directory) {
d56ed2
 	if (key->keydata.pkey == NULL)
d56ed2
 		return (DST_R_NULLKEY);
d56ed2
 
d56ed2
+	if (key->external) {
d56ed2
+		priv.nelements = 0;
d56ed2
+		return (dst__privstruct_writefile(key, &priv, directory));
d56ed2
+	}
d56ed2
+
d56ed2
 	pkey = key->keydata.pkey;
d56ed2
 
d56ed2
 	len = i2d_PrivateKey(pkey, NULL);
d56ed2
@@ -337,13 +342,21 @@ opensslgost_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
d56ed2
 	if (ret != ISC_R_SUCCESS)
d56ed2
 		return (ret);
d56ed2
 
d56ed2
-	INSIST(priv.elements[0].tag == TAG_GOST_PRIVASN1);
d56ed2
-	p = priv.elements[0].data;
d56ed2
-	if (d2i_PrivateKey(NID_id_GostR3410_2001, &pkey, &p,
d56ed2
-			   (long) priv.elements[0].length) == NULL)
d56ed2
-		DST_RET(dst__openssl_toresult2("d2i_PrivateKey",
d56ed2
-					       DST_R_INVALIDPRIVATEKEY));
d56ed2
-	key->keydata.pkey = pkey;
d56ed2
+	if (key->external) {
d56ed2
+		INSIST(priv.nelements == 0);
d56ed2
+		if (pub == NULL)
d56ed2
+			DST_RET(DST_R_INVALIDPRIVATEKEY);
d56ed2
+		key->keydata.pkey = pub->keydata.pkey;
d56ed2
+		pub->keydata.pkey = NULL;
d56ed2
+	} else {
d56ed2
+		INSIST(priv.elements[0].tag == TAG_GOST_PRIVASN1);
d56ed2
+		p = priv.elements[0].data;
d56ed2
+		if (d2i_PrivateKey(NID_id_GostR3410_2001, &pkey, &p,
d56ed2
+				   (long) priv.elements[0].length) == NULL)
d56ed2
+			DST_RET(dst__openssl_toresult2("d2i_PrivateKey",
d56ed2
+						     DST_R_INVALIDPRIVATEKEY));
d56ed2
+		key->keydata.pkey = pkey;
d56ed2
+	}
d56ed2
 	key->key_size = EVP_PKEY_bits(pkey);
d56ed2
 	dst__privstruct_free(&priv, mctx);
d56ed2
 	memset(&priv, 0, sizeof(priv));
d56ed2
diff --git a/lib/dns/opensslrsa_link.c b/lib/dns/opensslrsa_link.c
d56ed2
index fa7412c..894c7ae 100644
d56ed2
--- a/lib/dns/opensslrsa_link.c
d56ed2
+++ b/lib/dns/opensslrsa_link.c
d56ed2
@@ -1048,8 +1048,14 @@ opensslrsa_tofile(const dst_key_t *key, const char *directory) {
d56ed2
 		return (DST_R_NULLKEY);
d56ed2
 	rsa = key->keydata.rsa;
d56ed2
 #endif
d56ed2
-
d56ed2
 	memset(bufs, 0, sizeof(bufs));
d56ed2
+
d56ed2
+	if (key->external) {
d56ed2
+		priv.nelements = 0;
d56ed2
+		result = dst__privstruct_writefile(key, &priv, directory);
d56ed2
+		goto fail;
d56ed2
+	}
d56ed2
+
d56ed2
 	for (i = 0; i < 8; i++) {
d56ed2
 		bufs[i] = isc_mem_get(key->mctx, BN_num_bytes(rsa->n));
d56ed2
 		if (bufs[i] == NULL) {
d56ed2
@@ -1205,6 +1211,9 @@ opensslrsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
d56ed2
 	if (ret != ISC_R_SUCCESS)
d56ed2
 		goto err;
d56ed2
 
d56ed2
+	if (key->external && priv.nelements != 0)
d56ed2
+		DST_RET(DST_R_INVALIDPRIVATEKEY);
d56ed2
+
d56ed2
 	for (i = 0; i < priv.nelements; i++) {
d56ed2
 		switch (priv.elements[i].tag) {
d56ed2
 		case TAG_RSA_ENGINE:
d56ed2
@@ -1217,6 +1226,7 @@ opensslrsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
d56ed2
 			break;
d56ed2
 		}
d56ed2
 	}
d56ed2
+
d56ed2
 	/*
d56ed2
 	 * Is this key is stored in a HSM?
d56ed2
 	 * See if we can fetch it.
d56ed2
@@ -1328,8 +1338,10 @@ opensslrsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
d56ed2
 
d56ed2
 	if (rsa_check(rsa, pubrsa) != ISC_R_SUCCESS)
d56ed2
 		DST_RET(DST_R_INVALIDPRIVATEKEY);
d56ed2
-	if (BN_num_bits(rsa->e) > RSA_MAX_PUBEXP_BITS)
d56ed2
-		DST_RET(ISC_R_RANGE);
d56ed2
+	if (!key->external) {
d56ed2
+		if (BN_num_bits(rsa->e) > RSA_MAX_PUBEXP_BITS)
d56ed2
+			DST_RET(ISC_R_RANGE);
d56ed2
+	}
d56ed2
 	key->key_size = BN_num_bits(rsa->n);
d56ed2
 	if (pubrsa != NULL)
d56ed2
 		RSA_free(pubrsa);
d56ed2
diff --git a/lib/dns/zone.c b/lib/dns/zone.c
d56ed2
index daf495b..b82ad58 100644
d56ed2
--- a/lib/dns/zone.c
d56ed2
+++ b/lib/dns/zone.c
d56ed2
@@ -5545,6 +5545,7 @@ find_zone_keys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver,
d56ed2
 	const char *directory = dns_zone_getkeydirectory(zone);
d56ed2
 
d56ed2
 	CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node));
d56ed2
+	memset(keys, 0, sizeof(*keys) * maxkeys);
d56ed2
 	result = dns_dnssec_findzonekeys2(db, ver, node, dns_db_origin(db),
d56ed2
 					  directory, mctx, maxkeys, keys,
d56ed2
 					  nkeys);
d56ed2
@@ -13132,6 +13133,7 @@ sync_secure_db(dns_zone_t *seczone, dns_db_t *secdb,
d56ed2
 
d56ed2
 static void
d56ed2
 receive_secure_serial(isc_task_t *task, isc_event_t *event) {
d56ed2
+	static char me[] = "receive_secure_serial";
d56ed2
 	isc_result_t result;
d56ed2
 	dns_journal_t *rjournal = NULL;
d56ed2
 	isc_uint32_t start, end;
d56ed2
@@ -13147,6 +13149,8 @@ receive_secure_serial(isc_task_t *task, isc_event_t *event) {
d56ed2
 	end = ((struct secure_event *)event)->serial;
d56ed2
 	isc_event_free(&event);
d56ed2
 
d56ed2
+	ENTER;
d56ed2
+
d56ed2
 	LOCK_ZONE(zone);
d56ed2
 
d56ed2
 	dns_diff_init(zone->mctx, &diff);
d56ed2
-- 
d56ed2
1.9.3
d56ed2