From ea648e70a989cca190cd7403fe892fd2dcc290b4 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 5 May 2024 20:37:14 +0200 Subject: Adding upstream version 1:9.11.5.P4+dfsg. Signed-off-by: Daniel Baumann --- contrib/sdb/bdb/README | 37 ++ contrib/sdb/bdb/bdb.c | 245 +++++++++++ contrib/sdb/bdb/bdb.h | 30 ++ contrib/sdb/bdb/zone2bdb.c | 186 +++++++++ contrib/sdb/dir/dirdb.c | 200 +++++++++ contrib/sdb/dir/dirdb.h | 18 + contrib/sdb/ldap/INSTALL.ldap | 83 ++++ contrib/sdb/ldap/README.ldap | 48 +++ contrib/sdb/ldap/README.zone2ldap | 17 + contrib/sdb/ldap/ldapdb.c | 690 +++++++++++++++++++++++++++++++ contrib/sdb/ldap/ldapdb.h | 6 + contrib/sdb/ldap/zone2ldap.1 | 64 +++ contrib/sdb/ldap/zone2ldap.c | 772 +++++++++++++++++++++++++++++++++++ contrib/sdb/pgsql/pgsqldb.c | 353 ++++++++++++++++ contrib/sdb/pgsql/pgsqldb.h | 18 + contrib/sdb/pgsql/zonetodb.c | 283 +++++++++++++ contrib/sdb/sqlite/README.sdb_sqlite | 67 +++ contrib/sdb/sqlite/sqlitedb.c | 325 +++++++++++++++ contrib/sdb/sqlite/sqlitedb.h | 15 + contrib/sdb/sqlite/zone2sqlite.c | 296 ++++++++++++++ contrib/sdb/tcl/lookup.tcl | 43 ++ contrib/sdb/tcl/tcldb.c | 238 +++++++++++ contrib/sdb/tcl/tcldb.h | 18 + contrib/sdb/time/timedb.c | 148 +++++++ contrib/sdb/time/timedb.h | 18 + 25 files changed, 4218 insertions(+) create mode 100644 contrib/sdb/bdb/README create mode 100644 contrib/sdb/bdb/bdb.c create mode 100644 contrib/sdb/bdb/bdb.h create mode 100644 contrib/sdb/bdb/zone2bdb.c create mode 100644 contrib/sdb/dir/dirdb.c create mode 100644 contrib/sdb/dir/dirdb.h create mode 100644 contrib/sdb/ldap/INSTALL.ldap create mode 100644 contrib/sdb/ldap/README.ldap create mode 100644 contrib/sdb/ldap/README.zone2ldap create mode 100644 contrib/sdb/ldap/ldapdb.c create mode 100644 contrib/sdb/ldap/ldapdb.h create mode 100644 contrib/sdb/ldap/zone2ldap.1 create mode 100644 contrib/sdb/ldap/zone2ldap.c create mode 100644 contrib/sdb/pgsql/pgsqldb.c create mode 100644 contrib/sdb/pgsql/pgsqldb.h create mode 100644 contrib/sdb/pgsql/zonetodb.c create mode 100644 contrib/sdb/sqlite/README.sdb_sqlite create mode 100644 contrib/sdb/sqlite/sqlitedb.c create mode 100644 contrib/sdb/sqlite/sqlitedb.h create mode 100644 contrib/sdb/sqlite/zone2sqlite.c create mode 100644 contrib/sdb/tcl/lookup.tcl create mode 100644 contrib/sdb/tcl/tcldb.c create mode 100644 contrib/sdb/tcl/tcldb.h create mode 100644 contrib/sdb/time/timedb.c create mode 100644 contrib/sdb/time/timedb.h (limited to 'contrib/sdb') 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: +From: Nuno Miguel Rodrigues +Subject: Berkeley DB BIND9 SDB + +Replied: Thu, 16 May 2002 11:47:35 +1000 +Replied: Nuno Miguel Rodrigues +Return-Path: +X-X-Sender: +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 + + + + 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#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 + +/* + * 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 + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 \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 + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#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_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 ". 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 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 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 + + + 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 + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#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_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 +.SH AUTHOR +Jeff McNeil + 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 + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LDAP_DEPRECATED 1 + +#include + +#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 + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#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_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 +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * 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 + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#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_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 +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 \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 + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include + +#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_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 + +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#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_result_t timedb_init(void); + +void timedb_clear(void); + -- cgit v1.2.3