summaryrefslogtreecommitdiffstats
path: root/contrib/sdb
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 18:37:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 18:37:14 +0000
commitea648e70a989cca190cd7403fe892fd2dcc290b4 (patch)
treee2b6b1c647da68b0d4d66082835e256eb30970e8 /contrib/sdb
parentInitial commit. (diff)
downloadbind9-ea648e70a989cca190cd7403fe892fd2dcc290b4.tar.xz
bind9-ea648e70a989cca190cd7403fe892fd2dcc290b4.zip
Adding upstream version 1:9.11.5.P4+dfsg.upstream/1%9.11.5.P4+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'contrib/sdb')
-rw-r--r--contrib/sdb/bdb/README37
-rw-r--r--contrib/sdb/bdb/bdb.c245
-rw-r--r--contrib/sdb/bdb/bdb.h30
-rw-r--r--contrib/sdb/bdb/zone2bdb.c186
-rw-r--r--contrib/sdb/dir/dirdb.c200
-rw-r--r--contrib/sdb/dir/dirdb.h18
-rw-r--r--contrib/sdb/ldap/INSTALL.ldap83
-rw-r--r--contrib/sdb/ldap/README.ldap48
-rw-r--r--contrib/sdb/ldap/README.zone2ldap17
-rw-r--r--contrib/sdb/ldap/ldapdb.c690
-rw-r--r--contrib/sdb/ldap/ldapdb.h6
-rw-r--r--contrib/sdb/ldap/zone2ldap.164
-rw-r--r--contrib/sdb/ldap/zone2ldap.c772
-rw-r--r--contrib/sdb/pgsql/pgsqldb.c353
-rw-r--r--contrib/sdb/pgsql/pgsqldb.h18
-rw-r--r--contrib/sdb/pgsql/zonetodb.c283
-rw-r--r--contrib/sdb/sqlite/README.sdb_sqlite67
-rw-r--r--contrib/sdb/sqlite/sqlitedb.c325
-rw-r--r--contrib/sdb/sqlite/sqlitedb.h15
-rw-r--r--contrib/sdb/sqlite/zone2sqlite.c296
-rw-r--r--contrib/sdb/tcl/lookup.tcl43
-rw-r--r--contrib/sdb/tcl/tcldb.c238
-rw-r--r--contrib/sdb/tcl/tcldb.h18
-rw-r--r--contrib/sdb/time/timedb.c148
-rw-r--r--contrib/sdb/time/timedb.h18
25 files changed, 4218 insertions, 0 deletions
diff --git a/contrib/sdb/bdb/README b/contrib/sdb/bdb/README
new file mode 100644
index 0000000..d2acf4e
--- /dev/null
+++ b/contrib/sdb/bdb/README
@@ -0,0 +1,37 @@
+(Message trash:37216)
+
+Date: Wed, 15 May 2002 20:44:45 +0100
+To: <bind-workers@isc.org>
+From: Nuno Miguel Rodrigues <nmr@co.sapo.pt>
+Subject: Berkeley DB BIND9 SDB
+
+Replied: Thu, 16 May 2002 11:47:35 +1000
+Replied: Nuno Miguel Rodrigues <nmr@co.sapo.pt>
+Return-Path: <bind-workers-bounce@isc.org>
+X-X-Sender: <nmr@angelina.sl.pt>
+MIME-Version: 1.0
+X-ecartis-version: Ecartis v1.0.0
+Sender: bind-workers-bounce@isc.org
+Errors-To: bind-workers-bounce@isc.org
+X-original-sender: nmr@co.sapo.pt
+Precedence: bulk
+X-list: bind-workers
+Status:
+
+
+
+Hello all,
+
+I'm making available a BIND9 SDB using Berkeley DB 4.0.
+
+You can get it at http://www.dhis.org/~nmr/bind9_berkeleydb_sdb-1.0.tar
+
+Thanks,
+
+
+--
+Nuno M. Rodrigues <nmr@co.sapo.pt>
+
+
+
+
diff --git a/contrib/sdb/bdb/bdb.c b/contrib/sdb/bdb/bdb.c
new file mode 100644
index 0000000..6dbc187
--- /dev/null
+++ b/contrib/sdb/bdb/bdb.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2002 Nuno M. Rodrigues.
+ *
+ * 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 NUNO M. RODRIGUES
+ * 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.
+ */
+
+
+/*
+ * BIND 9.1.x simple database driver
+ * implementation, using Berkeley DB.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <isc/file.h>
+#include <isc/log.h>
+#include <isc/lib.h>
+#include <isc/mem.h>
+#include <isc/msgs.h>
+#include <isc/msgcat.h>
+#include <isc/region.h>
+#include <isc/result.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+#include <dns/sdb.h>
+#include <dns/log.h>
+#include <dns/lib.h>
+#include <dns/ttl.h>
+
+#include <named/bdb.h>
+#include <named/globals.h>
+#include <named/config.h>
+
+#include <db.h>
+
+#define DRIVERNAME "bdb"
+
+static dns_sdbimplementation_t *bdb_imp;
+
+static isc_result_t
+bdb_create(const char *zone, int argc, char **argv,
+ void *unused, void **dbdata)
+{
+ int ret;
+
+ UNUSED(zone);
+ UNUSED(unused);
+
+ if (argc < 1)
+ return ISC_R_FAILURE; /* database path must be given */
+
+ if (db_create((DB **)dbdata, NULL, 0) != 0) {
+ /*
+ * XXX Should use dns_msgcat et al
+ * but seems to be unavailable.
+ */
+ isc_log_iwrite(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_SDB, ISC_LOG_CRITICAL, isc_msgcat,
+ ISC_MSGSET_GENERAL, ISC_MSG_FATALERROR,
+ "db_create");
+ return ISC_R_FAILURE;
+ }
+
+ if (isc_file_exists(*argv) != true) {
+ isc_log_iwrite(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_SDB, ISC_LOG_CRITICAL, isc_msgcat,
+ ISC_MSGSET_GENERAL, ISC_MSG_FATALERROR,
+ "isc_file_exists: %s", *argv);
+ return ISC_R_FAILURE;
+ }
+
+ if ((ret = (*(DB **)dbdata)->open(*(DB **)dbdata, *argv, NULL, DB_HASH,
+ DB_RDONLY, 0)) != 0) {
+ isc_log_iwrite(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_SDB, ISC_LOG_CRITICAL,
+ isc_msgcat, ISC_MSGSET_GENERAL,
+ ISC_MSG_FATALERROR, "DB->open: %s",
+ db_strerror(ret));
+ return ISC_R_FAILURE;
+ }
+ return ISC_R_SUCCESS;
+}
+
+static isc_result_t
+#ifdef DNS_CLIENTINFO_VERSION
+bdb_lookup(const char *zone, const char *name, void *dbdata,
+ dns_sdblookup_t *l, dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo)
+#else
+bdb_lookup(const char *zone, const char *name, void *dbdata,
+ dns_sdblookup_t *l)
+#endif /* DNS_CLIENTINFO_VERSION */
+{
+ int ret;
+ char *type, *rdata;
+ dns_ttl_t ttl;
+ isc_consttextregion_t ttltext;
+ DBC *c;
+ DBT key, data;
+
+ UNUSED(zone);
+#ifdef DNS_CLIENTINFO_VERSION
+ UNUSED(methods);
+ UNUSED(clientinfo);
+#endif /* DNS_CLIENTINFO_VERSION */
+
+ if ((ret = ((DB *)dbdata)->cursor((DB *)dbdata, NULL, &c, 0)) != 0) {
+ isc_log_iwrite(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_SDB, ISC_LOG_ERROR,
+ isc_msgcat, ISC_MSGSET_GENERAL,
+ ISC_MSG_FAILED, "DB->cursor: %s",
+ db_strerror(ret));
+ return ISC_R_FAILURE;
+ }
+
+ memset(&key, 0, sizeof(DBT));
+ memset(&data, 0, sizeof(DBT));
+
+ (const char *)key.data = name;
+ key.size = strlen(name);
+
+ ret = c->c_get(c, &key, &data, DB_SET);
+ while (ret == 0) {
+ ((char *)key.data)[key.size] = 0;
+ ((char *)data.data)[data.size] = 0;
+ ttltext.base = strtok((char *)data.data, " ");
+ ttltext.length = strlen(ttltext.base);
+ dns_ttl_fromtext((isc_textregion_t *)&ttltext, &ttl);
+ type = strtok(NULL, " ");
+ rdata = type + strlen(type) + 1;
+
+ if (dns_sdb_putrr(l, type, ttl, rdata) != ISC_R_SUCCESS) {
+ isc_log_iwrite(dns_lctx,
+ DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_SDB, ISC_LOG_ERROR,
+ isc_msgcat, ISC_MSGSET_GENERAL,
+ ISC_MSG_FAILED, "dns_sdb_putrr");
+ return ISC_R_FAILURE;
+ }
+ ret = c->c_get(c, &key, &data, DB_NEXT_DUP);
+ }
+
+ c->c_close(c);
+ return ISC_R_SUCCESS;
+}
+
+static isc_result_t
+bdb_allnodes(const char *zone, void *dbdata, dns_sdballnodes_t *n)
+{
+ int ret;
+ char *type, *rdata;
+ dns_ttl_t ttl;
+ isc_consttextregion_t ttltext;
+ DBC *c;
+ DBT key, data;
+
+ UNUSED(zone);
+
+ if ((ret = ((DB *)dbdata)->cursor((DB *)dbdata, NULL, &c, 0)) != 0) {
+ isc_log_iwrite(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_SDB, ISC_LOG_ERROR,
+ isc_msgcat, ISC_MSGSET_GENERAL,
+ ISC_MSG_FAILED, "DB->cursor: %s",
+ db_strerror(ret));
+ return ISC_R_FAILURE;
+ }
+
+ memset(&key, 0, sizeof(DBT));
+ memset(&data, 0, sizeof(DBT));
+
+ while (c->c_get(c, &key, &data, DB_NEXT) == 0) {
+ ((char *)key.data)[key.size] = 0;
+ ((char *)data.data)[data.size] = 0;
+ ttltext.base = strtok((char *)data.data, " ");
+ ttltext.length = strlen(ttltext.base);
+ dns_ttl_fromtext((isc_textregion_t *)&ttltext, &ttl);
+ type = strtok(NULL, " ");
+ rdata = type + strlen(type) + 1;
+
+ if (dns_sdb_putnamedrr(n, key.data, type, ttl, rdata) !=
+ ISC_R_SUCCESS) {
+ isc_log_iwrite(dns_lctx,
+ DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_SDB, ISC_LOG_ERROR,
+ isc_msgcat, ISC_MSGSET_GENERAL,
+ ISC_MSG_FAILED, "dns_sdb_putnamedrr");
+ return ISC_R_FAILURE;
+ }
+
+ }
+
+ c->c_close(c);
+ return ISC_R_SUCCESS;
+}
+
+static isc_result_t
+bdb_destroy(const char *zone, void *unused, void **dbdata)
+{
+
+ UNUSED(zone);
+ UNUSED(unused);
+
+ (*(DB **)dbdata)->close(*(DB **)dbdata, 0);
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t
+bdb_init(void)
+{
+ static dns_sdbmethods_t bdb_methods = {
+ bdb_lookup,
+ NULL,
+ bdb_allnodes,
+ bdb_create,
+ bdb_destroy,
+ NULL /* lookup2 */
+ };
+
+ return dns_sdb_register(DRIVERNAME, &bdb_methods, NULL, 0, ns_g_mctx,
+ &bdb_imp);
+}
+
+void
+bdb_clear(void)
+{
+
+ if (bdb_imp != NULL)
+ dns_sdb_unregister(&bdb_imp);
+}
diff --git a/contrib/sdb/bdb/bdb.h b/contrib/sdb/bdb/bdb.h
new file mode 100644
index 0000000..9857380
--- /dev/null
+++ b/contrib/sdb/bdb/bdb.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2002 Nuno M. Rodrigues.
+ *
+ * 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 NUNO M. RODRIGUES
+ * 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.
+ */
+
+
+#ifndef BDB_H
+#define BDB_H 1
+
+#include <isc/types.h>
+
+/*
+ * Prototypes.
+ */
+isc_result_t bdb_init(void);
+void bdb_clear(void);
+
+#endif /* BDB_H */
diff --git a/contrib/sdb/bdb/zone2bdb.c b/contrib/sdb/bdb/zone2bdb.c
new file mode 100644
index 0000000..6599db1
--- /dev/null
+++ b/contrib/sdb/bdb/zone2bdb.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2002 Nuno M. Rodrigues.
+ *
+ * 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 NUNO M. RODRIGUES
+ * 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.
+ */
+
+
+#include <stdio.h>
+
+#include <isc/mem.h>
+#include <isc/result.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/rdatatype.h>
+#include <dns/ttl.h>
+#include <dns/types.h>
+
+#include <db.h>
+
+#define MAX_RDATATEXT 63 + 4 + 65535 + 2 /* ttl + type + rdata + sep */
+
+/*
+ * Returns a valid 'DB' handle.
+ *
+ * Requires:
+ * 'file' is a valid non-existant path.
+ */
+DB *
+bdb_init(const char *file)
+{
+ DB *db;
+
+ REQUIRE(db_create(&db, NULL, 0) == 0);
+ REQUIRE(db->set_flags(db, DB_DUP) == 0);
+ REQUIRE(db->open(db, file, NULL, DB_HASH, DB_CREATE | DB_EXCL, 0) == 0);
+
+ return db;
+}
+
+/*
+ * Puts resource record data on 'db'.
+ */
+isc_result_t
+bdb_putrdata(DB *db, dns_name_t *name, dns_ttl_t ttl, dns_rdata_t *rdata)
+{
+ static DBT key, data;
+ isc_buffer_t keybuf, databuf;
+ char nametext[DNS_NAME_MAXTEXT];
+ char rdatatext[MAX_RDATATEXT];
+
+ isc_buffer_init(&keybuf, nametext, DNS_NAME_MAXTEXT);
+
+ dns_name_totext(name, true, &keybuf);
+
+ key.data = isc_buffer_base(&keybuf);
+ key.size = isc_buffer_usedlength(&keybuf);
+
+ isc_buffer_init(&databuf, rdatatext, MAX_RDATATEXT);
+
+ dns_ttl_totext(ttl, false, &databuf);
+ *(char *)isc_buffer_used(&databuf) = ' ';
+ isc_buffer_add(&databuf, 1);
+
+ dns_rdatatype_totext(rdata->type, &databuf); /* XXX private data */
+ *(char *)isc_buffer_used(&databuf) = ' ';
+ isc_buffer_add(&databuf, 1);
+
+ dns_rdata_totext(rdata, NULL, &databuf);
+
+ data.data = isc_buffer_base(&databuf);
+ data.size = isc_buffer_usedlength(&databuf);
+
+ REQUIRE(db->put(db, NULL, &key, &data, 0) == 0);
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t
+bdb_destroy(DB *db)
+{
+
+ return (db->close(db, 0) == 0) ? ISC_R_SUCCESS : ISC_R_FAILURE;
+}
+
+void
+usage(const char *prog)
+{
+
+ fprintf(stderr, "Usage: %s <origin> <zonefile> <db>\n", prog);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ isc_mem_t *mctx = NULL;
+ isc_buffer_t b;
+ int n;
+ dns_fixedname_t origin, name;
+ dns_db_t *db = NULL;
+ dns_dbiterator_t *dbiter = NULL;
+ isc_result_t res;
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
+ dns_rdatasetiter_t *rdatasetiter = NULL;
+ dns_rdata_t rdata;
+ DB *bdb;
+
+ if (argc != 4) usage(*argv);
+
+ REQUIRE(isc_mem_create(0, 0, &mctx) == ISC_R_SUCCESS);
+
+ n = strlen(argv[1]);
+ isc_buffer_init(&b, argv[1], n);
+ isc_buffer_add(&b, n);
+
+ dns_fixedname_init(&origin);
+
+ REQUIRE(dns_name_fromtext(dns_fixedname_name(&origin), &b, dns_rootname,
+ 0, NULL) == ISC_R_SUCCESS);
+ REQUIRE(dns_db_create(mctx, "rbt", dns_fixedname_name(&origin),
+ dns_dbtype_zone, dns_rdataclass_in, 0, NULL,
+ &db) == ISC_R_SUCCESS);
+
+ REQUIRE(dns_db_load(db, argv[2]) == ISC_R_SUCCESS);
+
+ REQUIRE(dns_db_createiterator(db, 0, &dbiter) == ISC_R_SUCCESS);
+
+ dns_rdataset_init(&rdataset);
+ dns_rdata_init(&rdata);
+ dns_fixedname_init(&name);
+ bdb = bdb_init(argv[3]);
+
+ for (res = dns_dbiterator_first(dbiter); res == ISC_R_SUCCESS;
+ res = dns_dbiterator_next(dbiter)) {
+ dns_dbiterator_current(dbiter, &node, dns_fixedname_name(&name));
+ REQUIRE(dns_db_allrdatasets(db, node, NULL, 0, &rdatasetiter)
+ == ISC_R_SUCCESS);
+
+ for (res = dns_rdatasetiter_first(rdatasetiter);
+ res == ISC_R_SUCCESS;
+ res = dns_rdatasetiter_next(rdatasetiter)) {
+ dns_rdatasetiter_current(rdatasetiter, &rdataset);
+
+ res = dns_rdataset_first(&rdataset);
+ while (res == ISC_R_SUCCESS) {
+ dns_rdataset_current(&rdataset, &rdata);
+ REQUIRE(bdb_putrdata(bdb,
+ dns_fixedname_name(&name),
+ rdataset.ttl, &rdata)
+ == ISC_R_SUCCESS);
+
+ dns_rdata_reset(&rdata);
+ res = dns_rdataset_next(&rdataset);
+ }
+
+ dns_rdataset_disassociate(&rdataset);
+ }
+ dns_rdatasetiter_destroy(&rdatasetiter);
+ dns_db_detachnode(db, &node);
+ }
+ dns_dbiterator_destroy(&dbiter);
+
+ REQUIRE(bdb_destroy(bdb) == ISC_R_SUCCESS);
+
+ return 0;
+}
diff --git a/contrib/sdb/dir/dirdb.c b/contrib/sdb/dir/dirdb.c
new file mode 100644
index 0000000..a83c4f3
--- /dev/null
+++ b/contrib/sdb/dir/dirdb.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*
+ * A simple database driver that returns basic information about
+ * files and directories in the Unix file system as DNS data.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/util.h>
+
+#include <dns/sdb.h>
+
+#include <named/globals.h>
+
+#include "dirdb.h"
+
+static dns_sdbimplementation_t *dirdb = NULL;
+
+#define CHECK(op) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) return (result); \
+ } while (0)
+
+#define CHECKN(op) \
+ do { n = (op); \
+ if (n < 0) return (ISC_R_FAILURE); \
+ } while (0)
+
+
+/*
+ * This database operates on relative names.
+ *
+ * Any name will be interpreted as a pathname offset from the directory
+ * specified in the configuration file.
+ */
+#ifdef DNS_CLIENTINFO_VERSION
+static isc_result_t
+dirdb_lookup(const char *zone, const char *name, void *dbdata,
+ dns_sdblookup_t *lookup, dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo)
+#else
+static isc_result_t
+dirdb_lookup(const char *zone, const char *name, void *dbdata,
+ dns_sdblookup_t *lookup)
+#endif /* DNS_CLIENTINFO_VERSION */
+{
+ char filename[255];
+ char filename2[255];
+ char buf[1024];
+ struct stat statbuf;
+ isc_result_t result;
+ int n;
+
+ UNUSED(zone);
+ UNUSED(dbdata);
+#ifdef DNS_CLIENTINFO_VERSION
+ UNUSED(methods);
+ UNUSED(clientinfo);
+#endif /* DNS_CLIENTINFO_VERSION */
+
+ if (strcmp(name, "@") == 0)
+ snprintf(filename, sizeof(filename), "%s", (char *)dbdata);
+ else
+ snprintf(filename, sizeof(filename), "%s/%s",
+ (char *)dbdata, name);
+ CHECKN(lstat(filename, &statbuf));
+
+ if (S_ISDIR(statbuf.st_mode))
+ CHECK(dns_sdb_putrr(lookup, "txt", 3600, "dir"));
+ else if (S_ISCHR(statbuf.st_mode) || S_ISBLK(statbuf.st_mode)) {
+ CHECKN(snprintf(buf, sizeof(buf),
+ "\"%sdev\" \"major %d\" \"minor %d\"",
+ S_ISCHR(statbuf.st_mode) ? "chr" : "blk",
+ major(statbuf.st_rdev),
+ minor(statbuf.st_rdev)));
+ CHECK(dns_sdb_putrr(lookup, "txt", 3600, buf));
+ } else if (S_ISFIFO(statbuf.st_mode))
+ CHECK(dns_sdb_putrr(lookup, "txt", 3600, "pipe"));
+ else if (S_ISSOCK(statbuf.st_mode))
+ CHECK(dns_sdb_putrr(lookup, "txt", 3600, "socket"));
+ else if (S_ISLNK(statbuf.st_mode)) {
+ CHECKN(readlink(filename, filename2, sizeof(filename2) - 1));
+ buf[n] = 0;
+ CHECKN(snprintf(buf, sizeof(buf), "\"symlink\" \"%s\"",
+ filename2));
+ CHECK(dns_sdb_putrr(lookup, "txt", 3600, buf));
+ } else if (!S_ISREG(statbuf.st_mode))
+ CHECK(dns_sdb_putrr(lookup, "txt", 3600, "unknown"));
+ else {
+ CHECKN(snprintf(buf, sizeof(buf), "\"file\" \"size = %u\"",
+ (unsigned int)statbuf.st_size));
+ CHECK(dns_sdb_putrr(lookup, "txt", 3600, buf));
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * lookup () does not return SOA or NS records, so authority() must be defined.
+ */
+static isc_result_t
+dirdb_authority(const char *zone, void *dbdata, dns_sdblookup_t *lookup) {
+ isc_result_t result;
+
+ UNUSED(zone);
+ UNUSED(dbdata);
+
+ result = dns_sdb_putsoa(lookup, "ns", "hostmaster", 0);
+ INSIST(result == ISC_R_SUCCESS);
+ result = dns_sdb_putrr(lookup, "ns", 86400, "ns1");
+ INSIST(result == ISC_R_SUCCESS);
+ result = dns_sdb_putrr(lookup, "ns", 86400, "ns2");
+ INSIST(result == ISC_R_SUCCESS);
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Each database stores the top-level directory as the dbdata opaque
+ * object. The create() function allocates it. argv[0] holds the top
+ * level directory.
+ */
+static isc_result_t
+dirdb_create(const char *zone, int argc, char **argv,
+ void *driverdata, void **dbdata)
+{
+ UNUSED(zone);
+ UNUSED(driverdata);
+
+ if (argc < 1)
+ return (ISC_R_FAILURE);
+ *dbdata = isc_mem_strdup((isc_mem_t *)driverdata, argv[0]);
+ if (*dbdata == NULL)
+ return (ISC_R_NOMEMORY);
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * The destroy() function frees the memory allocated by create().
+ */
+static void
+dirdb_destroy(const char *zone, void *driverdata, void **dbdata) {
+ UNUSED(zone);
+ UNUSED(driverdata);
+ isc_mem_free((isc_mem_t *)driverdata, *dbdata);
+}
+
+/*
+ * This zone does not support zone transfer, so allnodes() is NULL.
+ */
+static dns_sdbmethods_t dirdb_methods = {
+ dirdb_lookup,
+ dirdb_authority,
+ NULL, /* allnodes */
+ dirdb_create,
+ dirdb_destroy,
+ NULL /* lookup2 */
+};
+
+/*
+ * Wrapper around dns_sdb_register(). Note that the first ns_g_mctx is
+ * being passed as the "driverdata" parameter, so that will it will be
+ * passed to create() and destroy().
+ */
+isc_result_t
+dirdb_init(void) {
+ unsigned int flags;
+ flags = DNS_SDBFLAG_RELATIVEOWNER | DNS_SDBFLAG_RELATIVERDATA |
+ DNS_SDBFLAG_THREADSAFE;
+ return (dns_sdb_register("dir", &dirdb_methods, ns_g_mctx, flags,
+ ns_g_mctx, &dirdb));
+}
+
+/*
+ * Wrapper around dns_sdb_unregister().
+ */
+void
+dirdb_clear(void) {
+ if (dirdb != NULL)
+ dns_sdb_unregister(&dirdb);
+}
diff --git a/contrib/sdb/dir/dirdb.h b/contrib/sdb/dir/dirdb.h
new file mode 100644
index 0000000..e72dd69
--- /dev/null
+++ b/contrib/sdb/dir/dirdb.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#include <isc/types.h>
+
+isc_result_t dirdb_init(void);
+
+void dirdb_clear(void);
+
diff --git a/contrib/sdb/ldap/INSTALL.ldap b/contrib/sdb/ldap/INSTALL.ldap
new file mode 100644
index 0000000..9151129
--- /dev/null
+++ b/contrib/sdb/ldap/INSTALL.ldap
@@ -0,0 +1,83 @@
+This is the INSTALL file for 1.0-beta. See
+http://www.venaas.no/ldap/bind-sdb/ for updates or other information.
+
+BUILDING
+
+You need the source for BIND 9.1.0 or newer (for zone transfers you
+will need at least 9.1.1rc3 due to a bug). Basically you need to follow
+the instructions in doc/misc/sdb, if my instructions don't make sense,
+please have a look at those as well.
+
+Copy ldapdb.c to bin/named and ldapdb.h to bin/named/include in the
+source tree.
+
+Next alter bin/named/Makefile.in. Add ldapdb.@O@ to DBDRIVER_OBJS and
+ldapdb.c to DBDRIVER_SRCS. You also need to add something like
+-I/usr/local/include to DBDRIVER_INCLUDES and
+-L/usr/local/lib -lldap -llber -lresolv to DBDRIVER_LIBS
+depending on what LDAP library you have and where you installed it.
+
+Finally you need to edit bin/named/main.c. Below where it says
+"#include "xxdb.h"", add the line "#include <ldapdb.h>". Below where
+it says "xxdb_init();" add the line "ldapdb_init();", and finally
+below where it says "xxdb_clear();", add "ldapdb_clear();".
+
+Now you should hopefully be able to build as usual; first configure
+and then make. If you get an error message about ldap_memfree() not
+being defined, you're probably using an LDAP library with the
+interface defined in RFC 1823. To build, uncomment the "#define
+LDAPDB_RFC1823API" line near the top of ldapdb.c.
+
+Also, if you're using an LDAPv2 only server, you need to change
+the line "#define LDAPDB_LDAP_VERSION 3" in ldapdb.c. Simply
+replace 3 with 2. Instead of editing the file, you may define
+LDAPDB_LDAP_VERSION yourself.
+
+If you want to use TLS, you need to uncommed the #define LDAPDB_TLS"
+line near the top of ldapdb.c.
+
+CONFIGURING
+
+Before you do any configuring of LDAP stuff, please try to configure
+and start bind as usual to see if things work.
+
+To do anything useful, you need to store a zone in some LDAP server.
+You must use a schema called dNSZone. Note that it relies on some
+attribute definitions in the Cosine schema, so that must be included
+as well. The Cosine schema probably comes with your LDAP server. You
+can find dNSZone and further details on how to store the data in your
+LDAP server at http://www.venaas.no/ldap/bind-sdb/
+
+To make BIND use a zone stored in LDAP, you will have to put something
+like this in named.conf:
+
+zone "venaas.com" {
+ type master;
+ database "ldap ldap://158.38.160.245/dc=venaas,dc=com,o=DNS,dc=venaas,dc=no 172800";
+};
+
+When doing lookups BIND will do a sub-tree search below the base in the
+URL. The number 172800 is the TTL which will be used for all entries that
+haven't got the dNSTTL attribute. It is also possible to add a filter to
+the URL, say "ldap://host/base???(o=internal)".
+
+Version 1.0 also has support for simple LDAP bind, that is, binding to
+LDAP using plain text authentication. The bind dn and password is coded
+into the URL as extensions, according to RFC 2255. If you want simple
+bind with say dn "cn=Manager,dc=venaas,dc=no" and password "secret", the
+URL will be something like this:
+
+ldap://158.38.160.245/dc=venaas,dc=com,o=DNS,dc=venaas,dc=no????!bindname=cn=Manager%2cdc=venaas%2cdc=no,!x-bindpw=secret
+
+This URL may also include a filter part if you need it. Note that in
+the bind dn, "," is hex-escaped as "%2c". This is necessary since ","
+is the separator between the extension elements. The "!" in front of
+"bindname" and "x-bindpw" can be omitted if you prefer. "x-bindpw" is
+not standardized, but it's used by several other LDAP applications. See
+RFC 2255 for details.
+
+Finally, if you enabled TLS when compiling, you can also use TLS if
+you like. To do this you use the extension "x-tls", e.g.
+ldap://158.38.160.245/dc=venaas,dc=com,o=DNS,dc=venaas,dc=no????!bindname=cn=Manager%2cdc=venaas%2cdc=no,!x-bindpw=secret,x-tls
+
+Stig Venaas <venaas@uninett.no> 2004-08-15
diff --git a/contrib/sdb/ldap/README.ldap b/contrib/sdb/ldap/README.ldap
new file mode 100644
index 0000000..b4ea18a
--- /dev/null
+++ b/contrib/sdb/ldap/README.ldap
@@ -0,0 +1,48 @@
+This is an attempt at an LDAP back-end for BIND 9 using the new simplified
+database interface "sdb". This is release 1.0-beta and should be pretty
+stable. Note that since version 0.4 a new schema is used. It is not
+backwards compatible with versions before 0.4.
+
+1.0-beta fixes a large memory leak. An extension x-tls for enabling TLS
+has been added.
+
+1.0-alpha uses LDAPv3 by default and also supports LDAP simple bind. That
+is, one can use plain text password for authentication. The bind dn and
+password is coded into the URL using extensions bindname and x-bindpw
+per RFC 2255.
+
+In 0.9 the code has been cleaned up a bit and should be slightly faster
+than previous versions. It also fixes an error with zone transfers (AXFR)
+and entries with multiple relativeDomainName values. The problem was
+that it would only use the first value in the result. There's no need
+to upgrade unless you use such entries.
+
+0.8 uses asynchronous LDAP search which should give better performance.
+Thanks to Ashley Burston for providing patch. Another new feature is
+allowing filters in URLs. The syntax is as in RFC 2255. Few people will
+need this, but if you have say an internal and external version of the
+same zone, you could stick say o=internal and o=external into different
+entries, and specify for instance ldap://host/base???(o=internal)
+Some error logging has also been added.
+
+0.7 allows space and other characters to be used in URLs by use of %-quoting.
+For instance space can be written as %20. It also fixes a problem with some
+servers and/or APIs that do not preserve attribute casing.
+
+0.6 fixes some memory leaks present in older versions unless compiled with
+the RFC 1823 API.
+
+The big changes in 0.5 are thread support and improved connection handling.
+Multiple threads can now access the back-end simultaneously, and rather than
+having one connection per zone, there is now one connection per thread per
+LDAP server. This should help people with multiple CPUs and people with a
+huge number of zones. One final change is support for literal IPv6 addresses
+in LDAP URLs. At least OpenLDAP 2 has IPv6 support, so if you use OpenLDAP 2
+libraries and server, you got all you need.
+
+If you have bug reports, fixes, comments, questions or whatever, please
+contact me. See also http://www.venaas.no/ldap/bind-sdb/ for information.
+
+See INSTALL for how to build, install and use.
+
+Stig Venaas <venaas@uninett.no> 2004-08-15
diff --git a/contrib/sdb/ldap/README.zone2ldap b/contrib/sdb/ldap/README.zone2ldap
new file mode 100644
index 0000000..dacb56b
--- /dev/null
+++ b/contrib/sdb/ldap/README.zone2ldap
@@ -0,0 +1,17 @@
+INSTALLATION
+
+To Compile zone2ldap from contrib/sdb directory:
+
+ gcc -g `../../../isc-config.sh --cflags isc dns` -c zone2ldap.c
+ gcc -g -o zone2ldap zone2ldap.o `../../../isc-config.sh --libs isc dns` -lldap -llber -lresolv
+
+USAGE:
+
+See zone2ldap.1
+
+BUGS:
+
+Jeff McNeil <jeff@snapcase.g-rock.net>
+
+
+
diff --git a/contrib/sdb/ldap/ldapdb.c b/contrib/sdb/ldap/ldapdb.c
new file mode 100644
index 0000000..c43342c
--- /dev/null
+++ b/contrib/sdb/ldap/ldapdb.c
@@ -0,0 +1,690 @@
+/*
+ * ldapdb.c version 1.0-beta
+ *
+ * Copyright (C) 2002, 2004 Stig Venaas
+ *
+ * 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.
+ *
+ * Contributors: Jeremy C. McDermond
+ */
+
+/*
+ * If you want to use TLS, uncomment the define below
+ */
+/* #define LDAPDB_TLS */
+
+/*
+ * If you are using an old LDAP API uncomment the define below. Only do this
+ * if you know what you're doing or get compilation errors on ldap_memfree().
+ * This also forces LDAPv2.
+ */
+/* #define LDAPDB_RFC1823API */
+
+/* Using LDAPv3 by default, change this if you want v2 */
+#ifndef LDAPDB_LDAP_VERSION
+#define LDAPDB_LDAP_VERSION 3
+#endif
+
+#include <config.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/util.h>
+#include <isc/thread.h>
+
+#include <dns/sdb.h>
+
+#include <named/globals.h>
+#include <named/log.h>
+
+#include <ldap.h>
+#include "ldapdb.h"
+
+/*
+ * A simple database driver for LDAP
+ */
+
+/* enough for name with 8 labels of max length */
+#define MAXNAMELEN 519
+
+static dns_sdbimplementation_t *ldapdb = NULL;
+
+struct ldapdb_data {
+ char *hostport;
+ char *hostname;
+ int portno;
+ char *base;
+ int defaultttl;
+ char *filterall;
+ int filteralllen;
+ char *filterone;
+ int filteronelen;
+ char *filtername;
+ char *bindname;
+ char *bindpw;
+#ifdef LDAPDB_TLS
+ int tls;
+#endif
+};
+
+/* used by ldapdb_getconn */
+
+struct ldapdb_entry {
+ void *index;
+ size_t size;
+ void *data;
+ struct ldapdb_entry *next;
+};
+
+static struct ldapdb_entry *ldapdb_find(struct ldapdb_entry *stack,
+ const void *index, size_t size) {
+ while (stack != NULL) {
+ if (stack->size == size && !memcmp(stack->index, index, size))
+ return stack;
+ stack = stack->next;
+ }
+ return NULL;
+}
+
+static void ldapdb_insert(struct ldapdb_entry **stack,
+ struct ldapdb_entry *item) {
+ item->next = *stack;
+ *stack = item;
+}
+
+static void ldapdb_lock(int what) {
+ static isc_mutex_t lock;
+
+ switch (what) {
+ case 0:
+ isc_mutex_init(&lock);
+ break;
+ case 1:
+ LOCK(&lock);
+ break;
+ case -1:
+ UNLOCK(&lock);
+ break;
+ }
+}
+
+/* data == NULL means cleanup */
+static LDAP **
+ldapdb_getconn(struct ldapdb_data *data)
+{
+ static struct ldapdb_entry *allthreadsdata = NULL;
+ struct ldapdb_entry *threaddata, *conndata;
+ unsigned long threadid;
+
+ if (data == NULL) {
+ /* cleanup */
+ /* lock out other threads */
+ ldapdb_lock(1);
+ while (allthreadsdata != NULL) {
+ threaddata = allthreadsdata;
+ free(threaddata->index);
+ while (threaddata->data != NULL) {
+ conndata = threaddata->data;
+ if (conndata->data != NULL)
+ ldap_unbind((LDAP *)conndata->data);
+ threaddata->data = conndata->next;
+ free(conndata);
+ }
+ allthreadsdata = threaddata->next;
+ free(threaddata);
+ }
+ ldapdb_lock(-1);
+ return (NULL);
+ }
+
+ /* look for connection data for current thread */
+ threadid = isc_thread_self();
+ threaddata = ldapdb_find(allthreadsdata, &threadid, sizeof(threadid));
+ if (threaddata == NULL) {
+ /* no data for this thread, create empty connection list */
+ threaddata = malloc(sizeof(*threaddata));
+ if (threaddata == NULL)
+ return (NULL);
+ threaddata->index = malloc(sizeof(threadid));
+ if (threaddata->index == NULL) {
+ free(threaddata);
+ return (NULL);
+ }
+ *(unsigned long *)threaddata->index = threadid;
+ threaddata->size = sizeof(threadid);
+ threaddata->data = NULL;
+
+ /* need to lock out other threads here */
+ ldapdb_lock(1);
+ ldapdb_insert(&allthreadsdata, threaddata);
+ ldapdb_lock(-1);
+ }
+
+ /* threaddata points at the connection list for current thread */
+ /* look for existing connection to our server */
+ conndata = ldapdb_find((struct ldapdb_entry *)threaddata->data,
+ data->hostport, strlen(data->hostport));
+ if (conndata == NULL) {
+ /* no connection data structure for this server, create one */
+ conndata = malloc(sizeof(*conndata));
+ if (conndata == NULL)
+ return (NULL);
+ conndata->index = data->hostport;
+ conndata->size = strlen(data->hostport);
+ conndata->data = NULL;
+ ldapdb_insert((struct ldapdb_entry **)&threaddata->data,
+ conndata);
+ }
+
+ return (LDAP **)&conndata->data;
+}
+
+static void
+ldapdb_bind(struct ldapdb_data *data, LDAP **ldp)
+{
+#ifndef LDAPDB_RFC1823API
+ const int ver = LDAPDB_LDAP_VERSION;
+#endif
+
+ if (*ldp != NULL)
+ ldap_unbind(*ldp);
+ *ldp = ldap_open(data->hostname, data->portno);
+ if (*ldp == NULL)
+ return;
+
+#ifndef LDAPDB_RFC1823API
+ ldap_set_option(*ldp, LDAP_OPT_PROTOCOL_VERSION, &ver);
+#endif
+
+#ifdef LDAPDB_TLS
+ if (data->tls) {
+ ldap_start_tls_s(*ldp, NULL, NULL);
+ }
+#endif
+
+ if (ldap_simple_bind_s(*ldp, data->bindname, data->bindpw) != LDAP_SUCCESS) {
+ ldap_unbind(*ldp);
+ *ldp = NULL;
+ }
+}
+
+#ifdef DNS_CLIENTINFO_VERSION
+static isc_result_t
+ldapdb_search(const char *zone, const char *name, void *dbdata, void *retdata,
+ dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo)
+#else
+static isc_result_t
+ldapdb_search(const char *zone, const char *name, void *dbdata, void *retdata,
+ void *methods, void *clientinfo)
+#endif /* DNS_CLIENTINFO_VERSION */
+{
+ struct ldapdb_data *data = dbdata;
+ isc_result_t result = ISC_R_NOTFOUND;
+ LDAP **ldp;
+ LDAPMessage *res, *e;
+ char *fltr, *a, **vals = NULL, **names = NULL;
+ char type[64];
+#ifdef LDAPDB_RFC1823API
+ void *ptr;
+#else
+ BerElement *ptr;
+#endif
+ int i, j, errno, msgid;
+
+ UNUSED(methods);
+ UNUSED(clientinfo);
+
+ ldp = ldapdb_getconn(data);
+ if (ldp == NULL)
+ return (ISC_R_FAILURE);
+ if (*ldp == NULL) {
+ ldapdb_bind(data, ldp);
+ if (*ldp == NULL) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "LDAP sdb zone '%s': bind failed", zone);
+ return (ISC_R_FAILURE);
+ }
+ }
+
+ if (name == NULL) {
+ fltr = data->filterall;
+ } else {
+ if (strlen(name) > MAXNAMELEN) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "LDAP sdb zone '%s': name %s too long", zone, name);
+ return (ISC_R_FAILURE);
+ }
+ sprintf(data->filtername, "%s))", name);
+ fltr = data->filterone;
+ }
+
+ msgid = ldap_search(*ldp, data->base, LDAP_SCOPE_SUBTREE, fltr, NULL, 0);
+ if (msgid == -1) {
+ ldapdb_bind(data, ldp);
+ if (*ldp != NULL)
+ msgid = ldap_search(*ldp, data->base, LDAP_SCOPE_SUBTREE, fltr, NULL, 0);
+ }
+
+ if (*ldp == NULL || msgid == -1) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "LDAP sdb zone '%s': search failed, filter %s", zone, fltr);
+ return (ISC_R_FAILURE);
+ }
+
+ /* Get the records one by one as they arrive and return them to bind */
+ while ((errno = ldap_result(*ldp, msgid, 0, NULL, &res)) != LDAP_RES_SEARCH_RESULT ) {
+ LDAP *ld = *ldp;
+ int ttl = data->defaultttl;
+
+ /* not supporting continuation references at present */
+ if (errno != LDAP_RES_SEARCH_ENTRY) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "LDAP sdb zone '%s': ldap_result returned %d", zone, errno);
+ ldap_msgfree(res);
+ return (ISC_R_FAILURE);
+ }
+
+ /* only one entry per result message */
+ e = ldap_first_entry(ld, res);
+ if (e == NULL) {
+ ldap_msgfree(res);
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "LDAP sdb zone '%s': ldap_first_entry failed", zone);
+ return (ISC_R_FAILURE);
+ }
+
+ if (name == NULL) {
+ names = ldap_get_values(ld, e, "relativeDomainName");
+ if (names == NULL)
+ continue;
+ }
+
+ vals = ldap_get_values(ld, e, "dNSTTL");
+ if (vals != NULL) {
+ ttl = atoi(vals[0]);
+ ldap_value_free(vals);
+ }
+
+ for (a = ldap_first_attribute(ld, e, &ptr); a != NULL; a = ldap_next_attribute(ld, e, ptr)) {
+ char *s;
+
+ for (s = a; *s; s++)
+ *s = toupper(*s);
+ s = strstr(a, "RECORD");
+ if ((s == NULL) || (s == a) || (s - a >= (signed int)sizeof(type))) {
+#ifndef LDAPDB_RFC1823API
+ ldap_memfree(a);
+#endif
+ continue;
+ }
+
+ strncpy(type, a, s - a);
+ type[s - a] = '\0';
+ vals = ldap_get_values(ld, e, a);
+ if (vals != NULL) {
+ for (i = 0; vals[i] != NULL; i++) {
+ if (name != NULL) {
+ result = dns_sdb_putrr(retdata, type, ttl, vals[i]);
+ } else {
+ for (j = 0; names[j] != NULL; j++) {
+ result = dns_sdb_putnamedrr(retdata, names[j], type, ttl, vals[i]);
+ if (result != ISC_R_SUCCESS)
+ break;
+ }
+ }
+; if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "LDAP sdb zone '%s': dns_sdb_put... failed for %s", zone, vals[i]);
+ ldap_value_free(vals);
+#ifndef LDAPDB_RFC1823API
+ ldap_memfree(a);
+ if (ptr != NULL)
+ ber_free(ptr, 0);
+#endif
+ if (name == NULL)
+ ldap_value_free(names);
+ ldap_msgfree(res);
+ return (ISC_R_FAILURE);
+ }
+ }
+ ldap_value_free(vals);
+ }
+#ifndef LDAPDB_RFC1823API
+ ldap_memfree(a);
+#endif
+ }
+#ifndef LDAPDB_RFC1823API
+ if (ptr != NULL)
+ ber_free(ptr, 0);
+#endif
+ if (name == NULL)
+ ldap_value_free(names);
+
+ /* free this result */
+ ldap_msgfree(res);
+ }
+
+ /* free final result */
+ ldap_msgfree(res);
+ return (result);
+}
+
+
+/* callback routines */
+#ifdef DNS_CLIENTINFO_VERSION
+static isc_result_t
+ldapdb_lookup(const char *zone, const char *name, void *dbdata,
+ dns_sdblookup_t *lookup, dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo)
+{
+ UNUSED(methods);
+ UNUSED(clientinfo);
+ return (ldapdb_search(zone, name, dbdata, lookup, NULL, NULL));
+}
+#else
+static isc_result_t
+ldapdb_lookup(const char *zone, const char *name, void *dbdata,
+ dns_sdblookup_t *lookup)
+{
+ return (ldapdb_search(zone, name, dbdata, lookup, methods,
+ clientinfo));
+}
+#endif /* DNS_CLIENTINFO_VERSION */
+
+static isc_result_t
+ldapdb_allnodes(const char *zone, void *dbdata,
+ dns_sdballnodes_t *allnodes)
+{
+ return (ldapdb_search(zone, NULL, dbdata, allnodes, NULL, NULL));
+}
+
+static char *
+unhex(char *in)
+{
+ static const char hexdigits[] = "0123456789abcdef";
+ char *p, *s = in;
+ int d1, d2;
+
+ while ((s = strchr(s, '%'))) {
+ if (!(s[1] && s[2]))
+ return NULL;
+ if ((p = strchr(hexdigits, tolower(s[1]))) == NULL)
+ return NULL;
+ d1 = p - hexdigits;
+ if ((p = strchr(hexdigits, tolower(s[2]))) == NULL)
+ return NULL;
+ d2 = p - hexdigits;
+ *s++ = d1 << 4 | d2;
+ memmove(s, s + 2, strlen(s) - 1);
+ }
+ return in;
+}
+
+/* returns 0 for ok, -1 for bad syntax, -2 for unknown critical extension */
+static int
+parseextensions(char *extensions, struct ldapdb_data *data)
+{
+ char *s, *next, *name, *value;
+ int critical;
+
+ while (extensions != NULL) {
+ s = strchr(extensions, ',');
+ if (s != NULL) {
+ *s++ = '\0';
+ next = s;
+ } else {
+ next = NULL;
+ }
+
+ if (*extensions != '\0') {
+ s = strchr(extensions, '=');
+ if (s != NULL) {
+ *s++ = '\0';
+ value = *s != '\0' ? s : NULL;
+ } else {
+ value = NULL;
+ }
+ name = extensions;
+
+ critical = *name == '!';
+ if (critical) {
+ name++;
+ }
+ if (*name == '\0') {
+ return -1;
+ }
+
+ if (!strcasecmp(name, "bindname")) {
+ data->bindname = value;
+ } else if (!strcasecmp(name, "x-bindpw")) {
+ data->bindpw = value;
+#ifdef LDAPDB_TLS
+ } else if (!strcasecmp(name, "x-tls")) {
+ data->tls = value == NULL || !strcasecmp(value, "true");
+#endif
+ } else if (critical) {
+ return -2;
+ }
+ }
+ extensions = next;
+ }
+ return 0;
+}
+
+static void
+free_data(struct ldapdb_data *data)
+{
+ if (data->hostport != NULL)
+ isc_mem_free(ns_g_mctx, data->hostport);
+ if (data->hostname != NULL)
+ isc_mem_free(ns_g_mctx, data->hostname);
+ if (data->filterall != NULL)
+ isc_mem_put(ns_g_mctx, data->filterall, data->filteralllen);
+ if (data->filterone != NULL)
+ isc_mem_put(ns_g_mctx, data->filterone, data->filteronelen);
+ isc_mem_put(ns_g_mctx, data, sizeof(struct ldapdb_data));
+}
+
+
+static isc_result_t
+ldapdb_create(const char *zone, int argc, char **argv,
+ void *driverdata, void **dbdata)
+{
+ struct ldapdb_data *data;
+ char *s, *filter = NULL, *extensions = NULL;
+ int defaultttl;
+
+ UNUSED(driverdata);
+
+ /* we assume that only one thread will call create at a time */
+ /* want to do this only once for all instances */
+
+ if ((argc < 2)
+ || (argv[0] != strstr( argv[0], "ldap://"))
+ || ((defaultttl = atoi(argv[1])) < 1))
+ return (ISC_R_FAILURE);
+ data = isc_mem_get(ns_g_mctx, sizeof(struct ldapdb_data));
+ if (data == NULL)
+ return (ISC_R_NOMEMORY);
+
+ memset(data, 0, sizeof(struct ldapdb_data));
+ data->hostport = isc_mem_strdup(ns_g_mctx, argv[0] + strlen("ldap://"));
+ if (data->hostport == NULL) {
+ free_data(data);
+ return (ISC_R_NOMEMORY);
+ }
+
+ data->defaultttl = defaultttl;
+
+ s = strchr(data->hostport, '/');
+ if (s != NULL) {
+ *s++ = '\0';
+ data->base = s;
+ /* attrs, scope, filter etc? */
+ s = strchr(s, '?');
+ if (s != NULL) {
+ *s++ = '\0';
+ /* ignore attributes */
+ s = strchr(s, '?');
+ if (s != NULL) {
+ *s++ = '\0';
+ /* ignore scope */
+ s = strchr(s, '?');
+ if (s != NULL) {
+ *s++ = '\0';
+ /* filter */
+ filter = s;
+ s = strchr(s, '?');
+ if (s != NULL) {
+ *s++ = '\0';
+ /* extensions */
+ extensions = s;
+ s = strchr(s, '?');
+ if (s != NULL) {
+ *s++ = '\0';
+ }
+ if (*extensions == '\0') {
+ extensions = NULL;
+ }
+ }
+ if (*filter == '\0') {
+ filter = NULL;
+ }
+ }
+ }
+ }
+ if (*data->base == '\0') {
+ data->base = NULL;
+ }
+ }
+
+ /* parse extensions */
+ if (extensions != NULL) {
+ int err;
+
+ err = parseextensions(extensions, data);
+ if (err < 0) {
+ /* err should be -1 or -2 */
+ free_data(data);
+ if (err == -1) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "LDAP sdb zone '%s': URL: extension syntax error", zone);
+ } else if (err == -2) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "LDAP sdb zone '%s': URL: unknown critical extension", zone);
+ }
+ return (ISC_R_FAILURE);
+ }
+ }
+
+ if ((data->base != NULL && unhex(data->base) == NULL) ||
+ (filter != NULL && unhex(filter) == NULL) ||
+ (data->bindname != NULL && unhex(data->bindname) == NULL) ||
+ (data->bindpw != NULL && unhex(data->bindpw) == NULL)) {
+ free_data(data);
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "LDAP sdb zone '%s': URL: bad hex values", zone);
+ return (ISC_R_FAILURE);
+ }
+
+ /* compute filterall and filterone once and for all */
+ if (filter == NULL) {
+ data->filteralllen = strlen(zone) + strlen("(zoneName=)") + 1;
+ data->filteronelen = strlen(zone) + strlen("(&(zoneName=)(relativeDomainName=))") + MAXNAMELEN + 1;
+ } else {
+ data->filteralllen = strlen(filter) + strlen(zone) + strlen("(&(zoneName=))") + 1;
+ data->filteronelen = strlen(filter) + strlen(zone) + strlen("(&(zoneName=)(relativeDomainName=))") + MAXNAMELEN + 1;
+ }
+
+ data->filterall = isc_mem_get(ns_g_mctx, data->filteralllen);
+ if (data->filterall == NULL) {
+ free_data(data);
+ return (ISC_R_NOMEMORY);
+ }
+ data->filterone = isc_mem_get(ns_g_mctx, data->filteronelen);
+ if (data->filterone == NULL) {
+ free_data(data);
+ return (ISC_R_NOMEMORY);
+ }
+
+ if (filter == NULL) {
+ sprintf(data->filterall, "(zoneName=%s)", zone);
+ sprintf(data->filterone, "(&(zoneName=%s)(relativeDomainName=", zone);
+ } else {
+ sprintf(data->filterall, "(&%s(zoneName=%s))", filter, zone);
+ sprintf(data->filterone, "(&%s(zoneName=%s)(relativeDomainName=", filter, zone);
+ }
+ data->filtername = data->filterone + strlen(data->filterone);
+
+ /* support URLs with literal IPv6 addresses */
+ data->hostname = isc_mem_strdup(ns_g_mctx, data->hostport + (*data->hostport == '[' ? 1 : 0));
+ if (data->hostname == NULL) {
+ free_data(data);
+ return (ISC_R_NOMEMORY);
+ }
+
+ if (*data->hostport == '[' &&
+ (s = strchr(data->hostname, ']')) != NULL )
+ *s++ = '\0';
+ else
+ s = data->hostname;
+ s = strchr(s, ':');
+ if (s != NULL) {
+ *s++ = '\0';
+ data->portno = atoi(s);
+ } else
+ data->portno = LDAP_PORT;
+
+ *dbdata = data;
+ return (ISC_R_SUCCESS);
+}
+
+static void
+ldapdb_destroy(const char *zone, void *driverdata, void **dbdata) {
+ struct ldapdb_data *data = *dbdata;
+
+ UNUSED(zone);
+ UNUSED(driverdata);
+
+ free_data(data);
+}
+
+static dns_sdbmethods_t ldapdb_methods = {
+ ldapdb_lookup,
+ NULL, /* authority */
+ ldapdb_allnodes,
+ ldapdb_create,
+ ldapdb_destroy,
+ NULL /* lookup2 */
+};
+
+/* Wrapper around dns_sdb_register() */
+isc_result_t
+ldapdb_init(void) {
+ unsigned int flags =
+ DNS_SDBFLAG_RELATIVEOWNER |
+ DNS_SDBFLAG_RELATIVERDATA |
+ DNS_SDBFLAG_THREADSAFE;
+
+ ldapdb_lock(0);
+ return (dns_sdb_register("ldap", &ldapdb_methods, NULL, flags,
+ ns_g_mctx, &ldapdb));
+}
+
+/* Wrapper around dns_sdb_unregister() */
+void
+ldapdb_clear(void) {
+ if (ldapdb != NULL) {
+ /* clean up thread data */
+ ldapdb_getconn(NULL);
+ dns_sdb_unregister(&ldapdb);
+ }
+}
diff --git a/contrib/sdb/ldap/ldapdb.h b/contrib/sdb/ldap/ldapdb.h
new file mode 100644
index 0000000..a08eb20
--- /dev/null
+++ b/contrib/sdb/ldap/ldapdb.h
@@ -0,0 +1,6 @@
+#include <isc/types.h>
+
+isc_result_t ldapdb_init(void);
+
+void ldapdb_clear(void);
+
diff --git a/contrib/sdb/ldap/zone2ldap.1 b/contrib/sdb/ldap/zone2ldap.1
new file mode 100644
index 0000000..781114b
--- /dev/null
+++ b/contrib/sdb/ldap/zone2ldap.1
@@ -0,0 +1,64 @@
+.TH zone2ldap 1 "8 March 2001"
+.SH NAME
+zone2ldap /- Load BIND 9 Zone files into LDAP Directory
+.SH SYNOPSIS
+zone2ldap [-D Bind DN] [-w Bind Password] [-b Base DN] [-z Zone] [-f Zone File ] [-h Ldap Host] [-cd] [-v]
+.SH DESCRIPTION
+zone2ldap will parse a complete BIND 9 format DNS zone file, and load
+the contents into an LDAP directory, for use with the LDAP sdb back-end.
+
+If the zone already exists, zone2ldap will exit succesfully. If the zone does not exists, or
+partially exists, zone2ldap will attempt to add all/missing zone data.
+
+.SS Options
+.TP
+-b
+LDAP Base DN. LDAP systems require a "base dn", which is generally considered the LDAP Directory root.
+If the zone you are loading is different from the base, then you will need to tell zone2ldap what your LDAP
+base is.
+.TP
+-v
+Print version information, and immediatly exit.
+.TP
+-f
+Zone file. Bind 9.1 compatible zone file, from which zone information will be read.
+.TP
+-d
+Dump debug information to standard out.
+.TP
+-w
+LDAP Bind password, corresponding the the value of "-b".
+.TP
+-h
+LDAP Directory host. This is the hostname of the LDAP system you wish to store zone information on.
+An LDAP server should be listening on port 389 of the target system. This may be ommited, and will default
+to "localhost".
+.TP
+-c
+This will create the zone portion of the DN you are importing. For instance, if you are creating a domain.com zone,
+zone2ldap should first create "dc=domain,dc=com". This is useful if you are creating multiple domains.
+.TP
+-z
+This is the name of the zone specified in the SOA record.
+.SH EXAMPLES
+Following are brief examples of how to import a zone file into your LDAP DIT.
+.SS Loading zone domain.com, with an LDAP Base DN of dc=domain,dc=com
+zone2ldap -D dc=root -w secret -h localhost -z domain.com -f domain.com.zone
+
+This will add Resource Records into an ALREADY EXISTING dc=domain,dc=com. The final SOA DN in this case, will be
+dc=@,dc=domain,dc=com
+
+.SS Loading customer.com, if your LDAP Base DN is dc=provider,dc=net.
+zone2ldap -D dc=root -w secret -h localhost -z customer.com -b dc=provider,dc=net -f customer.com.zone -c
+
+This will create dc=customer,dc=com under dc=provider,dc=net, and add all necessary Resource Records. The final
+root DN to the SOA will be dc=@,dc=customer,dc=com,dc=provider,dc=net.
+
+.SH "SEE ALSO"
+named(8) ldap(3)
+http://www.venaas.no/ldap/bind-sdb/
+.SH "BUGS"
+Send all bug reports to Jeff McNeil <jeff@snapcase.g-rock.net>
+.SH AUTHOR
+Jeff McNeil <jeff@snapcase.g-rock.net>
+
diff --git a/contrib/sdb/ldap/zone2ldap.c b/contrib/sdb/ldap/zone2ldap.c
new file mode 100644
index 0000000..301265a
--- /dev/null
+++ b/contrib/sdb/ldap/zone2ldap.c
@@ -0,0 +1,772 @@
+/*
+ * Copyright (C) 2001 Jeff McNeil <jeff@snapcase.g-rock.net>
+ *
+ * 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.
+ *
+ * Change Log
+ *
+ * Tue May 1 19:19:54 EDT 2001 - Jeff McNeil
+ * Update to objectClass code, and add_to_rr_list function
+ * (I need to rename that) to support the dNSZone schema,
+ * ditched dNSDomain2 schema support. Version 0.3-ALPHA
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include <isc/buffer.h>
+#include <isc/entropy.h>
+#include <isc/hash.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/string.h>
+
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/result.h>
+#include <dns/rdatatype.h>
+
+#define LDAP_DEPRECATED 1
+
+#include <ldap.h>
+
+#define DNS_OBJECT 6
+#define DNS_TOP 2
+
+#define VERSION "0.4-ALPHA"
+
+#define NO_SPEC 0
+#define WI_SPEC 1
+
+/* Global Zone Pointer */
+char *gbl_zone = NULL;
+
+typedef struct LDAP_INFO
+{
+ char *dn;
+ LDAPMod **attrs;
+ struct LDAP_INFO *next;
+ int attrcnt;
+}
+ldap_info;
+
+/* usage Info */
+void usage ();
+
+/* Add to the ldap dit */
+void add_ldap_values (ldap_info * ldinfo);
+
+/* Init an ldap connection */
+void init_ldap_conn ();
+
+/* Ldap error checking */
+void ldap_result_check (char *msg, char *dn, int err);
+
+/* Put a hostname into a char ** array */
+char **hostname_to_dn_list (char *hostname, char *zone, unsigned int flags);
+
+/* Find out how many items are in a char ** array */
+int get_attr_list_size (char **tmp);
+
+/* Get a DN */
+char *build_dn_from_dc_list (char **dc_list, unsigned int ttl, int flag);
+
+/* Add to RR list */
+void add_to_rr_list (char *dn, char *name, char *type, char *data,
+ unsigned int ttl, unsigned int flags);
+
+/* Error checking */
+void isc_result_check (isc_result_t res, char *errorstr);
+
+/* Generate LDIF Format files */
+void generate_ldap (dns_name_t * dnsname, dns_rdata_t * rdata,
+ unsigned int ttl);
+
+/* head pointer to the list */
+ldap_info *ldap_info_base = NULL;
+
+char *argzone, *ldapbase, *binddn, *bindpw = NULL;
+char *ldapsystem = "localhost";
+static char *objectClasses[] =
+ { "top", "dNSZone", NULL };
+static char *topObjectClasses[] = { "top", NULL };
+LDAP *conn;
+unsigned int debug = 0;
+
+#ifdef DEBUG
+debug = 1;
+#endif
+
+static void
+fatal(const char *msg) {
+ perror(msg);
+ if (conn != NULL)
+ ldap_unbind_s(conn);
+ exit(1);
+}
+
+int
+main (int argc, char **argv)
+{
+ isc_mem_t *mctx = NULL;
+ isc_entropy_t *ectx = NULL;
+ isc_result_t result;
+ char *basedn;
+ ldap_info *tmp;
+ LDAPMod *base_attrs[2];
+ LDAPMod base;
+ isc_buffer_t buff;
+ char *zonefile;
+ char fullbasedn[1024];
+ char *ctmp;
+ dns_fixedname_t fixedzone, fixedname;
+ dns_rdataset_t rdataset;
+ char **dc_list;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdatasetiter_t *riter;
+ dns_name_t *zone, *name;
+ dns_db_t *db = NULL;
+ dns_dbiterator_t *dbit = NULL;
+ dns_dbnode_t *node;
+ extern char *optarg;
+ extern int optind, opterr, optopt;
+ int create_base = 0;
+ int topt;
+
+ if (argc < 2)
+ {
+ usage ();
+ exit (-1);
+ }
+
+ while ((topt = getopt (argc, argv, "D:w:b:z:f:h:?dcv")) != -1)
+ {
+ switch (topt)
+ {
+ case 'v':
+ printf("%s\n", VERSION);
+ exit(0);
+ case 'c':
+ create_base++;
+ break;
+ case 'd':
+ debug++;
+ break;
+ case 'D':
+ binddn = strdup (optarg);
+ if (binddn == NULL)
+ fatal("strdup");
+ break;
+ case 'w':
+ bindpw = strdup (optarg);
+ if (bindpw == NULL)
+ fatal("strdup");
+ break;
+ case 'b':
+ ldapbase = strdup (optarg);
+ if (ldapbase == NULL)
+ fatal("strdup");
+ break;
+ case 'z':
+ argzone = strdup (optarg);
+ // We wipe argzone all to hell when we parse it for the DN */
+ gbl_zone = strdup(argzone);
+ if (argzone == NULL || gbl_zone == NULL)
+ fatal("strdup");
+ break;
+ case 'f':
+ zonefile = strdup (optarg);
+ if (zonefile == NULL)
+ fatal("strdup");
+ break;
+ case 'h':
+ ldapsystem = strdup (optarg);
+ if (ldapsystem == NULL)
+ fatal("strdup");
+ break;
+ case '?':
+ default:
+ usage ();
+ exit (0);
+ }
+ }
+
+ if ((argzone == NULL) || (zonefile == NULL))
+ {
+ usage ();
+ exit (-1);
+ }
+
+ if (debug)
+ printf ("Initializing ISC Routines, parsing zone file\n");
+
+ result = isc_mem_create (0, 0, &mctx);
+ isc_result_check (result, "isc_mem_create");
+
+ result = isc_entropy_create(mctx, &ectx);
+ isc_result_check (result, "isc_entropy_create");
+
+ result = isc_hash_create(mctx, ectx, DNS_NAME_MAXWIRE);
+ isc_result_check (result, "isc_hash_create");
+
+ isc_buffer_init (&buff, argzone, strlen (argzone));
+ isc_buffer_add (&buff, strlen (argzone));
+ zone = dns_fixedname_initname(&fixedzone);
+ result = dns_name_fromtext (zone, &buff, dns_rootname, 0, NULL);
+ isc_result_check (result, "dns_name_fromtext");
+
+ result = dns_db_create (mctx, "rbt", zone, dns_dbtype_zone,
+ dns_rdataclass_in, 0, NULL, &db);
+ isc_result_check (result, "dns_db_create");
+
+ result = dns_db_load (db, zonefile);
+ isc_result_check (result, "Check Zone Syntax: dns_db_load");
+
+ result = dns_db_createiterator (db, 0, &dbit);
+ isc_result_check (result, "dns_db_createiterator");
+
+ result = dns_dbiterator_first (dbit);
+ isc_result_check (result, "dns_dbiterator_first");
+
+ name = dns_fixedname_initname(&fixedname);
+ dns_rdataset_init (&rdataset);
+ dns_rdata_init (&rdata);
+
+ while (result == ISC_R_SUCCESS)
+ {
+ node = NULL;
+ result = dns_dbiterator_current (dbit, &node, name);
+
+ if (result == ISC_R_NOMORE)
+ break;
+
+ isc_result_check (result, "dns_dbiterator_current");
+
+ riter = NULL;
+ result = dns_db_allrdatasets (db, node, NULL, 0, &riter);
+ isc_result_check (result, "dns_db_allrdatasets");
+
+ result = dns_rdatasetiter_first (riter);
+ //isc_result_check(result, "dns_rdatasetiter_first");
+
+ while (result == ISC_R_SUCCESS)
+ {
+ dns_rdatasetiter_current (riter, &rdataset);
+ result = dns_rdataset_first (&rdataset);
+ isc_result_check (result, "dns_rdatasetiter_current");
+
+ while (result == ISC_R_SUCCESS)
+ {
+ dns_rdataset_current (&rdataset, &rdata);
+ generate_ldap (name, &rdata, rdataset.ttl);
+ dns_rdata_reset (&rdata);
+ result = dns_rdataset_next (&rdataset);
+ }
+ dns_rdataset_disassociate (&rdataset);
+ result = dns_rdatasetiter_next (riter);
+
+ }
+ dns_rdatasetiter_destroy (&riter);
+ result = dns_dbiterator_next (dbit);
+
+ }
+
+ /* Initialize the LDAP Connection */
+ if (debug)
+ printf ("Initializing LDAP Connection to %s as %s\n", ldapsystem, binddn);
+
+ init_ldap_conn ();
+
+ if (create_base)
+ {
+ if (debug)
+ printf ("Creating base zone DN %s\n", argzone);
+
+ dc_list = hostname_to_dn_list (argzone, argzone, DNS_TOP);
+ basedn = build_dn_from_dc_list (dc_list, 0, NO_SPEC);
+
+ for (ctmp = &basedn[strlen (basedn)]; ctmp >= &basedn[0]; ctmp--)
+ {
+ if ((*ctmp == ',') || (ctmp == &basedn[0]))
+ {
+ base.mod_op = LDAP_MOD_ADD;
+ base.mod_type = "objectClass";
+ base.mod_values = topObjectClasses;
+ base_attrs[0] = &base;
+ base_attrs[1] = NULL;
+
+ if (ldapbase)
+ {
+ if (ctmp != &basedn[0])
+ sprintf (fullbasedn, "%s,%s", ctmp + 1, ldapbase);
+ else
+ sprintf (fullbasedn, "%s,%s", ctmp, ldapbase);
+
+ }
+ else
+ {
+ if (ctmp != &basedn[0])
+ sprintf (fullbasedn, "%s", ctmp + 1);
+ else
+ sprintf (fullbasedn, "%s", ctmp);
+ }
+ result = ldap_add_s (conn, fullbasedn, base_attrs);
+ ldap_result_check ("intial ldap_add_s", fullbasedn, result);
+ }
+
+ }
+ }
+ else
+ {
+ if (debug)
+ printf ("Skipping zone base dn creation for %s\n", argzone);
+ }
+
+ for (tmp = ldap_info_base; tmp != NULL; tmp = tmp->next)
+ {
+
+ if (debug)
+ printf ("Adding DN: %s\n", tmp->dn);
+
+ add_ldap_values (tmp);
+ }
+
+ if (debug)
+ printf("Operation Complete.\n");
+
+ /* Cleanup */
+ isc_hash_destroy();
+ isc_entropy_detach(&ectx);
+ isc_mem_destroy(&mctx);
+ if (zonefile)
+ free(zonefile);
+
+ return 0;
+}
+
+
+/* Check the status of an isc_result_t after any isc routines.
+ * I should probably rename this function, as not to cause any
+ * confusion with the isc* routines. Will exit on error. */
+void
+isc_result_check (isc_result_t res, char *errorstr)
+{
+ if (res != ISC_R_SUCCESS)
+ {
+ fprintf (stderr, " %s: %s\n", errorstr, isc_result_totext (res));
+ exit (-1);
+ }
+}
+
+
+/* Takes DNS information, in bind data structure format, and adds textual
+ * zone information to the LDAP run queue. */
+void
+generate_ldap (dns_name_t * dnsname, dns_rdata_t * rdata, unsigned int ttl)
+{
+ char name[DNS_NAME_MAXTEXT + 1];
+ unsigned int len;
+ char type[20];
+ char data[2048];
+ char **dc_list;
+ char *dn;
+
+ isc_buffer_t buff;
+ isc_result_t result;
+
+ isc_buffer_init (&buff, name, sizeof (name));
+ result = dns_name_totext (dnsname, true, &buff);
+ isc_result_check (result, "dns_name_totext");
+ name[isc_buffer_usedlength (&buff)] = 0;
+
+ isc_buffer_init (&buff, type, sizeof (type));
+ result = dns_rdatatype_totext (rdata->type, &buff);
+ isc_result_check (result, "dns_rdatatype_totext");
+ type[isc_buffer_usedlength (&buff)] = 0;
+
+ isc_buffer_init (&buff, data, sizeof (data));
+ result = dns_rdata_totext (rdata, NULL, &buff);
+ isc_result_check (result, "dns_rdata_totext");
+ data[isc_buffer_usedlength (&buff)] = 0;
+
+ dc_list = hostname_to_dn_list (name, argzone, DNS_OBJECT);
+ len = (get_attr_list_size (dc_list) - 2);
+ dn = build_dn_from_dc_list (dc_list, ttl, WI_SPEC);
+
+ if (debug)
+ printf ("Adding %s (%s %s) to run queue list.\n", dn, type, data);
+
+ add_to_rr_list (dn, dc_list[len], type, data, ttl, DNS_OBJECT);
+}
+
+
+/* Locate an item in the Run queue linked list, by DN. Used by functions
+ * which add items to the run queue.
+ */
+ldap_info *
+locate_by_dn (char *dn)
+{
+ ldap_info *tmp;
+ for (tmp = ldap_info_base; tmp != (ldap_info *) NULL; tmp = tmp->next)
+ {
+ if (!strncmp (tmp->dn, dn, strlen (dn)))
+ return tmp;
+ }
+ return (ldap_info *) NULL;
+}
+
+
+
+/* Take textual zone data, and add to the LDAP Run queue. This works like so:
+ * If locate_by_dn does not return, alloc a new ldap_info structure, and then
+ * calloc a LDAPMod array, fill in the default "everyone needs this" information,
+ * including object classes and dc's. If it locate_by_dn does return, then we'll
+ * realloc for more LDAPMod structs, and appened the new data. If an LDAPMod exists
+ * for the parameter we're adding, then we'll realloc the mod_values array, and
+ * add the new value to the existing LDAPMod. Finnaly, it assures linkage exists
+ * within the Run queue linked ilst*/
+
+void
+add_to_rr_list (char *dn, char *name, char *type,
+ char *data, unsigned int ttl, unsigned int flags)
+{
+ int i;
+ int x;
+ ldap_info *tmp;
+ int attrlist;
+ char ldap_type_buffer[128];
+ char charttl[64];
+
+
+ if ((tmp = locate_by_dn (dn)) == NULL)
+ {
+
+ /* There wasn't one already there, so we need to allocate a new one,
+ * and stick it on the list */
+
+ tmp = (ldap_info *) malloc (sizeof (ldap_info));
+ if (tmp == (ldap_info *) NULL)
+ fatal("malloc");
+
+ tmp->dn = strdup (dn);
+ if (tmp->dn == NULL)
+ fatal("strdup");
+
+ tmp->attrs = (LDAPMod **) calloc (sizeof (LDAPMod *), flags);
+ if (tmp->attrs == (LDAPMod **) NULL)
+ fatal("calloc");
+
+ for (i = 0; i < flags; i++)
+ {
+ tmp->attrs[i] = (LDAPMod *) malloc (sizeof (LDAPMod));
+ if (tmp->attrs[i] == (LDAPMod *) NULL)
+ fatal("malloc");
+ }
+ tmp->attrs[0]->mod_op = LDAP_MOD_ADD;
+ tmp->attrs[0]->mod_type = "objectClass";
+
+ if (flags == DNS_OBJECT)
+ tmp->attrs[0]->mod_values = objectClasses;
+ else
+ {
+ tmp->attrs[0]->mod_values = topObjectClasses;
+ tmp->attrs[1] = NULL;
+ tmp->attrcnt = 2;
+ tmp->next = ldap_info_base;
+ ldap_info_base = tmp;
+ return;
+ }
+
+ tmp->attrs[1]->mod_op = LDAP_MOD_ADD;
+ tmp->attrs[1]->mod_type = "relativeDomainName";
+ tmp->attrs[1]->mod_values = (char **) calloc (sizeof (char *), 2);
+
+ if (tmp->attrs[1]->mod_values == (char **)NULL)
+ fatal("calloc");
+
+ tmp->attrs[1]->mod_values[0] = strdup (name);
+ tmp->attrs[1]->mod_values[2] = NULL;
+
+ if (tmp->attrs[1]->mod_values[0] == NULL)
+ fatal("strdup");
+
+ sprintf (ldap_type_buffer, "%sRecord", type);
+
+ tmp->attrs[2]->mod_op = LDAP_MOD_ADD;
+ tmp->attrs[2]->mod_type = strdup (ldap_type_buffer);
+ tmp->attrs[2]->mod_values = (char **) calloc (sizeof (char *), 2);
+
+ if (tmp->attrs[2]->mod_type == NULL ||
+ tmp->attrs[2]->mod_values == (char **)NULL)
+ fatal("strdup/calloc");
+
+ tmp->attrs[2]->mod_values[0] = strdup (data);
+ tmp->attrs[2]->mod_values[1] = NULL;
+
+ if (tmp->attrs[2]->mod_values[0] == NULL)
+ fatal("strdup");
+
+ tmp->attrs[3]->mod_op = LDAP_MOD_ADD;
+ tmp->attrs[3]->mod_type = "dNSTTL";
+ tmp->attrs[3]->mod_values = (char **) calloc (sizeof (char *), 2);
+
+ if (tmp->attrs[3]->mod_values == (char **)NULL)
+ fatal("calloc");
+
+ sprintf (charttl, "%d", ttl);
+ tmp->attrs[3]->mod_values[0] = strdup (charttl);
+ tmp->attrs[3]->mod_values[1] = NULL;
+
+ if (tmp->attrs[3]->mod_values[0] == NULL)
+ fatal("strdup");
+
+ tmp->attrs[4]->mod_op = LDAP_MOD_ADD;
+ tmp->attrs[4]->mod_type = "zoneName";
+ tmp->attrs[4]->mod_values = (char **)calloc(sizeof(char *), 2);
+
+ if (tmp->attrs[4]->mod_values == (char **)NULL)
+ fatal("calloc");
+
+ tmp->attrs[4]->mod_values[0] = gbl_zone;
+ tmp->attrs[4]->mod_values[1] = NULL;
+
+ tmp->attrs[5] = NULL;
+ tmp->attrcnt = flags;
+ tmp->next = ldap_info_base;
+ ldap_info_base = tmp;
+ }
+ else
+ {
+
+ for (i = 0; tmp->attrs[i] != NULL; i++)
+ {
+ sprintf (ldap_type_buffer, "%sRecord", type);
+ if (!strncmp
+ (ldap_type_buffer, tmp->attrs[i]->mod_type,
+ strlen (tmp->attrs[i]->mod_type)))
+ {
+ attrlist = get_attr_list_size (tmp->attrs[i]->mod_values);
+ tmp->attrs[i]->mod_values =
+ (char **) realloc (tmp->attrs[i]->mod_values,
+ sizeof (char *) * (attrlist + 1));
+
+ if (tmp->attrs[i]->mod_values == (char **) NULL)
+ fatal("realloc");
+
+ for (x = 0; tmp->attrs[i]->mod_values[x] != NULL; x++);
+
+ tmp->attrs[i]->mod_values[x] = strdup (data);
+ if (tmp->attrs[i]->mod_values[x] == NULL)
+ fatal("strdup");
+ tmp->attrs[i]->mod_values[x + 1] = NULL;
+
+ return;
+ }
+ }
+ tmp->attrs =
+ (LDAPMod **) realloc (tmp->attrs,
+ sizeof (LDAPMod) * ++(tmp->attrcnt));
+ if (tmp->attrs == NULL)
+ fatal("realloc");
+
+ for (x = 0; tmp->attrs[x] != NULL; x++);
+ tmp->attrs[x] = (LDAPMod *) malloc (sizeof (LDAPMod));
+ if (tmp->attrs[x] == NULL)
+ fatal("malloc");
+ tmp->attrs[x]->mod_op = LDAP_MOD_ADD;
+ tmp->attrs[x]->mod_type = strdup (ldap_type_buffer);
+ tmp->attrs[x]->mod_values = (char **) calloc (sizeof (char *), 2);
+
+ if (tmp->attrs[x]->mod_type == NULL ||
+ tmp->attrs[x]->mod_values == (char **)NULL)
+ fatal("strdup/calloc");
+
+ tmp->attrs[x]->mod_values[0] = strdup (data);
+ if (tmp->attrs[x]->mod_values[0] == NULL)
+ fatal("strdup");
+ tmp->attrs[x]->mod_values[1] = NULL;
+ tmp->attrs[x + 1] = NULL;
+ }
+}
+
+/* Size of a mod_values list, plus the terminating NULL field. */
+int
+get_attr_list_size (char **tmp)
+{
+ int i = 0;
+ char **ftmp = tmp;
+ while (*ftmp != NULL)
+ {
+ i++;
+ ftmp++;
+ }
+ return ++i;
+}
+
+
+/* take a hostname, and split it into a char ** of the dc parts,
+ * example, we have www.domain.com, this function will return:
+ * array[0] = com, array[1] = domain, array[2] = www. */
+
+char **
+hostname_to_dn_list (char *hostname, char *zone, unsigned int flags)
+{
+ char *tmp;
+ static char *dn_buffer[64];
+ int i = 0;
+ char *zname;
+ char *hnamebuff;
+
+ zname = strdup (hostname);
+ if (zname == NULL)
+ fatal("strdup");
+
+ if (flags == DNS_OBJECT)
+ {
+
+ if (strlen (zname) != strlen (zone))
+ {
+ tmp = &zname[strlen (zname) - strlen (zone)];
+ *--tmp = '\0';
+ hnamebuff = strdup (zname);
+ if (hnamebuff == NULL)
+ fatal("strdup");
+ zname = ++tmp;
+ }
+ else
+ hnamebuff = "@";
+ }
+ else
+ {
+ zname = zone;
+ hnamebuff = NULL;
+ }
+
+ for (tmp = strrchr (zname, '.'); tmp != (char *) 0;
+ tmp = strrchr (zname, '.'))
+ {
+ *tmp++ = '\0';
+ dn_buffer[i++] = tmp;
+ }
+ dn_buffer[i++] = zname;
+ dn_buffer[i++] = hnamebuff;
+ dn_buffer[i] = NULL;
+
+ return dn_buffer;
+}
+
+
+/* build an sdb compatible LDAP DN from a "dc_list" (char **).
+ * will append dNSTTL information to each RR Record, with the
+ * exception of "@"/SOA. */
+
+char *
+build_dn_from_dc_list (char **dc_list, unsigned int ttl, int flag)
+{
+ int size;
+ int x;
+ static char dn[1024];
+ char tmp[128];
+
+ bzero (tmp, sizeof (tmp));
+ bzero (dn, sizeof (dn));
+ size = get_attr_list_size (dc_list);
+ for (x = size - 2; x > 0; x--)
+ {
+ if (flag == WI_SPEC)
+ {
+ if (x == (size - 2) && (strncmp (dc_list[x], "@", 1) == 0) && (ttl))
+ sprintf (tmp, "relativeDomainName=%s + dNSTTL=%d,", dc_list[x], ttl);
+ else if (x == (size - 2))
+ sprintf(tmp, "relativeDomainName=%s,",dc_list[x]);
+ else
+ sprintf(tmp,"dc=%s,", dc_list[x]);
+ }
+ else
+ {
+ sprintf(tmp, "dc=%s,", dc_list[x]);
+ }
+
+
+ strlcat (dn, tmp, sizeof (dn));
+ }
+
+ sprintf (tmp, "dc=%s", dc_list[0]);
+ strlcat (dn, tmp, sizeof (dn));
+
+ fflush(NULL);
+ return dn;
+}
+
+
+/* Initialize LDAP Conn */
+void
+init_ldap_conn ()
+{
+ int result;
+ conn = ldap_open (ldapsystem, LDAP_PORT);
+ if (conn == NULL)
+ {
+ fprintf (stderr, "Error opening Ldap connection: %s\n",
+ strerror (errno));
+ exit (-1);
+ }
+
+ result = ldap_simple_bind_s (conn, binddn, bindpw);
+ ldap_result_check ("ldap_simple_bind_s", "LDAP Bind", result);
+}
+
+/* Like isc_result_check, only for LDAP */
+void
+ldap_result_check (char *msg, char *dn, int err)
+{
+ if ((err != LDAP_SUCCESS) && (err != LDAP_ALREADY_EXISTS))
+ {
+ fprintf(stderr, "Error while adding %s (%s):\n",
+ dn, msg);
+ ldap_perror (conn, dn);
+ ldap_unbind_s (conn);
+ exit (-1);
+ }
+}
+
+
+
+/* For running the ldap_info run queue. */
+void
+add_ldap_values (ldap_info * ldinfo)
+{
+ int result;
+ char dnbuffer[1024];
+
+
+ if (ldapbase != NULL)
+ sprintf (dnbuffer, "%s,%s", ldinfo->dn, ldapbase);
+ else
+ sprintf (dnbuffer, "%s", ldinfo->dn);
+
+ result = ldap_add_s (conn, dnbuffer, ldinfo->attrs);
+ ldap_result_check ("ldap_add_s", dnbuffer, result);
+}
+
+
+
+
+/* name says it all */
+void
+usage ()
+{
+ fprintf (stderr,
+ "zone2ldap -D [BIND DN] -w [BIND PASSWORD] -b [BASE DN] -z [ZONE] -f [ZONE FILE] -h [LDAP HOST] "
+ "[-c Create LDAP Base structure][-d Debug Output (lots !)] \n ");}
diff --git a/contrib/sdb/pgsql/pgsqldb.c b/contrib/sdb/pgsql/pgsqldb.c
new file mode 100644
index 0000000..2551099
--- /dev/null
+++ b/contrib/sdb/pgsql/pgsqldb.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#include <config.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <pgsql/libpq-fe.h>
+
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/util.h>
+
+#include <dns/sdb.h>
+#include <dns/result.h>
+
+#include <named/globals.h>
+
+#include "pgsqldb.h"
+
+/*
+ * A simple database driver that interfaces to a PostgreSQL database. This
+ * is not complete, and not designed for general use. It opens one
+ * connection to the database per zone, which is inefficient. It also may
+ * not handle quoting correctly.
+ *
+ * The table must contain the fields "name", "rdtype", and "rdata", and
+ * is expected to contain a properly constructed zone. The program "zonetodb"
+ * creates such a table.
+ */
+
+static dns_sdbimplementation_t *pgsqldb = NULL;
+
+struct dbinfo {
+ PGconn *conn;
+ char *database;
+ char *table;
+ char *host;
+ char *user;
+ char *passwd;
+};
+
+static void
+pgsqldb_destroy(const char *zone, void *driverdata, void **dbdata);
+
+/*
+ * Canonicalize a string before writing it to the database.
+ * "dest" must be an array of at least size 2*strlen(source) + 1.
+ */
+static void
+quotestring(const char *source, char *dest) {
+ while (*source != 0) {
+ if (*source == '\'')
+ *dest++ = '\'';
+ /* SQL doesn't treat \ as special, but PostgreSQL does */
+ else if (*source == '\\')
+ *dest++ = '\\';
+ *dest++ = *source++;
+ }
+ *dest++ = 0;
+}
+
+/*
+ * Connect to the database.
+ */
+static isc_result_t
+db_connect(struct dbinfo *dbi) {
+ dbi->conn = PQsetdbLogin(dbi->host, NULL, NULL, NULL, dbi->database,
+ dbi->user, dbi->passwd);
+
+ if (PQstatus(dbi->conn) == CONNECTION_OK)
+ return (ISC_R_SUCCESS);
+ else
+ return (ISC_R_FAILURE);
+}
+
+/*
+ * Check to see if the connection is still valid. If not, attempt to
+ * reconnect.
+ */
+static isc_result_t
+maybe_reconnect(struct dbinfo *dbi) {
+ if (PQstatus(dbi->conn) == CONNECTION_OK)
+ return (ISC_R_SUCCESS);
+
+ return (db_connect(dbi));
+}
+
+/*
+ * This database operates on absolute names.
+ *
+ * Queries are converted into SQL queries and issued synchronously. Errors
+ * are handled really badly.
+ */
+#ifdef DNS_CLIENTINFO_VERSION
+static isc_result_t
+pgsqldb_lookup(const char *zone, const char *name, void *dbdata,
+ dns_sdblookup_t *lookup, dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo)
+#else
+static isc_result_t
+pgsqldb_lookup(const char *zone, const char *name, void *dbdata,
+ dns_sdblookup_t *lookup)
+#endif /* DNS_CLIENTINFO_VERSION */
+{
+ isc_result_t result;
+ struct dbinfo *dbi = dbdata;
+ PGresult *res;
+ char str[1500];
+ char *canonname;
+ int i;
+
+ UNUSED(zone);
+#ifdef DNS_CLIENTINFO_VERSION
+ UNUSED(methods);
+ UNUSED(clientinfo);
+#endif /* DNS_CLIENTINFO_VERSION */
+
+ canonname = isc_mem_get(ns_g_mctx, strlen(name) * 2 + 1);
+ if (canonname == NULL)
+ return (ISC_R_NOMEMORY);
+ quotestring(name, canonname);
+ snprintf(str, sizeof(str),
+ "SELECT TTL,RDTYPE,RDATA FROM \"%s\" WHERE "
+ "lower(NAME) = lower('%s')", dbi->table, canonname);
+ isc_mem_put(ns_g_mctx, canonname, strlen(name) * 2 + 1);
+
+ result = maybe_reconnect(dbi);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ res = PQexec(dbi->conn, str);
+ if (!res || PQresultStatus(res) != PGRES_TUPLES_OK) {
+ PQclear(res);
+ return (ISC_R_FAILURE);
+ }
+ if (PQntuples(res) == 0) {
+ PQclear(res);
+ return (ISC_R_NOTFOUND);
+ }
+
+ for (i = 0; i < PQntuples(res); i++) {
+ char *ttlstr = PQgetvalue(res, i, 0);
+ char *type = PQgetvalue(res, i, 1);
+ char *data = PQgetvalue(res, i, 2);
+ dns_ttl_t ttl;
+ char *endp;
+ ttl = strtol(ttlstr, &endp, 10);
+ if (*endp != '\0') {
+ PQclear(res);
+ return (DNS_R_BADTTL);
+ }
+ result = dns_sdb_putrr(lookup, type, ttl, data);
+ if (result != ISC_R_SUCCESS) {
+ PQclear(res);
+ return (ISC_R_FAILURE);
+ }
+ }
+
+ PQclear(res);
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Issue an SQL query to return all nodes in the database and fill the
+ * allnodes structure.
+ */
+static isc_result_t
+pgsqldb_allnodes(const char *zone, void *dbdata, dns_sdballnodes_t *allnodes) {
+ struct dbinfo *dbi = dbdata;
+ PGresult *res;
+ isc_result_t result;
+ char str[1500];
+ int i;
+
+ UNUSED(zone);
+
+ snprintf(str, sizeof(str),
+ "SELECT TTL,NAME,RDTYPE,RDATA FROM \"%s\" ORDER BY NAME",
+ dbi->table);
+
+ result = maybe_reconnect(dbi);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ res = PQexec(dbi->conn, str);
+ if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ) {
+ PQclear(res);
+ return (ISC_R_FAILURE);
+ }
+ if (PQntuples(res) == 0) {
+ PQclear(res);
+ return (ISC_R_NOTFOUND);
+ }
+
+ for (i = 0; i < PQntuples(res); i++) {
+ char *ttlstr = PQgetvalue(res, i, 0);
+ char *name = PQgetvalue(res, i, 1);
+ char *type = PQgetvalue(res, i, 2);
+ char *data = PQgetvalue(res, i, 3);
+ dns_ttl_t ttl;
+ char *endp;
+ ttl = strtol(ttlstr, &endp, 10);
+ if (*endp != '\0') {
+ PQclear(res);
+ return (DNS_R_BADTTL);
+ }
+ result = dns_sdb_putnamedrr(allnodes, name, type, ttl, data);
+ if (result != ISC_R_SUCCESS) {
+ PQclear(res);
+ return (ISC_R_FAILURE);
+ }
+ }
+
+ PQclear(res);
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Create a connection to the database and save any necessary information
+ * in dbdata.
+ *
+ * argv[0] is the name of the database
+ * argv[1] is the name of the table
+ * argv[2] (if present) is the name of the host to connect to
+ * argv[3] (if present) is the name of the user to connect as
+ * argv[4] (if present) is the name of the password to connect with
+ */
+static isc_result_t
+pgsqldb_create(const char *zone, int argc, char **argv,
+ void *driverdata, void **dbdata)
+{
+ struct dbinfo *dbi;
+ isc_result_t result;
+
+ UNUSED(zone);
+ UNUSED(driverdata);
+
+ if (argc < 2)
+ return (ISC_R_FAILURE);
+
+ dbi = isc_mem_get(ns_g_mctx, sizeof(struct dbinfo));
+ if (dbi == NULL)
+ return (ISC_R_NOMEMORY);
+ dbi->conn = NULL;
+ dbi->database = NULL;
+ dbi->table = NULL;
+ dbi->host = NULL;
+ dbi->user = NULL;
+ dbi->passwd = NULL;
+
+#define STRDUP_OR_FAIL(target, source) \
+ do { \
+ target = isc_mem_strdup(ns_g_mctx, source); \
+ if (target == NULL) { \
+ result = ISC_R_NOMEMORY; \
+ goto cleanup; \
+ } \
+ } while (0);
+
+ STRDUP_OR_FAIL(dbi->database, argv[0]);
+ STRDUP_OR_FAIL(dbi->table, argv[1]);
+ if (argc > 2)
+ STRDUP_OR_FAIL(dbi->host, argv[2]);
+ if (argc > 3)
+ STRDUP_OR_FAIL(dbi->user, argv[3]);
+ if (argc > 4)
+ STRDUP_OR_FAIL(dbi->passwd, argv[4]);
+
+ result = db_connect(dbi);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ *dbdata = dbi;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ pgsqldb_destroy(zone, driverdata, (void **)&dbi);
+ return (result);
+}
+
+/*
+ * Close the connection to the database.
+ */
+static void
+pgsqldb_destroy(const char *zone, void *driverdata, void **dbdata) {
+ struct dbinfo *dbi = *dbdata;
+
+ UNUSED(zone);
+ UNUSED(driverdata);
+
+ if (dbi->conn != NULL)
+ PQfinish(dbi->conn);
+ if (dbi->database != NULL)
+ isc_mem_free(ns_g_mctx, dbi->database);
+ if (dbi->table != NULL)
+ isc_mem_free(ns_g_mctx, dbi->table);
+ if (dbi->host != NULL)
+ isc_mem_free(ns_g_mctx, dbi->host);
+ if (dbi->user != NULL)
+ isc_mem_free(ns_g_mctx, dbi->user);
+ if (dbi->passwd != NULL)
+ isc_mem_free(ns_g_mctx, dbi->passwd);
+ if (dbi->database != NULL)
+ isc_mem_free(ns_g_mctx, dbi->database);
+ isc_mem_put(ns_g_mctx, dbi, sizeof(struct dbinfo));
+}
+
+/*
+ * Since the SQL database corresponds to a zone, the authority data should
+ * be returned by the lookup() function. Therefore the authority() function
+ * is NULL.
+ */
+static dns_sdbmethods_t pgsqldb_methods = {
+ pgsqldb_lookup,
+ NULL, /* authority */
+ pgsqldb_allnodes,
+ pgsqldb_create,
+ pgsqldb_destroy,
+ NULL /* lookup2 */
+};
+
+/*
+ * Wrapper around dns_sdb_register().
+ */
+isc_result_t
+pgsqldb_init(void) {
+ unsigned int flags;
+ flags = 0;
+ return (dns_sdb_register("pgsql", &pgsqldb_methods, NULL, flags,
+ ns_g_mctx, &pgsqldb));
+}
+
+/*
+ * Wrapper around dns_sdb_unregister().
+ */
+void
+pgsqldb_clear(void) {
+ if (pgsqldb != NULL)
+ dns_sdb_unregister(&pgsqldb);
+}
diff --git a/contrib/sdb/pgsql/pgsqldb.h b/contrib/sdb/pgsql/pgsqldb.h
new file mode 100644
index 0000000..cd60f14
--- /dev/null
+++ b/contrib/sdb/pgsql/pgsqldb.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#include <isc/types.h>
+
+isc_result_t pgsqldb_init(void);
+
+void pgsqldb_clear(void);
+
diff --git a/contrib/sdb/pgsql/zonetodb.c b/contrib/sdb/pgsql/zonetodb.c
new file mode 100644
index 0000000..df7e939
--- /dev/null
+++ b/contrib/sdb/pgsql/zonetodb.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/buffer.h>
+#include <isc/entropy.h>
+#include <isc/hash.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/result.h>
+
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/rdatatype.h>
+#include <dns/result.h>
+
+#include <pgsql/libpq-fe.h>
+
+/*
+ * Generate a PostgreSQL table from a zone.
+ *
+ * This is compiled this with something like the following (assuming bind9 has
+ * been installed):
+ *
+ * gcc -g `isc-config.sh --cflags isc dns` -c zonetodb.c
+ * gcc -g -o zonetodb zonetodb.o `isc-config.sh --libs isc dns` -lpq
+ */
+
+PGconn *conn = NULL;
+char *dbname, *dbtable;
+char str[10240];
+
+void
+closeandexit(int status) {
+ if (conn != NULL)
+ PQfinish(conn);
+ exit(status);
+}
+
+void
+check_result(isc_result_t result, const char *message) {
+ if (result != ISC_R_SUCCESS) {
+ fprintf(stderr, "%s: %s\n", message,
+ isc_result_totext(result));
+ closeandexit(1);
+ }
+}
+
+/*
+ * Canonicalize a string before writing it to the database.
+ * "dest" must be an array of at least size 2*strlen(source) + 1.
+ */
+static void
+quotestring(const unsigned char *source, unsigned char *dest) {
+ while (*source != 0) {
+ if (*source == '\'')
+ *dest++ = '\'';
+ else if (*source == '\\')
+ *dest++ = '\\';
+ *dest++ = *source++;
+ }
+ *dest++ = 0;
+}
+
+void
+addrdata(dns_name_t *name, dns_ttl_t ttl, dns_rdata_t *rdata) {
+ unsigned char namearray[DNS_NAME_MAXTEXT + 1];
+ unsigned char canonnamearray[2 * DNS_NAME_MAXTEXT + 1];
+ unsigned char typearray[20];
+ unsigned char canontypearray[40];
+ unsigned char dataarray[2048];
+ unsigned char canondataarray[4096];
+ isc_buffer_t b;
+ isc_result_t result;
+ PGresult *res;
+
+ isc_buffer_init(&b, namearray, sizeof(namearray) - 1);
+ result = dns_name_totext(name, true, &b);
+ check_result(result, "dns_name_totext");
+ namearray[isc_buffer_usedlength(&b)] = 0;
+ quotestring((const unsigned char *)namearray, canonnamearray);
+
+ isc_buffer_init(&b, typearray, sizeof(typearray) - 1);
+ result = dns_rdatatype_totext(rdata->type, &b);
+ check_result(result, "dns_rdatatype_totext");
+ typearray[isc_buffer_usedlength(&b)] = 0;
+ quotestring((const unsigned char *)typearray, canontypearray);
+
+ isc_buffer_init(&b, dataarray, sizeof(dataarray) - 1);
+ result = dns_rdata_totext(rdata, NULL, &b);
+ check_result(result, "dns_rdata_totext");
+ dataarray[isc_buffer_usedlength(&b)] = 0;
+ quotestring((const unsigned char *)dataarray, canondataarray);
+
+ snprintf(str, sizeof(str),
+ "INSERT INTO %s (NAME, TTL, RDTYPE, RDATA)"
+ " VALUES ('%s', %d, '%s', '%s')",
+ dbtable, canonnamearray, ttl, canontypearray, canondataarray);
+ printf("%s\n", str);
+ res = PQexec(conn, str);
+ if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) {
+ fprintf(stderr, "INSERT INTO command failed: %s\n",
+ PQresultErrorMessage(res));
+ PQclear(res);
+ closeandexit(1);
+ }
+ PQclear(res);
+}
+
+int
+main(int argc, char **argv) {
+ char *porigin, *zonefile;
+ dns_fixedname_t forigin, fname;
+ dns_name_t *origin, *name;
+ dns_db_t *db = NULL;
+ dns_dbiterator_t *dbiter;
+ dns_dbnode_t *node;
+ dns_rdatasetiter_t *rdsiter;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_mem_t *mctx = NULL;
+ isc_entropy_t *ectx = NULL;
+ isc_buffer_t b;
+ isc_result_t result;
+ PGresult *res;
+
+ if (argc != 5) {
+ printf("usage: %s origin file dbname dbtable\n", argv[0]);
+ printf("Note that dbname must be an existing database.\n");
+ exit(1);
+ }
+
+ porigin = argv[1];
+ zonefile = argv[2];
+ dbname = argv[3];
+ dbtable = argv[4];
+
+ dns_result_register();
+
+ mctx = NULL;
+ result = isc_mem_create(0, 0, &mctx);
+ check_result(result, "isc_mem_create");
+
+ result = isc_entropy_create(mctx, &ectx);
+ check_result(result, "isc_entropy_create");
+
+ result = isc_hash_create(mctx, ectx, DNS_NAME_MAXWIRE);
+ check_result(result, "isc_hash_create");
+
+ isc_buffer_init(&b, porigin, strlen(porigin));
+ isc_buffer_add(&b, strlen(porigin));
+ origin = dns_fixedname_initname(&forigin);
+ result = dns_name_fromtext(origin, &b, dns_rootname, 0, NULL);
+ check_result(result, "dns_name_fromtext");
+
+ db = NULL;
+ result = dns_db_create(mctx, "rbt", origin, dns_dbtype_zone,
+ dns_rdataclass_in, 0, NULL, &db);
+ check_result(result, "dns_db_create");
+
+ result = dns_db_load(db, zonefile);
+ if (result == DNS_R_SEENINCLUDE)
+ result = ISC_R_SUCCESS;
+ check_result(result, "dns_db_load");
+
+ printf("Connecting to '%s'\n", dbname);
+ conn = PQsetdb(NULL, NULL, NULL, NULL, dbname);
+ if (PQstatus(conn) == CONNECTION_BAD) {
+ fprintf(stderr, "Connection to database '%s' failed: %s\n",
+ dbname, PQerrorMessage(conn));
+ closeandexit(1);
+ }
+
+ snprintf(str, sizeof(str),
+ "DROP TABLE %s", dbtable);
+ printf("%s\n", str);
+ res = PQexec(conn, str);
+ if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
+ fprintf(stderr, "DROP TABLE command failed: %s\n",
+ PQresultErrorMessage(res));
+ PQclear(res);
+
+ snprintf(str, sizeof(str), "BEGIN");
+ printf("%s\n", str);
+ res = PQexec(conn, str);
+ if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) {
+ fprintf(stderr, "BEGIN command failed: %s\n",
+ PQresultErrorMessage(res));
+ PQclear(res);
+ closeandexit(1);
+ }
+ PQclear(res);
+
+ snprintf(str, sizeof(str),
+ "CREATE TABLE %s "
+ "(NAME TEXT, TTL INTEGER, RDTYPE TEXT, RDATA TEXT)",
+ dbtable);
+ printf("%s\n", str);
+ res = PQexec(conn, str);
+ if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) {
+ fprintf(stderr, "CREATE TABLE command failed: %s\n",
+ PQresultErrorMessage(res));
+ PQclear(res);
+ closeandexit(1);
+ }
+ PQclear(res);
+
+ dbiter = NULL;
+ result = dns_db_createiterator(db, 0, &dbiter);
+ check_result(result, "dns_db_createiterator()");
+
+ result = dns_dbiterator_first(dbiter);
+ check_result(result, "dns_dbiterator_first");
+
+ name = dns_fixedname_initname(&fname);
+ dns_rdataset_init(&rdataset);
+ dns_rdata_init(&rdata);
+
+ while (result == ISC_R_SUCCESS) {
+ node = NULL;
+ result = dns_dbiterator_current(dbiter, &node, name);
+ if (result == ISC_R_NOMORE)
+ break;
+ check_result(result, "dns_dbiterator_current");
+
+ rdsiter = NULL;
+ result = dns_db_allrdatasets(db, node, NULL, 0, &rdsiter);
+ check_result(result, "dns_db_allrdatasets");
+
+ result = dns_rdatasetiter_first(rdsiter);
+
+ while (result == ISC_R_SUCCESS) {
+ dns_rdatasetiter_current(rdsiter, &rdataset);
+ result = dns_rdataset_first(&rdataset);
+ check_result(result, "dns_rdataset_first");
+ while (result == ISC_R_SUCCESS) {
+ dns_rdataset_current(&rdataset, &rdata);
+ addrdata(name, rdataset.ttl, &rdata);
+ dns_rdata_reset(&rdata);
+ result = dns_rdataset_next(&rdataset);
+ }
+ dns_rdataset_disassociate(&rdataset);
+ result = dns_rdatasetiter_next(rdsiter);
+ }
+ dns_rdatasetiter_destroy(&rdsiter);
+ dns_db_detachnode(db, &node);
+ result = dns_dbiterator_next(dbiter);
+ }
+
+ snprintf(str, sizeof(str), "COMMIT TRANSACTION");
+ printf("%s\n", str);
+ res = PQexec(conn, str);
+ if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) {
+ fprintf(stderr, "COMMIT command failed: %s\n",
+ PQresultErrorMessage(res));
+ PQclear(res);
+ closeandexit(1);
+ }
+ PQclear(res);
+ dns_dbiterator_destroy(&dbiter);
+ dns_db_detach(&db);
+ isc_hash_destroy();
+ isc_entropy_detach(&ectx);
+ isc_mem_destroy(&mctx);
+ closeandexit(0);
+ exit(0);
+}
diff --git a/contrib/sdb/sqlite/README.sdb_sqlite b/contrib/sdb/sqlite/README.sdb_sqlite
new file mode 100644
index 0000000..36128e1
--- /dev/null
+++ b/contrib/sdb/sqlite/README.sdb_sqlite
@@ -0,0 +1,67 @@
+ SQLite BIND SDB driver
+
+The SQLite BIND SDB "driver" is intended as an alternative both to the
+pgsqldb and dirdb drivers, for situations that would like the management
+simplicity and convenience of single filesystem files, with the additional
+capability of SQL databases. It is also intended as an alternative to
+the standard dynamic DNS update capability in bind, which effectively
+requires use of DNSSEC keys for authorization and is limited to 'nsupdate'
+for updates. An sqlite database, by contrast, uses and requires only
+normal filesystem permissions, and may be updated however a typical SQLite
+database might be updated, e.g., via a web service with an SQLite backend.
+
+This driver is not considered suitable for very high volume public
+nameserver use, while likely useful for smaller private nameserver
+applications, whether or not in a production environment. It should
+generally be suitable wherever SQLite is preferable over larger database
+engines, and not suitable where SQLite is not preferable.
+
+Usage:
+
+o Use the named_sdb process ( put ENABLE_SDB=yes in /etc/sysconfig/named )
+
+o Edit your named.conf to contain a database zone, eg.:
+
+zone "mydomain.net." IN {
+ type master;
+ database "sqlite /etc/named.d/mydomain.db mydomain";
+ # ^- DB file ^-Table
+};
+
+o Create the database zone table
+ The table must contain the columns "name", "rdtype", and "rdata", and
+ is expected to contain a properly constructed zone. The program
+ "zone2sqlite" creates such a table.
+
+ zone2sqlite usage:
+
+ zone2sqlite origin zonefile dbfile dbtable
+
+ where
+ origin : zone origin, eg "mydomain.net."
+ zonefile : master zone database file, eg. mydomain.net.zone
+ dbfile : name of SQLite database file
+ dbtable : name of table in database
+
+---
+# mydomain.net.zone:
+$TTL 1H
+@ SOA localhost. root.localhost. ( 1
+ 3H
+ 1H
+ 1W
+ 1H )
+ NS localhost.
+host1 A 192.168.2.1
+host2 A 192.168.2.2
+host3 A 192.168.2.3
+host4 A 192.168.2.4
+host5 A 192.168.2.5
+host6 A 192.168.2.6
+host7 A 192.168.2.7
+---
+
+# zone2sqlite mydomain.net. mydomain.net.zone mydomain.net.db mydomain
+
+will create/update the 'mydomain' table in database file 'mydomain.net.db'.
+
diff --git a/contrib/sdb/sqlite/sqlitedb.c b/contrib/sdb/sqlite/sqlitedb.c
new file mode 100644
index 0000000..dc93db4
--- /dev/null
+++ b/contrib/sdb/sqlite/sqlitedb.c
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2007, 2016 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+
+#include <config.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <sqlite3.h>
+
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/util.h>
+
+#include <dns/sdb.h>
+#include <dns/result.h>
+
+#include <named/globals.h>
+
+#include "sqlitedb.h"
+
+/*
+ * A simple database driver that interfaces to a SQLite database.
+ *
+ * The table must contain the fields "name", "rdtype", and "rdata", and
+ * is expected to contain a properly constructed zone. The program "zonetodb"
+ * creates such a table.
+ */
+
+static dns_sdbimplementation_t *sqlitedb = NULL;
+
+typedef struct _dbinfo {
+ sqlite3 *db;
+ char *filename;
+ char *table;
+} dbinfo_t;
+
+
+static isc_result_t
+db_connect(dbinfo_t *dbi)
+{
+ if (sqlite3_open(dbi->filename, &dbi->db) == SQLITE_OK) {
+ return (ISC_R_SUCCESS);
+ } else {
+ /* a connection is returned even if the open fails */
+ sqlite3_close(dbi->db);
+ dbi->db = NULL;
+ return (ISC_R_FAILURE);
+ }
+}
+
+
+typedef struct _lookup_parm_t {
+ int i;
+ dns_sdblookup_t *lookup;
+ isc_result_t result;
+} lookup_parm_t;
+
+
+static int
+sqlitedb_lookup_cb(void *p, int cc, char **cv, char **cn)
+{
+ lookup_parm_t *parm = p;
+ dns_ttl_t ttl;
+ char *endp;
+
+ /* FIXME - check these(num/names); I'm assuming a mapping for now */
+ char *ttlstr = cv[0];
+ char *type = cv[1];
+ char *data = cv[2];
+
+ UNUSED(cc);
+ UNUSED(cn);
+
+ ttl = strtol(ttlstr, &endp, 10);
+ if (*endp) {
+ parm->result = DNS_R_BADTTL;
+ return 1;
+ }
+
+ parm->result = dns_sdb_putrr(parm->lookup, type, ttl, data);
+
+ if (parm->result != ISC_R_SUCCESS)
+ return 1;
+
+ (parm->i)++;
+
+ return 0;
+}
+
+
+#ifdef DNS_CLIENTINFO_VERSION
+static isc_result_t
+sqlitedb_lookup(const char *zone, const char *name, void *dbdata,
+ dns_sdblookup_t *lookup, dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo)
+#else
+static isc_result_t
+sqlitedb_lookup(const char *zone, const char *name, void *dbdata,
+ dns_sdblookup_t *lookup)
+#endif /* DNS_CLIENTINFO_VERSION */
+/*
+ * synchronous absolute name lookup
+ */
+{
+ dbinfo_t *dbi = (dbinfo_t *) dbdata;
+ char *sql;
+ lookup_parm_t parm = { 0, lookup, ISC_R_SUCCESS };
+ char *errmsg = NULL;
+ int result;
+
+ UNUSED(zone);
+#ifdef DNS_CLIENTINFO_VERSION
+ UNUSED(methods);
+ UNUSED(clientinfo);
+#endif /* DNS_CLIENTINFO_VERSION */
+
+ sql = sqlite3_mprintf(
+ "SELECT TTL,RDTYPE,RDATA FROM \"%q\" WHERE "
+ "lower(NAME) = lower('%q')",
+ dbi->table, name);
+
+ result = sqlite3_exec(dbi->db, sql,
+ &sqlitedb_lookup_cb, &parm,
+ &errmsg);
+ sqlite3_free(sql);
+
+ if (result != SQLITE_OK)
+ return (ISC_R_FAILURE);
+ if (parm.i == 0)
+ return (ISC_R_NOTFOUND);
+
+ return (ISC_R_SUCCESS);
+}
+
+
+typedef struct _allnodes_parm_t {
+ int i;
+ dns_sdballnodes_t *allnodes;
+ isc_result_t result;
+} allnodes_parm_t;
+
+
+static int
+sqlitedb_allnodes_cb(void *p, int cc, char **cv, char **cn)
+{
+ allnodes_parm_t *parm = p;
+ dns_ttl_t ttl;
+ char *endp;
+
+ /* FIXME - check these(num/names); I'm assuming a mapping for now */
+ char *ttlstr = cv[0];
+ char *name = cv[1];
+ char *type = cv[2];
+ char *data = cv[3];
+
+ UNUSED(cc);
+ UNUSED(cn);
+
+ ttl = strtol(ttlstr, &endp, 10);
+ if (*endp) {
+ parm->result = DNS_R_BADTTL;
+ return 1;
+ }
+
+ parm->result = dns_sdb_putnamedrr(parm->allnodes, name, type, ttl, data);
+
+ if (parm->result != ISC_R_SUCCESS)
+ return 1;
+
+ (parm->i)++;
+
+ return 0;
+}
+
+
+static isc_result_t
+sqlitedb_allnodes(const char *zone,
+ void *dbdata,
+ dns_sdballnodes_t *allnodes)
+{
+ dbinfo_t *dbi = (dbinfo_t *) dbdata;
+ char *sql;
+ allnodes_parm_t parm = { 0, allnodes, ISC_R_SUCCESS };
+ char *errmsg = NULL;
+ int result;
+
+ UNUSED(zone);
+
+ sql = sqlite3_mprintf(
+ "SELECT TTL,NAME,RDTYPE,RDATA FROM \"%q\" ORDER BY NAME",
+ dbi->table);
+
+ result = sqlite3_exec(dbi->db, sql,
+ &sqlitedb_allnodes_cb, &parm,
+ &errmsg);
+ sqlite3_free(sql);
+
+ if (result != SQLITE_OK)
+ return (ISC_R_FAILURE);
+ if (parm.i == 0)
+ return (ISC_R_NOTFOUND);
+
+ return (ISC_R_SUCCESS);
+}
+
+
+static void
+sqlitedb_destroy(const char *zone, void *driverdata, void **dbdata)
+{
+ dbinfo_t *dbi = *dbdata;
+
+ UNUSED(zone);
+ UNUSED(driverdata);
+
+ if (dbi->db != NULL)
+ sqlite3_close(dbi->db);
+ if (dbi->table != NULL)
+ isc_mem_free(ns_g_mctx, dbi->table);
+ if (dbi->filename != NULL)
+ isc_mem_free(ns_g_mctx, dbi->filename);
+
+ isc_mem_put(ns_g_mctx, dbi, sizeof(dbinfo_t));
+}
+
+
+#define STRDUP_OR_FAIL(target, source) \
+ do { \
+ target = isc_mem_strdup(ns_g_mctx, source); \
+ if (target == NULL) { \
+ result = ISC_R_NOMEMORY; \
+ goto cleanup; \
+ } \
+ } while (0);
+
+/*
+ * Create a connection to the database and save any necessary information
+ * in dbdata.
+ *
+ * argv[0] is the name of the database file
+ * argv[1] is the name of the table
+ */
+static isc_result_t
+sqlitedb_create(const char *zone,
+ int argc, char **argv,
+ void *driverdata, void **dbdata)
+{
+ dbinfo_t *dbi;
+ isc_result_t result;
+
+ UNUSED(zone);
+ UNUSED(driverdata);
+
+ if (argc < 2)
+ return (ISC_R_FAILURE);
+
+ dbi = isc_mem_get(ns_g_mctx, sizeof(dbinfo_t));
+ if (dbi == NULL)
+ return (ISC_R_NOMEMORY);
+ dbi->db = NULL;
+ dbi->filename = NULL;
+ dbi->table = NULL;
+
+ STRDUP_OR_FAIL(dbi->filename, argv[0]);
+ STRDUP_OR_FAIL(dbi->table, argv[1]);
+
+ result = db_connect(dbi);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ *dbdata = dbi;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ sqlitedb_destroy(zone, driverdata, (void **)&dbi);
+ return (result);
+}
+
+
+/*
+ * Since the SQL database corresponds to a zone, the authority data should
+ * be returned by the lookup() function. Therefore the authority() function
+ * is NULL.
+ */
+static dns_sdbmethods_t sqlitedb_methods = {
+ sqlitedb_lookup,
+ NULL, /* authority */
+ sqlitedb_allnodes,
+ sqlitedb_create,
+ sqlitedb_destroy,
+ NULL /* lookup2 */
+};
+
+
+/*
+ * Wrapper around dns_sdb_register().
+ */
+isc_result_t
+sqlitedb_init(void)
+{
+ unsigned int flags;
+ flags = 0;
+ return (dns_sdb_register("sqlite", &sqlitedb_methods, NULL, flags,
+ ns_g_mctx, &sqlitedb));
+}
+
+
+/*
+ * Wrapper around dns_sdb_unregister().
+ */
+void
+sqlitedb_clear(void)
+{
+ if (sqlitedb != NULL)
+ dns_sdb_unregister(&sqlitedb);
+}
diff --git a/contrib/sdb/sqlite/sqlitedb.h b/contrib/sdb/sqlite/sqlitedb.h
new file mode 100644
index 0000000..34f63dc
--- /dev/null
+++ b/contrib/sdb/sqlite/sqlitedb.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2000-2002, 2016 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+
+#include <isc/types.h>
+
+isc_result_t sqlitedb_init(void);
+
+void sqlitedb_clear(void);
+
diff --git a/contrib/sdb/sqlite/zone2sqlite.c b/contrib/sdb/sqlite/zone2sqlite.c
new file mode 100644
index 0000000..86d577e
--- /dev/null
+++ b/contrib/sdb/sqlite/zone2sqlite.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2007, 2016 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/result.h>
+
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <isc/entropy.h>
+#include <dns/fixedname.h>
+#include <isc/hash.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/rdatatype.h>
+#include <dns/result.h>
+
+#include <sqlite3.h>
+
+#ifndef UNUSED
+#define UNUSED(x) (x) = (x)
+#endif
+
+/*
+ * Generate an SQLite table from a zone.
+ */
+
+typedef struct _dbinfo {
+ sqlite3 *db;
+ char *filename;
+ char *table;
+} dbinfo_t;
+
+dbinfo_t dbi = { NULL, NULL, NULL };
+
+
+static void
+closeandexit(int status)
+{
+ if (dbi.db) {
+ sqlite3_close(dbi.db);
+ dbi.db = NULL;
+ }
+ exit(status);
+}
+
+static void
+check_result(isc_result_t result, const char *message)
+{
+ if (result != ISC_R_SUCCESS) {
+ fprintf(stderr, "%s: %s\n", message,
+ isc_result_totext(result));
+ closeandexit(1);
+ }
+}
+
+static isc_result_t
+db_connect(dbinfo_t *dbi)
+{
+ if (sqlite3_open(dbi->filename, &dbi->db) == SQLITE_OK) {
+ return (ISC_R_SUCCESS);
+ } else {
+ /* a connection is returned even if the open fails */
+ sqlite3_close(dbi->db);
+ dbi->db = NULL;
+ return (ISC_R_FAILURE);
+ }
+}
+
+static int
+add_rdata_cb(void *parm, int cc, char **cv, char **cn)
+{
+ UNUSED(parm);
+ UNUSED(cc);
+ UNUSED(cv);
+ UNUSED(cn);
+
+ return 0;
+}
+
+
+static void
+addrdata(dns_name_t *name, dns_ttl_t ttl, dns_rdata_t *rdata)
+{
+ unsigned char namearray[DNS_NAME_MAXTEXT + 1];
+ unsigned char typearray[20];
+ unsigned char dataarray[2048];
+ isc_buffer_t b;
+ isc_result_t result;
+ char *sql;
+ char *errmsg = NULL;
+ int res;
+
+ isc_buffer_init(&b, namearray, sizeof(namearray) - 1);
+ result = dns_name_totext(name, true, &b);
+ check_result(result, "dns_name_totext");
+ namearray[isc_buffer_usedlength(&b)] = 0;
+
+ isc_buffer_init(&b, typearray, sizeof(typearray) - 1);
+ result = dns_rdatatype_totext(rdata->type, &b);
+ check_result(result, "dns_rdatatype_totext");
+ typearray[isc_buffer_usedlength(&b)] = 0;
+
+ isc_buffer_init(&b, dataarray, sizeof(dataarray) - 1);
+ result = dns_rdata_totext(rdata, NULL, &b);
+ check_result(result, "dns_rdata_totext");
+ dataarray[isc_buffer_usedlength(&b)] = 0;
+
+ sql = sqlite3_mprintf(
+ "INSERT INTO %Q (NAME, TTL, RDTYPE, RDATA)"
+ " VALUES ('%q', %d, '%q', '%q') ",
+ dbi.table,
+ namearray, ttl, typearray, dataarray);
+ printf("%s\n", sql);
+ res = sqlite3_exec(dbi.db, sql, add_rdata_cb, NULL, &errmsg);
+ sqlite3_free(sql);
+
+ if (res != SQLITE_OK) {
+ fprintf(stderr, "INSERT failed: %s\n", errmsg);
+ closeandexit(1);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *sql;
+ int res;
+ char *errmsg = NULL;
+ char *porigin, *zonefile;
+ dns_fixedname_t forigin, fname;
+ dns_name_t *origin, *name;
+ dns_db_t *db = NULL;
+ dns_dbiterator_t *dbiter;
+ dns_dbnode_t *node;
+ dns_rdatasetiter_t *rdsiter;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_mem_t *mctx = NULL;
+ isc_entropy_t *ectx = NULL;
+ isc_buffer_t b;
+ isc_result_t result;
+
+ if (argc != 5) {
+ printf("usage: %s <zone> <zonefile> <dbfile> <dbtable>\n", argv[0]);
+ exit(1);
+ }
+
+ porigin = argv[1];
+ zonefile = argv[2];
+
+ dbi.filename = argv[3];
+ dbi.table = argv[4];
+
+ dns_result_register();
+
+ result = isc_mem_create(0, 0, &mctx);
+ check_result(result, "isc_mem_create");
+ result = isc_entropy_create(mctx, &ectx);
+ check_result(result, "isc_entropy_create");
+ result = isc_hash_create(mctx, ectx, DNS_NAME_MAXWIRE);
+ check_result(result, "isc_hash_create");
+
+ isc_buffer_init(&b, porigin, strlen(porigin));
+ isc_buffer_add(&b, strlen(porigin));
+ origin = dns_fixedname_initname(&forigin);
+ result = dns_name_fromtext(origin, &b, dns_rootname, 0, NULL);
+ check_result(result, "dns_name_fromtext");
+
+ db = NULL;
+ result = dns_db_create(mctx, "rbt", origin, dns_dbtype_zone,
+ dns_rdataclass_in, 0, NULL, &db);
+ check_result(result, "dns_db_create");
+
+ result = dns_db_load(db, zonefile);
+ if (result == DNS_R_SEENINCLUDE)
+ result = ISC_R_SUCCESS;
+ check_result(result, "dns_db_load");
+
+ printf("Connecting to '%s'\n", dbi.filename);
+
+ if ((result = db_connect(&dbi)) != ISC_R_SUCCESS) {
+ fprintf(stderr, "Connection to database '%s' failed\n",
+ dbi.filename);
+ closeandexit(1);
+ }
+
+ sql = sqlite3_mprintf("DROP TABLE %Q ", dbi.table);
+ printf("%s\n", sql);
+ res = sqlite3_exec(dbi.db, sql, NULL, NULL, &errmsg);
+ sqlite3_free(sql);
+#if 0
+ if (res != SQLITE_OK) {
+ fprintf(stderr, "DROP TABLE %s failed: %s\n",
+ dbi.table, errmsg);
+ }
+#endif
+
+#if 0
+ sql = sqlite3_mprintf(sql, "BEGIN TRANSACTION");
+ printf("%s\n", sql);
+ res = sqlite3_exec(dbi.db, sql, NULL, NULL, &errmsg);
+ sqlite3_free(sql);
+ if (res != SQLITE_OK) {
+ fprintf(stderr, "BEGIN TRANSACTION failed: %s\n", errmsg);
+ closeandexit(1);
+ }
+#endif
+
+ sql = sqlite3_mprintf(
+ "CREATE TABLE %Q "
+ "(NAME TEXT, TTL INTEGER, RDTYPE TEXT, RDATA TEXT) ",
+ dbi.table);
+ printf("%s\n", sql);
+ res = sqlite3_exec(dbi.db, sql, NULL, NULL, &errmsg);
+ sqlite3_free(sql);
+ if (res != SQLITE_OK) {
+ fprintf(stderr, "CREATE TABLE %s failed: %s\n",
+ dbi.table, errmsg);
+ closeandexit(1);
+ }
+
+ dbiter = NULL;
+ result = dns_db_createiterator(db, 0, &dbiter);
+ check_result(result, "dns_db_createiterator()");
+
+ result = dns_dbiterator_first(dbiter);
+ check_result(result, "dns_dbiterator_first");
+
+ name = dns_fixedname_initname(&fname);
+ dns_rdataset_init(&rdataset);
+ dns_rdata_init(&rdata);
+
+ while (result == ISC_R_SUCCESS) {
+ node = NULL;
+ result = dns_dbiterator_current(dbiter, &node, name);
+ if (result == ISC_R_NOMORE)
+ break;
+ check_result(result, "dns_dbiterator_current");
+
+ rdsiter = NULL;
+ result = dns_db_allrdatasets(db, node, NULL, 0, &rdsiter);
+ check_result(result, "dns_db_allrdatasets");
+
+ result = dns_rdatasetiter_first(rdsiter);
+
+ while (result == ISC_R_SUCCESS) {
+ dns_rdatasetiter_current(rdsiter, &rdataset);
+ result = dns_rdataset_first(&rdataset);
+ check_result(result, "dns_rdataset_first");
+ while (result == ISC_R_SUCCESS) {
+ dns_rdataset_current(&rdataset, &rdata);
+ addrdata(name, rdataset.ttl, &rdata);
+ dns_rdata_reset(&rdata);
+ result = dns_rdataset_next(&rdataset);
+ }
+ dns_rdataset_disassociate(&rdataset);
+ result = dns_rdatasetiter_next(rdsiter);
+ }
+ dns_rdatasetiter_destroy(&rdsiter);
+ dns_db_detachnode(db, &node);
+ result = dns_dbiterator_next(dbiter);
+ }
+
+#if 0
+ sql = sqlite3_mprintf(sql, "COMMIT TRANSACTION ");
+ printf("%s\n", sql);
+ res = sqlite3_exec(dbi.db, sql, NULL, NULL, &errmsg);
+ sqlite3_free(sql);
+ if (res != SQLITE_OK) {
+ fprintf(stderr, "COMMIT TRANSACTION failed: %s\n", errmsg);
+ closeandexit(1);
+ }
+#endif
+
+ dns_dbiterator_destroy(&dbiter);
+ dns_db_detach(&db);
+ isc_hash_destroy();
+ isc_entropy_detach(&ectx);
+ isc_mem_destroy(&mctx);
+
+ closeandexit(0);
+
+ exit(0);
+}
diff --git a/contrib/sdb/tcl/lookup.tcl b/contrib/sdb/tcl/lookup.tcl
new file mode 100644
index 0000000..7672ed1
--- /dev/null
+++ b/contrib/sdb/tcl/lookup.tcl
@@ -0,0 +1,43 @@
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+#
+# Sample lookup procedure for tcldb
+#
+# This lookup procedure defines zones with identical SOA, NS, and MX
+# records at the apex and a single A record that varies from zone to
+# zone at the name "www".
+#
+# Something like this could be used by a web hosting company to serve
+# a number of domains without needing to create a separate master file
+# for each domain. Instead, all per-zone data (in this case, a single
+# IP address) specified in the named.conf file like this:
+#
+# zone "a.com." { type master; database "tcl 10.0.0.42"; };
+# zone "b.com." { type master; database "tcl 10.0.0.99"; };
+#
+# Since the tcldb driver doesn't support zone transfers, there should
+# be at least two identically configured master servers. In the
+# example below, they are assumed to be called ns1.isp.nil and
+# ns2.isp.nil.
+#
+
+proc lookup {zone name} {
+ global dbargs
+ switch -- $name {
+ @ { return [list \
+ {SOA 86400 "ns1.isp.nil. hostmaster.isp.nil. \
+ 1 3600 1800 1814400 3600"} \
+ {NS 86400 "ns1.isp.nil."} \
+ {NS 86400 "ns2.isp.nil."} \
+ {MX 86400 "10 mail.isp.nil."} ] }
+ www { return [list [list A 3600 $dbargs($zone)] ] }
+ }
+ return NXDOMAIN
+}
diff --git a/contrib/sdb/tcl/tcldb.c b/contrib/sdb/tcl/tcldb.c
new file mode 100644
index 0000000..61be7df
--- /dev/null
+++ b/contrib/sdb/tcl/tcldb.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*
+ * A simple database driver that calls a Tcl procedure to define
+ * the contents of the DNS namespace. The procedure is loaded
+ * from the file lookup.tcl; look at the comments there for
+ * more information.
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/util.h>
+
+#include <dns/log.h>
+#include <dns/sdb.h>
+
+#include <named/globals.h>
+
+#include <tcl.h>
+
+#include <tcldb.h>
+
+#define CHECK(op) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) return (result); \
+ } while (0)
+
+typedef struct tcldb_driver {
+ isc_mem_t *mctx;
+ Tcl_Interp *interp;
+} tcldb_driver_t;
+
+static tcldb_driver_t *the_driver = NULL;
+
+static dns_sdbimplementation_t *tcldb = NULL;
+
+static isc_result_t
+tcldb_driver_create(isc_mem_t *mctx, tcldb_driver_t **driverp) {
+ int tclres;
+ isc_result_t result = ISC_R_SUCCESS;
+ tcldb_driver_t *driver = isc_mem_get(mctx, sizeof(tcldb_driver_t));
+ if (driver == NULL)
+ return (ISC_R_NOMEMORY);
+ driver->mctx = mctx;
+ driver->interp = Tcl_CreateInterp();
+
+ tclres = Tcl_EvalFile(driver->interp, (char *) "lookup.tcl");
+ if (tclres != TCL_OK) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_SDB, ISC_LOG_ERROR,
+ "initializing tcldb: "
+ "loading 'lookup.tcl' failed: %s",
+ driver->interp->result);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ *driverp = driver;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ isc_mem_put(mctx, driver, sizeof(tcldb_driver_t));
+ return (result);
+
+}
+
+static void
+tcldb_driver_destroy(tcldb_driver_t **driverp) {
+ tcldb_driver_t *driver = *driverp;
+ Tcl_DeleteInterp(driver->interp);
+ isc_mem_put(driver->mctx, driver, sizeof(tcldb_driver_t));
+}
+
+/*
+ * Perform a lookup, by invoking the Tcl procedure "lookup".
+ */
+#ifdef DNS_CLIENTINFO_VERSION
+static isc_result_t
+tcldb_lookup(const char *zone, const char *name, void *dbdata,
+ dns_sdblookup_t *lookup, dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo)
+#else
+static isc_result_t
+tcldb_lookup(const char *zone, const char *name, void *dbdata,
+ dns_sdblookup_t *lookup)
+#endif /* DNS_CLIENTINFO_VERSION */
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ int tclres;
+ int rrc; /* RR count */
+ char **rrv; /* RR vector */
+ int i;
+ char *cmdv[3];
+ char *cmd;
+
+#ifdef DNS_CLIENTINFO_VERSION
+ UNUSED(methods);
+ UNUSED(clientinfo);
+#endif /* DNS_CLIENTINFO_VERSION */
+
+ tcldb_driver_t *driver = (tcldb_driver_t *) dbdata;
+
+ cmdv[0] = "lookup";
+ cmdv[1] = zone;
+ cmdv[2] = name;
+ cmd = Tcl_Merge(3, cmdv);
+ tclres = Tcl_Eval(driver->interp, cmd);
+ Tcl_Free(cmd);
+
+ if (tclres != TCL_OK) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_SDB, ISC_LOG_ERROR,
+ "zone '%s': tcl lookup function failed: %s",
+ zone, driver->interp->result);
+ return (ISC_R_FAILURE);
+ }
+
+ if (strcmp(driver->interp->result, "NXDOMAIN") == 0) {
+ result = ISC_R_NOTFOUND;
+ goto fail;
+ }
+
+ tclres = Tcl_SplitList(driver->interp, driver->interp->result,
+ &rrc, &rrv);
+ if (tclres != TCL_OK)
+ goto malformed;
+
+ for (i = 0; i < rrc; i++) {
+ isc_result_t tmpres;
+ int fieldc; /* Field count */
+ char **fieldv; /* Field vector */
+ tclres = Tcl_SplitList(driver->interp, rrv[i],
+ &fieldc, &fieldv);
+ if (tclres != TCL_OK) {
+ tmpres = ISC_R_FAILURE;
+ goto failrr;
+ }
+ if (fieldc != 3)
+ goto malformed;
+ tmpres = dns_sdb_putrr(lookup, fieldv[0], atoi(fieldv[1]),
+ fieldv[2]);
+ Tcl_Free((char *) fieldv);
+ failrr:
+ if (tmpres != ISC_R_SUCCESS)
+ result = tmpres;
+ }
+ Tcl_Free((char *) rrv);
+ if (result == ISC_R_SUCCESS)
+ return (result);
+
+ malformed:
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_SDB, ISC_LOG_ERROR,
+ "zone '%s': "
+ "malformed return value from tcl lookup function: %s",
+ zone, driver->interp->result);
+ result = ISC_R_FAILURE;
+ fail:
+ return (result);
+}
+
+/*
+ * Set up per-zone state. In our case, the database arguments of the
+ * zone are collected into a Tcl list and assigned to an element of
+ * the global array "dbargs".
+ */
+static isc_result_t
+tcldb_create(const char *zone, int argc, char **argv,
+ void *driverdata, void **dbdata)
+{
+ tcldb_driver_t *driver = (tcldb_driver_t *) driverdata;
+
+ char *list = Tcl_Merge(argc, argv);
+
+ Tcl_SetVar2(driver->interp, (char *) "dbargs", (char *) zone, list, 0);
+
+ Tcl_Free(list);
+
+ *dbdata = driverdata;
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * This driver does not support zone transfer, so allnodes() is NULL.
+ */
+static dns_sdbmethods_t tcldb_methods = {
+ tcldb_lookup,
+ NULL, /* authority */
+ NULL, /* allnodes */
+ tcldb_create,
+ NULL, /* destroy */
+ NULL /* lookup2 */
+};
+
+/*
+ * Initialize the tcldb driver.
+ */
+isc_result_t
+tcldb_init(void) {
+ isc_result_t result;
+ int flags = DNS_SDBFLAG_RELATIVEOWNER | DNS_SDBFLAG_RELATIVERDATA;
+
+ result = tcldb_driver_create(ns_g_mctx, &the_driver);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ return (dns_sdb_register("tcl", &tcldb_methods, the_driver, flags,
+ ns_g_mctx, &tcldb));
+}
+
+/*
+ * Wrapper around dns_sdb_unregister().
+ */
+void
+tcldb_clear(void) {
+ if (tcldb != NULL)
+ dns_sdb_unregister(&tcldb);
+ if (the_driver != NULL)
+ tcldb_driver_destroy(&the_driver);
+}
diff --git a/contrib/sdb/tcl/tcldb.h b/contrib/sdb/tcl/tcldb.h
new file mode 100644
index 0000000..1d4d16c
--- /dev/null
+++ b/contrib/sdb/tcl/tcldb.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#include <isc/types.h>
+
+isc_result_t tcldb_init(void);
+
+void tcldb_clear(void);
+
diff --git a/contrib/sdb/time/timedb.c b/contrib/sdb/time/timedb.c
new file mode 100644
index 0000000..11633ca
--- /dev/null
+++ b/contrib/sdb/time/timedb.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*
+ * A simple database driver that enables the server to return the
+ * current time in a DNS record.
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/util.h>
+
+#include <dns/sdb.h>
+
+#include <named/globals.h>
+
+#include "timedb.h"
+
+static dns_sdbimplementation_t *timedb = NULL;
+
+/*
+ * This database operates on relative names.
+ *
+ * "time" and "@" return the time in a TXT record.
+ * "clock" is a CNAME to "time"
+ * "current" is a DNAME to "@" (try time.current.time)
+ */
+#ifdef DNS_CLIENTINFO_VERSION
+static isc_result_t
+timedb_lookup(const char *zone, const char *name, void *dbdata,
+ dns_sdblookup_t *lookup, dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo)
+#else
+static isc_result_t
+timedb_lookup(const char *zone, const char *name, void *dbdata,
+ dns_sdblookup_t *lookup)
+#endif /* DNS_CLIENTINFO_VERSION */
+{
+ isc_result_t result;
+
+ UNUSED(zone);
+ UNUSED(dbdata);
+#ifdef DNS_CLIENTINFO_VERSION
+ UNUSED(methods);
+ UNUSED(clientinfo);
+#endif /* DNS_CLIENTINFO_VERSION */
+
+ if (strcmp(name, "@") == 0 || strcmp(name, "time") == 0) {
+ time_t now = time(NULL);
+ char buf[100];
+ int n;
+
+ /*
+ * Call ctime to create the string, put it in quotes, and
+ * remove the trailing newline.
+ */
+ n = snprintf(buf, sizeof(buf), "\"%s", ctime(&now));
+ if (n < 0)
+ return (ISC_R_FAILURE);
+ buf[n - 1] = '\"';
+ result = dns_sdb_putrr(lookup, "txt", 1, buf);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_FAILURE);
+ } else if (strcmp(name, "clock") == 0) {
+ result = dns_sdb_putrr(lookup, "cname", 1, "time");
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_FAILURE);
+ } else if (strcmp(name, "current") == 0) {
+ result = dns_sdb_putrr(lookup, "dname", 1, "@");
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_FAILURE);
+ } else
+ return (ISC_R_NOTFOUND);
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * lookup() does not return SOA or NS records, so authority() must be defined.
+ */
+static isc_result_t
+timedb_authority(const char *zone, void *dbdata, dns_sdblookup_t *lookup) {
+ isc_result_t result;
+
+ UNUSED(zone);
+ UNUSED(dbdata);
+
+ result = dns_sdb_putsoa(lookup, "localhost.", "root.localhost.", 0);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_FAILURE);
+
+ result = dns_sdb_putrr(lookup, "ns", 86400, "ns1.localdomain.");
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_FAILURE);
+ result = dns_sdb_putrr(lookup, "ns", 86400, "ns2.localdomain.");
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_FAILURE);
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * This zone does not support zone transfer, so allnodes() is NULL. There
+ * is no database specific data, so create() and destroy() are NULL.
+ */
+static dns_sdbmethods_t timedb_methods = {
+ timedb_lookup,
+ timedb_authority,
+ NULL, /* allnodes */
+ NULL, /* create */
+ NULL, /* destroy */
+ NULL /* lookup2 */
+};
+
+/*
+ * Wrapper around dns_sdb_register().
+ */
+isc_result_t
+timedb_init(void) {
+ unsigned int flags;
+ flags = DNS_SDBFLAG_RELATIVEOWNER | DNS_SDBFLAG_RELATIVERDATA;
+ return (dns_sdb_register("time", &timedb_methods, NULL, flags,
+ ns_g_mctx, &timedb));
+}
+
+/*
+ * Wrapper around dns_sdb_unregister().
+ */
+void
+timedb_clear(void) {
+ if (timedb != NULL)
+ dns_sdb_unregister(&timedb);
+}
diff --git a/contrib/sdb/time/timedb.h b/contrib/sdb/time/timedb.h
new file mode 100644
index 0000000..3089988
--- /dev/null
+++ b/contrib/sdb/time/timedb.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#include <isc/types.h>
+
+isc_result_t timedb_init(void);
+
+void timedb_clear(void);
+